diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 25ac0596d78b5..a2dbf8ab256ae 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -35,6 +35,9 @@ /clang/lib/CIR @lanza @bcardosolopes /clang/tools/cir-* @lanza @bcardosolopes +# BEGIN SWIFT +/lldb/**/*Swift* @adrian-prantl +# END SWIFT /lldb/ @JDevlieghere # MLIR Interfaces. diff --git a/.gitignore b/.gitignore index a84268a7f6863..6662481fd5227 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,9 @@ #OS X specific files. .DS_store +# Temporary file created by the automerger tool. +.am.txt + # Ignore the user specified CMake presets in subproject directories. /*/CMakeUserPresets.json diff --git a/README.md b/README.md index a9b29ecbc1a3a..390fda0b08a34 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +# Swift's fork of llvm-project + +This is Swift's fork of llvm-project. For more information on Swift's +branching scheme, please see +[apple-docs/AppleBranchingScheme.md](https://github.com/apple/llvm-project/tree/apple/main/apple-docs/AppleBranchingScheme.md). + +The LLVM project's main README follows. + # The LLVM Compiler Infrastructure [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/llvm/llvm-project/badge)](https://securityscorecards.dev/viewer/?uri=github.com/llvm/llvm-project) diff --git a/apple-ci/amtool b/apple-ci/amtool new file mode 100755 index 0000000000000..ba542cb9cd6d8 --- /dev/null +++ b/apple-ci/amtool @@ -0,0 +1,359 @@ +#!/usr/bin/env python3 +""" +Tool to reproduce and resolve the issues reported by the automerger. +""" + +import argparse +import json +import logging +import shlex +import subprocess +import sys +from typing import List, Optional + +log = logging.getLogger() + +REMOTE = 'git@github.com:swiftlang/llvm-project.git' + +class GitError(Exception): + """ + An exception thrown if the git command failed. + + Attributes + ---------- + args : List[str] + The list of arguments passed to `git`. + returncode : int + The exit code of the `git` process. + stdout : str + The output of `git`. + stderr : str + The error output of `git`. + """ + + def __init__(self, args, returncode: int, stdout: str, stderr: str): + self.args = args + self.returncode = returncode + self.stdout = stdout + self.stderr = stderr + + def __repr__(self): + return f'GitError({self.args}, {self.returncode}, "{self.stdout}", "{self.stderr}")' + + +def _git_to_str(args: List[str]): + return 'git ' + ' '.join(map(lambda arg: shlex.quote(arg), args)) + + +def invoke(*cmd, git_dir: Optional[str] = None, + stdin: Optional[str] = None, + stdout=None, + stderr=subprocess.PIPE, + strip: bool = True, ignore_error: bool = False, + timeout: Optional[int] = None): + """ Invokes a git subprocess with the passed string arguments and return + the stdout of the git command as a string if text otherwise a file + handle. + """ + if git_dir is not None: + all_args = ['-C', git_dir] + list(cmd) + else: + all_args = list(cmd) + log.debug('$ %s', _git_to_str(all_args)) + p = subprocess.Popen(['git'] + all_args, + stdout=stdout, + stderr=stderr, + stdin=subprocess.PIPE if stdin else None, + universal_newlines=True) + out, err = p.communicate(input=stdin, timeout=timeout) + if p.returncode == 0: + if out: + if strip: + out = out.rstrip() + for line in out.splitlines(): + log.debug('STDOUT: %s', line) + if err: + for line in err.rstrip().splitlines(): + log.debug('STDERR: %s', line) + return out + log.debug('EXIT STATUS: %d', p.returncode) + if err: + for line in err.rstrip().splitlines(): + log.debug('STDERR: %s', line) + if ignore_error: + return None + raise GitError(all_args, p.returncode, out, err) + + +def git(*cmd, **kwargs): + """ Invokes a git subprocess with the passed string arguments and return + the stdout of the git command. + """ + return invoke(*cmd, **kwargs, stdout=subprocess.PIPE) + + +class Commit: + """ Represents the commit being merged.""" + def __init__(self, sha: str): + self.sha = sha + + def short_sha(self): + return self.sha[0:12] + + def get_previous_commit(self): + return git('rev-parse', self.sha + '^') + + +class MergeId: + """ Encapsulates the merge ID constructed by the automerger and the + corresponding git operations. + """ + prefix = 'refs/am' + + def __init__(self, merge_id: str): + self.merge_id = merge_id + parts = merge_id.split('_') + try: + self.commit = Commit(parts[0]) + self.target_branch = '/'.join(parts[1:]) + except IndexError: + log.error("Merge Id not correctly formed.") + + @property + def ref_name(self): + return self.prefix + "/changes/" + self.merge_id + + @property + def merge_candidate_ref_name(self): + return self.prefix + "/merge-candidate/" + self.merge_id + + def get_previous_merge_id(self): + previous_commit = self.commit.get_previous_commit() + return MergeId(self.merge_id.replace(self.commit.sha, previous_commit)) + + @staticmethod + def fetch(*args): + """Helper function for the "git fetch" command.""" + try: + git('fetch', *args) + return True + except GitError as e: + if e.returncode == 128: + return False + raise e + + def fetch_ref_name(self): + refspec = self.ref_name + ":" + self.ref_name + return self.fetch(REMOTE, self.target_branch, refspec) + + def fetch_merge_candidate_ref_name(self): + refspec = "+" + self.merge_candidate_ref_name + ":" + self.merge_candidate_ref_name + return self.fetch(REMOTE, refspec) + + @staticmethod + def checkout(*args): + """Helper function for the "git checkout" command.""" + try: + git('checkout', *args) + return (True, '') + except GitError as e: + return (False, e.stderr) + + def checkout_merge_candidate(self): + """Checkout the merge candidate for this merge ID.""" + return self.checkout(self.merge_candidate_ref_name) + + def checkout_target_branch(self): + """Checkout the target branch for this merge ID.""" + if self.fetch(REMOTE, self.target_branch): + return self.checkout('FETCH_HEAD') + return (False, '') + + def get_source_branch_name(self): + """Get the source branch name (upstream) for this target branch.""" + content = None + if self.fetch(REMOTE, 'repo/apple-llvm-config/am'): + content = git('cat-file', '-p', + 'FETCH_HEAD:apple-llvm-config/am/am-config.json') + if not content: + return None + config = json.loads(content) + if not config: + return None + for json_dict in config: + if json_dict['target'] == self.target_branch: + return json_dict['upstream'] + return None + + def merge(self): + source_branch = self.get_source_branch_name() + if not source_branch: + log.error(f"Could not figure out the source branch for {self.target_branch}.") + try: + git('merge', '--no-edit', "-X", "diff-algorithm=histogram", + "--summary", self.ref_name, '-m', + f"Merge commit '{self.commit.short_sha()}' from {source_branch} into {self.target_branch}") + return True + except GitError as e: + if 'CONFLICT' in e.stdout: + return False + raise e + + def push(self): + try: + git('push', REMOTE, f'HEAD:{self.ref_name}') + return (True, '') + except GitError as e: + return (False, e.stdout) + + +def parse_args(): + """Parse the command line arguments.""" + + parser = argparse.ArgumentParser(description="Automerger Tool") + parser.add_argument('-v', '--verbose', action='store_true', required=False, + help='enable verbose outout and show commands being run') + + subparsers = parser.add_subparsers(dest='command', required=True, + help='the command to run') + # Reproduce + parser_reproduce = subparsers.add_parser('reproduce', + help='Reproduce the issue observed when performing merge') + parser_reproduce.add_argument('id', help='the merge ID to reproduce') + # Push + parser_push = subparsers.add_parser('push', + help='push the resolution, so that the automerger can pick it up') + # Override push uncommitted change error + parser_push.add_argument('--ignore-uncommitted-changes', dest='ignore_uncommitted', + action='store_true', required=False, + help='allowing pushing a commit even if uncommitted changes exist') + + args = parser.parse_args() + return args + + +def main(): + args = parse_args() + + # Default to INFO level. Increase to DEBUG level if verbose flag passed. + log_level = logging.INFO + if args.verbose: + log_level = logging.DEBUG + + log.setLevel(log_level) + # create console handler with a higher log level + ch = logging.StreamHandler() + ch.setLevel(log_level) + # create formatter and add it to the handlers + ch_fomatter = logging.Formatter('%(levelname)s: %(message)s') + ch.setFormatter(ch_fomatter) + # add the handlers to the logger + log.addHandler(ch) + + # File to record the merge ID locally so we can use it in the `push` + # command without having the user enter it again. + record = '.am.txt' + + # Reproduce mode. + if args.command == "reproduce": + log.info('Attempting to reproduce the issue.') + merge_id = MergeId(args.id) + + # Record the ref locally so we can use it in the `push` command + # without having the user enter it again. + with open(record, 'w') as f: + f.write(args.id) + + # Fetch the ref. If we failed to fetch then just return because it is + # likely that the commit has already been merged and the ref deleted. + log.info('Fetching the ref and the target branch ...') + status = merge_id.fetch_ref_name() + if not status: + log.error('Unable to fetch the ref. Are you in the right repo? Or, is it already merged?') + return 1 + log.info('Successfully fetched.') + + # Fetch the merge candidate ref for the previous commit and check it + # out in order to apply this commit on top of it. This allows us to + # reproduce just this issue and not any other issues in the prior + # commits which have not been merged yet. + # If we failed to fetch then it is likely that the previous commit has + # already been merged. Checkout the target branch in that case. + previous_merge_id = merge_id.get_previous_merge_id() + log.info('Fetching the previous commit ...') + status = previous_merge_id.fetch_merge_candidate_ref_name() + if not status: + log.info('Previous commit already merged. Checking out the target branch instead.') + status, msg = merge_id.checkout_target_branch() + if not status: + log.error('Failed to checkout.') + log.error(msg) + return 1 + log.info('Successfully checked out the target branch.') + else: + log.info('Successfully fetched.') + log.info('Now checking out the previous commit.') + status, msg = previous_merge_id.checkout_merge_candidate() + if not status: + log.error('Failed to checkout.') + log.error(msg) + return 1 + log.info('Successfully checked out the previous commit.') + + # Perform the merge. + log.info('Performing the merge ...') + rc = merge_id.merge() + if not rc: + log.info('Please resolve the conflicts and push the merge commit.') + return 0 + log.info('No merge conflict seen. Is this a build/test failure?') + log.info('Please resolve the issue and push the commit.') + return 0 + + # Push mode. + elif args.command == "push": + # Read the ref saved locally by the `reproduce` command. + try: + with open(record, 'r') as f: + content = f.read() + except FileNotFoundError: + log.error('Did you run the `reproduce` command before?') + return 1 + log.debug(f'Content : {content}') + + # Check if we happen to be still in the middle of the merge. + # Proceed to push if otherwise the merge has been concluded. + try: + git('rev-parse', '--verify', '--quiet', 'MERGE_HEAD') + log.error('Looks like you are in the middle of the merge.') + log.error('Please conclude the merge before pushing.') + return 1 + except GitError: + pass + + # Check if we have any unstaged or uncommitted changes in the tree. + if len(git('diff-index', 'HEAD')): + if args.ignore_uncommitted: + log.warning('Ignoring uncommitted changes.') + else: + log.error('Looks like you have unstaged or uncommitted changes.') + log.error('Please make sure everything has been added to the commit.') + log.error('Use `--ignore-uncommitted-changes` to ignore, and push anyway.') + return 1 + + # Save the commit sha so that we can include it in the output message. + merge_commit = git('rev-parse', 'HEAD') + + # Perform the push. + merge_id = MergeId(content) + log.info("Pushing ...") + status, msg = merge_id.push() + if not status: + log.error('Failed to push.') + log.error(msg) + return 1 + log.info(f'Successfully pushed `{merge_commit}`.') + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/apple-ci/clang/am/build.sh b/apple-ci/clang/am/build.sh new file mode 100644 index 0000000000000..736f5008def9f --- /dev/null +++ b/apple-ci/clang/am/build.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -eu + +SRC_DIR=$PWD/llvm-project +BUILD_DIR=$PWD/build + +for arg; do + case $arg in + --src=*) SRC_DIR="${arg##*=}"; shift ;; + --build=*) BUILD_DIR="${arg##*=}"; shift ;; + *) echo "Incorrect usage." >&2; exit 1 ;; + esac +done + +echo +echo "SRC_DIR . . . . = $SRC_DIR" +echo "BUILD_DIR . . . = $BUILD_DIR" +echo + +NINJA=$(xcrun -f ninja) + +export CLANG_CRASH_DIAGNOSTICS_DIR=${WORKSPACE}/.CLANG_CRASH_DIAGNOSTICS_DIR + +HOST_COMPILER_PATH=$(dirname $(xcrun -f clang)) + +mkdir -p $BUILD_DIR && cd $_ +set -x +xcrun cmake -G Ninja \ + -DCMAKE_MAKE_PROGRAM=$NINJA \ + -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON \ + -DCMAKE_C_COMPILER=$HOST_COMPILER_PATH/clang \ + -DCMAKE_CXX_COMPILER=$HOST_COMPILER_PATH/clang++ \ + -DCMAKE_C_COMPILER_LAUNCHER=$HOST_COMPILER_PATH/clang-cache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=$HOST_COMPILER_PATH/clang-cache \ + -DLLVM_TARGETS_TO_BUILD="X86;ARM;AArch64" \ + -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;compiler-rt;lldb" \ + -DLLDB_ENABLE_SWIFT_SUPPORT=OFF \ + -DLLDB_INCLUDE_TESTS=OFF \ + $SRC_DIR/llvm && $NINJA diff --git a/apple-ci/pr.sh b/apple-ci/pr.sh new file mode 100755 index 0000000000000..f21587f77896b --- /dev/null +++ b/apple-ci/pr.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Build script for ci.swift.org PR testing. +# Tools like cmake/ninja needs to be in $PATH +# and run the script in build directory. + +LLVM_PROJECT_SRC=$1 +LLVM_ENABLE_PROJECTS=${2:-"clang;clang-tools-extra"} + +echo '--- CMake Config ---' +cmake -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DLLVM_ENABLE_ASSERTIONS=On \ + -DLLVM_ENABLE_PROJECTS=${LLVM_ENABLE_PROJECTS} \ + '-DLLVM_TARGETS_TO_BUILD=X86;ARM;AArch64' \ + '-DLLVM_LIT_ARGS=-v' \ + ${LLVM_PROJECT_SRC}/llvm + +echo '--- Ninja Build ---' +ninja -v +echo '--- Ninja Test ---' +ninja -v -k 0 check-all diff --git a/apple-docs/AppleBranchingScheme.md b/apple-docs/AppleBranchingScheme.md new file mode 100644 index 0000000000000..694d2166bd372 --- /dev/null +++ b/apple-docs/AppleBranchingScheme.md @@ -0,0 +1,102 @@ +# Apple's branching scheme for llvm-project + +This document explains the various important branches in +[apple/llvm-project](https://github.com/apple/llvm-project), +how they relate to one another, and how they correspond to branches in the +[apple/swift](https://github.com/apple/swift) repository. + +These are the most important branches to know about: + +- [next](https://github.com/apple/llvm-project/tree/next) + is aligned with Swift's + [next](https://github.com/apple/swift/tree/next) branch. +- The current `stable` branch (check + [update-checkout-config.json](https://github.com/apple/swift/blob/main/utils/update_checkout/update-checkout-config.json) + for up-to-date info) is aligned with Swift's + [main](https://github.com/apple/swift/tree/main) branch. +- `swift/*` branches are aligned with the corresponding Swift branch without + the `swift/` prefix. + +## Upstream branches + +The `llvm.org/*` branches are forwarded, unchanged, from +[github.com/llvm/llvm-project](https://github.com/llvm/llvm-project). These +are read-only, exact copies of the upstream LLVM project's branches. They are +forwarded here as a convenience for easy reference, to avoid the need for extra +remotes. + +- [llvm.org/main](https://github.com/apple/llvm-project/tree/llvm.org/main) + is the most important branch here, matching the LLVM project's + [main](https://github.com/llvm/llvm-project/tree/main) branch. + +## Downstream branches + +Downstream branches contain changes on top of what is in the LLVM project. +This includes some patches that have not yet been upstreamed to the LLVM +project, including some special support for Swift. + +We are actively working on either upstreaming or reverting many of those +differences. The goal is to fully eliminate all *non-Swift* differences between +`next` and `llvm.org/main`. + +Any LLVM development that does not depend on the Swift repository should happen +upstream. The only changes that are allowed to be submitted without going +through upstream LLVM are those that are either directly related to upstreaming +content or that are needed because of the existing differences (e.g., resolving +merge conflicts or fixing build errors). + +- [next](https://github.com/apple/llvm-project/tree/next) is + downstream of + [llvm.org/main](https://github.com/apple/llvm-project/tree/llvm.org/main). + There is a gated automerger that does testing before merging in. Most + changes to this branch should be redirected to + (see also ). Changes made to a + stabilization branch are cherry-picked here. +- `stable/*`: These branches are periodic stabilization branches, where + fixes are made or manually cherry-picked from `llvm.org/main`. +- `swift/release/*`: These are Swift release branches, where fixed are cherry- + picked from a stabilization branch during release convergence. + +## Historical trivia + +### Mappings to branches from before the monorepo transition + +Before the LLVM project's monorepo transition, Apple maintained downstream +forks of various split repositories. Here is a mapping from a few of the new +branches in the llvm-project monorepo to their original split repositories. + +- [apple/main](https://github.com/apple/llvm-project/tree/apple/main) was + generated from the `upstream-with-swift` branches in + [swift-clang](https://github.com/apple/swift-clang/), + [swift-llvm](https://github.com/apple/swift-llvm/), + [swift-compiler-rt](https://github.com/apple/swift-compiler-rt/), + [swift-clang-tools-extra](https://github.com/apple/swift-clang-tools-extra/), + and [swift-libcxx](https://github.com/apple/swift-libcxx/), with the notable + **exclusion** of [swift-lldb](https://github.com/apple/swift-lldb/), +- [swift/next](https://github.com/apple/llvm-project/tree/swift/next) + was generated from the `upstream-with-swift` branch in + [swift-lldb](https://github.com/apple/swift-lldb/), interleaved with merges + from [apple/main](https://github.com/apple/llvm-project/tree/apple/main). +- [apple/stable/20190104](https://github.com/apple/llvm-project/tree/apple/stable/20190104) + was generated from the `swift-5.1-branch` branches in + [swift-clang](https://github.com/apple/swift-clang/), + [swift-llvm](https://github.com/apple/swift-llvm/), + [swift-compiler-rt](https://github.com/apple/swift-compiler-rt/), + [swift-clang-tools-extra](https://github.com/apple/swift-clang-tools-extra/), + and [swift-libcxx](https://github.com/apple/swift-libcxx/), with the notable + **exclusion** of [swift-lldb](https://github.com/apple/swift-lldb/), +- [swift/swift-5.1-branch](https://github.com/apple/llvm-project/tree/swift/swift-5.1-branch) + was generated from the `swift-5.1-branch` branch in + [swift-lldb](https://github.com/apple/swift-lldb/), interleaved with merges + from + [apple/stable/20190104](https://github.com/apple/llvm-project/tree/apple/stable/20190104). +- [swift/main](https://github.com/apple/llvm-project/tree/swift/main) was + generated from the `stable` branch from all six split repos. + +### Branching scheme prior to July 2021 change + +Prior to July 2021, the `apple:llvm-project` repository maintained two sets +of downstream branches: one set that did not include any content that _depended +on_ Swift, and another set that included all downstream changes. See +[this forum thread](https://forums.swift.org/t/simplifying-the-apple-llvm-project-branches/50287) +explaining the change. diff --git a/apple-llvm-config/am/apple-master.json b/apple-llvm-config/am/apple-master.json new file mode 100644 index 0000000000000..427b8b4bbf218 --- /dev/null +++ b/apple-llvm-config/am/apple-master.json @@ -0,0 +1,3 @@ +{ + "upstream": "llvm.org/master" +} diff --git a/apple-llvm-config/am/swift-master-next.json b/apple-llvm-config/am/swift-master-next.json new file mode 100644 index 0000000000000..6e0e6b553f348 --- /dev/null +++ b/apple-llvm-config/am/swift-master-next.json @@ -0,0 +1,3 @@ +{ + "upstream": "apple/master" +} diff --git a/apple-llvm-config/pr.json b/apple-llvm-config/pr.json new file mode 100644 index 0000000000000..9de9ce6265850 --- /dev/null +++ b/apple-llvm-config/pr.json @@ -0,0 +1,9 @@ +{ +"type": "github", +"domain": "github.com", +"user": "apple", +"repo": "llvm-project", +"test": { + "type":"swift-ci" +} +} diff --git a/bolt/test/runtime/AArch64/r_aarch64_prelxx.s b/bolt/test/runtime/AArch64/r_aarch64_prelxx.s new file mode 100644 index 0000000000000..89bc6ccf667fb --- /dev/null +++ b/bolt/test/runtime/AArch64/r_aarch64_prelxx.s @@ -0,0 +1,37 @@ +// This test checks processing of R_AARCH64_PREL64/32/16 relocations + +// RUN: %clang %cflags -nostartfiles -nostdlib %s -o %t.exe -Wl,-q \ +// RUN: -Wl,-z,max-page-size=4 +// RUN: llvm-readelf -Wa %t.exe | FileCheck %s -check-prefix=CHECKPREL + +// CHECKPREL: R_AARCH64_PREL16 {{.*}} .dummy + 0 +// CHECKPREL-NEXT: R_AARCH64_PREL32 {{.*}} _start + 4 +// CHECKPREL-NEXT: R_AARCH64_PREL64 {{.*}} _start + 8 + +// RUN: llvm-bolt %t.exe -o %t.bolt +// RUN: llvm-readobj -S --section-data %t.bolt | FileCheck %s + +// CHECK: Name: .data +// CHECK: SectionData ( +// CHECK: 0000: FCFF0000 44FF3F00 44FF3F00 00000000 +// CHECK: ) + + .text + .align 4 + .globl _start + .type _start, %function +_start: + adr x0, datatable + mov x0, #0 + ret + +.section .dummy, "da" +dummy: + .word 0 + +.section .data +datatable: + .hword dummy - datatable + .align 2 + .word _start - datatable + .xword _start - datatable diff --git a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp index 03a3e8404e069..21cad46ee11b2 100644 --- a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp +++ b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.cpp @@ -253,6 +253,13 @@ void ExpandModularHeadersPPCallbacks::PragmaAssumeNonNullEnd( SourceLocation Loc) { parseToLocation(Loc); } +/*TO_UPSTREAM(BoundsSafety) ON*/ +void ExpandModularHeadersPPCallbacks:: +PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef Spec) { + parseToLocation(Loc); +} +/*TO_UPSTREAM(BoundsSafety) OFF*/ void ExpandModularHeadersPPCallbacks::MacroExpands(const Token &MacroNameTok, const MacroDefinition &, SourceRange Range, diff --git a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h index a263681b3c633..076738dd7c1aa 100644 --- a/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h +++ b/clang-tools-extra/clang-tidy/ExpandModularHeadersPPCallbacks.h @@ -100,6 +100,10 @@ class ExpandModularHeadersPPCallbacks : public PPCallbacks { void PragmaWarningPop(SourceLocation Loc) override; void PragmaAssumeNonNullBegin(SourceLocation Loc) override; void PragmaAssumeNonNullEnd(SourceLocation Loc) override; + /*TO_UPSTREAM(BoundsSafety) ON*/ + void PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef) override; + /*TO_UPSTREAM(BoundsSafety) OFF*/ void MacroExpands(const Token &MacroNameTok, const MacroDefinition &, SourceRange Range, const MacroArgs *) override; void MacroDefined(const Token &MacroNameTok, diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index 1e981825c7c15..367b60b0239a2 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -893,30 +893,53 @@ void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams &Params, }); } +/// Validate that `Edits` are valid and form a `WorkspaceEdit` that contains +/// the edits as its `changes`. +static llvm::Expected +formWorkspaceEdit(const FileEdits &Edits, const ClangdServer &Server) { + if (auto Err = validateEdits(Server, Edits)) + return std::move(Err); + WorkspaceEdit Result; + // FIXME: use documentChanges if SupportDocumentChanges is true. + Result.changes.emplace(); + for (const auto &Rep : Edits) { + (*Result.changes)[URI::createFile(Rep.first()).toString()] = + Rep.second.asTextEdits(); + } + return Result; +} + void ClangdLSPServer::onRename(const RenameParams &Params, Callback Reply) { Path File = std::string(Params.textDocument.uri.file()); if (!Server->getDraft(File)) return Reply(llvm::make_error( "onRename called for non-added file", ErrorCode::InvalidParams)); + auto Callback = [Reply = std::move(Reply), + this](llvm::Expected R) mutable { + if (!R) + return Reply(R.takeError()); + llvm::Expected WorkspaceEdit = + formWorkspaceEdit(R->GlobalChanges, *Server); + Reply(std::move(WorkspaceEdit)); + }; Server->rename(File, Params.position, Params.newName, Opts.Rename, - [File, Params, Reply = std::move(Reply), - this](llvm::Expected R) mutable { - if (!R) - return Reply(R.takeError()); - if (auto Err = validateEdits(*Server, R->GlobalChanges)) - return Reply(std::move(Err)); - WorkspaceEdit Result; - // FIXME: use documentChanges if SupportDocumentChanges is - // true. - Result.changes.emplace(); - for (const auto &Rep : R->GlobalChanges) { - (*Result - .changes)[URI::createFile(Rep.first()).toString()] = - Rep.second.asTextEdits(); - } - Reply(Result); - }); + std::move(Callback)); +} + +void ClangdLSPServer::onIndexedRename(const IndexedRenameParams &Params, + Callback Reply) { + auto Callback = [Reply = std::move(Reply), + this](llvm::Expected Edits) mutable { + if (!Edits) { + return Reply(Edits.takeError()); + } + llvm::Expected WorkspaceEdit = + formWorkspaceEdit(*Edits, *Server); + Reply(std::move(WorkspaceEdit)); + }; + Server->indexedRename(Params.positions, Params.textDocument.uri.file(), + Params.oldName, Params.newName, std::move(Callback)); } void ClangdLSPServer::onDocumentDidClose( @@ -1679,6 +1702,7 @@ void ClangdLSPServer::bindMethods(LSPBinder &Bind, Bind.method("textDocument/switchSourceHeader", this, &ClangdLSPServer::onSwitchSourceHeader); Bind.method("textDocument/prepareRename", this, &ClangdLSPServer::onPrepareRename); Bind.method("textDocument/rename", this, &ClangdLSPServer::onRename); + Bind.method("workspace/indexedRename", this, &ClangdLSPServer::onIndexedRename); Bind.method("textDocument/hover", this, &ClangdLSPServer::onHover); Bind.method("textDocument/documentSymbol", this, &ClangdLSPServer::onDocumentSymbol); Bind.method("workspace/executeCommand", this, &ClangdLSPServer::onCommand); diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h index f43734ec1ede3..244cae9e42ca7 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -142,6 +142,7 @@ class ClangdLSPServer : private ClangdServer::Callbacks, void onPrepareRename(const TextDocumentPositionParams &, Callback); void onRename(const RenameParams &, Callback); + void onIndexedRename(const IndexedRenameParams &, Callback); void onHover(const TextDocumentPositionParams &, Callback>); void onPrepareTypeHierarchy(const TypeHierarchyPrepareParams &, diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index 52844129834c3..d5a04a28ea4c9 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -640,6 +640,50 @@ void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName, WorkScheduler->runWithAST("Rename", File, std::move(Action)); } +void ClangdServer::indexedRename( + const std::map> &Positions, + PathRef PrimaryFile, llvm::StringRef OldName, llvm::StringRef NewName, + Callback CB) { + ParseInputs Inputs; + Inputs.TFS = &TFS; + Inputs.CompileCommand = CDB.getCompileCommand(PrimaryFile) + .value_or(CDB.getFallbackCommand(PrimaryFile)); + IgnoreDiagnostics IgnoreDiags; + std::unique_ptr CI = + buildCompilerInvocation(Inputs, IgnoreDiags); + if (!CI) { + return CB(llvm::make_error( + "Unable to get compiler arguments for primary file", + llvm::inconvertibleErrorCode())); + } + const LangOptions &LangOpts = CI->getLangOpts(); + + tooling::SymbolName OldSymbolName(OldName, LangOpts); + tooling::SymbolName NewSymbolName(NewName, LangOpts); + + llvm::StringMap> FilesToRanges; + for (auto Entry : Positions) { + std::vector &Ranges = FilesToRanges[Entry.first.file()]; + for (Position Pos : Entry.second) { + // Compute the range for the given position: + // - If the old name is a simple identifier, we can add its length to the + // start position's column because identifiers can't contain newlines + // - If we have a multi-piece symbol name, them `editsForLocations` will + // only look at the start of the range to call + // `findObjCSymbolSelectorPieces`. It is thus fine to use an empty + // range that points to the symbol's start. + Position End = Pos; + if (std::optional Identifier = + OldSymbolName.getSinglePiece()) { + End.line += Identifier->size(); + } + Ranges.push_back({Pos, End}); + } + } + CB(editsForLocations(FilesToRanges, OldSymbolName, NewSymbolName, + *getHeaderFS().view(std::nullopt), LangOpts)); +} + namespace { // May generate several candidate selections, due to SelectionTree ambiguity. // vector of pointers because GCC doesn't like non-copyable Selection. diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index 1e612e2ba618e..83ddb24263709 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -354,6 +354,17 @@ class ClangdServer { void rename(PathRef File, Position Pos, llvm::StringRef NewName, const RenameOptions &Opts, Callback CB); + /// Rename all occurrences of a symbol named `OldName` to `NewName` at the + /// given `Positions`. + /// + /// `PrimaryFile` is used to determine the language options for the symbol to + /// rename, eg. to decide whether `OldName` and `NewName` are Objective-C + /// selectors or normal identifiers. + void + indexedRename(const std::map> &Positions, + PathRef PrimaryFile, llvm::StringRef OldName, + llvm::StringRef NewName, Callback CB); + struct TweakRef { std::string ID; /// ID to pass for applyTweak. std::string Title; /// A single-line message to show in the UI. diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index 0eb196fbad46a..6b167778ee612 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -98,6 +98,7 @@ toCompletionItemKind(index::SymbolKind Kind, using SK = index::SymbolKind; switch (Kind) { case SK::Unknown: + case SK::CommentTag: return CompletionItemKind::Missing; case SK::Module: case SK::Namespace: diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp index 05c8041df7de7..2d01ae7aafa19 100644 --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -298,6 +298,7 @@ SymbolKind adjustKindToCapability(SymbolKind Kind, SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) { switch (Kind) { case index::SymbolKind::Unknown: + case index::SymbolKind::CommentTag: return SymbolKind::Variable; case index::SymbolKind::Module: return SymbolKind::Module; @@ -1243,6 +1244,15 @@ llvm::json::Value toJSON(const PrepareRenameResult &PRR) { }; } +bool fromJSON(const llvm::json::Value &Params, + IndexedRenameParams &IndexedRename, llvm::json::Path P) { + llvm::json::ObjectMapper O(Params, P); + return O && O.map("textDocument", IndexedRename.textDocument) && + O.map("oldName", IndexedRename.oldName) && + O.map("newName", IndexedRename.newName) && + O.map("positions", IndexedRename.positions); +} + llvm::json::Value toJSON(const DocumentHighlight &DH) { return llvm::json::Object{ {"range", toJSON(DH.range)}, diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h index c7ef1a13e6e39..8a104fae317fa 100644 --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -128,6 +128,23 @@ struct URIForFile { llvm::json::Value toJSON(const URIForFile &U); bool fromJSON(const llvm::json::Value &, URIForFile &, llvm::json::Path); +template +bool fromJSON(const llvm::json::Value &E, std::map &Out, + llvm::json::Path P) { + if (auto *O = E.getAsObject()) { + Out.clear(); + for (const auto &KV : *O) { + URIForFile URI; + fromJSON(llvm::json::Value(KV.first), URI, P); + if (!fromJSON(KV.second, Out[URI], P.field(KV.first))) + return false; + } + return true; + } + P.report("expected object"); + return false; +} + struct TextDocumentIdentifier { /// The text document's URI. URIForFile uri; @@ -1448,6 +1465,37 @@ struct PrepareRenameResult { }; llvm::json::Value toJSON(const PrepareRenameResult &PRR); +/// Rename all occurrences of a symbol named `oldName` to `newName` at the +/// given `positions`. +/// +/// The use case of this method is for when the positions to rename are already +/// known, eg. from an index lookup outside of clangd's built-in index. In +/// particular, it determines the edits necessary to rename multi-piece +/// Objective-C selector names. +/// +/// `textDocument` is used to determine the language options for the symbol to +/// rename, eg. to decide whether `oldName` and `newName` are Objective-C +/// selectors or normal identifiers. +/// +/// This is a clangd extension. +struct IndexedRenameParams { + /// The document in which the declaration to rename is declared. Its compiler + /// arguments are used to infer language settings for the rename. + TextDocumentIdentifier textDocument; + + /// The old name of the symbol. + std::string oldName; + + /// The new name of the symbol. + std::string newName; + + /// The positions at which the symbol is known to appear and that should be + /// renamed. + std::map> positions; +}; +bool fromJSON(const llvm::json::Value &, IndexedRenameParams &, + llvm::json::Path); + enum class DocumentHighlightKind { Text = 1, Read = 2, Write = 3 }; /// A document highlight is a range inside a text document which deserves diff --git a/clang-tools-extra/clangd/Quality.cpp b/clang-tools-extra/clangd/Quality.cpp index c1ab63fb22f61..c5ab63e78073b 100644 --- a/clang-tools-extra/clangd/Quality.cpp +++ b/clang-tools-extra/clangd/Quality.cpp @@ -146,6 +146,7 @@ categorize(const index::SymbolInfo &D) { case index::SymbolKind::Using: case index::SymbolKind::Module: case index::SymbolKind::Unknown: + case index::SymbolKind::CommentTag: return SymbolQualitySignals::Unknown; } llvm_unreachable("Unknown index::SymbolKind"); diff --git a/clang-tools-extra/clangd/ScanningProjectModules.cpp b/clang-tools-extra/clangd/ScanningProjectModules.cpp index e561513fe687f..93cabfa7eb0d3 100644 --- a/clang-tools-extra/clangd/ScanningProjectModules.cpp +++ b/clang-tools-extra/clangd/ScanningProjectModules.cpp @@ -37,7 +37,8 @@ class ModuleDependencyScanner { const ThreadsafeFS &TFS) : CDB(CDB), TFS(TFS), Service(tooling::dependencies::ScanningMode::CanonicalPreprocessing, - tooling::dependencies::ScanningOutputFormat::P1689) {} + tooling::dependencies::ScanningOutputFormat::P1689, + CASOptions(), nullptr, nullptr, nullptr) {} /// The scanned modules dependency information for a specific source file. struct ModuleDependencyInfo { diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp index e464f1ad45c52..ce357362094f6 100644 --- a/clang-tools-extra/clangd/refactor/Rename.cpp +++ b/clang-tools-extra/clangd/refactor/Rename.cpp @@ -42,6 +42,8 @@ namespace clang { namespace clangd { namespace { +using tooling::SymbolName; + std::optional filePath(const SymbolLocation &Loc, llvm::StringRef HintFilePath) { if (!Loc) @@ -1074,6 +1076,47 @@ bool operator<(const SymbolRange &LHS, const SymbolRange &RHS) { return LHS.range() < RHS.range(); } +llvm::Expected +editsForLocations(const llvm::StringMap> &Ranges, + const SymbolName &OldName, const SymbolName &NewName, + llvm::vfs::FileSystem &FS, const LangOptions &LangOpts) { + FileEdits Results; + for (auto &FileAndOccurrences : Ranges) { + llvm::StringRef FilePath = FileAndOccurrences.first(); + + auto ExpBuffer = FS.getBufferForFile(FilePath); + if (!ExpBuffer) { + elog("Fail to read file content: Fail to open file {0}: {1}", FilePath, + ExpBuffer.getError().message()); + continue; + } + std::string RenameIdentifier = OldName.getNamePieces()[0]; + llvm::SmallVector NewNames(NewName.getNamePieces()); + + auto AffectedFileCode = (*ExpBuffer)->getBuffer(); + auto OldNameRenameSymbolName = RenameSymbolName(OldName.getNamePieces()); + auto RenameRanges = + adjustRenameRanges(AffectedFileCode, OldNameRenameSymbolName, + std::move(FileAndOccurrences.second), LangOpts); + if (!RenameRanges) { + // Our heuristics fails to adjust rename ranges to the current state of + // the file, it is most likely the index is stale, so we give up the + // entire rename. + return error("Index results don't match the content of file {0} " + "(the index may be stale)", + FilePath); + } + auto RenameEdit = + buildRenameEdit(FilePath, AffectedFileCode, *RenameRanges, NewNames); + if (!RenameEdit) + return error("failed to rename in file {0}: {1}", FilePath, + RenameEdit.takeError()); + if (!RenameEdit->Replacements.empty()) + Results.insert({FilePath, std::move(*RenameEdit)}); + } + return Results; +} + llvm::Expected rename(const RenameInputs &RInputs) { assert(!RInputs.Index == !RInputs.FS && "Index and FS must either both be specified or both null."); diff --git a/clang-tools-extra/clangd/refactor/Rename.h b/clang-tools-extra/clangd/refactor/Rename.h index ed18a3959668f..6f26f0137c82e 100644 --- a/clang-tools-extra/clangd/refactor/Rename.h +++ b/clang-tools-extra/clangd/refactor/Rename.h @@ -13,6 +13,7 @@ #include "SourceCode.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LangOptions.h" +#include "clang/Tooling/Refactoring/Rename/SymbolName.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Error.h" #include @@ -95,6 +96,22 @@ struct RenameInputs { RenameOptions Opts = {}; }; +/// Compute the edits that need to be applied to rename symbols in `Ranges` from +/// `OldName` to `NewName`. The key of `Ranges` is the file path of the file in +/// which the range resides. +/// +/// If `OldName` and `NewName` are single-piece identifiers, this just creates +/// edits to change the ranges to `NewName`. +/// +/// If `OldName` and `NewName` are multi-piece Objective-C selectors, only the +/// start of the ranges is considered and the file is lexed to find the argument +/// labels of the selector to rename. +llvm::Expected +editsForLocations(const llvm::StringMap> &Ranges, + const tooling::SymbolName &OldName, + const tooling::SymbolName &NewName, llvm::vfs::FileSystem &FS, + const LangOptions &LangOpts); + struct RenameResult { // The range of the symbol that the user can attempt to rename. Range Target; diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp b/clang-tools-extra/clangd/unittests/RenameTests.cpp index 2cb0722f7f285..c73ca3aa68b7a 100644 --- a/clang-tools-extra/clangd/unittests/RenameTests.cpp +++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp @@ -1542,6 +1542,7 @@ TEST(RenameTest, PrepareRename) { /*NewName=*/std::nullopt, {}); // Verify that for multi-file rename, we only return main-file occurrences. ASSERT_TRUE(bool(Results)) << Results.takeError(); + ASSERT_EQ(Results->Placeholder, "func"); // We don't know the result is complete in prepareRename (passing a nullptr // index internally), so GlobalChanges should be empty. EXPECT_TRUE(Results->GlobalChanges.empty()); @@ -1573,6 +1574,38 @@ TEST(RenameTest, PrepareRename) { } } +TEST(RenameTest, PrepareRenameObjC) { + Annotations Input(R"cpp( + @interface Foo + - (int)performA^ction:(int)action w^ith:(int)value; + @end + @implementation Foo + - (int)performA^ction:(int)action w^ith:(int)value { + return [self ^performAction^:action ^w^ith^:value]; + } + @end + )cpp"); + std::string Path = testPath("foo.m"); + MockFS FS; + FS.Files[Path] = std::string(Input.code()); + + auto ServerOpts = ClangdServer::optsForTest(); + ServerOpts.BuildDynamicSymbolIndex = true; + + trace::TestTracer Tracer; + MockCompilationDatabase CDB; + CDB.ExtraClangFlags = {"-xobjective-c"}; + ClangdServer Server(CDB, FS, ServerOpts); + runAddDocument(Server, Path, Input.code()); + + for (Position Point : Input.points()) { + auto Results = runPrepareRename(Server, Path, Point, + /*NewName=*/std::nullopt, {}); + ASSERT_TRUE(bool(Results)) << Results.takeError(); + ASSERT_EQ(Results->Placeholder, "performAction:with:"); + } +} + TEST(CrossFileRenameTests, DirtyBuffer) { Annotations FooCode("class [[Foo]] {};"); std::string FooPath = testPath("foo.cc"); @@ -2091,8 +2124,9 @@ TEST(CrossFileRenameTests, ObjC) { } @end )cpp", - }}; - + } + }; + trace::TestTracer Tracer; for (const auto &T : Cases) { SCOPED_TRACE(T.FooH); @@ -2468,6 +2502,130 @@ TEST(CrossFileRenameTests, adjustmentCost) { } } +static URIForFile uriForPath(StringRef Path) { + URI Uri = llvm::cantFail(URI::parse(("file://" + Path).str())); + return llvm::cantFail(URIForFile::fromURI(Uri, /*HintPath=*/"")); +} + +TEST(IndexedRename, IndexedRename) { + MockCompilationDatabase CDB; + CDB.ExtraClangFlags = {"-xobjective-c"}; + // rename is runnning on all "^" points in FooH, and "[[]]" ranges are the + // expected rename occurrences. + struct Case { + llvm::StringRef FooH; + llvm::StringRef FooM; + llvm::StringRef OldName; + llvm::StringRef NewName; + llvm::StringRef ExpectedFooH; + llvm::StringRef ExpectedFooM; + }; + Case Cases[] = { + { + // Input + R"cpp( + void ^foo(); + )cpp", + R"cpp( + void ^foo() { + return ^foo(); + } + )cpp", + // Old name + "foo", + // New name + "bar", + // Expected + R"cpp( + void bar(); + )cpp", + R"cpp( + void bar() { + return bar(); + } + )cpp", + }, + { + // Input + R"cpp( + @interface Foo + - (int)^performAction:(int)action with:(int)value; + @end + )cpp", + R"cpp( + @implementation Foo + - (int)^performAction:(int)action with:(int)value { + [self ^performAction:action with:value]; + } + @end + )cpp", + // Old name + "performAction:with:", + // New name + "performNewAction:by:", + // Expected + R"cpp( + @interface Foo + - (int)performNewAction:(int)action by:(int)value; + @end + )cpp", + R"cpp( + @implementation Foo + - (int)performNewAction:(int)action by:(int)value { + [self performNewAction:action by:value]; + } + @end + )cpp", + } + }; + trace::TestTracer Tracer; + for (const auto &T : Cases) { + SCOPED_TRACE(T.FooH); + Annotations FooH(T.FooH); + Annotations FooM(T.FooM); + std::string FooHPath = testPath("foo.h"); + std::string FooMPath = testPath("foo.m"); + + MockFS FS; + FS.Files[FooHPath] = std::string(FooH.code()); + FS.Files[FooMPath] = std::string(FooM.code()); + + auto ServerOpts = ClangdServer::optsForTest(); + ClangdServer Server(CDB, FS, ServerOpts); + + std::map> Positions; + Positions[uriForPath(FooHPath)] = FooH.points(); + Positions[uriForPath(FooMPath)] = FooM.points(); + FileEdits Edits = llvm::cantFail( + runIndexedRename(Server, Positions, FooHPath, T.OldName, T.NewName)); + + EXPECT_THAT(applyEdits(std::move(Edits)), + UnorderedElementsAre(Pair(Eq(FooHPath), Eq(T.ExpectedFooH)), + Pair(Eq(FooMPath), Eq(T.ExpectedFooM)))); + } +} + +TEST(IndexedRename, IndexedRenameDoesntCrashIfNoCompilerCommandsExistForFile) { + Annotations FooM(R"cpp( + void ^foo(); + )cpp"); + std::string Path = testPath("foo.swift"); + + MockFS FS; + FS.Files[Path] = std::string(FooM.code()); + + auto ServerOpts = ClangdServer::optsForTest(); + MockCompilationDatabase CDB; + ClangdServer Server(CDB, FS, ServerOpts); + + std::map> Positions; + Positions[uriForPath(Path)] = FooM.points(); + llvm::Expected Edits = + runIndexedRename(Server, Positions, Path, "cFunc", "dFunc"); + EXPECT_FALSE((bool)Edits); + consumeError(Edits.takeError()); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.cpp b/clang-tools-extra/clangd/unittests/SyncAPI.cpp index d48622eba5378..fe92958780b30 100644 --- a/clang-tools-extra/clangd/unittests/SyncAPI.cpp +++ b/clang-tools-extra/clangd/unittests/SyncAPI.cpp @@ -106,6 +106,15 @@ llvm::Expected runRename(ClangdServer &Server, PathRef File, return std::move(*Result); } +llvm::Expected runIndexedRename( + ClangdServer &Server, std::map> Positions, + PathRef PrimaryFile, llvm::StringRef OldName, llvm::StringRef NewName) { + std::optional> Result; + Server.indexedRename(Positions, PrimaryFile, OldName, NewName, + capture(Result)); + return std::move(*Result); +} + llvm::Expected runPrepareRename(ClangdServer &Server, PathRef File, Position Pos, std::optional NewName, diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.h b/clang-tools-extra/clangd/unittests/SyncAPI.h index cf3de4f742e84..7a48ad06f511a 100644 --- a/clang-tools-extra/clangd/unittests/SyncAPI.h +++ b/clang-tools-extra/clangd/unittests/SyncAPI.h @@ -47,6 +47,10 @@ llvm::Expected runRename(ClangdServer &Server, PathRef File, Position Pos, StringRef NewName, const clangd::RenameOptions &RenameOpts); +llvm::Expected runIndexedRename( + ClangdServer &Server, std::map> Positions, + PathRef PrimaryFile, llvm::StringRef OldName, llvm::StringRef NewName); + llvm::Expected runPrepareRename(ClangdServer &Server, PathRef File, Position Pos, std::optional NewName, diff --git a/clang/CONTRIBUTING.md b/clang/CONTRIBUTING.md new file mode 100644 index 0000000000000..a0c1644fc66a2 --- /dev/null +++ b/clang/CONTRIBUTING.md @@ -0,0 +1,14 @@ +By submitting a pull request, you represent that you have the right to license +your contribution to Apple and the community, and agree by submitting the patch +that your contributions are licensed under the [Swift +license](https://swift.org/LICENSE.txt). + +--- + +Changes to this repository follow special considerations as described on +Swift.org under "[LLVM and Swift](https://swift.org/contributing/#llvm-and-swift)". +Please make sure your change is appropriate for this repository. + +Before submitting a pull request, please make sure you have tested your +changes and that they follow the Swift project [guidelines for contributing +code](https://swift.org/contributing/#contributing-code). diff --git a/clang/cmake/caches/Apple-stage2.cmake b/clang/cmake/caches/Apple-stage2.cmake index e919c56739679..2e1ccec785aa2 100644 --- a/clang/cmake/caches/Apple-stage2.cmake +++ b/clang/cmake/caches/Apple-stage2.cmake @@ -56,6 +56,8 @@ set(LLVM_TOOLCHAIN_TOOLS llvm-size llvm-cxxfilt llvm-config + llvm-cas + llvm-cas-dump CACHE STRING "") set(LLVM_BUILD_UTILS ON CACHE BOOL "") @@ -73,6 +75,7 @@ set(LLVM_DISTRIBUTION_COMPONENTS clang-format clang-resource-headers Remarks + clang-features-file ${LLVM_TOOLCHAIN_TOOLS} ${LLVM_TOOLCHAIN_UTILITIES} CACHE STRING "") diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst index 41818d43ac687..1c587fbc2ff35 100644 --- a/clang/docs/PointerAuthentication.rst +++ b/clang/docs/PointerAuthentication.rst @@ -47,14 +47,13 @@ This document serves four purposes: - It documents several language extensions that are useful on targets using pointer authentication. -- It will eventually present a theory of operation for the security mitigation, - describing the basic requirements for correctness, various weaknesses in the - mechanism, and ways in which programmers can strengthen its protections - (including recommendations for language implementors). +- It presents a theory of operation for the security mitigation, describing the + basic requirements for correctness, various weaknesses in the mechanism, and + ways in which programmers can strengthen its protections (including + recommendations for language implementors). -- It will eventually document the language ABIs currently used for C, C++, - Objective-C, and Swift on arm64e, although these are not yet stable on any - target. +- It documents the language ABIs currently used for C, C++, Objective-C, and + Swift on arm64e, although these are not yet stable on any target. Basic Concepts -------------- @@ -125,7 +124,7 @@ independently for I and D keys.) interfaces or as primitives in a compiler IR because they expose raw pointers. Raw pointers require special attention in the language implementation to avoid the accidental creation of exploitable code - sequences. + sequences; see the section on `Attackable code sequences`_. The following details are all implementation-defined: @@ -255,19 +254,133 @@ signing schema breaks down even more simply: It is important that the signing schema be independently derived at all signing and authentication sites. Preferably, the schema should be hard-coded everywhere it is needed, but at the very least, it must not be derived by -inspecting information stored along with the pointer. +inspecting information stored along with the pointer. See the section on +`Attacks on pointer authentication`_ for more information. + + + + Language Features ----------------- -There is currently one main pointer authentication language feature: +There are three levels of the pointer authentication language feature: + +- The language implementation automatically signs and authenticates function + pointers (and certain data pointers) across a variety of standard situations, + including return addresses, function pointers, and C++ virtual functions. The + intent is for all pointers to code in program memory to be signed in some way + and for all branches to code in program text to authenticate those + signatures. -- The language provides the ```` intrinsic interface for manually - signing and authenticating pointers in code. These can be used in +- The language also provides extensions to override the default rules used by + the language implementation. For example, the ``__ptrauth`` type qualifier + can be used to change how pointers are signed when they are stored in + a particular variable or field; this provides much stronger protection than + is guaranteed by the default rules for C function and data pointers. + +- Finally, the language provides the ```` intrinsic interface for + manually signing and authenticating pointers in code. These can be used in circumstances where very specific behavior is required. +Language implementation +~~~~~~~~~~~~~~~~~~~~~~~ + +For the most part, pointer authentication is an unobserved detail of the +implementation of the programming language. Any element of the language +implementation that would perform an indirect branch to a pointer is implicitly +altered so that the pointer is signed when first constructed and authenticated +when the branch is performed. This includes: + +- indirect-call features in the programming language, such as C function + pointers, C++ virtual functions, C++ member function pointers, the "blocks" + C extension, and so on; + +- returning from a function, no matter how it is called; and -Language Extensions +- indirect calls introduced by the implementation, such as branches through the + global offset table (GOT) used to implement direct calls to functions defined + outside of the current shared object. + +For more information about this, see the `Language ABI`_ section. + +However, some aspects of the implementation are observable by the programmer or +otherwise require special notice. + +C data pointers +^^^^^^^^^^^^^^^ + +The current implementation in Clang does not sign pointers to ordinary data by +default. For a partial explanation of the reasoning behind this, see the +`Theory of Operation`_ section. + +A specific data pointer which is more security-sensitive than most can be +signed using the `__ptrauth qualifier`_ or using the ```` +intrinsics. + +C function pointers +^^^^^^^^^^^^^^^^^^^ + +The C standard imposes restrictions on the representation and semantics of +function pointer types which make it difficult to achieve satisfactory +signature diversity in the default language rules. See `Attacks on pointer +authentication`_ for more information about signature diversity. Programmers +should strongly consider using the ``__ptrauth`` qualifier to improve the +protections for important function pointers, such as the components of of +a hand-rolled "v-table"; see the section on the `__ptrauth qualifier`_ for +details. + +The value of a pointer to a C function includes a signature, even when the +value is cast to a non-function-pointer type like ``void*`` or ``intptr_t``. On +implementations that use high bits to store the signature, this means that +relational comparisons and hashes will vary according to the exact signature +value, which is likely to change between executions of a program. In some +implementations, it may also vary based on the exact function pointer type. + +Null pointers +^^^^^^^^^^^^^ + +In principle, an implementation could derive the signed null pointer value +simply by applying the standard signing algorithm to the raw null pointer +value. However, for likely signing algorithms, this would mean that the signed +null pointer value would no longer be statically known, which would have many +negative consequences. For one, it would become substantially more expensive +to emit null pointer values or to perform null-pointer checks. For another, +the pervasive (even if technically unportable) assumption that null pointers +are bitwise zero would be invalidated, making it substantially more difficult +to adopt pointer authentication, as well as weakening common optimizations for +zero-initialized memory such as the use of ``.bzz`` sections. Therefore it is +beneficial to treat null pointers specially by giving them their usual +representation. On AArch64, this requires additional code when working with +possibly-null pointers, such as when copying a pointer field that has been +signed with address diversity. + +Return addresses and frame pointers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The current implementation in Clang implicitly signs both return addresses and +frame pointers. While these values are technically implementation details of +a function, there are some important libraries and development tools which rely +on manually walking the chain of stack frames. These tools must be updated to +correctly account for pointer authentication, either by stripping signatures +(if security is not important for the tool, e.g. if it is capturing a stack +trace during a crash) or properly authenticating them. More information about +how these values are signed is available in the `Language ABI`_ section. + +C++ virtual functions +^^^^^^^^^^^^^^^^^^^^^ + +The current implementation in Clang signs virtual function pointers with +a discriminator derived from the full signature of the overridden method, +including the method name and parameter types. It is possible to write C++ +code that relies on v-table layout remaining constant despite changes to +a method signature; for example, a parameter might be a ``typedef`` that +resolves to a different type based on a build setting. Such code violates +C++'s One Definition Rule (ODR), but that violation is not normally detected; +however, pointer authentication will detect it. + + +Language extensions ~~~~~~~~~~~~~~~~~~~ Feature Testing @@ -280,6 +393,16 @@ a number of different tests. normal interface. This may be true even on targets where pointer authentication is not enabled by default. +- ``__has_feature(ptrauth_returns)`` is true if the target uses pointer + authentication to protect return addresses. + +- ``__has_feature(ptrauth_calls)`` is true if the target uses pointer + authentication to protect indirect branches. This implies + ``__has_feature(ptrauth_returns)`` and ``__has_feature(ptrauth_intrinsics)``. + +Clang provides several other tests only for historical purposes; for current +purposes they are all equivalent to ``ptrauth_calls``. + __ptrauth Qualifier ^^^^^^^^^^^^^^^^^^^ @@ -292,6 +415,11 @@ type, either to a function or to an object. It currently cannot be an Objective-C pointer type, a C++ reference type, or a block pointer type; these restrictions may be lifted in the future. +The current implementation in Clang is known to not provide adequate safety +guarantees against the creation of `signing oracles`_ when assigning data +pointers to ``__ptrauth``-qualified gl-values. See the section on `safe +derivation`_ for more information. + The qualifier's operands are as follows: - ``key`` - an expression evaluating to a key value from ````; must @@ -326,6 +454,54 @@ a discriminator determined as follows: is ``ptrauth_blend_discriminator(&x, discriminator)``; see `ptrauth_blend_discriminator`_. +Non-triviality from address diversity ++++++++++++++++++++++++++++++++++++++ + +Address diversity must impose additional restrictions in order to allow the +implementation to correctly copy values. In C++, a type qualified with address +diversity is treated like a class type with non-trivial copy/move constructors +and assignment operators, with the usual effect on containing classes and +unions. C does not have a standard concept of non-triviality, and so we must +describe the basic rules here, with the intention of imitating the emergent +rules of C++: + +- A type may be **non-trivial to copy**. + +- A type may also be **illegal to copy**. Types that are illegal to copy are + always non-trivial to copy. + +- A type may also be **address-sensitive**. + +- A type qualified with a ``ptrauth`` qualifier that requires address diversity + is non-trivial to copy and address-sensitive. + +- An array type is illegal to copy, non-trivial to copy, or address-sensitive + if its element type is illegal to copy, non-trivial to copy, or + address-sensitive, respectively. + +- A struct type is illegal to copy, non-trivial to copy, or address-sensitive + if it has a field whose type is illegal to copy, non-trivial to copy, or + address-sensitive, respectively. + +- A union type is both illegal and non-trivial to copy if it has a field whose + type is non-trivial or illegal to copy. + +- A union type is address-sensitive if it has a field whose type is + address-sensitive. + +- A program is ill-formed if it uses a type that is illegal to copy as + a function parameter, argument, or return type. + +- A program is ill-formed if an expression requires a type to be copied that is + illegal to copy. + +- Otherwise, copying a type that is non-trivial to copy correctly copies its + subobjects. + +- Types that are address-sensitive must always be passed and returned + indirectly. Thus, changing the address-sensitivity of a type may be + ABI-breaking even if its size and alignment do not change. + ```` ~~~~~~~~~~~~~~~ @@ -432,7 +608,7 @@ Produce a signed pointer for the given raw pointer without applying any authentication or extra treatment. This operation is not required to have the same behavior on a null pointer that the language implementation would. -This is a treacherous operation that can easily result in signing oracles. +This is a treacherous operation that can easily result in `signing oracles`_. Programs should use it seldom and carefully. ``ptrauth_auth_and_resign`` @@ -453,7 +629,29 @@ a null pointer that the language implementation would. The code sequence produced for this operation must not be directly attackable. However, if the discriminator values are not constant integers, their computations may still be attackable. In the future, Clang should be enhanced -to guaranteed non-attackability if these expressions are safely-derived. +to guaranteed non-attackability if these expressions are +:ref:`safely-derived`. + +``ptrauth_auth_function`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: c + + ptrauth_auth_function(pointer, key, discriminator) + +Authenticate that ``pointer`` is signed with ``key`` and ``discriminator`` and +re-sign it to the standard schema for a function pointer of its type. + +``pointer`` must have function pointer type. The result will have the same +type as ``pointer``. This operation is not required to have the same behavior +on a null pointer that the language implementation would. + +This operation makes the same attackability guarantees as +``ptrauth_auth_and_resign``. + +If this operation appears syntactically as the function operand of a call, +Clang guarantees that the call will directly authenticate the function value +using the given schema rather than re-signing to the standard schema. ``ptrauth_auth_data`` ^^^^^^^^^^^^^^^^^^^^^ @@ -469,7 +667,7 @@ remove the signature. as ``pointer``. This operation is not required to have the same behavior on a null pointer that the language implementation would. -In the future when Clang makes safe derivation guarantees, the result of +In the future when Clang makes `safe derivation`_ guarantees, the result of this operation should be considered safely-derived. ``ptrauth_sign_generic_data`` @@ -499,6 +697,466 @@ type. Implementations are not required to make all bits of the result equally significant; in particular, some implementations are known to not leave meaningful data in the low bits. +Standard ``__ptrauth`` qualifiers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +```` additionally provides several macros which expand to +``__ptrauth`` qualifiers for common ABI situations. + +For convenience, these macros expand to nothing when pointer authentication is +disabled. + +These macros can be found in the header; some details of these macros may be +unstable or implementation-specific. + + + + + +Theory of Operation +------------------- + +The threat model of pointer authentication is as follows: + +- The attacker has the ability to read and write to a certain range of addresses, possibly the entire address space. However, they are constrained by the normal rules of the process: for example, they cannot write to memory that is mapped read-only, and if they access unmapped memory it will trigger a trap. + +- The attacker has no ability to add arbitrary executable code to the program. For example, the program does not include malicious code to begin with, and the attacker cannot alter existing instructions, load a malicious shared library, or remap writable pages as executable. If the attacker wants to get the process to perform a specific sequence of actions, they must somehow subvert the normal control flow of the process. + +In both of the above paragraphs, it is merely assumed that the attacker's *current* capabilities are restricted; that is, their current exploit does not directly give them the power to do these things. The attacker's immediate goal may well be to leverage their exploit to gain these capabilities, e.g. to load a malicious dynamic library into the process, even though the process does not directly contain code to do so. + +Note that any bug that fits the above threat model can be immediately exploited as a denial-of-service attack by simply performing an illegal access and crashing the program. Pointer authentication cannot protect against this. While denial-of-service attacks are unfortunate, they are also unquestionably the best possible result of a bug this severe. Therefore, pointer authentication enthusiastically embraces the idea of halting the program on a pointer authentication failure rather than continuing in a possibly-compromised state. + +Pointer authentication is a form of control-flow integrity (CFI) enforcement. The basic security hypothesis behind CFI enforcement is that many bugs can only be usefully exploited (other than as a denial-of-service) by leveraging them to subvert the control flow of the program. If this is true, then by inhibiting or limiting that subversion, it may be possible to largely mitigate the security consequences of those bugs by rendering them impractical (or, ideally, impossible) to exploit. + +Every indirect branch in a program has a purpose. Using human intelligence, a programmer can describe where a particular branch *should* go according to this purpose: a ``return`` in ``printf`` should return to the call site, a particular call in ``qsort`` should call the comparator that was passed in as an argument, and so on. But for CFI to enforce that every branch in a program goes where it *should* in this sense would require CFI to perfectly enforce every semantic rule of the program's abstract machine; that is, it would require making the programming environment perfectly sound. That is out of scope. Instead, the goal of CFI is merely to catch attempts to make a branch go somewhere that its obviously *shouldn't* for its purpose: for example, to stop a call from branching into the middle of a function rather than its beginning. As the information available to CFI gets better about the purpose of the branch, CFI can enforce tighter and tighter restrictions on where the branch is permitted to go. Still, ultimately CFI cannot make the program sound. This may help explain why pointer authentication makes some of the choices it does: for example, to sign and authenticate mostly code pointers rather than every pointer in the program. Preventing attackers from redirecting branches is both particularly important and particularly approachable as a goal. Detecting corruption more broadly is infeasible with these techniques, and the attempt would have far higher cost. + +Attacks on pointer authentication +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pointer authentication works as follows. Every indirect branch in a program has a purpose. For every purpose, the implementation chooses a :ref:`signing schema`. At some place where a pointer is known to be correct for its purpose, it is signed according to the purpose's schema. At every place where the pointer is needed for its purpose, it is authenticated according to the purpose's schema. If that authentication fails, the program is halted. + +There are a variety of ways to attack this. + +Attacks of interest to programmers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These attacks arise from weaknesses in the default protections offered by pointer authentication. They can be addressed by using attributes or intrinsics to opt in to stronger protection. + +Substitution attacks +++++++++++++++++++++ + +An attacker can simply overwrite a pointer intended for one purpose with a pointer intended for another purpose if both purposes use the same signing schema and that schema does not use address diversity. + +The most common source of this weakness is when code relies on using the default language rules for C function pointers. The current implementation uses the exact same signing schema for all C function pointers, even for functions of substantially different type. While efforts are ongoing to improve constant diversity for C function pointers of different type, there are necessary limits to this. The C standard requires function pointers to be copyable with ``memcpy``, which means that function pointers can never use address diversity. Furthermore, even if a function pointer can only be replaced with another function of the exact same type, that can still be useful to an attacker, as in the following example of a hand-rolled "v-table": + +.. code-block:: c + + struct ObjectOperations { + void (*retain)(Object *); + void (*release)(Object *); + void (*deallocate)(Object *); + void (*logStatus)(Object *); + }; + +This weakness can be mitigated by using a more specific signing schema for each purpose. For example, in this example, the ``__ptrauth`` qualifier can be used with a different constant discriminator for each field. Since there's no particular reason it's important for this v-table to be copyable with ``memcpy``, the functions can also be signed with address diversity: + +.. code-block:: c + + #if __has_feature(ptrauth_calls) + #define objectOperation(discriminator) \ + __ptrauth(ptrauth_key_function_pointer, 1, discriminator) + #else + #define objectOperation(discriminator) + #endif + + struct ObjectOperations { + void (*objectOperation(0xf017) retain)(Object *); + void (*objectOperation(0x2639) release)(Object *); + void (*objectOperation(0x8bb0) deallocate)(Object *); + void (*objectOperation(0xc5d4) logStatus)(Object *); + }; + +This weakness can also sometimes be mitigated by simply keeping the signed pointer in constant memory, but this is less effective than using better signing diversity. + +.. _Access path attacks: + +Access path attacks ++++++++++++++++++++ + +If a signed pointer is often accessed indirectly (that is, by first loading the address of the object where the signed pointer is stored), an attacker can affect uses of it by overwriting the intermediate pointer in the access path. + +The most common scenario exhibiting this weakness is an object with a pointer to a "v-table" (a structure holding many function pointers). An attacker does not need to replace a signed function pointer in the v-table if they can instead simply replace the v-table pointer in the object with their own pointer --- perhaps to memory where they've constructed their own v-table, or to existing memory that coincidentally happens to contain a signed pointer at the right offset that's been signed with the right signing schema. + +This attack arises because data pointers are not signed by default. It works even if the signed pointer uses address diversity: address diversity merely means that each pointer is signed with its own storage address, which (by design) is invariant to changes in the accessing pointer. + +Using sufficiently diverse signing schemas within the v-table can provide reasonably strong mitigation against this weakness. Always use address diversity in v-tables to prevent attackers from assembling their own v-table. Avoid re-using constant discriminators to prevent attackers from replacing a v-table pointer with a pointer to totally unrelated memory that just happens to contain an similarly-signed pointer. + +Further mitigation can be attained by signing pointers to v-tables. Any signature at all should prevent attackers from forging v-table pointers; they will need to somehow harvest an existing signed pointer from elsewhere in memory. Using a meaningful constant discriminator will force this to be harvested from an object with similar structure (e.g. a different implementation of the same interface). Using address diversity will prevent such harvesting entirely. However, care must be taken when sourcing the v-table pointer originally; do not blindly sign a pointer that is not :ref:`safely derived`. + +.. _Signing oracles: + +Signing oracles ++++++++++++++++ + +A signing oracle is a bit of code which can be exploited by an attacker to sign an arbitrary pointer in a way that can later be recovered. Such oracles can be used by attackers to forge signatures matching the oracle's signing schema, which is likely to cause a total compromise of pointer authentication's effectiveness. + +This attack only affects ordinary programmers if they are using certain treacherous patterns of code. Currently this includes: + +- all uses of the ``__ptrauth_sign_unauthenticated`` intrinsic and +- assigning data pointers to ``__ptrauth``-qualified l-values. + +Care must be taken in these situations to ensure that the pointer being signed has been :ref:`safely derived` or is otherwise not possible to attack. (In some cases, this may be challenging without compiler support.) + +A diagnostic will be added in the future for implicitly dangerous patterns of code, such as assigning a non-safely-derived data pointer to a ``__ptrauth``-qualified l-value. + +.. _Authentication oracles: + +Authentication oracles +++++++++++++++++++++++ + +An authentication oracle is a bit of code which can be exploited by an attacker to leak whether a signed pointer is validly signed without halting the program if it isn't. Such oracles can be used to forge signatures matching the oracle's signing schema if the attacker can repeatedly invoke the oracle for different candidate signed pointers. This is likely to cause a total compromise of pointer authentication's effectiveness. + +There should be no way for an ordinary programmer to create an authentication oracle using the current set of operations. However, implementation flaws in the past have occasionally given rise to authentication oracles due to a failure to immediately trap on authentication failure. + +The likelihood of creating an authentication oracle is why there is currently no intrinsic which queries whether a signed pointer is validly signed. + + +Attacks of interest to implementors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These attacks are not inherent to the model; they arise from mistakes in either implementing or using the `sign` and `auth` operations. Avoiding these mistakes requires careful work throughout the system. + +Failure to trap on authentication failure ++++++++++++++++++++++++++++++++++++++++++ + +Any failure to halt the program on an authentication failure is likely to be exploitable by attackers to create an :ref:`authentication oracle`. + +There are several different ways to introduce this problem: + +- The implementation might try to halt the program in some way that can be intercepted. + + For example, the ``auth`` instruction in ARMv8.3 does not directly trap; instead it corrupts its result so that it is always an invalid pointer. If the program subsequently attempts to use that pointer, that will be a bad memory access, and it will trap into the kernel. However, kernels do not usually immediately halt programs that trigger traps due to bad memory accesses; instead they notify the process to give it an opportunity to recover. If this happens with an ``auth`` failure, the attacker may be able to exploit the recovery path in a way that creates an oracle. Kernels should ensure that these sorts of traps are not recoverable. + +- A compiler might use an intermediate representation (IR) for ``sign`` and ``auth`` operations that cannot make adequate correctness guarantees. + + For example, suppose that an IR uses ARMv8.3-like semantics for ``auth``: the operation merely corrupts its result on failure instead of promising the trap. A frontend might emit patterns of IR that always follow an ``auth`` with a memory access, thinking that this ensures correctness. But if the IR can be transformed to insert code between the ``auth`` and the access, or if the ``auth`` can be speculated, then this potentially creates an oracle. It is better for ``auth`` to semantically guarantee to trap, potentially requiring an explicit check in the generated code. An ARMv8.3-like target can avoid this explicit check in the common case by recognizing the pattern of an ``auth`` followed immediately by an access. + +Attackable code sequences ++++++++++++++++++++++++++ + +If code that is part of a pointer authentication operation is interleaved with code that may itself be vulnerable to attacks, an attacker may be able to use this to create a :ref:`signing` or :ref:`authentication` oracle. + +For example, suppose that the compiler is generating a call to a function and passing two arguments: a signed constant pointer and a value derived from a call. In ARMv8.3, this code might look like so: + +.. code-block:: asm + + adr x19, _callback. ; compute &_callback + paciza x19 ; sign it with a constant discriminator of 0 + blr _argGenerator ; call _argGenerator() (returns in x0) + mov x1, x0 ; move call result to second arg register + mov x0, x19 ; move signed &_callback to first arg register + blr _function ; call _function + +This code is correct, as would be a sequencing that does *both* the ``adr`` and the ``paciza`` after the call to ``_argGenerator``. But a sequence that computes the address of ``_callback`` but leaves it as a raw pointer in a register during the call to ``_argGenerator`` would be vulnerable: + +.. code-block:: asm + + adr x19, _callback. ; compute &_callback + blr _argGenerator ; call _argGenerator() (returns in x0) + mov x1, x0 ; move call result to second arg register + paciza x19 ; sign &_callback + mov x0, x19 ; move signed &_callback to first arg register + blr _function ; call _function + +If ``_argGenerator`` spills ``x19`` (a callee-save register), and if the attacker can perform a write during this call, then the attacker can overwrite the spill slot with an arbitrary pointer that will eventually be unconditionally signed after the function returns. This would be a signing oracle. + +The implementation can avoid this by obeying two basic rules: + +- The compiler's intermediate representations (IR) should not provide operations that expose intermediate raw pointers. This may require providing extra operations that perform useful combinations of operations. + + For example, there should be an "atomic" auth-and-resign operation that should be used instead of emitting an ``auth`` operation whose result is fed into a ``sign``. + + Similarly, if a pointer should be authenticated as part of doing a memory access or a call, then the access or call should be decorated with enough information to perform the authentication; there should not be a separate ``auth`` whose result is used as the pointer operand for the access or call. (In LLVM IR, we do this for calls, but not yet for loads or stores.) + + "Operations" includes things like materializing a signed pointer to a known function or global variable. The compiler must be able to recognize and emit this as a unified operation, rather than potentially splitting it up as in the example above. + +- The compiler backend should not be too aggressive about scheduling instructions that are part of a pointer authentication operation. This may require custom code-generation of these operations in some cases. + +Register clobbering ++++++++++++++++++++ + +As a refinement of the section on `Attackable code sequences`_, if the attacker has the ability to modify arbitrary *register* state at arbitrary points in the program, then special care must be taken. + +For example, ARMv8.3 might materialize a signed function pointer like so: + +.. code-block:: asm + + adr x0, _callback. ; compute &_callback + paciza x0 ; sign it with a constant discriminator of 0 + +If an attacker has the ability to overwrite ``x0`` between these two instructions, this code sequence is vulnerable to becoming a signing oracle. + +For the most part, this sort of attack is not possible: it is a basic element of the design of modern computation that register state is private and inviolable. However, in systems that support asynchronous interrupts, this property requires the cooperation of the interrupt-handling code. If that code saves register state to memory, and that memory can be overwritten by an attacker, then essentially the attack can overwrite arbitrary register state at an arbitrary point. This could be a concern if the threat model includes attacks on the kernel or if the program uses user-space preemptive multitasking. + +(Readers might object that an attacker cannot rely on asynchronous interrupts triggering at an exact instruction boundary. In fact, researchers have had some success in doing exactly that. Even ignoring that, though, we should aim to protect against lucky attackers just as much as good ones.) + +To protect against this, saved register state must be at least partially signed (using something like `ptrauth_sign_generic_data`_). This is required for correctness anyway because saved thread states include security-critical registers such as SP, FP, PC, and LR (where applicable). Ideally, this signature would cover all the registers, but since saving and restoring registers can be very performance-sensitive, that may not be acceptable. It is sufficient to set aside a small number of scratch registers that will be guaranteed to be preserved correctly; the compiler can then be careful to only store critical values like intermediate raw pointers in those registers. + +``setjmp`` and ``longjmp`` should sign and authenticate the core registers (SP, FP, PC, and LR), but they do not need to worry about intermediate values because ``setjmp`` can only be called synchronously, and the compiler should never schedule pointer-authentication operations interleaved with arbitrary calls. + +.. _Relative addresses: + +Attacks on relative addressing +++++++++++++++++++++++++++++++ + +Relative addressing is a technique used to compress and reduce the load-time cost of infrequently-used global data. The pointer authentication system is unlikely to support signing or authenticating a relative address, and in most cases it would defeat the point to do so: it would take additional storage space, and applying the signature would take extra work at load time. + +Relative addressing is not precluded by the use of pointer authentication, but it does take extra considerations to make it secure: + +- Relative addresses must only be stored in read-only memory. A writable relative address can be overwritten to point nearly anywhere, making it inherently insecure; this danger can only be compensated for with techniques for protecting arbitrary data like `ptrauth_sign_generic_data`_. + +- Relative addresses must only be accessed through signed pointers with adequate diversity. If an attacker can perform an `access path attack` to replace the pointer through which the relative address is accessed, they can easily cause the relative address to point wherever they want. + +Signature forging ++++++++++++++++++ + +If an attacker can exactly reproduce the behavior of the signing algorithm, and they know all the correct inputs to it, then they can perfectly forge a signature on an arbitrary pointer. + +There are three components to avoiding this mistake: + +- The abstract signing algorithm should be good: it should not have glaring flaws which would allow attackers to predict its result with better than random accuracy without knowing all the inputs (like the key values). + +- The key values should be kept secret. If at all possible, they should never be stored in accessible memory, or perhaps only stored encrypted. + +- Contexts that are meant to be independently protected should use different key values. For example, the kernel should not use the same keys as user processes. Different user processes should also use different keys from each other as much as possible, although this may pose its own technical challenges. + +Remapping ++++++++++ + +If an attacker can change the memory protections on certain pages of the program's memory, that can substantially weaken the protections afforded by pointer authentication. + +- If an attacker can inject their own executable code, they can also certainly inject code that can be used as a :ref:`signing oracle`. The same is true if they can write to the instruction stream. + +- If an attacker can remap read-only program sections to be writable, then any use of :ref:`relative addresses` in global data becomes insecure. + +- If an attacker can remap read-only program sections to be writable, then it is unsafe to use unsigned pointers in `global offset tables`_. + +Remapping memory in this way often requires the attacker to have already substantively subverted the control flow of the process. Nonetheless, if the operating system has a mechanism for mapping pages in a way that cannot be remapped, this should be used wherever possible. + + + +.. _Safe Derivation: + +Safe derivation +~~~~~~~~~~~~~~~ + +Whether a data pointer is stored, even briefly, as a raw pointer can affect the security-correctness of a program. (Function pointers are never implicitly stored as raw pointers; raw pointers to functions can only be produced with the ```` intrinsics.) Repeated re-signing can also impact performance. Clang makes a modest set of guarantees in this area: + +- An expression of pointer type is said to be **safely derived** if: + + - it takes the address of a global variable or function, or + + - it is a load from a gl-value of ``__ptrauth``-qualified type. + +- If a value that is safely derived is assigned to a ``__ptrauth``-qualified object, including by initialization, then the value will be directly signed as appropriate for the target qualifier and will not be stored as a raw pointer. + +- If the function expression of a call is a gl-value of ``__ptrauth``-qualified type, then the call will be authenticated directly according to the source qualifier and will not be resigned to the default rule for a function pointer of its type. + +These guarantees are known to be inadequate for data pointer security. In particular, Clang should be enhanced to make the following guarantees: + +- A pointer should additionally be considered safely derived if it is: + + - the address of a gl-value that is safely derived, + + - the result of pointer arithmetic on a pointer that is safely derived (with some restrictions on the integer operand), + + - the result of a comma operator where the second operand is safely derived, + + - the result of a conditional operator where the selected operand is safely derived, or + + - the result of loading from a safely derived gl-value. + +- A gl-value should be considered safely derived if it is: + + - a dereference of a safely derived pointer, + + - a member access into a safely derived gl-value, or + + - a reference to a variable. + +- An access to a safely derived gl-value should be guaranteed to not allow replacement of any of the safely-derived component values at any point in the access. "Access" should include loading a function pointer. + +- Assignments should include pointer-arithmetic operators like ``+=``. + +Making these guarantees will require further work, including significant new support in LLVM IR. + +Furthermore, Clang should implement a warning when assigning a data pointer that is not safely derived to a ``__ptrauth``-qualified gl-value. + + + +Language ABI +------------ + +This section describes the pointer-authentication ABI currently implemented in Clang for the Apple arm64e target. As other targets adopt pointer authentication, this section should be generalized to express their ABIs as well. + +Key assignments +~~~~~~~~~~~~~~~ + +ARMv8.3 provides four abstract signing keys: ``IA``, ``IB``, ``DA``, and ``DB``. The architecture designates ``IA`` and ``IB`` for signing code pointers and ``DA`` and ``DB`` for signing data pointers; this is reinforced by two properties: + +- The ISA provides instructions that perform combined auth+call and auth+load operations; these instructions can only use the ``I`` keys and ``D`` keys, respectively. + +- AArch64's TBI feature can be separately enabled for code pointers (controlling whether indirect-branch instructions ignore those bits) and data pointers (controlling whether memory-access instructions) ignore those bits. If TBI is enabled for a kind of pointer, the sign and auth operations preserve the TBI bits when signing with an associated keys (at the cost of shrinking the number of available signing bits by 8). + +arm64e then further subdivides the keys as follows: + +- The ``A`` keys are used for primarily "global" purposes like signing v-tables and function pointers. These keys are sometimes called *process-independent* or *cross-process* because on existing OSes they are not changed when changing processes, although this is not a platform guarantee. + +- The ``B`` keys are used for primarily "local" purposes like signing return addresses and frame pointers. These keys are sometimes called *process-specific* because they are typically different between processes. However, they are in fact shared across processes in one situation: systems which provide ``fork`` cannot change these keys in the child process; they can only be changed during ``exec``. + +Implementation-defined algorithms and quantities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The cryptographic hash algorithm used to compute signatures in ARMv8.3 is a private detail of the hardware implementation. + +arm64e restricts constant discriminators (used in ``__ptrauth`` and ``ptrauth_blend_discriminator``) to the range from 0 to 65535, inclusive. A 0 discriminator generally signifies that no blending is required; see the documentation for ``ptrauth_blend_discriminator``. This range is somewhat narrow but has two advantages: + +- The AArch64 ISA allows an arbitrary 16-bit immediate to be written over the top 16 bits of a register in a single instruction: + + .. code-block:: asm + + movk xN, #0x4849, LSL 48 + + This is ideal for the discriminator blending operation because it adds minimal code-size overhead and avoids overwriting any interesting bits from the pointer. Blending in a wider constant discriminator would either clobber interesting bits (e.g. if it was loaded with ``movk xN, #0x4c4f, LSL 32``) or require significantly more code (e.g. if the discriminator was loaded with a ``mov+bfi`` sequence). + +- It is possible to pack a 16-bit discriminator into loader metadata with minimal compromises, whereas a wider discriminator would require extra metadata storage and therefore significantly impact load times. + +The string hash used by ``ptrauth_string_discriminator`` is a 64-bit SipHash-2-4 using the constant seed ``b5d4c9eb79104a796fec8b1b428781d4`` (big-endian), with the result reduced by modulo to the range of non-zero discriminators (i.e. ``(rawHash % 65535) + 1``). + +Return addresses +~~~~~~~~~~~~~~~~ + +The kernel must ensure that attackers cannot replace LR due to an asynchronous exception; see `Register clobbering`_. If this is done by generally protecting LR, then functions which don't spill LR to the stack can avoid signing it entirely. Otherwise, the return address must be signed; on arm64e it is signed with the ``IB`` key using the stack pointer on entry as the discriminator. + +Protecting return addresses is of such particular importance that the ``IB`` key is almost entirely reserved for this purpose. + +Global offset tables +~~~~~~~~~~~~~~~~~~~~ + +The global offset table (GOT) is not ABI, but it is a common implementation technique for dynamic linking which deserves special discussion here. + +Whenever possible, signed pointers should be materialized directly in code rather than via the GOT, e.g. using an ``adrp+add+pac`` sequence on ARMv8.3. This decreases the amount of work necessary at load time to initialize the GOT, but more importantly, it defines away the potential for several attacks: + +- Attackers cannot change instructions, so there is no way to cause this code sequence to materialize a different pointer, whereas an access via the GOT always has *at minimum* a probabilistic chance to be the target of successful `substitution attacks`_. + +- The GOT is a dense pool of fixed pointers at a fixed offset relative to code; attackers can search this pool for useful pointers that can be used in `substitution attacks`_, whereas pointers that are only materialized directly are not so easily available. + +- Similarly, attackers can use `access path attacks`_ to replace a pointer to a signed pointer with a pointer to the GOT if the signing schema used within the GOT happens to be the same as the original pointer. This kind of collision becomes much less likely to be useful the fewer pointers are in the GOT in the first place. + +If this can be done for a symbol, then the compiler need only ensure that it materializes the signed pointer using registers that are safe against `register clobbering`_. + +However, many symbols can only be accessed via the GOT, e.g. because they resolve to definitions outside of the current image. In this case, care must be taken to ensure that using the GOT does not introduce weaknesses. + +- If the entire GOT can be mapped read-only after loading, then no signing is required within the GOT. In fact, not signing pointers in the GOT is preferable in this case because it makes the GOT useless for the harvesting and access-path attacks above. Storing raw pointers in this way is usually extremely unsafe, but for the special case of an immutable GOT entry it's fine because the GOT is always accessed via an address that is directly materialized in code and thus provably unattackable. (But see `Remapping`_.) + +- Otherwise, GOT entries which are used for producing a signed pointer constant must be signed. The signing schema used in the GOT need not match the target signing schema for the signed constant. To counteract the threats of substitution attacks, it's best if GOT entries can be signed with address diversity. Using a good constant discriminator as well (perhaps derived from the symbol name) can make it less useful to use a pointer to the GOT as the replacement in an :ref:`access path attack`. + +In either case, the compiler must ensure that materializing the address of a GOT entry as part of producing a signed pointer constant is not vulnerable to `register clobbering`_. If the linker also generates code for this, e.g. for call stubs, this generated code must take the same precautions. + +C function pointers +~~~~~~~~~~~~~~~~~~~ + +On arm64e, C function pointers are currently signed with the ``IA`` key without address diversity and with a constant discriminator of 0. + +The C and C++ standards do not permit C function pointers to be signed with address diversity by default: in C++ terms, function pointer types are required to be trivially copyable, which means they must be copyable with ``memcpy``. + +The use of a uniform constant discriminator is seen as a serious defect which should be remedied, and improving this is under investigation. + +C++ virtual tables +~~~~~~~~~~~~~~~~~~ + +The pointer to a C++ virtual table is currently signed with the ``DA`` key, no address diversity, and a constant discriminator of 0. The use of no address diversity, as well as the uniform constant discriminator, are seen as weaknesses. Not using address diversity allows attackers to simply copy valid v-table pointers from one object to another. However, using a uniform discriminator of 0 does have positive performance and code-size implications on ARMv8.3, and diversity for the most important v-table access pattern (virtual dispatch) is already better assured by the signing schemas used on the virtual functions. It is also known that some code in practice copies objects containing v-tables with ``memcpy``, and while this is not permitted formally, it is something that may be invasive to eliminate. + +Virtual functions in a C++ virtual table are signed with the ``IA`` key, address diversity, and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangled name of the function which originally gave rise to the v-table slot. + +C++ member function pointers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A member function pointer is signed with the ``IA`` key, no address diversity, and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the member pointer type. Address diversity is not permitted by C++ for member function pointers because they must be trivially-copyable types. + +The Itanium C++ ABI specifies that member function pointers to virtual functions simply store an offset to the correct v-table slot. This ABI cannot be used securely with pointer authentication because there is no safe place to store the constant discriminator for the target v-table slot: if it's stored with the offset, an attacker can simply overwrite it with the right discriminator for the offset. Even if the programmer never uses pointers to virtual functions, the existence of this code path makes all member function pointer dereferences insecure. + +arm64e changes this ABI so that virtual function pointers are stored using dispatch thunks with vague linkage. Because arm64e supports interoperation with ``arm64`` code when pointer authentication is disabled, an arm64e member function pointer dereference still recognizes the virtual-function representation but uses an bogus discriminator on that path that should always trap if pointer authentication is enabled dynamically. + +The use of dispatch thunks means that ``==`` on member function pointers is no longer reliable for virtual functions, but this is acceptable because the standard makes no guarantees about it in the first place. + +The use of dispatch thunks also potentially enables v-tables to be signed using a declaration-specific constant discriminator in the future; otherwise this discriminator would also need to be stored in the member pointer. + +Blocks +~~~~~~ + +Block pointers are data pointers which must interoperate with the ObjC `id` type and therefore cannot be signed themselves. + +The invocation pointer in a block is signed with the ``IA`` key using address diversity and a constant dicriminator of 0. Using a uniform discriminator is seen as a weakness to be potentially improved, but this is tricky due to the subtype polymorphism directly permitted for blocks. + +Block descriptors and ``__block`` variables can contain pointers to functions that can be used to copy or destroy the object. These functions are signed with the ``IA`` key, address diversity, and a constant discriminator of 0. The structure of block descriptors is under consideration for improvement. + +Objective-C methods +~~~~~~~~~~~~~~~~~~~ + +Objective-C method lists sign methods with the ``IA`` key using address diversity and a constant discriminator of 0. Using a uniform constant discriminator is believed to be acceptable because these tables are only accessed internally to the Objective-C runtime. + +The Objective-C runtime provides additional protection to methods that have been loaded into the Objective-C method cache; this protection is private to the runtime. + +Pointer authentication cannot protect against access-path atacks against the Objective-C ``isa`` pointer, through which all dispatch occurs, because of compatibility requirements and existing and important usage of high bits in the pointer. + +Swift class methods +~~~~~~~~~~~~~~~~~~~ + +Class methods in Swift are signed in the class object with the ``IA`` key using address diversity and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangling of the original overridable method. + +Resilient class-method lookup relies on passing a method descriptor; this method descriptor should be signed but currently isn't. The lookup function returns a function pointer that is signed using ``IA`` without address diversity and with the correct constant discriminator for the looked-up method. + +Swift's equivalent of a C++ v-table pointer is the ``isa`` pointer of an object. On arm64e, this is constrained by Objective-C compatibility and cannot be a signed pointer. + +Swift heap destructors +~~~~~~~~~~~~~~~~~~~~~~ + +Objects that are retained and released with Swift's native reference-counting system, including both native classes and temporary "box" allocations, must provide a destructor function in their metadata. This destructor function is signed with the ``IA`` key using address diversity and a constant discriminator of ``0xbbbf``. + +Swift protocol requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Protocol function requirements are signed in the protocol witness table with the ``IA`` key using address diversity and a constant discriminator equal to the string hash (see `ptrauth_string_discriminator`_) of the mangling of the protocol requirement. + +Swift function types +~~~~~~~~~~~~~~~~~~~~ + +The invocation pointers of Swift function values are signed using the ``IA`` key without address diversity and with a constant discriminator derived loosely from the function type. + +Address diversity cannot be used by default for function values because function types are intended to be a "loadable" type which can be held and passed in registers. + +The constant discriminator currently accounts for potential abstraction in the function signature in ways that decrease the diversity of signatures; improving this is under investigation. + +Swift metadata +~~~~~~~~~~~~~~ + +Type metadata pointers in Swift are not signed. + +Type context descriptors must be signed because they frequently contain `relative addresses`_. Type context descriptors are signed with the ``DA`` key without address diversity (except when stored in type metadata) and with a constant discriminator of ``0xae86``. + +Swift value witnesses +~~~~~~~~~~~~~~~~~~~~~ + +Value witness functions in Swift are signed in the value witness table using the ``IA`` key with address diversity and an operation-specific constant discriminator which can be found in the Swift project headers. + +Swift coroutines +~~~~~~~~~~~~~~~~ + +Resumption functions for Swift coroutines are signed using the ``IA`` key without address diversity and with a constant discriminator derived from the yield type of the coroutine. Resumption functions cannot be signed with address diversity as they are returned directly in registers from the coroutine. + + + Alternative Implementations diff --git a/clang/include/clang-c/CAS.h b/clang/include/clang-c/CAS.h new file mode 100644 index 0000000000000..a9aecbc6c51f6 --- /dev/null +++ b/clang/include/clang-c/CAS.h @@ -0,0 +1,390 @@ +/*==-- clang-c/CAS.h - CAS Interface ------------------------------*- C -*-===*\ +|* *| +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| +|* Exceptions. *| +|* See https://llvm.org/LICENSE.txt for license information. *| +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This header provides interfaces for creating and working with CAS and *| +|* ActionCache interfaces. *| +|* *| +|* An example of its usage is available in c-index-test/core_main.cpp. *| +|* *| +|* EXPERIMENTAL: These interfaces are experimental and will change. If you *| +|* use these be prepared for them to change without notice on any commit. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_C_CAS_H +#define LLVM_CLANG_C_CAS_H + +#include "clang-c/CXErrorCode.h" +#include "clang-c/CXString.h" +#include "clang-c/Platform.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup CAS CAS and ActionCache interface. + * @{ + */ + +/** + * Configuration options for ObjectStore and ActionCache. + */ +typedef struct CXOpaqueCASOptions *CXCASOptions; + +/** + * Encapsulates instances of ObjectStore and ActionCache, created from a + * particular configuration of \p CXCASOptions. + */ +typedef struct CXOpaqueCASDatabases *CXCASDatabases; + +/** + * Content-addressable storage for objects. + */ +typedef struct CXOpaqueCASObjectStore *CXCASObjectStore; + +/** + * A cache from a key describing an action to the result of doing it. + */ +typedef struct CXOpaqueCASActionCache *CXCASActionCache; + +typedef struct CXOpaqueCASObject *CXCASObject; + +/** + * Result of \c clang_experimental_cas_getCachedCompilation. + */ +typedef struct CXOpaqueCASCachedCompilation *CXCASCachedCompilation; + +/** + * Result of \c clang_experimental_cas_replayCompilation. + */ +typedef struct CXOpaqueCASReplayResult *CXCASReplayResult; + +/** + * Used for cancelling asynchronous actions. + */ +typedef struct CXOpaqueCASCancellationToken *CXCASCancellationToken; + +/** + * Create a \c CXCASOptions object. + */ +CINDEX_LINKAGE CXCASOptions clang_experimental_cas_Options_create(void); + +/** + * Dispose of a \c CXCASOptions object. + */ +CINDEX_LINKAGE void clang_experimental_cas_Options_dispose(CXCASOptions); + +/** + * Configure the file path to use for on-disk CAS/cache instances. + */ +CINDEX_LINKAGE void +clang_experimental_cas_Options_setOnDiskPath(CXCASOptions, const char *Path); + +/** + * Configure the path to a library that implements the LLVM CAS plugin API. + */ +CINDEX_LINKAGE void +clang_experimental_cas_Options_setPluginPath(CXCASOptions, const char *Path); + +/** + * Set a value for a named option that the CAS plugin supports. + */ +CINDEX_LINKAGE void +clang_experimental_cas_Options_setPluginOption(CXCASOptions, const char *Name, + const char *Value); + +/** + * Creates instances for a CAS object store and action cache based on the + * configuration of a \p CXCASOptions. + * + * \param Opts configuration options. + * \param[out] Error The error string to pass back to client (if any). + * + * \returns The resulting instances object, or null if there was an error. + */ +CINDEX_LINKAGE CXCASDatabases +clang_experimental_cas_Databases_create(CXCASOptions Opts, CXString *Error); + +/** + * Dispose of a \c CXCASDatabases object. + */ +CINDEX_LINKAGE void clang_experimental_cas_Databases_dispose(CXCASDatabases); + +/** + * Get the local storage size of the CAS/cache data in bytes. + * + * \param[out] OutError The error object to pass back to client (if any). + * If non-null the object must be disposed using \c clang_Error_dispose. + * \returns the local storage size of the CAS/cache data, or -1 if the + * implementation does not support reporting such size, or -2 if an error + * occurred. + */ +CINDEX_LINKAGE int64_t clang_experimental_cas_Databases_get_storage_size( + CXCASDatabases, CXError *OutError); + +/** + * Set the size for limiting disk storage growth. + * + * \param size_limit the maximum size limit in bytes. 0 means no limit. Negative + * values are invalid. + * \returns an error object if there was an error, NULL otherwise. + * If non-null the object must be disposed using \c clang_Error_dispose. + */ +CINDEX_LINKAGE CXError clang_experimental_cas_Databases_set_size_limit( + CXCASDatabases, int64_t size_limit); + +/** + * Prune local storage to reduce its size according to the desired size limit. + * Pruning can happen concurrently with other operations. + * + * \returns an error object if there was an error, NULL otherwise. + * If non-null the object must be disposed using \c clang_Error_dispose. + */ +CINDEX_LINKAGE +CXError clang_experimental_cas_Databases_prune_ondisk_data(CXCASDatabases); + +/** + * Checks whether an object is materialized in the database using its printed + * \p CASID. + * + * \param CASID The printed CASID string for the object. + * \param[out] OutError The error object to pass back to client (if any). + * If non-null the object must be disposed using \c clang_Error_dispose. + * + * \returns whether the object is materialized in the database. + */ +CINDEX_LINKAGE bool clang_experimental_cas_isMaterialized(CXCASDatabases, + const char *CASID, + CXError *OutError); + +/** + * Loads an object using its printed \p CASID. + * + * \param CASID The printed CASID string for the object. + * \param[out] OutError The error object to pass back to client (if any). + * If non-null the object must be disposed using \c clang_Error_dispose. + * + * \returns The resulting object, or null if the object was not found or an + * error occurred. The object should be disposed using + * \c clang_experimental_cas_CASObject_dispose. + */ +CINDEX_LINKAGE CXCASObject clang_experimental_cas_loadObjectByString( + CXCASDatabases, const char *CASID, CXError *OutError); + +/** + * Asynchronous version of \c clang_experimental_cas_loadObjectByString. + * + * \param CASID The printed CASID string for the object. + * \param Ctx opaque value to pass to the callback. + * \param Callback receives a \c CXCASObject, or \c CXError if an error occurred + * or both NULL if the object was not found or the call was cancelled. + * The objects should be disposed with + * \c clang_experimental_cas_CASObject_dispose or \c clang_Error_dispose. + * \param[out] OutToken if non-null receives a \c CXCASCancellationToken that + * can be used to cancel the call using + * \c clang_experimental_cas_CancellationToken_cancel. The object should be + * disposed using \c clang_experimental_cas_CancellationToken_dispose. + */ +CINDEX_LINKAGE void clang_experimental_cas_loadObjectByString_async( + CXCASDatabases, const char *CASID, void *Ctx, + void (*Callback)(void *Ctx, CXCASObject, CXError), + CXCASCancellationToken *OutToken); + +/** + * Dispose of a \c CXCASObject object. + */ +CINDEX_LINKAGE void clang_experimental_cas_CASObject_dispose(CXCASObject); + +/** + * Looks up a cache key and returns the associated set of compilation output IDs + * + * \param CacheKey The printed compilation cache key string. + * \param Globally if true it is a hint to the underlying CAS implementation + * that the lookup is profitable to be done on a distributed caching level, not + * just locally. + * \param[out] OutError The error object to pass back to client (if any). + * If non-null the object must be disposed using \c clang_Error_dispose. + * + * \returns The resulting object, or null if the cache key was not found or an + * error occurred. The object should be disposed using + * \c clang_experimental_cas_CachedCompilation_dispose. + */ +CINDEX_LINKAGE CXCASCachedCompilation +clang_experimental_cas_getCachedCompilation(CXCASDatabases, + const char *CacheKey, bool Globally, + CXError *OutError); + +/** + * Asynchronous version of \c clang_experimental_cas_getCachedCompilation. + * + * \param CacheKey The printed compilation cache key string. + * \param Globally if true it is a hint to the underlying CAS implementation + * that the lookup is profitable to be done on a distributed caching level, not + * just locally. + * \param Ctx opaque value to pass to the callback. + * \param Callback receives a \c CXCASCachedCompilation, or \c CXError if an + * error occurred or both NULL if the object was not found or the call was + * cancelled. The objects should be disposed with + * \c clang_experimental_cas_CachedCompilation_dispose or \c clang_Error_dispose + * \param[out] OutToken if non-null receives a \c CXCASCancellationToken that + * can be used to cancel the call using + * \c clang_experimental_cas_CancellationToken_cancel. The object should be + * disposed using \c clang_experimental_cas_CancellationToken_dispose. + */ +CINDEX_LINKAGE void clang_experimental_cas_getCachedCompilation_async( + CXCASDatabases, const char *CacheKey, bool Globally, void *Ctx, + void (*Callback)(void *Ctx, CXCASCachedCompilation, CXError), + CXCASCancellationToken *OutToken); + +/** + * Dispose of a \c CXCASCachedCompilation object. + */ +CINDEX_LINKAGE void + clang_experimental_cas_CachedCompilation_dispose(CXCASCachedCompilation); + +/** + * \returns number of compilation outputs. + */ +CINDEX_LINKAGE size_t clang_experimental_cas_CachedCompilation_getNumOutputs( + CXCASCachedCompilation); + +/** + * \returns the compilation output name given the index via \p OutputIdx. + */ +CINDEX_LINKAGE CXString clang_experimental_cas_CachedCompilation_getOutputName( + CXCASCachedCompilation, size_t OutputIdx); + +/** + * \returns the compilation output printed CASID given the index via + * \p OutputIdx. + */ +CINDEX_LINKAGE CXString +clang_experimental_cas_CachedCompilation_getOutputCASIDString( + CXCASCachedCompilation, size_t OutputIdx); + +/** + * \returns whether the compilation output data exist in the local CAS given the + * index via \p OutputIdx. + */ +CINDEX_LINKAGE bool +clang_experimental_cas_CachedCompilation_isOutputMaterialized( + CXCASCachedCompilation, size_t OutputIdx); + +/** + * If distributed caching is available it uploads the compilation outputs and + * the association of key <-> outputs to the distributed cache. + * This allows separating the task of computing the compilation outputs and + * storing them in the local cache, from the task of "uploading" them. + * + * \param Ctx opaque value to pass to the callback. + * \param Callback receives a \c CXError if an error occurred. The error will be + * NULL if the call was successful or cancelled. The error should be disposed + * via \c clang_Error_dispose. + * \param[out] OutToken if non-null receives a \c CXCASCancellationToken that + * can be used to cancel the call using + * \c clang_experimental_cas_CancellationToken_cancel. The object should be + * disposed using \c clang_experimental_cas_CancellationToken_dispose. + */ +CINDEX_LINKAGE void clang_experimental_cas_CachedCompilation_makeGlobal( + CXCASCachedCompilation, void *Ctx, void (*Callback)(void *Ctx, CXError), + CXCASCancellationToken *OutToken); + +/** + * Replays a cached compilation by writing the cached outputs to the filesystem + * and/or stderr based on the given compilation arguments. + * + * \param argc number of compilation arguments. + * \param argv array of compilation arguments. + * \param WorkingDirectory working directory to use, can be NULL. + * \param reserved for future use, caller must pass NULL. + * \param[out] OutError The error object to pass back to client (if any). + * If non-null the object must be disposed using \c clang_Error_dispose. + * + * \returns a \c CXCASReplayResult object or NULL if an error occurred or a + * compilation output was not found in the CAS. The object should be disposed + * via \c clang_experimental_cas_ReplayResult_dispose. + */ +CINDEX_LINKAGE CXCASReplayResult clang_experimental_cas_replayCompilation( + CXCASCachedCompilation, int argc, const char *const *argv, + const char *WorkingDirectory, void *reserved, CXError *OutError); + +/** + * Dispose of a \c CXCASReplayResult object. + */ +CINDEX_LINKAGE void + clang_experimental_cas_ReplayResult_dispose(CXCASReplayResult); + +/** + * Get the diagnostic text of a replayed cached compilation. + */ +CINDEX_LINKAGE +CXString clang_experimental_cas_ReplayResult_getStderr(CXCASReplayResult); + +/** + * Cancel an asynchronous CAS-related action. + */ +CINDEX_LINKAGE void + clang_experimental_cas_CancellationToken_cancel(CXCASCancellationToken); + +/** + * Dispose of a \c CXCASCancellationToken object. + */ +CINDEX_LINKAGE void + clang_experimental_cas_CancellationToken_dispose(CXCASCancellationToken); + +/** + * Dispose of a \c CXCASObjectStore object. + */ +CINDEX_LINKAGE void +clang_experimental_cas_ObjectStore_dispose(CXCASObjectStore CAS); + +/** + * Dispose of a \c CXCASActionCache object. + */ +CINDEX_LINKAGE void +clang_experimental_cas_ActionCache_dispose(CXCASActionCache Cache); + +/** + * Gets or creates a persistent on-disk CAS object store at \p Path. + * Deprecated, use \p clang_experimental_cas_Databases_create() instead. + * + * \param Path The path to locate the object store. + * \param[out] Error The error string to pass back to client (if any). + * + * \returns The resulting object store, or null if there was an error. + */ +CINDEX_DEPRECATED CINDEX_LINKAGE CXCASObjectStore +clang_experimental_cas_OnDiskObjectStore_create(const char *Path, + CXString *Error); + +/** + * Gets or creates a persistent on-disk action cache at \p Path. + * Deprecated, use \p clang_experimental_cas_Databases_create() instead. + * + * \param Path The path to locate the object store. + * \param[out] Error The error string to pass back to client (if any). + * + * \returns The resulting object store, or null if there was an error. + */ +CINDEX_DEPRECATED CINDEX_LINKAGE CXCASActionCache +clang_experimental_cas_OnDiskActionCache_create(const char *Path, + CXString *Error); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif // LLVM_CLANG_C_CAS_H diff --git a/clang/include/clang-c/CXDiagnostic.h b/clang/include/clang-c/CXDiagnostic.h index 911d001f06697..faa7e816de47e 100644 --- a/clang/include/clang-c/CXDiagnostic.h +++ b/clang/include/clang-c/CXDiagnostic.h @@ -148,6 +148,30 @@ CINDEX_LINKAGE void clang_disposeDiagnosticSet(CXDiagnosticSet Diags); */ CINDEX_LINKAGE CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic D); +/** + * Get the contents if the given file that was provided via diagnostics. + * + * \param diags the diagnostics set to query for the contents of the file. + * \param file the file to get the contents of. + * \param outFileSize if non-null, set to the file size on success. + * \returns on success, a pointer to the file contents. Otherwise, NULL. + */ +CINDEX_LINKAGE const char *clang_getDiagnosticFileContents( + CXDiagnosticSet diags, CXFile file, size_t *outFileSize); + +/** + * Retrieve the original source range if the given file was provided via + * diagnostics and is conceptually a replacement for the original source range. + * + * \param diags the diagnostics set to query for the contents of the file. + * \param file the file to get the contents of. + * \returns on success, the source range (into another file) that is + * conceptually replaced by the contents of the given file (available via + * \c clang_getDiagnosticFileContents). + */ +CINDEX_LINKAGE CXSourceRange clang_getDiagnosticFileOriginalSourceRange( + CXDiagnosticSet diags, CXFile file); + /** * Destroy a diagnostic. */ @@ -314,6 +338,14 @@ clang_getDiagnosticCategoryName(unsigned Category); */ CINDEX_LINKAGE CXString clang_getDiagnosticCategoryText(CXDiagnostic); +/** + * Retrieve the diagnostic category's URL for a given diagnostic. + * + * \returns The URL that provides additional documentation for the + * category of this diagnostic. + */ +CINDEX_LINKAGE CXString clang_getDiagnosticCategoryURL(CXDiagnostic); + /** * Determine the number of source ranges associated with the given * diagnostic. diff --git a/clang/include/clang-c/CXErrorCode.h b/clang/include/clang-c/CXErrorCode.h index b3a0b9d66d5f8..e284b2843a7ff 100644 --- a/clang/include/clang-c/CXErrorCode.h +++ b/clang/include/clang-c/CXErrorCode.h @@ -53,9 +53,47 @@ enum CXErrorCode { /** * An AST deserialization error has occurred. */ - CXError_ASTReadError = 4 + CXError_ASTReadError = 4, + + /** + * \brief A refactoring action is not available at the given location + * or in the given source range. + */ + CXError_RefactoringActionUnavailable = 5, + + /** + * \brief A refactoring action is not able to use the given name because + * it contains an unexpected number of strings. + */ + CXError_RefactoringNameSizeMismatch = 6, + + /** + * \brief A name of a symbol is invalid, i.e. it is reserved by the source + * language and can't be used as a name for this symbol. + */ + CXError_RefactoringNameInvalid = 7 }; +/** + * Represents an error with error code and description string. + */ +typedef struct CXOpaqueError *CXError; + +/** + * \returns the error code. + */ +CINDEX_LINKAGE enum CXErrorCode clang_Error_getCode(CXError); + +/** + * \returns the error description string. + */ +CINDEX_LINKAGE const char *clang_Error_getDescription(CXError); + +/** + * Dispose of a \c CXError object. + */ +CINDEX_LINKAGE void clang_Error_dispose(CXError); + LLVM_CLANG_C_EXTERN_C_END #endif diff --git a/clang/include/clang-c/CXString.h b/clang/include/clang-c/CXString.h index 63dce4d140ce2..47909540537c4 100644 --- a/clang/include/clang-c/CXString.h +++ b/clang/include/clang-c/CXString.h @@ -16,6 +16,7 @@ #include "clang-c/ExternC.h" #include "clang-c/Platform.h" +#include LLVM_CLANG_C_EXTERN_C_BEGIN @@ -44,6 +45,14 @@ typedef struct { unsigned Count; } CXStringSet; +/** + * An array of C strings. + */ +typedef struct { + const char **Strings; + size_t Count; +} CXCStringArray; + /** * Retrieve the character data associated with the given string. * diff --git a/clang/include/clang-c/Dependencies.h b/clang/include/clang-c/Dependencies.h new file mode 100644 index 0000000000000..fd041344c6fdc --- /dev/null +++ b/clang/include/clang-c/Dependencies.h @@ -0,0 +1,596 @@ +/*==-- clang-c/Dependencies.h - Dependency Discovery C Interface --*- C -*-===*\ +|* *| +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| +|* Exceptions. *| +|* See https://llvm.org/LICENSE.txt for license information. *| +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This header provides a dependency discovery interface similar to *| +|* clang-scan-deps. *| +|* *| +|* An example of its usage is available in c-index-test/core_main.cpp. *| +|* *| +|* EXPERIMENTAL: These interfaces are experimental and will change. If you *| +|* use these be prepared for them to change without notice on any commit. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_C_DEPENDENCIES_H +#define LLVM_CLANG_C_DEPENDENCIES_H + +#include "clang-c/BuildSystem.h" +#include "clang-c/CAS.h" +#include "clang-c/CXDiagnostic.h" +#include "clang-c/CXErrorCode.h" +#include "clang-c/CXString.h" +#include "clang-c/Platform.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup SCAN_DEPS Dependency scanning service. + * @{ + */ + +/** + * An output file kind needed by module dependencies. + */ +typedef enum { + CXOutputKind_ModuleFile = 0, + CXOutputKind_Dependencies = 1, + CXOutputKind_DependenciesTarget = 2, + CXOutputKind_SerializedDiagnostics = 3, +} CXOutputKind; + +/** + * Object encapsulating instance of a dependency scanner service. + * + * The dependency scanner service is a global instance that owns the + * global cache and other global state that's shared between the dependency + * scanner workers. The service APIs are thread safe. + * + * The service aims to provide a consistent view of file content throughout + * its lifetime. A client that wants to see changes to file content should + * create a new service at the time. For example, a build system might use + * one service for each build. + * + * TODO: Consider using DirectoryWatcher to get notified about file changes + * and adding an API that allows clients to invalidate changed files. This + * could allow a build system to reuse a single service between builds. + */ +typedef struct CXOpaqueDependencyScannerService *CXDependencyScannerService; + +/** + * The mode to report module dependencies in. + */ +typedef enum { + /** + * Flatten all module dependencies. This reports the full transitive set of + * header and module map dependencies needed to do an implicit module build. + */ + CXDependencyMode_Flat, + + /** + * Report the full module graph. This reports only the direct dependencies of + * each file, and calls a callback for each module that is discovered. + */ + CXDependencyMode_Full, +} CXDependencyMode; + +/** + * Options used to construct a \c CXDependencyScannerService. + */ +typedef struct CXOpaqueDependencyScannerServiceOptions + *CXDependencyScannerServiceOptions; + +/** + * Creates a default set of service options. + * Must be disposed with \c + * clang_experimental_DependencyScannerServiceOptions_dispose. + */ +CINDEX_LINKAGE CXDependencyScannerServiceOptions +clang_experimental_DependencyScannerServiceOptions_create(void); + +/** + * Dispose of a \c CXDependencyScannerServiceOptions object. + */ +CINDEX_LINKAGE void clang_experimental_DependencyScannerServiceOptions_dispose( + CXDependencyScannerServiceOptions); + +/** + * Specify a \c CXDependencyMode in the given options. + */ +CINDEX_LINKAGE void +clang_experimental_DependencyScannerServiceOptions_setDependencyMode( + CXDependencyScannerServiceOptions Opts, CXDependencyMode Mode); + +/** + * Specify the object store and action cache databases in the given options. + * With this set, the scanner will produce cached commands. + */ +CINDEX_LINKAGE void +clang_experimental_DependencyScannerServiceOptions_setCASDatabases( + CXDependencyScannerServiceOptions Opts, CXCASDatabases); + +/** + * Specify the specific CAS options for the scanner to use for the produced + * compiler arguments. + */ +CINDEX_LINKAGE void +clang_experimental_DependencyScannerServiceOptions_setCASOptions( + CXDependencyScannerServiceOptions Opts, CXCASOptions); + +/** + * Set the working directory optimization option. + * The dependency scanner service option Opts will indicate to the scanner that + * the current working directory can or cannot be ignored when computing the + * pcms' context hashes. The scanner will then determine if it is safe to + * optimize each module and act accordingly. + * + * \param Value If it is non zero, the option is on. Otherwise the + * option is off. + */ +CINDEX_LINKAGE void +clang_experimental_DependencyScannerServiceOptions_setCWDOptimization( + CXDependencyScannerServiceOptions Opts, int Value); + +/** + * Specify a \c CXCASObjectStore in the given options. If an object store and + * action cache are available, the scanner will produce cached commands. + * Deprecated, use + * \p clang_experimental_DependencyScannerServiceOptions_setCASDatabases() + * instead. + */ +CINDEX_DEPRECATED CINDEX_LINKAGE void +clang_experimental_DependencyScannerServiceOptions_setObjectStore( + CXDependencyScannerServiceOptions Opts, CXCASObjectStore CAS); + +/** + * Specify a \c CXCASActionCache in the given options. If an object store and + * action cache are available, the scanner will produce cached commands. + * Deprecated, use + * \p clang_experimental_DependencyScannerServiceOptions_setCASDatabases() + * instead. + */ +CINDEX_DEPRECATED CINDEX_LINKAGE void +clang_experimental_DependencyScannerServiceOptions_setActionCache( + CXDependencyScannerServiceOptions Opts, CXCASActionCache Cache); + +/** + * Create a \c CXDependencyScannerService object. + * Must be disposed with \c + * clang_experimental_DependencyScannerService_dispose_v0(). + */ +CINDEX_LINKAGE CXDependencyScannerService +clang_experimental_DependencyScannerService_create_v1( + CXDependencyScannerServiceOptions Opts); + +/** + * Dispose of a \c CXDependencyScannerService object. + * + * The service object must be disposed of after the workers are disposed of. + */ +CINDEX_LINKAGE void clang_experimental_DependencyScannerService_dispose_v0( + CXDependencyScannerService); + +/** + * Object encapsulating instance of a dependency scanner worker. + * + * The dependency scanner workers are expected to be used in separate worker + * threads. An individual worker is not thread safe. + * + * Operations on a worker are not thread-safe and should only be used from a + * single thread at a time. They are intended to be used by a single dedicated + * thread in a thread pool, but they are not inherently pinned to a thread. + */ +typedef struct CXOpaqueDependencyScannerWorker *CXDependencyScannerWorker; + +/** + * Create a \c CXDependencyScannerWorker object. + * Must be disposed with + * \c clang_experimental_DependencyScannerWorker_dispose_v0(). + */ +CINDEX_LINKAGE CXDependencyScannerWorker + clang_experimental_DependencyScannerWorker_create_v0( + CXDependencyScannerService); + +CINDEX_LINKAGE void clang_experimental_DependencyScannerWorker_dispose_v0( + CXDependencyScannerWorker); + +/** + * A callback that is called to determine the paths of output files for each + * module dependency. The ModuleFile (pcm) path mapping is mandatory. + * + * \param Context the MLOContext that was passed to + * \c clang_experimental_DependencyScannerWorkerScanSettings_create(). + * \param ModuleName the name of the dependent module. + * \param ContextHash the context hash of the dependent module. + * See \c clang_experimental_DepGraphModule_getContextHash(). + * \param OutputKind the kind of module output to lookup. + * \param[out] Output the output path(s) or name, whose total size must be <= + * \p MaxLen. In the case of multiple outputs of the same + * kind, this can be a null-separated list. + * \param MaxLen the maximum size of Output. + * + * \returns the actual length of Output. If the return value is > \p MaxLen, + * the callback will be repeated with a larger buffer. + */ +typedef size_t CXModuleLookupOutputCallback(void *Context, + const char *ModuleName, + const char *ContextHash, + CXOutputKind OutputKind, + char *Output, size_t MaxLen); + +/** + * Output of \c clang_experimental_DependencyScannerWorker_getDepGraph. + */ +typedef struct CXOpaqueDepGraph *CXDepGraph; + +/** + * An individual module dependency that is part of an overall compilation + * \c CXDepGraph. + */ +typedef struct CXOpaqueDepGraphModule *CXDepGraphModule; + +/** + * An individual command-line invocation that is part of an overall compilation + * \c CXDepGraph. + */ +typedef struct CXOpaqueDepGraphTUCommand *CXDepGraphTUCommand; + +/** + * Settings to use for the + * \c clang_experimental_DependencyScannerWorker_getDepGraph action. + */ +typedef struct CXOpaqueDependencyScannerWorkerScanSettings + *CXDependencyScannerWorkerScanSettings; + +/** + * A callback that is called to determine the paths of output files for each + * module dependency. The ModuleFile (pcm) path mapping is mandatory. + * + * \param Context the MLOContext that was passed to + * \c clang_experimental_DependencyScannerWorker_getFileDependencies_vX. + * \param CXDepMod the ModuleDep that represents the dependent module. + * \param OutputKind the kind of module output to lookup. + * \param[out] Output the output path(s) or name, whose total size must be <= + * \p MaxLen. In the case of multiple outputs of the same + * kind, this can be a null-separated list. + * \param MaxLen the maximum size of Output. + * + * \returns the actual length of Output. If the return value is > \p MaxLen, + * the callback will be repeated with a larger buffer. + */ +typedef size_t CXModuleLookupOutputCallback_v2(void *Context, + CXDepGraphModule CXDepMod, + CXOutputKind OutputKind, + char *Output, size_t MaxLen); + +/** + * Creates a set of settings for + * \c clang_experimental_DependencyScannerWorker_getDepGraph action. + * Must be disposed with + * \c clang_experimental_DependencyScannerWorkerScanSettings_dispose. + * Memory for settings is not copied. Any provided pointers must be valid until + * the call to \c clang_experimental_DependencyScannerWorker_getDepGraph. + * + * \param argc the number of compiler invocation arguments (including argv[0]). + * \param argv the compiler driver invocation arguments (including argv[0]). + * \param ModuleName If non-null, the dependencies of the named module are + * returned. Otherwise, the dependencies of the whole + * translation unit are returned. + * \param WorkingDirectory the directory in which the invocation runs. + * \param MLOContext the context that will be passed to \c MLO each time it is + * called. + * \param MLO a callback that is called to determine the paths of output files + * for each module dependency. This may receive the same module on + * different workers. This should be NULL if + * \c clang_experimental_DependencyScannerService_create_v1 was + * called with \c CXDependencyMode_Flat. This callback will be called + * on the same thread that called \c + * clang_experimental_DependencyScannerWorker_getDepGraph. + */ +CINDEX_LINKAGE CXDependencyScannerWorkerScanSettings +clang_experimental_DependencyScannerWorkerScanSettings_create( + int argc, const char *const *argv, const char *ModuleName, + const char *WorkingDirectory, void *MLOContext, + CXModuleLookupOutputCallback *MLO); + +/** + * Override the ModuleLookupOutputCallback with \c + * CXModuleLookupOutputCallback_v2 in the scanner settings for queries of module + * dependencies. This is required for handling output paths of modules that + * depend on attributes encoded in + * \c CXDepGraphModule. + * + * \param Settings object created via + * \c clang_experimental_DependencyScannerWorkerScanSettings_create. + * \param MLO a callback that is called to determine the paths of output files + * for each module dependency. This may receive the same module on + * different workers. + */ +CINDEX_LINKAGE void +clang_experimental_DependencyScannerWorkerScanSettings_setModuleLookupCallback( + CXDependencyScannerWorkerScanSettings, CXModuleLookupOutputCallback_v2 *); + +/** + * Dispose of a \c CXDependencyScannerWorkerScanSettings object. + */ +CINDEX_LINKAGE void + clang_experimental_DependencyScannerWorkerScanSettings_dispose( + CXDependencyScannerWorkerScanSettings); + +/** + * Produces the dependency graph for a particular compiler invocation. + * + * \param Settings object created via + * \c clang_experimental_DependencyScannerWorkerScanSettings_create. + * \param [out] Out A non-NULL pointer to store the resulting dependencies. The + * output must be freed by calling + * \c clang_experimental_DepGraph_dispose. + * + * \returns \c CXError_Success on success; otherwise a non-zero \c CXErrorCode + * indicating the kind of error. When returning \c CXError_Failure there will + * be a \c CXDepGraph object on \p Out that can be used to get diagnostics via + * \c clang_experimental_DepGraph_getDiagnostics. + */ +CINDEX_LINKAGE enum CXErrorCode +clang_experimental_DependencyScannerWorker_getDepGraph( + CXDependencyScannerWorker, CXDependencyScannerWorkerScanSettings Settings, + CXDepGraph *Out); + +/** + * Dispose of a \c CXDepGraph object. + */ +CINDEX_LINKAGE void clang_experimental_DepGraph_dispose(CXDepGraph); + +/** + * \returns the number of \c CXDepGraphModule objects in the graph. + */ +CINDEX_LINKAGE size_t clang_experimental_DepGraph_getNumModules(CXDepGraph); + +/** + * \returns the \c CXDepGraphModule object at the given \p Index. + * + * The \c CXDepGraphModule object is only valid to use while \c CXDepGraph is + * valid. Must be disposed with \c clang_experimental_DepGraphModule_dispose. + */ +CINDEX_LINKAGE CXDepGraphModule +clang_experimental_DepGraph_getModule(CXDepGraph, size_t Index); + +/** + * \returns the \c CXDepGraphModule object at the given \p Index in + * a topologically sorted list. + * + * The \c CXDepGraphModule object is only valid to use while \c CXDepGraph is + * valid. Must be disposed with \c clang_experimental_DepGraphModule_dispose. + */ +CINDEX_LINKAGE CXDepGraphModule +clang_experimental_DepGraph_getModuleTopological(CXDepGraph, size_t Index); + +CINDEX_LINKAGE void clang_experimental_DepGraphModule_dispose(CXDepGraphModule); + +/** + * \returns the name of the module. This may include `:` for C++20 module + * partitions, or a header-name for C++20 header units. + * + * The string is only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE +const char *clang_experimental_DepGraphModule_getName(CXDepGraphModule); + +/** + * \returns the context hash of a module represents the set of compiler options + * that may make one version of a module incompatible from another. This + * includes things like language mode, predefined macros, header search paths, + * etc... + * + * Modules with the same name but a different \c ContextHash should be treated + * as separate modules for the purpose of a build. + * + * The string is only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE +const char *clang_experimental_DepGraphModule_getContextHash(CXDepGraphModule); + +/** + * \returns the path to the modulemap file which defines this module. If there's + * no modulemap (e.g. for a C++ module) returns \c NULL. + * + * This can be used to explicitly build this module. This file will + * additionally appear in \c FileDeps as a dependency. + * + * The string is only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE const char * + clang_experimental_DepGraphModule_getModuleMapPath(CXDepGraphModule); + +/** + * \returns the list of files which this module directly depends on. + * + * If any of these change then the module needs to be rebuilt. + * + * The strings are only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE CXCStringArray + clang_experimental_DepGraphModule_getFileDeps(CXDepGraphModule); + +/** + * \returns the list of modules which this module direct depends on. + * + * This does include the context hash. The format is + * `:` + * + * The strings are only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE CXCStringArray + clang_experimental_DepGraphModule_getModuleDeps(CXDepGraphModule); + +/** + * \returns whether the provided module is comprised of + * inputs that resolve into stable directories. + */ +CINDEX_LINKAGE bool + clang_experimental_DepGraphModule_isInStableDirs(CXDepGraphModule); + +/** + * \returns the canonical command line to build this module. + * + * The strings are only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE CXCStringArray + clang_experimental_DepGraphModule_getBuildArguments(CXDepGraphModule); + +/** + * @returns the CASID of the filesystem root for this module, if any. + * + * The string is only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE const char * + clang_experimental_DepGraphModule_getFileSystemRootID(CXDepGraphModule); + +/** + * @returns the CASID of the include-tree for this module, if any. + * + * The string is only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE const char * + clang_experimental_DepGraphModule_getIncludeTreeID(CXDepGraphModule); + +/** + * \returns the \c ActionCache key for this module, if any. + * + * The string is only valid to use while the \c CXDepGraphModule object is + * valid. + */ +CINDEX_LINKAGE +const char *clang_experimental_DepGraphModule_getCacheKey(CXDepGraphModule); + +/** + * \returns 1 if the scanner ignores the current working directory when + * computing the module's context hash. Otherwise returns 0. + */ +CINDEX_LINKAGE +int clang_experimental_DepGraphModule_isCWDIgnored(CXDepGraphModule); + +/** + * \returns the number \c CXDepGraphTUCommand objects in the graph. + */ +CINDEX_LINKAGE size_t clang_experimental_DepGraph_getNumTUCommands(CXDepGraph); + +/** + * \returns the \c CXDepGraphTUCommand object at the given \p Index. + * + * The \c CXDepGraphTUCommand object is only valid to use while \c CXDepGraph is + * valid. Must be disposed with \c clang_experimental_DepGraphTUCommand_dispose. + */ +CINDEX_LINKAGE CXDepGraphTUCommand +clang_experimental_DepGraph_getTUCommand(CXDepGraph, size_t Index); + +/** + * Dispose of a \c CXDepGraphTUCommand object. + */ +CINDEX_LINKAGE void + clang_experimental_DepGraphTUCommand_dispose(CXDepGraphTUCommand); + +/** + * \returns the executable name for the command. + * + * The string is only valid to use while the \c CXDepGraphTUCommand object is + * valid. + */ +CINDEX_LINKAGE const char * + clang_experimental_DepGraphTUCommand_getExecutable(CXDepGraphTUCommand); + +/** + * \returns the canonical command line to build this translation unit. + * + * The strings are only valid to use while the \c CXDepGraphTUCommand object is + * valid. + */ +CINDEX_LINKAGE CXCStringArray + clang_experimental_DepGraphTUCommand_getBuildArguments(CXDepGraphTUCommand); + +/** + * \returns the \c ActionCache key for this translation unit, if any. + * + * The string is only valid to use while the \c CXDepGraphTUCommand object is + * valid. + */ +CINDEX_LINKAGE const char * + clang_experimental_DepGraphTUCommand_getCacheKey(CXDepGraphTUCommand); + +/** + * \returns the list of files which this translation unit directly depends on. + * + * The strings are only valid to use while the \c CXDepGraph object is valid. + */ +CINDEX_LINKAGE +CXCStringArray clang_experimental_DepGraph_getTUFileDeps(CXDepGraph); + +/** + * \returns the list of modules which this translation unit direct depends on. + * + * This does include the context hash. The format is + * `:` + * + * The strings are only valid to use while the \c CXDepGraph object is valid. + */ +CINDEX_LINKAGE +CXCStringArray clang_experimental_DepGraph_getTUModuleDeps(CXDepGraph); + +/** + * @returns the CASID of the filesystem root for this TU, if any. + * + * This string is only valid to use while the \c CXDepGraph object is valid. + */ +CINDEX_LINKAGE +const char *clang_experimental_DepGraph_getTUFileSystemRootID(CXDepGraph); + +/** + * @returns the CASID of the include-tree for this TU, if any. + * + * The string is only valid to use while the \c CXDepGraph object is valid. + */ +CINDEX_LINKAGE +const char *clang_experimental_DepGraph_getTUIncludeTreeID(CXDepGraph); + +/** + * \returns the context hash of the C++20 module this translation unit exports. + * + * If the translation unit is not a module then this is empty. + * + * The string is only valid to use while the \c CXDepGraph object is valid. + */ +CINDEX_LINKAGE +const char *clang_experimental_DepGraph_getTUContextHash(CXDepGraph); + +/** + * \returns The diagnostics emitted during scanning. These must be always freed + * by calling \c clang_disposeDiagnosticSet. + */ +CINDEX_LINKAGE +CXDiagnosticSet clang_experimental_DepGraph_getDiagnostics(CXDepGraph); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif // LLVM_CLANG_C_DEPENDENCIES_H diff --git a/clang/include/clang-c/Driver.h b/clang/include/clang-c/Driver.h new file mode 100644 index 0000000000000..54dd1b0b043a3 --- /dev/null +++ b/clang/include/clang-c/Driver.h @@ -0,0 +1,78 @@ +/*==-- clang-c/Driver.h - A C Interface for the Clang Driver ------*- C -*-===*\ +|* *| +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| +|* Exceptions. *| +|* See https://llvm.org/LICENSE.txt for license information. *| +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This header provides a C API for extracting information from the clang *| +|* driver. *| +|* *| +\*===----------------------------------------------------------------------===*/ + + +#ifndef CLANG_CLANG_C_DRIVER +#define CLANG_CLANG_C_DRIVER + +#include "clang-c/Index.h" +#include "clang-c/Platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Contains the command line arguments for an external action. Same format as + * provided to main. + */ +typedef struct { + /* Number of arguments in ArgV */ + int ArgC; + /* Null terminated array of pointers to null terminated argument strings */ + const char **ArgV; +} CXExternalAction; + +/** + * Contains the list of external actions clang would invoke. + */ +typedef struct { + int Count; + CXExternalAction **Actions; +} CXExternalActionList; + +/** + * Get the external actions that the clang driver will invoke for the given + * command line. + * + * \param ArgC number of arguments in \p ArgV. + * \param ArgV array of null terminated arguments. Doesn't need to be null + * terminated. + * \param Environment must be null. + * \param WorkingDirectory a null terminated path to the working directory to + * use for this invocation. `nullptr` to use the current working directory of + * the process. + * \param OutDiags will be set to a \c CXDiagnosticSet if there's an error. + * Must be freed by calling \c clang_disposeDiagnosticSet . + * \returns A pointer to a \c CXExternalActionList on success, null on failure. + * The returned \c CXExternalActionList must be freed by calling + * \c clang_Driver_ExternalActionList_dispose . + */ +CINDEX_LINKAGE CXExternalActionList * +clang_Driver_getExternalActionsForCommand_v0(int ArgC, const char **ArgV, + const char **Environment, + const char *WorkingDirectory, + CXDiagnosticSet *OutDiags); + +/** + * Deallocate a \c CXExternalActionList + */ +CINDEX_LINKAGE void +clang_Driver_ExternalActionList_dispose(CXExternalActionList *EAL); + +#ifdef __cplusplus +} +#endif + +#endif // CLANG_CLANG_C_DRIVER diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 25700a48c928c..7973212d58c49 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -34,7 +34,7 @@ * compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable. */ #define CINDEX_VERSION_MAJOR 0 -#define CINDEX_VERSION_MINOR 64 +#define CINDEX_VERSION_MINOR 65 #define CINDEX_VERSION_ENCODE(major, minor) (((major)*10000) + ((minor)*1)) @@ -1691,7 +1691,17 @@ enum CXCursorKind { */ CXCursor_PackIndexingExpr = 156, - CXCursor_LastExpr = CXCursor_PackIndexingExpr, + /* TO_UPSTREAM(BoundsSafety) ON */ + /** + * BoundsSafety '__builtin_unsafe_forge_bidi_indexable(addr, size)' or + * '__builtin_forge_single(addr) + */ + CXCursor_ForgePtrExpr = 198, + + CXCursor_GetBoundExpr = 199, + + CXCursor_LastExpr = CXCursor_GetBoundExpr, + /* TO_UPSTREAM(BoundsSafety) OFF */ /* Statements */ CXCursor_FirstStmt = 200, diff --git a/clang/include/clang-c/Refactor.h b/clang/include/clang-c/Refactor.h new file mode 100644 index 0000000000000..983a652481d9e --- /dev/null +++ b/clang/include/clang-c/Refactor.h @@ -0,0 +1,1313 @@ +/*==-- clang-c/Refactor.h - Refactoring Public C Interface --------*- C -*-===*\ +|* *| +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| +|* Exceptions. *| +|* See https://llvm.org/LICENSE.txt for license information. *| +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This header provides a public inferface to a Clang library for performing *| +|* refactoring actions on projects without exposing the full Clang C++ API. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_C_REFACTOR_H +#define LLVM_CLANG_C_REFACTOR_H + +#include "clang-c/Index.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup CINDEX_REFACTOR Refactoring options. + * + * @{ + */ + +/** + * \brief The refactoring options that can be specified for each refactoring + * action. + */ +enum CXRefactoringOption { + /** + * \brief The refactoring actions like 'rename' will avoid looking for + * occurrences of the renamed symbol in comments if this option is enabled. + */ + CXRefactorOption_AvoidTextualMatches = 1 +}; + +/** + * \brief Opaque pointer representing a set of options that can be given to + * a refactoring action. + */ +typedef void *CXRefactoringOptionSet; + +/** + * \brief Returns a new option set. + */ +CINDEX_LINKAGE +CXRefactoringOptionSet clang_RefactoringOptionSet_create(void); + +/** + * \brief Parses and returns a new option set or NULL if the given string is + * invalid. + */ +CINDEX_LINKAGE +CXRefactoringOptionSet +clang_RefactoringOptionSet_createFromString(const char *String); + +/** + * \brief Adds a new option to the given refactoring option set. + */ +CINDEX_LINKAGE +void clang_RefactoringOptionSet_add(CXRefactoringOptionSet Set, + enum CXRefactoringOption Option); + +/** + * \brief Converts the given refactoring option set to a string value. + */ +CINDEX_LINKAGE +CXString clang_RefactoringOptionSet_toString(CXRefactoringOptionSet Set); + +/** + * \brief Free the given option set. + * + * Option sets should be freed by this function only when they were created + * using the \c clang_RefactoringOptionSet_create* methods. + */ +CINDEX_LINKAGE +void clang_RefactoringOptionSet_dispose(CXRefactoringOptionSet Set); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR Refactoring actions. + * + * @{ + */ + +/** + * \brief The refactoring actions that can be performed by libclang. + */ +enum CXRefactoringActionType { + /** + * \brief The 'rename' refactoring action. + */ + CXRefactor_Rename = 0, + + /** + * \brief The local 'rename' refactoring action. + */ + CXRefactor_Rename_Local = 1, + + /** + * \brief The 'extract' refactoring action extracts source code into a + * new function. + */ + CXRefactor_Extract = 2, + + /** + * \brief The sub-action of 'extract' that extracts source code into a new + * method. + */ + CXRefactor_Extract_Method = 3, + + /** + * \brief The action that converts an if/else constructs to a switch block. + */ + CXRefactor_IfSwitchConversion = 4, + + /** + * \brief The action that wraps an Objective-C string literal in an + * NSLocalizedString macro. + */ + CXRefactor_LocalizeObjCStringLiteral = 5, + + /** + * \brief The action that adds missing switch cases to an switch over an enum. + */ + CXRefactor_FillInEnumSwitchCases = 6, + + /** + * \brief The action that adds missing protocol methods to an Objective-C + * class. + */ + CXRefactor_FillInMissingProtocolStubs = 7, + + /** + * \brief The action that extracts an expression that's repeated in a function + * into a new variable. + */ + CXRefactor_ExtractRepeatedExpressionIntoVariable = 8, + + /** + * \brief The action that adds missing abstract class method overrides to a + * class. + */ + CXRefactor_FillInMissingMethodStubsFromAbstractClasses = 9, + + /** + * \brief The action that generates dummy method definitions for method + * declarations without respective definitions. + */ + CXRefactor_ImplementDeclaredMethods = 10, + + /** + * \brief The sub-action of 'extract' that extracts source expression into a + * new variable. + */ + CXRefactor_Extract_Expression = 11, +}; + +/** + * \brief Return the name of the given refactoring action. + */ +CINDEX_LINKAGE +CXString +clang_RefactoringActionType_getName(enum CXRefactoringActionType Action); + +/** + * \brief A set of refactoring actions that can be performed at some specific + * location in a source file. + * + * The actions in the action set are ordered by their priority: most important + * actions are placed before the less important ones. + */ +typedef struct { + const enum CXRefactoringActionType *Actions; + unsigned NumActions; +} CXRefactoringActionSet; + +/** + * \brief Free the given refactoring action set. + */ +CINDEX_LINKAGE void +clang_RefactoringActionSet_dispose(CXRefactoringActionSet *Set); + +typedef struct { + enum CXRefactoringActionType Action; + /** + * \brief The set of diagnostics that describes the reason why this action + * couldn't be initiated. This set of diagnostics is managed by the + * \c CXRefactoringActionSetWithDiagnostics and shouldn't be freed manually. + */ + CXDiagnosticSet Diagnostics; +} CXRefactoringActionWithDiagnostics; + +/** + * \brief A set of refactoring actions that couldn't be initiated at some + * location and their respective diagnostics that describe the reason why + * the initiation failed. + */ +typedef struct { + CXRefactoringActionWithDiagnostics *Actions; + unsigned NumActions; +} CXRefactoringActionSetWithDiagnostics; + +/** + * \brief Free the given refactoring action set with diagnostics. + */ +CINDEX_LINKAGE void clang_RefactoringActionSetWithDiagnostics_dispose( + CXRefactoringActionSetWithDiagnostics *Set); + +/** + * \brief Find the set of refactoring actions that can be performed at the given + * location. + * + * This function examines the AST around the given source range and creates a + * \c CXRefactoringActionSet that contains all of the actions that can be + * performed in the given source range. + * + * \param TU The translation unit which contains the given source range. + * + * \param Location The location at which the refactoring action will be + * performed. + * + * \param SelectionRange The range in which the AST should be checked. Usually + * corresponds to the selection range or location of the cursor in the editor. + * Can be a null range. + * + * \param Options The optional refactoring options that might influence the way + * the search is performed. + * + * \param[out] OutSet A non-NULL pointer to store the created + * \c CXRefactoringActionSet. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * there are no actions available in the given range, or an error code + * otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode +clang_Refactoring_findActionsAt(CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, + CXRefactoringOptionSet Options, + CXRefactoringActionSet *OutSet); + +/** + * \brief Find the set of refactoring actions that can be performed at the given + * location. + * + * This function examines the AST around the given source range and creates a + * \c CXRefactoringActionSet that contains all of the actions that can be + * performed in the given source range. It also creates a + * \c CXRefactoringActionSetWithDiagnostics that might describe the reason why + * some refactoring actions are not be available. + * + * \param TU The translation unit which contains the given source range. + * + * \param Location The location at which the refactoring action will be + * performed. + * + * \param SelectionRange The range in which the AST should be checked. Usually + * corresponds to the selection range or location of the cursor in the editor. + * Can be a null range. + * + * \param Options The optional refactoring options that might influence the way + * the search is performed. + * + * \param[out] OutSet A non-NULL pointer to store the created + * \c CXRefactoringActionSet. + * + * \param[out] OutFailureSet An optional pointer to store the created + * \c CXRefactoringActionSetWithDiagnostics that describes the failures reasons + * for some of the refactoring actions. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * there are no actions available in the given range, or an error code + * otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, CXRefactoringOptionSet Options, + CXRefactoringActionSet *OutSet, + CXRefactoringActionSetWithDiagnostics *OutFailureSet); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR_INITIATE Refactoring initiation + * + * @{ + */ + +/** + * \brief Opaque pointer representing the initiated refactoring action. + */ +typedef void *CXRefactoringAction; + +/** + * \brief Free the given refactoring action. + * + * The refactoring action should be freed before the initiation and/or + * implementation translation units. + */ +CINDEX_LINKAGE void clang_RefactoringAction_dispose(CXRefactoringAction Action); + +/** + * \brief Return the source range that's associated with the initiated + * refactoring action. + * + * The returned source range covers the source that will be modified by the + * given refactoring action. If the action has no associated source range, + * then this function will return a null \c CXSourceRange. + */ +CINDEX_LINKAGE CXSourceRange +clang_RefactoringAction_getSourceRangeOfInterest(CXRefactoringAction Action); + +/** + * \brief Return the type of the initiated action, which might be different + * to the type of the requested action. For an operation 'rename', the action + * could actually initiate the local 'rename' operation. + */ +CINDEX_LINKAGE +enum CXRefactoringActionType +clang_RefactoringAction_getInitiatedActionType(CXRefactoringAction Action); + +/** + * \brief Return a non-zero value when the refactoring action requires access + * to an additional translation unit that contains an implementation of some + * declaration. + */ +// TODO: Remove (this is no longer needed due to refactoring continuations). +CINDEX_LINKAGE +int clang_RefactoringAction_requiresImplementationTU( + CXRefactoringAction Action); + +/** + * \brief Return a USR that corresponds to the declaration whose implementation + * is required in order for the given refactoring action to work correctly. + */ +// TODO: Remove (this is no longer needed due to refactoring continuations). +CINDEX_LINKAGE +CXString clang_RefactoringAction_getUSRThatRequiresImplementationTU( + CXRefactoringAction Action); + +/** + * \brief Set the translation unit that contains the declaration whose + * implementation is required for the given refactoring action to work + * correctly. + */ +// TODO: Remove (this is no longer needed due to refactoring continuations). +CINDEX_LINKAGE +enum CXErrorCode +clang_RefactoringAction_addImplementationTU(CXRefactoringAction Action, + CXTranslationUnit TU); + +/** + * \brief A refactoring candidate determines on which piece of source code the + * action should be applied. + * + * Most refactoring actions have just one candidate, but some actions, like + * 'Extract' can produce multiple candidates. + * + * The candidates are managed by the refactoring action, and their description + * string doesn't need to be freed manually. + */ +typedef struct { CXString Description; } CXRefactoringCandidate; + +/** + * \brief A set of refactoring candidates on which the previously initiatied + * refactoring action can be performed. + * + * The candidates in the candidate set are ordered by their priority: the + * ones that are more likely to be selected are placed before the other ones. + * + * A non-empty refactoring candidate set always has more than one refactoring + * candidate, because when a refactoring action has just one candidate, + * \c clang_RefactoringAction_getRefactoringCandidates will return an empty + * candidate set. + */ +typedef struct { + const CXRefactoringCandidate *Candidates; + unsigned NumCandidates; +} CXRefactoringCandidateSet; + +/** + * \brief Returns the given action's refactoring candidates. + * + * The resulting refactoring candidate set will be empty when the given \c + * CXRefactoringAction has just one refactoring candidate. + * + * \param Action A previously initiated \c CXRefactoringAction. + * + * \param[out] OutRefactoringCandidateSet An pointer to store the action's + * refactoring candidate set. + * + * \returns Zero on success, or an error code otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_RefactoringAction_getRefactoringCandidates( + CXRefactoringAction Action, + CXRefactoringCandidateSet *OutRefactoringCandidateSet); + +/** + * \brief Tells the given refactoring action that it has to perform the + * operation on the refactoring candidate that's located at \p Index in the \c + * CXRefactoringCandidateSet. + */ +CINDEX_LINKAGE +enum CXErrorCode +clang_RefactoringAction_selectRefactoringCandidate(CXRefactoringAction Action, + unsigned Index); + +// TODO: Remove. +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_initiateActionAt( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType, + CXRefactoringOptionSet Options, CXRefactoringAction *OutAction, + CXString *OutFailureReason); + +/** + * \brief Initiate a specific refactoring action at the given location. + * + * This function initiates an \p ActionType refactoring action when it can + * be initiated at the given location and creates a \c CXRefactoringAction + * action that will allow the control. + * + * \param TU The translation unit in which the action should be initiated. + * + * \param Location The location at which the refactoring action will be + * performed. + * + * \param SelectionRange The range in which the AST should be checked. Usually + * corresponds to the selection range or location of the cursor in the editor. + * Can be a null range. + * + * \param ActionType The type of action that should be initiated. + * + * \param Options The optional refactoring options that might have an influence + * on the initiation process. + * + * \param[out] OutAction A non-NULL pointer to store the created + * \c CXRefactoringAction. + * + * \param[out] OutDiagnostics An optional pointer to store any diagnostics that + * describe why the action wasn't initiated. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * the given refactoring action can't be performed at the given location, or an + * error code otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_initiateAction( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType, + CXRefactoringOptionSet Options, CXRefactoringAction *OutAction, + CXDiagnosticSet *OutDiagnostics); + +/** + * \brief Initiate a specific refactoring action on a particular declaration. + * + * This function searches for the declaration that corresponds to \p DeclUSR + * and initiates an \p ActionType a refactoring action on that declaration + * if possible. + * + * \param TU The translation unit in which the declaration is defined. + * + * \param DeclUSR The USR that corresponds to the declaration of interest. + * + * \param ActionType The type of action that should be initiated. + * + * \param Options The optional refactoring options that might have an influence + * on the initiation process. + * + * \param[out] OutAction A non-NULL pointer to store the created + * \c CXRefactoringAction. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * the given refactoring action can't be performed on the found declaration, or + * an error code otherwise. + */ +// TODO: Remove (not needed). +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_initiateActionOnDecl( + CXTranslationUnit TU, const char *DeclUSR, + enum CXRefactoringActionType ActionType, CXRefactoringOptionSet Options, + CXRefactoringAction *OutAction, CXString *OutFailureReason); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR_REPLACEMENT Refactoring replacement + * + * @{ + */ + +/** + * \brief A source location in a single file that is independent of \c + * CXTranslationUnit. + */ +typedef struct { unsigned Line, Column; } CXFileLocation; + +/** + * \brief A source range in a single file that is independent of \c + * CXTranslationUnit. + */ +typedef struct { CXFileLocation Begin, End; } CXFileRange; + +// TODO: Remove +typedef struct { + CXFileRange Range; + CXString ReplacementString; +} CXRefactoringReplacement_Old; + +// TODO: Remove +typedef struct { + CXString Filename; + const CXRefactoringReplacement_Old *Replacements; + unsigned NumReplacements; +} CXRefactoringFileReplacementSet_Old; + +// TODO: Remove +typedef struct { + const CXRefactoringFileReplacementSet_Old *FileReplacementSets; + unsigned NumFileReplacementSets; +} CXRefactoringReplacements_Old; + +/** + * \brief Identifies a character range in the source code of a single file that + * should be replaced with the replacement string. + * + * Replacements are managed by the result of a specific refactoring action, + * like \c CXRenamingResult, and are invalidated when the refactoring result is + * destroyed. + */ +typedef struct { + CXFileRange Range; + CXString ReplacementString; + void *AssociatedData; +} CXRefactoringReplacement; + +/** +* \brief A set of refactoring replacements that are applicable to a certain + * file. + */ +typedef struct { + CXString Filename; + const CXRefactoringReplacement *Replacements; + unsigned NumReplacements; +} CXRefactoringFileReplacementSet; + +/** + * \brief A set of refactoring replacements that have been produced by a + * refactoring operation. + * + * The refactoring replacements depend on \c CXRefactoringResult, and can't be + * used after the refactoring result is freed. + */ +typedef struct { + const CXRefactoringFileReplacementSet *FileReplacementSets; + unsigned NumFileReplacementSets; +} CXRefactoringReplacements; + +/** + * @} + */ + +/** + * \defgroup CINDEX_SYMBOL_OPERATION Symbol-based refactoring operation + * (e.g. Rename). + * + * @{ + */ + +/** + * \brief The type of a symbol occurrence. + * + * The occurrence kind determines if an occurrence can be renamed automatically + * or if the user has to make the decision whether or not this occurrence + * should be renamed. + */ +enum CXSymbolOccurrenceKind { + /** + * \brief This occurrence is an exact match and can be renamed automatically. + */ + CXSymbolOccurrence_MatchingSymbol = 0, + + /** + * \brief This is an occurrence of a matching selector. It can't be renamed + * automatically unless the indexer proves that this selector refers only + * to the declarations that correspond to the renamed symbol. + */ + CXSymbolOccurrence_MatchingSelector = 1, + + /** + * \brief This is an occurrence of an implicit property that uses the + * renamed method. + */ + CXSymbolOccurrence_MatchingImplicitProperty = 2, + + /** + * \brief This is an occurrence of an symbol name in a comment. + */ + CXSymbolOccurrence_MatchingCommentString = 3, + + /** + * \brief This is an occurrence of an symbol name in a documentation comment. + */ + CXSymbolOccurrence_MatchingDocCommentString = 4, + + /** + * \brief This is an occurrence of an symbol name in a filename in an inclusion + * directive. + */ + CXSymbolOccurrence_MatchingFilename = 5, + + /** + * \brief This is an occurrence of an symbol name in a string literal. + */ + CXSymbolOccurrence_MatchingStringLiteral = 6, + + /** + * \brief This is an occurrence of a symbol name that belongs to the extracted + * declaration. Note: this occurrence can be in two replacements as we might + * extract an out-of-line method that will be both declared and defined. + */ + CXSymbolOccurrence_ExtractedDeclaration = 100, + + /** + * \brief This is an occurrence of a symbol name that references the extracted + * declaration. + */ + CXSymbolOccurrence_ExtractedDeclaration_Reference = 101, +}; + +// TODO: Remove +typedef struct { + const CXRefactoringReplacement_Old *Replacements; + unsigned ReplacementCount; + enum CXSymbolOccurrenceKind Kind; + /** + * Whether or not this occurrence is inside a macro. When this is true, the + * replacements of the occurrence contain just a single empty replacement that + * points to the location of the macro expansion. + */ + int IsMacroExpansion; +} CXRenamedSymbolOccurrence; + +/** + * \brief An occurrence of a symbol. + * + * Contains the source ranges that represent the pieces of the name of the + * symbol. The occurrences are managed by \c CXRenamingResult, and are + * invalidated when \c CXRenamingResult is destroyed. + */ +typedef struct { + const CXFileRange *NamePieces; + unsigned NumNamePieces; + enum CXSymbolOccurrenceKind Kind; + /** + * Whether or not this occurrence is inside a macro. When this is true, the + * replacements of the occurrence contain just a single empty replacement that + * points to the location of the macro expansion. + */ + int IsMacroExpansion; + unsigned SymbolIndex; +} CXSymbolOccurrence; + +// TODO: Remove +typedef struct { + CXString Filename; + const CXRenamedSymbolOccurrence *Occurrences; + unsigned NumOccurrences; +} CXFileRenamingResult; // TODO: Remove + +/** +* \brief A set of symbol occurrences that occur in a single file. + */ +typedef struct { + CXString Filename; + /** + * The set of occurrences for each symbol of interest. + */ + const CXSymbolOccurrence *Occurrences; + unsigned NumOccurrences; +} CXSymbolOccurrencesInFile; + +/** + * \brief Opaque pointer representing all of the renames that should take place + * in a single translation unit. + * + * The result of a renaming action is indepedent from \c CXRenamingAction, and + * remains valid after \c CXRenamingAction is destroyed. + */ +typedef void *CXRenamingResult; + +/** + * \brief Opaque pointer representing all of the symbol occurrences from a + * single TU/file. + * + * The result of a symbol search occurrence search operation is indepedent from + * \c CXRefactoringAction, and remains valid after \c CXRefactoringAction is + * destroyed. + */ +typedef void *CXSymbolOccurrencesResult; + +/** + * \brief Find the cursor that's being renamed at the given location. + * + * \param TU The translation unit in which the cursor is present. + * + * \param Location The location at which the refactoring action will be + * performed. + * + * \param SelectionRange The range in which the AST should be checked. Usually + * corresponds to the selection range or location of the cursor in the editor. + * Can be a null range. + * + * \returns Zero on success, CXError_RefactoringActionUnavailable when + * there's no suitable cursor at the given location, or an error code otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_findRenamedCursor( + CXTranslationUnit TU, CXSourceLocation Location, + CXSourceRange SelectionRange, CXCursor *OutCursor); + +/** + * \brief Initiates a renaming operation on a previously initiated refactoring + * action. + * + * The initiation process finds the symbols that have to be renamed for a + * previously initiated \c CXRefactor_Rename refactoring action. + * + * \returns Zero on success, or an error code otherwise. + */ +// TODO: Remove +CINDEX_LINKAGE +enum CXErrorCode +clang_Refactoring_initiateRenamingOperation(CXRefactoringAction Action); + +/** + * \brief Set the new name of the renamed symbol in the given \c + * RenamingAction. + * + * \returns Zero on success, CXError_RefactoringNameInvalid when the new name + * isn't a valid identifier, CXError_RefactoringNameSizeMismatch when the new + * name has an incorrect number of pieces or a different error code otherwise. + */ +// TODO: Remove +CINDEX_LINKAGE +enum CXErrorCode clang_RenamingOperation_setNewName(CXRefactoringAction Action, + const char *NewName); + +/** + * \brief Return the number of symbols that are renamed by the given renaming + * action. + * + * A renaming action typically works on just one symbol. However, there are + * certain language constructs that require work with more than one symbol in + * order for them to be renamed correctly. Property declarations in Objective-C + * are the perfect example: in addition to the actual property, the action has + * to rename the corresponding getters and setters, as well as the backing ivar. + */ +// TODO: Remove +CINDEX_LINKAGE +unsigned clang_RenamingOperation_getNumSymbols(CXRefactoringAction Action); + +/** + * \brief Return the USR of the declaration that was found for the symbol at the + * given \p Index in the given renaming action. + */ +// TODO: Remove +CINDEX_LINKAGE +CXString clang_RenamingOperation_getUSRForSymbol(CXRefactoringAction Action, + unsigned Index); + +// TODO: Remove +CINDEX_LINKAGE +CXRenamingResult clang_Refactoring_findRenamedOccurrencesInPrimaryTUs( + CXRefactoringAction Action, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles); + +/** + * \brief Find all of the occurrences of the symbol that is being searched for + * by the given refactoring action in the translation unit that was used to + * initiate the refactoring action. + * + * This function searches for all of the \c CXSymbolOccurrence in the + * translation units that are referenced by the given \c CXRefactoringAction by + * iterating through the AST of the each translation unit. The occurrences that + * are found don't have to be from the main file in the translation unit, they + * can be from files included in that translation unit. + * + * \param Action The \c CXRefactoringAction operation that was inititated by + * \c clang_Refactoring_initiateActionAt(). + * + * \param CommandLineArgs The command-line arguments that would be + * passed to the \c clang executable if it were being invoked out-of-process. + * + * \param NumCommandLineArgs The number of command-line arguments in + * \c CommandLineArgs. + * + * \param UnsavedFiles the files that have not yet been saved to disk + * but may be required for parsing, including the contents of + * those files. The contents and name of these files (as specified by + * CXUnsavedFile) are copied when necessary, so the client only needs to + * guarantee their validity until the call to this function returns. + * + * \param NumUnsavedFiles the number of unsaved file entries in \p + * UnsavedFiles. + * + * \returns If successful, a new \c CXSymbolOccurrencesResult structure + * containing the occurrences of the symbol in the initiation translation unit, + * which should eventually be freed with \c clang_SymbolOccurrences_dispose(). + * If symbol search fails, returns NULL. + */ +CINDEX_LINKAGE +CXSymbolOccurrencesResult clang_Refactoring_findSymbolOccurrencesInInitiationTU( + CXRefactoringAction Action, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles); + +// TODO: Remove +typedef struct { + CXFileLocation Location; + /** + * The kind of the declaration/expression that was indexed at this location. + * This is particularly important for Objective-C selectors. The refactoring + * engine requires the following cursor kinds for the following indexed + * occurrences: + * - ObjC method declaration: CXCursor_ObjC(Instance/Class)MethodDecl + * - ObjC method message send: CXCursor_ObjCMessageExpr + * Other occurrences can use any other cursor cursor kinds. + */ + enum CXCursorKind CursorKind; +} CXRenamedIndexedSymbolLocation; + +// TODO: Remove +typedef struct { + /** + * An array of occurrences that represent indexed occurrences of a symbol. + * It's valid to pass-in no indexed locations, the refactoring engine will + * just perform textual search in that case. + */ + const CXRenamedIndexedSymbolLocation *IndexedLocations; + unsigned IndexedLocationCount; + /** + * The kind of the declaration that is being renamed. + * This is particularly important for Objective-C selectors. The refactoring + * engine requires the following cursor kinds for the following renamed + * declaration: + * - ObjC methods: CXCursor_ObjC(Instance/Class)MethodDecl + * Other declarations can use any other cursor cursor kinds. + */ + enum CXCursorKind CursorKind; + const char *Name; + const char *NewName; +} CXRenamedIndexedSymbol; + +// TODO: Remove +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_findRenamedOccurrencesInIndexedFile( + const CXRenamedIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx, + const char *Filename, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, + CXRenamingResult *OutResult); + +/** + * \brief A location of an already known occurrence of a symbol. + * + * Used for rename-indexed operation where the renaming is performed on an + * already indexed source file. + */ +typedef struct { + CXFileLocation Location; + /** + * The kind of the declaration/expression that was indexed at this location. + * This is particularly important for Objective-C selectors. The refactoring + * engine requires the following cursor kinds for the following indexed + * occurrences: + * - ObjC method declaration: CXCursor_ObjC(Instance/Class)MethodDecl + * - ObjC method message send: CXCursor_ObjCMessageExpr + * - filename in an #include: CXCursor_InclusionDirective + * Other occurrences can use any other cursor cursor kinds. + */ + enum CXCursorKind CursorKind; +} CXIndexedSymbolLocation; + +/** + * \brief A symbol that should be found the an indexer symbol search operation. + * + * Used for rename-indexed operation where the renaming is performed on an + * already indexed source file. + */ +typedef struct { + /** + * An array of occurrences that represent indexed occurrences of a symbol. + * It's valid to pass-in no indexed locations, the refactoring engine will + * just perform textual search in that case. + */ + const CXIndexedSymbolLocation *IndexedLocations; + unsigned IndexedLocationCount; + /** + * The kind of the declaration that is being renamed. + * This is particularly important for Objective-C selectors. The refactoring + * engine requires the following cursor kinds for the following renamed + * declaration: + * - ObjC methods: CXCursor_ObjC(Instance/Class)MethodDecl + * - ObjC class: CXCursor_ObjCInterfaceDecl + * Other declarations can use any other cursor cursor kinds. + */ + enum CXCursorKind CursorKind; + /** + * The name of the symbol. Objective-C selector names should be specified + * using the ':' separator for selector pieces. + */ + const char *Name; +} CXIndexedSymbol; + +/** + * \brief Find all of the occurrences of a symbol in an indexed file. + * + * This function searches for all of the \c CXIndexedSymbol in the + * given file by inspecting the source code at the given indexed locations. + * + * The indexed operations are thread-safe and can be performed concurrently. + * + * \param Symbols The information about the symbols that includes the locations + * for a symbol in the file as determined by the indexer. + * + * \param NumSymbols The number of symbols in \p Symbols. + * + * \param CIdx The index object with which the translation unit will be + * associated. + * + * \param Filename The name of the source file that contains the given + * \p Locations. + * + * \param CommandLineArgs The command-line arguments that would be + * passed to the \c clang executable if it were being invoked out-of-process. + * These command-line options will be parsed and will affect how the translation + * unit is parsed. + * + * \param NumCommandLineArgs The number of command-line arguments in + * \c CommandLineArgs. + * + * \param UnsavedFiles the files that have not yet been saved to disk + * but may be required for parsing, including the contents of + * those files. The contents and name of these files (as specified by + * CXUnsavedFile) are copied when necessary, so the client only needs to + * guarantee their validity until the call to this function returns. + * + * \param NumUnsavedFiles the number of unsaved file entries in \p + * UnsavedFiles. + * + * \param Options The optional refactoring options that might have an influence + * on the initiation process. + * + * \param[out] OutResult A non-NULL pointer to store the created + * \c CXSymbolOccurrencesResult. + * + * \returns Zero on success, or a different error code otherwise. + */ +CINDEX_LINKAGE +enum CXErrorCode clang_Refactoring_findSymbolOccurrencesInIndexedFile( + const CXIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx, + const char *Filename, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, + CXSymbolOccurrencesResult *OutResult); + +// TODO: Remove +CINDEX_LINKAGE +unsigned clang_RenamingResult_getNumModifiedFiles(CXRenamingResult Result); + +// TODO: Remove +CINDEX_LINKAGE +void clang_RenamingResult_getResultForFile(CXRenamingResult Result, + unsigned FileIndex, + CXFileRenamingResult *OutResult); + +// TODO: Remove +CINDEX_LINKAGE +void clang_RenamingResult_dispose(CXRenamingResult Result); + +/** + * \brief Return the number of files that have occurrences of the specific + * symbol. + */ +CINDEX_LINKAGE +unsigned clang_SymbolOccurrences_getNumFiles(CXSymbolOccurrencesResult Result); + +/** + * \brief Return the set of symbol occurrences in a single file. + * + * The resulting \c CXSymbolOccurrencesInFile is managed by the + * \c CXSymbolOccurrencesResult and doesn't have to be disposed of manually. + */ +CINDEX_LINKAGE +void clang_SymbolOccurrences_getOccurrencesForFile( + CXSymbolOccurrencesResult Result, unsigned FileIndex, + CXSymbolOccurrencesInFile *OutResult); + +// TODO: Support refactoring continuations for \c CXSymbolOccurrencesResult, +// e.g. for function parameter name rename. + +/** + * \brief Free the given symbol occurrences result. + */ +CINDEX_LINKAGE +void clang_SymbolOccurrences_dispose(CXSymbolOccurrencesResult Result); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR_PERFORM Performing refactoring operations. + * + * @{ + */ + +/** + * \brief Opaque pointer representing the results of the refactoring operation. + * + * The result of a refactoring action depends on the \c CXRefactoringAction, and + * is invalidated after \c CXRefactoringAction is destroyed. + */ +typedef void *CXRefactoringResult; + +/** + * \brief Opaque pointer representing a refactoring continuation. + * + * Refactoring continuations allow refactoring operations to run in external + * AST units with some results that were obtained after querying the indexer. + * + * The refactoring continuation is not dependent on the \c CXRefactoringAction + * or \c CXRefactoringResult. It does depend on the initiation + * \c CXTranslationUnit initially, but that dependency can be terminated. + */ +typedef void *CXRefactoringContinuation; + +/** + * \brief Opaque pointer representing a query to the indexer. + */ +typedef void *CXIndexerQuery; + +/** + * \brief Performs the previously initiated refactoring operation. + * + * This function executes the refactoring operation which produces a set of + * candidate source replacements that can be applied to the source files. + * + * \param Action The refactoring action. + * + * \param CommandLineArgs The command-line arguments that would be + * passed to the \c clang executable if it were being invoked out-of-process. + * These command-line options will be parsed and will affect how the translation + * unit is parsed. + * + * \param NumCommandLineArgs The number of command-line arguments in + * \c CommandLineArgs. + * + * \param UnsavedFiles the files that have not yet been saved to disk + * but may be required for parsing, including the contents of + * those files. The contents and name of these files (as specified by + * CXUnsavedFile) are copied when necessary, so the client only needs to + * guarantee their validity until the call to this function returns. + * + * \param NumUnsavedFiles the number of unsaved file entries in \p + * UnsavedFiles. + * + * \param Options The optional refactoring options that might have an influence + * on the way the particular action will be performed. + * + * \param[out] OutFailureReason An optional pointer to store a message that + * describes why the action wasn't performed. + * + * \returns If successful, a new \c CXRefactoringResult structure containing the + * source replacement candidates, which should eventually be freed with + * \c clang_RefactoringResult_dispose(). If the refactoring operation fails, + * returns NULL. + */ +CINDEX_LINKAGE +CXRefactoringResult clang_Refactoring_performOperation( + CXRefactoringAction Action, const char *const *CommandLineArgs, + int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, + unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, + CXString *OutFailureReason); + +// TODO: Remove. This is the deprecated API. +CINDEX_LINKAGE +void clang_RefactoringResult_getReplacements( + CXRefactoringResult Result, CXRefactoringReplacements_Old *OutReplacements); + +/** + * \brief Return the set of refactoring source replacements. + * + * The resulting \c CXRefactoringReplacements are managed by the + * \c CXRefactoringResult and don't have to be disposed of manually. + */ +CINDEX_LINKAGE +CXRefactoringReplacements +clang_RefactoringResult_getSourceReplacements(CXRefactoringResult Result); + +/** + * \brief Represents a set of symbol occurrences that are associated with a + * single refactoring replacement. + * + * The symbol occurrences depend on \c CXRefactoringResult, and can't be + * used after the refactoring result is freed. + */ +typedef struct { + const CXSymbolOccurrence *AssociatedSymbolOccurrences; + unsigned NumAssociatedSymbolOccurrences; +} CXRefactoringReplacementAssociatedSymbolOccurrences; + +/** + * \brief Return the set of symbol occurrences that are associated with the + * given \p Replacement. + */ +CINDEX_LINKAGE +CXRefactoringReplacementAssociatedSymbolOccurrences +clang_RefactoringReplacement_getAssociatedSymbolOccurrences( + CXRefactoringReplacement Replacement); + +/** + * \brief Returns the refactoring continuation associated with this result, or + * NULL if this result has no refactoring continuation. + */ +CINDEX_LINKAGE +CXRefactoringContinuation +clang_RefactoringResult_getContinuation(CXRefactoringResult Result); + +/** + * \brief Free the given refactoring result. + */ +CINDEX_LINKAGE +void clang_RefactoringResult_dispose(CXRefactoringResult Result); + +/** + * \brief Load the indexer query results from a YAML string. + * + * Mainly used for testing. + */ +CINDEX_LINKAGE +enum CXErrorCode +clang_RefactoringContinuation_loadSerializedIndexerQueryResults( + CXRefactoringContinuation Continuation, const char *Source); + +/** + * \brief Return the number of indexer queries that a refactoring continuation + * has. + */ +CINDEX_LINKAGE +unsigned clang_RefactoringContinuation_getNumIndexerQueries( + CXRefactoringContinuation Continuation); + +/** + * \brief Return the indexer query at index \p Index. + */ +CINDEX_LINKAGE +CXIndexerQuery clang_RefactoringContinuation_getIndexerQuery( + CXRefactoringContinuation Continuation, unsigned Index); + +/** + * \brief Verify that the all of the indexer queries are satisfied by the + * continuation. + * + * \returns Null if all of the queries are satisfied an no errors have been + * reported, or a set of diagnostics that describes why the continuation should + * not be run. + */ +CINDEX_LINKAGE +CXDiagnosticSet clang_RefactoringContinuation_verifyBeforeFinalizing( + CXRefactoringContinuation Continuation); + +/** + * \brief Terminate the connection between the initiation TU and the refactoring + * continuation. + * + * The continuation converts all the TU-specific state to TU-independent state. + * The indexer queries that are associate with this continuation are also + * invalidated. + */ +CINDEX_LINKAGE +void clang_RefactoringContinuation_finalizeEvaluationInInitationTU( + CXRefactoringContinuation Continuation); + +/** + * \brief Continue performing the previously initiated and performed refactoring + * operation in the given translation unit \p TU. + */ +CINDEX_LINKAGE +CXRefactoringResult clang_RefactoringContinuation_continueOperationInTU( + CXRefactoringContinuation Continuation, CXTranslationUnit TU, + CXString *OutFailureReason); + +/** + * \brief Free the given refactoring continuation. + */ +CINDEX_LINKAGE +void clang_RefactoringContinuation_dispose( + CXRefactoringContinuation Continuation); + +/** + * @} + */ + +/** + * \defgroup CINDEX_REFACTOR_INDEXER_QUERY Indexer Queries. + * + * @{ + */ + +/** + * \brief The types of indexer queries. + */ +enum CXIndexerQueryKind { + CXIndexerQuery_Unknown = 0, + + /** + * \brief The indexer should find the file that contains/should contain the + * implementation of some declaration. + * A file result is expected. + */ + CXIndexerQuery_Decl_FileThatShouldImplement = 1, + + /** + * \brief The indexer should determine if the some declaration is defined. + * An integer result is expected. + */ + CXIndexerQuery_Decl_IsDefined = 2, +}; + +/** + * \brief Return the kind of the indexer query \p Query. + */ +CINDEX_LINKAGE +enum CXIndexerQueryKind clang_IndexerQuery_getKind(CXIndexerQuery Query); + +/** + * \brief Return the number of cursors that the \p Query has. + */ +CINDEX_LINKAGE +unsigned clang_IndexerQuery_getNumCursors(CXIndexerQuery Query); + +/** + * \brief Return the cursor at the given \p CursorIndex. + */ +CINDEX_LINKAGE +CXCursor clang_IndexerQuery_getCursor(CXIndexerQuery Query, + unsigned CursorIndex); + +/** + * \brief The action that the indexer should take after evaluating the query. + */ +enum CXIndexerQueryAction { + /** + * \brief This result requires no further action. + */ + CXIndexerQueryAction_None = 0, + + /** + * \brief The indexer should run the \c CXRefactoringContinuaton in a + * translation unit that contains this file. + */ + CXIndexerQueryAction_RunContinuationInTUThatHasThisFile = 1, +}; + +/** + * \brief Consumes an integer/boolean query result. + */ +CINDEX_LINKAGE +enum CXIndexerQueryAction +clang_IndexerQuery_consumeIntResult(CXIndexerQuery Query, unsigned CursorIndex, + int Value); + +/** + * \brief Consumes a filename query result. + * + * This function may return + * \c CXIndexerQueryAction_RunContinuationInTUThatHasThisFile which + * should tell the indexer that it has to run the refactoring continuation in + * the TU that contains this file. + */ +CINDEX_LINKAGE +enum CXIndexerQueryAction +clang_IndexerQuery_consumeFileResult(CXIndexerQuery Query, unsigned CursorIndex, + const char *Filename); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* LLVM_CLANG_C_REFACTOR_H */ diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index baf6334064024..00513258c263c 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -46,6 +46,13 @@ class APINotesReader { APINotesReader(const APINotesReader &) = delete; APINotesReader &operator=(const APINotesReader &) = delete; + /// Retrieve the name of the module for which this reader is providing API + /// notes. + llvm::StringRef getModuleName() const; + + /// Retrieve the module options + ModuleOptions getModuleOptions() const; + /// Captures the completed versioned information for a particular part of /// API notes, including both unversioned API notes and each versioned API /// note for that particular entity. diff --git a/clang/include/clang/APINotes/APINotesWriter.h b/clang/include/clang/APINotes/APINotesWriter.h index 3cc16c3d959fa..6a5e59f96869e 100644 --- a/clang/include/clang/APINotes/APINotesWriter.h +++ b/clang/include/clang/APINotes/APINotesWriter.h @@ -130,6 +130,9 @@ class APINotesWriter { /// \param Info Information about this typedef. void addTypedef(std::optional Ctx, llvm::StringRef Name, const TypedefInfo &Info, llvm::VersionTuple SwiftVersion); + + /// Add module options + void addModuleOptions(ModuleOptions opts); }; } // namespace api_notes } // namespace clang diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 9c01978fd49ef..b66d0d1da2192 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -662,6 +662,9 @@ class ObjCMethodInfo : public FunctionInfo { } LLVM_DUMP_METHOD void dump(llvm::raw_ostream &OS); + + void mergePropInfoIntoGetter(const ObjCPropertyInfo &pInfo); + void mergePropInfoIntoSetter(const ObjCPropertyInfo &pInfo); }; inline bool operator==(const ObjCMethodInfo &LHS, const ObjCMethodInfo &RHS) { @@ -890,6 +893,11 @@ struct ObjCSelectorRef { unsigned NumArgs; llvm::ArrayRef Identifiers; }; + +/// Descripts a series of options for a module +struct ModuleOptions { + bool SwiftInferImportAsMember = false; +}; } // namespace api_notes } // namespace clang diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h index 9999a30c51ade..eece37ccd02da 100644 --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_AST_APVALUE_H #define LLVM_CLANG_AST_APVALUE_H +#include "clang/AST/CharUnits.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/APFixedPoint.h" #include "llvm/ADT/APFloat.h" @@ -29,7 +30,6 @@ template class BasicReaderBase; class AddrLabelExpr; class ASTContext; - class CharUnits; class CXXRecordDecl; class Decl; class DiagnosticBuilder; @@ -88,6 +88,60 @@ class DynamicAllocLValue { static constexpr int NumLowBitsAvailable = 3; }; + +/* TO_UPSTREAM(BoundsSafety) ON*/ +/// Symbolic representation of a size forged pointer +class ForgedPtrLValue { + const ValueDecl *Base; + +public: + ForgedPtrLValue() : Base(nullptr) {} + explicit ForgedPtrLValue(const ValueDecl *Base) : Base(Base) {} + explicit operator bool() const { return Base; } + + const ValueDecl *getBaseValueDecl() const { return Base; } + + void *getOpaqueValue() { return const_cast(Base); } + static ForgedPtrLValue getFromOpaqueValue(void *Value) { + ForgedPtrLValue V; + V.Base = reinterpret_cast(Value); + return V; + } +}; + +/// This represents a pointer union that is either DynamicAllocLValue or +/// ForgedPtrLValue. +/// +/// This is a hack to be able to add ForgedPtrLValue as another pointer member +/// to the pointer union of APValue::LValueBase because it already has the +/// maximum number of members for the available low bits, i.e., 2. +/// DynamicAllocLValue and ValueDecl* have 3 low bits available and thus we can +/// use this one more remaining bit to encode ForgedPtrLValue. +class DynamicAllocOrForgedPtrLValue + : public llvm::PointerUnion { + +public: + using BaseTy = llvm::PointerUnion; + + DynamicAllocOrForgedPtrLValue() = default; + DynamicAllocOrForgedPtrLValue(DynamicAllocLValue LV) : BaseTy(LV) {} + DynamicAllocOrForgedPtrLValue(ForgedPtrLValue LV) : BaseTy(LV) {} + + explicit operator bool() const { + PointerUnion pu = *this; + if (isa(pu)) { + return static_cast(cast(pu)); + } + return static_cast(cast(pu)); + } + + static DynamicAllocOrForgedPtrLValue getFromOpaqueValue(void *Value) { + DynamicAllocOrForgedPtrLValue V; + V.Val.setFromOpaqueValue(Value); + return V; + } +}; +/* TO_UPSTREAM(BoundsSafety) OFF*/ } namespace llvm { @@ -113,6 +167,28 @@ template<> struct PointerLikeTypeTraits { static constexpr int NumLowBitsAvailable = clang::DynamicAllocLValue::NumLowBitsAvailable; }; + +/* TO_UPSTREAM(BoundsSafety) ON*/ +template <> struct PointerLikeTypeTraits { + static void *getAsVoidPointer(clang::ForgedPtrLValue V) { + return V.getOpaqueValue(); + } + static clang::ForgedPtrLValue getFromVoidPointer(void *P) { + return clang::ForgedPtrLValue::getFromOpaqueValue(P); + } + static constexpr int NumLowBitsAvailable = 3; +}; + +template <> struct PointerLikeTypeTraits { + static void *getAsVoidPointer(clang::DynamicAllocOrForgedPtrLValue V) { + return V.getOpaqueValue(); + } + static clang::DynamicAllocOrForgedPtrLValue getFromVoidPointer(void *P) { + return clang::DynamicAllocOrForgedPtrLValue::getFromOpaqueValue(P); + } + static constexpr int NumLowBitsAvailable = 2; +}; +/* TO_UPSTREAM(BoundsSafety) OFF*/ } namespace clang { @@ -145,27 +221,53 @@ class APValue { class LValueBase { typedef llvm::PointerUnion + DynamicAllocOrForgedPtrLValue> PtrTy; + template + static constexpr bool IsDAOrForgedV = + std::is_same::value || + std::is_same::value; + + template + using EnableIfDAOrForged = + typename std::enable_if, U>::type; + + template + using EnableIfNotDANorForged = + typename std::enable_if, U>::type; + public: LValueBase() : Local{} {} LValueBase(const ValueDecl *P, unsigned I = 0, unsigned V = 0); LValueBase(const Expr *P, unsigned I = 0, unsigned V = 0); static LValueBase getDynamicAlloc(DynamicAllocLValue LV, QualType Type); static LValueBase getTypeInfo(TypeInfoLValue LV, QualType TypeInfo); + static LValueBase getForgedPtr(ForgedPtrLValue LV, QualType Type); void Profile(llvm::FoldingSetNodeID &ID) const; - template bool is() const { return isa(Ptr); } + template EnableIfNotDANorForged is() const { + return isa(Ptr); + } + + template EnableIfDAOrForged is() const; + + template EnableIfNotDANorForged get() const { + return cast(Ptr); + } - template T get() const { return cast(Ptr); } + template EnableIfDAOrForged get() const; - template T dyn_cast() const { + template EnableIfNotDANorForged dyn_cast() const { return dyn_cast_if_present(Ptr); } + template EnableIfDAOrForged dyn_cast() const; + void *getOpaqueValue() const; + // TO_UPSTREAM(BoundsSafety) + const ValueDecl *getValueDecl() const; bool isNull() const; @@ -175,6 +277,7 @@ class APValue { unsigned getVersion() const; QualType getTypeInfoType() const; QualType getDynamicAllocType() const; + QualType getForgedPtrAsArrayType() const; QualType getType() const; @@ -196,6 +299,8 @@ class APValue { void *TypeInfoType; /// The QualType, if this is a DynamicAllocLValue. void *DynamicAllocType; + /// The QualType, if this is a ForgedPtrLValue. + void *ForgedPtrAsArrayType; }; }; @@ -306,10 +411,45 @@ class APValue { }; struct MemberPointerData; + /* TO_UPSTREAM(BoundsSafety) ON */ + struct LVBase { + APValue::LValueBase Base; + CharUnits Offset; + // BoundsSafety : While the base also holds a corresponding constant array type + // for forged pointer, we still keep track of forged size because the array + // size will be different from the actual forged size if it is not a multiple + // of element type size after a bitcast. The codegen doesn't round up/down + // the bounds to be a type-size multiple, we should keep it the same for + // constant emission. Once __builtin_forge_* has a type as an argument, we + // may consider round down the size with the element type size. + CharUnits ForgedSize; + // While 'Offset' is the offset within the LValue, 'ForgedOffset' is the + // offset of the base pointer of __builtin_unsafe_forge*. For example, in + // the following, + // '__bidi_indexable_unsafe_forge_bidi_indexable(base + N) + M' + // 'N' should be 'ForgedOffset' and 'M' should be 'Offset'. This way, the + // forged pointer itself becomes an LValue starting at base + 'ForgedOffset'. + CharUnits ForgedOffset; + unsigned PathLength; + bool IsNullPtr : 1; + bool IsOnePastTheEnd : 1; + bool IsForgeBidi : 1; + bool IsForgeSingle : 1; + bool IsForgeTerminatedBy : 1; + }; + + struct LVPlaceHolder { + LVBase Base; + LValuePathEntry Path[1]; + }; + /* TO_UPSTREAM(BoundsSafety) OFF */ + // We ensure elsewhere that Data is big enough for LV and MemberPointerData. typedef llvm::AlignedCharArrayUnion DataType; + UnionData, AddrLabelDiffData, + // TO_UPSTREAM(BoundsSafety) + LVPlaceHolder> DataType; static const size_t DataSize = sizeof(DataType); DataType Data; @@ -553,12 +693,33 @@ class APValue { const CharUnits &getLValueOffset() const { return const_cast(this)->getLValueOffset(); } + + /* TO_UPSTREAM(BoundsSafety) ON */ + CharUnits &getLValueForgedSize(); + const CharUnits &getLValueForgedSize() const { + return const_cast(this)->getLValueForgedSize(); + } + + CharUnits &getLValueForgedOffset(); + const CharUnits &getLValueForgedOffset() const { + return const_cast(this)->getLValueForgedOffset(); + } + + CharUnits getUnwrappedLValueOffset() const; + /* TO_UPSTREAM(BoundsSafety) OFF */ + bool isLValueOnePastTheEnd() const; bool hasLValuePath() const; ArrayRef getLValuePath() const; unsigned getLValueCallIndex() const; unsigned getLValueVersion() const; bool isNullPointer() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + bool isLValueForgeBidi() const; + bool isLValueForgeSingle() const; + bool isLValueForgeTerminatedBy() const; + bool isLValueForge() const; + /* TO_UPSTREAM(BoundsSafety) OFF */ APValue &getVectorElt(unsigned I) { assert(isVector() && "Invalid accessor"); @@ -694,6 +855,12 @@ class APValue { ((AddrLabelDiffData *)(char *)&Data)->RHSExpr = RHSExpr; } + /* TO_UPSTREAM(BoundsSafety) ON */ + void setLValueForgedBidi(const CharUnits &Size, const CharUnits &Offset); + void setLValueForgedSingle(const CharUnits &Offset); + void setLValueForgedTerminatedBy(const CharUnits &Offset); + /* TO_UPSTREAM(BoundsSafety) OFF */ + private: void DestroyDataAndMakeUninit(); void MakeInt() { diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h index 447f2592d2359..6cf4504dcfa60 100644 --- a/clang/include/clang/AST/ASTConsumer.h +++ b/clang/include/clang/AST/ASTConsumer.h @@ -27,6 +27,7 @@ namespace clang { class VarDecl; class FunctionDecl; class ImportDecl; + class TargetInfo; /// ASTConsumer - This is an abstract interface that should be implemented by /// clients that read ASTs. This abstraction layer allows the client to be @@ -47,6 +48,14 @@ class ASTConsumer { /// ASTContext. virtual void Initialize(ASTContext &Context) {} + /// Initialize - This is called to initialize the consumer, providing the + /// ASTContext. 'CodeGenTargetInfo' specifies the code-generation configuration + /// for this compilation instance, which may differ from the one carried + /// by the Context itself only in the OS Version number - + /// for example when type-checking must be performed against an epoch OS version + /// while code-generation must run according to the user-specified OS version. + virtual void Initialize(ASTContext &Context, const TargetInfo &CodeGenTargetInfo) {} + /// HandleTopLevelDecl - Handle the specified top-level declaration. This is /// called by the parser to process every top-level Decl*. /// diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 3c78833a3f069..31a1c4c0fc05d 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -26,6 +26,7 @@ #include "clang/AST/SYCLKernelInfo.h" #include "clang/AST/TemplateName.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/DenseMap.h" @@ -262,6 +263,14 @@ class ASTContext : public RefCountedBase { mutable llvm::FoldingSet CountAttributedTypes; + /* TO_UPSTREAM(BoundsSafety) ON */ + mutable llvm::FoldingSet + DynamicRangePointerTypes; + + mutable llvm::ContextualFoldingSet + ValueTerminatedTypes; + /* TO_UPSTREAM(BoundsSafety) OFF */ + mutable llvm::FoldingSet QualifiedTemplateNames; mutable llvm::FoldingSet DependentTemplateNames; mutable llvm::FoldingSet @@ -500,6 +509,24 @@ class ASTContext : public RefCountedBase { ASTContext &this_() { return *this; } + mutable std::optional ObjCMsgSendUsageFile; + llvm::SmallVector ObjCMsgSendUsage; + +public: + /// Check whether env variable CLANG_COMPILER_OBJC_MESSAGE_TRACE_PATH is set. + /// If it is set, assign the value to ObjCMsgSendUsageFile. + bool isObjCMsgSendUsageFileSpecified() const; + + /// Return the file name stored in ObjCMsgSendUsageFile if it has a value, + /// return an empty string otherwise. + std::string getObjCMsgSendUsageFilename() const; + + /// Record an ObjC method. + void recordObjCMsgSendUsage(const ObjCMethodDecl *Method); + + /// Write the collected ObjC method tracing information to a file. + void writeObjCMsgSendUsages(const std::string &Filename); + public: /// A type synonym for the TemplateOrInstantiation mapping. using TemplateOrSpecializationInfo = @@ -795,6 +822,36 @@ class ASTContext : public RefCountedBase { return DiagAllocator; } + struct AvailabilityDomainInfo { + FeatureAvailKind Kind = FeatureAvailKind::None; + clang::Decl *Decl = nullptr; + ImplicitCastExpr *Call = nullptr; + bool isInvalid() const { return Kind == FeatureAvailKind::None; } + }; + + std::map AvailabilityDomainMap; + + void addAvailabilityDomainMap(StringRef Name, VarDecl *VD) { + AvailabilityDomainMap[Name] = VD; + } + + std::pair + checkNewFeatureAvailability(Decl *D, StringRef DomainName, bool Unavailable); + + bool hasFeatureAvailabilityAttr(const Decl *D) const; + + // Retrieve availability domain information for a feature. + AvailabilityDomainInfo getFeatureAvailInfo(StringRef FeatureName) const; + + // Retrieve feature name and availability domain information on a decl. If the + // decl doesn't have attribute availability_domain on it, the name will be + // empty and AvailabilityDomainInfo::Kind will be set to + // FeatureAvailKind::None. + std::pair + getFeatureAvailInfo(Decl *D) const; + + bool hasUnavailableFeature(const Decl *D) const; + const TargetInfo &getTargetInfo() const { return *Target; } const TargetInfo *getAuxTargetInfo() const { return AuxTarget; } @@ -1454,9 +1511,12 @@ class ASTContext : public RefCountedBase { /// Return the uniqued reference to the type for a pointer to /// the specified type. - QualType getPointerType(QualType T) const; - CanQualType getPointerType(CanQualType T) const { - return CanQualType::CreateUnsafe(getPointerType((QualType) T)); + QualType getPointerType(QualType T, BoundsSafetyPointerAttributes A = + BoundsSafetyPointerAttributes()) const; + CanQualType getPointerType( + CanQualType T, + BoundsSafetyPointerAttributes A = BoundsSafetyPointerAttributes()) const { + return CanQualType::CreateUnsafe(getPointerType((QualType)T, A)); } QualType @@ -1464,6 +1524,42 @@ class ASTContext : public RefCountedBase { bool OrNull, ArrayRef DependentDecls) const; + /* TO_UPSTREAM(BoundsSafety) ON */ + QualType getDynamicRangePointerType( + QualType T, Expr *StartPtr, Expr *EndPtr, + ArrayRef StartPtrDecls, + ArrayRef EndPtrDecls) const; + + QualType getValueTerminatedType(QualType T, Expr *TerminatorExpr) const; + + /// Return a new pointer type with the -fbounds-safety pointer attribute with + /// preserving existing AttributedTypes and qualifiers. + QualType getBoundsSafetyPointerType(QualType PointerTy, + BoundsSafetyPointerAttributes); + + /// Return a result type of merging -fbounds-safety pointer attributes of \p SrcTy + /// to \p DstTy, while preserving existing AttributedTypes and qualifiers of + /// \p DstTy. The type merging is performed recursively in nested pointers. + /// The caller should provide \p MergeFunctor to create a merged pointer type + /// using the recursively merged pointee type. + /// mergeBoundsSafetyPointerTypes removes any AttributedType(s) from \p + /// DstTy, calls \p MergeFunctor to merge the attributes at each level, and + /// then reapplies the AttributedType(s) to the merged type. \p OrigDstTy is + /// the same as \p DstTy but without dropping the AttributedType(s). This + /// allows us to check any AttributedType(s) in \p MergeFunctor in order to + /// make decision about the merged type. + QualType mergeBoundsSafetyPointerTypes( + QualType DstTy, QualType SrcTy, + std::function &MergeFunctor, + QualType OrigDstTy = QualType()); + + QualType getBoundsSafetyAutoPointerType(QualType T, + BoundsSafetyPointerAttributes AbiFAttr, + bool ShouldAutoBound); + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Return the uniqued reference to a type adjusted from the original /// type to a new type. QualType getAdjustedType(QualType Orig, QualType New) const; @@ -2516,6 +2612,14 @@ class ASTContext : public RefCountedBase { uint64_t getTypeSize(QualType T) const { return getTypeInfo(T).Width; } uint64_t getTypeSize(const Type *T) const { return getTypeInfo(T).Width; } + /*TO_UPSTREAM(BoundsSafety) ON*/ + uint64_t getTypeSizeOrNull(QualType T) const { + if (T->isIncompleteOrSizelessType()) + return 0; + return getTypeSize(T); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + /// Return the size of the character type, in bits. uint64_t getCharWidth() const { return getTypeSize(CharTy); @@ -2762,6 +2866,37 @@ class ASTContext : public RefCountedBase { return getCanonicalType(T1) == getCanonicalType(T2); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// These enum values (other than CanMerge) are aligned with the options for + /// the third parameter in + /// diag::err_cond_expr_nested_bounds_safety_pointer_attribute_mismatch + enum BoundsSafePointerTypeMergeKind { + BSPTMK_NestedBoundsMismatch, + BSPTMK_FunctionTypeMismatch, + BSPTMK_TerminatedByMismatch, + BSPTMK_CanMerge, + }; + /// Given two pointer types that can be unified without losing type safety, + /// check whether they can be merged without losing bounds safety. + /// In practice this means checking whether their inner pointer type bounds + /// sugar nodes match (if any exist). Non-nested pointer types can always be + /// unified, potentially requiring dynamic checks. + BoundsSafePointerTypeMergeKind canMergeTypeBounds(QualType LHSTy, + QualType RHSTy) const; + + /// Given two pointer types, check whether either of them if is a + /// ValueTerminatedType, and if so, that the other is a ValueTerminatedType + /// with the same terminator. Does not check pointee type! + BoundsSafePointerTypeMergeKind + checkTerminatedByMismatch(QualType LHSTy, QualType RHSTy) const; + BoundsSafePointerTypeMergeKind canMergeInnerTypeBounds(QualType LHSTy, + QualType RHSTy) const; + BoundsSafePointerTypeMergeKind + canMergeFunctionTypeBounds(const FunctionProtoType *LHSTy, + const FunctionProtoType *RHSTy) const; + + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Determine whether the given expressions \p X and \p Y are equivalent. bool hasSameExpr(const Expr *X, const Expr *Y) const; @@ -2839,6 +2974,16 @@ class ASTContext : public RefCountedBase { /// Determine if two types are similar, ignoring only CVR qualifiers. bool hasCvrSimilarType(QualType T1, QualType T2); + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Determine if two types have same -fbounds-safety pointer layouts, recursively. + bool hasSameBoundsSafetyPointerLayout(QualType T1, QualType T2); + + /// Determine if two types have compatible -fbounds-safety pointer layouts, + /// recursively. + bool hasCompatibleBoundsSafetyPointerLayout(QualType T1, QualType T2, + bool ExactCheck = false); + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Retrieves the "canonical" nested name specifier for a /// given nested name specifier. /// diff --git a/clang/include/clang/AST/ASTMutationListener.h b/clang/include/clang/AST/ASTMutationListener.h index 2c4ec2ce67f36..b8bd5403df477 100644 --- a/clang/include/clang/AST/ASTMutationListener.h +++ b/clang/include/clang/AST/ASTMutationListener.h @@ -149,6 +149,17 @@ class ASTMutationListener { virtual void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) {} + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// An attribute to a Decl to write in a separate record because the attribute + /// and the Decl create a cycle during deserialization. + /// + /// \param Attr The attribute to the Decl + /// + /// \param Record The Decl owns the attribute + virtual void LazyAttributeToDecl(const Attr *Attr, + const Decl *D) {} + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// The parser find the named module declaration. virtual void EnteringModulePurview() {} diff --git a/clang/include/clang/AST/AbstractBasicReader.h b/clang/include/clang/AST/AbstractBasicReader.h index 5ab438715ecf7..273cd9c4f7498 100644 --- a/clang/include/clang/AST/AbstractBasicReader.h +++ b/clang/include/clang/AST/AbstractBasicReader.h @@ -219,6 +219,13 @@ class DataStreamBasicReader : public BasicReaderBase { return Qualifiers::fromOpaqueValue(value); } + /* TO_UPSTREAM(BoundsSafety) ON */ + BoundsSafetyPointerAttributes readBoundsSafetyPointerAttributes() { + uint32_t value = asImpl().readUInt32(); + return BoundsSafetyPointerAttributes::fromOpaqueValue(value); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + FunctionProtoType::ExceptionSpecInfo readExceptionSpecInfo(llvm::SmallVectorImpl &buffer) { FunctionProtoType::ExceptionSpecInfo esi; diff --git a/clang/include/clang/AST/AbstractBasicWriter.h b/clang/include/clang/AST/AbstractBasicWriter.h index f65d94abc2ff1..4572c813621a9 100644 --- a/clang/include/clang/AST/AbstractBasicWriter.h +++ b/clang/include/clang/AST/AbstractBasicWriter.h @@ -201,6 +201,12 @@ class DataStreamBasicWriter : public BasicWriterBase { asImpl().writeUInt64(value.getAsOpaqueValue()); } + /* TO_UPSTREAM(BoundsSafety) ON */ + void writeBoundsSafetyPointerAttributes(BoundsSafetyPointerAttributes value) { + asImpl().writeUInt32(value.getAsOpaqueValue()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void writeExceptionSpecInfo( const FunctionProtoType::ExceptionSpecInfo &esi) { asImpl().writeUInt32(uint32_t(esi.Type)); diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index 994f236337b99..693c7a2be7c7d 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -24,6 +24,7 @@ #include "clang/Basic/OpenMPKinds.h" #include "clang/Basic/Sanitizers.h" #include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/PointerEmbeddedInt.h" #include "clang/Support/Compiler.h" #include "llvm/Frontend/HLSL/HLSLResource.h" #include "llvm/Support/CodeGen.h" diff --git a/clang/include/clang/AST/AttrIterator.h b/clang/include/clang/AST/AttrIterator.h index 2f39c144dc160..decf1a3ebe395 100644 --- a/clang/include/clang/AST/AttrIterator.h +++ b/clang/include/clang/AST/AttrIterator.h @@ -97,6 +97,8 @@ class specific_attr_iterator { specific_attr_iterator Right) { return !(Left == Right); } + + Iterator getCurrent() const { return Current; } }; template diff --git a/clang/include/clang/AST/Availability.h b/clang/include/clang/AST/Availability.h index 60ca1383f0a44..520a734ec4c28 100644 --- a/clang/include/clang/AST/Availability.h +++ b/clang/include/clang/AST/Availability.h @@ -37,6 +37,8 @@ class AvailabilitySpec { /// Name of the platform that Version corresponds to. StringRef Platform; + StringRef DomainName; + SourceLocation BeginLoc, EndLoc; public: @@ -45,6 +47,9 @@ class AvailabilitySpec { : Version(Version), Platform(Platform), BeginLoc(BeginLoc), EndLoc(EndLoc) {} + AvailabilitySpec(StringRef DomainName, SourceLocation Loc) + : DomainName(DomainName), BeginLoc(Loc), EndLoc(Loc) {} + /// This constructor is used when representing the '*' case. AvailabilitySpec(SourceLocation StarLoc) : BeginLoc(StarLoc), EndLoc(StarLoc) {} @@ -55,7 +60,12 @@ class AvailabilitySpec { SourceLocation getEndLoc() const { return EndLoc; } /// Returns true when this represents the '*' case. - bool isOtherPlatformSpec() const { return Version.empty(); } + bool isOtherPlatformSpec() const { + return Version.empty() && DomainName.empty(); + } + + bool isDomainName() const { return !DomainName.empty(); } + StringRef getDomainName() const { return DomainName; } }; class Decl; diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index e96275e5f2e07..92f96d01999bc 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -79,6 +79,15 @@ class LambdaExpr; class CXXUnresolvedConstructExpr; class CXXDependentScopeMemberExpr; class MaterializeTemporaryExpr; +/* TO_UPSTREAM(BoundsSafety) ON */ +class MaterializeSequenceExpr; +class PredefinedBoundsCheckExpr; +class BoundsCheckExpr; +class AssumptionExpr; +class BoundsSafetyPointerPromotionExpr; +class GetBoundExpr; +class ForgePtrExpr; +/* TO_UPSTREAM(BoundsSafety) OFF */ class CXXFoldExpr; class CXXParenListInitExpr; class TypeTraitExpr; @@ -178,6 +187,16 @@ ExprDependence computeDependence(TypeTraitExpr *E); ExprDependence computeDependence(ConceptSpecializationExpr *E, bool ValueDependent); +/* TO_UPSTREAM(BoundsSafety) ON */ +ExprDependence computeDependence(MaterializeSequenceExpr *E); +ExprDependence computeDependence(PredefinedBoundsCheckExpr *E); +ExprDependence computeDependence(BoundsCheckExpr *E); +ExprDependence computeDependence(AssumptionExpr *E); +ExprDependence computeDependence(BoundsSafetyPointerPromotionExpr *E); +ExprDependence computeDependence(GetBoundExpr *E); +ExprDependence computeDependence(ForgePtrExpr *E); +/* TO_UPSTREAM(BoundsSafety) OFF */ + ExprDependence computeDependence(SYCLUniqueStableNameExpr *E); ExprDependence computeDependence(PredefinedExpr *E); ExprDependence computeDependence(CallExpr *E, llvm::ArrayRef PreArgs); diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 3faf63e395a08..b34e60c995110 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -709,6 +709,14 @@ class ValueDecl : public NamedDecl { /// can be captured. bool isInitCapture() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Whether this decl is a dependent parameter referred to by the return type + /// that is a bounds-attributed type. + bool isDependentParamOfReturnType( + const BoundsAttributedType **RetType = nullptr, + const TypeCoupledDeclRefInfo **Info = nullptr) const; + /* TO_UPSTREAM(BoundsSafety) OFF */ + // If this is a VarDecl, or a BindindDecl with an // associated decomposed VarDecl, return that VarDecl. VarDecl *getPotentiallyDecomposedVarDecl(); @@ -4436,6 +4444,9 @@ class RecordDecl : public TagDecl { /// leaks. bool isOrContainsUnion() const; + // TO_UPSTREAM(BoundsSafety) + bool isParentStructOf(const Decl *D) const; + // Iterator access to field members. The field iterator only visits // the non-static data members of this class, ignoring any static // data members, functions, constructors, destructors, etc. @@ -5220,6 +5231,12 @@ void Redeclarable::setPreviousDecl(decl_type *PrevDecl) { cast(static_cast(this))->isLinkageValid()); } +/* TO_UPSTREAM(BoundsSafety) ON */ +// A BoundsSafety helper function. +/// Return `true` if \p D is const qualified or attributed as immutable. +bool IsConstOrLateConst(const Decl *D); +/* TO_UPSTREAM(BoundsSafety) OFF */ + // Inline function definitions. /// Check if the given decl is complete. diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index 4663603f79754..5d5d6e147dda0 100644 --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -881,6 +881,11 @@ class ObjCPropertyDecl : public NamedDecl { return Assign; } + /// Return true if this property has an explicitly specified getter name. + bool hasExplicitGetterName() const { + return (PropertyAttributes & ObjCPropertyAttribute::kind_getter); + } + Selector getGetterName() const { return GetterName; } SourceLocation getGetterNameLoc() const { return GetterNameLoc; } @@ -889,6 +894,11 @@ class ObjCPropertyDecl : public NamedDecl { GetterNameLoc = Loc; } + /// Return true if this property has an explicitly specified setter name. + bool hasExplicitSetterName() const { + return (PropertyAttributes & ObjCPropertyAttribute::kind_setter); + } + Selector getSetterName() const { return SetterName; } SourceLocation getSetterNameLoc() const { return SetterNameLoc; } @@ -1239,7 +1249,8 @@ class ObjCInterfaceDecl : public ObjCContainerDecl /// which will be NULL if this class has not yet been defined. /// /// The bit indicates when we don't need to check for out-of-date - /// declarations. It will be set unless modules are enabled. + /// declarations. It will be set unless there is an ExternalASTSource that + /// could provide a definition. llvm::PointerIntPair Data; ObjCInterfaceDecl(const ASTContext &C, DeclContext *DC, SourceLocation AtLoc, @@ -1528,7 +1539,7 @@ class ObjCInterfaceDecl : public ObjCContainerDecl // If the name of this class is out-of-date, bring it up-to-date, which // might bring in a definition. // Note: a null value indicates that we don't have a definition and that - // modules are enabled. + // there is a ExternalASTSource that could provide a definition. if (!Data.getOpaqueValue()) getMostRecentDecl(); @@ -2100,7 +2111,8 @@ class ObjCProtocolDecl : public ObjCContainerDecl, /// which will be NULL if this class has not yet been defined. /// /// The bit indicates when we don't need to check for out-of-date - /// declarations. It will be set unless modules are enabled. + /// declarations. It will be set unless there is an ExternalASTSource that + /// could provide a definition. llvm::PointerIntPair Data; ObjCProtocolDecl(ASTContext &C, DeclContext *DC, IdentifierInfo *Id, @@ -2238,7 +2250,7 @@ class ObjCProtocolDecl : public ObjCContainerDecl, // If the name of this protocol is out-of-date, bring it up-to-date, which // might bring in a definition. // Note: a null value indicates that we don't have a definition and that - // modules are enabled. + // there is a ExternalASTSource that could provide a definition. if (!Data.getOpaqueValue()) getMostRecentDecl(); @@ -2774,17 +2786,25 @@ raw_ostream &operator<<(raw_ostream &OS, const ObjCImplementationDecl &ID); class ObjCCompatibleAliasDecl : public NamedDecl { /// Class that this is an alias of. ObjCInterfaceDecl *AliasedClass; + /// The location of the name of the referenced class. + SourceLocation AliasedClassLoc; + /// The location of the '@'. + SourceLocation AtLoc; - ObjCCompatibleAliasDecl(DeclContext *DC, SourceLocation L, IdentifierInfo *Id, - ObjCInterfaceDecl* aliasedClass) - : NamedDecl(ObjCCompatibleAlias, DC, L, Id), AliasedClass(aliasedClass) {} + ObjCCompatibleAliasDecl(DeclContext *DC, SourceLocation NameLoc, + IdentifierInfo *Id, ObjCInterfaceDecl *AliasedClass, + SourceLocation AliasedClassLoc, SourceLocation AtLoc) + : NamedDecl(ObjCCompatibleAlias, DC, NameLoc, Id), + AliasedClass(AliasedClass), AliasedClassLoc(AliasedClassLoc), + AtLoc(AtLoc) {} void anchor() override; public: - static ObjCCompatibleAliasDecl *Create(ASTContext &C, DeclContext *DC, - SourceLocation L, IdentifierInfo *Id, - ObjCInterfaceDecl* aliasedClass); + static ObjCCompatibleAliasDecl * + Create(ASTContext &C, DeclContext *DC, SourceLocation NameLoc, + IdentifierInfo *Id, ObjCInterfaceDecl *AliasedClass, + SourceLocation AliasedClassLoc, SourceLocation AtLoc); static ObjCCompatibleAliasDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID); @@ -2793,6 +2813,17 @@ class ObjCCompatibleAliasDecl : public NamedDecl { ObjCInterfaceDecl *getClassInterface() { return AliasedClass; } void setClassInterface(ObjCInterfaceDecl *D) { AliasedClass = D; } + SourceLocation getClassInterfaceLoc() const { return AliasedClassLoc; } + + void setClassInterfaceLoc(SourceLocation Loc) { AliasedClassLoc = Loc; } + + SourceLocation getAtLoc() const { return AtLoc; } + void setAtLoc(SourceLocation Loc) { AtLoc = Loc; } + + SourceRange getSourceRange() const override LLVM_READONLY { + return SourceRange(AtLoc, AtLoc); + } + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == ObjCCompatibleAlias; } }; diff --git a/clang/include/clang/AST/DependentDiagnostic.h b/clang/include/clang/AST/DependentDiagnostic.h index cadf970620041..4196f6a5db731 100644 --- a/clang/include/clang/AST/DependentDiagnostic.h +++ b/clang/include/clang/AST/DependentDiagnostic.h @@ -149,9 +149,11 @@ class DeclContext::ddiag_iterator { return tmp; } +#ifndef __swift__ bool operator==(ddiag_iterator Other) const { return Ptr == Other.Ptr; } +#endif bool operator!=(ddiag_iterator Other) const { return Ptr != Other.Ptr; diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index a83320a7ddec2..864c0dc708131 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -31,6 +31,7 @@ #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" @@ -262,6 +263,12 @@ class Expr : public ValueStmt { bool isUnusedResultAWarning(const Expr *&WarnExpr, SourceLocation &Loc, SourceRange &R1, SourceRange &R2, ASTContext &Ctx) const; + /* TO_UPSTREAM(BoundsSafety) ON */ + bool isUnusedResultAWarning(const Expr *&WarnExpr, SourceLocation &Loc, + SourceRange &R1, SourceRange &R2, + ASTContext &Ctx, + llvm::SmallPtrSetImpl &B) const; + /* TO_UPSTREAM(BoundsSafety) OFF */ /// isLValue - True if this expression is an "l-value" according to /// the rules of the current language. C and C++ give somewhat @@ -782,6 +789,18 @@ class Expr : public ValueStmt { /// strlen, false otherwise. bool tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const; + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// If the current Expr is an array or a pointer to an array element, this + /// will try to statically determine the value of the last element of that + /// array. + bool tryEvaluateTerminatorElement(EvalResult &Result, + const ASTContext &Ctx) const; + + bool EvaluateAsTerminatorValue( + llvm::APSInt &Result, const ASTContext &Ctx, + SideEffectsKind AllowSideEffects = SE_NoSideEffects) const; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + bool EvaluateCharRangeAsString(std::string &Result, const Expr *SizeExpression, const Expr *PtrExpression, ASTContext &Ctx, @@ -841,6 +860,11 @@ class Expr : public ValueStmt { ASTContext &Ctx, NullPointerConstantValueDependence NPC) const; + /* TO_UPSTREAM(BoundsSafety) ON*/ + NullPointerConstantKind isNullPointerConstantIgnoreCastsAndOVEs( + ASTContext &Ctx, NullPointerConstantValueDependence NPC) const; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// isOBJCGCCandidate - Return true if this expression may be used in a read/ /// write barrier. bool isOBJCGCCandidate(ASTContext &Ctx) const; @@ -923,6 +947,8 @@ class Expr : public ValueStmt { /// * What IgnoreImpCasts() skips /// * MaterializeTemporaryExpr /// * SubstNonTypeTemplateParmExpr + // TO_UPSTREAM(BoundsSafety) + Expr *IgnoreParenImpCasts(llvm::SmallPtrSetImpl &BoundValues) LLVM_READONLY; Expr *IgnoreParenImpCasts() LLVM_READONLY; const Expr *IgnoreParenImpCasts() const { return const_cast(this)->IgnoreParenImpCasts(); @@ -1179,6 +1205,13 @@ class OpaqueValueExpr : public Expr { Expr *SourceExpr; public: + /*TO_UPSTREAM(BoundsSafety) ON*/ + static OpaqueValueExpr *Wrap(const ASTContext &Context, Expr *E); + static OpaqueValueExpr *EnsureWrapped( + const ASTContext &Context, Expr *E, + SmallVectorImpl &OVEs); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + OpaqueValueExpr(SourceLocation Loc, QualType T, ExprValueKind VK, ExprObjectKind OK = OK_Ordinary, Expr *SourceExpr = nullptr) : Expr(OpaqueValueExprClass, T, VK, OK), SourceExpr(SourceExpr) { @@ -3892,6 +3925,832 @@ class CStyleCastExpr final friend class CastExpr; }; +/* TO_UPSTREAM(BoundsSafety) ON */ +// Attaches one or more assumptions to an expression. The assumptions are each +// codegen'd like they were the parameter of `__builtin_assume`. +class AssumptionExpr final + : public Expr, + private llvm::TrailingObjects { + friend TrailingObjects; + friend class ASTStmtWriter; + + Expr **getTrailingExprs() { + return const_cast(getTrailingObjects()); + } + + Expr *const *getTrailingExprs() const { + return getTrailingObjects(); + } + + AssumptionExpr(EmptyShell Empty, unsigned NumExprs) + : Expr(AssumptionExprClass, Empty) { + AssumptionExprBits.NumExprs = NumExprs; + } + + AssumptionExpr(Expr *ResultExpr, llvm::ArrayRef Assumptions); + +public: + static bool classof(const Stmt *T) { + return T->getStmtClass() == AssumptionExprClass; + } + + static AssumptionExpr *Create(const ASTContext &Ctx, Expr *Result, + llvm::ArrayRef Assumptions); + + static AssumptionExpr *CreateEmpty(const ASTContext &Ctx, unsigned NumExprs); + + Expr *getWrappedExpr() { return getTrailingExprs()[0]; } + const Expr *getWrappedExpr() const { return getTrailingExprs()[0]; } + + void setWrappedExpr(Expr *E) { + if (E) { + getTrailingExprs()[0] = E; + setType(E->getType()); + setValueKind(E->getValueKind()); + setObjectKind(E->getObjectKind()); + } else { + setType(QualType()); + } + } + + unsigned getNumSubExprs() const { return AssumptionExprBits.NumExprs; } + Expr *getSubExpr(unsigned I) { return getTrailingExprs()[I]; } + const Expr *getSubExpr(unsigned I) const { return getTrailingExprs()[I]; } + void setSubExpr(unsigned I, Expr *E) { + if (I == 0) + setWrappedExpr(E); + else + getTrailingExprs()[I] = E; + } + + unsigned getNumAssumptions() const { return getNumSubExprs() - 1; } + Expr *getAssumption(unsigned I) { return getSubExpr(I + 1); } + const Expr *getAssumption(unsigned I) const { return getSubExpr(I + 1); } + void setAssumption(unsigned I, Expr *E) { setSubExpr(I + 1, E); } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getWrappedExpr()->getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { + return getWrappedExpr()->getEndLoc(); + } + + typedef Expr * const * assumption_iterator; + typedef const Expr * const * const_assumption_iterator; + + assumption_iterator assumptions_begin() { + return reinterpret_cast (getTrailingExprs()) + 1; + } + const_assumption_iterator assumptions_begin() const { + return reinterpret_cast (getTrailingExprs()) + 1; + } + assumption_iterator assumptions_end() { + return assumptions_begin() + getNumAssumptions(); + } + const_assumption_iterator assumptions_end() const { + return assumptions_begin() + getNumAssumptions(); + } + + llvm::iterator_range assumptions() { + return llvm::make_range(assumptions_begin(), assumptions_end()); + } + llvm::iterator_range assumptions() const { + return llvm::make_range(assumptions_begin(), assumptions_end()); + } + + child_range children() { + Stmt **begin = reinterpret_cast(getTrailingExprs()); + return child_range(begin, begin + getNumSubExprs()); + } + + const_child_range children() const { + Stmt *const *begin = reinterpret_cast(getTrailingExprs()); + return const_child_range(begin, begin + getNumSubExprs()); + } +}; + +// Implicitly promote a pointer with external bounds to a wide pointer. Although +// this expression doesn't belong to the CastExpr family, it should usually be +// treated as such. +class BoundsSafetyPointerPromotionExpr final + : public Expr, + private llvm::TrailingObjects { + friend TrailingObjects; + friend class ASTStmtWriter; + + BoundsSafetyPointerPromotionExpr(EmptyShell Empty) + : Expr(BoundsSafetyPointerPromotionExprClass, Empty) { + setPointer(nullptr); + setNullCheck(false); + } + + BoundsSafetyPointerPromotionExpr(QualType QT, Expr *Ptr, Expr *UpperBound, + Expr *LowerBound, bool NullCheck = false) + : Expr(BoundsSafetyPointerPromotionExprClass, QT, VK_PRValue, OK_Ordinary) { + setPointer(Ptr); + setNullCheck(NullCheck); + if (Stmt **lowerPtr = getLowerBoundPtr()) + *lowerPtr = LowerBound; + if (Stmt **upperPtr = getUpperBoundPtr()) + *upperPtr = UpperBound; + setDependence(computeDependence(this)); + } + + BoundsSafetyPointerPromotionExpr *unconst() const { + return const_cast(this); + } + + Stmt **getPointerPtr(); + Stmt **getLowerBoundPtr(); + Stmt **getUpperBoundPtr(); + +public: + static BoundsSafetyPointerPromotionExpr * + Create(const ASTContext &Context, QualType QT, Expr *Ptr, Expr *UpperBound, + Expr *LowerBound = nullptr, bool NullCheck = false); + + static BoundsSafetyPointerPromotionExpr *CreateEmpty(const ASTContext &Context, + unsigned SubExprCount); + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getPointer()->getBeginLoc(); + } + + SourceLocation getEndLoc() const LLVM_READONLY { + return getPointer()->getEndLoc(); + } + + Expr *getSubExprAsWritten(); + Expr *getPointer() { return cast_or_null(*getPointerPtr()); } + Expr *getLowerBound() { return cast_or_null(*getLowerBoundPtr()); } + Expr *getUpperBound() { return cast_or_null(*getUpperBoundPtr()); } + const Expr *getSubExpr() const { return getPointer(); } + const Expr *getPointer() const { return unconst()->getPointer(); } + const Expr *getLowerBound() const { return unconst()->getLowerBound(); } + const Expr *getUpperBound() const { return unconst()->getUpperBound(); } + bool getNullCheck() const { + return BoundsSafetyPointerPromotionExprBits.NullCheck; + } + + Expr *getSubExpr() { return getPointer(); } + const Expr *getSubExprAsWritten() const { + return unconst()->getSubExprAsWritten(); + } + + void setPointer(Expr *newValue) { *getPointerPtr() = newValue; } + void setLowerBound(Expr *newValue) { *getLowerBoundPtr() = newValue; } + void setUpperBound(Expr *newValue) { *getUpperBoundPtr() = newValue; } + void setNullCheck(bool NullCheck) { + BoundsSafetyPointerPromotionExprBits.NullCheck = NullCheck; + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == BoundsSafetyPointerPromotionExprClass; + } + + unsigned getNumChildren() const { + return unconst()->getLowerBoundPtr() ? 3 : 2; + } + + child_range children() { + return child_range(getPointerPtr(), getPointerPtr() + getNumChildren()); + } + + const_child_range children() const { + auto Range = unconst()->children(); + return const_child_range(Range.begin(), Range.end()); + } +}; + +/// __unsafe_forge_bidi_indexable(addr, size) +/// __unsafe_forge_single(addr) +/// __unsafe_forge_terminated_by(addr, terminator) +class ForgePtrExpr final : public Expr { + enum { ADDR, SIZE, TERMINATOR, NUM_SUBEXPRS }; + + Stmt *SubExprs[NUM_SUBEXPRS]; + + SourceLocation KWLoc; + SourceLocation RParenLoc; + +public: + ForgePtrExpr(QualType T, ExprValueKind VK, Expr *AddrExpr, Expr *SizeExpr, + Expr *TermExpr, SourceLocation KWLoc, SourceLocation RParenLoc) + : Expr(ForgePtrExprClass, T, VK, OK_Ordinary), KWLoc(KWLoc), + RParenLoc(RParenLoc) { + SubExprs[ADDR] = AddrExpr; + SubExprs[SIZE] = SizeExpr; + SubExprs[TERMINATOR] = TermExpr; + setDependence(computeDependence(this)); + } + + explicit ForgePtrExpr(EmptyShell Empty) + : Expr(ForgePtrExprClass, Empty) { + SubExprs[ADDR] = nullptr; + SubExprs[SIZE] = nullptr; + SubExprs[TERMINATOR] = nullptr; + } + + bool ForgesBidiIndexablePointer() const { + return !ForgesSinglePointer() && !ForgesTerminatedByPointer(); + } + bool ForgesSinglePointer() const { + auto BaseFA = getType()->getAs()->getPointerAttributes(); + return !BaseFA.hasUpperBound() && !ForgesTerminatedByPointer(); + } + bool ForgesTerminatedByPointer() const { + return getType()->isValueTerminatedType(); + } + + Expr *getAddr() const { return cast(SubExprs[ADDR]); } + void setAddr(Expr *E) { SubExprs[ADDR] = E; } + Expr *getSize() const { return cast_or_null(SubExprs[SIZE]); } + void setSize(Expr *E) { SubExprs[SIZE] = E; } + Expr *getTerminator() const { + return cast_or_null(SubExprs[TERMINATOR]); + } + void setTerminator(Expr *E) { SubExprs[TERMINATOR] = E; } + + SourceLocation getBeginLoc() const LLVM_READONLY { return KWLoc; } + void setBeginLoc(SourceLocation Loc) { KWLoc = Loc; } + SourceLocation getEndLoc() const LLVM_READONLY { return RParenLoc; } + void setEndLoc(SourceLocation Loc) { RParenLoc = Loc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ForgePtrExprClass; + } + + // Iterators + child_range children() { + return child_range(&SubExprs[0], &SubExprs[0] + NUM_SUBEXPRS); + } + const_child_range children() const { + return const_child_range(&SubExprs[0], &SubExprs[0] + NUM_SUBEXPRS); + } +}; + +/// GetBoundExpr - get the lower or upper bound of a pointer expression. +/// This would be a builtin if it wasn't easier to create an entirely new +/// expression subclass than to instantiate a call to a builtin from Sema. +/// Most GetBoundExpr are synthetic, and their source locations default to +class GetBoundExpr final : public Expr { +public: + enum BoundKind { BK_Lower, BK_Upper }; + +private: + Stmt *SubExpr; + BoundKind Kind; + SourceLocation BuiltinLoc, RParenLoc; + +public: + GetBoundExpr(SourceLocation BuiltinLoc, SourceLocation RParenLoc, + Expr *SubExpr, BoundKind Kind, QualType ResultType) + : Expr(GetBoundExprClass, ResultType, VK_PRValue, OK_Ordinary), + SubExpr(SubExpr), Kind(Kind), BuiltinLoc(BuiltinLoc), RParenLoc(RParenLoc) + { + setDependence(computeDependence(this)); + } + + GetBoundExpr(Expr *SubExpr, BoundKind Kind, QualType ResultType) + : GetBoundExpr(SourceLocation(), SourceLocation(), SubExpr, Kind, + ResultType) + { } + + explicit GetBoundExpr(EmptyShell Empty) + : Expr(GetBoundExprClass, Empty), SubExpr(nullptr), Kind(BK_Lower) { + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == GetBoundExprClass; + } + + SourceLocation getBuiltinLoc() const LLVM_READONLY { return BuiltinLoc; } + void setBuiltinLoc(SourceLocation Loc) { BuiltinLoc = Loc; } + + SourceLocation getRParenLoc() const LLVM_READONLY { return RParenLoc; } + void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return BuiltinLoc.isInvalid() ? getSubExpr()->getBeginLoc() : BuiltinLoc; + } + SourceLocation getEndLoc() const LLVM_READONLY { + return RParenLoc.isInvalid() ? getSubExpr()->getEndLoc() : RParenLoc; + } + + child_range children() { + return child_range(&SubExpr, &SubExpr + 1); + } + + const_child_range children() const { + return const_child_range(&SubExpr, &SubExpr + 1); + } + + Expr *getSubExpr() { return cast(SubExpr); } + const Expr *getSubExpr() const { return cast(SubExpr); } + void setSubExpr(Expr *NewValue) { SubExpr = NewValue; } + + BoundKind getBoundKind() const { return Kind; } + void setBoundKind(BoundKind K) { Kind = K; } +}; + +enum class BoundsCheckKind : unsigned { + FlexibleArrayCountAssign, + FlexibleArrayCountCast, + FlexibleArrayCountDeref +}; + +/// PredefinedBoundsCheckExpr - AST representation of bounds checks that +/// BoundsSafety added. This is a wrapper expression to wrap the expression +/// (GuardedExpr) that will be executed if the bounds check succeeds. The +/// subexpressions except the first one (GuardedExpr) are used for bounds check +/// whose semantics is determined by the kind of bounds check (BoundsCheckKind). +/// Most subexpressions are likely to be OpaqueValueExpr to avoid re-evaluating +/// expressions. As bounds checks are necessarily implicit, the expression uses +/// the source location of the wrapped expression. +class PredefinedBoundsCheckExpr final + : public Expr, + public llvm::TrailingObjects { + static constexpr unsigned CheckArgsOffset = 1; + + PredefinedBoundsCheckExpr(Expr *GuardedExpr, BoundsCheckKind Kind, + ArrayRef CheckArgs); + PredefinedBoundsCheckExpr(EmptyShell Empty, unsigned NumChildren) + : Expr(PredefinedBoundsCheckExprClass, Empty) { + PredefinedBoundsCheckExprBits.NumChildren = NumChildren; + } + + Stmt **getTrailingStmts() { + return const_cast(getTrailingObjects()); + } + + Stmt *const *getTrailingStmts() const { return getTrailingObjects(); } + + Expr **getSubExprs() { return reinterpret_cast(getTrailingStmts()); } + + Expr *const *getSubExprs() const { + return reinterpret_cast(getTrailingStmts()); + } + +public: + static PredefinedBoundsCheckExpr *Create(ASTContext &Ctx, Expr *GuardedExpr, + BoundsCheckKind Kind, + ArrayRef CheckArgs); + + static PredefinedBoundsCheckExpr *CreateEmpty(ASTContext &Ctx, + unsigned NumChildren); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == PredefinedBoundsCheckExprClass; + } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getGuardedExpr()->getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { + return getGuardedExpr()->getEndLoc(); + } + + unsigned getNumSubExprs() const { + return PredefinedBoundsCheckExprBits.NumChildren; + } + + unsigned getNumCheckArgs() const { + assert(getNumSubExprs() >= CheckArgsOffset); + return getNumSubExprs() - CheckArgsOffset; + } + + static constexpr unsigned getCheckArgsOffset() { return CheckArgsOffset; } + + typedef Expr *const *checkargs_iterator; + typedef const Expr *const *const_checkargs_iterator; + + checkargs_iterator checkargs_begin() { + return reinterpret_cast(getSubExprs() + + getCheckArgsOffset()); + } + const_checkargs_iterator checkargs_begin() const { + return reinterpret_cast(getSubExprs() + + getCheckArgsOffset()); + } + checkargs_iterator checkargs_end() { + return checkargs_begin() + getNumCheckArgs(); + } + const_checkargs_iterator checkargs_end() const { + return checkargs_begin() + getNumCheckArgs(); + } + + llvm::iterator_range checkargs() { + return llvm::make_range(checkargs_begin(), checkargs_end()); + } + llvm::iterator_range checkargs() const { + return llvm::make_range(checkargs_begin(), checkargs_end()); + } + + child_range children() { + Stmt **begin = getTrailingStmts(); + return child_range(begin, begin + getNumSubExprs()); + } + + const_child_range children() const { + Stmt *const *begin = getTrailingStmts(); + return const_child_range(begin, begin + getNumSubExprs()); + } + + BoundsCheckKind getKind() const { + return static_cast(PredefinedBoundsCheckExprBits.Kind); + } + + StringRef getKindName() const; + + Expr *getGuardedExpr() { return getSubExpr(0); } + const Expr *getGuardedExpr() const { return getSubExpr(0); } + void setGuardedExpr(Expr *NewValue) { setSubExpr(0, NewValue); } + + Expr *getSubExpr(unsigned i) { + assert(i < getNumSubExprs()); + return cast(getTrailingStmts()[i]); + } + + const Expr *getSubExpr(unsigned i) const { + assert(i < getNumSubExprs()); + return cast(getTrailingStmts()[i]); + } + + // This returns the pointer to the base struct with flexible array member. + const Expr *getFamBasePtr() const { + switch (getKind()) { + case BoundsCheckKind::FlexibleArrayCountAssign: + case BoundsCheckKind::FlexibleArrayCountCast: + case BoundsCheckKind::FlexibleArrayCountDeref: + return getSubExpr(CheckArgsOffset); + } + llvm_unreachable("Unsupported BoundsCheckKind"); + } + + const Expr *getFamPtr() const { + switch (getKind()) { + case BoundsCheckKind::FlexibleArrayCountAssign: + case BoundsCheckKind::FlexibleArrayCountCast: + case BoundsCheckKind::FlexibleArrayCountDeref: + return getSubExpr(CheckArgsOffset + 1); + } + llvm_unreachable("Unsupported BoundsCheckKind"); + } + + const Expr *getFamCount() const { + switch (getKind()) { + case BoundsCheckKind::FlexibleArrayCountAssign: + case BoundsCheckKind::FlexibleArrayCountCast: + case BoundsCheckKind::FlexibleArrayCountDeref: + return getSubExpr(CheckArgsOffset + 2); + } + llvm_unreachable("Unsupported BoundsCheckKind"); + } + + void setSubExpr(unsigned i, Expr *E) { + assert(i < getNumSubExprs()); + getTrailingStmts()[i] = E; + } +}; + +/// BoundsCheckExpr - AST representation of a bounds check that BoundsSafety added. +/// This wraps the expression that will be evaluated if the bounds check +/// succeeds or evaluated before the bounds checks if PostGuard is true. The object +/// also holds all the bounds to check. Many operands are +/// likely to be OpaqueValueExpr to avoid re-evaluating expressions. As bounds +/// checks are necessarily implicit, they never have a source location. +class BoundsCheckExpr final : + public Expr, public llvm::TrailingObjects { + BoundsCheckExpr(Expr *GuardedExpr, Expr *Cond, + ArrayRef CommonExprs); + BoundsCheckExpr(EmptyShell Empty, unsigned NumChildren) + : Expr(BoundsCheckExprClass, Empty) { + BoundsCheckExprBits.NumChildren = NumChildren; + } + + Stmt **getTrailingStmts() { + return const_cast(getTrailingObjects()); + } + + Stmt *const *getTrailingStmts() const { + return getTrailingObjects(); + } + + Expr **getSubExprs() { + return reinterpret_cast(getTrailingStmts()); + } + + Expr *const *getSubExprs() const { + return reinterpret_cast(getTrailingStmts()); + } + +public: + static BoundsCheckExpr *Create(ASTContext &Ctx, Expr *GuardedExpr, + Expr *Cond, ArrayRef CommonExprs); + + static BoundsCheckExpr *CreateEmpty(ASTContext &Ctx, unsigned NumChildren); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == BoundsCheckExprClass; + } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getGuardedExpr()->getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { + return getGuardedExpr()->getEndLoc(); + } + + unsigned getNumSubExprs() const { return BoundsCheckExprBits.NumChildren; } + unsigned getNumCommonExprs() const { + assert(getNumSubExprs() >= 2); + return getNumSubExprs() - 2; + } + + typedef OpaqueValueExpr * const * opaquevalues_iterator; + typedef const OpaqueValueExpr * const * const_opaquevalues_iterator; + + opaquevalues_iterator opaquevalues_begin() { + return reinterpret_cast (getSubExprs() + 2); + } + const_opaquevalues_iterator opaquevalues_begin() const { + return reinterpret_cast (getSubExprs() + 2); + } + opaquevalues_iterator opaquevalues_end() { + return opaquevalues_begin() + getNumCommonExprs(); + } + const_opaquevalues_iterator opaquevalues_end() const { + return opaquevalues_begin() + getNumCommonExprs(); + } + + llvm::iterator_range opaquevalues() { + return llvm::make_range(opaquevalues_begin(), opaquevalues_end()); + } + llvm::iterator_range opaquevalues() const { + return llvm::make_range(opaquevalues_begin(), opaquevalues_end()); + } + + child_range children() { + Stmt **begin = getTrailingStmts(); + return child_range(begin, begin + getNumSubExprs()); + } + + const_child_range children() const { + Stmt *const *begin = getTrailingStmts(); + return const_child_range(begin, begin + getNumSubExprs()); + } + + Expr *getGuardedExpr() { return getSubExpr(0); } + const Expr *getGuardedExpr() const { return getSubExpr(0); } + void setGuardedExpr(Expr *NewValue) { setSubExpr(0, NewValue); } + + Expr *getCond() { return getSubExpr(1); } + const Expr *getCond() const { return getSubExpr(1); } + void setCond(Expr *Cond) { setSubExpr(1, Cond); } + + Expr *getSubExpr(unsigned i) { + assert(i < getNumSubExprs()); + return cast(getTrailingStmts()[i]); + } + + const Expr *getSubExpr(unsigned i) const { + assert(i < getNumSubExprs()); + return cast(getTrailingStmts()[i]); + } + + void setSubExpr(unsigned i, Expr *E) { + assert(i < getNumSubExprs()); + getTrailingStmts()[i] = E; + } +}; + +class MaterializeSequenceExpr final : + public Expr, public llvm::TrailingObjects { + + MaterializeSequenceExpr(Expr *WrappedExpr, ArrayRef Values, bool Unbind) + : Expr(MaterializeSequenceExprClass, WrappedExpr->getType(), + WrappedExpr->getValueKind(), + WrappedExpr->getObjectKind()) { + MaterializeSequenceExprBits.NumExprs = Values.size() + 1; + MaterializeSequenceExprBits.Unbind = Unbind; + getSubExprs()[0] = WrappedExpr; + std::copy(Values.begin(), Values.end(), getSubExprs() + 1); + setDependence(computeDependence(this)); + } + + MaterializeSequenceExpr(EmptyShell Empty, unsigned NumExprs) + : Expr(MaterializeSequenceExprClass, Empty) { + MaterializeSequenceExprBits.NumExprs = NumExprs; + } + + Expr **getSubExprs() { + return getTrailingObjects(); + } + + Expr *const *getSubExprs() const { + return getTrailingObjects(); + } + + friend class ASTStmtReader; +public: + static MaterializeSequenceExpr *Create(const ASTContext &Ctx, Expr *WrappedExpr, + ArrayRef Values, + bool Unbind = false); + + static MaterializeSequenceExpr *CreateEmpty(const ASTContext &Ctx, unsigned NumExprs); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == MaterializeSequenceExprClass; + } + + unsigned getNumSubExprs() const { + return MaterializeSequenceExprBits.NumExprs; + } + + bool isBinding() const { + return !isUnbinding(); + } + + bool isUnbinding() const { + return MaterializeSequenceExprBits.Unbind; + } + + unsigned getNumOpaqueValueExprs() const { + assert(getNumSubExprs() > 1); + return getNumSubExprs() - 1; + } + + Expr *getWrappedExpr() const { + assert(getNumSubExprs() > 0); + return getSubExprs()[0]; + } + + typedef OpaqueValueExpr * const * opaquevalues_iterator; + typedef const OpaqueValueExpr * const * const_opaquevalues_iterator; + + opaquevalues_iterator opaquevalues_begin() { + return reinterpret_cast (getSubExprs() + 1); + } + const_opaquevalues_iterator opaquevalues_begin() const { + return reinterpret_cast (getSubExprs() + 1); + } + opaquevalues_iterator opaquevalues_end() { + return opaquevalues_begin() + getNumOpaqueValueExprs(); + } + const_opaquevalues_iterator opaquevalues_end() const { + return opaquevalues_begin() + getNumOpaqueValueExprs(); + } + + llvm::iterator_range opaquevalues() { + return llvm::make_range(opaquevalues_begin(), opaquevalues_end()); + } + llvm::iterator_range opaquevalues() const { + return llvm::make_range(opaquevalues_begin(), opaquevalues_end()); + } + + SourceLocation getBeginLoc() const { + return getWrappedExpr()->getBeginLoc(); + } + + SourceLocation getEndLoc() const { + return getWrappedExpr()->getEndLoc(); + } + + child_range children() { + Stmt **begin = reinterpret_cast(getSubExprs()); + return child_range(begin, + begin + getNumSubExprs()); + } + + const_child_range children() const { + Stmt *const *begin = reinterpret_cast(getSubExprs()); + return const_child_range(begin, + begin + getNumSubExprs()); + } +}; + +/// TerminatedByToIndexableExpr - The AST representation of +/// __builtin_terminated_by_to_indexable() and +/// __builtin_unsafe_terminated_by_to_indexable(). +class TerminatedByToIndexableExpr final : public Expr { +private: + enum { POINTER, TERMINATOR, END_EXPR }; + Stmt *SubExprs[END_EXPR]; + bool IncludeTerminator; + SourceLocation BuiltinLoc, RParenLoc; + +public: + TerminatedByToIndexableExpr(SourceLocation BuiltinLoc, + SourceLocation RParenLoc, Expr *Pointer, + Expr *Terminator, bool IncludeTerminator, + QualType ResultType) + : Expr(TerminatedByToIndexableExprClass, ResultType, VK_PRValue, + OK_Ordinary), + SubExprs{Pointer, Terminator}, IncludeTerminator(IncludeTerminator), + BuiltinLoc(BuiltinLoc), RParenLoc(RParenLoc) {} + + TerminatedByToIndexableExpr(Expr *Pointer, Expr *Terminator, + QualType ResultType) + : TerminatedByToIndexableExpr(SourceLocation(), SourceLocation(), Pointer, + Terminator, false, ResultType) {} + + explicit TerminatedByToIndexableExpr(EmptyShell Empty) + : Expr(TerminatedByToIndexableExprClass, Empty), SubExprs{}, + IncludeTerminator(false) {} + + static bool classof(const Stmt *T) { + return T->getStmtClass() == TerminatedByToIndexableExprClass; + } + + SourceLocation getBuiltinLoc() const LLVM_READONLY { return BuiltinLoc; } + void setBuiltinLoc(SourceLocation Loc) { BuiltinLoc = Loc; } + + SourceLocation getRParenLoc() const LLVM_READONLY { return RParenLoc; } + void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return BuiltinLoc.isInvalid() ? getPointer()->getBeginLoc() : BuiltinLoc; + } + SourceLocation getEndLoc() const LLVM_READONLY { + return RParenLoc.isInvalid() ? getPointer()->getEndLoc() : RParenLoc; + } + + child_range children() { return child_range(SubExprs, SubExprs + END_EXPR); } + + const_child_range children() const { + return const_child_range(SubExprs, SubExprs + END_EXPR); + } + + Expr *getPointer() const { return cast(SubExprs[POINTER]); } + void setPointer(Expr *E) { SubExprs[POINTER] = E; } + Expr *getTerminator() const { return cast(SubExprs[TERMINATOR]); } + void setTerminator(Expr *E) { SubExprs[TERMINATOR] = E; } + + bool includesTerminator() const { return IncludeTerminator; } + void setIncludeTerminator(bool Include) { IncludeTerminator = Include; } +}; + +/// TerminatedByFromIndexableExpr - The AST representation of +/// __builtin_unsafe_terminated_by_from_indexable(). +class TerminatedByFromIndexableExpr final : public Expr { +private: + enum { POINTER, POINTER_TO_TERMINATOR, END_EXPR }; + Stmt *SubExprs[END_EXPR]; + SourceLocation BuiltinLoc, RParenLoc; + +public: + TerminatedByFromIndexableExpr(SourceLocation BuiltinLoc, + SourceLocation RParenLoc, Expr *Pointer, + Expr *PointerToTerminator, QualType ResultType) + : Expr(TerminatedByFromIndexableExprClass, ResultType, VK_PRValue, + OK_Ordinary), + SubExprs{Pointer, PointerToTerminator}, BuiltinLoc(BuiltinLoc), + RParenLoc(RParenLoc) {} + + TerminatedByFromIndexableExpr(Expr *Pointer, Expr *PointerToTerminator, + QualType ResultType) + : TerminatedByFromIndexableExpr(SourceLocation(), SourceLocation(), + Pointer, PointerToTerminator, + ResultType) {} + + explicit TerminatedByFromIndexableExpr(EmptyShell Empty) + : Expr(TerminatedByFromIndexableExprClass, Empty), SubExprs{} {} + + static bool classof(const Stmt *T) { + return T->getStmtClass() == TerminatedByFromIndexableExprClass; + } + + SourceLocation getBuiltinLoc() const LLVM_READONLY { return BuiltinLoc; } + void setBuiltinLoc(SourceLocation Loc) { BuiltinLoc = Loc; } + + SourceLocation getRParenLoc() const LLVM_READONLY { return RParenLoc; } + void setRParenLoc(SourceLocation Loc) { RParenLoc = Loc; } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return BuiltinLoc.isInvalid() ? getPointer()->getBeginLoc() : BuiltinLoc; + } + SourceLocation getEndLoc() const LLVM_READONLY { + if (RParenLoc.isValid()) + return RParenLoc; + return getPointerToTerminator() ? getPointerToTerminator()->getEndLoc() + : SourceLocation(); + } + + child_range children() { return child_range(SubExprs, SubExprs + END_EXPR); } + + const_child_range children() const { + return const_child_range(SubExprs, SubExprs + END_EXPR); + } + + Expr *getPointer() const { return cast(SubExprs[POINTER]); } + void setPointer(Expr *E) { SubExprs[POINTER] = E; } + Expr *getPointerToTerminator() const { + return cast_or_null(SubExprs[POINTER_TO_TERMINATOR]); + } + void setPointerToTerminator(Expr *E) { SubExprs[POINTER_TO_TERMINATOR] = E; } +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// A builtin binary operation expression such as "x + y" or "x <= y". /// /// This expression node kind describes a builtin binary operation, @@ -5231,6 +6090,28 @@ class InitListExpr : public Expr { return cast_or_null(InitExprs[Init]); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + Expr *getInitForField(const ValueDecl *VD) { + assert(isSemanticForm()); + if (auto FD = dyn_cast(VD)) { + unsigned FieldIdx = 0; + for (FieldDecl *Sibling : FD->getParent()->fields()) { + if (Sibling->isUnnamedBitField()) + continue; + if (Sibling == FD) + break; + ++FieldIdx; + } + if (FieldIdx >= getNumInits()) + return nullptr; + return getInit(FieldIdx); + } + + auto IFD = cast(VD); + return getInitForField(IFD->getAnonField()); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + void setInit(unsigned Init, Expr *expr) { assert(Init < getNumInits() && "Initializer access out of range!"); InitExprs[Init] = expr; diff --git a/clang/include/clang/AST/ExprObjC.h b/clang/include/clang/AST/ExprObjC.h index f87fa85569c44..cea2eb198fb6d 100644 --- a/clang/include/clang/AST/ExprObjC.h +++ b/clang/include/clang/AST/ExprObjC.h @@ -1689,30 +1689,78 @@ class ObjCBridgedCastExpr final /// be used in the condition of an \c if, but it is also usable as top level /// expressions. /// -class ObjCAvailabilityCheckExpr : public Expr { +class ObjCAvailabilityCheckExpr final + : public Expr, + private llvm::TrailingObjects { +public: + struct VersionAsWritten { + /// Platform version canonicalized for use with availability checks. + VersionTuple Version; + /// Platform version as written in the source. + VersionTuple SourceVersion; + }; + +private: friend class ASTStmtReader; + friend llvm::TrailingObjects; - VersionTuple VersionToCheck; + VersionAsWritten VersionToCheck; SourceLocation AtLoc, RParen; + void setHasDomainName(bool V) { + ObjCAvailabilityCheckExprBits.HasDomainName = V; + } + + ObjCAvailabilityCheckExpr(SourceLocation AtLoc, SourceLocation RParen, + QualType Ty, StringRef DomainName) + : Expr(ObjCAvailabilityCheckExprClass, Ty, VK_PRValue, OK_Ordinary), + VersionToCheck(), AtLoc(AtLoc), RParen(RParen) { + setDependence(ExprDependence::None); + setHasDomainName(true); + strcpy(getTrailingObjects(), DomainName.data()); + } + public: - ObjCAvailabilityCheckExpr(VersionTuple VersionToCheck, SourceLocation AtLoc, + ObjCAvailabilityCheckExpr(VersionAsWritten VersionToCheck, + SourceLocation AtLoc, SourceLocation RParen, QualType Ty) : Expr(ObjCAvailabilityCheckExprClass, Ty, VK_PRValue, OK_Ordinary), VersionToCheck(VersionToCheck), AtLoc(AtLoc), RParen(RParen) { setDependence(ExprDependence::None); + setHasDomainName(false); } + static ObjCAvailabilityCheckExpr * + CreateAvailabilityFeatureCheck(SourceLocation AtLoc, SourceLocation RParen, + QualType Ty, StringRef DomainName, + const ASTContext &C); + explicit ObjCAvailabilityCheckExpr(EmptyShell Shell) - : Expr(ObjCAvailabilityCheckExprClass, Shell) {} + : Expr(ObjCAvailabilityCheckExprClass, Shell) { + setHasDomainName(false); + } + + static ObjCAvailabilityCheckExpr * + CreateEmpty(const ASTContext &C, Stmt::EmptyShell Empty, size_t FeaturesLen); SourceLocation getBeginLoc() const { return AtLoc; } SourceLocation getEndLoc() const { return RParen; } SourceRange getSourceRange() const { return {AtLoc, RParen}; } /// This may be '*', in which case this should fold to true. - bool hasVersion() const { return !VersionToCheck.empty(); } - VersionTuple getVersion() const { return VersionToCheck; } + bool hasVersion() const { return !VersionToCheck.Version.empty(); } + VersionTuple getVersion() const { return VersionToCheck.Version; } + VersionTuple getVersionAsWritten() const { + return VersionToCheck.SourceVersion; + } + + bool hasDomainName() const { + return ObjCAvailabilityCheckExprBits.HasDomainName; + } + StringRef getDomainName() const { + assert(hasDomainName()); + return getTrailingObjects(); + } child_range children() { return child_range(child_iterator(), child_iterator()); diff --git a/clang/include/clang/AST/ExternalASTSource.h b/clang/include/clang/AST/ExternalASTSource.h index f45e3af7602c1..3d9d152e29f05 100644 --- a/clang/include/clang/AST/ExternalASTSource.h +++ b/clang/include/clang/AST/ExternalASTSource.h @@ -142,6 +142,11 @@ class ExternalASTSource : public RefCountedBase { /// Update an out-of-date identifier. virtual void updateOutOfDateIdentifier(const IdentifierInfo &II) {} + // Retrieve the variable declaration that has the information for the domain. + virtual Decl *getAvailabilityDomainDecl(StringRef DomainName) { + return nullptr; + } + /// Find all declarations with the given name in the given context, /// and add them to the context by calling SetExternalVisibleDeclsForName /// or SetNoExternalVisibleDeclsForName. diff --git a/clang/include/clang/AST/IgnoreExpr.h b/clang/include/clang/AST/IgnoreExpr.h index 917bada61fa6f..e7a2c49fde863 100644 --- a/clang/include/clang/AST/IgnoreExpr.h +++ b/clang/include/clang/AST/IgnoreExpr.h @@ -45,6 +45,35 @@ const Expr *IgnoreExprNodes(const Expr *E, FnTys &&...Fns) { return IgnoreExprNodes(const_cast(E), std::forward(Fns)...); } +/* TO_UPSTREAM(BoundsSafety) ON */ +inline Expr *IgnoreBoundsSafetyImplicitImpl( + Expr *E, llvm::SmallPtrSetImpl &BoundValues) { + + if (auto *FPPE = dyn_cast(E)) + return FPPE->getSubExpr(); + + if (auto *AE = dyn_cast(E)) + return AE->getWrappedExpr(); + + if (auto *MSE = dyn_cast(E)) { + if (!MSE->isUnbinding()) + BoundValues.insert(MSE->opaquevalues_begin(), MSE->opaquevalues_end()); + return MSE->getWrappedExpr(); + } + + if (auto *BCE = dyn_cast(E)) { + BoundValues.insert(BCE->opaquevalues_begin(), BCE->opaquevalues_end()); + return BCE->getGuardedExpr(); + } + + if (auto *Opaque = dyn_cast(E)) + if (BoundValues.count(Opaque)) + return Opaque->getSourceExpr(); + + return E; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + inline Expr *IgnoreImplicitCastsSingleStep(Expr *E) { if (auto *ICE = dyn_cast(E)) return ICE->getSubExpr(); diff --git a/clang/include/clang/AST/NestedNameSpecifier.h b/clang/include/clang/AST/NestedNameSpecifier.h index d7da3272d0943..11e6f601715d1 100644 --- a/clang/include/clang/AST/NestedNameSpecifier.h +++ b/clang/include/clang/AST/NestedNameSpecifier.h @@ -235,6 +235,23 @@ class NestedNameSpecifier : public llvm::FoldingSetNode { void dump() const; void dump(llvm::raw_ostream &OS) const; void dump(llvm::raw_ostream &OS, const LangOptions &LO) const; + + /// \brief Compute the qualification required to get from the current context + /// (\p CurContext) to the target context (\p TargetContext). + /// + /// \param Context the AST context in which the qualification will be used. + /// + /// \param CurContext the context where an entity is being named, which is + /// typically based on the current scope. + /// + /// \param TargetContext the context in which the named entity actually + /// resides. + /// + /// \returns a nested name specifier that refers into the target context, or + /// NULL if no qualification is needed. + static NestedNameSpecifier * + getRequiredQualification(ASTContext &Context, const DeclContext *CurContext, + const DeclContext *TargetContext); }; /// A C++ nested-name-specifier augmented with source location diff --git a/clang/include/clang/AST/ObjCMethodReferenceInfo.h b/clang/include/clang/AST/ObjCMethodReferenceInfo.h new file mode 100644 index 0000000000000..0a3046f2a6593 --- /dev/null +++ b/clang/include/clang/AST/ObjCMethodReferenceInfo.h @@ -0,0 +1,43 @@ +//===- ObjcMethodReferenceInfo.h - API for ObjC method tracing --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines APIs for ObjC method tracing. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_OBJC_METHOD_REFERENCE_INFO_H +#define LLVM_CLANG_OBJC_METHOD_REFERENCE_INFO_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace clang { + +class ObjCMethodDecl; + +struct ObjCMethodReferenceInfo { + static constexpr unsigned FormatVersion = 1; + std::string ToolName, ToolVersion; + std::string Target, TargetVariant; + + /// Paths to the files in which ObjC methods are referenced. + llvm::SmallVector FilePaths; + + /// A map from the index of a file in FilePaths to the list of ObjC methods. + std::map> References; +}; + +/// This function serializes the ObjC message tracing information in JSON. +void serializeObjCMethodReferencesAsJson(const ObjCMethodReferenceInfo &Info, + llvm::raw_ostream &OS); + +} // namespace clang + +#endif // LLVM_CLANG_OBJC_METHOD_REFERENCE_INFO_H diff --git a/clang/include/clang/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def index c2dca895e8411..5baf89e8694d8 100644 --- a/clang/include/clang/AST/OperationKinds.def +++ b/clang/include/clang/AST/OperationKinds.def @@ -361,6 +361,9 @@ CAST_OPERATION(AddressSpaceConversion) // Convert an integer initializer to an OpenCL sampler. CAST_OPERATION(IntToOCLSampler) +// BoundsSafety: Casting between pointer types with different -fbounds-safety attributes +CAST_OPERATION(BoundsSafetyPointerCast) + // Truncate a vector type by dropping elements from the end (HLSL only). CAST_OPERATION(HLSLVectorTruncation) diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index 5a98ae1987b16..d64ff3ad2f865 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -60,6 +60,7 @@ struct PrintingPolicy { /// Create a default printing policy for the specified language. PrintingPolicy(const LangOptions &LO) : Indentation(2), SuppressSpecifiers(false), + SupressStorageClassSpecifiers(false), SuppressTagKeyword(LO.CPlusPlus), IncludeTagDefinition(false), SuppressScope(false), SuppressUnwrittenScope(false), SuppressInlineNamespace(SuppressInlineNamespaceMode::Redundant), @@ -75,11 +76,21 @@ struct PrintingPolicy { PolishForDeclaration(false), Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true), MSVCFormatting(false), ConstantsAsWritten(false), - SuppressImplicitBase(false), FullyQualifiedName(false), + SuppressImplicitBase(false), UseStdFunctionForLambda(false), FullyQualifiedName(false), PrintAsCanonical(false), PrintInjectedClassNameWithArguments(true), UsePreferredNames(true), AlwaysIncludeTypeForTemplateArgument(false), CleanUglifiedParameters(false), EntireContentsOfLargeArray(true), - UseEnumerators(true), UseHLSLTypes(LO.HLSL) {} + UseEnumerators(true), UseHLSLTypes(LO.HLSL), + /* TO_UPSTREAM(BoundsSafety) ON */ + CountedByInArrayBracket(false), DelayedArrayQual() { + // GNU Attributes cannot be placed inside the array bracket, hence 'counted_by' + // attribute in the upstream is printed after the array bracket. Internally, + // 'counted_by' can be either inside or outside the bracket, while the inside + // has been the primary style. For now, we print it inside the bracket when + // -fbounds-safety is enabled, and outside the bracket otherwise. + CountedByInArrayBracket = LO.BoundsSafety; + /* TO_UPSTREAM(BoundsSafety) OFF */ + } /// Adjust this printing policy for cases where it's known that we're /// printing C++ code (for instance, if AST dumping reaches a C++-only @@ -111,6 +122,10 @@ struct PrintingPolicy { LLVM_PREFERRED_TYPE(bool) unsigned SuppressSpecifiers : 1; + /// \brief Whether we should supress the printing of the actual storage class + /// specifiers for the given declaration. + bool SupressStorageClassSpecifiers : 1; + /// Whether type printing should skip printing the tag keyword. /// /// This is used when printing the inner type of elaborated types, @@ -305,6 +320,9 @@ struct PrintingPolicy { LLVM_PREFERRED_TYPE(bool) unsigned SuppressImplicitBase : 1; + /// \brief Whether we should use std::function<...> for lambda record types. + bool UseStdFunctionForLambda : 1; + /// When true, print the fully qualified name of function declarations. /// This is the opposite of SuppressScope and thus overrules it. LLVM_PREFERRED_TYPE(bool) @@ -351,8 +369,14 @@ struct PrintingPolicy { LLVM_PREFERRED_TYPE(bool) unsigned UseHLSLTypes : 1; + /// Whether to print 'counted_by' attribute inside the array bracket. + LLVM_PREFERRED_TYPE(bool) + unsigned CountedByInArrayBracket : 1; + /// Callbacks to use to allow the behavior of printing to be customized. const PrintingCallbacks *Callbacks = nullptr; + + std::string DelayedArrayQual; }; } // end namespace clang diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index 33336d57b6298..2cdc4fef336b2 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -135,6 +135,7 @@ def QualType : DefaultValuePropertyType; def RefQualifierKind : EnumPropertyType; def Selector : PropertyType; def SourceLocation : PropertyType; +def BoundsSafetyPointerAttributes : PropertyType; def StmtRef : RefPropertyType<"Stmt"> { let ConstWhenWriting = 1; } def ExprRef : SubclassPropertyType<"Expr", StmtRef>; def TemplateArgument : PropertyType; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 3edc8684d0a19..f85f7aec8dc89 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1148,6 +1148,16 @@ DEF_TRAVERSE_TYPE(CountAttributedType, { TRY_TO(TraverseType(T->desugar())); }) +/* TO_UPSTREAM(BoundsSafety) ON */ +DEF_TRAVERSE_TYPE(DynamicRangePointerType, { + if (auto *EndPtr = T->getEndPointer()) + TRY_TO(TraverseStmt(EndPtr)); + TRY_TO(TraverseType(T->desugar())); +}) + +DEF_TRAVERSE_TYPE(ValueTerminatedType, { TRY_TO(TraverseType(T->desugar())); }) +/* TO_UPSTREAM(BoundsSafety) OFF */ + DEF_TRAVERSE_TYPE(BTFTagAttributedType, { TRY_TO(TraverseType(T->getWrappedType())); }) @@ -1451,6 +1461,14 @@ DEF_TRAVERSE_TYPELOC(AttributedType, DEF_TRAVERSE_TYPELOC(CountAttributedType, { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); }) +/* TO_UPSTREAM(BoundsSafety) ON */ +DEF_TRAVERSE_TYPELOC(DynamicRangePointerType, + { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); }) + +DEF_TRAVERSE_TYPELOC(ValueTerminatedType, + { TRY_TO(TraverseTypeLoc(TL.getOriginalLoc())); }) +/* TO_UPSTREAM(BoundsSafety) OFF */ + DEF_TRAVERSE_TYPELOC(BTFTagAttributedType, { TRY_TO(TraverseTypeLoc(TL.getWrappedLoc())); }) @@ -2567,6 +2585,24 @@ DEF_TRAVERSE_STMT(BuiltinBitCastExpr, { TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc())); }) +DEF_TRAVERSE_STMT(BoundsSafetyPointerPromotionExpr, {}) +DEF_TRAVERSE_STMT(AssumptionExpr, {}) +DEF_TRAVERSE_STMT(ForgePtrExpr, {}) +DEF_TRAVERSE_STMT(GetBoundExpr, {}) +DEF_TRAVERSE_STMT(PredefinedBoundsCheckExpr, {}) +DEF_TRAVERSE_STMT(BoundsCheckExpr, {}) +DEF_TRAVERSE_STMT(MaterializeSequenceExpr, { + if (!S->isUnbinding()) { + for (auto *OVE : S->opaquevalues()) { + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(OVE->getSourceExpr()); + } + TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getWrappedExpr()); + ShouldVisitChildren = false; + } +}) +DEF_TRAVERSE_STMT(TerminatedByToIndexableExpr, {}) +DEF_TRAVERSE_STMT(TerminatedByFromIndexableExpr, {}) + template bool RecursiveASTVisitor::TraverseSynOrSemInitListExpr( InitListExpr *S, DataRecursionQueue *Queue) { diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 336eb6d3df7e1..5251ca54f29ad 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -745,6 +745,55 @@ class alignas(void *) Stmt { unsigned TemplateDepth; }; + class AssumptionExprBitfields { + friend class ASTStmtReader; + friend class AssumptionExpr; + + unsigned : NumExprBits; + unsigned NumExprs : 4; + }; + + class PredefinedBoundsCheckExprBitfields { + friend class ASTStmtReader; + friend class PredefinedBoundsCheckExpr; + + unsigned : NumExprBits; + + unsigned Kind : 6; + unsigned NumChildren; + }; + + class BoundsCheckExprBitfields { + friend class ASTStmtReader; + friend class BoundsCheckExpr; + + unsigned : NumExprBits; + + unsigned NumChildren : 31; + }; + + class BoundsSafetyPointerPromotionExprBitfields { + friend class ASTStmtReader; + friend class BoundsSafetyPointerPromotionExpr; + + unsigned : NumExprBits; + + /// If this field is is set, CodeGen should perform a null check on the + /// pointer and skip evaluating the bounds to avoid introducing unexpected + /// null dereference. + unsigned NullCheck : 1; + }; + + class MaterializeSequenceExprBitfields { + friend class ASTStmtReader; + friend class MaterializeSequenceExpr; + + unsigned : NumExprBits; + + unsigned NumExprs : 32 - 1; + unsigned Unbind : 1; + }; + //===--- C++ Expression bitfields classes ---===// class CXXOperatorCallExprBitfields { @@ -1204,6 +1253,15 @@ class alignas(void *) Stmt { unsigned ShouldCopy : 1; }; + class ObjCAvailabilityCheckExprBitfields { + friend class ObjCAvailabilityCheckExpr; + LLVM_PREFERRED_TYPE(ExprBitfields) + unsigned : NumExprBits; + + LLVM_PREFERRED_TYPE(bool) + unsigned HasDomainName : 1; + }; + //===--- Clang Extensions bitfields classes ---===// class OpaqueValueExprBitfields { @@ -1279,6 +1337,13 @@ class alignas(void *) Stmt { // GNU Extensions. StmtExprBitfields StmtExprBits; + // BoundsSafety Expression + AssumptionExprBitfields AssumptionExprBits; + PredefinedBoundsCheckExprBitfields PredefinedBoundsCheckExprBits; + BoundsCheckExprBitfields BoundsCheckExprBits; + BoundsSafetyPointerPromotionExprBitfields BoundsSafetyPointerPromotionExprBits; + MaterializeSequenceExprBitfields MaterializeSequenceExprBits; + // C++ Expressions CXXOperatorCallExprBitfields CXXOperatorCallExprBits; CXXRewrittenBinaryOperatorBitfields CXXRewrittenBinaryOperatorBits; @@ -1310,6 +1375,7 @@ class alignas(void *) Stmt { // Obj-C Expressions ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits; + ObjCAvailabilityCheckExprBitfields ObjCAvailabilityCheckExprBits; // Clang Extensions OpaqueValueExprBitfields OpaqueValueExprBits; @@ -2395,6 +2461,8 @@ class IfStmt final bool isObjCAvailabilityCheck() const; + bool isObjCAvailabilityCheckWithDomainName() const; + SourceLocation getBeginLoc() const { return getIfLoc(); } SourceLocation getEndLoc() const LLVM_READONLY { if (getElse()) diff --git a/clang/include/clang/AST/StmtIterator.h b/clang/include/clang/AST/StmtIterator.h index e98408c51a505..9adb3f21492d0 100644 --- a/clang/include/clang/AST/StmtIterator.h +++ b/clang/include/clang/AST/StmtIterator.h @@ -133,10 +133,10 @@ struct StmtIterator : public StmtIteratorImpl { StmtIterator(const VariableArrayType *t) : StmtIteratorImpl(t) {} -private: StmtIterator(const StmtIteratorBase &RHS) : StmtIteratorImpl(RHS) {} +private: inline friend StmtIterator cast_away_const(const ConstStmtIterator &RHS); }; diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index ea3a0f058a8ed..9cc05c15e096e 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -319,6 +319,10 @@ class TextNodeDumper void VisitObjCSubscriptRefExpr(const ObjCSubscriptRefExpr *Node); void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Node); void VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *Node); + void VisitMaterializeSequenceExpr(const MaterializeSequenceExpr *Node); + void VisitBoundsCheckExpr(const BoundsCheckExpr *Node); + void VisitPredefinedBoundsCheckExpr(const PredefinedBoundsCheckExpr *Node); + void VisitGetBoundExpr(const GetBoundExpr *Node); void VisitOMPIteratorExpr(const OMPIteratorExpr *Node); void VisitConceptSpecializationExpr(const ConceptSpecializationExpr *Node); void VisitRequiresExpr(const RequiresExpr *Node); diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 3e1fb05ad537c..aaf0e8d9c79a8 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -422,6 +422,7 @@ class Qualifiers { L.removeAddressSpace(); R.removeAddressSpace(); } + return Q; } @@ -487,22 +488,22 @@ class Qualifiers { unsigned getCVRQualifiers() const { return Mask & CVRMask; } unsigned getCVRUQualifiers() const { return Mask & (CVRMask | UMask); } - void setCVRQualifiers(unsigned mask) { + void setCVRQualifiers(uint64_t mask) { assert(!(mask & ~CVRMask) && "bitmask contains non-CVR bits"); Mask = (Mask & ~CVRMask) | mask; } - void removeCVRQualifiers(unsigned mask) { + void removeCVRQualifiers(uint64_t mask) { assert(!(mask & ~CVRMask) && "bitmask contains non-CVR bits"); Mask &= ~static_cast(mask); } void removeCVRQualifiers() { removeCVRQualifiers(CVRMask); } - void addCVRQualifiers(unsigned mask) { + void addCVRQualifiers(uint64_t mask) { assert(!(mask & ~CVRMask) && "bitmask contains non-CVR bits"); Mask |= mask; } - void addCVRUQualifiers(unsigned mask) { + void addCVRUQualifiers(uint64_t mask) { assert(!(mask & ~CVRMask & ~UMask) && "bitmask contains non-CVRU bits"); Mask |= mask; } @@ -616,25 +617,27 @@ class Qualifiers { // on a QualType object. bool hasFastQualifiers() const { return getFastQualifiers(); } unsigned getFastQualifiers() const { return Mask & FastMask; } - void setFastQualifiers(unsigned mask) { + void setFastQualifiers(uint64_t mask) { assert(!(mask & ~FastMask) && "bitmask contains non-fast qualifier bits"); Mask = (Mask & ~FastMask) | mask; } - void removeFastQualifiers(unsigned mask) { + void removeFastQualifiers(uint64_t mask) { assert(!(mask & ~FastMask) && "bitmask contains non-fast qualifier bits"); Mask &= ~static_cast(mask); } void removeFastQualifiers() { removeFastQualifiers(FastMask); } - void addFastQualifiers(unsigned mask) { + void addFastQualifiers(uint64_t mask) { assert(!(mask & ~FastMask) && "bitmask contains non-fast qualifier bits"); Mask |= mask; } /// Return true if the set contains any qualifiers which require an ExtQuals /// node to be allocated. - bool hasNonFastQualifiers() const { return Mask & ~FastMask; } + bool hasNonFastQualifiers() const { + return (Mask & ~FastMask); + } Qualifiers getNonFastQualifiers() const { Qualifiers Quals = *this; Quals.setFastQualifiers(0); @@ -643,7 +646,7 @@ class Qualifiers { /// Return true if the set contains any qualifiers. bool hasQualifiers() const { return Mask; } - bool empty() const { return !Mask; } + bool empty() const { return !hasQualifiers(); } /// Add the qualifiers from the given set to this set. void addQualifiers(Qualifiers Q) { @@ -2272,6 +2275,17 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { }; static_assert(sizeof(CountAttributedTypeBitfields) <= sizeof(unsigned)); + /* TO_UPSTREAM(BoundsSafety) ON */ + class DynamicRangePointerTypeBitfields { + friend class DynamicRangePointerType; + + unsigned : NumTypeBits; + + unsigned NumEndPtrDecls : 16; + unsigned NumStartPtrDecls : 16; + }; + /* TO_UPSTREAM(BoundsSafety) OFF */ + union { TypeBitfields TypeBits; ArrayTypeBitfields ArrayTypeBits; @@ -2296,6 +2310,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { DependentTemplateSpecializationTypeBits; PackExpansionTypeBitfields PackExpansionTypeBits; CountAttributedTypeBitfields CountAttributedTypeBits; + /* TO_UPSTREAM(BoundsSafety) ON */ + DynamicRangePointerTypeBitfields DynamicRangePointerTypeBits; + /* TO_UPSTREAM(BoundsSafety) OFF */ }; private: @@ -2443,12 +2460,28 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { /// class), will be set to the declaration. bool isIncompleteType(NamedDecl **Def = nullptr) const; + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Return true if the size of this type is "meaningless". For a meaninglessly + /// sized type T, sizeof(T) is either ill-formed or has a useless or + /// misleading result. This could be because the type is a function type, is a + /// void type, is a sizeless type, is an incomplete type (including an + /// incomplete array type), or is a record type with a flexible array member. + bool isSizeMeaningless() const; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Return true if this is an incomplete or object /// type, in other words, not a function type. bool isIncompleteOrObjectType() const { return !isFunctionType(); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Return true if this is an incomplete or sizeless type including a function type. + bool isIncompleteOrSizelessType() const { + return isIncompleteType() || isSizelessType() || isFunctionType(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// \returns True if the type is incomplete and it is also a type that /// cannot be completed by a later type definition. /// @@ -2564,8 +2597,23 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isPointerType() const; bool isPointerOrReferenceType() const; bool isSignableType() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + bool isUnsafeIndexablePointerType() const; + bool isSinglePointerType() const; + bool isIndexablePointerType() const; + bool isBidiIndexablePointerType() const; + bool isUnspecifiedPointerType() const; + bool isSafePointerType() const; + /* TO_UPSTREAM(BoundsSafety) OFF */ bool isAnyPointerType() const; // Any C pointer or ObjC object pointer bool isCountAttributedType() const; + /* TO_UPSTREAM(BoundsSafety) ON */ + bool isAnyVaListType(ASTContext &) const; + bool isDynamicRangePointerType() const; + bool isBoundsAttributedType() const; + bool isValueTerminatedType() const; + bool isImplicitlyNullTerminatedType(const ASTContext &) const; + /* TO_UPSTREAM(BoundsSafety) OFF */ bool isBlockPointerType() const; bool isVoidPointerType() const; bool isReferenceType() const; @@ -2661,6 +2709,8 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isAtomicType() const; // C11 _Atomic() bool isUndeducedAutoType() const; // C++11 auto or // C++14 decltype(auto) + // TO_UPSTREAM(BoundsSafety) + bool isPointerTypeWithBounds() const; // BoundsSafety __indexable or __bidi_indexable bool isTypedefNameType() const; // typedef or alias template #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ @@ -3068,6 +3118,16 @@ template <> const BoundsAttributedType *Type::getAs() const; /// sugar until it reaches an CountAttributedType or a non-sugared type. template <> const CountAttributedType *Type::getAs() const; +/* TO_UPSTREAM(BoundsSafety) ON */ +/// This will check for a DynamicRangePointerType by removing any existing +/// sugar until it reaches an DynamicRangePointerType or a non-sugared type. +template <> const DynamicRangePointerType *Type::getAs() const; + +/// This will check for a ValueTerminatedType by removing any existing sugar +/// until it reaches a ValueTerminatedType or a non-sugared type. +template <> const ValueTerminatedType *Type::getAs() const; +/* TO_UPSTREAM(BoundsSafety) OFF */ + // We can do canonical leaf types faster, because we don't have to // worry about preserving child type decoration. #define TYPE(Class, Base) @@ -3245,28 +3305,253 @@ class ParenType : public Type, public llvm::FoldingSetNode { static bool classof(const Type *T) { return T->getTypeClass() == Paren; } }; +class BoundsSafetyPointerAttributes { +public: + // This ordering of attributes also serves as precedence to create a composite + // type between two pointer types. We currently use the precedence for conditional + // operators for which '__unsafe_indexable' has the highest precedence while + // '__single' being the lowest. + enum BK { + Unspecified = 0, + UnsafeIndexable = 1, + BidiIndexable = 2, + Indexable = 3, + Single = 4, + BoundsWidth = 3, + BoundsMask = (1 << BoundsWidth) - 1 + }; + + enum TK { + UnsafeCastable = 1, + NoCastable = 2, + Castable = 3, + TypeWidth = 2, + TypeMask = (1 << TypeWidth) - 1, + ShiftedTypeMask = TypeMask << BoundsWidth + }; + + static BoundsSafetyPointerAttributes unsafeIndexable() { + BoundsSafetyPointerAttributes FA; + FA.setUnsafeIndexable(); + return FA; + } + + static BoundsSafetyPointerAttributes bidiIndexable() { + BoundsSafetyPointerAttributes FA; + FA.setBidiIndexable(); + return FA; + } + + static BoundsSafetyPointerAttributes indexable() { + BoundsSafetyPointerAttributes FA; + FA.setIndexable(); + return FA; + } + + static BoundsSafetyPointerAttributes single() { + BoundsSafetyPointerAttributes FA; + FA.setSingle(); + return FA; + } + + static BoundsSafetyPointerAttributes unspecified() { + BoundsSafetyPointerAttributes FA; + return FA; + } + + uint32_t getBoundsAttr() const { return BoundsAttr; } + + uint32_t getTypeAttr() const { return TypeAttr; } + + uint32_t getAsOpaqueValue() const { + return (TypeAttr << BoundsWidth) | BoundsAttr; + } + + bool isUnspecified() const { return getBoundsAttr() == Unspecified; } + bool isUnsafeIndexable() const { return getBoundsAttr() == UnsafeIndexable; } + bool isSingle() const { return getBoundsAttr() == Single; } + bool isIndexable() const { return getBoundsAttr() == Indexable; } + bool isBidiIndexable() const { return getBoundsAttr() == BidiIndexable; } + bool isUnsafeOrUnspecified() const { return isUnsafeIndexable() || isUnspecified(); } + + void setUnspecified() { setBoundsAttr(Unspecified); } + void setUnsafeIndexable() { setBoundsAttr(UnsafeIndexable); } + void setSingle() { setBoundsAttr(Single); } + void setIndexable() { setBoundsAttr(Indexable); } + void setBidiIndexable() { setBoundsAttr(BidiIndexable); } + void copyBoundsAttr(BoundsSafetyPointerAttributes FAttr) { + setBoundsAttr(FAttr.getBoundsAttr()); + } + + void copyTypeAttr(BoundsSafetyPointerAttributes FAttr) { + setTypeAttr(FAttr.getTypeAttr()); + } + + BoundsSafetyPointerAttributes(uint32_t value = 0) + : BoundsAttr(value & BoundsMask), + TypeAttr((value & ShiftedTypeMask) >> BoundsWidth) {} + + void print(raw_ostream &OS) const { + switch (getBoundsAttr()) { + case Unspecified: + break; + case UnsafeIndexable: + OS << "__unsafe_indexable"; + break; + case Single: + OS << "__single"; + break; + case Indexable: + OS << "__indexable"; + break; + case BidiIndexable: + OS << "__bidi_indexable"; + break; + default: + llvm_unreachable("Unknown -fbounds-safety bounds only attributes"); + } + + switch (getTypeAttr()) { + case Unspecified: + break; + default: + llvm_unreachable("Unknown -fbounds-safety type attributes"); + } + } + + bool operator==(BoundsSafetyPointerAttributes other) const { + return getAsOpaqueValue() == other.getAsOpaqueValue(); + } + + bool operator!=(BoundsSafetyPointerAttributes other) const { + return !(*this == other); + } + + bool hasRawPointerLayout() const { + return isUnspecified() || isSingle() || isUnsafeIndexable(); + } + + bool hasLowerBound() const { return isBidiIndexable(); } + + bool hasUpperBound() const { return isBidiIndexable() || isIndexable(); } + + void takeBoundsAttr(BoundsSafetyPointerAttributes Other) { + setBoundsAttr(Other.getBoundsAttr()); + } + + static bool areEquivalentLayouts(BoundsSafetyPointerAttributes A1, + BoundsSafetyPointerAttributes A2) { + if (A1 == A2) + return true; + + if (A1.getTypeAttr() != A2.getTypeAttr()) + return false; + + // Single and unsafe have equivalent layout. + if ((A1.isUnsafeOrUnspecified() && A2.hasRawPointerLayout()) || + (A1.hasRawPointerLayout() && A2.isUnsafeOrUnspecified())) + return true; + + return false; + } + + + static bool areCompatible(BoundsSafetyPointerAttributes Left, + BoundsSafetyPointerAttributes Right) { + if (Left == Right) + return true; + + // Allow unspecified pointer attributes to merge with either of + // unsafe_indexable and single. + if (Left.isUnspecified()) + return Right.hasRawPointerLayout(); + if (Right.isUnspecified()) + return Left.hasRawPointerLayout(); + + return false; + } + + static BoundsSafetyPointerAttributes fromOpaqueValue(uint32_t value) { + return BoundsSafetyPointerAttributes(value); + } + + // BoundsSafetyPointerAttributes precedence in conditional operator + // conversion (bounds only): + // 1) __unsafe_indexable or unspecified + // 2) __bidi_indexable + // 3) __indexable + // 4) __single + static BoundsSafetyPointerAttributes merge(BoundsSafetyPointerAttributes A1, + BoundsSafetyPointerAttributes A2) { + uint32_t NewBoundsAtt = A1.getBoundsAttr() > A2.getBoundsAttr() + ? A2.getBoundsAttr() + : A1.getBoundsAttr(); + + uint32_t NewTypeAtt = A1.getTypeAttr() > A2.getTypeAttr() + ? A2.getTypeAttr() + : A1.getTypeAttr(); + + BoundsSafetyPointerAttributes NewAtt; + NewAtt.setBoundsAttr(NewBoundsAtt); + NewAtt.setTypeAttr(NewTypeAtt); + return NewAtt; + } + +private: + void setBoundsAttr(uint32_t attr) { + assert(!(attr & ~BoundsMask) && "attr contains non-bounds bits"); + BoundsAttr = attr; + } + void setTypeAttr(uint32_t attr) { + assert(!(attr & ~TypeMask) && "attr contains non-type bits"); + TypeAttr = attr; + } + +private: + // |0 .. 2|3 .. 4|5 .. 31| + // |Bounds|Type |Reserved| + uint32_t BoundsAttr : 3; + uint32_t TypeAttr : 2; +}; + /// PointerType - C99 6.7.5.1 - Pointer Declarators. class PointerType : public Type, public llvm::FoldingSetNode { friend class ASTContext; // ASTContext creates these. + using FA = BoundsSafetyPointerAttributes; + QualType PointeeType; + FA FAttr; - PointerType(QualType Pointee, QualType CanonicalPtr) + PointerType(QualType Pointee, QualType CanonicalPtr, FA FAttr) : Type(Pointer, CanonicalPtr, Pointee->getDependence()), - PointeeType(Pointee) {} + PointeeType(Pointee), FAttr(FAttr) {} public: QualType getPointeeType() const { return PointeeType; } + FA getPointerAttributes() const { return FAttr; } bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } + bool hasRawPointerLayout() const { return FAttr.hasRawPointerLayout(); } + bool isSafePointer() const { + return !(FAttr.isUnsafeIndexable() || FAttr.isUnspecified()); + } + + bool isSingle() const { return FAttr.isSingle(); } + bool isIndexable() const { return FAttr.isIndexable(); } + bool isBidiIndexable() const { return FAttr.isBidiIndexable(); } + bool isUnsafeIndexable() const { return FAttr.isUnsafeIndexable(); } + bool isUnspecified() const { return FAttr.isUnspecified(); } + void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getPointeeType()); + Profile(ID, getPointeeType(), getPointerAttributes()); } - static void Profile(llvm::FoldingSetNodeID &ID, QualType Pointee) { + static void Profile(llvm::FoldingSetNodeID &ID, QualType Pointee, FA FAttr) { ID.AddPointer(Pointee.getAsOpaquePtr()); + ID.AddInteger(FAttr.getAsOpaqueValue()); } static bool classof(const Type *T) { return T->getTypeClass() == Pointer; } @@ -3276,12 +3561,16 @@ class PointerType : public Type, public llvm::FoldingSetNode { /// arguments of the `counted_by` attribute and the likes. class TypeCoupledDeclRefInfo { public: - using BaseTy = llvm::PointerIntPair; + using BaseTy = llvm::PointerIntPair; private: enum { DerefShift = 0, DerefMask = 1, + /*TO_UPSTREAM(BoundsSafety) ON*/ + MemberShift = 1, + MemberMask = 1, + /*TO_UPSTREAM(BoundsSafety) OFF*/ }; BaseTy Data; @@ -3289,9 +3578,17 @@ class TypeCoupledDeclRefInfo { /// \p D is to a declaration referenced by the argument of attribute. \p Deref /// indicates whether \p D is referenced as a dereferenced form, e.g., \p /// Deref is true for `*n` in `int *__counted_by(*n)`. - TypeCoupledDeclRefInfo(ValueDecl *D = nullptr, bool Deref = false); + /* TO_UPSTREAM(BoundsSafety) ON */ + /// \p Member indicates that \p D is referenced as the member for a struct, + /// e.g __counted_by(hdr.len) `hdr`, although a FieldDecl is referred to by + /// a DeclRefExpr, while `len` is referred to by a MemberExpr. In this + /// example `Member` is false for `hdr` and true for `len`. + TypeCoupledDeclRefInfo(ValueDecl *D = nullptr, bool Deref = false, + bool Member = false); + /* TO_UPSTREAM(BoundsSafety) OFF */ bool isDeref() const; + bool isMember() const; ValueDecl *getDecl() const; unsigned getInt() const; void *getOpaqueValue() const; @@ -3315,6 +3612,16 @@ class BoundsAttributedType : public Type, public llvm::FoldingSetNode { BoundsAttributedType(TypeClass TC, QualType Wrapped, QualType Canon); public: + /* TO_UPSTREAM(BoundsSafety) ON*/ + enum BoundsAttrKind { + CountedBy = 0, + SizedBy, + CountedByOrNull, + SizedByOrNull, + EndedBy + }; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + bool isSugared() const { return true; } QualType desugar() const { return WrappedTy; } @@ -3342,6 +3649,9 @@ class BoundsAttributedType : public Type, public llvm::FoldingSetNode { // annotations. switch (T->getTypeClass()) { case CountAttributed: +/* TO_UPSTREAM(BoundsSafety) ON */ + case DynamicRangePointer: +/* TO_UPSTREAM(BoundsSafety) OFF */ return true; default: return false; @@ -3373,22 +3683,17 @@ class CountAttributedType final } public: - enum DynamicCountPointerKind { - CountedBy = 0, - SizedBy, - CountedByOrNull, - SizedByOrNull, - }; - Expr *getCountExpr() const { return CountExpr; } bool isCountInBytes() const { return CountAttributedTypeBits.CountInBytes; } bool isOrNull() const { return CountAttributedTypeBits.OrNull; } - DynamicCountPointerKind getKind() const { + /* TO_UPSTREAM(BoundsSafety) ON*/ + BoundsAttrKind getKind() const { if (isOrNull()) return isCountInBytes() ? SizedByOrNull : CountedByOrNull; return isCountInBytes() ? SizedBy : CountedBy; } + /* TO_UPSTREAM(BoundsSafety) OFF*/ void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, desugar(), CountExpr, isCountInBytes(), isOrNull()); @@ -3404,6 +3709,119 @@ class CountAttributedType final StringRef getAttributeName(bool WithMacroPrefix) const; }; +/* TO_UPSTREAM(BoundsSafety) ON */ +class DynamicRangePointerType final + : public BoundsAttributedType, + public llvm::TrailingObjects { + friend class ASTContext; + + // We have 0 or 1 primary start and end pointer expressions and 0 or more + // start and end declarations that are associated with the type. + Expr *StartPtr; + Expr *EndPtr; + DynamicRangePointerType(QualType PointerTy, QualType CanPointerTy, + Expr *StartPtr, Expr *EndPtr, + ArrayRef StartPtrDecls, + ArrayRef EndPtrDecls); + + unsigned numTrailingObjects(OverloadToken) const { + return DynamicRangePointerTypeBits.NumEndPtrDecls + + DynamicRangePointerTypeBits.NumStartPtrDecls; + } + +public: + BoundsAttrKind getKind() const { return EndedBy; } + + Expr *getStartPointer() const { return StartPtr; } + Expr *getEndPointer() const { return EndPtr; } + + unsigned getNumStartPtrDecls() const { + return DynamicRangePointerTypeBits.NumStartPtrDecls; + } + + unsigned getNumEndPtrDecls() const { + return DynamicRangePointerTypeBits.NumEndPtrDecls; + } + + decl_iterator startptr_decl_begin() const { + return getTrailingObjects(); + } + + decl_iterator startptr_decl_end() const { + return startptr_decl_begin() + getNumStartPtrDecls(); + } + + decl_range startptr_decls() const { + return decl_range(startptr_decl_begin(), startptr_decl_end()); + } + + ArrayRef getStartPtrDecls() const { + return {startptr_decl_begin(), startptr_decl_end()}; + } + + decl_iterator endptr_decl_begin() const { + return startptr_decl_end(); + } + + decl_iterator endptr_decl_end() const { + return endptr_decl_begin() + getNumEndPtrDecls(); + } + + decl_range endptr_decls() const { + return decl_range(endptr_decl_begin(), endptr_decl_end()); + } + + ArrayRef getEndPtrDecls() const { + return {endptr_decl_begin(), endptr_decl_end()}; + } + + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, desugar(), StartPtr, EndPtr, getNumStartPtrDecls(), + getNumEndPtrDecls()); + } + + static void Profile(llvm::FoldingSetNodeID &ID, QualType PointerTy, + Expr *StartPtr, Expr *EndPtr, unsigned NumStartDecls, + unsigned NumEndDecls); + + static bool classof(const Type *T) { + return T->getTypeClass() == DynamicRangePointer; + } +}; + +class ValueTerminatedType final : public Type, public llvm::FoldingSetNode { + friend class ASTContext; + + QualType OriginalTy; + Expr *TerminatorExpr; + + explicit ValueTerminatedType(QualType OriginalTy, QualType CanOriginalTy, + Expr *TerminatorExpr) + : Type(ValueTerminated, CanOriginalTy, OriginalTy->getDependence()), + OriginalTy(OriginalTy), TerminatorExpr(TerminatorExpr) {} + +public: + bool isSugared() const { return true; } + QualType desugar() const { return OriginalTy; } + + Expr *getTerminatorExpr() const { return TerminatorExpr; } + + llvm::APSInt getTerminatorValue(const ASTContext &Ctx) const; + + void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx) const { + Profile(ID, Ctx, OriginalTy, TerminatorExpr); + } + + static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx, + QualType OriginalTy, const Expr *TerminatorExpr); + + static bool classof(const Type *T) { + return T->getTypeClass() == ValueTerminated; + } +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// Represents a type which was implicitly adjusted by the semantic /// engine for arbitrary reasons. For example, array and function types can /// decay, and function types can have their calling conventions adjusted. @@ -8218,6 +8636,59 @@ inline bool Type::isAnyPointerType() const { inline bool Type::isSignableType() const { return isPointerType(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +inline bool Type::isUnsafeIndexablePointerType() const { + const auto *PT = dyn_cast(CanonicalType); + if (!PT) + return false; + if (PT->isUnsafeIndexable()) + return true; + if (PT->isUnspecified() && hasAttr(attr::PtrUnsafeIndexable)) + return true; + return false; +} + +inline bool Type::isSinglePointerType() const { + const auto *PT = dyn_cast(CanonicalType); + if (!PT) + return false; + if (PT->isSingle()) + return true; + if (PT->isUnspecified() && hasAttr(attr::PtrSingle)) + return true; + return false; +} + +inline bool Type::isBidiIndexablePointerType() const { + const auto *PT = dyn_cast(CanonicalType); + return PT && PT->isBidiIndexable(); +} + +inline bool Type::isIndexablePointerType() const { + const auto *PT = dyn_cast(CanonicalType); + return PT && PT->isIndexable(); +} + +inline bool Type::isUnspecifiedPointerType() const { + return isPointerType() && + !(isUnsafeIndexablePointerType() || isSinglePointerType() || + isBidiIndexablePointerType() || isIndexablePointerType() || + isBoundsAttributedType() || isValueTerminatedType()); +} + +inline bool Type::isSafePointerType() const { + return isPointerType() && + (isSinglePointerType() || isBidiIndexablePointerType() || + isIndexablePointerType() || isBoundsAttributedType() || + isValueTerminatedType()); +} + +inline bool Type::isPointerTypeWithBounds() const { + const auto *PT = dyn_cast(CanonicalType); + return PT && PT->getPointerAttributes().hasUpperBound(); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + inline bool Type::isBlockPointerType() const { return isa(CanonicalType); } diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h index 92661b8b13fe0..375ec871187fb 100644 --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -1179,6 +1179,40 @@ class CountAttributedTypeLoc final SourceRange getLocalSourceRange() const; }; +/* TO_UPSTREAM(BoundsSafety) ON */ +class DynamicRangePointerTypeLoc final + : public InheritingConcreteTypeLoc { +public: + Expr *getStartPointer() const { return getTypePtr()->getStartPointer(); } + Expr *getEndPointer() const { return getTypePtr()->getEndPointer(); } + + SourceRange getLocalSourceRange() const; +}; + +struct ValueTerminatedTypeLocInfo {}; + +class ValueTerminatedTypeLoc final + : public ConcreteTypeLoc { +public: + TypeLoc getOriginalLoc() const { return getInnerTypeLoc(); } + + void initializeLocal(ASTContext &Context, SourceLocation Loc) { + // do nothing + } + + QualType getInnerType() const { return getTypePtr()->desugar(); } + + SourceRange getLocalSourceRange() const { return {}; } + + unsigned getLocalDataSize() const { return 0; } + + Expr *getTerminatorExpr() const { return getTypePtr()->getTerminatorExpr(); } +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + struct MacroQualifiedLocInfo { SourceLocation ExpansionLoc; }; @@ -1340,6 +1374,10 @@ class PointerTypeLoc : public PointerLikeTypeLocgetTypePtr()->getPointerAttributes(); + } }; /// Wrapper for source info for block pointers. diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index f4b8ce0994ba8..7694a10717bb8 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -22,7 +22,13 @@ let Class = PointerType in { let Read = [{ node->getPointeeType() }]; } - def : Creator<[{ return ctx.getPointerType(pointeeType); }]>; + // TO_UPSTREAM(BoundsSafety) ON + def : Property<"pointerAttributes", BoundsSafetyPointerAttributes> { + let Read = [{ node->getPointerAttributes() }]; + } + + def : Creator<[{ return ctx.getPointerType(pointeeType, pointerAttributes); }]>; + // TO_UPSTREAM(BoundsSafety) OFF } let Class = CountAttributedType in { @@ -44,6 +50,40 @@ let Class = CountAttributedType in { def : Creator<[{ return ctx.getCountAttributedType(WrappedTy, CountExpr, CountInBytes, OrNull, CoupledDecls); }]>; } +// TO_UPSTREAM(BoundsSafety) ON +let Class = DynamicRangePointerType in { + def : Property<"PointerTy", QualType> { + let Read = [{ node->desugar() }]; + } + def : Property<"StartPtr", ExprRef> { + let Read = [{ node->getStartPointer() }]; + } + def : Property<"EndPtr", ExprRef> { + let Read = [{ node->getEndPointer() }]; + } + def : Property<"StartPtrDecls", Array> { + let Read = [{ node->getStartPtrDecls() }]; + } + def : Property<"EndPtrDecls", Array> { + let Read = [{ node->getEndPtrDecls() }]; + } + def : Creator<[{ return ctx.getDynamicRangePointerType(PointerTy, StartPtr, EndPtr, + StartPtrDecls, EndPtrDecls); }]>; +} + +let Class = ValueTerminatedType in { + def : Property<"originalType", QualType> { + let Read = [{ node->desugar() }]; + } + def : Property<"terminatorExpr", ExprRef> { + let Read = [{ node->getTerminatorExpr() }]; + } + def : Creator<[{ + return ctx.getValueTerminatedType(originalType, terminatorExpr); + }]>; +} +// TO_UPSTREAM(BoundsSafety) OFF + let Class = AdjustedType in { def : Property<"originalType", QualType> { let Read = [{ node->getOriginalType() }]; diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h index 9b53f1dc1d759..47753a7efdbbd 100644 --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h @@ -127,6 +127,23 @@ class UnsafeBufferUsageHandler { bool IsRelatedToDecl, ASTContext &Ctx) = 0; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Invoked when an unsafe passing to count-attributed pointer is found. + virtual void handleUnsafeCountAttributedPointerArgument(const CallExpr *Call, + const Expr *Arg, + bool IsRelatedToDecl, + ASTContext &Ctx) { + handleUnsafeOperation(Arg, IsRelatedToDecl, Ctx); + } + + /// Invoked when an unsafe passing to __single pointer is found. + virtual void handleUnsafeSinglePointerArgument(const Expr *Arg, + bool IsRelatedToDecl, + ASTContext &Ctx) { + handleUnsafeOperation(Arg, IsRelatedToDecl, Ctx); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Invoked when a fix is suggested against a variable. This function groups /// all variables that must be fixed together (i.e their types must be changed /// to the same target type to prevent type mismatches) into a single fixit. diff --git a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def index 09fa510bc0472..6af6ea2c2c338 100644 --- a/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def +++ b/clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def @@ -24,6 +24,12 @@ #define WARNING_OPTIONAL_GADGET(name) WARNING_GADGET(name) #endif +/// A `WARNING_GADGET` subset, each of which corresponds to an unsafe +/// interaction with bounds-attributed constructs +#ifndef WARNING_BOUNDS_SAFETY_GADGET +#define WARNING_BOUNDS_SAFETY_GADGET(name) WARNING_GADGET(name) +#endif + /// Safe gadgets correspond to code patterns that aren't unsafe but need to be /// properly recognized in order to emit correct warnings and fixes over unsafe /// gadgets. @@ -38,6 +44,10 @@ WARNING_GADGET(PointerArithmetic) WARNING_GADGET(UnsafeBufferUsageAttr) WARNING_GADGET(UnsafeBufferUsageCtorAttr) WARNING_GADGET(DataInvocation) +// TO_UPSTREAM(BoundsSafety) ON +WARNING_BOUNDS_SAFETY_GADGET(CountAttributedPointerArgument) +WARNING_BOUNDS_SAFETY_GADGET(SinglePointerArgument) +// TO_UPSTREAM(BoundsSafety) OFF WARNING_OPTIONAL_GADGET(UnsafeLibcFunctionCall) WARNING_OPTIONAL_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, arg1)` FIXABLE_GADGET(ULCArraySubscript) // `DRE[any]` in an Unspecified Lvalue Context @@ -54,4 +64,5 @@ FIXABLE_GADGET(PointerInit) #undef FIXABLE_GADGET #undef WARNING_GADGET #undef WARNING_OPTIONAL_GADGET +#undef WARNING_BOUNDS_SAFETY_GADGET #undef GADGET diff --git a/clang/include/clang/Basic/AllDiagnosticKinds.inc b/clang/include/clang/Basic/AllDiagnosticKinds.inc index a946b4a640ac6..83498a857eae4 100644 --- a/clang/include/clang/Basic/AllDiagnosticKinds.inc +++ b/clang/include/clang/Basic/AllDiagnosticKinds.inc @@ -30,4 +30,5 @@ #include "clang/Basic/DiagnosticAnalysisKinds.inc" #include "clang/Basic/DiagnosticRefactoringKinds.inc" #include "clang/Basic/DiagnosticInstallAPIKinds.inc" +#include "clang/Basic/DiagnosticCASKinds.inc" // clang-format on diff --git a/clang/include/clang/Basic/AllDiagnostics.h b/clang/include/clang/Basic/AllDiagnostics.h index e64634cc138f7..b71748483b065 100644 --- a/clang/include/clang/Basic/AllDiagnostics.h +++ b/clang/include/clang/Basic/AllDiagnostics.h @@ -16,6 +16,7 @@ #include "clang/Basic/DiagnosticAST.h" #include "clang/Basic/DiagnosticAnalysis.h" +#include "clang/Basic/DiagnosticCAS.h" #include "clang/Basic/DiagnosticComment.h" #include "clang/Basic/DiagnosticCrossTU.h" #include "clang/Basic/DiagnosticDriver.h" diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index dcdcff8c46fe2..0c168e294633e 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -126,6 +126,10 @@ def NonStaticNonConstCXXMethod [{!S->isStatic() && !S->isConst()}], "non-static non-const member functions">; +def ObjCClassMethod : SubsetSubjectisInstanceMethod()}], + "Objective-C class methods">; + def ObjCInstanceMethod : SubsetSubjectisInstanceMethod()}], "Objective-C instance methods">; @@ -259,6 +263,8 @@ class VariadicUnsignedArgument : Argument; class VariadicExprArgument : Argument; class VariadicStringArgument : Argument; class VariadicIdentifierArgument : Argument; +// TO_UPSTREAM(BoundsSafety) +class VariadicDeclArgument : Argument; // Like VariadicUnsignedArgument except values are ParamIdx. class VariadicParamIdxArgument : Argument; @@ -436,6 +442,19 @@ def HLSL : LangOpt<"HLSL">; // Language option for CMSE extensions def Cmse : LangOpt<"Cmse">; +// TO_UPSTREAM(BoundsSafety) ON +def BoundsSafety : LangOpt<"BoundsSafety">; +def BoundsSafetyAttributes : LangOpt<"BoundsSafetyAttributes">; + +// 'counted_by' is now available without -fbounds-safety for COnly (!LangOpts.CPlusPlus). +// Sema still ignores the attribute (with a warning) if it's applied to subjects other than +// flexible array members. +def CountedBySupported : LangOpt<"", [{ + // LangOpts.BoundsSafetyAttributes is to make it still available for experimental C++/Obj-C++. + !LangOpts.CPlusPlus || LangOpts.BoundsSafetyAttributes +}]>; +// TO_UPSTREAM(BoundsSafety) OFF + // Defines targets for target-specific attributes. Empty lists are unchecked. class TargetSpec { // Specifies Architectures for which the target applies, based off the @@ -904,6 +923,12 @@ def Artificial : InheritableAttr { let SimpleHandler = 1; } +def TransparentStepping: InheritableAttr { + let Spellings = [Clang<"transparent_stepping">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [TransparentSteppingDocs]; +} + def XRayInstrument : InheritableAttr { let Spellings = [Clang<"xray_always_instrument">, Clang<"xray_never_instrument">]; @@ -1174,6 +1199,25 @@ static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environm let Documentation = [AvailabilityDocs]; } +def DomainAvailability : InheritableAttr { + // This attribute has no spellings as it is created implicitly when + // __attribute__((availability(domain:...))) is seen. + let Spellings = []; + let Args = [StringArgument<"domain">, BoolArgument<"unavailable">]; + let AdditionalMembers = [{ + std::string getFeatureAttributeStr() const; + }]; + let Documentation = [InternalOnly]; +} + +// This attribute is used to annotate structs of type `_AvailabilityDomain`. +def AvailabilityDomain : InheritableAttr { + let Spellings = [Clang<"availability_domain">]; + let Args = [IdentifierArgument<"name">]; + let Subjects = SubjectList<[Var]>; + let Documentation = [Undocumented]; +} + def ExternalSourceSymbol : InheritableAttr { let Spellings = [Clang<"external_source_symbol", /*allowInC=*/1, /*version=*/20230206>]; @@ -2479,44 +2523,150 @@ def TypeNullUnspecified : TypeAttr { def CountedBy : DeclOrTypeAttr { let Spellings = [Clang<"counted_by">]; - let Subjects = SubjectList<[Field], ErrorDiag>; + // TO_UPSTREAM(BoundsSafety) ON + // `Subjects` which is present upstream is commented out because internally + // the attribute can go on Decls other than FieldDecl. + // + // let Subjects = SubjectList<[Field], ErrorDiag>; + // let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>]; let LateParsed = LateAttrParseExperimentalExt; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; - let LangOpts = [COnly]; + let LangOpts = [CountedBySupported]; + // TO_UPSTREAM(BoundsSafety) OFF } def CountedByOrNull : DeclOrTypeAttr { + // TO_UPSTREAM(BoundsSafety) ON + // `Subjects` which is present upstream is commented out because internally + // the attribute can go on Decls other than FieldDecl. + // + // let Subjects = SubjectList<[Field], ErrorDiag>; + // let Spellings = [Clang<"counted_by_or_null">]; - let Subjects = SubjectList<[Field], ErrorDiag>; let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>]; let LateParsed = LateAttrParseExperimentalExt; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; - let LangOpts = [COnly]; + let LangOpts = [CountedBySupported]; + // TO_UPSTREAM(BoundsSafety) OFF } def SizedBy : DeclOrTypeAttr { + // TO_UPSTREAM(BoundsSafety) ON + // `Subjects` which is present upstream is commented out because internally + // the attribute can go on Decls other than FieldDecl. + // + // let Subjects = SubjectList<[Field], ErrorDiag>; + // let Spellings = [Clang<"sized_by">]; - let Subjects = SubjectList<[Field], ErrorDiag>; let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>]; let LateParsed = LateAttrParseExperimentalExt; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; - let LangOpts = [COnly]; + let LangOpts = [CountedBySupported]; + // TO_UPSTREAM(BoundsSafety) OFF } def SizedByOrNull : DeclOrTypeAttr { + // TO_UPSTREAM(BoundsSafety) ON + // `Subjects` which is present upstream is commented out because internally + // the attribute can go on Decls other than FieldDecl. + // + // let Subjects = SubjectList<[Field], ErrorDiag>; + // let Spellings = [Clang<"sized_by_or_null">]; - let Subjects = SubjectList<[Field], ErrorDiag>; let Args = [ExprArgument<"Size">, IntArgument<"NestedLevel", 1>]; let LateParsed = LateAttrParseExperimentalExt; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; - let LangOpts = [COnly]; + let LangOpts = [CountedBySupported]; + // TO_UPSTREAM(BoundsSafety) OFF +} + +// TO_UPSTREAM(BoundsSafety) ON +// BoundsSafety pointer type attributes. +def PtrUnsafeIndexable : TypeAttr { + let Spellings = [Clang<"unsafe_indexable">]; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; } +def PtrSingle : TypeAttr { + let Spellings = [Clang<"single">]; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +def PtrIndexable : TypeAttr { + let Spellings = [Clang<"indexable">]; + let LangOpts = [BoundsSafety]; + let Documentation = [Undocumented]; +} + +def PtrBidiIndexable : TypeAttr { + let Spellings = [Clang<"bidi_indexable">]; + let LangOpts = [BoundsSafety]; + let Documentation = [Undocumented]; +} + +def PtrEndedBy : DeclOrTypeAttr { + let Spellings = [Clang<"ended_by">]; + let Args = [ExprArgument<"EndPtr">, IntArgument<"NestedLevel">]; + let LateParsed = LateAttrParseExperimentalExt; + let ParseArgumentsAsUnevaluated = 1; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +def PtrTerminatedBy : TypeAttr { + let Spellings = [Clang<"terminated_by">]; + let Args = [ExprArgument<"Terminator">]; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +def UnsafeLateConst : InheritableAttr { + let Spellings = [Clang<"unsafe_late_const">]; + let Subjects = SubjectList<[GlobalVar], ErrorDiag>; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +def ArrayDecayDiscardsCountInParameters : TypeAttr { + let Spellings = [Clang<"decay_discards_count_in_parameters">]; + let LangOpts = [BoundsSafetyAttributes]; + let Documentation = [Undocumented]; +} + +// BoundsSafety: Don't need to gate implicit attributes. +def PtrAutoAttr : TypeAttr { + // This attribute has no spellings as it is only ever created implicitly + // during -fbounds-safety's implicit pointer type bounding. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [InternalOnly]; +} + +def PtrAutoNullTerminatedAttr : TypeAttr { + // This attribute has no spellings as it is only ever created implicitly + // during -fbounds-safety's implicit pointer type bounding. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [InternalOnly]; +} + +def DependerDecls : InheritableAttr { + let Spellings = []; + let Args = [BoolArgument<"IsDeref">, + VariadicDeclArgument<"DependerDecls">, + VariadicUnsignedArgument<"DependerLevels">]; + let Subjects = SubjectList<[Field, Var]>; + let Documentation = [InternalOnly]; +} +// TO_UPSTREAM(BoundsSafety) OFF + // This is a marker used to indicate that an __unsafe_unretained qualifier was // ignored because ARC is not enabled. The usual representation for this // qualifier is as an ObjCOwnership attribute with Kind == "none". @@ -2750,6 +2900,12 @@ def ObjCSubclassingRestricted : InheritableAttr { let SimpleHandler = 1; } +def ObjCCompleteDefinition : InheritableAttr { + let Spellings = [GNU<"objc_complete_definition">]; + let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; + let Documentation = [Undocumented]; +} + def ObjCExplicitProtocolImpl : InheritableAttr { let Spellings = [Clang<"objc_protocol_requires_explicit_implementation">]; let Subjects = SubjectList<[ObjCProtocol], ErrorDiag>; @@ -3023,7 +3179,7 @@ def SwiftImportAsNonGeneric : InheritableAttr { let Documentation = [InternalOnly]; } -def SwiftImportPropertyAsAccessors : InheritableAttr { +def SwiftImportPropertyAsAccessors : InheritableAttr { // This attribute has no spellings as it is only ever created implicitly // from API notes. let Spellings = []; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 6e2dbe5655189..bd015ba2b9157 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -8619,6 +8619,66 @@ As such, this function attribute is currently only supported on X86 targets. }]; } +def TransparentSteppingDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``transparent_stepping`` attribute is intended as a hint for debuggers that this +function itself is not interesting, but it calls a function that might be. So, when +stepping in arrives at a function with this attribute, debuggers should transparently +step-in through it into the functions called by the annotated function (but not by +subsequent calls made by those functions), stopping at the first one its normal rules +for whether to stop says to stop at - or stepping out again if none qualify. Also, when +stepping out arrives at a function with this attribute, the debugger should continue +stepping out to its caller. + +For example: + +.. code-block:: c + + int bar(void) { + return 42; + } + + __attribute__((transparent_stepping)) + int foo(void) { + return bar(); + } + + int caller(void) { + return foo(); + } + +Stepping into ``foo`` should step directly into ``bar`` instead, and stepping out of ``bar`` +should stop in ``caller``. + +Functions with the ``transparent_stepping`` attribute can be chained together: + +.. code-block:: c + + int baz(void) { + return 42; + } + + __attribute__((transparent_stepping)) + int bar(void) { + return baz(); + } + + __attribute__((transparent_stepping)) + int foo(void) { + return bar(); + } + + int caller(void) { + return foo(); + } + +In this example, stepping into ``foo`` should step directly into ``baz``, and stepping out of +``baz`` should stop in ``caller``. + }]; +} + + def ReadOnlyPlacementDocs : Documentation { let Category = DocCatType; let Content = [{This attribute is attached to a structure, class or union declaration. diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 37bdac814a347..3d492eff07e0a 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -2663,7 +2663,7 @@ def Rotl : MSLangBuiltin { def Lrotl : MSLangBuiltin { let Spellings = ["_lrotl"]; let Attributes = [NoThrow, Constexpr]; - let Prototype = "unsigned long int(unsigned long int, int)"; + let Prototype = "msuint32_t(msuint32_t, int)"; } def Rotl64 : MSLangBuiltin { @@ -2693,7 +2693,7 @@ def Rotr : MSLangBuiltin { def Lrotr : MSLangBuiltin { let Spellings = ["_lrotr"]; let Attributes = [NoThrow, Constexpr]; - let Prototype = "unsigned long int(unsigned long int, int)"; + let Prototype = "msuint32_t(msuint32_t, int)"; } def Rotr64 : MSLangBuiltin { diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt index 265ea1fc06494..c6e711aeacd3b 100644 --- a/clang/include/clang/Basic/CMakeLists.txt +++ b/clang/include/clang/Basic/CMakeLists.txt @@ -22,6 +22,7 @@ endmacro(clang_diag_gen) clang_diag_gen(Analysis) clang_diag_gen(AST) +clang_diag_gen(CAS) clang_diag_gen(Comment) clang_diag_gen(Common) clang_diag_gen(CrossTU) diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index c5990fb248689..94141e98b8b5d 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -181,6 +181,7 @@ CODEGENOPT(IncrementalLinkerCompatible, 1, 0) ///< Emit an object file which can ///< linker. CODEGENOPT(MergeAllConstants , 1, 1) ///< Merge identical constants. CODEGENOPT(MergeFunctions , 1, 0) ///< Set when -fmerge-functions is enabled. +CODEGENOPT(SplitColdCode , 1, 0) ///< Set when -fsplit-cold-code is enabled. CODEGENOPT(NoCommon , 1, 0) ///< Set when -fno-common or C++ is enabled. CODEGENOPT(NoExecStack , 1, 0) ///< Set when -Wa,--noexecstack is enabled. CODEGENOPT(MipsMsa , 1, 0) ///< Set when -Wa,-mmsa is enabled. @@ -333,6 +334,15 @@ CODEGENOPT(VectorizeLoop , 1, 0) ///< Run loop vectorizer. CODEGENOPT(VectorizeSLP , 1, 0) ///< Run SLP vectorizer. CODEGENOPT(ProfileSampleAccurate, 1, 0) ///< Sample profile is accurate. +/* TO_UPSTREAM(BoundsSafety) ON*/ +CODEGENOPT(TrapFuncReturns , 1, 0) ///< When true, the function specified with + ///< -ftrap-function may return normally +CODEGENOPT( + UniqueTrapBlocks, 1, + 0) ///< When true, basic blocks that contain traps they will be prevented + ///< from being merged by optimization passes and the backends. +/* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Treat loops as finite: language, always, never. ENUM_CODEGENOPT(FiniteLoops, FiniteLoopsKind, 2, FiniteLoopsKind::Language) @@ -466,6 +476,16 @@ CODEGENOPT(SkipRaxSetup, 1, 0) ENUM_CODEGENOPT(ZeroCallUsedRegs, llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind, 5, llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::Skip) +/// Whether to use CASID backend. +CODEGENOPT(UseCASBackend, 1, 0) + +/// Whether to emit a .casid File. +CODEGENOPT(EmitCASIDFile, 1, 0) + +/// The CASObjectFormat used when CAS output is used. +ENUM_CODEGENOPT(CASObjMode, llvm::CASBackendMode, + 2, llvm::CASBackendMode::Native) + /// Modify C++ ABI to returning `this` pointer from constructors and /// non-deleting destructors. (No effect on Microsoft ABI.) CODEGENOPT(CtorDtorReturnThis, 1, 0) diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index e39a73bdb13ac..a7f4bf1f23625 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -169,6 +169,8 @@ class CodeGenOptions : public CodeGenOptionsBase { Enabled, Forced, }; + /// The callback for mc result. + std::optional MCCallBack; /// The code model to use (-mcmodel). std::string CodeModel; diff --git a/clang/include/clang/Basic/DebugOptions.def b/clang/include/clang/Basic/DebugOptions.def index bc96d5dfdf890..0b4a6712f7857 100644 --- a/clang/include/clang/Basic/DebugOptions.def +++ b/clang/include/clang/Basic/DebugOptions.def @@ -107,6 +107,9 @@ ENUM_DEBUGOPT(DebugSimpleTemplateNames, ENUM_DEBUGOPT(DebugInfo, llvm::codegenoptions::DebugInfoKind, 4, llvm::codegenoptions::NoDebugInfo) +/// Whether to make debug info reproducible. +DEBUGOPT(ReproducibleDebugInfo, 1, 0) + /// Whether to generate macro debug info. DEBUGOPT(MacroDebugInfo, 1, 0) diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index 19524856a9bb3..f7495dc257bb2 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -39,6 +39,7 @@ namespace llvm { class Error; +class format_object_base; class raw_ostream; class MemoryBuffer; namespace vfs { @@ -905,6 +906,18 @@ class DiagnosticsEngine : public RefCountedBase { StringRef(FormatString, N - 1)); } + unsigned getCustomDiagID(DiagnosticIDs::CustomDiagDesc Desc) { + return Diags->getCustomDiagID(std::move(Desc)); + } + + std::optional getMaxCustomDiagID() const { + return Diags->getMaxCustomDiagID(); + } + const DiagnosticIDs::CustomDiagDesc & + getCustomDiagDesc(unsigned DiagID) const { + return Diags->getCustomDiagDesc(DiagID); + } + /// Converts a diagnostic argument (as an intptr_t) into the string /// that represents it. void ConvertArgToString(ArgumentKind Kind, intptr_t Val, @@ -1505,6 +1518,9 @@ inline DiagnosticBuilder DiagnosticsEngine::Report(SourceLocation Loc, const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, llvm::Error &&E); +const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, + const llvm::format_object_base &Fmt); + inline DiagnosticBuilder DiagnosticsEngine::Report(unsigned DiagID) { return Report(SourceLocation(), DiagID); } diff --git a/clang/include/clang/Basic/Diagnostic.td b/clang/include/clang/Basic/Diagnostic.td index 65b19f3feea4f..8f8862773e112 100644 --- a/clang/include/clang/Basic/Diagnostic.td +++ b/clang/include/clang/Basic/Diagnostic.td @@ -224,6 +224,7 @@ multiclass CXX26Compat : CXXCompat, DefaultIgnore; +// TO_UPSTREAM(BoundsSafety) ON +def err_bounds_safety_evaluate_no_bounds : Error< + "cannot get %select{lower,upper}0 bound because object size is unknown">; +// TO_UPSTREAM(BoundsSafety) OFF + // -Wunaligned-access def warn_unaligned_access : Warning< "field %1 within %0 is less aligned than %2 and is usually due to %0 being " diff --git a/clang/include/clang/Basic/DiagnosticCAS.h b/clang/include/clang/Basic/DiagnosticCAS.h new file mode 100644 index 0000000000000..dce073b4e3923 --- /dev/null +++ b/clang/include/clang/Basic/DiagnosticCAS.h @@ -0,0 +1,28 @@ +//===--- DiagnosticCAS.h - Diagnostics for the CAS --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_DIAGNOSTICCAS_H +#define LLVM_CLANG_BASIC_DIAGNOSTICCAS_H + +#include "clang/Basic/Diagnostic.h" + +namespace clang { +namespace diag { +enum { +#define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \ + SHOWINSYSHEADER, SHOWINSYSMACRO, DEFERRABLE, CATEGORY) \ + ENUM, +#define CASSTART +#include "clang/Basic/DiagnosticCASKinds.inc" +#undef DIAG + NUM_BUILTIN_CAS_DIAGNOSTICS +}; +} // end namespace diag +} // end namespace clang + +#endif // LLVM_CLANG_BASIC_DIAGNOSTICCAS_H diff --git a/clang/include/clang/Basic/DiagnosticCASKinds.td b/clang/include/clang/Basic/DiagnosticCASKinds.td new file mode 100644 index 0000000000000..7821889c21f8a --- /dev/null +++ b/clang/include/clang/Basic/DiagnosticCASKinds.td @@ -0,0 +1,82 @@ +//===- DiagnosticCASKinds.td - CAS diagnostics ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +let Component = "CAS" in { + +def err_cas_cannot_be_initialized : Error< + "CAS cannot be initialized from the specified '-fcas-*' options: %0">, + DefaultFatal; +def err_cas_cannot_parse_root_id : Error< + "CAS cannot parse root-id '%0' specified by -fcas-fs">, DefaultFatal; +def err_cas_filesystem_cannot_be_initialized : Error< + "CAS filesystem cannot be initialized from root-id '%0': %1">, DefaultFatal; +def err_cas_filesystem_cannot_set_working_directory : Error< + "CAS filesystem cannot set working directory to '%0' specified by" + " -fcas-fs-working-directory">, DefaultFatal; +def err_cas_depscan_daemon_connection: Error< + "failed to establish connection with depscan daemon: %0">, DefaultFatal; +def err_cas_depscan_failed: Error< + "CAS-based dependency scan failed: %0">, DefaultFatal; +def err_cas_store: Error<"failed to store to CAS: %0">, DefaultFatal; +def err_cas_cannot_get_module_cache_key : Error< + "CAS cannot load module with key '%0' from %1: %2">, DefaultFatal; +def err_cas_missing_root_id : Error< + "CAS missing expected root-id '%0'">, DefaultFatal; +def err_cas_cannot_parse_include_tree_id : Error< + "CAS cannot parse include-tree-id '%0'">, DefaultFatal; +def err_cas_missing_include_tree_id : Error< + "CAS missing expected include-tree '%0'">, DefaultFatal; +def err_cas_cannot_parse_input_cache_key : Error< + "CAS cannot parse input cache key '%0'">, DefaultFatal; +def err_cas_cannot_lookup_input_cache_key : Error< + "CAS cannot lookup input cache key '%0'">, DefaultFatal; +def err_cas_missing_input_cache_entry : Error< + "CAS missing expected input cache key '%0'">, DefaultFatal; +def err_cas_cannot_load_api_notes_include_tree : Error< + "cannot load APINotes from include-tree-id '%0'">, DefaultFatal; + +def warn_clang_cache_disabled_caching: Warning< + "caching disabled because %0">, + InGroup>; +def err_clang_cache_failed_execution: Error< + "clang-cache failed to execute compiler: %0">; +def err_clang_cache_cannot_find_binary: Error< + "clang-cache cannot find compiler binary %0">; +def err_clang_cache_missing_compiler_command: Error< + "missing compiler command for clang-cache">; +def err_clang_cache_scanserve_missing_args: Error< + "missing arguments for dep-scanning server mode">; +def err_caching_backend_fail: Error< + "caching backend error: %0">, DefaultFatal; +def err_caching_output_non_deterministic: Error< + "caching failed because the output can be non-deterministic">; + +def remark_compile_job_cache_hit : Remark< + "compile job cache hit for '%0' => '%1'">, InGroup; +def remark_compile_job_cache_miss : Remark< + "compile job cache miss for '%0'">, InGroup; +def remark_compile_job_cache_miss_result_not_found : Remark< + "compile job cache miss for '%0' (result not found: '%1')">, + InGroup; +def remark_compile_job_cache_backend_output_not_found : Remark< + "compile job cache backend did not find output '%0' for key '%1' (output ID: '%2')">, + InGroup; +def remark_compile_job_cache_skipped : Remark< + "compile job cache skipped for '%0'">, InGroup; +def remark_compile_job_cache_timing_depscan : Remark< + "compile job dependency scanning time: %0">, InGroup; +def remark_compile_job_cache_timing_backend_key_query : Remark< + "compile job cache backend key query time: %0">, InGroup; +def remark_compile_job_cache_timing_backend_key_update : Remark< + "compile job cache backend key update time: %0">, InGroup; +def remark_compile_job_cache_timing_backend_load : Remark< + "compile job cache backend load artifacts time: %0">, InGroup; +def remark_compile_job_cache_timing_backend_store : Remark< + "compile job cache backend store artifacts time: %0">, InGroup; + +} // let Component = "CAS" in diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index b15cba698030c..4290fe216bb73 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -268,6 +268,10 @@ def err_drv_cannot_read_config_file : Error< "cannot read configuration file '%0': %1">; def err_drv_arg_requires_bitcode_input: Error< "option '%0' requires input to be LLVM bitcode">; +/* TO_UPSTREAM(BoundsSafety) ON*/ +def err_drv_option_requires_option: Error< + "cannot use '%0' without using '%1'">; +/* TO_UPSTREAM(BoundsSafety) OFF*/ def err_target_unsupported_arch : Error<"the target architecture '%0' is not supported by the target '%1'">; @@ -308,6 +312,10 @@ def err_drv_invalid_value : Error<"invalid value '%1' in '%0'">; def err_drv_invalid_int_value : Error<"invalid integral value '%1' in '%0'">; def err_drv_invalid_value_with_suggestion : Error< "invalid value '%1' in '%0', expected one of: %2">; +/* TO_UPSTREAM(BoundsSafety) ON*/ +def err_drv_invalid_value_with_flag_suggestion : Error< + "invalid value '%1' in '%0'; did you mean '%2'?">; +/* TO_UPSTREAM(BoundsSafety) OFF*/ def err_drv_alignment_not_power_of_two : Error<"alignment is not a power of 2 in '%0'">; def err_drv_invalid_remap_file : Error< "invalid option '%0' not of the form ;">; @@ -361,6 +369,9 @@ def err_drv_omp_host_ir_file_not_found : Error< "target regions but cannot be found">; def err_drv_omp_host_target_not_supported : Error< "target '%0' is not a supported OpenMP host target">; +def err_drv_ptrauth_not_supported : Error< + "target '%0' does not support native pointer authentication">; + def err_drv_expecting_fopenmp_with_fopenmp_targets : Error< "'-fopenmp-targets' must be used in conjunction with a '-fopenmp' option " "compatible with offloading; e.g., '-fopenmp=libomp' or '-fopenmp=libiomp5'">; @@ -566,6 +577,9 @@ def err_test_module_file_extension_format : Error< def err_drv_module_output_with_multiple_arch : Error< "option '-fmodule-output' cannot be used with multiple arch options">; +def err_modules_no_lsv : Error<"standard C++ modules require " + "the -fmodules-local-submodule-visibility -cc1 option">; + def err_drv_reduced_module_output_overrided : Warning< "the implicit output of reduced BMI may be overrided by the output file specified by '--precompile'. " "please consider use '-fmodule-output=' to specify the output file for reduced BMI explicitly">, @@ -779,6 +793,13 @@ def warn_target_override_arm64ec : Warning< def err_drv_target_variant_invalid : Error< "unsupported '%0' value '%1'; use 'ios-macabi' instead">; +def err_drv_inputs_and_cas_input : Error< + "passing input files is incompatible with '-fcas-include-tree' and '-fcas-input-file-cache-key'">; +def err_drv_incompatible_option_include_tree : Error< + "passing incompatible option '%0' with '-fcas-include-tree'">; +def err_drv_include_tree_miss_input_kind : Error< + "missing '-x' input kind when using '-fcas-include-tree'">; + def err_drv_invalid_directx_shader_module : Error< "invalid profile : %0">; def err_drv_dxc_missing_target_profile : Error< diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 8a8db27490f06..6c68170bf2308 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -12,6 +12,7 @@ let Component = "Frontend" in { def err_fe_error_opening : Error<"error opening '%0': %1">; def err_fe_error_reading : Error<"error reading '%0': %1">; +def err_fe_error_writing : Error<"error writing '%0': %1">; def err_fe_error_reading_stdin : Error<"error reading stdin: %0">; def err_fe_error_backend : Error<"error in backend: %0">, DefaultFatal; @@ -141,6 +142,18 @@ def err_fe_invalid_exception_model def err_fe_invalid_source_date_epoch : Error< "environment variable 'SOURCE_DATE_EPOCH' ('%0') must be a non-negative decimal integer <= %1">; +def err_feature_availability_flag_invalid_value : Error< + "invalid value '%0' passed to '-ffeature-availability='; expected :">; + +def err_fe_incompatible_option_with_remote_cache : Error< + "'%0' is incompatible with remote caching backend">; +def err_fe_unable_to_load_include_tree : Error< + "unable to load the CAS include-tree '%0': '%1'">; +def err_unable_to_load_include_tree_node : Error< + "unable to load a node from the CAS include-tree: '%0'">; +def err_fe_unable_to_load_input_cache_key : Error< + "unable to load the input CAS cache key '%0': '%1'">; + def err_fe_unable_to_load_basic_block_sections_file : Error< "unable to load basic block sections function list: '%0'">; @@ -227,6 +240,8 @@ def err_missing_module_name : Error< def err_file_is_not_module : Error<"file '%0' is not a module file">, DefaultFatal; def err_missing_module : Error< "no module named '%0' declared in module map file '%1'">, DefaultFatal; +def err_missing_module_include_tree : Error< + "no module named '%0' declared in include-tree module map '%1'">, DefaultFatal; def err_no_submodule : Error<"no submodule named %0 in module '%1'">; def err_no_submodule_suggest : Error< "no submodule named %0 in module '%1'; did you mean '%2'?">; @@ -265,6 +280,10 @@ def err_frontend_action_unsupported_input_format "'%select{Source|ModuleMap|Precompiled|Unknown}2'">, DefaultFatal; +def remark_index_producing_module_file_data : Remark<"producing index data for " + "module file '%0'">, + InGroup; + def err_test_module_file_extension_version : Error< "test module file extension '%0' has different version (%1.%2) than expected " "(%3.%4)">; @@ -353,6 +372,38 @@ def warn_alias_with_section : Warning< "as the %select{aliasee|resolver}2">, InGroup; +// TO_UPSTREAM(BoundsSafety) ON +def error_bounds_safety_lang_not_supported : Error< + "-fbounds-safety is supported only for C language">; + +def err_bounds_safety_attributes_cannot_be_disabled : Error< + "-fexperimental-bounds-safety-attributes cannot be disabled when " + "-fbounds-safety is enabled">; + +def err_bounds_safety_initializer_out_of_range : Error< + "initializing value is out of valid range">; + +def warn_bounds_attributes_cxx_experimental_ignored : Warning< + "-fbounds-attributes-cxx-experimental without -fbounds-attributes is " + "ignored">, + InGroup; + +def warn_bounds_attributes_objc_experimental_ignored : Warning< + "-fbounds-attributes-objc-experimental without -fbounds-attributes is " + "ignored">, + InGroup; + +def warn_bounds_safety_relaxed_system_headers_ignored : Warning< + "-fno-bounds-safety-relaxed-system-headers without -fbounds-safety is " + "ignored">, + InGroup; + +def warn_bounds_safety_adoption_mode_ignored : Warning< + "-fbounds-safety-adoption-mode without -fbounds-safety is " + "ignored">, + InGroup; +// TO_UPSTREAM(BoundsSafety) OFF + let CategoryName = "Instrumentation Issue" in { def warn_profile_data_out_of_date : Warning< "profile data may be out of date: of %0 function%s0, %1 %plural{1:has|:have}1" diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index fc1ce197ef134..c348f5cac6480 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -601,6 +601,7 @@ def ModuleImport : DiagGroup<"module-import">; def ModuleConflict : DiagGroup<"module-conflict">; def ModuleFileExtension : DiagGroup<"module-file-extension">; def ModuleIncludeDirectiveTranslation : DiagGroup<"module-include-translation">; +def IndexStore : DiagGroup<"index-store">; def RoundTripCC1Args : DiagGroup<"round-trip-cc1-args">; def NewlineEOF : DiagGroup<"newline-eof">; def Nullability : DiagGroup<"nullability">; @@ -958,8 +959,7 @@ def DeallocInCategory:DiagGroup<"dealloc-in-category">; def SelectorTypeMismatch : DiagGroup<"selector-type-mismatch">; def Selector : DiagGroup<"selector", [SelectorTypeMismatch]>; def Protocol : DiagGroup<"protocol">; -// No longer in use, preserve for backwards compatibility. -def : DiagGroup<"at-protocol">; +def AtProtocol : DiagGroup<"at-protocol">; def PropertyAccessDotSyntax: DiagGroup<"property-access-dot-syntax">; def PropertyAttr : DiagGroup<"property-attribute-mismatch">; def SuperSubClassMismatch : DiagGroup<"super-class-method-mismatch">; @@ -1556,6 +1556,46 @@ def FunctionMultiVersioning def NoDeref : DiagGroup<"noderef">; +// TO_UPSTREAM(BoundsSafety) ON +def BoundsSafetyIncompleteArray : DiagGroup<"bounds-safety-incomplete-array">; +def BoundsSafetySingleToCount : DiagGroup<"bounds-safety-single-to-count">; +def BoundsSafetyInitListPartialNull : DiagGroup<"bounds-safety-init-list-partial-null">; +def BoundsSafetyInitList : DiagGroup<"bounds-safety-init-list">; +def BoundsAttributesExternArrayCount : DiagGroup<"bounds-attributes-extern-array-count">; +def BoundsAttributesInitSideEffect + : DiagGroup<"bounds-attributes-init-list-side-effect">; +def BoundsAttributesCXXExperimentalIgnored + : DiagGroup<"bounds-attributes-cxx-experimental-ignored">; +def BoundsAttributesObjCExperimentalIgnored + : DiagGroup<"bounds-attributes-objc-experimental-ignored">; +def BoundsSafetyRelaxedSystemHeadersIgnored + : DiagGroup<"bounds-safety-relaxed-system-headers-ignore">; +def BoundsAttributesImplicitConvSingleToExplicitIndexable : + DiagGroup< + "bounds-attributes-implicit-conversion-single-to-explicit-indexable">; +def BoundsSafetyConvSingleToImplicitIndexable : + DiagGroup< + "bounds-safety-conversion-single-to-implicit-indexable">; +def BoundsSafetyConvSingleToImplicitIndexableThenConvertToLargerType : + DiagGroup< + "bounds-safety-conversion-single-to-implicit-indexable-then-convert-to-larger-type">; +// FIXME: This name is wrong but we have to keep it because some adopter's +// build system currently expects (rdar://119832922). +def BoundsSafetyConvSingleToImplicitIndexableThenConvertToDynamicCountPtr : + DiagGroup< + "bounds-safety-conversion-single-to-implicit-indexable-then-convert-at-call-site">; +def BoundsSafetyConvertSingleToIndexableBoundsTruncated + : DiagGroup<"bounds-safety-single-to-indexable-bounds-truncated">; +def BoundsSafetyRedundantAttribute : DiagGroup<"bounds-attributes-redundant">; +def BoundsSafetyNullability : DiagGroup<"bounds-safety-nullability">; +def BoundsSafetyAdoptionModeIgnored + : DiagGroup<"bounds-safety-adoption-mode-ignored">; +def BoundsSafetyStrictTerminatedByCast + : DiagGroup<"bounds-safety-strict-terminated-by-cast">; +def BoundsSafetyCountAttrArithConstantCount : + DiagGroup<"bounds-safety-externally-counted-ptr-arith-constant-count">; +// TO_UPSTREAM(BoundsSafety) OFF + // -fbounds-safety and bounds annotation related warnings def BoundsSafetyCountedByEltTyUnknownSize : DiagGroup<"bounds-safety-counted-by-elt-type-unknown-size">; @@ -1608,6 +1648,13 @@ def RTTI : DiagGroup<"rtti">; def OpenCLCoreFeaturesDiagGroup : DiagGroup<"pedantic-core-features">; +// Remarks for cached -cc1. +def CompileJobCacheMiss : DiagGroup<"compile-job-cache-miss">; +def CompileJobCacheHit : DiagGroup<"compile-job-cache-hit">; +def CompileJobCacheTiming : DiagGroup<"compile-job-cache-timing">; +def CompileJobCache : DiagGroup<"compile-job-cache", [CompileJobCacheMiss, + CompileJobCacheHit]>; + // Warnings and extensions to make preprocessor macro usage pedantic. def PedanticMacros : DiagGroup<"pedantic-macros", [DeprecatedPragma, diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 80d52a0d01112..41ccdcb84fa4d 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -47,6 +47,7 @@ namespace clang { DIAG_SIZE_ANALYSIS = 100, DIAG_SIZE_REFACTORING = 1000, DIAG_SIZE_INSTALLAPI = 100, + DIAG_SIZE_CAS = 100, }; // Start position for diagnostics. enum { @@ -63,7 +64,8 @@ namespace clang { DIAG_START_ANALYSIS = DIAG_START_SEMA + static_cast(DIAG_SIZE_SEMA), DIAG_START_REFACTORING = DIAG_START_ANALYSIS + static_cast(DIAG_SIZE_ANALYSIS), DIAG_START_INSTALLAPI = DIAG_START_REFACTORING + static_cast(DIAG_SIZE_REFACTORING), - DIAG_UPPER_LIMIT = DIAG_START_INSTALLAPI + static_cast(DIAG_SIZE_INSTALLAPI) + DIAG_START_CAS = DIAG_START_INSTALLAPI + static_cast(DIAG_START_INSTALLAPI), + DIAG_UPPER_LIMIT = DIAG_START_CAS + static_cast(DIAG_SIZE_CAS) }; class CustomDiagInfo; @@ -238,6 +240,7 @@ class DiagnosticIDs : public RefCountedBase { Class GetClass() const { return static_cast(DiagClass); } std::string_view GetDescription() const { return Description; } bool ShouldShowInSystemHeader() const { return ShowInSystemHeader; } + bool ShouldShowInSystemMacro() const { return ShowInSystemMacro; } friend bool operator==(const CustomDiagDesc &lhs, const CustomDiagDesc &rhs) { @@ -311,6 +314,9 @@ class DiagnosticIDs : public RefCountedBase { }()); } + std::optional getMaxCustomDiagID() const; + const CustomDiagDesc &getCustomDiagDesc(unsigned DiagID) const; + //===--------------------------------------------------------------------===// // Diagnostic classification and reporting interfaces. // diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index f29edfa835d42..50435c04af5a2 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -837,6 +837,13 @@ def warn_pp_date_time : Warning< "expansion of date or time macro is not reproducible">, ShowInSystemHeader, DefaultIgnore, InGroup>; +def warn_pp_encounter_nonreproducible: Warning< + "encountered non-reproducible token, caching will be skipped">, + InGroup>, + DefaultWarnNoWerror, ShowInSystemHeader, ShowInSystemMacro; +def err_pp_encounter_nonreproducible: Error< + "encountered non-reproducible token, caching failed">; + // Module map parsing def err_mmap_unknown_token : Error<"skipping stray token">; def err_mmap_expected_module : Error<"expected module declaration">; @@ -938,7 +945,7 @@ def warn_quoted_include_in_framework_header : Warning< >, InGroup, DefaultIgnore; def warn_framework_include_private_from_public : Warning< "public framework header includes private framework header '%0'" - >, InGroup; + >, InGroup, DefaultIgnore; def warn_deprecated_module_dot_map : Warning< "'%0' as a module map name is deprecated, rename it to %select{module.modulemap|module.private.modulemap}1%select{| in the 'Modules' directory of the framework}2">, InGroup; @@ -999,6 +1006,9 @@ def warn_defined_in_function_type_macro : Extension< "macro expansion producing 'defined' has undefined behavior">, InGroup; +def err_pp_missing_module_include_tree : Error< + "no module named '%0' declared in include-tree module map">, DefaultFatal; + let CategoryName = "Nullability Issue" in { def err_pp_assume_nonnull_syntax : Error<"expected 'begin' or 'end'">; @@ -1014,6 +1024,15 @@ def err_pp_eof_in_assume_nonnull : Error< } +// TO_UPSTREAM(BoundsSafety) ON +let CategoryName = "BoundsSafety Pointer Attributes Issue" in { + +def err_pp_abi_ptr_attr_set_syntax : Error< + "expected 'set(attr1 [attr2 ...])'">; + +} +// TO_UPSTREAM(BoundsSafety) OFF + let CategoryName = "Dependency Directive Source Scanner Issue" in { def err_dep_source_scanner_missing_semi_after_at_import : Error< diff --git a/clang/include/clang/Basic/DiagnosticOptions.def b/clang/include/clang/Basic/DiagnosticOptions.def index 6d0c1b14acc12..ba8ce8f2e6d4f 100644 --- a/clang/include/clang/Basic/DiagnosticOptions.def +++ b/clang/include/clang/Basic/DiagnosticOptions.def @@ -98,6 +98,8 @@ VALUE_DIAGOPT(TabStop, 32, DefaultTabStop) /// The distance between tab stops. VALUE_DIAGOPT(MessageLength, 32, 0) DIAGOPT(ShowSafeBufferUsageSuggestions, 1, 0) +// TO_UPSTREAM(BoundsSafety) +DIAGOPT(BoundsSafetyAdoptionMode, 1, 0) #undef DIAGOPT #undef ENUM_DIAGOPT diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 72e765bcb800d..c3d6aec7f52ab 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -356,7 +356,7 @@ def err_atimport : Error< def warn_atimport_in_framework_header : Warning< "use of '@import' in framework header is discouraged, " "including this header requires -fmodules">, - InGroup; + InGroup, DefaultIgnore; def err_invalid_reference_qualifier_application : Error< "'%0' qualifier may not be applied to a reference">; @@ -1175,6 +1175,11 @@ def err_availability_expected_platform : Error< def err_availability_expected_environment : Error< "expected an environment name, e.g., 'compute'">; +def err_features_domain_name : Error< + "expected a domain name">; +def err_feature_invalid_availability_check : Error< + "cannot pass a domain argument along with other arguments">; + // objc_bridge_related attribute def err_objcbridge_related_expected_related_class : Error< "expected a related Objective-C class name, e.g., 'NSColor'">; diff --git a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td index e060fffc7280a..d06e01a72dd74 100644 --- a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td +++ b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td @@ -30,4 +30,29 @@ def err_refactor_extract_prohibited_expression : Error<"the selected " } +// On github swift-clang only; to be upstreamed: + +let CategoryName = "Rename Issue" in { +def err_rename_builtin_function : Error<"%0 is a builtin function that " + "cannot be renamed">; +def err_rename_sys_header : Error<"%0 cannot be renamed because it is " + "declared in a system header">; +def err_method_rename_override_sys_framework : Error<"method %0 cannot be " + "renamed because it overrides a method declared in a system framework">; +def err_rename_external_source_symbol : Error<"%0 is declared in a %1 file; " + "rename can be initiated in a %1 file only">; +} + +let CategoryName = "Refactoring Continuation Issue" in { + +def err_ref_continuation_missing_implementation : Error< + "no %select{implementation file|@implementation declaration}0 for the " + "selected %select{declaration|@interface}0 %1; please add one and run the " + "refactoring action again">; + +def err_implement_declared_methods_all_implemented : Error< + "the selected methods are already implemented">; + +} + } // end of Refactoring diagnostics diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index ad5bf26be2590..7e46e6789cfa7 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -983,6 +983,7 @@ def warn_fortify_scanf_overflow : Warning< "%2, but the corresponding specifier may require size %3">, InGroup; + def err_function_start_invalid_type: Error< "argument must be a function">; @@ -1008,12 +1009,32 @@ def warn_ptrauth_sign_null_pointer : def warn_ptrauth_auth_null_pointer : Warning<"authenticating a null pointer will almost certainly trap">, InGroup; + +def err_ptrauth_disabled_target : + Error<"this target does not support pointer authentication">; + def err_ptrauth_string_not_literal : Error< "argument must be a string literal%select{| of char type}0">; def err_ptrauth_type_disc_undiscriminated : Error< "cannot pass undiscriminated type %0 to " "'__builtin_ptrauth_type_discriminator'">; +// __ptrauth qualifier +def err_ptrauth_qualifier_return : Error< + "return types may not be qualified with __ptrauth; type is %0">; +def err_ptrauth_qualifier_param : Error< + "parameter types may not be qualified with __ptrauth; type is %0">; +def err_ptrauth_qualifier_cast : Error< + "cannot cast to '__ptrauth'-qualified type %0">; +def err_ptrauth_qualifier_nonpointer : Error< + "'__ptrauth' qualifier only applies to pointer types; %0 is invalid">; +def err_ptrauth_qualifier_arg_not_ice : Error< + "argument to __ptrauth must be an integer constant expression">; +def err_ptrauth_qualifier_address_discrimination_invalid : Error< + "address discrimination flag for __ptrauth must be 0 or 1; value is %0">; +def err_ptrauth_qualifier_extra_discriminator_invalid : Error< + "extra discriminator for __ptrauth must be between 0 and %1; value is %0">; + def note_ptrauth_virtual_function_pointer_incomplete_arg_ret : Note<"cannot take an address of a virtual member function if its return or " "argument types are incomplete">; @@ -1027,10 +1048,6 @@ def err_ptrauth_indirect_goto_addrlabel_arithmetic : Error< // __ptrauth qualifier def err_ptrauth_qualifier_invalid : Error< "%select{return type|parameter type|property}1 may not be qualified with '__ptrauth'; type is %0">; -def err_ptrauth_qualifier_cast : Error< - "cannot cast to '__ptrauth'-qualified type %0">; -def err_ptrauth_qualifier_nonpointer : Error< - "'__ptrauth' qualifier only applies to pointer types; %0 is invalid">; def err_ptrauth_qualifier_redundant : Error< "type %0 is already %1-qualified">; def err_ptrauth_arg_not_ice : Error< @@ -1259,8 +1276,8 @@ def err_protocol_has_circular_dependency : Error< "protocol has circular dependency">; def err_undeclared_protocol : Error<"cannot find protocol declaration for %0">; def warn_undef_protocolref : Warning<"cannot find protocol definition for %0">; -def err_atprotocol_protocol : Error< - "@protocol is using a forward protocol declaration of %0">; +def warn_atprotocol_protocol : Warning< + "@protocol is using a forward protocol declaration of %0">, InGroup; def warn_readonly_property : Warning< "attribute 'readonly' of property %0 restricts attribute " "'readwrite' of property inherited from %1">, @@ -1687,6 +1704,12 @@ def warn_unimplemented_selector: Warning< InGroup, DefaultIgnore; def warn_unimplemented_protocol_method : Warning< "method %0 in protocol %1 not implemented">, InGroup; +def warn_class_does_not_conform_protocol : Warning< + "%select{class|category}0 %1 does not conform to protocol" + "%plural{1: %3|2:s %3 and %4|3:s %3, %4 and %5|:s %3, %4, %5, ...}2">, + InGroup; +def note_add_missing_protocol_stubs : Note< + "add stubs for missing protocol requirements">; def warn_multiple_selectors: Warning< "several methods with selector %0 of mismatched types are found " "for the @selector expression">, @@ -3995,6 +4018,28 @@ def err_availability_unexpected_parameter: Error< def warn_unguarded_availability : Warning<"%0 is only available %select{|in %4 environment }3on %1 %2 or newer">, InGroup, DefaultIgnore; +def err_unguarded_feature : Error< + "use of %0 requires feature '%1' to be %select{available|unavailable}2">; +def err_label_in_conditionally_guarded_feature : Error< + "labels cannot appear in regions conditionally guarded by features">; +def err_features_invalid_for_decl : Error< + "feature attributes cannot be applied to %0">; +def err_features_invalid_name : Error< + "cannot find definition of feature attribute '%0'">; +def err_features_invalid__arg : Error< + "invalid argument %0: must evaluate to 0 or 1">; +def err_feature_invalid_for_decl : Error< + "feature attribute '%0(%1)' cannot be applied to this decl">; +def err_feature_merge_incompatible : Error< + "cannot merge incompatible feature attribute to this decl">; +def err_new_feature_redeclaration : Error< + "new feature attributes cannot be added to redeclarations">; +def err_feature_invalid_added : Error< + "cannot add feature availability to this decl">; +def note_feature_incompatible0 : Note< + "feature attribute %0">; +def note_feature_incompatible1 : Note< + "is incompatible with %0">; def warn_unguarded_availability_unavailable : Warning<"%0 is unavailable">, InGroup, DefaultIgnore; @@ -4388,6 +4433,16 @@ def note_reference_is_return_value : Note<"%0 returns a reference">; def note_pointer_declared_here : Note< "pointer %0 declared here">; +/* TO_UPSTREAM(BoundsSafety) ON */ +def note_pointer_declared_here_quoted : Note< + "pointer '%0' declared here">; +def note_pointer_initialized_here : Note< + "pointer %0 initialized here">; +def note_pointer_assigned_here : Note< + "pointer %0 assigned here">; +def note_unnamed_pointer_declared_here : Note< + "pointer declared here">; +/* TO_UPSTREAM(BoundsSafety) OFF */ def warn_division_sizeof_ptr : Warning<"%0 will return the size of the pointer, not the array itself">, InGroup>; @@ -4481,6 +4536,8 @@ def err_mismatched_visibility: Error<"visibility does not match previous declara def note_previous_attribute : Note<"previous attribute is here">; def note_conflicting_attribute : Note<"conflicting attribute is here">; def note_attribute : Note<"attribute is here">; +// TO_UPSTREAM(BoundsSafety) +def note_named_attribute : Note<"%0 attribute is here">; def err_mismatched_ms_inheritance : Error< "inheritance model does not match %select{definition|previous declaration}0">; def warn_ignored_ms_inheritance : Warning< @@ -4510,6 +4567,9 @@ def warn_attribute_nonnull_no_pointers : Warning< def warn_attribute_nonnull_parm_no_args : Warning< "'nonnull' attribute when used on parameters takes no arguments">, InGroup; +def warn_attribute_noescape_non_pointer : Warning< + "'noescape' attribute ignored on parameter of non-pointer type %0">, + InGroup; def warn_function_stmt_attribute_precedence : Warning< "statement attribute %0 has higher precedence than function attribute " "'%select{always_inline|flatten|noinline}1'">, @@ -4756,6 +4816,9 @@ def err_attr_swift_error_no_error_parameter : Error< def err_attr_swift_error_return_type : Error< "%0 attribute with '%1' convention can only be applied to a " "%select{function|method}2 returning %select{an integral type|a pointer}3">; +def warn_swift_newtype_attribute_non_typedef : Warning< + "'swift_newtype' attribute may be put on a typedef only; " + "attribute is ignored">, InGroup>; def err_swift_async_no_access : Error< "first argument to 'swift_async' must be either 'none', 'swift_private', or " @@ -10626,6 +10689,7 @@ def warn_missing_case : Warning<"%plural{" "3:enumeration values %1, %2, and %3 not handled in switch|" ":%0 enumeration values not handled in switch: %1, %2, %3...}0">, InGroup; +def note_fill_in_missing_cases : Note<"add missing switch cases">; def warn_switch_default : Warning<"'switch' missing 'default' label">, InGroup, DefaultIgnore; @@ -12544,6 +12608,839 @@ def warn_target_clone_no_impact_options : Warning<"version list contains entries that don't impact code generation">, InGroup; +// TO_UPSTREAM(BoundsSafety) ON +let CategoryName = "BoundsSafety Pointer Attributes Issue" in { + +def err_bounds_safety_conflicting_pointer_attributes : Error< + "%select{array|pointer}0 cannot have more than one %select{bound|type|count|end|terminator}1 attribute">; +def note_bounds_safety_conflicting_pointer_attribute_args : Note< + "conflicting arguments for %select{count|end|terminator}0 were %1 and %2">; +def warn_bounds_safety_duplicate_pointer_attributes : Warning< + "%select{array|pointer}0 annotated with %select{__unsafe_indexable|__bidi_indexable|__indexable|__single|__terminated_by}1 " + "multiple times. Annotate only once to remove this warning">, InGroup; +def err_bounds_safety_complete_array_with_count : Error< + "arrays with an explicit size decay to counted pointers and cannot also have " + "a count attribute">; +def warn_bounds_safety_promoting_incomplete_array_without_count : Warning< + "accessing elements of an unannotated incomplete array always fails at runtime">, InGroup; +def err_bounds_safety_conflicting_count_bound_attributes : Error< + "pointer cannot be %0 and '__%select{bidi_indexable|indexable}1' at the same time">; +def err_bounds_safety_end_pointer_single : Error< + "end-pointer must be '__single'">; +def err_bounds_safety_conflicting_count_range_attributes : Error< + "pointer cannot have count and range at the same time">; +def err_flexible_array_member_passed_by_copy : Error< + "-fbounds-safety forbids passing %0 by copy because it has a flexible array " + "member">; +def err_bounds_safety_dynamic_count_function_call : Error< + "argument of '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}0' attribute can only reference " + "function with 'const' attribute">; +def err_bounds_safety_dynamic_count_function_call_argument : Error< + "argument of function call %0 in '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}1' attribute is not a constant expression">; +def err_attribute_invalid_argument_expression_for_pointer_bounds_attribute : Error< + "invalid argument expression to bounds attribute">; +def err_invalid_decl_kind_bounds_safety_dynamic_count : Error< + "count expression %select{on struct field|in function declaration}0 may only " + "reference %select{other fields of the same struct|parameters of that function}0">; +def err_invalid_decl_kind_bounds_safety_union_count : Error< + "cannot use %0 on union fields">; +def err_multiple_coupled_decls_in_bounds_safety_dynamic_count : Error< + "multiple coupled declarations in a -fbounds-safety attribute are not supported yet">; +def err_attribute_argument_type_for_bounds_safety_count : Error< + "%0 attribute requires an integer type argument">; +def err_attribute_argument_type_for_bounds_safety_range : Error< + "%0 attribute requires a pointer type argument">; +def err_bounds_safety_function_pointers_cannot_be_indexable : Error< + "function pointers cannot be indexable">; +def err_bounds_safety_voidptr_must_use_byte_count : Error< + "void pointers must use a byte count attribute instead of an item count">; +def err_bounds_safety_array_decay_to_single : Error< + "parameter of array type %0 decays to a __single pointer, and will not allow " + "arithmetic">; +def note_bounds_safety_array_decay_use_count_annotation : Note< + "add a count attribute within the declarator brackets or convert the " + "parameter to a pointer with a count or size attribute">; +def err_nested_bounds_safety_pointer_attribute_mismatch : Error< + "%select{%diff{assigning to $ from $|assigning to incompatible nested pointer type}0,1" + "|%diff{passing $ to parameter of incompatible nested pointer type $|" + "passing to parameter of incompatible nested pointer type}0,1" + "|%diff{returning $ from a function with result of incompatible nested pointer type $|" + "returning from function with return of incompatible nested pointer type}0,1" + "|%diff{converting $ to incompatible nested pointer type $|" + "converting between incompatible nested pointer types}0,1" + "|%diff{initializing $ with an expression of incompatible nested pointer type $|" + "initializing with expression of incompatible nested pointer type}0,1" + "|%diff{sending $ to parameter of incompatible nested pointer type $|" + "sending to parameter of incompatible nested pointer type}0,1" + "|%diff{casting $ to incompatible nested pointer type $|" + "casting between incompatible nested pointer types}0,1}2; " + "use explicit cast to perform this conversion">; +def err_cond_expr_nested_bounds_safety_pointer_attribute_mismatch : Error< + "conditional expression evaluates %select{values with incompatible nested pointer types" + "|functions with incompatible bound attributes|values with mismatching __terminated_by attributes}2" + "%diff{ $ and $|}0,1">; +def err_typecheck_cond_incompatible_pointers : Error< + "conditional expression evaluates values with incompatible pointee types" + "%diff{ $ and $|}0,1; use explicit casts to perform this conversion">; +def err_bounds_safety_non_to_pointer : Error< + "non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use " + "'__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'">; +def err_incompatible_bounds_safety_function_pointer : Error< + "conversion between pointers to functions with incompatible bound attributes">; +def err_bounds_safety_unsafe_to_safe : Error< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' " + "or '__unsafe_forge_bidi_indexable' to perform this conversion">; +// %2 is AssignmentAction enum +def err_bounds_safety_incomplete_single_to_indexable : Error< + "cannot %select{" + "assign to indexable pointer with type %0 from __single pointer to incomplete type %1%select{|; consider declaring pointer '%3' as '__single'}4|" // assign + "pass __single pointer to incomplete type argument %0 when parameter is an indexable pointer %1; consider making the%select{| '%3'}4 parameter '__single'|" // pass + "return __single pointer to incomplete type %0 when return type is an indexable pointer %1; consider making the return type '__single'|" // return + "convert __single pointer to incomplete type %0 to indexable pointer %1; consider making the destination '__single'|" // convert + "initialize indexable pointer with type %0 from __single pointer to incomplete type %1%select{|; consider declaring pointer '%3' as '__single'}4|" // initialize + "send __single to incomplete type %0 to indexable pointer %1; consider making the destination '__single'|" // send + "cast from __single pointer to incomplete type %0 to indexable pointer type %1" // cast + "}2">; +def warn_bounds_safety_implicit_conv_single_to_explicit_indexable : Warning< + "%select{" + "%diff{assigning to%select{ __indexable| __bidi_indexable|}3 type $ from%select{ __single|}4 type $|" + "assigning%select{ __indexable| __bidi_indexable|}3 type from%select{ __single|}4 type}0,1|" + "%diff{passing%select{ __single|}4 type $ to parameter of%select{ __indexable| __bidi_indexable|}3 type $|" + "passing%select{ __single|}4 type to parameter of%select{ __indexable| __bidi_indexable|}3 type}0,1|" + "%diff{returning%select{ __single|}4 type $ from a function with%select{ __indexable| __bidi_indexable|}3 result type $|" + "returning%select{ __single|}4 type from a function with%select{ __indexable| __bidi_indexable|}3 result type}0,1|" + "%diff{converting%select{ __single|}4 type $ to%select{ __indexable| __bidi_indexable|}3 type $|" + "converting%select{ __single|}4 type to%select{ __indexable| __bidi_indexable|}3 type}0,1|" + "%diff{initializing%select{ __indexable| __bidi_indexable|}3 type $ with an expression of%select{ __single|}4 type $|" + "initializing%select{ __indexable| __bidi_indexable|}3 type with an expression of%select{ __single|}4 type}0,1|" + "%diff{sending%select{ __single|}4 type $ to parameter of%select{ __indexable| __bidi_indexable|}3 type $|" + "sending%select{ __single|}4 type to parameter of%select{ __indexable| __bidi_indexable|}3 type}0,1|" + "%diff{casting%select{ __single|}4 type $ to%select{ __indexable| __bidi_indexable|}3 type $|" + "casting%select{ __single|}4 type to%select{ __indexable| __bidi_indexable|}3 type}0,1|" + "}2 results in %select{an __indexable|a __bidi_indexable}5 pointer that will trap if a non-zero offset " + "is dereferenced%select{|. consider adding '__counted_by' to '%7'}6">, + InGroup; +def warn_bounds_safety_conv_single_to_implicit_indexable : Warning< + "%select{indexing into|pointer arithmetic over}1 a __bidi_indexable local " + "variable %2 %select{assigned|initialized}3 from __single %select{" + "parameter|" // AssignmentSourceKind::Parameter + "global|" // AssignmentSourceKind::GlobalVar + "local variable|" // AssignmentSourceKind::LocalVar + "return value from call to|" // AssignmentSourceKind::FunctionCallReturnValue + "element from array|" // AssignmentSourceKind::ArrayElement + "struct member|" // AssignmentSourceKind::StructMember + "union member" // AssignmentSourceKind::UnionMember + "}0 %4 results in %select{" + "a trap|" // UnsafeOpKind::Index + "an out-of-bounds pointer" // UnsafeOpKind::Arithmetic + "}1" + "%select{" + "|" // PtrArithOOBKind:::ALWAYS_OOB_BASE_OOB + "|" // PtrArithOOBKind::ALWAYS_OOB_CONSTANT_OFFSET + " if the %select{index expression|offset}1 is >= %6|" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET + " if the %select{index expression|offset}1 is >= %6 or < 0|" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET_OR_LT_ZERO + " if the %select{index expression|offset}1 is < 0" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_LT_ZERO + "}5">, + InGroup; + +def warn_bounds_safety_conv_single_to_implicit_indexable_multiple_assignments : + Warning< "%select{indexing into|pointer arithmetic over}0 __bidi_indexable " + "local variable %1 that is assigned from a __single pointer results in " + "%select{" + "a trap|" // UnsafeOpKind::Index + "an out-of-bounds pointer" // UnsafeOpKind::Arithmetic + "}0" + "%select{" + "|" // PtrArithOOBKind:::ALWAYS_OOB_BASE_OOB + "|" // PtrArithOOBKind::ALWAYS_OOB_CONSTANT_OFFSET + " if the %select{index expression|offset}0 is >= %3|" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET + " if the %select{index expression|offset}0 is >= %3 or < 0|" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET_OR_LT_ZERO + " if the %select{index expression|offset}0 is < 0" // PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_LT_ZERO + "}2">, + InGroup; +def note_single_entity_assigned_here : Note< + "__single %select{" + "parameter|" // AssignmentSourceKind::Parameter + "global|" // AssignmentSourceKind::GlobalVar + "local variable|" // AssignmentSourceKind::LocalVar + "return value from call to|" // AssignmentSourceKind::FunctionCallReturnValue + "element from array|" // AssignmentSourceKind::ArrayElement + "struct member|" // AssignmentSourceKind::StructMember + "union member" // AssignmentSourceKind::UnionMember + "}0 %1 assigned to %2 here">; + +def warn_bounds_safety_conv_single_to_implicit_indexable_unsafe_zeroth_element : + Warning<"%select{" + "indexing|" // UnsafeOpKind::Index + "UNUSED|" // UnsafeOpKind::Arithmetic + "dereferencing|" // UnsafeOpKind::Deref + "accessing field %5 through|" // UnsafeOpKind::MemberAccess + "assigning from|" // UnsafeOpKind::Assignment + "returning|" // UnsafeOpKind::Return + "passing|" // UnsafeOpKind::CallArg + "UNUSED" // UnsafeOpKind::Cast + "}0 __bidi_indexable %1" + "%select{" + " at any index|||||||}0 " + "will %select{" + "access out-of-bounds memory and will trap in a future compiler version|" // UnsafeOpKind::Index + "UNUSED|" // UnsafeOpKind::Arithmetic + "access out-of-bounds memory and will trap in a future compiler version|" // UnsafeOpKind::Deref + "always trap|" // UnsafeOpKind::MemberAccess + "propagate an out-of-bounds pointer|" // UnsafeOpKind::Assignment + "return an out-of-bounds pointer|" // UnsafeOpKind::Return + "pass an out-of-bounds pointer|" // UnsafeOpKind::CallArg + "UNUSED" // UnsafeOpKind::Cast + "}0. At runtime %4 is assigned a __single pointer that" + " results in %1 having bounds smaller than a single %2 (%3 byte%s3)">, + InGroup; + +def warn_bounds_safety_conv_single_to_implicit_indexable_unsafely_cast : + Warning< + "%select{explicit|implicit}0 cast of __bidi_indexable %1 to a larger pointee" + " type creates an out-of-bounds pointer. Later uses of the result may trap">, + InGroup; + +def warn_bounds_safety_conv_single_to_implicit_indexable_unsafely_cast_to_single_trap : + Warning< + "%select{explicit|implicit}0 cast of out-of-bounds __bidi_indexable to " + "__single will trap in a future compiler version due to the bounds of %1 " + "being too small to access a single element of type %2">, + InGroup; + +def note_single_entity_assigned_here_with_pointee_sizes : Note< + "__single %select{" + "parameter|" // AssignmentSourceKind::Parameter + "global|" // AssignmentSourceKind::GlobalVar + "local variable|" // AssignmentSourceKind::LocalVar + "return value from call to|" // AssignmentSourceKind::FunctionCallReturnValue + "element from array|" // AssignmentSourceKind::ArrayElement + "struct member|" // AssignmentSourceKind::StructMember + "union member" // AssignmentSourceKind::UnionMember + "}0 %1 %select{assigned to|used to initialize}9 %2 here results in %2 having the bounds of a single %3 " + "(%4 bytes) but %select{%8|cast of %2 to %8}7 has pointee type %5 (%6 bytes)">; + +def warn_bounds_safety_conv_single_to_implicit_indexable_dynamic_count_conversion : + Warning< + "%select{" + "UNUSED|" // UnsafeOpKind::Index + "UNUSED|" // UnsafeOpKind::Arithmetic + "UNUSED|" // UnsafeOpKind::Deref + "UNUSED|" // UnsafeOpKind::MemberAccess + "assigning from|" // UnsafeOpKind::Assignment + "returning|" // UnsafeOpKind::Return + "passing|" // UnsafeOpKind::CallArg + "casting" // // UnsafeOpKind::Cast + "}0 __bidi_indexable local variable %1 will %select{" + "UNUSED|" // WillTrapKind::NoTrap + "likely trap|" // WillTrapKind::Unknown + "trap|" // WillTrapKind::Trap + "trap (unless %1 is null)|" // WillTrapKind::TrapIffPtrNotNull + "UNUSED" // WillTrapKind::TrapIffPtrNull + "}2%select{" + " |" + " in a future compiler version " + "}6" + "when converting to %3 due to %1 having the bounds of a __single pointer" + "%select{" + // Non-constant count + ". If %1 is non-null then any %select{" + "count|" // __counted_by(or_null) + "size" // __sized_by(or_null) + "}7 > %8 will trap" + "%select{" + // __counted_by/__sized_by + ". If %1 is null then any %select{count|size}7 != 0 will trap|" + // __counted_by_or_null/__sized_by_or_null + "" + "}5|" + // Constant count + "" + "}4">, + InGroup; + + +def note_single_entity_assigned_here_with_dyn_count_conversion: Note< + "__single %select{" + "parameter|" // AssignmentSourceKind::Parameter + "global|" // AssignmentSourceKind::GlobalVar + "local variable|" // AssignmentSourceKind::LocalVar + "return value from call to|" // AssignmentSourceKind::FunctionCallReturnValue + "element from array|" // AssignmentSourceKind::ArrayElement + "struct member|" // AssignmentSourceKind::StructMember + "union member" // AssignmentSourceKind::UnionMember + "}0 %1 assigned to %2 here results in %2 having the bounds of a single %3 " + "(%4 bytes)" + "%select{" + // non-constant count + "|" + // constant count + " but conversion of %2 to %6 requires %7 bytes or more" + "}5">; + +def err_bounds_safety_dynamic_bound_redeclaration : Error< + "conflicting '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by|__terminated_by}0' " + "attribute with the previous %select{function|variable}1 declaration">; +def err_bounds_safety_nullable_fam : Error< + "%select{array objects|flexible array members}1 cannot be null; did you mean %select{__counted_by|__sized_by}0 instead?">; +def warn_bounds_safety_nullable_dynamic_count_nonnullable : Warning< + "combining %select{'__counted_by_or_null'|'__sized_by_or_null'}0 and '_Nonnull'; did you mean %select{'__counted_by'|'__sized_by'}0 instead?">, InGroup; +def warn_bounds_safety_nonnullable_dynamic_count_nullable : Warning< + "combining %select{'__counted_by'|'__sized_by'}0 with non-zero %select{count|size}0 (which cannot be null) and '_Nullable'; did you mean %select{'__counted_by_or_null'|'__sized_by_or_null'}0 instead?">, InGroup; +def err_bounds_safety_nullable_dynamic_count_nonnullable : Error< + "cannot combine %select{'__counted_by_or_null'|'__sized_by_or_null'}0 and %1; did you mean %select{'__counted_by'|'__sized_by'}0 instead?">; + +def warn_bounds_safety_single_bitcast_lose_bounds : Warning< + "casting %0 to %1 creates a '%select{__indexable|__bidi_indexable}2' pointer " + "%select{with bounds containing only one %3|with zero length due to %3 having unknown size)}4">, + InGroup; + +def note_bounds_safety_single_bitcast_lose_bounds_keep_bound : Note< + "cast to %0 first to keep bounds of %1">; + +def note_bounds_safety_single_bitcast_lose_bounds_silence : Note< + "silence by making the destination '__single'">; + +def err_bounds_safety_atomic_unsupported_attribute : Error< + "_Atomic on " + "'%select{__indexable|__bidi_indexable|__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by|__terminated_by|end}0' " + "pointer is not yet supported">; +def err_bounds_safety_atomic_op_unsupported_attribute : Error< + "atomic operation on " + "'%select{__indexable|__bidi_indexable|__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' " + "pointer is not yet supported">; +def err_bounds_safety_atomic_op_arithmetic_needs_unsafe_pointer : Error< + "address argument to atomic arithmetic operation must be a pointer to " + "'__unsafe_indexable' pointer (%0 invalid)">; + +def err_bounds_safety_typeof_dbpt : Error< + "__typeof__ on an expression of type %0 is not yet supported">; + +def err_bounds_safety_single_pointer_arithmetic : Error< + "pointer arithmetic on single pointer %0 is out of bounds%select{|; consider adding '__counted_by' to '%2'}1">; +def err_bounds_safety_flexible_array_member_record_pointer_arithmetic : Error< + "-fbounds-safety forbids arithmetic on pointers to types with a flexible array " + "member">; +def err_bounds_safety_member_reference_null_base : Error< + "base of member reference is a null pointer">; +def err_bounds_safety_counted_by_without_size : Error< + "cannot apply '__counted_by%select{|_or_null}2' attribute to %0 because %1 has unknown size" + "%select{|; did you mean to use '__sized_by%select{|_or_null}2' instead?}3">; +def err_bounds_safety_counted_by_on_incomplete_type_on_use : Error < + "cannot %select{" + "use '%1'|" // Generic expr + "call '%1'" // CallExpr + "}0 with%select{" + "|" // Generic expr + " return" // CallExpr + "}0 type %2 because the pointee type %3 is incomplete " + "and the '%4' attribute requires the pointee type be complete in this context; " + "consider providing a complete definition for %3 or using the " + "'__sized_by%select{|_or_null}5' attribute" +>; + +def err_bounds_safety_counted_by_on_incomplete_type_on_assign : Error < + "cannot %select{" + "assign to %select{object|'%1'}2 that has|" // AssignmentAction::Assigning, + "pass argument to %select{parameter|parameter '%1'}2 that has|" // AssignmentAction::Passing, + "return|" // AssignmentAction::Returning, + "convert to|" // AssignmentAction::Converting (UNUSED) + "%select{|implicitly }3initialize %select{object|'%1'}2 that has|" // AssignmentAction::Initializing, + "pass argument to parameter that has|" // AssignmentAction::Sending (UNUSED) + "cast to|" // AssignmentAction::Casting (UNUSED) + "pass argument to parameter that has" // AssignmentAction::Passing_CFAudited (UNUSED) + "}0 type %4 because the pointee type %6 is incomplete and the '%5' attribute " + "requires the pointee type be complete when %select{" + "assigning|" // AssignmentAction::Assigning, + "passing|" // AssignmentAction::Passing, + "returning|" // AssignmentAction::Returning, + "converting|" // AssignmentAction::Converting (UNUSED) + "initializing|" // AssignmentAction::Initializing, + "passing|" // AssignmentAction::Sending (UNUSED) + "casting|" // AssignmentAction::Casting (UNUSED) + "passing" // AssignmentAction::Passing_CFAudited (UNUSED) + "}0; " + "consider providing a complete definition for %6 or using the " + "'__sized_by%select{|_or_null}7' attribute" +>; + +def err_bounds_safety_counted_by_on_incomplete_type_on_func_def : Error< + "cannot apply '%0' attribute to %select{" + "return|" // return type + "parameter%select{" // parameter type + "|" // unnamed parameter + " '%3'" // named parameter + "}2 with" + "}1 type %4 on a function definition because the pointee type %5 is " + "incomplete" +>; + +def err_bounds_safety_counted_by_on_incomplete_type_on_var_decl : Error< + "cannot apply '%0' attribute to%select{| tentative}1 variable definition " + "'%2' with type %3 because the pointee type %4 is " + "incomplete; consider providing a complete definition for %4 before this " + "definition or using the '__sized_by%select{|_or_null}5' attribute" +>; + +def err_bounds_safety_pointer_subscript : Error< + "array subscript on %select{single|'__terminated_by'}0 pointer %1 " + "%select{must use a constant index of 0 to be in bounds|is not allowed}0">; +def err_bounds_safety_indexable_pointer_arithmetic : Error< + "decremented indexable pointer %0 is out of bounds">; +def err_bounds_safety_indexable_pointer_subscript : Error< + "array subscript with a negative index on indexable pointer %0 is out of bounds">; +def err_bounds_safety_terminated_by_pointer_arithmetic_dec : Error< + "cannot decrement '__terminated_by' pointer %0">; +def err_bounds_safety_terminated_by_pointer_arithmetic : Error< + "pointer arithmetic on '__terminated_by' pointer %0 can only increase the " + "value by one">; + +def err_bounds_safety_dynamic_bound_pointer_unary_arithmetic : Error< + "%select{negative|positive}0 pointer arithmetic on " + "%select{'__counted_by' |'__sized_by' |'__counted_by_or_null' |'__sized_by_or_null' |end |}1pointer" + "%select{||||| that starts the '__ended_by' chain}1 always traps">; + +// TODO: This should be an Error, not a Warning (rdar://138902282) +def warn_bounds_safety_count_attr_pointer_unary_arithmetic_constant_count : Warning< + "%select{negative|positive}0 pointer arithmetic on " + "'%1' attributed pointer with constant %select{count|size}2 " + "%select{of %4|%4}3 always traps">, + InGroup, DefaultError; + +// TODO: This should be an Error, not a Warning (rdar://138902282) +def warn_bounds_safety_count_attr_pointer_binary_assign_constant_count : Warning< + "compound %select{subtraction|addition}0-assignment on " + "'%1' attributed pointer with constant %select{count|size}2 " + "%select{of %4|%4}3 always traps">, + InGroup, DefaultError; + +def err_bounds_safety_dependent_vars_assign_side_effect : Error< + "assignments to dependent variables should not have side effects between them">; +def err_bounds_safety_missing_dependent_var_assignment : Error< + "assignment to %select{%2 |}3'%0' requires corresponding assignment to %select{|%2 }3'%1'; " + "add self assignment '%1 = %1' if the value has not changed">; +def err_bounds_safety_no_preceding_fam_base_assignment : Error< + "assignment to '%0' requires an %select{|immediately preceding }2assignment to '%1' with a wide pointer%select{ immediately preceding the group of dependent field assignments|}2">; +def note_bounds_safety_flex_base_assign : Note< + "'%0' is initialized with a '__single' pointer">; +def note_bounds_safety_first_dep_assign: Note< + "group of dependent field assignments starts here, place assignment to '%0' before it">; +def err_bounds_safety_multiple_assignments_to_dynamic_bound_decl : Error< + "multiple consecutive assignments to a %select{dynamic count|dynamic count pointer|ranged pointer}0 %1 " + "must be simplified; keep only one of the assignments">; +def note_bounds_safety_previous_assignment : Note< + "previously assigned here">; +def err_bounds_safety_non_adjacent_dependent_var_decl : Error< + "local variable %0 must be declared right next to its dependent decl">; +def err_bounds_safety_dependent_out_count_assign : Error< + "not allowed to change out parameter used as dependent count expression of other parameter or return type">; +def err_bounds_safety_dependent_out_count_buf_assign : Error< + "not allowed to change out parameter with dependent count">; +def err_bounds_safety_read_only_dynamic_count_pointer : Error< + "parameter '%0' with '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}1' attribute depending " + "on an indirect count is implicitly read-only">; +def err_bounds_safety_read_only_dynamic_range_pointer : Error< + "parameter '%0' %select{with '__ended_by' attribute depending " + "on an indirect end pointer|referred to by an indirect '__ended_by' pointer}1 " + "is implicitly read-only">; +def err_bounds_safety_read_only_dependent_param_in_return : Error< + "parameter '%0' is implicitly read-only due to being used by the " + "'%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}1' " + "attribute in the return type of '%2' (%3)">; +def err_bounds_safety_cannot_pass_read_only_dynamic_bound_pointer : Error< + "parameter '%0' with '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}1' attribute " + "depending on an indirect count is implicitly read-only and cannot be passed " + "as an indirect argument">; +def err_bounds_safety_cannot_pass_read_only_dependent_param_in_return : Error< + "parameter '%0' is implicitly read-only and cannot be passed as an indirect " + "argument due to being used by the " + "'%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}1' " + "attribute in the return type of '%2' (%3)">; +def err_deref_in_bounds_safety_count_non_parm_decl : Error< + "dereference operator in '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' is only allowed for function parameters">; +def err_bounds_safety_dynamic_bound_arg_different_scope : Error< + "argument of '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' attribute cannot " + "refer to declaration from a different scope">; +def err_bounds_safety_dynamic_bound_arg_different_lifetime : Error< + "argument of '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' attribute cannot " + "refer to declaration of a different lifetime">; +def err_bounds_safety_global_dynamic_bound : Error< + "pointer with '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' " + "and the argument of the attribute must be defined in the same translation unit">; +def warn_bounds_safety_extern_array_dynamic_count : Warning< + "array with '%select{__counted_by|__sized_by}0' " + "and the argument of the attribute should be defined in the same translation unit">, + InGroup; +def err_bounds_safety_local_dependent_count_block : Error< + "local dependent count decl should be declared side by side to its dependent pointer">; +def err_bounds_safety_external_bound_share : Error< + "%select{variable|field}1 %0 referred to by %select{%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}2 variable|flexible array member}1 cannot be used in other dynamic bounds attributes">; +def err_bounds_safety_nested_dynamic_bound : Error< + "%0 attribute on nested pointer type is only allowed on indirect parameters">; +def err_bounds_safety_nested_dynamic_range : Error< + "%0 is not allowed for nested pointers">; +def err_bounds_safety_increment_dynamic_count : Error< + "incrementing '%0' without updating '%1' always traps">; +def err_bounds_safety_increment_dynamic_count_nodep : Error< + "incrementing '%0' always traps">; +def err_bounds_safety_dynamic_bound_pointer_compound_assign_side_effect : Error< + "compound assignment on dynamic bound pointer type %0 with side effects is not yet supported;" + "instead, use seperate assignment and binary expressions">; +def err_bounds_safety_dependent_assignments_order : Error< + "cannot reference '%0' after it is changed during consecutive assignments">; +def note_bounds_safety_decl_assignment : Note< + "'%0' has been assigned here">; +def err_bounds_safety_dependent_struct_assignment : Error< + "cannot assign %0 because it contains field %1 referred to by flexible array member %2">; +def note_bounds_safety_count_param_loc : Note< + "referred to by count parameter here">; +def err_bounds_safety_dependent_field_duplicates : Error< + "cannot depend on nested field %0 because it exists in multiple instances in struct %1">; +def note_bounds_safety_struct_fields_only_in_fam : Note< + "nested struct member in count parameter only supported for flexible array members">; +def error_bounds_safety_no_arrow_members : Error< + "arrow notation not allowed for struct member in count parameter">; +def error_bounds_safety_no_count_in_unions : Error< + "count parameter refers to union %0 of type %1">; + +def err_bounds_safety_taking_address_dynamic_bound_dependent : Error< + "%select{variable|field}1 referred to by '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' " + "cannot be pointed to by any other variable; exception is when the pointer " + "is passed as a compatible argument to a function">; +def err_bounds_safety_taking_address_dynamic_bound_pointer : Error< + "%select{pointer|array}1 with '%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null|__ended_by}0' cannot be " + "pointed to by any other variable; exception is when the variable is passed " + "as a compatible argument to a function">; + +def err_bounds_safety_incompatible_dynamic_count_argument: Error< + "incompatible dynamic count pointer argument to parameter of type %0">; +def err_bounds_safety_incompatible_indirect_count_parameter: Error< + "passing%select{| address of}1 %0 referred to by " + "'%select{__counted_by|__sized_by|__counted_by_or_null|__sized_by_or_null}2' to a parameter that is not referred to " + "by the same attribute">; +def err_bounds_safety_incompatible_dynamic_bound_argument: Error< + "type of %0, %1, is incompatible with parameter of type %2">; +def err_bounds_safety_mismatching_count_type_argument: Error< + "incompatible pointer types assigning %0 with an expression with mismatching size attributes %1">; +def err_bounds_safety_incompatible_count_expression: Error< + "incompatible count expression %0 vs. %1 in argument to function">; +def err_bounds_safety_unsynchronized_indirect_param: Error< + "passing%select{| address of}1 %0 as an indirect parameter; " + "must also pass %2 %select{|or its address }3because the type of %select{%0|%2}4, " + "%5, refers to %select{%2|%0}4">; + +def err_bounds_safety_dynamic_count_negative : Error< + "negative %select{count|size}1 value of %2 for%select{| '%4' of type}3 %0">; +def err_bounds_safety_dynamic_count_from_array_bad_size : Error< + "%select{" + "assigning to%select{| '%7' of type}6 %1 with %select{count|size}2 value of %4 from array %3 (which has %5 %select{elements|bytes}2)|" + "passing array %3 (which has %5 %select{elements|bytes}2) to parameter%select{| '%7'}6 of type %1 with %select{count|size}2 value of %4|" + "returning array %3 (which has %5 %select{elements|bytes}2) from a function with result type %1 and %select{count|size}2 value of %4|" + "converting array %3 (which has %5 %select{elements|bytes}2) to type %1 with %select{count|size}2 value of %4|" + "initializing%select{| '%7' of type}6 %1 and %select{count|size}2 value of %4 with array %3 (which has %5 %select{elements|bytes}2)|" + "sending array %3 (which has %5 %select{elements|bytes}2) to parameter%select{| '%7'}6 of type %1 with %select{count|size}2 value of %4|" + "casting array %3 (which has %5 %select{elements|bytes}2) to type %1 with %select{count|size}2 value of %4" + "}0 always fails">; +def err_bounds_safety_dynamic_count_from_unbounded_bad_size : Error< + "%select{" + "assigning to%select{| '%7' of type}6 %1 with %select{count|size}2 value of %4 from %3%select{| with pointee of size %5}2|" + "passing %3%select{| with pointee of size %5}2 to parameter%select{| '%7'}6 of type %1 with %select{count|size}2 value of %4|" + "returning %3%select{| with pointee of size %5}2 from a function with result type %1 and %select{count|size}2 value of %4|" + "converting %3%select{| with pointee of size %5}2 to type %1 with %select{count|size}2 value of %4|" + "initializing%select{| '%7' of type}6 %1 and %select{count|size}2 value of %4 with %3%select{| and pointee of size %5}2|" + "sending %3%select{| with pointee of size %5}2 to parameter%select{| '%7'}6 of type %1 with %select{count|size}2 value of %4|" + "casting %3%select{| with pointee of size %5}2 to type %1 with %select{count|size}2 value of %4" + "}0 always fails">; +def err_bounds_safety_dynamic_count_from_null_nonzero_count : Error< + "%select{" + "assigning null to%select{| '%5' of type}4 %1 with %select{count|size}2 value of %3|" + "passing null to parameter%select{| '%5'}4 of type %1 with %select{count|size}2 value of %3|" + "returning null from a function with result type %1 and %select{count|size}2 value of %3|" + "converting null to type %1 with %select{count|size}2 value of %3|" + "%select{|implicitly }6initializing%select{| '%5' of type}4 %1 and %select{count|size}2 value of %3 with null|" + "sending null to parameter%select{| '%5'}4 of type %1 with %select{count|size}2 value of %3|" + "casting null to type %1 with %select{count|size}2 value of %3" + "}0 always fails">; + +def warn_bounds_safety_dynamic_count_from_nonnull_implicit_zero_count : Warning< + "possibly %select{" + "assigning non-null to%select{| '%4' of type}3 %1 with implicit %select{count|size}2 value of 0|" + "passing non-null to parameter%select{| '%4'}3 of type %1 with implicit %select{count|size}2 value of 0|" + "returning non-null from a function with result type %1 and implicit %select{count|size}2 value of 0|" + "converting non-null to type %1 with implicit %select{count|size}2 value of 0|" + "initializing%select{| '%4' of type}3 %1 and implicit %select{count|size}2 value of 0 with non-null|" + "sending non-null to parameter%select{| '%4'}3 of type %1 with implicit %select{count|size}2 value of 0|" + "casting non-null to type %1 with implicit %select{count|size}2 value of 0" + "}0, which creates a non-dereferenceable pointer; " + "explicitly set %select{count|size}2 value to 0 to remove this warning">, + InGroup; +def warn_bounds_safety_dynamic_count_from_nonnull_negative_count : Warning< + "possibly %select{" + "assigning non-null to%select{| '%5' of type}4 %1 with %select{count|size}2 value of %3|" + "passing non-null to parameter%select{| '%5'}4 of type %1 with %select{count|size}2 value of %3|" + "returning non-null from a function with result type %1 with %select{count|size}2 value of %3|" + "converting non-null to type %1 with %select{count|size}2 value of %3|" + "initializing%select{| '%5' of type}4 %1 and %select{count|size}2 value of %3 with non-null|" + "sending non-null to parameter%select{| '%5'}4 of type %1 with %select{count|size}2 value of %3|" + "casting non-null to type %1 with %select{count|size}2 value of %3" + "}0; explicitly %select{assign|pass|return|convert|initialize|send|cast}0 null to remove this warning">, + InGroup, DefaultError; + +def warn_bounds_safety_dynamic_count_from_unbounded : Warning< + "%select{count|size}2 value is not statically known: " + "%select{" + "assigning to%select{| '%6' of type}5 %1 from %4|" + "passing %4 to parameter%select{| '%6'}5 of type %1|" + "returning %4 from a function with result type %1|" + "converting %4 to type %1|" + "initializing%select{| '%6' of type}5 %1 with %4|" + "sending %4 to parameter%select{| '%6'}5 of type %1|" + "casting %4 to type %1" + "}0 is invalid for any %select{count other than 0 or 1|size greater than %7}2" + "%select{| unless the pointer is null}3">, + InGroup; +def note_bounds_safety_dynamic_count_from_unbounded_count_location : Note< + "%select{count|size}1 %select{assigned|passed|returned|converted|initialized|sent|cast}0 here">; +def note_bounds_safety_consider_adding_to : Note< + "consider adding '%1' to '%0'">; +def note_bounds_safety_consider_adding_to_return : Note< + "consider adding '%1' to %0 return type of '%2'">; + +def warn_bounds_safety_initlist_range_partial_null : Warning< + "%select{|implicitly }0initializing field %1 of type %2 to NULL while " + "%3 is initialized with a value rarely succeeds">, InGroup; + +def err_bounds_safety_dynamic_bound_init_side_effect : Error< + "initalizer for %select{count|size|'__counted_by' pointer|'__sized_by' pointer|'__counted_by_or_null' pointer|'__sized_by_or_null' pointer|'__ended_by' pointer|end pointer}0 with side effects is not yet supported">; + +def warn_bounds_safety_struct_init_side_effect : Warning< + "initializer %0 has a side effect; this may lead to an unexpected result " + "because the evaluation order of initialization list expressions is " + "indeterminate">, InGroup; + +def err_bounds_safety_flexible_global_wrong_count : Error< + "flexible array member is initialized with %0 element%select{|s}2, but count value is " + "initialized to %1">; +def err_bounds_safety_flexible_global_not_counted : Error< + "flexible array member is initialized without a count">; +def err_bounds_safety_flexible_global_non_int_count_init : Error< + "count %0 has non-integer value %1 of type %2">; + +def err_bounds_safety_local_external_dynamic_count : Error< + "attribute %0 is not allowed for local variables with external storage">; +def err_bounds_safety_scope_dynamic_range : Error< + "attribute %0 is only allowed for struct fields or function parameters">; +def err_bounds_safety_typedef_dynamic_bound : Error< + "%0 inside typedef is only allowed for function type">; +def err_bounds_safety_sized_by_array : Error< + "%0 cannot apply to arrays: use 'counted_by' instead">; +def err_bounds_safety_forge_addr_type : Error< + "'__unsafe_forge_%select{bidi_indexable|single|terminated_by}0' requires a pointer, array " + "or integer address argument">; +def err_bounds_safety_forge_bidi_size_type : Error< + "'__unsafe_forge_bidi_indexable' requires an integer size argument">; +def err_bounds_safety_forge_negative : Error< + "negative %select{address|size}0 argument to '__unsafe_forge_" + "%select{bidi_indexable|single|terminated_by}1'">; + +def err_bounds_safety_abi_ptr_attr_invalid : Error< + "%0 cannot be set as a default pointer attribute">; + +def err_bounds_safety_no_bounds : Error< + "cannot extract the %select{lower|upper}0 bound of %1 because %select{" + "it is not a pointer|it has no bounds specification|" + "its bounds are not contained within the pointer}2">; + +def err_bounds_safety_ptr_with_bounds_in_assembly : Error< + "pointer with bounds cannot be used with inline assembly">; +def err_bounds_safety_dyn_count_in_assembly : Error< + "external count of a pointer cannot be used with inline assembly">; + +def err_bounds_safety_ptrauth_on_indexable_pointer : Error< + "pointer authentication is currently unsupported on indexable pointers">; + +def err_bounds_safety_auto_dynamic_bound : Error< + "passing %select{'__counted_by'|'__sized_by'|'__counted_by_or_null'|'__sized_by_or_null'|'__ended_by'|end}0 pointer " + "as __auto_type initializer is not yet supported">; + +def err_bounds_safety_unsupported_address_of_incomplete_array : Error< + "cannot take address of incomplete __counted_by array">; +def note_bounds_safety_remove_address_of_operator : Note< + "remove '&' to get address as %0 instead of %1">; +def err_bounds_safety_unsupported_address_of_incomplete_array_type : Error< + "pointer to incomplete __counted_by array type %0 not allowed; " + "did you mean to use a nested pointer type?">; +def err_bounds_safety_unsupported_address_of_incomplete_array_count : Error< + "cannot take address of field referred to by __counted_by on a flexible array member">; + +def err_bounds_safety_terminated_by_wrong_type : Error< + "'__terminated_by' attribute can be applied to pointers, constant-length " + "arrays or incomplete arrays">; +def err_bounds_safety_terminated_by_wrong_pointer_type : Error< + "'__terminated_by' attribute currently can be applied only to '__single' " + "pointers">; +def err_bounds_safety_terminated_by_wrong_elem_or_pointee_type : Error< + "%select{element|pointee}0 type of %select{array|pointer}0 with " + "'__terminated_by' attribute must be an integer or a non-wide pointer">; +def err_bounds_safety_terminated_by_empty_array : Error< + "'__terminated_by' attribute cannot be applied to empty arrays">; + +def err_bounds_safety_terminated_by_terminator_must_be_const : Error< + "terminator value is not a constant expression">; + +def err_bounds_safety_terminated_by_to_indexable_wrong_ptr_type : Error< + "pointer argument must be a '__terminated_by' pointer (%0 invalid)">; +def err_bounds_safety_terminated_by_to_indexable_wrong_terminator : Error< + "pointer argument must be terminated by %0 (got %1)">; + +def err_bounds_safety_terminated_by_from_indexable_wrong_ptr_type : Error< + "pointer argument must be a safe pointer (%0 invalid)">; +def err_bounds_safety_terminated_by_from_indexable_wrong_pointee_type : Error< + "pointee type of the pointer argument must be an integer or a non-wide " + "pointer">; +def err_bounds_safety_terminated_by_from_indexable_wrong_ptr_to_term_type : Error< + "pointer to terminator argument must be a pointer (%0 invalid)">; +def err_bounds_safety_terminated_by_from_indexable_pointee_mismatch : Error< + "pointee types of the pointer and pointer to terminator arguments must be " + "the same">; + +def err_bounds_safety_incompatible_string_literal_to_terminated_by : Error< + "'__terminated_by' pointer converted from a string literal must be " + "NUL-terminated">; + +def note_fixit_null_terminated_to_indexable: Note<"consider using '%select{__null_terminated_to_indexable|__unsafe_null_terminated_to_indexable}0()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound %select{excludes|includes}0 the null terminator">; +def note_fixit_unsafe_null_terminated_from_indexable : Note<"consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator">; +def note_fixit_unsafe_null_terminated_from_indexable_ptr : Note<"consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time">; + +def err_bounds_safety_incompatible_terminated_by_terminators : Error< + "pointers with incompatible terminators " + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2">; +def err_bounds_safety_incompatible_terminated_by_to_non_terminated_by : Error< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 requires a linear search for the terminator; use " + "'%select{__terminated_by_to_indexable|__null_terminated_to_indexable}3()' to perform this conversion explicitly">; +def warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by : Warning< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 is an unsafe operation" + "%select{; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion|" + "; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion|" + "}3">, InGroup, DefaultError; +def err_bounds_safety_incompatible_non_terminated_by_to_terminated_by : Error; +def err_bounds_safety_incompatible_terminated_by_to_non_terminated_by_mismatch : Error< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 that discards '__terminated_by' attribute is not allowed">; +def warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch : Warning< + "%select{" + "%diff{assigning to $ from incompatible type $|" + "assigning type from incompatible type}0,1|" + "%diff{passing $ to parameter of incompatible type $|" + "passing type to parameter of incompatible type}0,1|" + "%diff{returning $ from a function with incompatible result type $|" + "returning type from a function with incompatible result type}0,1|" + "%diff{converting $ to incompatible type $|" + "converting type to incompatible type}0,1|" + "%diff{initializing $ with an expression of incompatible type $|" + "initializing type with an expression of incompatible type}0,1|" + "%diff{sending $ to parameter of incompatible type $|" + "sending type to parameter of incompatible type}0,1|" + "%diff{casting $ to incompatible type $|" + "casting type to incompatible type}0,1|" + "}2 that adds '__terminated_by' attribute is not allowed">, InGroup, DefaultError; +def err_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch : Error; + +def err_bounds_safety_terminated_by_uninitialized : Error< + "array '%0' with '__terminated_by' attribute must be initialized">; +def err_bounds_safety_terminated_by_incomplete_array_empty_init : Error< + "incomplete array '%0' with '__terminated_by' attribute must be initialized " + "with at least one element">; +def err_bounds_safety_terminated_by_terminator_in_array_must_be_const : Error< + "terminator in array '%0' must be a compile-time constant">; +def err_bounds_safety_terminated_by_wrong_initializer_kind : Error< + "array '%0' with '__terminated_by' attribute must be initialized with a " + "string literal or an initializer list">; +def err_bounds_safety_terminated_by_terminator_mismatch : Error< + "array '%0' with '__terminated_by' attribute is initialized with an " + "incorrect terminator (expected: %quoted1; got %quoted2)">; + +def err_bounds_safety_irrecoverable_attr : Error< + "bounds attribute '%select{__bidi_indexable|__indexable|__single|__unsafe_indexable}0' cannot be applied to attributed type %1 in this context%select{| due to the surrounding 'typeof' specifier}2">; +def err_bounds_safety_undeduced : Error< + "bounds attribute '%select{__bidi_indexable|__indexable|__single|__unsafe_indexable}0' cannot be applied to an undeduced type">; +} // End of CategoryName = "BoundsSafety Pointer Attributes Issue" +// TO_UPSTREAM(BoundsSafety) OFF + // three-way comparison operator diagnostics def err_implied_comparison_category_type_not_found : Error< "cannot %select{use builtin operator '<=>'|default 'operator<=>'}1 " @@ -12902,6 +13799,16 @@ def note_safe_buffer_usage_suggestions_disabled : Note< def warn_unsafe_buffer_usage_in_container : Warning< "the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information">, InGroup, DefaultIgnore; +def warn_unsafe_count_attributed_pointer_argument : Warning< + "unsafe assignment to function parameter of count-attributed type">, + InGroup, DefaultIgnore; +def note_unsafe_count_attributed_pointer_argument : Note< + "consider using %select{|a safe container and passing '.data()' to the " + "parameter%select{| '%3'}2 and '.size()' to its dependent parameter '%4' or }0" + "'std::span' and passing '.first(...).data()' to the parameter%select{| '%3'}2">; +def warn_unsafe_single_pointer_argument : Warning< + "unsafe assignment to function parameter of __single pointer type">, + InGroup, DefaultIgnore; #ifndef NDEBUG // Not a user-facing diagnostic. Useful for debugging false negatives in // -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits). diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 14bff8a68846d..0943100247049 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -65,6 +65,7 @@ FEATURE(attribute_availability_app_extension, true) FEATURE(attribute_availability_with_version_underscores, true) FEATURE(attribute_availability_tvos, true) FEATURE(attribute_availability_watchos, true) +FEATURE(attribute_availability_swift, true) FEATURE(attribute_availability_driverkit, true) FEATURE(attribute_availability_with_strict, true) FEATURE(attribute_availability_with_replacement, true) @@ -94,6 +95,7 @@ FEATURE(cxx_rtti, LangOpts.RTTI &&LangOpts.RTTIData) EXTENSION(define_target_os_macros, PP.getPreprocessorOpts().DefineTargetOSMacros) FEATURE(enumerator_attributes, true) +FEATURE(generalized_swift_name, true) FEATURE(nullability, true) FEATURE(nullability_on_arrays, true) FEATURE(nullability_on_classes, true) @@ -105,6 +107,11 @@ FEATURE(memory_sanitizer, FEATURE(type_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Type)) FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread)) FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow)) +FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics) +FEATURE(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics) +FEATURE(ptrauth_calls, LangOpts.PointerAuthCalls) +FEATURE(ptrauth_returns, LangOpts.PointerAuthReturns) +FEATURE(ptrauth_indirect_gotos, LangOpts.PointerAuthIndirectGotos) FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo)) FEATURE(ptrauth_intrinsics, LangOpts.PointerAuthIntrinsics) EXTENSION(ptrauth_qualifier, LangOpts.PointerAuthIntrinsics) @@ -325,5 +332,12 @@ FEATURE(clang_atomic_attributes, true) FEATURE(cuda_noinline_keyword, LangOpts.CUDA) EXTENSION(cuda_implicit_host_device_templates, LangOpts.CUDA && LangOpts.OffloadImplicitHostDeviceTemplates) +// TODO(BoundsSafety): Deprecate this feature name +FEATURE(bounds_attributes, LangOpts.BoundsSafety) +/* TO_UPSTREAM(BoundsSafety) ON*/ +FEATURE(bounds_safety, LangOpts.BoundsSafety) +FEATURE(bounds_safety_attributes, LangOpts.BoundsSafetyAttributes) +/* TO_UPSTREAM(BoundsSafety) OFF*/ + #undef EXTENSION #undef FEATURE diff --git a/clang/include/clang/Basic/FileManager.h b/clang/include/clang/Basic/FileManager.h index e83a61d6ff00c..fc88642220bda 100644 --- a/clang/include/clang/Basic/FileManager.h +++ b/clang/include/clang/Basic/FileManager.h @@ -261,21 +261,32 @@ class FileManager : public RefCountedBase { getBufferForFile(FileEntryRef Entry, bool isVolatile = false, bool RequiresNullTerminator = true, std::optional MaybeLimit = std::nullopt, - bool IsText = true); + bool IsText = true, + std::optional *CASContents = nullptr); llvm::ErrorOr> getBufferForFile(StringRef Filename, bool isVolatile = false, bool RequiresNullTerminator = true, std::optional MaybeLimit = std::nullopt, - bool IsText = true) const { + bool IsText = true, + std::optional *CASContents = nullptr) const { return getBufferForFileImpl(Filename, /*FileSize=*/MaybeLimit.value_or(-1), - isVolatile, RequiresNullTerminator, IsText); + isVolatile, RequiresNullTerminator, IsText, + CASContents); } + /// This is a convenience method that opens a file, gets the \p cas::ObjectRef + /// for its contents if supported by the file system, and then closes the + /// file. If both the buffer and its `cas::ObjectRef` are needed use \p + /// getBufferForFile to avoid the extra file lookup. + llvm::ErrorOr> + getObjectRefForFileContent(const Twine &Filename); + private: llvm::ErrorOr> getBufferForFileImpl(StringRef Filename, int64_t FileSize, bool isVolatile, - bool RequiresNullTerminator, bool IsText) const; + bool RequiresNullTerminator, bool IsText, + std::optional *CASContents) const; DirectoryEntry *&getRealDirEntry(const llvm::vfs::Status &Status); diff --git a/clang/include/clang/Basic/FileSystemOptions.h b/clang/include/clang/Basic/FileSystemOptions.h index 458af0c7b6592..d1c6a67884fdd 100644 --- a/clang/include/clang/Basic/FileSystemOptions.h +++ b/clang/include/clang/Basic/FileSystemOptions.h @@ -24,6 +24,14 @@ class FileSystemOptions { /// If set, paths are resolved as if the working directory was /// set to the value of WorkingDir. std::string WorkingDir; + + /// If set, uses this root ID with \a CASFileSystem. + std::string CASFileSystemRootID; + + /// If set, used as the working directory for -fcas-fs. + /// + /// FIXME: Merge with WorkingDir? + std::string CASFileSystemWorkingDirectory; }; } // end namespace clang diff --git a/clang/include/clang/Basic/LLVM.h b/clang/include/clang/Basic/LLVM.h index f4956cd16cbcf..1328f0c938af3 100644 --- a/clang/include/clang/Basic/LLVM.h +++ b/clang/include/clang/Basic/LLVM.h @@ -47,6 +47,14 @@ namespace llvm { class raw_ostream; class raw_pwrite_stream; // TODO: DenseMap, ... + + namespace cas { + class ActionCache; + class ObjectStore; + class CASID; + class ObjectProxy; + class ObjectRef; + } // namespace cas } @@ -84,6 +92,14 @@ namespace clang { using llvm::raw_ostream; using llvm::raw_pwrite_stream; + + namespace cas { + using llvm::cas::ActionCache; + using llvm::cas::CASID; + using llvm::cas::ObjectProxy; + using llvm::cas::ObjectRef; + using llvm::cas::ObjectStore; + } // namespace cas } // end namespace clang. #endif diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 1258a349ebf00..e0bbb8b304f3c 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -160,12 +160,12 @@ LANGOPT(GNUAsm , 1, 1, "GNU-style inline assembly") LANGOPT(Coroutines , 1, 0, "C++20 coroutines") LANGOPT(CoroAlignedAllocation, 1, 0, "prefer Aligned Allocation according to P2014 Option 2") LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods") +LANGOPT(Unstable , 1, 0, "Enable unstable and experimental features") LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features") LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics") LANGOPT(PointerAuthCalls , 1, 0, "function pointer authentication") LANGOPT(PointerAuthReturns, 1, 0, "return pointer authentication") -LANGOPT(PointerAuthIndirectGotos, 1, 0, "indirect gotos pointer authentication") LANGOPT(PointerAuthAuthTraps, 1, 0, "pointer authentication failure traps") LANGOPT(PointerAuthVTPtrAddressDiscrimination, 1, 0, "incorporate address discrimination in authenticated vtable pointers") LANGOPT(PointerAuthVTPtrTypeDiscrimination, 1, 0, "incorporate type discrimination in authenticated vtable pointers") @@ -178,6 +178,12 @@ LANGOPT(PointerAuthInitFiniAddressDiscrimination, 1, 0, LANGOPT(PointerAuthELFGOT, 1, 0, "authenticate pointers from GOT") LANGOPT(AArch64JumpTableHardening, 1, 0, "use hardened lowering for jump-table dispatch") +LANGOPT(PointerAuthIndirectGotos, 1, 0, "indirect gotos pointer authentication") +LANGOPT(SoftPointerAuth , 1, 0, "software emulation of pointer authentication") +VALUE_LANGOPT(PointerAuthABIVersion, 32, 0, "pointer authentication ABI version") +LANGOPT(PointerAuthKernelABIVersion, 1, 0, "controls whether the pointer auth abi version represents a kernel ABI") +LANGOPT(PointerAuthABIVersionEncoded, 1, 0, "controls whether the pointer auth abi version should be encoded in the IR") + LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes") LANGOPT(ExperimentalLateParseAttributes, 1, 0, "experimental late parsing of attributes") @@ -206,6 +212,7 @@ COMPATIBLE_LANGOPT(ModulesValidateTextualHeaderIncludes, 1, 1, "validation of te BENIGN_LANGOPT(ModulesErrorRecovery, 1, 1, "automatically importing modules as needed when performing error recovery") BENIGN_LANGOPT(ImplicitModules, 1, 1, "building modules that are not specified via -fmodule-file") COMPATIBLE_LANGOPT(ModulesLocalVisibility, 1, 0, "local submodule visibility") +COMPATIBLE_LANGOPT(ModulesHashErrorDiags, 1, 0, "hash out diagnostic errors as part of the module hash") COMPATIBLE_LANGOPT(Optimize , 1, 0, "__OPTIMIZE__ predefined macro") COMPATIBLE_LANGOPT(OptimizeSize , 1, 0, "__OPTIMIZE_SIZE__ predefined macro") COMPATIBLE_LANGOPT(Static , 1, 0, "__STATIC__ predefined macro (as opposed to __DYNAMIC__)") @@ -445,6 +452,7 @@ COMPATIBLE_LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation LANGOPT(APINotes, 1, 0, "use external API notes") LANGOPT(APINotesModules, 1, 0, "use module-based external API notes") +LANGOPT(NeededByPCHOrCompilationUsesPCH, 1, 0, "compilation involves pch") LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan " "field padding (0: none, 1:least " @@ -524,6 +532,8 @@ VALUE_LANGOPT(FuchsiaAPILevel, 32, 0, "Fuchsia API level") // on large _BitInts. BENIGN_VALUE_LANGOPT(MaxBitIntWidth, 32, 128, "Maximum width of a _BitInt") +LANGOPT(IgnoreConflictingTypes, 1, 0, "Suppress conflicting type errors from mismatching declarations") + COMPATIBLE_LANGOPT(IncrementalExtensions, 1, 0, "True if we want to process statements " "on the global scope, ignore EOF token and continue later on (thus " "avoid tearing the Lexer and etc. down). Controlled by " @@ -541,6 +551,15 @@ BENIGN_LANGOPT(CheckConstexprFunctionBodies, 1, 1, "be used in a constant expression.") LANGOPT(BoundsSafety, 1, 0, "Bounds safety extension for C") +/* TO_UPSTREAM(BoundsSafety) ON*/ +LANGOPT(BoundsSafetyAttributes, 1, 0, "Experimental bounds safety attributes") +LANGOPT(BoundsAttributesCXXExperimental, 1, 0, "Experimental bounds attributes for C++") +LANGOPT(BoundsAttributesObjCExperimental, 1, 0, "Experimental bounds attributes for Objective-C") +LANGOPT(BoundsSafetyRelaxedSystemHeaders, 1, 1, + "Relax bounds safety assignment rules in system headers") +LANGOPT(BoundsSafetyBringUpMissingChecks, 7, clang::LangOptions::getDefaultBoundsSafetyNewChecksMask(), + "Bring up new -fbounds-safety run-time checks") +/* TO_UPSTREAM(BoundsSafety) OFF*/ LANGOPT(PreserveVec3Type, 1, 0, "Preserve 3-component vector type") diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index bbebf7af9ede3..4b12fb943b588 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -65,6 +65,8 @@ enum class PointerAuthenticationMode : unsigned { SignAndAuth }; +enum class FeatureAvailKind { None, Available, Unavailable, Dynamic }; + /// Bitfields of LangOptions, split out from LangOptions in order to ensure that /// this large collection of bitfields is a trivial class type. class LangOptionsBase { @@ -444,6 +446,25 @@ class LangOptionsBase { IncompleteOnly = 3, }; + /* TODO(BoundsSafety) Deprecate the flag */ + enum BoundsSafetyNewChecks { + BS_CHK_None = 0, + + BS_CHK_AccessSize = 1 << 0, // rdar://72252593 + BS_CHK_IndirectCountUpdate = 1 << 1, // rdar://98749526 + BS_CHK_ReturnSize = 1 << 2, // rdar://83900556 + BS_CHK_EndedByLowerBound = 1 << 3, // rdar://91596663 + BS_CHK_CompoundLiteralInit = 1 << 4, // rdar://110871666 + BS_CHK_LibCAttributes = 1 << 5, // rdar://84733153 + BS_CHK_ArraySubscriptAgg = 1 << 6, // rdar://145020583 + }; + using BoundsSafetyNewChecksMaskIntTy = + std::underlying_type_t; + + static BoundsSafetyNewChecksMaskIntTy + getBoundsSafetyNewChecksMaskForGroup(StringRef GroupName); + static BoundsSafetyNewChecksMaskIntTy getDefaultBoundsSafetyNewChecksMask(); + /// Controls the various implementations for complex multiplication and // division. enum ComplexRangeKind { @@ -567,6 +588,8 @@ class LangOptions : public LangOptionsBase { /// This list is sorted. std::vector ModuleFeatures; + std::vector FeatureAvailability; + /// Options for parsing comments. CommentOptions CommentOpts; @@ -584,6 +607,16 @@ class LangOptions : public LangOptionsBase { /// host code generation. std::string OMPHostIRFile; + llvm::StringMap FeatureAvailabilityMap; + + void setFeatureAvailability(StringRef Feature, FeatureAvailKind Kind) { + FeatureAvailabilityMap[Feature] = Kind; + } + + FeatureAvailKind getFeatureAvailability(StringRef Feature) const { + return FeatureAvailabilityMap.lookup(Feature); + } + /// The user provided compilation unit ID, if non-empty. This is used to /// externalize static variables which is needed to support accessing static /// device variables in host code for single source offloading languages @@ -843,6 +876,36 @@ class LangOptions : public LangOptionsBase { bool isTargetDevice() const { return OpenMPIsTargetDevice || CUDAIsDevice || SYCLIsDevice; } + + /* TO_UPSTREAM(BoundsSafety) ON */ + // Returns true iff -fbounds-safety is enabled. This is a convenience function + // that can be called from Xcode disclosed clang without hitting a compilation + // error (unlike directly accessing the `BoundsSafety` field). + bool hasBoundsSafety() const { + return BoundsSafety; + } + + // Returns true iff -fbounds-safety-attributes is enabled. This is a + // convenience function that can be called from Xcode disclosed clang without + // hitting a compilation error (unlike directly accessing the + // `BoundsSafetyAttributes` field). + bool hasBoundsSafetyAttributes() const { + return BoundsSafetyAttributes; + } + + // Returns true iff we are compiling in bounds-safety's attribute-only mode. + bool isBoundsSafetyAttributeOnlyMode() const { + return hasBoundsSafetyAttributes() && !hasBoundsSafety(); + } + + // Take a BoundsSafetyNewChecksMask (or check mask) and return true if + // the check (or mask) is set. New -fbounds-safety run-time checks should use + // this helper to decide whether to enable/disable the checks. + bool hasNewBoundsSafetyCheck(BoundsSafetyNewChecksMaskIntTy ChkMask) const { + return (BoundsSafetyBringUpMissingChecks & ChkMask) != 0; + } + + /* TO_UPSTREAM(BoundsSafety) OFF */ }; /// Floating point control options diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h index 62cc8acf9588b..d1b822f08fb05 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -114,6 +114,9 @@ struct ModuleAttributes { LLVM_PREFERRED_TYPE(bool) unsigned IsExhaustive : 1; + /// \brief Whether this is a module who has its swift_names inferred. + unsigned IsSwiftInferImportAsMember : 1; + /// Whether files in this module can only include non-modular headers /// and headers from used modules. LLVM_PREFERRED_TYPE(bool) @@ -155,6 +158,10 @@ class alignas(8) Module { /// This is a C++20 header unit. ModuleHeaderUnit, + /// This is a module that was defined by a module map and built out + /// of header files as part of an \c IncludeTree. + IncludeTreeModuleMap, + /// This is a C++20 module interface unit. ModuleInterfaceUnit, @@ -243,7 +250,9 @@ class alignas(8) Module { bool isPrivateModule() const { return Kind == PrivateModuleFragment; } - bool isModuleMapModule() const { return Kind == ModuleMapModule; } + bool isModuleMapModule() const { + return Kind == ModuleMapModule || Kind == IncludeTreeModuleMap; + } private: /// The submodules of this module, indexed by name. @@ -257,6 +266,9 @@ class alignas(8) Module { /// corresponding serialized AST file, or null otherwise. OptionalFileEntryRef ASTFile; + /// The \c ActionCache key for this module, if any. + std::optional ModuleCacheKey; + /// The top-level headers associated with this module. llvm::SmallSetVector TopHeaders; @@ -393,6 +405,11 @@ class alignas(8) Module { LLVM_PREFERRED_TYPE(bool) unsigned IsInferred : 1; + /// Whether this is an inferred submodule that's missing from the umbrella + /// header. + LLVM_PREFERRED_TYPE(bool) + unsigned IsInferredMissingFromUmbrellaHeader : 1; + /// Whether we should infer submodules for this module based on /// the headers. /// @@ -428,6 +445,9 @@ class alignas(8) Module { LLVM_PREFERRED_TYPE(bool) unsigned ModuleMapIsPrivate : 1; + /// \brief Whether this is a module who has its swift_names inferred. + unsigned IsSwiftInferImportAsMember : 1; + /// Whether this C++20 named modules doesn't need an initializer. /// This is only meaningful for C++20 modules. LLVM_PREFERRED_TYPE(bool) @@ -739,6 +759,15 @@ class alignas(8) Module { getTopLevelModule()->ASTFile = File; } + std::optional getModuleCacheKey() const { + return getTopLevelModule()->ModuleCacheKey; + } + + void setModuleCacheKey(std::string Key) { + assert(!getModuleCacheKey() || *getModuleCacheKey() == Key); + getTopLevelModule()->ModuleCacheKey = std::move(Key); + } + /// Retrieve the umbrella directory as written. std::optional getUmbrellaDirAsWritten() const { if (const auto *Dir = std::get_if(&Umbrella)) diff --git a/clang/include/clang/Basic/PathRemapper.h b/clang/include/clang/Basic/PathRemapper.h new file mode 100644 index 0000000000000..e035a290f1966 --- /dev/null +++ b/clang/include/clang/Basic/PathRemapper.h @@ -0,0 +1,74 @@ +//===--- PathRemapper.h - Remap filepath prefixes ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a data structure that stores a string-to-string +// mapping used to transform file paths based on a prefix mapping. It +// is optimized for the common case of having 0, 1, or 2 mappings. +// +// Remappings are stored such that they are applied in the order they +// are passed on the command line, with the first one winning - a path will +// only be remapped by a single mapping, if any, not multiple. The ordering +// would only matter if one source mapping was a prefix of another. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_PATHREMAPPER_H +#define LLVM_CLANG_BASIC_PATHREMAPPER_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Path.h" + +#include +#include + +namespace clang { + +class PathRemapper { + SmallVector, 2> PathMappings; +public: + /// Adds a mapping such that any paths starting with `FromPrefix` have that + /// portion replaced with `ToPrefix`. + void addMapping(StringRef FromPrefix, StringRef ToPrefix) { + PathMappings.emplace_back(FromPrefix.str(), ToPrefix.str()); + } + + /// Returns a remapped `Path` if it starts with a prefix in the remapper; + /// otherwise the original path is returned. + std::string remapPath(StringRef Path) const { + for (const auto &Mapping : PathMappings) + if (Path.starts_with(Mapping.first)) + return (Twine(Mapping.second) + + Path.substr(Mapping.first.size())).str(); + return Path.str(); + } + + /// Remaps `PathBuf` if it starts with a prefix in the remapper. Avoids any + /// heap allocations if the path is not remapped. + void remapPath(SmallVectorImpl &PathBuf) const { + for (const auto &E : PathMappings) + if (llvm::sys::path::replace_path_prefix(PathBuf, E.first, E.second)) + break; + } + + /// Return true if there are no path remappings (meaning remapPath will always + /// return the path given). + bool empty() const { + return PathMappings.empty(); + } + + ArrayRef> getMappings() const { + return PathMappings; + } +}; + +} // end namespace clang + +#endif diff --git a/clang/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h index a3a3e50bcde5d..79716898e89d9 100644 --- a/clang/include/clang/Basic/PointerAuthOptions.h +++ b/clang/include/clang/Basic/PointerAuthOptions.h @@ -38,9 +38,22 @@ class PointerAuthSchema { public: enum class Kind : unsigned { None, + Soft, ARM8_3, }; + /// Software pointer-signing "keys". If you add a new key, make sure this->Key + /// has a large enough bit-width. + enum class SoftKey : unsigned { + FunctionPointers = 0, + BlockInvocationFunctionPointers = 1, + BlockHelperFunctionPointers = 2, + ObjCMethodListFunctionPointers = 3, + CXXVTablePointers = 4, + CXXVirtualFunctionPointers = 5, + CXXMemberFunctionPointers = 6, + }; + /// Hardware pointer-signing keys in ARM8.3. /// /// These values are the same used in ptrauth.h. @@ -73,7 +86,7 @@ class PointerAuthSchema { unsigned AuthenticatesNullValues : 1; PointerAuthenticationMode SelectedAuthenticationMode : 2; Discrimination DiscriminationKind : 2; - unsigned Key : 2; + unsigned Key : 3; unsigned ConstantDiscriminator : 16; public: @@ -97,6 +110,24 @@ class PointerAuthSchema { ConstantDiscriminator = *ConstantDiscriminatorOrNone; } + PointerAuthSchema( + SoftKey Key, bool IsAddressDiscriminated, + PointerAuthenticationMode AuthenticationMode, + Discrimination OtherDiscrimination, + std::optional ConstantDiscriminatorOrNone = std::nullopt, + bool IsIsaPointer = false, bool AuthenticatesNullValues = false) + : TheKind(Kind::Soft), IsAddressDiscriminated(IsAddressDiscriminated), + IsIsaPointer(IsIsaPointer), + AuthenticatesNullValues(AuthenticatesNullValues), + SelectedAuthenticationMode(AuthenticationMode), + DiscriminationKind(OtherDiscrimination), Key(unsigned(Key)) { + assert((getOtherDiscrimination() != Discrimination::Constant || + ConstantDiscriminatorOrNone) && + "constant discrimination requires a constant!"); + if (ConstantDiscriminatorOrNone) + ConstantDiscriminator = *ConstantDiscriminatorOrNone; + } + PointerAuthSchema( ARM8_3Key Key, bool IsAddressDiscriminated, Discrimination OtherDiscrimination, @@ -107,6 +138,17 @@ class PointerAuthSchema { OtherDiscrimination, ConstantDiscriminatorOrNone, IsIsaPointer, AuthenticatesNullValues) {} + PointerAuthSchema( + SoftKey Key, bool IsAddressDiscriminated, + Discrimination OtherDiscrimination, + std::optional ConstantDiscriminatorOrNone = std::nullopt, + bool IsIsaPointer = false, bool AuthenticatesNullValues = false) + : PointerAuthSchema(Key, IsAddressDiscriminated, + PointerAuthenticationMode::SignAndAuth, + OtherDiscrimination, ConstantDiscriminatorOrNone, + IsIsaPointer, AuthenticatesNullValues) {} + + Kind getKind() const { return TheKind; } explicit operator bool() const { return isEnabled(); } @@ -146,6 +188,8 @@ class PointerAuthSchema { switch (getKind()) { case Kind::None: llvm_unreachable("calling getKey() on disabled schema"); + case Kind::Soft: + return unsigned(getSoftKey()); case Kind::ARM8_3: return llvm::to_underlying(getARM8_3Key()); } @@ -156,6 +200,11 @@ class PointerAuthSchema { return SelectedAuthenticationMode; } + SoftKey getSoftKey() const { + assert(getKind() == Kind::Soft); + return SoftKey(Key); + } + ARM8_3Key getARM8_3Key() const { assert(getKind() == Kind::ARM8_3); return ARM8_3Key(Key); @@ -163,6 +212,10 @@ class PointerAuthSchema { }; struct PointerAuthOptions { + /// Do member function pointers to virtual functions need to be built + /// as thunks? + bool ThunkCXXVirtualMemberPointers = false; + /// Should return addresses be authenticated? bool ReturnAddresses = false; @@ -202,6 +255,18 @@ struct PointerAuthOptions { /// The ABI for function addresses in .init_array and .fini_array PointerAuthSchema InitFiniPointers; + + /// The ABI for block invocation function pointers. + PointerAuthSchema BlockInvocationFunctionPointers; + + /// The ABI for block object copy/destroy function pointers. + PointerAuthSchema BlockHelperFunctionPointers; + + /// The ABI for __block variable copy/destroy function pointers. + PointerAuthSchema BlockByrefHelperFunctionPointers; + + /// The ABI for Objective-C method lists. + PointerAuthSchema ObjCMethodListFunctionPointers; }; } // end namespace clang diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h index e0f1ea435d54e..386582034c72c 100644 --- a/clang/include/clang/Basic/SourceManager.h +++ b/clang/include/clang/Basic/SourceManager.h @@ -389,6 +389,11 @@ class ExpansionInfo { : ExpansionLocEnd; } + /// The actual value of \c ExpansionLocEnd, useful for serialization purposes. + SourceLocation getUnderlyingExpansionLocEnd() const { + return ExpansionLocEnd; + } + bool isExpansionTokenRange() const { return ExpansionIsTokenRange; } CharSourceRange getExpansionLocRange() const { diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 9526fa5808aa5..771381bd5b4be 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -217,6 +217,19 @@ def SEHFinallyStmt : StmtNode; def SEHLeaveStmt : StmtNode; def MSDependentExistsStmt : StmtNode; +// TO_UPSTREAM(BoundsSafety) ON +// BoundsSafety Extensions. +def AssumptionExpr : StmtNode; +def BoundsSafetyPointerPromotionExpr : StmtNode; +def ForgePtrExpr : StmtNode; +def GetBoundExpr : StmtNode; +def PredefinedBoundsCheckExpr : StmtNode; +def BoundsCheckExpr : StmtNode; +def MaterializeSequenceExpr : StmtNode; +def TerminatedByToIndexableExpr : StmtNode; +def TerminatedByFromIndexableExpr : StmtNode; +// TO_UPSTREAM(BoundsSafety) OFF + // OpenCL Extensions. def AsTypeExpr : StmtNode; diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 77763a5b1d574..60c08282f16bf 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -283,6 +283,8 @@ class TargetInfo : public TransferrableTargetInfo, unsigned ARMCDECoprocMask : 8; + unsigned PointerAuthSupported : 1; + unsigned MaxOpenCLWorkGroupSize; std::optional MaxBitIntWidth; @@ -1599,6 +1601,14 @@ class TargetInfo : public TransferrableTargetInfo, return TLSSupported; } + /// \brief Whether the target supports pointer authentication at all. + /// + /// Whether pointer authentication is actually being used is determined + /// by the language option. + bool isPointerAuthSupported() const { + return PointerAuthSupported; + } + /// Return the maximum alignment (in bits) of a TLS variable /// /// Gets the maximum alignment (in bits) of a TLS variable on this target. diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 868e851342eb8..91a0afacedf12 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -805,6 +805,17 @@ KEYWORD(__builtin_sycl_unique_stable_name, KEYSYCL) // Clang-specific keywords enabled only in testing. TESTING_KEYWORD(__unknown_anytype , KEYALL) +/* TO_UPSTREAM(BoundsSafety) ON */ +// Bounds-safety extensions. +KEYWORD(__builtin_unsafe_forge_bidi_indexable , KEYBOUNDSSAFETY) +KEYWORD(__builtin_unsafe_forge_single , KEYBOUNDSSAFETYATTRIBUTES) +KEYWORD(__builtin_unsafe_forge_terminated_by , KEYBOUNDSSAFETYATTRIBUTES) +KEYWORD(__builtin_get_pointer_lower_bound , KEYBOUNDSSAFETY) +KEYWORD(__builtin_get_pointer_upper_bound , KEYBOUNDSSAFETY) +KEYWORD(__builtin_terminated_by_to_indexable , KEYBOUNDSSAFETY) +KEYWORD(__builtin_unsafe_terminated_by_to_indexable , KEYBOUNDSSAFETY) +KEYWORD(__builtin_unsafe_terminated_by_from_indexable, KEYBOUNDSSAFETY) +/* TO_UPSTREAM(BoundsSafety) OFF */ //===----------------------------------------------------------------------===// // Objective-C @-preceded keywords. diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td index 7e550ca2992f3..b320f2542cd3c 100644 --- a/clang/include/clang/Basic/TypeNodes.td +++ b/clang/include/clang/Basic/TypeNodes.td @@ -112,6 +112,10 @@ def ObjCInterfaceType : TypeNode, LeafType; def ObjCObjectPointerType : TypeNode; def BoundsAttributedType : TypeNode; def CountAttributedType : TypeNode, NeverCanonical; +// TO_UPSTREAM(BoundsSafety) ON +def DynamicRangePointerType : TypeNode, NeverCanonical; +def ValueTerminatedType : TypeNode, NeverCanonical; +// TO_UPSTREAM(BoundsSafety) OFF def PipeType : TypeNode; def AtomicType : TypeNode; def BitIntType : TypeNode; diff --git a/clang/include/clang/Basic/Version.h b/clang/include/clang/Basic/Version.h index 8e4e6928fded5..bbfb1052447e2 100644 --- a/clang/include/clang/Basic/Version.h +++ b/clang/include/clang/Basic/Version.h @@ -59,6 +59,9 @@ namespace clang { /// for use in the CPP __VERSION__ macro, which includes the clang version /// number, the repository version, and the vendor tag. std::string getClangFullCPPVersion(); + + /// Returns the major version number of clang. + unsigned getClangMajorVersionNumber(); } #endif // LLVM_CLANG_BASIC_VERSION_H diff --git a/clang/include/clang/CAS/CASOptions.h b/clang/include/clang/CAS/CASOptions.h new file mode 100644 index 0000000000000..03fb00a7fbff0 --- /dev/null +++ b/clang/include/clang/CAS/CASOptions.h @@ -0,0 +1,138 @@ +//===- CASOptions.h - Options for configuring the CAS -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines the clang::CASOptions interface. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CAS_CASOPTIONS_H +#define LLVM_CLANG_CAS_CASOPTIONS_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace llvm { +namespace cas { +class ActionCache; +class ObjectStore; +} // end namespace cas +} // end namespace llvm + +namespace clang { + +class DiagnosticsEngine; + +/// Base class for options configuring which CAS to use. Separated for the +/// fields where we don't need special move/copy logic. +/// +/// TODO: Add appropriate options once we support plugins. +class CASConfiguration { +public: + enum CASKind { + UnknownCAS, + InMemoryCAS, + OnDiskCAS, + }; + + /// Kind of CAS to use. + CASKind getKind() const { + return IsFrozen ? UnknownCAS : CASPath.empty() ? InMemoryCAS : OnDiskCAS; + } + + /// Path to a persistent backing store on-disk. This is optional, although \a + /// CASFileSystemRootID is unlikely to work without it. + /// + /// - "" means there is none; falls back to in-memory. + /// - "auto" is an alias for an automatically chosen location in the user's + /// system cache. + std::string CASPath; + + std::string PluginPath; + /// Each entry is a (, ) pair. + std::vector> PluginOptions; + + friend bool operator==(const CASConfiguration &LHS, + const CASConfiguration &RHS) { + return LHS.CASPath == RHS.CASPath && LHS.PluginPath == RHS.PluginPath && + LHS.PluginOptions == RHS.PluginOptions; + } + friend bool operator!=(const CASConfiguration &LHS, + const CASConfiguration &RHS) { + return !(LHS == RHS); + } + +private: + /// Whether the configuration has been "frozen", in order to hide the kind of + /// CAS that's in use. + bool IsFrozen = false; + friend class CASOptions; +}; + +/// Options configuring which CAS to use. User-accessible fields should be +/// defined in CASConfiguration to enable caching a CAS instance. +/// +/// CASOptions includes \a getOrCreateDatabases() for creating CAS and +/// ActionCache. +/// +/// FIXME: The the caching is done here, instead of as a field in \a +/// CompilerInstance, in order to ensure that \a +/// clang::createVFSFromCompilerInvocation() uses the same CAS instance that +/// the rest of the compiler job does, without updating all callers. Probably +/// it would be better to update all callers and remove it from here. +class CASOptions : public CASConfiguration { +public: + /// Get a CAS & ActionCache defined by the options above. Future calls will + /// return the same instances... unless the configuration has changed, in + /// which case new ones will be created. + /// + /// If \p CreateEmptyDBsOnFailure, returns empty in-memory databases on + /// failure. Else, returns \c nullptr on failure. + std::pair, + std::shared_ptr> + getOrCreateDatabases(DiagnosticsEngine &Diags, + bool CreateEmptyDBsOnFailure = false) const; + + llvm::Expected, + std::shared_ptr>> + getOrCreateDatabases() const; + + /// Freeze CAS Configuration. Future calls will return the same + /// CAS instance, even if the configuration changes again later. + /// + /// The configuration will be wiped out to prevent it being observable or + /// affecting the output of something that takes \a CASOptions as an input. + /// This also "locks in" the return value of \a getOrCreateDatabases(): + /// future calls will not check if the configuration has changed. + void freezeConfig(DiagnosticsEngine &Diags); + + /// If the configuration is not for a persistent store, it modifies it to the + /// default on-disk CAS, otherwise this is a noop. + void ensurePersistentCAS(); + +private: + /// Initialize Cached CAS and ActionCache. + llvm::Error initCache() const; + + struct CachedCAS { + /// A cached CAS instance. + std::shared_ptr CAS; + /// An ActionCache instnace. + std::shared_ptr AC; + + /// Remember how the CAS was created. + CASConfiguration Config; + }; + mutable CachedCAS Cache; +}; + +} // end namespace clang + +#endif // LLVM_CLANG_CAS_CASOPTIONS_H diff --git a/clang/include/clang/CAS/IncludeTree.h b/clang/include/clang/CAS/IncludeTree.h new file mode 100644 index 0000000000000..761f55c3a29d3 --- /dev/null +++ b/clang/include/clang/CAS/IncludeTree.h @@ -0,0 +1,881 @@ +//===- IncludeTree.h - Include-tree CAS graph -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CAS_CASINCLUDETREE_H +#define LLVM_CLANG_CAS_CASINCLUDETREE_H + +#include "clang/Basic/SourceManager.h" +#include "llvm/CAS/ObjectStore.h" + +namespace llvm { +class SmallBitVector; +} + +namespace clang { +namespace cas { + +/// Base class for include-tree related nodes. It makes it convenient to +/// add/skip/check the "node kind identifier" (\p getNodeKind()) that is put +/// at the beginning of the object data for every include-tree related node. +template class IncludeTreeBase : public ObjectProxy { +protected: + explicit IncludeTreeBase(ObjectProxy Node) : ObjectProxy(std::move(Node)) { + assert(isValid(*this)); + } + + StringRef getData() const { + return ObjectProxy::getData().substr(NodeT::getNodeKind().size()); + } + + static Expected create(ObjectStore &DB, ArrayRef Refs, + ArrayRef Data); + + static bool isValid(const ObjectProxy &Node) { + return Node.getData().starts_with(NodeT::getNodeKind()); + } + + friend NodeT; +}; + +/// Represents a DAG of included files by the preprocessor and module imports. +/// Each node in the DAG represents a particular inclusion of a file or module +/// that encompasses inclusions of other files as sub-trees, along with all the +/// \p __has_include() preprocessor checks that occurred during preprocessing +/// of that file. +class IncludeTree : public IncludeTreeBase { +public: + static constexpr StringRef getNodeKind() { return "Tree"; } + + class File; + class FileList; + class Node; + class Module; + class ModuleImport; + class SpuriousImport; + class ModuleMap; + class APINotes; + + Expected getBaseFile(); + + /// The include file that resulted in this include-tree. + ObjectRef getBaseFileRef() const { return getReference(0); } + + struct FileInfo { + StringRef Filename; + StringRef Contents; + }; + + Expected getBaseFileInfo(); + + SrcMgr::CharacteristicKind getFileCharacteristic() const { + return (SrcMgr::CharacteristicKind)(getData().front() & ~IsSubmoduleBit); + } + + bool isSubmodule() const { return (getData().front() & IsSubmoduleBit) != 0; } + + std::optional getSubmoduleNameRef() const { + if (isSubmodule()) + return getReference(getNumReferences() - 1); + return std::nullopt; + } + + /// If \c getBaseFile() is a modular header, get its submodule name. + Expected> getSubmoduleName() { + auto Ref = getSubmoduleNameRef(); + if (!Ref) + return std::nullopt; + auto Node = getCAS().getProxy(*Ref); + if (!Node) + return Node.takeError(); + return Node->getData(); + } + + size_t getNumIncludes() const { + return getNumReferences() - (isSubmodule() ? 2 : 1); + }; + + ObjectRef getIncludeRef(size_t I) const { + assert(I < getNumIncludes()); + return getReference(I + 1); + } + + enum class NodeKind : uint8_t { + Tree, + ModuleImport, + SpuriousImport, + }; + + /// The kind of node included at the given index. + NodeKind getIncludeKind(size_t I) const; + + /// The sub-include-tree or module import for the given index. + Expected getIncludeNode(size_t I); + + /// The sub-include-trees of included files, in the order that they occurred. + Expected getIncludeTree(size_t I) { + assert(getIncludeKind(I) == NodeKind::Tree); + return getIncludeTree(getIncludeRef(I)); + } + + /// The source byte offset for a particular include, pointing to the beginning + /// of the line just after the #include directive. The offset represents the + /// location at which point the include-tree would have been processed by the + /// preprocessor and parser. + /// + /// For example: + /// \code + /// #include "a.h" -> include-tree("a.h") + /// | <- include-tree-offset("a.h") + /// \endcode + /// + /// Using the "after #include" offset makes it trivial to identify the part + /// of the source file that encompasses a set of include-trees (for example in + /// the case where want to identify the "includes preamble" of the main file. + uint32_t getIncludeOffset(size_t I) const; + + /// The \p __has_include() preprocessor checks, in the order that they + /// occurred. The source offsets for the checks are not tracked, "replaying" + /// the include-tree depends on the invariant that the same exact checks will + /// occur in the same order. + bool getCheckResult(size_t I) const; + + /// Passes pairs of (Node, include offset) to \p Callback. + llvm::Error forEachInclude( + llvm::function_ref)> Callback); + + struct IncludeInfo { + ObjectRef Ref; ///< IncludeTree or IncludeTreeModuleImport + uint32_t Offset; + NodeKind Kind; + }; + + static Expected + create(ObjectStore &DB, SrcMgr::CharacteristicKind FileCharacteristic, + ObjectRef BaseFile, ArrayRef Includes, + std::optional SubmoduleName, llvm::SmallBitVector Checks); + + static Expected get(ObjectStore &DB, ObjectRef Ref); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + +private: + friend class IncludeTreeBase; + friend class IncludeTreeRoot; + + static constexpr char IsSubmoduleBit = 0x80; + + explicit IncludeTree(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + Expected getIncludeTree(ObjectRef Ref) { + auto Node = getCAS().getProxy(Ref); + if (!Node) + return Node.takeError(); + return IncludeTree(std::move(*Node)); + } + + Expected getIncludeNode(ObjectRef Ref, NodeKind Kind); + + StringRef dataSkippingFlags() const { return getData().drop_front(); } + StringRef dataSkippingIncludes() const { + return dataSkippingFlags().drop_front(getNumIncludes() * + (sizeof(uint32_t) + 1)); + } + + static bool isValid(const ObjectProxy &Node); + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } +}; + +/// Represents a \p SourceManager file (or buffer in the case of preprocessor +/// predefines) that got included by the preprocessor. +class IncludeTree::File : public IncludeTreeBase { +public: + static constexpr StringRef getNodeKind() { return "File"; } + + ObjectRef getFilenameRef() const { return getReference(0); } + ObjectRef getContentsRef() const { return getReference(1); } + + Expected getFilename() { + return getCAS().getProxy(getFilenameRef()); + } + + Expected getContents() { + return getCAS().getProxy(getContentsRef()); + } + + Expected getFileInfo() { + auto Filename = getFilename(); + if (!Filename) + return Filename.takeError(); + auto Contents = getContents(); + if (!Contents) + return Contents.takeError(); + return IncludeTree::FileInfo{Filename->getData(), Contents->getData()}; + } + + Expected> getMemoryBuffer() { + auto Filename = getFilename(); + if (!Filename) + return Filename.takeError(); + auto Contents = getContents(); + if (!Contents) + return Contents.takeError(); + return Contents->getMemoryBuffer(Filename->getData()); + } + + static Expected create(ObjectStore &DB, StringRef Filename, + ObjectRef Contents); + + static Expected get(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) + return Node.takeError(); + return File(std::move(*Node)); + } + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + return Base.getNumReferences() == 2 && Base.getData().empty(); + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + friend class IncludeTreeBase; + friend class FileList; + friend class IncludeTree; + friend class IncludeTreeRoot; + + explicit File(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } +}; + +/// A list of \c File entries. Multiple \c FileList can be combined without +/// copying their contents. This is used along with a simple implementation of a +/// \p vfs::FileSystem produced via \p createIncludeTreeFileSystem(). +class IncludeTree::FileList : public IncludeTreeBase { +public: + static constexpr StringRef getNodeKind() { return "List"; } + + using FileSizeTy = uint32_t; + + /// \returns each \c File entry along with its file size. + llvm::Error + forEachFile(llvm::function_ref Callback); + + /// We record the file size as well to avoid needing to materialize the + /// underlying buffer for the \p IncludeTreeFileSystem::status() + /// implementation to provide the file size. + struct FileEntry { + ObjectRef FileRef; + FileSizeTy Size; + }; + static Expected create(ObjectStore &DB, ArrayRef Files, + ArrayRef FileLists); + + static Expected get(ObjectStore &CAS, ObjectRef Ref); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + +private: + friend class IncludeTreeBase; + friend class IncludeTreeRoot; + + explicit FileList(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + size_t getNumFilesCurrentList() const; + FileSizeTy getFileSize(size_t I) const; + + llvm::Error + forEachFileImpl(llvm::DenseSet &Seen, + llvm::function_ref Callback); + + static bool isValid(const ObjectProxy &Node); + static bool isValid(ObjectStore &CAS, ObjectRef Ref) { + auto Node = CAS.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } +}; + +/// Represents a module imported by an IncludeTree. +class IncludeTree::ModuleImport : public IncludeTreeBase { +public: + static constexpr StringRef getNodeKind() { return "ModI"; } + + static Expected create(ObjectStore &DB, StringRef ModuleName, + bool VisibilityOnly); + + StringRef getModuleName() const { return getData().drop_front(); } + /// Whether this module should only be "marked visible" rather than imported. + bool visibilityOnly() const { return (bool)getData()[0]; } + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0, + char End = '\n'); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + return Base.getNumReferences() == 0 && Base.getData().size() > 1; + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + friend class IncludeTreeBase; + friend class SpuriousImport; + friend class Node; + + explicit ModuleImport(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } +}; + +class IncludeTree::SpuriousImport + : public IncludeTreeBase { +public: + static Expected + create(ObjectStore &DB, ObjectRef ImportRef, ObjectRef TreeRef); + + static constexpr StringRef getNodeKind() { return "SpIm"; } + + Expected getModuleImport() { + std::optional Proxy; + if (llvm::Error Err = getCAS().getProxy(getReference(0)).moveInto(Proxy)) + return std::move(Err); + return ModuleImport(*Proxy); + } + + Expected getIncludeTree() { + std::optional Proxy; + if (llvm::Error Err = getCAS().getProxy(getReference(1)).moveInto(Proxy)) + return std::move(Err); + return IncludeTree(*Proxy); + } + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + if (Base.getNumReferences() != 2 && Base.getData().size() != 0) + return false; + return ModuleImport::isValid(Base.getCAS(), Base.getReference(0)) && + IncludeTree::isValid(Base.getCAS(), Base.getReference(1)); + } + + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + friend class IncludeTreeBase; + friend class Node; + + explicit SpuriousImport(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } +}; + +/// Represents an \c IncludeTree or \c ModuleImport. +class IncludeTree::Node { +public: + IncludeTree getIncludeTree() const { + assert(K == NodeKind::Tree); + return IncludeTree(N); + } + ModuleImport getModuleImport() const { + assert(K == NodeKind::ModuleImport); + return ModuleImport(N); + } + SpuriousImport getSpuriousImport() const { + assert(K == NodeKind::SpuriousImport); + return SpuriousImport(N); + } + NodeKind getKind() const { return K; } + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + +private: + friend class IncludeTree; + Node(ObjectProxy N, NodeKind K) : N(std::move(N)), K(K) {} + ObjectProxy N; + NodeKind K; +}; + +/// Module or submodule declaration as part of the \c IncludeTreeRoot module +/// map structure. +class IncludeTree::Module : public IncludeTreeBase { +public: + static constexpr StringRef getNodeKind() { return "Modu"; } + + class ExportList; + class LinkLibraryList; + + struct ModuleFlags { + bool IsFramework : 1; + bool IsExplicit : 1; + bool IsExternC : 1; + bool IsSystem : 1; + bool InferSubmodules : 1; + bool InferExplicitSubmodules : 1; + bool InferExportWildcard : 1; + bool UseExportAsModuleLinkName: 1; + ModuleFlags() + : IsFramework(false), IsExplicit(false), IsExternC(false), + IsSystem(false), InferSubmodules(false), + InferExplicitSubmodules(false), InferExportWildcard(false), + UseExportAsModuleLinkName(false) {} + }; + + ModuleFlags getFlags() const; + + /// The name of the current (sub)module. + StringRef getName() const { + return dataAfterFlags().split('\0').first; + } + + StringRef getExportAsModule() const { + return dataAfterFlags().split('\0').second; + } + + size_t getNumSubmodules() const; + + Expected getSubmodule(size_t I) { + assert(I < getNumSubmodules()); + auto Node = getCAS().getProxy(getReference(I)); + if (!Node) + return Node.takeError(); + return Module(*Node); + } + + llvm::Error forEachSubmodule(llvm::function_ref CB); + + std::optional getExportsRef() const { + if (std::optional Index = getExportsIndex()) + return getReference(*Index); + return std::nullopt; + } + std::optional getLinkLibrariesRef() const { + if (std::optional Index = getLinkLibrariesIndex()) + return getReference(*Index); + return std::nullopt; + } + + /// The list of modules that this submodule re-exports. + Expected> getExports(); + + /// The list of modules that this submodule re-exports. + Expected> getLinkLibraries(); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static Expected create(ObjectStore &DB, StringRef ModuleName, + StringRef ExportAs, ModuleFlags Flags, + ArrayRef Submodules, + std::optional ExportList, + std::optional LinkLibraries); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + return Base.getData().size() > 2; + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + uint16_t rawFlags() const; + StringRef dataAfterFlags() const { return getData().drop_front(2); } + bool hasExports() const; + bool hasLinkLibraries() const; + std::optional getExportsIndex() const; + std::optional getLinkLibrariesIndex() const; + + explicit Module(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + friend class IncludeTreeBase; + friend class IncludeTreeRoot; + friend class ModuleMap; +}; + +/// The set of modules re-exported by another module. +class IncludeTree::Module::ExportList : public IncludeTreeBase { +public: + static constexpr StringRef getNodeKind() { return "ExpL"; } + + /// An explicit export. + struct Export { + StringRef ModuleName; + bool Wildcard; + }; + + /// Whether this module exports all imported modules (`export *`). + bool hasGlobalWildcard() const; + + size_t getNumExplicitExports() const { return getNumReferences(); } + + /// Whether the explicit export at \p I has a wildcard (`export MyModule.*`). + bool exportHasWildcard(size_t I) const; + + Expected getExplicitExport(size_t I); + + /// Calls \p CB for each explicit export declaration. + llvm::Error forEachExplicitExport(llvm::function_ref CB); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static Expected create(ObjectStore &DB, ArrayRef Exports, + bool GlobalWildcard); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + size_t ExpectedBitSize = Base.getNumReferences() + 1; + size_t ExpectedSize = + ExpectedBitSize / 8 + (ExpectedBitSize % 8 == 0 ? 0 : 1); + return Base.getData().size() == ExpectedSize; + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + explicit ExportList(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + friend class IncludeTreeBase; + friend class Module; +}; + +/// The set of libraries to link against when using a module. +class IncludeTree::Module::LinkLibraryList + : public IncludeTreeBase { +public: + static constexpr StringRef getNodeKind() { return "LnkL"; } + + struct LinkLibrary { + StringRef Library; + bool IsFramework; + }; + + size_t getNumLibraries() const { return getNumReferences(); } + + /// Whether the library at \p I is a framework. + bool isFramework(size_t I) const; + + ObjectRef getLibraryNameRef(size_t I) const { return getReference(I); } + + Expected getLinkLibrary(size_t I) { + auto Name = getCAS().getProxy(getLibraryNameRef(I)); + if (!Name) + return Name.takeError(); + return LinkLibrary{Name->getData(), isFramework(I)}; + } + + /// Calls \p CB for each link libary. + llvm::Error + forEachLinkLibrary(llvm::function_ref CB); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static Expected create(ObjectStore &DB, + ArrayRef Exports); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + size_t ExpectedBitSize = Base.getNumReferences(); + size_t ExpectedSize = + ExpectedBitSize / 8 + (ExpectedBitSize % 8 == 0 ? 0 : 1); + return Base.getData().size() == ExpectedSize; + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + explicit LinkLibraryList(ObjectProxy Node) + : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + friend class IncludeTreeBase; + friend class Module; +}; + +class IncludeTree::ModuleMap : public IncludeTreeBase { +public: + static constexpr StringRef getNodeKind() { return "ModM"; } + + size_t getNumModules() const { return getNumReferences(); } + + Expected getModule(size_t I) { + auto Node = getCAS().getProxy(getReference(I)); + if (!Node) + return Node.takeError(); + return Module(*Node); + } + + /// Calls \p CB for each module declaration. + llvm::Error forEachModule(llvm::function_ref CB); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static Expected create(ObjectStore &DB, + ArrayRef Modules); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + return Base.getData().empty(); + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + explicit ModuleMap(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + friend class IncludeTreeBase; + friend class IncludeTreeRoot; +}; + +/// A list of \c APINotes that is compiled and loaded. +class IncludeTree::APINotes : public IncludeTreeBase { +public: + static constexpr StringRef getNodeKind() { return "APIN"; } + + llvm::Error + forEachAPINotes(llvm::function_ref Callback); + + static Expected create(ObjectStore &DB, + ArrayRef APINoteList); + + static Expected get(ObjectStore &CAS, ObjectRef Ref); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + +private: + friend class IncludeTreeBase; + friend class IncludeTreeRoot; + + explicit APINotes(ObjectProxy Node) : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + return Base.getData().empty() && Base.getNumReferences() < 2; + } + + static bool isValid(ObjectStore &CAS, ObjectRef Ref) { + auto Node = CAS.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } +}; + +/// Represents the include-tree result for a translation unit. +class IncludeTreeRoot : public IncludeTreeBase { +public: + static constexpr StringRef getNodeKind() { return "Root"; } + + ObjectRef getMainFileTreeRef() const { return getReference(0); } + + ObjectRef getFileListRef() const { return getReference(1); } + + std::optional getPCHRef() const { + if (auto Index = getPCHRefIndex()) + return getReference(*Index); + return std::nullopt; + } + + std::optional getModuleMapRef() const { + if (auto Index = getModuleMapRefIndex()) + return getReference(*Index); + return std::nullopt; + } + + std::optional getAPINotesRef() const { + if (auto Index = getAPINotesRefIndex()) + return getReference(*Index); + return std::nullopt; + } + + Expected getMainFileTree() { + auto Node = getCAS().getProxy(getMainFileTreeRef()); + if (!Node) + return Node.takeError(); + return IncludeTree(std::move(*Node)); + } + + Expected getFileList() { + auto Node = getCAS().getProxy(getFileListRef()); + if (!Node) + return Node.takeError(); + return IncludeTree::FileList(std::move(*Node)); + } + + Expected> getPCH() { + if (std::optional Ref = getPCHRef()) { + auto Node = getCAS().getProxy(*Ref); + if (!Node) + return Node.takeError(); + return IncludeTree::File(std::move(*Node)); + } + return std::nullopt; + } + + Expected> getModuleMap() { + if (std::optional Ref = getModuleMapRef()) { + auto Node = getCAS().getProxy(*Ref); + if (!Node) + return Node.takeError(); + return IncludeTree::ModuleMap(*Node); + } + return std::nullopt; + } + + Expected> getAPINotes() { + if (std::optional Ref = getAPINotesRef()) { + auto Node = getCAS().getProxy(*Ref); + if (!Node) + return Node.takeError(); + return IncludeTree::APINotes(*Node); + } + return std::nullopt; + } + + static Expected + create(ObjectStore &DB, ObjectRef MainFileTree, ObjectRef FileList, + std::optional PCHRef, std::optional ModuleMapRef, + std::optional APINotesRef); + + static Expected get(ObjectStore &DB, ObjectRef Ref); + + llvm::Error print(llvm::raw_ostream &OS, unsigned Indent = 0); + + static bool isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + return (Base.getNumReferences() >= 2 && Base.getNumReferences() <= 4) && + Base.getData().size() == 1; + } + static bool isValid(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) { + llvm::consumeError(Node.takeError()); + return false; + } + return isValid(*Node); + } + +private: + friend class IncludeTreeBase; + + std::optional getPCHRefIndex() const; + std::optional getModuleMapRefIndex() const; + std::optional getAPINotesRefIndex() const; + + explicit IncludeTreeRoot(ObjectProxy Node) + : IncludeTreeBase(std::move(Node)) { + assert(isValid(*this)); + } +}; + +/// An implementation of a \p vfs::FileSystem that supports the simple queries +/// of the preprocessor, for creating \p FileEntries using a file path, while +/// "replaying" an \p IncludeTreeRoot. It is not intended to be a complete +/// implementation of a file system. +Expected> +createIncludeTreeFileSystem(IncludeTreeRoot &Root); + +/// Create the same IncludeTreeFileSystem but from +/// ArrayRef. +Expected> createIncludeTreeFileSystem( + llvm::cas::ObjectStore &CAS, + llvm::ArrayRef List); + +} // namespace cas +} // namespace clang + +#endif diff --git a/clang/include/clang/CodeGen/BackendUtil.h b/clang/include/clang/CodeGen/BackendUtil.h index 92e0d13bf25b6..bae2798b09f66 100644 --- a/clang/include/clang/CodeGen/BackendUtil.h +++ b/clang/include/clang/CodeGen/BackendUtil.h @@ -28,6 +28,7 @@ namespace clang { class CompilerInstance; class DiagnosticsEngine; class CodeGenOptions; +class CASOptions; // MCCAS class BackendConsumer; enum BackendAction { @@ -40,9 +41,11 @@ enum BackendAction { }; void emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts, + const CASOptions &CASOpts, // MCCAS StringRef TDesc, llvm::Module *M, BackendAction Action, llvm::IntrusiveRefCntPtr VFS, std::unique_ptr OS, + std::unique_ptr CasidOS = nullptr, BackendConsumer *BC = nullptr); void EmbedBitcode(llvm::Module *M, const CodeGenOptions &CGOpts, diff --git a/clang/include/clang/CodeGen/CodeGenABITypes.h b/clang/include/clang/CodeGen/CodeGenABITypes.h index 836fdd75477c7..1df379d54c382 100644 --- a/clang/include/clang/CodeGen/CodeGenABITypes.h +++ b/clang/include/clang/CodeGen/CodeGenABITypes.h @@ -32,6 +32,7 @@ namespace llvm { class AttrBuilder; class Constant; +class ConstantInt; class Function; class FunctionType; class Type; @@ -119,13 +120,6 @@ llvm::Type *convertTypeForMemory(CodeGenModule &CGM, QualType T); unsigned getLLVMFieldNumber(CodeGenModule &CGM, const RecordDecl *RD, const FieldDecl *FD); -/// Return a declaration discriminator for the given global decl. -uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD); - -/// Return a type discriminator for the given function type. -uint16_t getPointerAuthTypeDiscriminator(CodeGenModule &CGM, - QualType FunctionType); - /// Given the language and code-generation options that Clang was configured /// with, set the default LLVM IR attributes for a function definition. /// The attributes set here are mostly global target-configuration and diff --git a/clang/include/clang/CodeGen/ConstantInitBuilder.h b/clang/include/clang/CodeGen/ConstantInitBuilder.h index 28d4764b6d60b..2560aa27dfa73 100644 --- a/clang/include/clang/CodeGen/ConstantInitBuilder.h +++ b/clang/include/clang/CodeGen/ConstantInitBuilder.h @@ -207,6 +207,12 @@ class ConstantAggregateBuilderBase { const PointerAuthSchema &Schema, GlobalDecl CalleeDecl, QualType CalleeType); + /// Add a signed pointer using the given pointer authentication schema. + void addSignedPointer(llvm::Constant *pointer, + unsigned key, + bool useAddressDiscrimination, + llvm::ConstantInt *otherDiscriminator); + /// Add a null pointer of a specific type. void addNullPointer(llvm::PointerType *ptrTy) { add(llvm::ConstantPointerNull::get(ptrTy)); diff --git a/clang/include/clang/CodeGen/SwiftCallingConv.h b/clang/include/clang/CodeGen/SwiftCallingConv.h index d7a0c84699ab0..806c3f13ffe62 100644 --- a/clang/include/clang/CodeGen/SwiftCallingConv.h +++ b/clang/include/clang/CodeGen/SwiftCallingConv.h @@ -88,6 +88,8 @@ class SwiftAggLowering { /// passed indirectly as an argument bool shouldPassIndirectly(bool asReturnValue) const; + bool shouldReturnTypedErrorIndirectly() const; + using EnumerationCallback = llvm::function_ref; diff --git a/clang/include/clang/Driver/Action.h b/clang/include/clang/Driver/Action.h index 92bb19314e3d6..6d6c53f714c7e 100644 --- a/clang/include/clang/Driver/Action.h +++ b/clang/include/clang/Driver/Action.h @@ -56,6 +56,7 @@ class Action { InputClass = 0, BindArchClass, OffloadClass, + DepscanJobClass, PreprocessJobClass, PrecompileJobClass, ExtractAPIJobClass, @@ -77,7 +78,7 @@ class Action { BinaryAnalyzeJobClass, BinaryTranslatorJobClass, - JobClassFirst = PreprocessJobClass, + JobClassFirst = DepscanJobClass, JobClassLast = BinaryTranslatorJobClass }; @@ -411,6 +412,23 @@ class JobAction : public Action { } }; +class DepscanJobAction : public JobAction { + void anchor() override; + +public: + DepscanJobAction(Action *Input, types::ID OutputType); + + static bool classof(const Action *A) { + return A->getKind() == DepscanJobClass; + } + + const JobAction &getScanningJobAction() const { return *JA; } + void setScanningJobAction(const JobAction *Job) { JA = Job; } + +private: + const JobAction *JA; +}; + class PreprocessJobAction : public JobAction { void anchor() override; diff --git a/clang/include/clang/Driver/BoundsSafetyArgs.h b/clang/include/clang/Driver/BoundsSafetyArgs.h new file mode 100644 index 0000000000000..310bdf8a7faf6 --- /dev/null +++ b/clang/include/clang/Driver/BoundsSafetyArgs.h @@ -0,0 +1,23 @@ +//===- BoundsSafetyArgs.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_BASIC_BOUNDS_SAFETY_ARGS_H +#define LLVM_CLANG_BASIC_BOUNDS_SAFETY_ARGS_H +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LangOptions.h" +#include "llvm/Option/ArgList.h" + +namespace clang { +namespace driver { + +LangOptions::BoundsSafetyNewChecksMaskIntTy +ParseBoundsSafetyNewChecksMaskFromArgs(const llvm::opt::ArgList &Args, + DiagnosticsEngine *Diags); +} // namespace driver +} // namespace clang + +#endif diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h index b463dc2a93550..0ccfd50ffafca 100644 --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -790,6 +790,7 @@ class Driver { /// compilation based on which -f(no-)?lto(=.*)? option occurs last. void setLTOMode(const llvm::opt::ArgList &Args); +public: /// Retrieves a ToolChain for a particular \p Target triple. /// /// Will cache ToolChains for the life of the driver object, and create them @@ -797,6 +798,9 @@ class Driver { const ToolChain &getToolChain(const llvm::opt::ArgList &Args, const llvm::Triple &Target) const; + /// @} +private: + /// Retrieves a ToolChain for a particular \p Target triple for offloading. /// /// Will cache ToolChains for the life of the driver object, and create them @@ -875,6 +879,9 @@ llvm::Error expandResponseFiles(SmallVectorImpl &Args, bool ClangCLMode, llvm::BumpPtrAllocator &Alloc, llvm::vfs::FileSystem *FS = nullptr); +/// Checks whether the value produced by getDriverMode is for 'cache' mode. +bool isClangCache(StringRef DriverMode); + /// Apply a space separated list of edits to the input argument lists. /// See applyOneOverrideOption. void applyOverrideOptions(SmallVectorImpl &Args, diff --git a/clang/include/clang/Driver/Job.h b/clang/include/clang/Driver/Job.h index 561866197b780..2fc40eaed7ce4 100644 --- a/clang/include/clang/Driver/Job.h +++ b/clang/include/clang/Driver/Job.h @@ -33,9 +33,11 @@ class Tool; struct CrashReportInfo { StringRef Filename; StringRef VFSPath; + StringRef IndexStorePath; - CrashReportInfo(StringRef Filename, StringRef VFSPath) - : Filename(Filename), VFSPath(VFSPath) {} + CrashReportInfo(StringRef Filename, StringRef VFSPath, + StringRef IndexStorePath) + : Filename(Filename), VFSPath(VFSPath), IndexStorePath(IndexStorePath) {} }; // Encodes the kind of response file supported for a command invocation. @@ -143,6 +145,7 @@ class Command { /// See Command::setEnvironment std::vector Environment; + std::vector EnvironmentDisplay; /// Optional redirection for stdin, stdout, stderr. std::vector> RedirectFiles; @@ -217,6 +220,9 @@ class Command { Arguments = std::move(List); } + /// Sets the environment to display in `-###`. + virtual void setEnvironmentDisplay(llvm::ArrayRef Display); + void replaceExecutable(const char *Exe) { Executable = Exe; } const char *getExecutable() const { return Executable; } @@ -256,6 +262,24 @@ class CC1Command : public Command { void setEnvironment(llvm::ArrayRef NewEnvironment) override; }; +/// Automatically cache -cc1 commands when possible. +class CachingCC1Command : public CC1Command { +public: + CachingCC1Command(const Action &Source, const Tool &Creator, + ResponseFileSupport ResponseSupport, const char *Executable, + const llvm::opt::ArgStringList &Arguments, + ArrayRef Inputs, + ArrayRef Outputs = std::nullopt); + + void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote, + CrashReportInfo *CrashInfo = nullptr) const override; + + int Execute(ArrayRef> Redirects, std::string *ErrMsg, + bool *ExecutionFailed) const override; + + void setEnvironment(llvm::ArrayRef NewEnvironment) override; +}; + /// JobList - A sequence of jobs to perform. class JobList { public: diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index c0f469e04375c..37affa3b4ffb1 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -330,6 +330,7 @@ class PreprocessorOutputOpts : KeyPathAndMacro<"PreprocessorOutputOpts.", base, "PREPROCESSOR_OUTPUT_"> {} class DependencyOutputOpts : KeyPathAndMacro<"DependencyOutputOpts.", base, "DEPENDENCY_OUTPUT_"> {} +class CASOpts : KeyPathAndMacro<"CASOpts.", base, "CAS_"> {} class CodeGenOpts : KeyPathAndMacro<"CodeGenOpts.", base, "CODEGEN_"> {} class HeaderSearchOpts @@ -634,6 +635,7 @@ defvar hip = LangOpts<"HIP">; defvar gnu_mode = LangOpts<"GNUMode">; defvar asm_preprocessor = LangOpts<"AsmPreprocessor">; defvar hlsl = LangOpts<"HLSL">; +defvar objc = LangOpts<"ObjC">; defvar std = !strconcat("LangStandard::getLangStandardForKind(", lang_std.KeyPath, ")"); @@ -710,6 +712,31 @@ def no_round_trip_args : Flag<["-"], "no-round-trip-args">, Visibility<[CC1Option]>, HelpText<"Disable command line arguments round-trip.">; +def index_store_path : Separate<["-"], "index-store-path">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Enable indexing with the specified data store path">, + MarshallingInfoString>; +def index_ignore_system_symbols : Flag<["-"], "index-ignore-system-symbols">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Ignore symbols from system headers">, + MarshallingInfoFlag>; +def index_record_codegen_name : Flag<["-"], "index-record-codegen-name">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Record the codegen name for symbols">, + MarshallingInfoFlag>; +def index_unit_output_path : Separate<["-"], "index-unit-output-path">, MetaVarName<"">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Use as the output path for this compilation unit in the index unit file">, + MarshallingInfoString>; +def index_ignore_macros : Flag<["-"], "index-ignore-macros">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Ignore macros during indexing">, + MarshallingInfoFlag>; +def index_ignore_pcms : Flag<["-"], "index-ignore-pcms">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Ignore symbols from imported pcm modules">, + MarshallingInfoFlag>; + // Make sure all other -ccc- options are rejected. def ccc_ : Joined<["-"], "ccc-">, Group, Flags<[Unsupported]>; @@ -1005,7 +1032,7 @@ def all__load : Flag<["-"], "all_load">; def allowable__client : Separate<["-"], "allowable_client">; def ansi : Flag<["-", "--"], "ansi">, Group; def arch__errors__fatal : Flag<["-"], "arch_errors_fatal">; -def arch : Separate<["-"], "arch">, Flags<[NoXarchOption,TargetSpecific]>; +def arch : Separate<["-"], "arch">, Flags<[NoXarchOption]>; def arch__only : Separate<["-"], "arch_only">; def autocomplete : Joined<["--"], "autocomplete=">; def bind__at__load : Flag<["-"], "bind_at_load">; @@ -1881,6 +1908,9 @@ def fapinotes_swift_version : Joined<["-"], "fapinotes-swift-version=">, Group, Visibility<[ClangOption, CC1Option]>, MetaVarName<"">, HelpText<"Specify the Swift version to use when filtering API notes">; +def fapinotes_cache_path : Joined<["-"], "fapinotes-cache-path=">, + Group, Flags<[NoXarchOption]>, MetaVarName<"">, + HelpText<"Does nothing; API notes are no longer cached separately from modules">; defm bounds_safety : BoolFOption< "experimental-bounds-safety", @@ -1890,6 +1920,77 @@ defm bounds_safety : BoolFOption< BothFlags<[], [CC1Option], " experimental bounds safety extension for C">>; +// TO_UPSTREAM(BoundsSafety) ON +// Apple internal clients currently use `-fbounds-safety` so provide +// aliases to upstream's `-fexperimental-bounds-safety` flag. When +// upstream eventually drops the `experimental-` prefix these aliases +// can be removed. +def : Flag<["-"], "fbounds-safety">, Visibility<[ClangOption, CC1Option]>, + Alias, HelpText<"Enable bounds safety extension for C">; +def : Flag<["-"], "fno-bounds-safety">, Visibility<[ClangOption, CC1Option]>, + Alias, HelpText<"Disable bounds safety extension for C">; + +// Aliases for -fbounds-safety to remove once these are stripped from adopters +def : Flag<["-"], "fbounds-attributes">, Visibility<[ClangOption, CC1Option]>, + Alias; +def : Flag<["-"], "fno-bounds-attributes">, Visibility<[ClangOption, CC1Option]>, + Alias; +defm experimental_bounds_safety_attributes : BoolFOption< + "experimental-bounds-safety-attributes", + LangOpts<"BoundsSafetyAttributes">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption, CC1Option], " experimental attributes">>; +defm bounds_attributes_cxx_experimental : BoolFOption< + "bounds-attributes-cxx-experimental", + LangOpts<"BoundsAttributesCXXExperimental">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption, CC1Option], + " experimental bounds attributes for C++">>; +defm bounds_attributes_objc_experimental : BoolFOption< + "bounds-attributes-objc-experimental", + LangOpts<"BoundsAttributesObjCExperimental">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption, CC1Option], + " experimental bounds attributes for ObjC">>; +defm bounds_safety_relaxed_system_headers : BoolFOption< + "bounds-safety-relaxed-system-headers", + LangOpts<"BoundsSafetyRelaxedSystemHeaders">, DefaultTrue, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption, CC1Option], + " relaxed bounds safety rules for code in system headers">>; +defm bounds_safety_adoption_mode : BoolFOption<"bounds-safety-adoption-mode", + DiagnosticOpts<"BoundsSafetyAdoptionMode">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption, CC1Option], + " generating improved -fbounds-safety diagnostics to aid engineers " + "adopting -fbounds-safety. This is off by default to avoid impacting " + "compile times">>; + +def fbounds_safety_bringup_missing_checks_EQ : CommaJoined<["-"], "fbounds-safety-bringup-missing-checks=">, Group, + MetaVarName<"">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Enable a set of new -fbounds-safety run-time checks, (option: access_size, indirect_count_update, return_size, ended_by_lower_bound, compound_literal_init, libc_attributes, array_subscript_agg, all, batch_0)">; + +def fno_bounds_safety_bringup_missing_checks_EQ : CommaJoined<["-"], "fno-bounds-safety-bringup-missing-checks=">, Group, + MetaVarName<"">, + Visibility<[ClangOption, CC1Option]>, + HelpText<"Disable a set of new -fbounds-safety run-time checks, (option: access_size, indirect_count_update, return_size, ended_by_lower_bound, compound_literal_init, libc_attributes, array_subscript_agg, all, batch_0)">; + +def fbounds_safety_bringup_missing_checks : Flag<["-"], "fbounds-safety-bringup-missing-checks">, Group, + Visibility<[ClangOption]>, + Alias, AliasArgs<["all"]>, + HelpText<"Enable new -fbounds-safety run-time checks">; + +def fno_bounds_safety_bringup_missing_checks : Flag<["-"], "fno-bounds-safety-bringup-missing-checks">, Group, + Visibility<[ClangOption]>, + Alias, AliasArgs<["all"]>, + HelpText<"Disable new -fbounds-safety run-time checks">; + defm addrsig : BoolFOption<"addrsig", CodeGenOpts<"Addrsig">, DefaultFalse, PosFlag, @@ -2032,9 +2133,10 @@ defm async_exceptions: BoolFOption<"async-exceptions", "Enable EH Asynchronous exceptions">, NegFlag>; defm cxx_modules : BoolFOption<"cxx-modules", - LangOpts<"CPlusPlusModules">, Default, + // FIXME: improve tblgen to not need strconcat here. + LangOpts<"CPlusPlusModules">, Default, NegFlag, - PosFlag, + PosFlag, BothFlags<[], [], " modules for C++">>, ShouldParseIf; def fdebug_pass_arguments : Flag<["-"], "fdebug-pass-arguments">, Group; @@ -3175,6 +3277,9 @@ defm prebuilt_implicit_modules : BoolFOption<"prebuilt-implicit-modules", HeaderSearchOpts<"EnablePrebuiltImplicitModules">, DefaultFalse, PosFlag, NegFlag, BothFlags<[], [ClangOption, CC1Option]>>; +def fmodule_related_to_pch : Flag<["-"], "fmodule-related-to-pch">, + Group, Visibility<[ClangOption, CC1Option]>, + HelpText<"Mark module as related to a PCH">; def fmodule_output_EQ : Joined<["-"], "fmodule-output=">, Flags<[NoXarchOption]>, Visibility<[ClangOption, CLOption, CC1Option]>, @@ -3299,10 +3404,10 @@ defm pch_codegen: OptInCC1FFlag<"pch-codegen", "Generate ", "Do not generate ", defm pch_debuginfo: OptInCC1FFlag<"pch-debuginfo", "Generate ", "Do not generate ", "debug info for types in an object file built from this PCH and do not generate them elsewhere">; -def fimplicit_module_maps : Flag <["-"], "fimplicit-module-maps">, Group, - Visibility<[ClangOption, CC1Option, CLOption]>, - HelpText<"Implicitly search the file system for module map files.">, - MarshallingInfoFlag>; +defm implicit_module_maps : BoolFOption<"implicit-module-maps", + HeaderSearchOpts<"ImplicitModuleMaps">, DefaultFalse, + PosFlag, + NegFlag, BothFlags<[], [ClangOption, CC1Option, CLOption]>>; defm modulemap_allow_subdirectory_search : BoolFOption <"modulemap-allow-subdirectory-search", HeaderSearchOpts<"AllowModuleMapSubdirectorySearch">, DefaultTrue, PosFlag, @@ -3402,6 +3507,10 @@ def fno_builtin : Flag<["-"], "fno-builtin">, Group, def fno_builtin_ : Joined<["-"], "fno-builtin-">, Group, Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>, HelpText<"Disable implicit builtin knowledge of a specific function">; +def ffeature_availability_EQ : Joined<["-"], "ffeature-availability=">, Group, + Visibility<[CC1Option]>, + HelpText<"feature availability">, + MarshallingInfoStringVector>; def fno_common : Flag<["-"], "fno-common">, Group, Visibility<[ClangOption, CC1Option]>, HelpText<"Compile common globals like normal definitions">; @@ -3432,7 +3541,6 @@ def fveclib : Joined<["-"], "fveclib=">, Group, MarshallingInfoEnum, "NoLibrary">; def fno_lax_vector_conversions : Flag<["-"], "fno-lax-vector-conversions">, Group, Alias, AliasArgs<["none"]>; -def fno_implicit_module_maps : Flag <["-"], "fno-implicit-module-maps">, Group; def fno_module_maps : Flag <["-"], "fno-module-maps">, Alias; def fno_modules_strict_decluse : Flag <["-"], "fno-strict-modules-decluse">, Group; def fmodule_file_deps : Flag <["-"], "fmodule-file-deps">, Group; @@ -4133,6 +4241,24 @@ def floop_interchange : Flag<["-"], "floop-interchange">, Group, HelpText<"Enable the loop interchange pass">, Visibility<[ClangOption, CC1Option]>; def fno_loop_interchange: Flag<["-"], "fno-loop-interchange">, Group, HelpText<"Disable the loop interchange pass">, Visibility<[ClangOption, CC1Option]>; +/* TO_UPSTREAM(BoundsSafety) ON */ +defm trap_function_returns : BoolFOption<"trap-function-returns", + CodeGenOpts<"TrapFuncReturns">, DefaultFalse, + PosFlag, + NegFlag>; +defm unique_traps : BoolFOption<"unique-traps", + CodeGenOpts<"UniqueTrapBlocks">, DefaultFalse, + PosFlag, + NegFlag>; +/* TO_UPSTREAM(BoundsSafety) OFF */ def funroll_loops : Flag<["-"], "funroll-loops">, Group, HelpText<"Turn on loop unroller">, Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>; def fno_unroll_loops : Flag<["-"], "fno-unroll-loops">, Group, @@ -4375,6 +4501,28 @@ defm strict_return : BoolFOption<"strict-return", " of a non-void function as unreachable">, PosFlag>; +let Group = f_Group in { + let Visibility = [ClangOption, CC1Option] in { + def fptrauth_soft : Flag<["-"], "fptrauth-soft">, + HelpText<"Enable software lowering of pointer authentication">; + } + def fno_ptrauth_soft : Flag<["-"], "fno-ptrauth-soft">; +} + +let Group = f_Group in { + def fptrauth_abi_version_EQ : Joined<["-"], "fptrauth-abi-version=">, + Visibility<[ClangOption, CC1Option, CC1AsOption]>, + HelpText<"Pointer Authentication ABI version">; + def fno_ptrauth_abi_version : Flag<["-"], "fno-ptrauth-abi-version">, + HelpText<"Disable Pointer Authentication ABI versioning">; + + def fptrauth_kernel_abi_version : Flag<["-"], "fptrauth-kernel-abi-version">, + Visibility<[ClangOption, CC1Option, CC1AsOption]>, + HelpText<"Enable Pointer Authentication kernel ABI version">; + def fno_ptrauth_kernel_abi_version : Flag<["-"], "fno-ptrauth-kernel-abi-version">, + HelpText<"Disable Pointer Authentication kernel ABI versioning">; +} + let Flags = [TargetSpecific] in { defm ptrauth_intrinsics : OptInCC1FFlag<"ptrauth-intrinsics", "Enable pointer authentication intrinsics">; defm ptrauth_calls : OptInCC1FFlag<"ptrauth-calls", "Enable signing and authentication of all indirect calls">; @@ -4541,6 +4689,11 @@ def gcoff : Joined<["-"], "gcoff">, Group, Flags<[Unsupported]>; def gxcoff : Joined<["-"], "gxcoff">, Group, Flags<[Unsupported]>; def gvms : Joined<["-"], "gvms">, Group, Flags<[Unsupported]>; def gtoggle : Flag<["-"], "gtoggle">, Group, Flags<[Unsupported]>; +defm reproducible : BoolOption<"g", "reproducible", + CodeGenOpts<"ReproducibleDebugInfo">, DefaultFalse, + PosFlag, + NegFlag>, Group; def grecord_command_line : Flag<["-"], "grecord-command-line">, Group; def gno_record_command_line : Flag<["-"], "gno-record-command-line">, @@ -7532,6 +7685,10 @@ def fdump_vtable_layouts : Flag<["-"], "fdump-vtable-layouts">, def fmerge_functions : Flag<["-"], "fmerge-functions">, HelpText<"Permit merging of identical functions when optimizing.">, MarshallingInfoFlag>; +def fsplit_cold_code : Flag<["-"], "fsplit-cold-code">, + HelpText<"Permit splitting of cold code when optimizing (off by default).">; +def fno_split_cold_code : Flag<["-"], "fno-split-cold-code">, + HelpText<"Disable splitting of cold code when optimizing.">; def : Joined<["-"], "coverage-data-file=">, MarshallingInfoString>; def : Joined<["-"], "coverage-notes-file=">, @@ -7781,6 +7938,9 @@ let Visibility = [CC1Option] in { def sys_header_deps : Flag<["-"], "sys-header-deps">, HelpText<"Include system headers in dependency output">, MarshallingInfoFlag>; +def skip_unused_modulemap_file_deps : Flag<["-"], "skip-unused-modulemap-deps">, + HelpText<"Include module map files only for imported modules in dependency output">, + MarshallingInfoFlag>; def module_file_deps : Flag<["-"], "module-file-deps">, HelpText<"Include module files in dependency output">, MarshallingInfoFlag>; @@ -7969,12 +8129,13 @@ defm fimplicit_modules_use_lock : BoolOption<"f", "implicit-modules-use-lock", "duplicating work in competing clang invocations.">>; // FIXME: We only need this in C++ modules if we might textually // enter a different module (eg, when building a header unit). -def fmodules_local_submodule_visibility : - Flag<["-"], "fmodules-local-submodule-visibility">, - HelpText<"Enforce name visibility rules across submodules of the same " - "top-level module.">, - MarshallingInfoFlag>, - ImpliedByAnyOf<[fcxx_modules.KeyPath]>; +defm modules_local_submodule_visibility : + BoolFOption<"modules-local-submodule-visibility", + LangOpts<"ModulesLocalVisibility">, + Default, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption], " name visibility rules across submodules of the same top-level module">>; def fmodules_codegen : Flag<["-"], "fmodules-codegen">, HelpText<"Generate code for uses of this module that assumes an explicit " @@ -7989,10 +8150,19 @@ def fmodule_format_EQ : Joined<["-"], "fmodule-format=">, HelpText<"Select the container format for clang modules and PCH. " "Supported options are 'raw' and 'obj'.">, MarshallingInfoString, [{"raw"}]>; +def fmodules_hash_error_diagnostics : Flag<["-"], "fmodules-hash-error-diagnostics">, + HelpText<"Use a separate module cache for modules compiled with conflicting -Werror options">, + MarshallingInfoFlag>; def ftest_module_file_extension_EQ : Joined<["-"], "ftest-module-file-extension=">, HelpText<"introduce a module file extension for testing purposes. " "The argument is parsed as blockname:major:minor:hashed:user info">; +def fmodule_file_cache_key : MultiArg<["-"], "fmodule-file-cache-key", 2>, + MetaVarName<" ">, + HelpText<"Make the module with the given compile job cache key available as " + "if it were at . This option may be combined with " + "-fmodule-file= to import the module. The module must have " + "previously been built with -fcache-compile-job.">; defm recovery_ast : BoolOption<"f", "recovery-ast", LangOpts<"RecoveryAST">, DefaultTrue, @@ -8496,6 +8666,239 @@ def fsycl_is_host : Flag<["-"], "fsycl-is-host">, Visibility<[CC1Option]>, MarshallingInfoFlag>; +//===----------------------------------------------------------------------===// +// CAS Options +//===----------------------------------------------------------------------===// + +// Example command-lines we expect: +// +// For building against some pre-ingested, CAS-based filesystem in the builtin +// on-disk CAS: +// ``` +// % clang -fcas-path=auto -fcas-fs=llvmcas://1af12fa2afa1af2a1fa +// % clang -fcas-path=path/to/cas -fcas-fs=llvmcas://1af12fa2afa1af2a1fa +// ``` +// +// For building against some pre-ingested, CAS-based filesystem using a plugin: +// ``` +// % clang -fcas-plugin=path/to/some/cas-plugind \ +// -fcas-plugin-args=-X,-Y,-Z \ +// -fcas-fs=llvmcas://1af12fa2afa1af2a1fa +// ``` +// +// For automatically ingesting from the live filesystem into a CAS, +// canonicalizing the paths, and running the `-cc1` against the CAS tree: +// ``` +// % clang -fdepscan \ +// -fdepscan-prefix-map=/my/sources=/^src \ +// -fdepscan-prefix-map=/my/build/dir=/^build +// ``` +// +// For pruning and canonicalizing even when starting from a pre-ingested, +// CAS-based filesystem: +// ``` +// % clang -fcas-path=path/to/the/cas \ +// -fcas-fs=llvmcas://1af12fa2afa1af2a1fa \ +// -fdepscan \ +// -fdepscan-prefix-map=/my/sources=/^src \ +// -fdepscan-prefix-map=/my/build/dir=/^build +// ``` + +// Driver CAS options. +def fdepscan_EQ : Joined<["-"], "fdepscan=">, + Group, + HelpText<"Scan for dependencies ahead of compiling, generating a" + " pruned CAS tree to send to -fcas-fs. Values are" + " 'auto'," + " 'daemon' (see -fdepscan-share and -fdepscan-share-parent)," + " 'inline', or" + " 'off' (default).">; +def fdepscan : Flag<["-"], "fdepscan">, + Group, HelpText<"Turn on -fdepscan=auto.">, + Alias, AliasArgs<["auto"]>; +def fno_depscan : Flag<["-"], "fno-depscan">, + Group, + Alias, AliasArgs<["off"]>; +def fdepscan_share_EQ : Joined<["-"], "fdepscan-share=">, + Group, + HelpText<"If the argument is the name of a command in the process tree," + " share state based on its PID." + " E.g., -fdepscan -fdepscan-share=ninja will search for 'ninja'" + " in the process tree and share state based on its PID if found." + " See also -fdepscan-share-stop.">; +def fdepscan_share_parent_EQ : Joined<["-"], "fdepscan-share-parent=">, + Group, + HelpText<"Share state based on the PID of the parent command if the name" + " matches." + " See also -fdepscan-share-stop.">; +def fdepscan_share_parent : Flag<["-"], "fdepscan-share-parent">, + Group, + HelpText<"Share state based on the PID of the parent command." + " See also -fdepscan-share-stop.">; +def fno_depscan_share : Flag<["-"], "fno-depscan-share">, + Group, + HelpText<"Turn off -fdepscan-share and -fdepscan-share-parent.">; +def fdepscan_share_stop_EQ : Joined<["-"], "fdepscan-share-stop=">, + Group, + HelpText<"Stop looking for the command named by -fdepscan-share if a" + " process with the name of the provided argument is found first." + " Also blocks -fdepscan-share=parent if the parent has this name." + " E.g., -fdepscan -fdepscan-share=ninja" + " -fdepscan-share-stop=cmake looks for 'ninja' and 'cmake' in the" + " process tree; if 'ninja' is found first, state is shared based" + " on ninja's PID; if 'cmake' is found first, state is not" + " shared.">; +def fdepscan_share_identifier : Separate<["-"], "fdepscan-share-identifier">, + Group, + HelpText<"Share depscan daemon for Clang invocations using the same string " + "identifier.">; +def fdepscan_share_identifier_EQ : Joined<["-"], "fdepscan-share-identifier=">, + Alias; +def fdepscan_daemon_EQ : Joined<["-"], "fdepscan-daemon=">, Group, + HelpText<"Specify the path to the daemon to be used. Clang will use the" + " daemon specified, rather than try to spawn its own based on" + " parent processes.">; + +def fdepscan_include_tree : Flag<["-"], "fdepscan-include-tree">, + Group, HelpText<"Set dep-scanner to produce the include tree">; + +// CAS prefix map options. +// +// FIXME: Add DepscanOption flag. +def fdepscan_prefix_map_EQ : Joined<["-"], "fdepscan-prefix-map=">, + Group, Visibility<[ClangOption, CC1Option]>, + MetaVarName<"=">, + HelpText<"With -fdepscan, seamlessly filter the CAS filesystem to" + " apply the given prefix, updating the command-line to match.">, + MarshallingInfoStringVector>; +def fdepscan_prefix_map_sdk_EQ : + Joined<["-"], "fdepscan-prefix-map-sdk=">, + Group, MetaVarName<"">, + HelpText<"With -fdepscan, auto-detect the SDK path on-disk and remap" + " it to the given path (see -fdepscan-prefix-map=).">; +def fdepscan_prefix_map_toolchain_EQ : + Joined<["-"], "fdepscan-prefix-map-toolchain=">, + Group, MetaVarName<"">, + HelpText<"With -fdepscan, auto-detect the toolchain path on-disk and" + " remap it to the given path (see -fdepscan-prefix-map=).">; + +// -cc1depscan options. +// +// FIXME: Add to their own group; add NoDriverOption and DepscanOption flags. +def cc1_args : Option<["-"], "cc1-args", KIND_REMAINING_ARGS>, + HelpText<"pass cc1 options to depscan afterwards">; +def dump_depscan_tree_EQ : Option<["-"], "dump-depscan-tree=", KIND_JOINED>, + HelpText<"emit the CAS identifier for the tree instead of the full -cc1">; + +// -cc1 options. These should be available in the driver too, but the driver +// doesn't currently support most of them. For example, the driver should +// read only from -fcas-fs if specified, in which case the -fdepscan +// should scan to prune the CAS filesystem. Also, -fdepscan shouldn't +// override -fcas / etc., it should just add those if not already specified. +let Visibility = [CC1Option] in { + +// FIXME: Add to driver once it's supported by (not clobbered by) -fdepscan. +// +// The driver should also have: +// - '-fcas-path=|auto' ('='-joined alias). +// - '-fno-cas-path' (same as '-fcas-path ""', the default). +def fcas_path : Separate<["-"], "fcas-path">, + Group, MetaVarName<"|auto">, + HelpText<"Path to a persistent on-disk backing store for the builtin CAS." + " '-fcas-path=auto' chooses a path in the user's system cache.">, + MarshallingInfoString>; + +def fcas_plugin_path : Separate<["-"], "fcas-plugin-path">, + Group, MetaVarName<"">, + HelpText<"Path to a shared library implementing the LLVM CAS plugin API.">, + MarshallingInfoString>; + +def fcas_plugin_option : Separate<["-"], "fcas-plugin-option">, + Group, MetaVarName<"=">, + HelpText<"Option to pass to the CAS plugin.">, + MarshallingInfoStringPairVector>; + +// FIXME: Add to driver once it's supported by -fdepscan. +def fcas_fs : Separate<["-"], "fcas-fs">, + Group, MetaVarName<"">, + HelpText<"Configure the filesystem to read from the provided CAS tree." + " See also -fcas-builtin-path for loading a tree.">, + MarshallingInfoString>; + +// FIXME: Remove / merge with -fworking-directory? +def fcas_fs_working_directory : Separate<["-"], "fcas-fs-working-directory">, + Group, MetaVarName<"">, + HelpText<"Working directory for -fcas-fs (if not the root).">, + ShouldParseIf, + MarshallingInfoString>; + +def fcas_include_tree : Separate<["-"], "fcas-include-tree">, + Group, MetaVarName<"">, + HelpText<"Configure the frontend to use a CAS include tree.">, + MarshallingInfoString>; + +def fcas_input_file_cache_key : Separate<["-"], "fcas-input-file-cache-key">, + Group, MetaVarName<"">, + MarshallingInfoString>; + +def fcas_input_file_id : Separate<["-"], "fcas-input-file-casid">, + Group, MetaVarName<"">, + MarshallingInfoString>; + +defm module_load_ignore_cas : BoolFOption<"module-load-ignore-cas", + FrontendOpts<"ModuleLoadIgnoreCAS">, DefaultFalse, + PosFlag, + NegFlag>; + +// FIXME: Add to driver under -fexperimental-cache=compile-job. +defm cache_compile_job : BoolFOption<"cache-compile-job", + FrontendOpts<"CacheCompileJob">, DefaultFalse, + PosFlag, + NegFlag>; + +defm cache_disable_replay : BoolFOption<"cache-disable-replay", + FrontendOpts<"DisableCachedCompileJobReplay">, DefaultFalse, + PosFlag, + NegFlag>; + +def fcompilation_caching_service_path : Separate<["-"], "fcompilation-caching-service-path">, + Group, MetaVarName<"">, + HelpText<"Specify the socket path for connecting to a remote caching service">, + MarshallingInfoString>; + +defm casid_output : BoolFOption<"casid-output", + FrontendOpts<"WriteOutputAsCASID">, DefaultFalse, + PosFlag, + NegFlag>; + +defm include_tree_preserve_pch_path : BoolFOption<"include-tree-preserve-pch-path", + FrontendOpts<"IncludeTreePreservePCHPath">, DefaultFalse, + PosFlag, + NegFlag>; + +/// BEGIN MCCAS +defm cas_backend : BoolFOption<"cas-backend", + CodeGenOpts<"UseCASBackend">, DefaultFalse, + PosFlag, + NegFlag>; + +defm cas_emit_casid_file : BoolFOption<"cas-emit-casid-file", + CodeGenOpts<"EmitCASIDFile">, DefaultFalse, + PosFlag, + NegFlag>; + +def fcas_backend_mode : Joined<["-"], "fcas-backend-mode=">, + Group, MetaVarName<"">, + HelpText<"CASBackendMode for output kind">, + Values<"native,casid,verify">, NormalizedValuesScope<"llvm::CASBackendMode">, + NormalizedValues<["Native", "CASID", "Verify"]>, + MarshallingInfoEnum, "Native">; +/// END MCCAS + +} // let Visibility = [CC1Option] + def sycl_std_EQ : Joined<["-"], "sycl-std=">, Group, Flags<[NoArgumentUnused]>, Visibility<[ClangOption, CC1Option, CLOption]>, @@ -9201,6 +9604,12 @@ def dxc_hlsl_version : Option<["/", "-"], "HV", KIND_JOINED_OR_SEPARATE>, Visibility<[DXCOption]>, HelpText<"HLSL Version">, Values<"2016, 2017, 2018, 2021, 202x, 202y">; + +def fsuppress_conflicting_types: Flag<["-"], "fsuppress-conflicting-types">, Group, + MarshallingInfoFlag>, + HelpText<"Ignore errors from conflicting types in function declarations">, + Visibility<[ClangOption, CC1Option]>; + def dxc_validator_path_EQ : Joined<["--"], "dxv-path=">, Group, HelpText<"DXIL validator installation path">; def dxc_disable_validation : DXCFlag<"Vd">, diff --git a/clang/include/clang/Driver/Phases.h b/clang/include/clang/Driver/Phases.h index 9003c58573513..e30d33926f84d 100644 --- a/clang/include/clang/Driver/Phases.h +++ b/clang/include/clang/Driver/Phases.h @@ -15,6 +15,7 @@ namespace phases { /// ID - Ordered values for successive stages in the /// compilation process which interact with user options. enum ID { + Depscan, Preprocess, Precompile, Compile, diff --git a/clang/include/clang/Driver/Types.def b/clang/include/clang/Driver/Types.def index 76944ec656917..6ca30d7a63810 100644 --- a/clang/include/clang/Driver/Types.def +++ b/clang/include/clang/Driver/Types.def @@ -121,3 +121,6 @@ TYPE("hip-fatbin", HIP_FATBIN, INVALID, "hipfb", phases TYPE("api-information", API_INFO, INVALID, "json", phases::Precompile) TYPE("dx-container", DX_CONTAINER, INVALID, "dxo", phases::Compile, phases::Backend) TYPE("none", Nothing, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link) + +// Generic command line type, used to pass command as response file to the next action. +TYPE("response-file", ResponseFile, INVALID, "rsp", phases::Depscan) diff --git a/clang/include/clang/Edit/RefactoringFixits.h b/clang/include/clang/Edit/RefactoringFixits.h new file mode 100644 index 0000000000000..385f078080747 --- /dev/null +++ b/clang/include/clang/Edit/RefactoringFixits.h @@ -0,0 +1,65 @@ +//===--- RefactoringFixits.h - Fixit producers for refactorings -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_EDIT_REFACTORING_FIXITS_H +#define LLVM_CLANG_EDIT_REFACTORING_FIXITS_H + +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/STLExtras.h" + +namespace clang { + +class ASTContext; +class SwitchStmt; +class EnumDecl; +class ObjCContainerDecl; + +namespace edit { + +/** + * Generates the fix-its that perform the "add missing switch cases" refactoring + * operation. + */ +void fillInMissingSwitchEnumCases( + ASTContext &Context, const SwitchStmt *Switch, const EnumDecl *Enum, + const DeclContext *SwitchContext, + llvm::function_ref Consumer); + +/// Responsible for the fix-its that perform the +/// "add missing protocol requirements" refactoring operation. +namespace fillInMissingProtocolStubs { + +class FillInMissingProtocolStubsImpl; +class FillInMissingProtocolStubs { + std::unique_ptr Impl; + +public: + FillInMissingProtocolStubs(); + ~FillInMissingProtocolStubs(); + FillInMissingProtocolStubs(FillInMissingProtocolStubs &&); + FillInMissingProtocolStubs &operator=(FillInMissingProtocolStubs &&); + + /// Initiate the FillInMissingProtocolStubs edit. + /// + /// \returns true on Error. + bool initiate(ASTContext &Context, const ObjCContainerDecl *Container); + bool hasMissingRequiredMethodStubs(); + void perform(ASTContext &Context, + llvm::function_ref Consumer); +}; + +void addMissingProtocolStubs( + ASTContext &Context, const ObjCContainerDecl *Container, + llvm::function_ref Consumer); + +} // end namespace fillInMissingProtocolStubs + +} // end namespace edit +} // end namespace clang + +#endif // LLVM_CLANG_EDIT_REFACTORING_FIXITS_H diff --git a/clang/include/clang/Frontend/CASDependencyCollector.h b/clang/include/clang/Frontend/CASDependencyCollector.h new file mode 100644 index 0000000000000..1fa101bec866c --- /dev/null +++ b/clang/include/clang/Frontend/CASDependencyCollector.h @@ -0,0 +1,54 @@ +//===- CASDependencyCollector.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_CASDEPENDENCYCOLLECTOR_H +#define LLVM_CLANG_FRONTEND_CASDEPENDENCYCOLLECTOR_H + +#include "clang/Frontend/Utils.h" +#include "llvm/CAS/CASReference.h" + +namespace clang { + +/// Collects dependencies when attached to a Preprocessor (for includes) and +/// ASTReader (for module imports), and writes it to the CAS in a manner +/// suitable to be replayed into a DependencyFileGenerator. +class CASDependencyCollector : public DependencyFileGenerator { +public: + /// Create a \CASDependencyCollector for the given output options. + /// + /// \param Opts Output options. Only options that affect the list of + /// dependency files are significant. + /// \param CAS The CAS to write the dependency list to. + /// \param Callback Callback that receives the resulting dependencies on + /// completion, or \c None if an error occurred. + CASDependencyCollector( + DependencyOutputOptions Opts, cas::ObjectStore &CAS, + std::function)> Callback); + + /// Replay the given result, which should have been created by a + /// \c CASDependencyCollector instance. + /// + /// \param Opts Output options. Only options that affect the output format of + /// a dependency file are signficant. + /// \param CAS The CAS to read the result from. + /// \param Deps The dependencies. + /// \param OS The output stream to write the dependency file to. + static llvm::Error replay(const DependencyOutputOptions &Opts, + cas::ObjectStore &CAS, cas::ObjectProxy Deps, + llvm::raw_ostream &OS); + +private: + void finishedMainFile(DiagnosticsEngine &Diags) override; + + cas::ObjectStore &CAS; + std::function)> Callback; +}; + +} // namespace clang + +#endif // LLVM_CLANG_FRONTEND_CASDEPENDENCYCOLLECTOR_H diff --git a/clang/include/clang/Frontend/CompileJobCache.h b/clang/include/clang/Frontend/CompileJobCache.h new file mode 100644 index 0000000000000..f876f08c4362a --- /dev/null +++ b/clang/include/clang/Frontend/CompileJobCache.h @@ -0,0 +1,115 @@ +//===- CompileJobCache.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_COMPILEJOBCACHE_H +#define LLVM_CLANG_FRONTEND_COMPILEJOBCACHE_H + +#include "clang/Frontend/CompileJobCacheResult.h" + +namespace clang { + +class CompilerInstance; +class CompilerInvocation; +class DiagnosticsEngine; + +// Manage caching and replay of compile jobs. +// +// The high-level model is: +// +// 1. Extract options from the CompilerInvocation: +// - that can be simulated and +// - that don't affect the compile job's result. +// 2. Canonicalize the options extracted in (1). +// 3. Compute the result of the compile job using the canonicalized +// CompilerInvocation, with hooks installed to redirect outputs and +// enable live-streaming of a running compile job to stdout or stderr. +// - Compute a cache key. +// - Check the cache, and run the compile job if there's a cache miss. +// - Store the result of the compile job in the cache. +// 4. Replay the compile job, using the options extracted in (1). +// +// An example (albeit not yet implemented) is handling options controlling +// output of diagnostics. The CompilerInvocation can be canonicalized to +// serialize the diagnostics to a virtual path (.diag or something). +// +// - On a cache miss, the compile job runs, and the diagnostics are +// serialized and stored in the cache per the canonicalized options +// from (2). +// - Either way, the diagnostics are replayed according to the options +// extracted from (1) during (4). +// +// The above will produce the correct output for diagnostics, but the experience +// will be degraded in the common command-line case (emitting to stderr) +// because the diagnostics will not be streamed live. This can be improved: +// +// - Change (3) to accept a hook: a DiagnosticsConsumer that diagnostics +// are mirrored to (in addition to canonicalized options from (2)). +// - If diagnostics would be live-streamed, send in a diagnostics consumer +// that matches (1). Otherwise, send in an IgnoringDiagnosticsConsumer. +// - In step (4), only skip replaying the diagnostics if they were already +// handled. +class CompileJobCache { +public: + CompileJobCache(); + ~CompileJobCache(); + + using OutputKind = clang::cas::CompileJobCacheResult::OutputKind; + + StringRef getPathForOutputKind(OutputKind Kind); + + /// Canonicalize \p Clang. + /// + /// \returns status if should exit immediately, otherwise None. + /// + /// TODO: Refactor \a cc1_main() so that instead this canonicalizes the + /// CompilerInvocation before Clang gets access to command-line arguments, to + /// control what might leak. + std::optional initialize(CompilerInstance &Clang); + + /// Try looking up a cached result and replaying it. + /// + /// \returns status if should exit immediately, otherwise None. + std::optional tryReplayCachedResult(CompilerInstance &Clang); + + /// Finish writing outputs from a computed result, after a cache miss. + /// + /// \returns true if finished successfully. + bool finishComputedResult(CompilerInstance &Clang, bool Success); + + static llvm::Expected> + replayCachedResult(std::shared_ptr Invok, + StringRef WorkingDir, const llvm::cas::CASID &CacheKey, + cas::CompileJobCacheResult &CachedResult, + SmallVectorImpl &DiagText, + bool WriteOutputAsCASID = false, + std::optional *MCOutputID = nullptr); + + class CachingOutputs; + +private: + /// \returns true if the output from the compilation is not supported for + /// caching. + Expected + maybeIngestNonVirtualOutputFromFileSystem(CompilerInstance &Clang); + int reportCachingBackendError(DiagnosticsEngine &Diag, llvm::Error &&E); + + bool CacheCompileJob = false; + bool DisableCachedCompileJobReplay = false; + std::optional MCOutputID; + + std::shared_ptr CAS; + std::shared_ptr Cache; + std::optional ResultCacheKey; + std::optional ResultCacheKeyWithInputCacheKeysResolved; + + std::unique_ptr CacheBackend; +}; + +} // namespace clang + +#endif // LLVM_CLANG_FRONTEND_COMPILEJOBCACHE_H diff --git a/clang/include/clang/Frontend/CompileJobCacheKey.h b/clang/include/clang/Frontend/CompileJobCacheKey.h new file mode 100644 index 0000000000000..b3959379e9249 --- /dev/null +++ b/clang/include/clang/Frontend/CompileJobCacheKey.h @@ -0,0 +1,84 @@ +//===- CompileJobCacheKey.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file Functions for working with compile job cache keys. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_COMPILEJOBCACHEKEY_H +#define LLVM_CLANG_FRONTEND_COMPILEJOBCACHEKEY_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/CAS/CASID.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace cas { +class ActionCache; +class ObjectStore; +} // namespace cas +class raw_ostream; +} // namespace llvm + +namespace clang { + +class CompilerInvocation; +class CowCompilerInvocation; +class DiagnosticsEngine; + +enum class CachingInputKind { + IncludeTree, + FileSystemRoot, + CachedCompilation, + Object, +}; + +/// Caching-related options for a given \c CompilerInvocation that are +/// canonicalized away by the cache key. See \c canonicalizeAndCreateCacheKey. +struct CompileJobCachingOptions { + /// See \c FrontendOptions::CompilationCachingServicePath. + std::string CompilationCachingServicePath; + /// See \c FrontendOptions::DisableCachedCompileJobReplay. + bool DisableCachedCompileJobReplay; + /// See \c FrontendOptions::WriteOutputAsCASID. + bool WriteOutputAsCASID; + /// See \c FrontendOptions::PathPrefixMappings. + std::vector PathPrefixMappings; +}; + +/// Create a cache key for the given \c CompilerInvocation as a \c CASID. If \p +/// Invocation will later be used to compile code, use \c +/// canonicalizeAndCreateCacheKey instead. +std::optional +createCompileJobCacheKey(llvm::cas::ObjectStore &CAS, DiagnosticsEngine &Diags, + const CompilerInvocation &Invocation); +std::optional +createCompileJobCacheKey(llvm::cas::ObjectStore &CAS, DiagnosticsEngine &Diags, + const CowCompilerInvocation &Invocation); + +/// Perform any destructive changes needed to canonicalize \p Invocation for +/// caching, extracting the settings that affect compilation even if they do not +/// affect caching. +/// Returns the resulting cache key as the first \c CASID. The second \c CASID +/// is a cache key for the invocation input cache key resolved to object CASIDs. +std::optional> +canonicalizeAndCreateCacheKeys(llvm::cas::ObjectStore &CAS, + llvm::cas::ActionCache &Cache, + DiagnosticsEngine &Diags, + CompilerInvocation &Invocation, + CompileJobCachingOptions &Opts); + +/// Print the structure of the cache key given by \p Key to \p OS. Returns an +/// error if the key object does not exist in \p CAS, or is malformed. +llvm::Error printCompileJobCacheKey(llvm::cas::ObjectStore &CAS, + const llvm::cas::CASID &Key, + llvm::raw_ostream &OS); + +} // namespace clang + +#endif // LLVM_CLANG_FRONTEND_COMPILEJOBCACHEKEY_H diff --git a/clang/include/clang/Frontend/CompileJobCacheResult.h b/clang/include/clang/Frontend/CompileJobCacheResult.h new file mode 100644 index 0000000000000..349ab316e742d --- /dev/null +++ b/clang/include/clang/Frontend/CompileJobCacheResult.h @@ -0,0 +1,126 @@ +//===- CompileJobCacheResult.h ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_COMPILEJOBCACHERESULT_H +#define LLVM_CLANG_FRONTEND_COMPILEJOBCACHERESULT_H + +#include "clang/Basic/LLVM.h" +#include "llvm/CAS/CASNodeSchema.h" +#include "llvm/CAS/ObjectStore.h" + +namespace clang { +namespace cas { +class CompileJobResultSchema; + +class CompileJobCacheResult : public ObjectProxy { +public: + /// Categorization for the output kinds that is used to decouple the + /// compilation cache key from the specific output paths. + enum class OutputKind : char { + MainOutput, ///< Main output file, e.g. object file, pcm file, etc. + SerializedDiagnostics, + Dependencies, + }; + + /// Returns all \c OutputKind values. + static ArrayRef getAllOutputKinds(); + + /// A single output file or stream. + struct Output { + /// The CAS object for this output. + ObjectRef Object; + /// The output kind. + OutputKind Kind; + + bool operator==(const Output &Other) const { + return Object == Other.Object && Kind == Other.Kind; + } + }; + + /// Retrieves each \c Output from this result. + llvm::Error + forEachOutput(llvm::function_ref Callback) const; + + /// Loads all outputs concurrently and passes the resulting \c ObjectProxy + /// objects to \p Callback. If there was an error during loading then the + /// callback will not be invoked. + llvm::Error forEachLoadedOutput( + llvm::function_ref)> + Callback); + + size_t getNumOutputs() const; + + Output getOutput(size_t I) const; + + /// Retrieves a specific output specified by \p Kind, if it exists. + std::optional getOutput(OutputKind Kind) const; + + /// \returns a string for the given \p Kind. + static StringRef getOutputKindName(OutputKind Kind); + + /// Print this result to \p OS. + llvm::Error print(llvm::raw_ostream &OS); + + /// Helper to build a \c CompileJobCacheResult from individual outputs. + class Builder { + public: + Builder(); + ~Builder(); + /// Treat outputs added for \p Path as having the given \p Kind. Otherwise + /// they will have kind \c Unknown. + void addKindMap(OutputKind Kind, StringRef Path); + /// Add an output with an explicit \p Kind. + void addOutput(OutputKind Kind, ObjectRef Object); + /// Add an output for the given \p Path. There must be a a kind map for it. + llvm::Error addOutput(StringRef Path, ObjectRef Object); + /// Build a single \c ObjectRef representing the provided outputs. The + /// result can be used with \c CompileJobResultSchema to retrieve the + /// original outputs. + Expected build(ObjectStore &CAS); + + private: + struct PrivateImpl; + PrivateImpl &Impl; + }; + +private: + ObjectRef getOutputObject(size_t I) const; + ObjectRef getPathsListRef() const; + OutputKind getOutputKind(size_t I) const; + Expected getOutputPath(size_t I) const; + +private: + friend class CompileJobResultSchema; + CompileJobCacheResult(const ObjectProxy &); +}; + +class CompileJobResultSchema + : public llvm::RTTIExtends { +public: + static char ID; + + CompileJobResultSchema(ObjectStore &CAS); + + /// Attempt to load \p Ref as a \c CompileJobCacheResult if it matches the + /// schema. + Expected load(ObjectRef Ref) const; + + bool isRootNode(const ObjectProxy &Node) const final; + bool isNode(const ObjectProxy &Node) const final; + + /// Get this schema's marker node. + ObjectRef getKindRef() const { return KindRef; } + +private: + ObjectRef KindRef; +}; + +} // namespace cas +} // namespace clang + +#endif // LLVM_CLANG_FRONTEND_COMPILEJOBCACHERESULT_H diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index 8d38c7c7c15eb..496d7d0502a23 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -23,9 +23,13 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringRef.h" +#include "llvm/CAS/CASID.h" +#include "llvm/CAS/CASOutputBackend.h" #include "llvm/Support/BuryPointer.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/PrefixMapper.h" #include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/VirtualOutputBackend.h" #include #include #include @@ -94,9 +98,24 @@ class CompilerInstance : public ModuleLoader { /// Auxiliary Target info. IntrusiveRefCntPtr AuxTarget; + /// The CAS, if any. + std::shared_ptr CAS; + + /// The ActionCache, if any. + std::shared_ptr ActionCache; + + /// The \c ActionCache key for this compilation, if caching is enabled. + std::optional CompileJobCacheKey; + + /// The prefix mapper; empty by default. + llvm::PrefixMapper PrefixMapper; + /// The file manager. IntrusiveRefCntPtr FileMgr; + /// The output context. + IntrusiveRefCntPtr TheOutputBackend; + /// The source manager. IntrusiveRefCntPtr SourceMgr; @@ -180,26 +199,22 @@ class CompilerInstance : public ModuleLoader { /// The stream for verbose output. raw_ostream *VerboseOutputStream = &llvm::errs(); - /// Holds information about the output file. - /// - /// If TempFilename is not empty we must rename it to Filename at the end. - /// TempFilename may be empty and Filename non-empty if creating the temporary - /// failed. - struct OutputFile { - std::string Filename; - std::optional File; + /// The list of active output files. + std::list OutputFiles; - OutputFile(std::string filename, - std::optional file) - : Filename(std::move(filename)), File(std::move(file)) {} - }; + using GenModuleActionWrapperFunc = + std::function( + const FrontendOptions &opts, std::unique_ptr action)>; - /// The list of active output files. - std::list OutputFiles; + /// An optional callback function used to wrap all FrontendActions + /// produced to generate imported modules before they are executed. + GenModuleActionWrapperFunc GenModuleActionWrapper; /// Force an output buffer. std::unique_ptr OutputStream; + void createCASDatabases(); + CompilerInstance(const CompilerInstance &) = delete; void operator=(const CompilerInstance &) = delete; public: @@ -349,6 +364,26 @@ class CompilerInstance : public ModuleLoader { return Invocation->getTargetOpts(); } + CASOptions &getCASOpts() { + return Invocation->getCASOpts(); + } + const CASOptions &getCASOpts() const { + return Invocation->getCASOpts(); + } + + std::optional getCompileJobCacheKey() const { + return CompileJobCacheKey; + } + void setCompileJobCacheKey(cas::CASID Key) { + assert(!CompileJobCacheKey || CompileJobCacheKey == Key); + CompileJobCacheKey = std::move(Key); + } + bool isSourceNonReproducible() const; + + llvm::PrefixMapper &getPrefixMapper() { return PrefixMapper; } + + void setPrefixMapper(llvm::PrefixMapper PM) { PrefixMapper = std::move(PM); } + /// @} /// @name Diagnostics Engine /// @{ @@ -452,6 +487,21 @@ class CompilerInstance : public ModuleLoader { /// Replace the current file manager and virtual file system. void setFileManager(FileManager *Value); + /// Set the output manager. + void setOutputBackend(IntrusiveRefCntPtr NewOutputs); + + /// Create an output manager. + void createOutputBackend(); + + bool hasOutputBackend() const { return bool(TheOutputBackend); } + + llvm::vfs::OutputBackend &getOutputBackend(); + llvm::vfs::OutputBackend &getOrCreateOutputBackend(); + + /// Get the CAS, or create it using the configuration in CompilerInvocation. + llvm::cas::ObjectStore &getOrCreateObjectStore(); + llvm::cas::ActionCache &getOrCreateActionCache(); + /// @} /// @name Source Manager /// @{ @@ -705,7 +755,8 @@ class CompilerInstance : public ModuleLoader { std::string getSpecificModuleCachePath(StringRef ModuleHash); std::string getSpecificModuleCachePath() { - return getSpecificModuleCachePath(getInvocation().getModuleHash()); + return getSpecificModuleCachePath( + getInvocation().getModuleHash(getDiagnostics())); } /// Create the AST context. @@ -716,7 +767,8 @@ class CompilerInstance : public ModuleLoader { void createPCHExternalASTSource( StringRef Path, DisableValidationForModuleKind DisableValidation, bool AllowPCHWithCompilerErrors, void *DeserializationListener, - bool OwnDeserializationListener); + bool OwnDeserializationListener, + std::unique_ptr PCHBuffer = nullptr); /// Create an external AST source to read a PCH file. /// @@ -729,7 +781,9 @@ class CompilerInstance : public ModuleLoader { ArrayRef> Extensions, ArrayRef> DependencyCollectors, void *DeserializationListener, bool OwnDeserializationListener, - bool Preamble, bool UseGlobalModuleIndex); + bool Preamble, bool UseGlobalModuleIndex, + cas::ObjectStore &CAS, cas::ActionCache &Cache, bool ignoreCAS, + std::unique_ptr PCHBuffer = nullptr); /// Create a code completion consumer using the invocation; note that this /// will cause the source manager to truncate the input source file at the @@ -816,6 +870,9 @@ class CompilerInstance : public ModuleLoader { FileManager &FileMgr, SourceManager &SourceMgr); + /// Initialize inputs from CAS. + void initializeDelayedInputFileFromCAS(); + /// @} void setOutputStream(std::unique_ptr OutStream) { @@ -918,12 +975,30 @@ class CompilerInstance : public ModuleLoader { bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override; + void setGenModuleActionWrapper(GenModuleActionWrapperFunc Wrapper) { + GenModuleActionWrapper = Wrapper; + } + + GenModuleActionWrapperFunc getGenModuleActionWrapper() const { + return GenModuleActionWrapper; + } + void addDependencyCollector(std::shared_ptr Listener) { DependencyCollectors.push_back(std::move(Listener)); } void setExternalSemaSource(IntrusiveRefCntPtr ESS); + /// Adds a module to the \c InMemoryModuleCache at \p Path by retrieving the + /// pcm output from the \c ActionCache for \p CacheKey. + /// + /// \param Provider description of what provided this cache key, e.g. + /// "-fmodule-file-cache-key", or an imported pcm file. Used in diagnostics. + /// + /// \returns true on failure. + bool addCachedModuleFile(StringRef Path, StringRef CacheKey, + StringRef Provider); + ModuleCache &getModuleCache() const { return *ModCache; } }; diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h index 3ca900729b4a8..ab045d6f2e5f1 100644 --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -16,6 +16,7 @@ #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/LangStandard.h" +#include "clang/CAS/CASOptions.h" #include "clang/Frontend/DependencyOutputOptions.h" #include "clang/Frontend/FrontendOptions.h" #include "clang/Frontend/MigratorOptions.h" @@ -42,6 +43,11 @@ class FileSystem; } // namespace vfs +namespace cas { + +class ObjectStore; +} + } // namespace llvm namespace clang { @@ -96,6 +102,9 @@ class CompilerInvocationBase { /// Options controlling API notes. std::shared_ptr APINotesOpts; + /// Options configuring the CAS. + std::shared_ptr CASOpts; + /// Options controlling IRgen and the backend. std::shared_ptr CodeGenOpts; @@ -136,6 +145,7 @@ class CompilerInvocationBase { const AnalyzerOptions &getAnalyzerOpts() const { return *AnalyzerOpts; } const MigratorOptions &getMigratorOpts() const { return *MigratorOpts; } const APINotesOptions &getAPINotesOpts() const { return *APINotesOpts; } + const CASOptions &getCASOpts() const { return *CASOpts; } const CodeGenOptions &getCodeGenOpts() const { return *CodeGenOpts; } const FileSystemOptions &getFileSystemOpts() const { return *FSOpts; } const FrontendOptions &getFrontendOpts() const { return *FrontendOpts; } @@ -199,6 +209,21 @@ class CompilerInvocationBase { const std::string &OutputFile, const LangOptions *LangOpts); /// @} + +public: + /// Generate command line options from CASOptions. + static void GenerateCASArgs(const CASOptions &Opts, + ArgumentConsumer Consumer); + static void GenerateCASArgs(const CASOptions &Opts, + SmallVectorImpl &Args, + StringAllocator SA) { + GenerateCASArgs(Opts, [&](const Twine &Arg) { + // No need to allocate static string literals. + Args.push_back(Arg.isSingleStringLiteral() + ? Arg.getSingleStringRef().data() + : SA(Arg)); + }); + } }; class CowCompilerInvocation; @@ -237,6 +262,7 @@ class CompilerInvocation : public CompilerInvocationBase { using CompilerInvocationBase::getAnalyzerOpts; using CompilerInvocationBase::getMigratorOpts; using CompilerInvocationBase::getAPINotesOpts; + using CompilerInvocationBase::getCASOpts; using CompilerInvocationBase::getCodeGenOpts; using CompilerInvocationBase::getFileSystemOpts; using CompilerInvocationBase::getFrontendOpts; @@ -254,6 +280,7 @@ class CompilerInvocation : public CompilerInvocationBase { AnalyzerOptions &getAnalyzerOpts() { return *AnalyzerOpts; } MigratorOptions &getMigratorOpts() { return *MigratorOpts; } APINotesOptions &getAPINotesOpts() { return *APINotesOpts; } + CASOptions &getCASOpts() { return *CASOpts; } CodeGenOptions &getCodeGenOpts() { return *CodeGenOpts; } FileSystemOptions &getFileSystemOpts() { return *FSOpts; } FrontendOptions &getFrontendOpts() { return *FrontendOpts; } @@ -265,6 +292,14 @@ class CompilerInvocation : public CompilerInvocationBase { } /// @} + /// Base class internals. + /// @{ + std::shared_ptr getCASOptsPtr() { return CASOpts; } + void setCASOption(std::shared_ptr CASOpts) { + this->CASOpts = CASOpts; + } + /// @} + /// Create a compiler invocation from a list of input options. /// \returns true on success. /// @@ -302,7 +337,7 @@ class CompilerInvocation : public CompilerInvocationBase { /// Retrieve a module hash string that is suitable for uniquely /// identifying the conditions under which the module was built. - std::string getModuleHash() const; + std::string getModuleHash(DiagnosticsEngine &Diags) const; /// Check that \p Args can be parsed and re-serialized without change, /// emiting diagnostics for any differences. @@ -323,6 +358,10 @@ class CompilerInvocation : public CompilerInvocationBase { /// implicit modules. void clearImplicitModuleBuildOptions(); + /// Parse command line options that map to \p CASOptions. + static bool ParseCASArgs(CASOptions &Opts, const llvm::opt::ArgList &Args, + DiagnosticsEngine &Diags); + private: static bool CreateFromArgsImpl(CompilerInvocation &Res, ArrayRef CommandLineArgs, @@ -339,7 +378,10 @@ class CompilerInvocation : public CompilerInvocationBase { InputKind IK, DiagnosticsEngine &Diags, const llvm::Triple &T, const std::string &OutputFile, - const LangOptions &LangOptsRef); + const LangOptions &LangOptsRef, + const FileSystemOptions &FSOpts, + const FrontendOptions &FEOpts, + const CASOptions &CASOpts); }; /// Same as \c CompilerInvocation, but with copy-on-write optimization. @@ -377,6 +419,7 @@ class CowCompilerInvocation : public CompilerInvocationBase { AnalyzerOptions &getMutAnalyzerOpts(); MigratorOptions &getMutMigratorOpts(); APINotesOptions &getMutAPINotesOpts(); + CASOptions &getMutCASOpts(); CodeGenOptions &getMutCodeGenOpts(); FileSystemOptions &getMutFileSystemOpts(); FrontendOptions &getMutFrontendOpts(); @@ -385,9 +428,9 @@ class CowCompilerInvocation : public CompilerInvocationBase { /// @} }; -IntrusiveRefCntPtr -createVFSFromCompilerInvocation(const CompilerInvocation &CI, - DiagnosticsEngine &Diags); +IntrusiveRefCntPtr createVFSFromCompilerInvocation( + const CompilerInvocation &CI, DiagnosticsEngine &Diags, + std::shared_ptr OverrideCAS = nullptr); IntrusiveRefCntPtr createVFSFromCompilerInvocation( const CompilerInvocation &CI, DiagnosticsEngine &Diags, diff --git a/clang/include/clang/Frontend/DependencyOutputOptions.h b/clang/include/clang/Frontend/DependencyOutputOptions.h index d92a87d78d7c5..dcf38aae4461a 100644 --- a/clang/include/clang/Frontend/DependencyOutputOptions.h +++ b/clang/include/clang/Frontend/DependencyOutputOptions.h @@ -46,6 +46,8 @@ class DependencyOutputOptions { LLVM_PREFERRED_TYPE(bool) unsigned IncludeModuleFiles : 1; ///< Include module file dependencies. LLVM_PREFERRED_TYPE(bool) + unsigned SkipUnusedModuleMaps : 1; ///< Skip unused module map dependencies. + LLVM_PREFERRED_TYPE(bool) unsigned ShowSkippedHeaderIncludes : 1; ///< With ShowHeaderIncludes, show /// also includes that were skipped /// due to the "include guard @@ -89,7 +91,7 @@ class DependencyOutputOptions { public: DependencyOutputOptions() : IncludeSystemHeaders(0), ShowHeaderIncludes(0), UsePhonyTargets(0), - AddMissingHeaderDeps(0), IncludeModuleFiles(0), + AddMissingHeaderDeps(0), IncludeModuleFiles(0), SkipUnusedModuleMaps(0), ShowSkippedHeaderIncludes(0), HeaderIncludeFormat(HIFMT_Textual), HeaderIncludeFiltering(HIFIL_None) {} }; diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h index c919a53ae089e..625f16c8f1c17 100644 --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -15,6 +15,7 @@ #include "clang/Sema/CodeCompleteOptions.h" #include "clang/Serialization/ModuleFileExtension.h" #include "llvm/ADT/StringRef.h" +#include "llvm/CAS/CASReference.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MemoryBuffer.h" #include @@ -235,6 +236,9 @@ class FrontendInputFile { /// that it outlives any users. std::optional Buffer; + /// The input, if it comes from \p FrontendOptions::CASIncludeTreeID. + std::optional IncludeTree; + /// The kind of input, e.g., C source, AST file, LLVM IR. InputKind Kind; @@ -248,6 +252,14 @@ class FrontendInputFile { FrontendInputFile(llvm::MemoryBufferRef Buffer, InputKind Kind, bool IsSystem = false) : Buffer(Buffer), Kind(Kind), IsSystem(IsSystem) {} + FrontendInputFile(cas::ObjectRef Tree, StringRef File, InputKind Kind, + bool IsSystem = false) + : File(File.str()), IncludeTree(std::move(Tree)), Kind(Kind), + IsSystem(IsSystem) {} + FrontendInputFile(cas::ObjectRef Tree, llvm::MemoryBufferRef Buffer, + InputKind Kind, bool IsSystem = false) + : Buffer(Buffer), IncludeTree(std::move(Tree)), Kind(Kind), + IsSystem(IsSystem) {} InputKind getKind() const { return Kind; } bool isSystem() const { return IsSystem; } @@ -255,6 +267,7 @@ class FrontendInputFile { bool isEmpty() const { return File.empty() && Buffer == std::nullopt; } bool isFile() const { return !isBuffer(); } bool isBuffer() const { return Buffer != std::nullopt; } + bool isIncludeTree() const { return IncludeTree.has_value(); } bool isPreprocessed() const { return Kind.isPreprocessed(); } bool isHeader() const { return Kind.isHeader(); } InputKind::HeaderUnitKind getHeaderUnitKind() const { @@ -270,6 +283,11 @@ class FrontendInputFile { assert(isBuffer()); return *Buffer; } + + cas::ObjectRef getIncludeTree() const { + assert(isIncludeTree()); + return *IncludeTree; + } }; /// FrontendOptions - Options for controlling the behavior of the frontend. @@ -380,6 +398,37 @@ class FrontendOptions { LLVM_PREFERRED_TYPE(bool) unsigned IsSystemModule : 1; + unsigned IndexIgnoreSystemSymbols : 1; + unsigned IndexRecordCodegenName : 1; + unsigned IndexIgnoreMacros : 1; + unsigned IndexIgnorePcms : 1; + + /// Cache -cc1 compilations when possible. Ignored unless CASFileSystemRootID + /// is specified. + unsigned CacheCompileJob : 1; + + /// Whether this invocation is dependency scanning for include-tree. Used to + /// separate module cache for include-tree from cas-fs. + unsigned ForIncludeTreeScan : 1; + + /// Avoid checking if the compile job is already cached, force compilation and + /// caching of compilation outputs. This is used for testing purposes. + unsigned DisableCachedCompileJobReplay : 1; + + /// Whether to preserve the original PCH path in the include-tree, or to + /// canonicalize it to a fixed value. Setting this to \c true allows the use + /// of gmodules with PCH and include tree. + unsigned IncludeTreePreservePCHPath : 1; + + /// Keep the diagnostic client open for receiving diagnostics after the source + /// files have been processed. + unsigned MayEmitDiagnosticsAfterProcessingSourceFiles : 1; + + /// When using CacheCompileJob, write a CASID for the output file. + /// + /// FIXME: Add clang tests for this functionality. + unsigned WriteOutputAsCASID : 1; + /// Output (and read) PCM files regardless of compiler errors. LLVM_PREFERRED_TYPE(bool) unsigned AllowPCMWithCompilerErrors : 1; @@ -425,6 +474,9 @@ class FrontendOptions { /// Specifies the output format of the AST. ASTDumpOutputFormat ASTDumpFormat = ADOF_Default; + std::string IndexStorePath; + std::string IndexUnitOutputPath; + /// The input kind, either specified via -x argument or deduced from the input /// file name. InputKind DashX; @@ -432,6 +484,18 @@ class FrontendOptions { /// The input files and their types. SmallVector Inputs; + /// Use the provided CAS include tree. + std::string CASIncludeTreeID; + + /// Use the main output of the provided compilation cache key as the input. + std::string CASInputFileCacheKey; + + /// Use the blob in the CAS object as the input. + std::string CASInputFileCASID; + + /// If ignore all the CAS info from serialized AST like modules and PCHs. + bool ModuleLoadIgnoreCAS = false; + /// When the input is a module map, the original module map file from which /// that map was inferred, if any (for umbrella modules). std::string OriginalModuleMap; @@ -458,6 +522,14 @@ class FrontendOptions { /// The name of the product the input files belong too. std::string ProductName; + /// Socket path for remote caching service. + std::string CompilationCachingServicePath; + + /// When caching is enabled, represents remappings for all the file paths that + /// the compilation may access. This is useful for canonicalizing the + /// compilation for caching purposes. + std::vector PathPrefixMappings; + // Currently this is only used as part of the `-extract-api` action. // A comma separated list of files providing a list of APIs to // ignore when extracting documentation. @@ -492,6 +564,10 @@ class FrontendOptions { /// The list of AST files to merge. std::vector ASTMergeFiles; + /// The list of prebuilt module file paths to make available by reading their + /// contents from the \c ActionCache with the given compile job cache key. + std::vector> ModuleCacheKeys; + /// A list of arguments to forward to LLVM's option processing; this /// should only be used for debugging and experimental features. std::vector LLVMArgs; @@ -544,7 +620,10 @@ class FrontendOptions { ASTDumpDecls(false), ASTDumpLookups(false), BuildingImplicitModule(false), BuildingImplicitModuleUsesLock(true), ModulesEmbedAllFiles(false), IncludeTimestamps(true), - UseTemporary(true), AllowPCMWithCompilerErrors(false), + UseTemporary(true), CacheCompileJob(false), ForIncludeTreeScan(false), + DisableCachedCompileJobReplay(false), IncludeTreePreservePCHPath(false), + MayEmitDiagnosticsAfterProcessingSourceFiles(false), + WriteOutputAsCASID(false), AllowPCMWithCompilerErrors(false), ModulesShareFileManager(true), EmitSymbolGraph(false), EmitExtensionSymbolGraphs(false), EmitSymbolGraphSymbolLabelsForTesting(false), diff --git a/clang/include/clang/Frontend/IncludeTreePPActions.h b/clang/include/clang/Frontend/IncludeTreePPActions.h new file mode 100644 index 0000000000000..a2a0460a25613 --- /dev/null +++ b/clang/include/clang/Frontend/IncludeTreePPActions.h @@ -0,0 +1,32 @@ +//===- IncludeTreePPActions.h - PP actions using include-tree ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Uses the info from an include-tree to drive the preprocessor via +// \p PPCachedActions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FRONTEND_INCLUDETREEPPACTIONS_H +#define LLVM_CLANG_FRONTEND_INCLUDETREEPPACTIONS_H + +#include "clang/Basic/LLVM.h" + +namespace clang { + +class PPCachedActions; + +namespace cas { +class IncludeTreeRoot; +} + +Expected> +createPPActionsFromIncludeTree(cas::IncludeTreeRoot &Root); + +} // namespace clang + +#endif diff --git a/clang/include/clang/Frontend/SerializedDiagnosticPrinter.h b/clang/include/clang/Frontend/SerializedDiagnosticPrinter.h index 5586ef65e393f..2c6253f80e03a 100644 --- a/clang/include/clang/Frontend/SerializedDiagnosticPrinter.h +++ b/clang/include/clang/Frontend/SerializedDiagnosticPrinter.h @@ -31,9 +31,16 @@ namespace serialized_diags { /// This allows wrapper tools for Clang to get diagnostics from Clang /// (via libclang) without needing to parse Clang's command line output. /// -std::unique_ptr create(StringRef OutputFile, - DiagnosticOptions *Diags, - bool MergeChildRecords = false); +/// \param OS optional stream to output the serialized diagnostics buffer, +/// instead of writing out directly to a file. +/// FIXME: \p OS is temporary transition until we have structured diagnostics +/// caching in which case we won't need to manage serialized diagnostics files +/// explicitly for caching purposes and the changes to add \p OS in this +/// function should be reverted. +std::unique_ptr +create(StringRef OutputFile, DiagnosticOptions *Diags, + bool MergeChildRecords = false, + std::unique_ptr OS = nullptr); } // end serialized_diags namespace } // end clang namespace diff --git a/clang/include/clang/Frontend/SerializedDiagnosticReader.h b/clang/include/clang/Frontend/SerializedDiagnosticReader.h index f7c2012a7662a..c355b9c20c841 100644 --- a/clang/include/clang/Frontend/SerializedDiagnosticReader.h +++ b/clang/include/clang/Frontend/SerializedDiagnosticReader.h @@ -65,6 +65,9 @@ class SerializedDiagnosticReader { /// Read the diagnostics in \c File std::error_code readDiagnostics(StringRef File); + /// Read the diagnostics in \c Buffer. + std::error_code readDiagnostics(llvm::MemoryBufferRef Buffer); + private: enum class Cursor; @@ -86,10 +89,21 @@ class SerializedDiagnosticReader { virtual std::error_code visitEndOfDiagnostic() { return {}; } /// Visit a category. This associates the category \c ID to a \c Name. + /// + /// This entrypoint has been superseded by the overload that follows, which + /// also takes a (possibly-empty) URL providing additional documentation for + /// the category. virtual std::error_code visitCategoryRecord(unsigned ID, StringRef Name) { return {}; } + /// Visit a category. This associates the category \c ID to a \c Name with + /// a (possibly empty) URL. + virtual std::error_code visitCategoryRecord(unsigned ID, StringRef Name, + StringRef URL) { + return visitCategoryRecord(ID, Name); + } + /// Visit a flag. This associates the flag's \c ID to a \c Name. virtual std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name) { return {}; @@ -109,6 +123,16 @@ class SerializedDiagnosticReader { return {}; } + /// Visit file contents. This associates the file's \c ID with the + /// contents of + virtual std::error_code visitSourceFileContentsRecord( + unsigned ID, + const Location &OriginalStartLoc, + const Location &OriginalEndLoc, + StringRef Contents) { + return {}; + } + /// Visit a fixit hint. virtual std::error_code visitFixitRecord(const Location &Start, const Location &End, StringRef Text) { diff --git a/clang/include/clang/Frontend/SerializedDiagnostics.h b/clang/include/clang/Frontend/SerializedDiagnostics.h index 6464693c14820..3dda959d44f44 100644 --- a/clang/include/clang/Frontend/SerializedDiagnostics.h +++ b/clang/include/clang/Frontend/SerializedDiagnostics.h @@ -32,8 +32,9 @@ enum RecordIDs { RECORD_CATEGORY, RECORD_FILENAME, RECORD_FIXIT, + RECORD_SOURCE_FILE_CONTENTS, RECORD_FIRST = RECORD_VERSION, - RECORD_LAST = RECORD_FIXIT + RECORD_LAST = RECORD_SOURCE_FILE_CONTENTS }; /// A stable version of DiagnosticIDs::Level. diff --git a/clang/include/clang/Frontend/Utils.h b/clang/include/clang/Frontend/Utils.h index 604e42067a3f1..4e76006905f5a 100644 --- a/clang/include/clang/Frontend/Utils.h +++ b/clang/include/clang/Frontend/Utils.h @@ -24,6 +24,7 @@ #include "llvm/ADT/StringSet.h" #include "llvm/Support/FileCollector.h" #include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/VirtualOutputBackend.h" #include #include #include @@ -34,6 +35,7 @@ namespace clang { class ASTReader; +class CASOptions; class CompilerInstance; class CompilerInvocation; class DiagnosticsEngine; @@ -103,7 +105,12 @@ class DependencyCollector { /// loaded. class DependencyFileGenerator : public DependencyCollector { public: - DependencyFileGenerator(const DependencyOutputOptions &Opts); + /// Constructs a \c DependencyFileGenerator with the given options and output + /// backend. If \p OutputBackend is null, a default on-disk backend will be + /// used. + DependencyFileGenerator( + const DependencyOutputOptions &Opts, + IntrusiveRefCntPtr OutputBackend = nullptr); void attachToPreprocessor(Preprocessor &PP) override; @@ -120,6 +127,7 @@ class DependencyFileGenerator : public DependencyCollector { private: void outputDependencyFile(DiagnosticsEngine &Diags); + IntrusiveRefCntPtr OutputBackend; std::string OutputFile; std::vector Targets; bool IncludeSystemHeaders; @@ -127,6 +135,7 @@ class DependencyFileGenerator : public DependencyCollector { bool AddMissingHeaderDeps; bool SeenMissingHeader; bool IncludeModuleFiles; + bool SkipUnusedModuleMaps; DependencyOutputFormat OutputFormat; unsigned InputFileIndex; }; diff --git a/clang/include/clang/Index/IndexDataStore.h b/clang/include/clang/Index/IndexDataStore.h new file mode 100644 index 0000000000000..694943e847629 --- /dev/null +++ b/clang/include/clang/Index/IndexDataStore.h @@ -0,0 +1,76 @@ +//===--- IndexDataStore.h - Index data store info -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXDATASTORE_H +#define LLVM_CLANG_INDEX_INDEXDATASTORE_H + +#include "clang/Basic/LLVM.h" +#include "clang/Basic/PathRemapper.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Chrono.h" +#include +#include +#include + +namespace clang { +namespace index { + +class IndexDataStore { +public: + ~IndexDataStore(); + + static std::unique_ptr create(StringRef IndexStorePath, + const PathRemapper &Remapper, + std::string &Error); + + StringRef getFilePath() const; + const PathRemapper &getPathRemapper() const; + bool foreachUnitName(bool sorted, + llvm::function_ref receiver); + + static unsigned getFormatVersion(); + + enum class UnitEventKind { + Removed, + Modified, + /// The directory got deleted. No more events will follow. + DirectoryDeleted, + Failure + }; + struct UnitEvent { + UnitEventKind Kind; + StringRef UnitName; + }; + struct UnitEventNotification { + bool IsInitial; + ArrayRef Events; + }; + typedef std::function UnitEventHandler; + + void setUnitEventHandler(UnitEventHandler Handler); + /// \returns true if an error occurred. + bool startEventListening(bool waitInitialSync, std::string &Error); + void stopEventListening(); + + void discardUnit(StringRef UnitName); + void discardRecord(StringRef RecordName); + + void purgeStaleData(); + +private: + IndexDataStore(void *Impl) : Impl(Impl) {} + + void *Impl; // An IndexDataStoreImpl. +}; + +} // namespace index +} // namespace clang + +#endif diff --git a/clang/include/clang/Index/IndexDataStoreSymbolUtils.h b/clang/include/clang/Index/IndexDataStoreSymbolUtils.h new file mode 100644 index 0000000000000..dd9a35f8865fd --- /dev/null +++ b/clang/include/clang/Index/IndexDataStoreSymbolUtils.h @@ -0,0 +1,52 @@ +//===--- IndexDataStoreSymbolUtils.h - Utilities for indexstore symbols ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXDATASTORESYMBOLUTILS_H +#define LLVM_CLANG_INDEX_INDEXDATASTORESYMBOLUTILS_H + +#include "indexstore/indexstore.h" +#include "clang/Index/IndexSymbol.h" + +namespace clang { +namespace index { + +/// Map an indexstore_symbol_kind_t to a SymbolKind, handling unknown values. +SymbolKind getSymbolKind(indexstore_symbol_kind_t K); + +SymbolSubKind getSymbolSubKind(indexstore_symbol_subkind_t K); + +/// Map an indexstore_symbol_language_t to a SymbolLanguage, handling unknown +/// values. +SymbolLanguage getSymbolLanguage(indexstore_symbol_language_t L); + +/// Map an indexstore representation to a SymbolPropertySet, handling +/// unknown values. +SymbolPropertySet getSymbolProperties(uint64_t Props); + +/// Map an indexstore representation to a SymbolRoleSet, handling unknown +/// values. +SymbolRoleSet getSymbolRoles(uint64_t Roles); + +/// Map a SymbolLanguage to a indexstore_symbol_language_t. +indexstore_symbol_kind_t getIndexStoreKind(SymbolKind K); + +indexstore_symbol_subkind_t getIndexStoreSubKind(SymbolSubKind K); + +/// Map a SymbolLanguage to a indexstore_symbol_language_t. +indexstore_symbol_language_t getIndexStoreLang(SymbolLanguage L); + +/// Map a SymbolPropertySet to its indexstore representation. +indexstore_symbol_property_t getIndexStoreProperties(SymbolPropertySet Props); + +/// Map a SymbolRoleSet to its indexstore representation. +indexstore_symbol_role_t getIndexStoreRoles(SymbolRoleSet Roles); + +} // end namespace index +} // end namespace clang + +#endif // LLVM_CLANG_INDEX_INDEXDATASTORESYMBOLUTILS_H diff --git a/clang/include/clang/Index/IndexRecordReader.h b/clang/include/clang/Index/IndexRecordReader.h new file mode 100644 index 0000000000000..a572b9e84be10 --- /dev/null +++ b/clang/include/clang/Index/IndexRecordReader.h @@ -0,0 +1,108 @@ +//===--- IndexRecordReader.h - Index record deserialization ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXRECORDREADER_H +#define LLVM_CLANG_INDEX_INDEXRECORDREADER_H + +#include "clang/Index/IndexSymbol.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include + +namespace llvm { + class MemoryBuffer; +} + +namespace clang { +namespace index { + +struct IndexRecordDecl { + unsigned DeclID; + SymbolInfo SymInfo; + SymbolRoleSet Roles; + SymbolRoleSet RelatedRoles; + StringRef Name; + StringRef USR; + StringRef CodeGenName; +}; + +struct IndexRecordRelation { + SymbolRoleSet Roles; + const IndexRecordDecl *Dcl = nullptr; + + IndexRecordRelation() = default; + IndexRecordRelation(SymbolRoleSet Roles, const IndexRecordDecl *Dcl) + : Roles(Roles), Dcl(Dcl) {} +}; + +struct IndexRecordOccurrence { + const IndexRecordDecl *Dcl; + SmallVector Relations; + SymbolRoleSet Roles; + unsigned Line; + unsigned Column; +}; + +class IndexRecordReader { + IndexRecordReader(); + +public: + static std::unique_ptr + createWithRecordFilename(StringRef RecordFilename, StringRef StorePath, + std::string &Error); + static std::unique_ptr + createWithFilePath(StringRef FilePath, std::string &Error); + static std::unique_ptr + createWithBuffer(std::unique_ptr Buffer, + std::string &Error); + + ~IndexRecordReader(); + + struct DeclSearchReturn { + bool AcceptDecl; + bool ContinueSearch; + }; + typedef DeclSearchReturn(DeclSearchCheck)(const IndexRecordDecl &); + + /// Goes through and passes record decls, after filtering using a \c Checker + /// function. + /// + /// Resulting decls can be used as filter for \c foreachOccurrence. This + /// allows allocating memory only for the record decls that the caller is + /// interested in. + bool searchDecls(llvm::function_ref Checker, + llvm::function_ref Receiver); + + /// \param NoCache if true, avoids allocating memory for the decls. + /// Useful when the caller does not intend to keep \c IndexRecordReader + /// for more queries. + bool foreachDecl(bool NoCache, + llvm::function_ref Receiver); + + /// \param DeclsFilter if non-empty indicates the list of decls that we want + /// to get occurrences for. An empty array indicates that we want occurrences + /// for all decls. + /// \param RelatedDeclsFilter Same as \c DeclsFilter but for related decls. + bool foreachOccurrence(ArrayRef DeclsFilter, + ArrayRef RelatedDeclsFilter, + llvm::function_ref Receiver); + bool foreachOccurrence( + llvm::function_ref Receiver); + + bool foreachOccurrenceInLineRange(unsigned lineStart, unsigned lineCount, + llvm::function_ref Receiver); + + struct Implementation; +private: + Implementation &Impl; +}; + +} // namespace index +} // namespace clang + +#endif diff --git a/clang/include/clang/Index/IndexRecordWriter.h b/clang/include/clang/Index/IndexRecordWriter.h new file mode 100644 index 0000000000000..e8020aa6ad620 --- /dev/null +++ b/clang/include/clang/Index/IndexRecordWriter.h @@ -0,0 +1,101 @@ +//===--- IndexRecordWriter.h - Index record serialization -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXRECORDWRITER_H +#define LLVM_CLANG_INDEX_INDEXRECORDWRITER_H + +#include "clang/Index/IndexSymbol.h" +#include "llvm/ADT/SmallString.h" + +namespace clang { +namespace index { + +namespace writer { +/// An opaque pointer to a declaration or other symbol used by the +/// IndexRecordWriter to identify when two occurrences refer to the same symbol, +/// and as a token for getting information about a symbol from the caller. +typedef const void *OpaqueDecl; + +/// An indexer symbol suitable for serialization. +/// +/// This includes all the information about the symbol that will be serialized +/// except for roles, which are synthesized by looking at all the occurrences. +/// +/// \seealso IndexRecordDecl +/// \note this struct is generally accompanied by a buffer that owns the string +/// storage. It should not be stored permanently. +struct Symbol { + SymbolInfo SymInfo; + StringRef Name; + StringRef USR; + StringRef CodeGenName; +}; + +/// An relation to an opaque symbol. +/// \seealso IndexRecordRelation +struct SymbolRelation { + OpaqueDecl RelatedSymbol; + SymbolRoleSet Roles; +}; + +typedef llvm::function_ref &Scratch)> + SymbolWriterCallback; +} // end namespace writer + +/// A language-independent utility for serializing index record files. +/// +/// Internally, this class is a small state machine. Users should first call +/// beginRecord, and if the file does not already exist, then proceed to add +/// all symbol occurrences (addOccurrence) and finally finish with endRecord. +class IndexRecordWriter { + SmallString<64> RecordsPath; ///< The records directory path. + void *Record = nullptr; ///< The state of the current record. +public: + IndexRecordWriter(StringRef IndexPath); + + enum class Result { + Success, + Failure, + AlreadyExists, + }; + + /// Begin writing a record for the file \p Filename with contents uniquely + /// identified by \p RecordHash. + /// + /// \param Filename the name of the file this is a record for. + /// \param RecordHash the unique hash of the record contents. + /// \param Error on failure, set to the error message. + /// \param RecordFile if non-null, this is set to the name of the record file. + /// + /// \returns Success if we should continue writing this record, AlreadyExists + /// if the record file has already been written, or Failure if there was an + /// error, in which case \p Error will be set. + Result beginRecord(StringRef Filename, uint64_t RecordHash, + std::string &Error, std::string *RecordFile = nullptr); + + /// Finish writing the record file. + /// + /// \param Error on failure, set to the error message. + /// \param GetSymbolForDecl a callback mapping an writer::OpaqueDecl to its + /// writer::Symbol. This is how the language-specific symbol information is + /// provided to the IndexRecordWriter. The scratch parameter can be used for + /// any necessary storage. + /// + /// \return Success, or Failure and sets \p Error. + Result endRecord(std::string &Error, + writer::SymbolWriterCallback GetSymbolForDecl); + + /// Add an occurrence of the symbol \p D with the given \p Roles and location. + void addOccurrence(writer::OpaqueDecl D, SymbolRoleSet Roles, unsigned Line, + unsigned Column, ArrayRef Related); +}; + +} // end namespace index +} // end namespace clang + +#endif // LLVM_CLANG_INDEX_INDEXRECORDWRITER_H diff --git a/clang/include/clang/Index/IndexSymbol.h b/clang/include/clang/Index/IndexSymbol.h index 59e90fced3dda..b4b784510ebf0 100644 --- a/clang/include/clang/Index/IndexSymbol.h +++ b/clang/include/clang/Index/IndexSymbol.h @@ -54,6 +54,8 @@ enum class SymbolKind : uint8_t { Parameter, Using, + + CommentTag, TemplateTypeParm, TemplateTemplateParm, NonTypeTemplateParm, @@ -78,9 +80,32 @@ enum class SymbolSubKind : uint8_t { UsingTypename, UsingValue, UsingEnum, + + // Swift sub-kinds + + SwiftAccessorWillSet, + SwiftAccessorDidSet, + SwiftAccessorAddressor, + SwiftAccessorMutableAddressor, + SwiftAccessorRead, + SwiftAccessorModify, + + SwiftExtensionOfStruct, + SwiftExtensionOfClass, + SwiftExtensionOfEnum, + SwiftExtensionOfProtocol, + + SwiftPrefixOperator, + SwiftPostfixOperator, + SwiftInfixOperator, + + SwiftSubscript, + SwiftAssociatedType, + SwiftGenericTypeParam, + SwiftAccessorInit, }; -typedef uint16_t SymbolPropertySet; +typedef uint32_t SymbolPropertySet; /// Set of properties that provide additional info about a symbol. enum class SymbolProperty : SymbolPropertySet { Generic = 1 << 0, @@ -93,8 +118,10 @@ enum class SymbolProperty : SymbolPropertySet { Local = 1 << 7, /// Symbol is part of a protocol interface. ProtocolInterface = 1 << 8, + + /// Swift-only properties + SwiftAsync = 1 << 16, }; -static const unsigned SymbolPropertyBitNum = 9; /// Set of roles that are attributed to symbol occurrences. /// diff --git a/clang/include/clang/Index/IndexUnitReader.h b/clang/include/clang/Index/IndexUnitReader.h new file mode 100644 index 0000000000000..9b209ae576d19 --- /dev/null +++ b/clang/include/clang/Index/IndexUnitReader.h @@ -0,0 +1,85 @@ +//===--- IndexUnitReader.h - Index unit deserialization -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXUNITREADER_H +#define LLVM_CLANG_INDEX_INDEXUNITREADER_H + +#include "clang/Basic/LLVM.h" +#include "clang/Basic/PathRemapper.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Chrono.h" +#include + +namespace clang { +namespace index { + +class IndexUnitReader { +public: + enum class DependencyKind { + Unit, + Record, + File, + }; + + ~IndexUnitReader(); + + static std::unique_ptr + createWithUnitFilename(StringRef UnitFilename, StringRef StorePath, + const PathRemapper &Remapper, std::string &Error); + static std::unique_ptr + createWithFilePath(StringRef FilePath, const PathRemapper &Remapper, + std::string &Error); + + static std::optional> + getModificationTimeForUnit(StringRef UnitFilename, StringRef StorePath, + std::string &Error); + + StringRef getProviderIdentifier() const; + StringRef getProviderVersion() const; + + llvm::sys::TimePoint<> getModificationTime() const; + StringRef getWorkingDirectory() const; + StringRef getOutputFile() const; + StringRef getSysrootPath() const; + StringRef getMainFilePath() const; + StringRef getModuleName() const; + StringRef getTarget() const; + bool hasMainFile() const; + bool isSystemUnit() const; + bool isModuleUnit() const; + bool isDebugCompilation() const; + + struct DependencyInfo { + DependencyKind Kind; + bool IsSystem; + StringRef UnitOrRecordName; + StringRef FilePath; + StringRef ModuleName; + }; + struct IncludeInfo { + StringRef SourcePath; + unsigned SourceLine; + StringRef TargetPath; + }; + /// Unit dependencies are provided ahead of record ones, record ones + /// ahead of the file ones. + bool foreachDependency(llvm::function_ref Receiver); + + bool foreachInclude(llvm::function_ref Receiver); + +private: + IndexUnitReader(void *Impl) : Impl(Impl) {} + + void *Impl; // An IndexUnitReaderImpl. +}; + +} // namespace index +} // namespace clang + +#endif diff --git a/clang/include/clang/Index/IndexUnitWriter.h b/clang/include/clang/Index/IndexUnitWriter.h new file mode 100644 index 0000000000000..1edc51ac2dd9d --- /dev/null +++ b/clang/include/clang/Index/IndexUnitWriter.h @@ -0,0 +1,148 @@ +//===--- IndexUnitWriter.h - Index unit serialization ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXUNITWRITER_H +#define LLVM_CLANG_INDEX_INDEXUNITWRITER_H + +#include "clang/Basic/FileEntry.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallString.h" +#include +#include + +namespace llvm { + class BitstreamWriter; +} + +namespace clang { + class FileEntry; + class FileManager; + class PathRemapper; + +namespace index { + +namespace writer { +/// An opaque pointer to a module used by the IndexUnitWriter to associate +/// record and file dependencies with a module, and as a token for getting +/// information about the module from the caller. +typedef const void *OpaqueModule; + +/// Module info suitable for serialization. +/// +/// This is used for top-level modules and sub-modules. +struct ModuleInfo { + /// Full, dot-separate, module name. + StringRef Name; +}; + +typedef llvm::function_ref &Scratch)> + ModuleInfoWriterCallback; +} // end namespace writer + +class IndexUnitWriter { + FileManager &FileMgr; + SmallString<64> UnitsPath; + std::string ProviderIdentifier; + std::string ProviderVersion; + std::string OutputFile; + std::string ModuleName; + OptionalFileEntryRef MainFile; + bool IsSystemUnit; + bool IsModuleUnit; + bool IsDebugCompilation; + std::string TargetTriple; + std::string WorkDir; + std::string SysrootPath; + const PathRemapper &Remapper; + std::function &Scratch)> GetInfoForModuleFn; + struct FileInclude { + int Index; + unsigned Line; + }; + struct FileEntryData { + FileEntryRef File; + bool IsSystem; + int ModuleIndex; + std::vector Includes; + }; + std::vector Files; + std::vector Modules; + llvm::DenseMap IndexByFile; + llvm::DenseMap IndexByModule; + llvm::DenseSet SeenASTFiles; + struct RecordOrUnitData { + std::string Name; + int FileIndex; + int ModuleIndex; + bool IsSystem; + }; + std::vector Records; + std::vector ASTFileUnits; + +public: + /// \param MainFile the main file for a compiled source file. This should be + /// null for PCH and module units. + /// \param IsSystem true for system module units, false otherwise. + /// \param Remapper Remapper to use to standardize file paths to make them + /// hermetic/reproducible. This applies to all paths emitted in the unit file. + IndexUnitWriter(FileManager &FileMgr, + StringRef StorePath, + StringRef ProviderIdentifier, StringRef ProviderVersion, + StringRef OutputFile, + StringRef ModuleName, + OptionalFileEntryRef MainFile, + bool IsSystem, + bool IsModuleUnit, + bool IsDebugCompilation, + StringRef TargetTriple, + StringRef SysrootPath, + const PathRemapper &Remapper, + writer::ModuleInfoWriterCallback GetInfoForModule); + ~IndexUnitWriter(); + + int addFileDependency(OptionalFileEntryRef File, bool IsSystem, + writer::OpaqueModule Mod); + void addRecordFile(StringRef RecordFile, OptionalFileEntryRef File, + bool IsSystem, writer::OpaqueModule Mod); + void addASTFileDependency(OptionalFileEntryRef File, bool IsSystem, + writer::OpaqueModule Mod, + bool withoutUnitName = false); + void addUnitDependency(StringRef UnitFile, OptionalFileEntryRef File, + bool IsSystem, writer::OpaqueModule Mod); + bool addInclude(const FileEntry *Source, unsigned Line, const FileEntry *Target); + + bool write(std::string &Error); + + void getUnitNameForOutputFile(StringRef FilePath, SmallVectorImpl &Str); + void getUnitPathForOutputFile(StringRef FilePath, SmallVectorImpl &Str); + /// If the unit file exists and \p timeCompareFilePath is provided, it will + /// return true if \p timeCompareFilePath is older than the unit file. + std::optional + isUnitUpToDateForOutputFile(StringRef FilePath, + std::optional TimeCompareFilePath, + std::string &Error); + static void getUnitNameForAbsoluteOutputFile(StringRef FilePath, SmallVectorImpl &Str, + const PathRemapper &Remapper); + static bool initIndexDirectory(StringRef StorePath, std::string &Error); + +private: + class PathStorage; + int addModule(writer::OpaqueModule Mod); + void writeUnitInfo(llvm::BitstreamWriter &Stream, PathStorage &PathStore); + void writeDependencies(llvm::BitstreamWriter &Stream, PathStorage &PathStore); + void writeIncludes(llvm::BitstreamWriter &Stream, PathStorage &PathStore); + void writePaths(llvm::BitstreamWriter &Stream, PathStorage &PathStore); + void writeModules(llvm::BitstreamWriter &Stream); +}; + +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/include/clang/Index/IndexingAction.h b/clang/include/clang/Index/IndexingAction.h index 4baa2d5e72603..37022964301a8 100644 --- a/clang/include/clang/Index/IndexingAction.h +++ b/clang/include/clang/Index/IndexingAction.h @@ -16,14 +16,18 @@ #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/ArrayRef.h" #include +#include namespace clang { class ASTContext; class ASTConsumer; class ASTReader; class ASTUnit; + class CompilerInstance; class Decl; class FrontendAction; + class FrontendOptions; + class Module; namespace serialization { class ModuleFile; @@ -31,7 +35,20 @@ namespace serialization { namespace index { class IndexDataConsumer; +class IndexUnitWriter; +struct RecordingOptions { + enum class IncludesRecordingKind { + None, + UserOnly, // only record includes inside non-system files. + All, + }; + + std::string DataDirPath; + bool RecordSymbolCodeGenName = false; + bool RecordSystemDependencies = true; + IncludesRecordingKind RecordIncludes = IncludesRecordingKind::UserOnly; +}; /// Creates an ASTConsumer that indexes all symbols (macros and AST decls). std::unique_ptr createIndexingASTConsumer(std::shared_ptr DataConsumer, @@ -68,6 +85,18 @@ std::unique_ptr indexMacrosCallback(IndexDataConsumer &Consumer, void indexModuleFile(serialization::ModuleFile &Mod, ASTReader &Reader, IndexDataConsumer &DataConsumer, IndexingOptions Opts); +/// \param WrappedAction another frontend action to wrap over or null. +std::unique_ptr +createIndexDataRecordingAction(const FrontendOptions &FEOpts, + std::unique_ptr WrappedAction); + +/// Checks if the unit file exists for the module file, if it doesn't it +/// generates index data for it. +/// +/// \returns true if the index data were generated, false otherwise. +bool emitIndexDataForModuleFile(const Module *Mod, const CompilerInstance &CI, + IndexUnitWriter &ParentUnitWriter); + } // namespace index } // namespace clang diff --git a/clang/include/clang/Index/IndexingOptions.h b/clang/include/clang/Index/IndexingOptions.h index 97847dd7d5d88..b52f96c2c0129 100644 --- a/clang/include/clang/Index/IndexingOptions.h +++ b/clang/include/clang/Index/IndexingOptions.h @@ -33,6 +33,7 @@ struct IndexingOptions { // callback is not available (e.g. after parsing has finished). Note that // macro references are not available in Preprocessor. bool IndexMacrosInPreprocessor = false; + bool IndexPcms = true; // Has no effect if IndexFunctionLocals are false. bool IndexParametersInDeclarations = false; bool IndexTemplateParameters = false; diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h index bb65ae010cffa..05144dd676e3f 100644 --- a/clang/include/clang/Lex/Lexer.h +++ b/clang/include/clang/Lex/Lexer.h @@ -224,6 +224,10 @@ class Lexer : public PreprocessorLexer { /// isPragmaLexer - Returns true if this Lexer is being used to lex a pragma. bool isPragmaLexer() const { return Is_PragmaLexer; } + /// Note that this Lexer is being used to lex a pragma, or something like it + /// that has simple end-of-file behavior. + void setIsPragmaLexer(bool value) { Is_PragmaLexer = value; } + private: /// IndirectLex - An indirect call to 'Lex' that can be invoked via /// the PreprocessorLexer interface. @@ -574,6 +578,14 @@ class Lexer : public PreprocessorLexer { const LangOptions &LangOpts, bool SkipTrailingWhitespaceAndNewLine); + /// \brief Returns the source location of the token that comes after the + /// token located at the given location \p Loc (excluding any comments and + /// whitespace). The returned source location will be invalid if the location + /// is inside a macro. + static SourceLocation + findNextTokenLocationAfterTokenAt(SourceLocation Loc, const SourceManager &SM, + const LangOptions &LangOpts); + /// Returns true if the given character could appear in an identifier. static bool isAsciiIdentifierContinueChar(char c, const LangOptions &LangOpts); diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h index 43c3890631bd1..9b3be5dc6d86e 100644 --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/include/clang/Lex/ModuleMap.h @@ -60,6 +60,14 @@ class ModuleMapCallbacks { virtual void moduleMapFileRead(SourceLocation FileStart, FileEntryRef File, bool IsSystem) {} + /// Called when a module map file matches a module lookup + /// + /// \param File The file itself. + /// \param M The module found that matches this module map. + /// \param IsSystem Whether this is a module map from a system include path. + virtual void moduleMapFoundForModule(FileEntryRef File, const Module *M, + bool IsSystem) {} + /// Called when a header is added during module map parsing. /// /// \param Filename The header file itself. diff --git a/clang/include/clang/Lex/PPCachedActions.h b/clang/include/clang/Lex/PPCachedActions.h new file mode 100644 index 0000000000000..b2934c8219520 --- /dev/null +++ b/clang/include/clang/Lex/PPCachedActions.h @@ -0,0 +1,78 @@ +//===--- PPCachedActions.h - Callbacks for PP cached actions ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines the PPCachedActions interface. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LEX_PPCACHEDACTIONS_H +#define LLVM_CLANG_LEX_PPCACHEDACTIONS_H + +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/SmallVector.h" +#include + +namespace clang { + +class IdentifierInfo; +class Module; +class Preprocessor; + +/// This interface provides a way to override the actions of the preprocessor as +/// it does its thing. +/// +/// A client can use this to control how include directives are resolved. +class PPCachedActions { + virtual void anchor(); + +public: + /// The file that is included by an \c #include directive. + struct IncludeFile { + FileID FID; + Module *Submodule; + }; + /// The module that is imported by an \c #include directive or \c \@import. + struct IncludeModule { + SmallVector ImportPath; + // Whether this module should only be "marked visible" rather than imported. + bool VisibilityOnly; + }; + /// The module that is loaded and discarded for an \c #include directive, and + /// the file that is included instead. + struct SpuriousImport { + IncludeModule IM; + IncludeFile IF; + }; + + virtual ~PPCachedActions() = default; + + /// \returns the \p FileID that should be used for predefines. + virtual FileID handlePredefines(Preprocessor &PP) = 0; + + /// \returns the evaluation result for a \p __has_include check. + virtual bool evaluateHasInclude(Preprocessor &PP, SourceLocation Loc, + bool IsIncludeNext) = 0; + + /// \returns the file that should be entered or module that should be imported + /// for an \c #include directive. \c {} indicates that the directive + /// should be skipped. + virtual std::variant + handleIncludeDirective(Preprocessor &PP, SourceLocation IncludeLoc, + SourceLocation AfterDirectiveLoc) = 0; + + /// Notifies the \p PPCachedActions implementation that the preprocessor + /// finished lexing an include file. + virtual void exitedFile(Preprocessor &PP, FileID FID) {} +}; + +} // namespace clang + +#endif diff --git a/clang/include/clang/Lex/PPCallbacks.h b/clang/include/clang/Lex/PPCallbacks.h index 313b730afbab8..abc6d09015881 100644 --- a/clang/include/clang/Lex/PPCallbacks.h +++ b/clang/include/clang/Lex/PPCallbacks.h @@ -335,6 +335,14 @@ class PPCallbacks { /// is read. virtual void PragmaAssumeNonNullEnd(SourceLocation Loc) {} + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Callback invoked when a \#pragma clang abi_ptr_attr set (*ATTR*) + /// directive is read. + virtual void + PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef Spec) {} + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Called by Preprocessor::HandleMacroExpandedIdentifier when a /// macro invocation is found. virtual void MacroExpands(const Token &MacroNameTok, @@ -666,6 +674,15 @@ class PPChainedCallbacks : public PPCallbacks { Second->PragmaAssumeNonNullEnd(Loc); } + /* TO_UPSTREAM(BoundsSafety) ON */ + void PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef Spec) + override { + First->PragmaAbiPointerAttributesSet(Loc, Spec); + Second->PragmaAbiPointerAttributesSet(Loc, Spec); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, SourceRange Range, const MacroArgs *Args) override { First->MacroExpands(MacroNameTok, MD, Range, Args); diff --git a/clang/include/clang/Lex/PreprocessingRecord.h b/clang/include/clang/Lex/PreprocessingRecord.h index 7886aef7f0c7f..2c8869d85d1fd 100644 --- a/clang/include/clang/Lex/PreprocessingRecord.h +++ b/clang/include/clang/Lex/PreprocessingRecord.h @@ -297,9 +297,6 @@ class Token; FileID FID) { return std::nullopt; } - - /// Read a preallocated skipped range from the external source. - virtual SourceRange ReadSkippedRange(unsigned Index) = 0; }; /// A record of the steps taken while preprocessing a source file, @@ -325,8 +322,6 @@ class Token; /// The set of ranges that were skipped by the preprocessor, std::vector SkippedRanges; - bool SkippedRangesAllLoaded = true; - /// Global (loaded or local) ID for a preprocessed entity. /// Negative values are used to indicate preprocessed entities /// loaded from the external source while non-negative values are used to @@ -382,16 +377,6 @@ class Token; /// corresponds to the first newly-allocated entity. unsigned allocateLoadedEntities(unsigned NumEntities); - /// Allocate space for a new set of loaded preprocessed skipped - /// ranges. - /// - /// \returns The index into the set of loaded preprocessed ranges, which - /// corresponds to the first newly-allocated range. - unsigned allocateSkippedRanges(unsigned NumRanges); - - /// Ensures that all external skipped ranges have been loaded. - void ensureSkippedRangesLoaded(); - /// Register a new macro definition. void RegisterMacroDefinition(MacroInfo *Macro, MacroDefinitionRecord *Def); @@ -515,7 +500,6 @@ class Token; /// Retrieve all ranges that got skipped while preprocessing. const std::vector &getSkippedRanges() { - ensureSkippedRangesLoaded(); return SkippedRanges; } diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index f2dfd3a349b8b..e28211a9f7cb4 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -28,6 +28,7 @@ #include "clang/Lex/MacroInfo.h" #include "clang/Lex/ModuleLoader.h" #include "clang/Lex/ModuleMap.h" +#include "clang/Lex/PPCachedActions.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/PPEmbedParameters.h" #include "clang/Lex/Token.h" @@ -261,6 +262,9 @@ class Preprocessor { /// True if we are pre-expanding macro arguments. bool InMacroArgPreExpansion; + /// True if we encountered any of the non-deterministic macros. + bool IsSourceNonReproducible; + /// Mapping/lookup information for all identifiers in /// the program, including program keywords. mutable IdentifierTable Identifiers; @@ -815,6 +819,10 @@ class Preprocessor { /// encountered (e.g. a file is \#included, etc). std::unique_ptr Callbacks; + /// Actions that can override certain preprocessor activities, like handling + /// of \#include directives. + std::unique_ptr CachedActions; + struct MacroExpandsInfo { Token Tok; MacroDefinition MD; @@ -1256,6 +1264,8 @@ class Preprocessor { void setPragmasEnabled(bool Enabled) { PragmasEnabled = Enabled; } bool getPragmasEnabled() const { return PragmasEnabled; } + bool isSourceNonReproducible() const { return IsSourceNonReproducible; } + void SetSuppressIncludeNotFoundError(bool Suppress) { SuppressIncludeNotFoundError = Suppress; } @@ -1312,6 +1322,11 @@ class Preprocessor { } /// \} + PPCachedActions *getPPCachedActions() const { return CachedActions.get(); } + void setPPCachedActions(std::unique_ptr CA) { + CachedActions = std::move(CA); + } + /// Get the number of tokens processed so far. unsigned getTokenCount() const { return TokenCount; } @@ -2431,6 +2446,28 @@ class Preprocessor { !IsAtImport; } +private: + /// The include tree that is being built, if any. + /// See \c FrontendOptions::CASIncludeTreeID. + std::optional CASIncludeTreeID; + + /// The cas-fs tree that is being built, if any. + /// See \c FileSystemOptions::CASFileSystemRootID. + std::optional CASFileSystemRootID; + +public: + std::optional getCASIncludeTreeID() const { + return CASIncludeTreeID; + } + void setCASIncludeTreeID(std::string ID) { CASIncludeTreeID = std::move(ID); } + + std::optional getCASFileSystemRootID() const { + return CASFileSystemRootID; + } + void setCASFileSystemRootID(std::string ID) { + CASFileSystemRootID = std::move(ID); + } + /// Allocate a new MacroInfo object with the provided SourceLocation. MacroInfo *AllocateMacroInfo(SourceLocation L); @@ -2989,6 +3026,11 @@ class Preprocessor { } LoadedSafeBufferOptOutMap; public: + // FIXME: The result of saving Safe Buffers opt-out regions in PP is that + // clang needs to check two places---PP and DiagnosticsEngine, in order to + // confirm if C++ Safe Buffers is enabled at a location. This might not be + // ideal as developers can forget to check one of the places. + // /// \return true iff the given `Loc` is in a "-Wunsafe-buffer-usage" opt-out /// region. This `Loc` must be a source location that has been pre-processed. bool isSafeBufferOptOut(const SourceManager&SourceMgr, const SourceLocation &Loc) const; diff --git a/clang/include/clang/Lex/PreprocessorOptions.h b/clang/include/clang/Lex/PreprocessorOptions.h index d4c4e1ccbf2c4..60ef8b7412c98 100644 --- a/clang/include/clang/Lex/PreprocessorOptions.h +++ b/clang/include/clang/Lex/PreprocessorOptions.h @@ -61,6 +61,18 @@ enum class DisableValidationForModuleKind { LLVM_MARK_AS_BITMASK_ENUM(Module) }; +/// Diagnostic options for caching related behaviors. +enum class CachingDiagKind { + /// Do not emit diagnosis for caching. + None = 0, + + /// Warning about nondeterministic caching. + Warning = 1, + + /// Error about nondeterministic caching. + Error = 2 +}; + /// PreprocessorOptions - This class is used for passing the various options /// used in preprocessor initialization to InitializePreprocessor(). class PreprocessorOptions { @@ -195,6 +207,9 @@ class PreprocessorOptions { /// Prevents intended crashes when using #pragma clang __debug. For testing. bool DisablePragmaDebugCrash = false; + /// Should diagnose caching related issues. + CachingDiagKind CachingDiagOption = CachingDiagKind::None; + /// If set, the UNIX timestamp specified by SOURCE_DATE_EPOCH. std::optional SourceDateEpoch; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index c30fe7c04e28d..203574a4c30fa 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1462,10 +1462,12 @@ class Parser : public CodeCompletionHandler { IdentifierInfo *MacroII = nullptr; SourceLocation AttrNameLoc; SmallVector Decls; + // TO_UPSTREAM(BoundsSafety) + unsigned NestedTypeLevel; explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name, - SourceLocation Loc) - : Self(P), AttrName(Name), AttrNameLoc(Loc) {} + SourceLocation Loc, unsigned Level = 0) + : Self(P), AttrName(Name), AttrNameLoc(Loc), NestedTypeLevel(Level) {} void ParseLexedAttributes() override; @@ -1770,7 +1772,9 @@ class Parser : public CodeCompletionHandler { void SkipFunctionBody(); Decl *ParseFunctionDefinition(ParsingDeclarator &D, const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(), - LateParsedAttrList *LateParsedAttrs = nullptr); + LateParsedAttrList *LateParsedAttrs = nullptr, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *BoundsSafetyLateAttrs = nullptr); void ParseKNRParamDeclarations(Declarator &D); // EndLoc is filled with the location of the last token of the simple-asm. ExprResult ParseSimpleAsm(bool ForAsmLabel, SourceLocation *EndLoc); @@ -2067,6 +2071,36 @@ class Parser : public CodeCompletionHandler { /// Parse a __builtin_bit_cast(T, E), used to implement C++2a std::bit_cast. ExprResult ParseBuiltinBitCast(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_unsafe_forge_bidi_indexable(expr, size) + ExprResult ParseUnsafeForgeBidiIndexable(); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_unsafe_forge_single(expr) + ExprResult ParseUnsafeForgeSingle(); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_unsafe_forge_terminated_by(expr, terminator) + ExprResult ParseUnsafeForgeTerminatedBy(); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_get_pointer_lower_bound(expr) + enum PointerBoundKind { PBK_Lower, PBK_Upper }; + ExprResult ParseGetPointerBound(PointerBoundKind K); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: + /// __builtin_terminated_by_to_indexable(pointer [, terminator]) + /// __builtin_unsafe_terminated_by_to_indexable(pointer [, terminator]) + ExprResult ParseTerminatedByToIndexable(bool Unsafe); + + //===--------------------------------------------------------------------===// + /// BoundsSafety: __builtin_unsafe_terminated_by_from_indexable(terminator, + /// pointer [, pointer-to-terminator]) + ExprResult ParseUnsafeTerminatedByFromIndexable(); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + //===--------------------------------------------------------------------===// // C++ 5.2p1: C++ Type Identification ExprResult ParseCXXTypeid(); @@ -2517,7 +2551,8 @@ class Parser : public CodeCompletionHandler { ParsedAttributes &Attrs, ParsedTemplateInfo &TemplateInfo, SourceLocation *DeclEnd = nullptr, - ForRangeInit *FRI = nullptr); + ForRangeInit *FRI = nullptr, + LateParsedAttrList *BoundsSafetyLateAttrs = nullptr); Decl *ParseDeclarationAfterDeclarator(Declarator &D, const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo()); bool ParseAsmAttributesAfterDeclarator(Declarator &D); @@ -2564,14 +2599,19 @@ class Parser : public CodeCompletionHandler { void ParseSpecifierQualifierList( DeclSpec &DS, AccessSpecifier AS = AS_none, - DeclSpecContext DSC = DeclSpecContext::DSC_normal) { - ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC); + DeclSpecContext DSC = DeclSpecContext::DSC_normal, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr) { + ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC, + LateAttrs); } void ParseSpecifierQualifierList( DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename, AccessSpecifier AS = AS_none, - DeclSpecContext DSC = DeclSpecContext::DSC_normal); + DeclSpecContext DSC = DeclSpecContext::DSC_normal, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr); void ParseObjCTypeQualifierList(ObjCDeclSpec &DS, DeclaratorContext Context); @@ -2819,7 +2859,9 @@ class Parser : public CodeCompletionHandler { ParseTypeName(SourceRange *Range = nullptr, DeclaratorContext Context = DeclaratorContext::TypeName, AccessSpecifier AS = AS_none, Decl **OwnedType = nullptr, - ParsedAttributes *Attrs = nullptr); + ParsedAttributes *Attrs = nullptr, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr); private: void ParseBlockId(SourceLocation CaretLoc); @@ -2978,11 +3020,15 @@ class Parser : public CodeCompletionHandler { void ParseGNUAttributes(ParsedAttributes &Attrs, LateParsedAttrList *LateAttrs = nullptr, Declarator *D = nullptr); + /* TO_UPSTREAM(BoundsSafety) ON */ + // NestedTypeLevel is not a parameter in upstream void ParseGNUAttributeArgs(IdentifierInfo *AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, SourceLocation ScopeLoc, - ParsedAttr::Form Form, Declarator *D); + ParsedAttr::Form Form, Declarator *D, + size_t NestedTypeLevel=0); + /* TO_UPSTREAM(BoundsSafety) OFF */ IdentifierLoc *ParseIdentifierLoc(); unsigned @@ -3110,10 +3156,18 @@ class Parser : public CodeCompletionHandler { void ParseOpenCLQualifiers(ParsedAttributes &Attrs); void ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs); void ParseCUDAFunctionAttributes(ParsedAttributes &attrs); + // TO_UPSTREAM(BoundsSafety) + void ParseBoundsSafetyTypeSpecifiers(ParsedAttributes &attrs); bool isHLSLQualifier(const Token &Tok) const; void ParseHLSLQualifiers(ParsedAttributes &Attrs); VersionTuple ParseVersionTuple(SourceRange &Range); + void ParseFeatureAvailabilityAttribute( + IdentifierInfo &Availability, SourceLocation AvailabilityLoc, + ParsedAttributes &attrs, SourceLocation *endLoc, + IdentifierInfo *ScopeName, SourceLocation ScopeLoc, ParsedAttr::Form Form, + BalancedDelimiterTracker &T); + void ParseAvailabilityAttribute(IdentifierInfo &Availability, SourceLocation AvailabilityLoc, ParsedAttributes &attrs, @@ -3164,19 +3218,27 @@ class Parser : public CodeCompletionHandler { SourceLocation ScopeLoc, ParsedAttr::Form Form); - void DistributeCLateParsedAttrs(Decl *Dcl, LateParsedAttrList *LateAttrs); + /* TO_UPSTREAM(BoundsSafety) ON */ + // Upstream doesn't have the `Declarator` parameter + void DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl, + LateParsedAttrList *LateAttrs); + /* TO_UPSTREAM(BoundsSafety) OFF */ void ParseBoundsAttribute(IdentifierInfo &AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, IdentifierInfo *ScopeName, SourceLocation ScopeLoc, - ParsedAttr::Form Form); + ParsedAttr::Form Form, + // TO_UPSTREAM(BoundsSafety) + unsigned NestedTypeLevel = 0); void ParseTypeofSpecifier(DeclSpec &DS); SourceLocation ParseDecltypeSpecifier(DeclSpec &DS); void AnnotateExistingDecltypeSpecifier(const DeclSpec &DS, SourceLocation StartLoc, SourceLocation EndLoc); - void ParseAtomicSpecifier(DeclSpec &DS); + void ParseAtomicSpecifier(DeclSpec &DS, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr); ExprResult ParseAlignArgument(StringRef KWName, SourceLocation Start, SourceLocation &EllipsisLoc, bool &IsType, @@ -3254,7 +3316,9 @@ class Parser : public CodeCompletionHandler { DeclSpec &DS, unsigned AttrReqs = AR_AllAttributesParsed, bool AtomicAllowed = true, bool IdentifierRequired = false, std::optional> CodeCompletionHandler = - std::nullopt); + std::nullopt, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs = nullptr); void ParseDirectDeclarator(Declarator &D); void ParseDecompositionDeclarator(Declarator &D); void ParseParenDeclarator(Declarator &D); @@ -3273,16 +3337,21 @@ class Parser : public CodeCompletionHandler { void ParseParameterDeclarationClause( Declarator &D, ParsedAttributes &attrs, SmallVectorImpl &ParamInfo, - SourceLocation &EllipsisLoc) { + SourceLocation &EllipsisLoc, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateParamAttrs = nullptr) { return ParseParameterDeclarationClause( D.getContext(), attrs, ParamInfo, EllipsisLoc, D.getCXXScopeSpec().isSet() && - D.isFunctionDeclaratorAFunctionDeclaration()); + D.isFunctionDeclaratorAFunctionDeclaration(), + LateParamAttrs); } void ParseParameterDeclarationClause( DeclaratorContext DeclaratorContext, ParsedAttributes &attrs, SmallVectorImpl &ParamInfo, - SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration = false); + SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration = false, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateParamAttrs = nullptr); void ParseBracketDeclarator(Declarator &D); void ParseMisplacedBracketDeclarator(Declarator &D); @@ -3382,10 +3451,9 @@ class Parser : public CodeCompletionHandler { AccessSpecifier AS, ParsedAttributes &Attr, ParsedTemplateInfo &TemplateInfo, ParsingDeclRAIIObject *DiagsFromTParams = nullptr); - DeclGroupPtrTy - ParseCXXClassMemberDeclarationWithPragmas(AccessSpecifier &AS, - ParsedAttributes &AccessAttrs, - DeclSpec::TST TagType, Decl *Tag); + DeclGroupPtrTy ParseCXXClassMemberDeclarationWithPragmas( + AccessSpecifier &AS, ParsedAttributes &AccessAttrs, DeclSpec::TST TagType, + Decl *Tag); void ParseConstructorInitializer(Decl *ConstructorDecl); MemInitResult ParseMemInitializer(Decl *ConstructorDecl); void HandleMemberFunctionDeclDelays(Declarator& DeclaratorInfo, diff --git a/clang/include/clang/Sema/BoundsSafetySuggestions.h b/clang/include/clang/Sema/BoundsSafetySuggestions.h new file mode 100644 index 0000000000000..037b0f0b0684c --- /dev/null +++ b/clang/include/clang/Sema/BoundsSafetySuggestions.h @@ -0,0 +1,147 @@ +/* TO_UPSTREAM(BoundsSafety) ON */ +//===- BoundsSafetySuggestions.h - -fbounds-safety suggestions --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a collection of analyses that aid adoption of +// -fbounds-safety annotations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SEMA_BOUNDSSAFETYSUGGESTIONS_H +#define LLVM_CLANG_SEMA_BOUNDSSAFETYSUGGESTIONS_H + +#include "clang/AST/Type.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/ArrayRef.h" +namespace clang { + +// Forward-declare to reduce bloat. +class ASTContext; +class Decl; +class Expr; +class FieldDecl; +class FunctionDecl; +class NamedDecl; +class ParmVarDecl; +class Sema; +class Stmt; +class VarDecl; +// An abstract interface to the bounds safety suggestion machine. +class BoundsSafetySuggestionHandler { +public: + enum class UnsafeOpKind { + Index, + Arithmetic, + Deref, + MemberAccess, + Assignment, + Return, + CallArg, + Cast + }; + + enum class AssignmentSourceKind { + Parameter = 0, + GlobalVar, + LocalVar, + FunctionCallReturnValue, + ArrayElement, + StructMember, + UnionMember, + }; + + enum class WillTrapKind { + NoTrap, + Unknown, + Trap, + TrapIffPtrNotNull, + TrapIffPtrNull + }; + + enum class PtrArithOOBKind { + NEVER_OOB = 0, + ALWAYS_OOB_BASE_OOB, + ALWAYS_OOB_CONSTANT_OFFSET, + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET, + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET_OR_LT_ZERO, + OOB_IF_EFFECTIVE_OFFSET_LT_ZERO, + UNKNOWN + }; + + struct SingleEntity { + AssignmentSourceKind Kind; + const NamedDecl *Entity; + const Expr *AssignmentExpr; + + // The bounds of the __bidi_indexable will store the bounds of a single + // `SinglePointeeTy`. Not necessarily the same as the + // `AssignmentExpr->getType()` due to additional casts. + const QualType SinglePointeeTy; + }; + + /// Callback for when analysis in UnsafeOperationVisitor detects that an + /// implicitly __bidi_indexable local variable (that participates in + /// potentially unsafe pointer arithmetic or indexing) is initialized or + /// assigned from a an entity that is a __single pointer. + virtual void handleSingleEntitiesFlowingToIndexableVariableIndexOrPtrArith( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + PtrArithOOBKind IsOOB, size_t MinimumPtrArithOOBOffset) = 0; + + /// Callback for when analysis in UnsafeOperationVisitor detects that an + /// implicitly __bidi_indexable local variable (that participates in + /// an unsafe buffer operation that accesses the 0th element) is initialized + /// or assigned from an entity that is a __single pointer. + virtual void handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, + const Expr *Operand, UnsafeOpKind Kind) = 0; + + /// Callback for when analysis in UnsafeOperationVisitor detects that an + /// implicitly __bidi_indexable local variable (that is + /// "unsafely cast") is initialized + /// or assigned from an entity that is a __single pointer. + /// + /// The following types of cast are considered unsafe: + /// + /// 1. `__bidi_indexable` -> `__single` (CK_BoundsSafetyPointerCast) where + /// `local` has insufficient bounds to allow access to a single element of the + /// pointee type. + /// + /// 2. Bit cast where the pointee type of the pointer (CK_BitCast) is changed + /// to a larger type than `local` has the bounds for. I.e. the resulting + /// pointer is an out-of-bounds pointer. + virtual void handleSingleEntitiesFlowingToIndexableVariableUnsafelyCasted( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + const Expr *Operand) = 0; + + virtual void handleSingleEntitiesFlowingToIndexableDynamicCountConversion( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + const Expr *Operand, const QualType DCPT, WillTrapKind WillTrap, + std::optional ConstantCount, size_t MaxSafeSizeOrCount) = 0; + + // Always provide a virtual destructor! + virtual ~BoundsSafetySuggestionHandler() = default; +}; + +void checkBoundsSafetySuggestions(const Decl *D, + BoundsSafetySuggestionHandler &H, Sema &S); + +inline const StreamingDiagnostic & +operator<<(const StreamingDiagnostic &PD, + const BoundsSafetySuggestionHandler::AssignmentSourceKind Kind) { + PD.AddTaggedVal(static_cast(Kind), DiagnosticsEngine::ak_uint); + return PD; +} + +} // namespace clang + +#endif /* LLVM_CLANG_SEMA_BOUNDSSAFETYSUGGESTIONS_H */ +/* TO_UPSTREAM(BoundsSafety) OFF */ diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 6c4a32c4ac2f0..2948a31b5bf1b 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1267,6 +1267,20 @@ struct DeclaratorChunk { ParsedAttributesView AttrList; + /* TO_UPSTREAM(BoundsSafety) ON */ + struct LateParsedAttrInfo { + CachedTokens Toks; + IdentifierInfo &AttrName; + IdentifierInfo *MacroII = nullptr; + SourceLocation AttrNameLoc; + + explicit LateParsedAttrInfo(CachedTokens Toks, IdentifierInfo &AttrName, + IdentifierInfo *MacroII, + SourceLocation AttrNameLoc) + : Toks(Toks), AttrName(AttrName), MacroII(MacroII), AttrNameLoc(AttrNameLoc) {} + }; + /* TO_UPSTREAM(BoundsSafety) OFF */ + struct PointerTypeInfo { /// The type qualifiers: const/volatile/restrict/unaligned/atomic. LLVM_PREFERRED_TYPE(DeclSpec::TQ) @@ -1287,8 +1301,26 @@ struct DeclaratorChunk { /// The location of the __unaligned-qualifier, if any. SourceLocation UnalignedQualLoc; + /* TO_UPSTREAM(BoundsSafety) ON */ + // LateParsedAttrInfo objects and their count. + unsigned NumLateParsedAttrs; + LateParsedAttrInfo **LateAttrInfos; + /* TO_UPSTREAM(BoundsSafety) OFF */ + void destroy() { + /* TO_UPSTREAM(BoundsSafety) ON */ + for (unsigned i = 0; i < NumLateParsedAttrs; ++i) + delete LateAttrInfos[i]; + if (NumLateParsedAttrs != 0) + delete[] LateAttrInfos; + /* TO_UPSTREAM(BoundsSafety) OFF */ + } + + /* TO_UPSTREAM(BoundsSafety) ON */ + ArrayRef getLateParsedAttrInfos() const { + return {LateAttrInfos, NumLateParsedAttrs}; } + /* TO_UPSTREAM(BoundsSafety) OFF */ }; struct ReferenceTypeInfo { @@ -1319,7 +1351,26 @@ struct DeclaratorChunk { /// expression class on all clients, NumElts is untyped. Expr *NumElts; - void destroy() {} + /* TO_UPSTREAM(BoundsSafety) ON */ + // LateParsedAttrInfo objects and their count. + unsigned NumLateParsedAttrs; + LateParsedAttrInfo **LateAttrInfos; + /* TO_UPSTREAM(BoundsSafety) OFF */ + + void destroy() { + /* TO_UPSTREAM(BoundsSafety) ON */ + for (unsigned i = 0; i < NumLateParsedAttrs; ++i) + delete LateAttrInfos[i]; + if (NumLateParsedAttrs != 0) + delete[] LateAttrInfos; + /* TO_UPSTREAM(BoundsSafety) OFF */ + } + + /* TO_UPSTREAM(BoundsSafety) ON */ + ArrayRef getLateParsedAttrInfos() const { + return {LateAttrInfos, NumLateParsedAttrs}; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ }; /// ParamInfo - An array of paraminfo objects is allocated whenever a function @@ -1668,7 +1719,9 @@ struct DeclaratorChunk { SourceLocation VolatileQualLoc, SourceLocation RestrictQualLoc, SourceLocation AtomicQualLoc, - SourceLocation UnalignedQualLoc) { + SourceLocation UnalignedQualLoc, + // TO_UPSTREAM(BoundsSafety) + ArrayRef LateAttrInfos = {}) { DeclaratorChunk I; I.Kind = Pointer; I.Loc = Loc; @@ -1679,6 +1732,14 @@ struct DeclaratorChunk { I.Ptr.RestrictQualLoc = RestrictQualLoc; I.Ptr.AtomicQualLoc = AtomicQualLoc; I.Ptr.UnalignedQualLoc = UnalignedQualLoc; + /* TO_UPSTREAM(BoundsSafety) ON */ + I.Ptr.NumLateParsedAttrs = LateAttrInfos.size(); + if (!LateAttrInfos.empty()) { + I.Ptr.LateAttrInfos = new LateParsedAttrInfo *[LateAttrInfos.size()]; + for (size_t J = 0; J < LateAttrInfos.size(); ++J) + I.Ptr.LateAttrInfos[J] = LateAttrInfos[J]; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ return I; } @@ -1696,7 +1757,9 @@ struct DeclaratorChunk { /// Return a DeclaratorChunk for an array. static DeclaratorChunk getArray(unsigned TypeQuals, bool isStatic, bool isStar, Expr *NumElts, - SourceLocation LBLoc, SourceLocation RBLoc) { + SourceLocation LBLoc, SourceLocation RBLoc, + // TO_UPSTREAM(BoundsSafety) + ArrayRef LateAttrInfos = {}) { DeclaratorChunk I; I.Kind = Array; I.Loc = LBLoc; @@ -1705,6 +1768,14 @@ struct DeclaratorChunk { I.Arr.hasStatic = isStatic; I.Arr.isStar = isStar; I.Arr.NumElts = NumElts; + /* TO_UPSTREAM(BoundsSafety) ON */ + I.Arr.NumLateParsedAttrs = LateAttrInfos.size(); + if (!LateAttrInfos.empty()) { + I.Arr.LateAttrInfos = new LateParsedAttrInfo *[LateAttrInfos.size()]; + for (size_t J = 0; J < LateAttrInfos.size(); ++J) + I.Arr.LateAttrInfos[J] = LateAttrInfos[J]; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ return I; } @@ -2407,6 +2478,21 @@ class Declarator { return DeclTypeInfo[i]; } + /*TO_UPSTREAM(BoundsSafety) ON*/ + /// Add all DeclaratorChunks from Other to the end of this declarator, and + /// remove them from Other. This transfers ownership of the DeclaratorChunks + /// and their attributes to this object. + void TakeTypeObjects(Declarator &Other) { + DeclTypeInfo.append(Other.DeclTypeInfo); // Keep the ordering + while (!Other.DeclTypeInfo.empty()) { + // Remove without calling destroy(), so it won't be destroyed when + // calling the destructor on Other + DeclaratorChunk Removed = Other.DeclTypeInfo.pop_back_val(); + getAttributePool().takeFrom(Removed.getAttrs(), Other.getAttributePool()); + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + typedef SmallVectorImpl::const_iterator type_object_iterator; typedef llvm::iterator_range type_object_range; diff --git a/clang/include/clang/Sema/DelayedDiagnostic.h b/clang/include/clang/Sema/DelayedDiagnostic.h index 0105089a393f1..93ecae3529231 100644 --- a/clang/include/clang/Sema/DelayedDiagnostic.h +++ b/clang/include/clang/Sema/DelayedDiagnostic.h @@ -125,7 +125,12 @@ class AccessedEntity { /// the complete parsing of the current declaration. class DelayedDiagnostic { public: - enum DDKind : unsigned char { Availability, Access, ForbiddenType }; + enum DDKind : unsigned char { + Availability, + FeatureAvailability, + Access, + ForbiddenType + }; DDKind Kind; bool Triggered; @@ -143,6 +148,9 @@ class DelayedDiagnostic { StringRef Msg, bool ObjCPropertyAccess); + static DelayedDiagnostic + makeFeatureAvailability(NamedDecl *D, ArrayRef Locs); + static DelayedDiagnostic makeAccess(SourceLocation Loc, const AccessedEntity &Entity) { DelayedDiagnostic DD; @@ -201,6 +209,12 @@ class DelayedDiagnostic { return AvailabilityData.AR; } + const NamedDecl *getFeatureAvailabilityDecl() const { + assert(Kind == FeatureAvailability && + "Not a feature availability diagnostic."); + return FeatureAvailabilityData.Decl; + } + /// The diagnostic ID to emit. Used like so: /// Diag(diag.Loc, diag.getForbiddenTypeDiagnostic()) /// << diag.getForbiddenTypeOperand() @@ -246,6 +260,10 @@ class DelayedDiagnostic { bool ObjCPropertyAccess; }; + struct FAD { + const NamedDecl *Decl; + }; + struct FTD { unsigned Diagnostic; unsigned Argument; @@ -254,6 +272,7 @@ class DelayedDiagnostic { union { struct AD AvailabilityData; + struct FAD FeatureAvailabilityData; struct FTD ForbiddenTypeData; /// Access control. diff --git a/clang/include/clang/Sema/DependentASTVisitor.h b/clang/include/clang/Sema/DependentASTVisitor.h new file mode 100644 index 0000000000000..dd225b2955187 --- /dev/null +++ b/clang/include/clang/Sema/DependentASTVisitor.h @@ -0,0 +1,92 @@ +//===--- DependentASTVisitor.h - Helper for dependent nodes -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the DependentASTVisitor RecursiveASTVisitor layer, which +// is responsible for visiting unresolved symbol references. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_DEPENDENT_SEMA_VISITOR_H +#define LLVM_CLANG_AST_DEPENDENT_SEMA_VISITOR_H + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Type.h" +#include "clang/Sema/HeuristicResolver.h" + +namespace clang { + +// TODO: Use in the indexer. +template +class DependentASTVisitor : public RecursiveASTVisitor { +private: + bool visitDependentReference( + const Type *T, const DeclarationName &Name, SourceLocation Loc, + llvm::function_ref Filter) { + if (!T) + return true; + const TemplateSpecializationType *TST = + T->getAs(); + if (!TST) + return true; + TemplateName TN = TST->getTemplateName(); + const ClassTemplateDecl *TD = + dyn_cast_or_null(TN.getAsTemplateDecl()); + if (!TD) + return true; + CXXRecordDecl *RD = TD->getTemplatedDecl(); + if (!RD->hasDefinition()) + return true; + RD = RD->getDefinition(); + std::vector Symbols = + HeuristicResolver(RD->getASTContext()) + .lookupDependentName(RD, Name, Filter); + // FIXME: Improve overload handling. + if (Symbols.size() != 1) + return true; + if (Loc.isInvalid()) + return true; + return RecursiveASTVisitor::getDerived() + .VisitDependentSymbolReference(Symbols[0], Loc); + } + +public: + bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) { + const DeclarationNameInfo &Info = E->getMemberNameInfo(); + return visitDependentReference( + E->getBaseType().getTypePtrOrNull(), Info.getName(), Info.getLoc(), + [](const NamedDecl *D) { return D->isCXXInstanceMember(); }); + } + + bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) { + const DeclarationNameInfo &Info = E->getNameInfo(); + const NestedNameSpecifier *NNS = E->getQualifier(); + return visitDependentReference( + NNS->getAsType(), Info.getName(), Info.getLoc(), + [](const NamedDecl *D) { return !D->isCXXInstanceMember(); }); + } + + bool VisitDependentNameTypeLoc(DependentNameTypeLoc TL) { + const DependentNameType *DNT = TL.getTypePtr(); + const NestedNameSpecifier *NNS = DNT->getQualifier(); + DeclarationName Name(DNT->getIdentifier()); + return visitDependentReference( + NNS->getAsType(), Name, TL.getNameLoc(), + [](const NamedDecl *ND) { return isa(ND); }); + } + + bool VisitDependentSymbolReference(const NamedDecl *Symbol, + SourceLocation SymbolNameLoc) { + return true; + } +}; + +} // end namespace clang + +#endif // LLVM_CLANG_AST_DEPENDENT_SEMA_VISITOR_H diff --git a/clang/include/clang/Sema/DynamicCountPointerAssignmentAnalysisExported.h b/clang/include/clang/Sema/DynamicCountPointerAssignmentAnalysisExported.h new file mode 100644 index 0000000000000..f1a64f01911a2 --- /dev/null +++ b/clang/include/clang/Sema/DynamicCountPointerAssignmentAnalysisExported.h @@ -0,0 +1,31 @@ +//===--- DynamicCountPointerAssignmentAnalysisExported.h --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file exports DynamicCountPointer Analysis utilities to other libraries. +// +// This is a workaround necessitated in the fact that Sema/TreeTransform.h is a +// private header file (not in the include path that other libraries can use). +// The "right" solution here would be to move the header so it is public but +// that would create a conflict with upstream which is really not desirable. +// +// So instead this file exports a really simple interface that hides the +// `TreeTransform` type (and its dependencies) +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_SEMA_DYNAMIC_COUNT_POINTER_ASSIGNMENT_ANALYSIS_EXPORTED_H +#define LLVM_CLANG_SEMA_DYNAMIC_COUNT_POINTER_ASSIGNMENT_ANALYSIS_EXPORTED_H +#include "clang/Sema/Ownership.h" +#include "clang/Sema/Sema.h" + +namespace clang { + +ExprResult ReplaceCountExprParamsWithArgsFromCall(const Expr *CountExpr, + const CallExpr *CE, Sema &S); + +} + +#endif diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h index 428d3111de80d..8b6b273373bed 100644 --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -181,6 +181,10 @@ class ParsedAttr final LLVM_PREFERRED_TYPE(bool) mutable unsigned IsPragmaClangAttribute : 1; + /// Determines if the attribute will be printed as macro in the diagnostics. + LLVM_PREFERRED_TYPE(bool) + mutable unsigned PrintMacroName : 1; + /// The location of the 'unavailable' keyword in an /// availability attribute. SourceLocation UnavailableLoc; @@ -215,7 +219,7 @@ class ParsedAttr final UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), HasProcessingCache(false), IsPragmaClangAttribute(false), - Info(ParsedAttrInfo::get(*this)) { + PrintMacroName(false), Info(ParsedAttrInfo::get(*this)) { if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion)); } @@ -232,8 +236,8 @@ class ParsedAttr final NumArgs(1), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), HasProcessingCache(false), IsPragmaClangAttribute(false), - UnavailableLoc(unavailable), MessageExpr(messageExpr), - Info(ParsedAttrInfo::get(*this)) { + PrintMacroName(false), UnavailableLoc(unavailable), + MessageExpr(messageExpr), Info(ParsedAttrInfo::get(*this)) { ArgsUnion PVal(Parm); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); new (getAvailabilityData()) @@ -250,7 +254,8 @@ class ParsedAttr final NumArgs(3), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), HasProcessingCache(false), - IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) { + IsPragmaClangAttribute(false), PrintMacroName(false), + Info(ParsedAttrInfo::get(*this)) { ArgsUnion *Args = getArgsBuffer(); Args[0] = Parm1; Args[1] = Parm2; @@ -266,7 +271,8 @@ class ParsedAttr final NumArgs(1), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(true), IsProperty(false), HasParsedType(false), HasProcessingCache(false), - IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) { + IsPragmaClangAttribute(false), PrintMacroName(false), + Info(ParsedAttrInfo::get(*this)) { ArgsUnion PVal(ArgKind); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); detail::TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot(); @@ -284,7 +290,7 @@ class ParsedAttr final UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(true), HasProcessingCache(false), IsPragmaClangAttribute(false), - Info(ParsedAttrInfo::get(*this)) { + PrintMacroName(false), Info(ParsedAttrInfo::get(*this)) { new (&getTypeBuffer()) ParsedType(typeArg); } @@ -296,7 +302,8 @@ class ParsedAttr final NumArgs(0), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(true), HasParsedType(false), HasProcessingCache(false), - IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) { + IsPragmaClangAttribute(false), PrintMacroName(false), + Info(ParsedAttrInfo::get(*this)) { new (&getPropertyDataBuffer()) detail::PropertyData(getterId, setterId); } @@ -346,6 +353,8 @@ class ParsedAttr final return IsProperty; } + bool isAvailabilityAttribute() const { return IsAvailability; } + bool isInvalid() const { return Invalid; } void setInvalid(bool b = true) const { Invalid = b; } @@ -481,9 +490,11 @@ class ParsedAttr final /// Set the macro identifier info object that this parsed attribute was /// declared in if it was declared in a macro. Also set the expansion location /// of the macro. - void setMacroIdentifier(IdentifierInfo *MacroName, SourceLocation Loc) { + void setMacroIdentifier(IdentifierInfo *MacroName, SourceLocation Loc, + bool PrintMcrName) { MacroII = MacroName; MacroExpansionLoc = Loc; + PrintMacroName = PrintMcrName; } /// Returns true if this attribute was declared in a macro. @@ -499,6 +510,12 @@ class ParsedAttr final return MacroExpansionLoc; } + bool printMacroName() const { + if (!hasMacroIdentifier()) + return false; + return PrintMacroName; + } + /// Check if the attribute has exactly as many args as Num. May output an /// error. Returns false if a diagnostic is produced. bool checkExactlyNumArgs(class Sema &S, unsigned Num) const; @@ -1109,16 +1126,16 @@ enum AttributeDeclKind { inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, const ParsedAttr &At) { - DB.AddTaggedVal(reinterpret_cast(At.getAttrName()), + const IdentifierInfo *AttrName = + At.printMacroName() ? At.getMacroIdentifier() : At.getAttrName(); + DB.AddTaggedVal(reinterpret_cast(AttrName), DiagnosticsEngine::ak_identifierinfo); return DB; } inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB, const ParsedAttr *At) { - DB.AddTaggedVal(reinterpret_cast(At->getAttrName()), - DiagnosticsEngine::ak_identifierinfo); - return DB; + return DB << *At; } /// AttributeCommonInfo has a non-explicit constructor which takes an diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index 6bf9ae8d074fb..7cc5a8c61bc6b 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -144,6 +144,8 @@ class FunctionScopeInfo { /// unavailable. bool HasPotentialAvailabilityViolations : 1; + bool HasPotentialFeatureAvailabilityViolations : 1; + /// A flag that is set when parsing a method that must call super's /// implementation, such as \c -dealloc, \c -finalize, or any method marked /// with \c __attribute__((objc_requires_super)). @@ -394,7 +396,8 @@ class FunctionScopeInfo { HasBranchIntoScope(false), HasIndirectGoto(false), HasMustTail(false), HasDroppedStmt(false), HasOMPDeclareReductionCombiner(false), HasFallthroughStmt(false), UsesFPIntrin(false), - HasPotentialAvailabilityViolations(false), ObjCShouldCallSuper(false), + HasPotentialAvailabilityViolations(false), + HasPotentialFeatureAvailabilityViolations(false), ObjCShouldCallSuper(false), ObjCIsDesignatedInit(false), ObjCWarnForNoDesignatedInitChain(false), ObjCIsSecondaryInit(false), ObjCWarnForNoInitDelegation(false), NeedsCoroutineSuspends(true), FoundImmediateEscalatingExpression(false), diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2c203e1ba4305..5f3ea04b56e48 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -193,6 +193,8 @@ class TypoCorrectionConsumer; class UnresolvedSetImpl; class UnresolvedSetIterator; class VisibleDeclConsumer; +// TO_UPSTREAM(BoundsSafety) +class CopyExpr; namespace sema { class BlockScopeInfo; @@ -349,6 +351,145 @@ class PreferredTypeBuilder { llvm::function_ref ComputeType; }; +/* TO_UPSTREAM(BoundsSafety) ON */ +/// Groups together the parameters to emit a bounds check. +class BoundsCheckBuilder { + // (the larger pointer we are slicing) + /// The wide pointer representing the range that is being sliced into. Either + /// it or both of WideLowerBound and WideUpperBound must be set. + Expr *WidePointerExpr; + + /// The lower bound of the range that is being sliced into. Either it and + /// WideUpperBound must be set, or WidePointerExpr must be set. + Expr *WideLowerBound; + + /// The upper bound of the range that is being sliced into. Either it and + /// WideLowerBound must be set, or WidePointerExpr must be set. + Expr *WideUpperBound; + + /// An index from the lower bound that is being accessed. At most one of it or + /// AccessStartIndex must be set. If neither is set, this is assumed to be + /// dereferencing the wide pointer. + Expr *AccessStartIndex; + + /// The start of the range being accessed and that is presumed to lie within + /// the wide pointer. At most one of it or AccessStartIndex must be set. If + /// neither is set, this is assumed to be dereferencing the wide pointer. + Expr *AccessLowerBound; + + /// The end of the range being accessed and that is presumed to lie within + /// the wide pointer. At most one of it or AccessCount must be set. If neither + /// is set, behaves as if AccessCount was the integer literal 1. + Expr *AccessUpperBound; + + /// The number of items being accessed, starting from AccessLowerBound. At + /// most one of it or AccessUpperBound must be set. If neither is set, behaves + /// as if AccessCount was the integer literal 1. + Expr *AccessCount; + + llvm::SmallVectorImpl &OVEs; + + /// If set to 1, AccessCount is a byte count. Must be 0 if AccessUpperBound is + /// set. + unsigned CountInBytes : 1; + /// If set to 1, the accessed bounds may be wider than the sliced bounds + /// (which are typically 0 when the sliced pointer is null). + unsigned OrNull : 1; + + OpaqueValueExpr *OpaqueWrap(Sema &S, Expr *E); + bool BuildIndexBounds(Sema &S, Expr *Min, Expr *Max, + llvm::SmallVectorImpl &Result); + bool BuildPtrBounds(Sema &S, Expr *Min, Expr *Max, + llvm::SmallVectorImpl &Result); + +public: + BoundsCheckBuilder(llvm::SmallVectorImpl &OVEs) + : WidePointerExpr(), WideLowerBound(), WideUpperBound(), + AccessStartIndex(), AccessLowerBound(), AccessUpperBound(), + AccessCount(), OVEs(OVEs), CountInBytes(0), OrNull(0) {} + + Expr *getWidePointer() { return WidePointerExpr; } + std::pair getWidePointerBounds() { + assert(!WideLowerBound == !WideUpperBound); + return std::make_pair(WideLowerBound, WideUpperBound); + } + + Expr *getAccessStartIndex() { return AccessStartIndex; } + Expr *getAccessLowerBound() { return AccessLowerBound; } + Expr *getAccessUpperBound() { return AccessUpperBound; } + Expr *getAccessCount() { return AccessCount; } + bool isCountInBytes() { assert(AccessCount); return CountInBytes; } + bool isOrNull() { return OrNull; } + + void setWidePointer(Expr *Wide) { + WidePointerExpr = Wide; + WideLowerBound = nullptr; + WideUpperBound = nullptr; + } + + void setWidePointer(Expr *Lower, Expr *Upper) { + WideLowerBound = Lower; + WideUpperBound = Upper; + WidePointerExpr = nullptr; + } + + void setAccessStartIndex(Expr *StartIndex) { + AccessStartIndex = StartIndex; + AccessLowerBound = nullptr; + } + + void setAccessLowerBound(Expr *Lower) { + AccessLowerBound = Lower; + AccessStartIndex = nullptr; + } + + void setAccessUpperBound(Expr *Upper) { + AccessUpperBound = Upper; + AccessCount = nullptr; + CountInBytes = 0; + OrNull = 0; + } + + void setAccessCount(Expr *Count, bool IsByteCount, bool IsOrNull) { + AccessCount = Count; + CountInBytes = IsByteCount; + OrNull = IsOrNull; + AccessUpperBound = nullptr; + } + + /// Create the bounds check expression. The value the bounds check evaluates + /// to when it succeeds will be GuardedValue. + ExprResult Build(Sema &S, Expr *GuardedValue); + + /// Create the bounds check expression to emit inequality `<=` checks with elements of \p Bounds in the order. + /// The function materializes elements of \p Bounds when they are reused and add the materialized values to + /// \p OVEs. + static ExprResult BuildLEChecks(Sema &S, SourceLocation Loc, + ArrayRef Bounds, + SmallVectorImpl &OVEs); + + /// Create the bounds check expression that verifies that the flexible array + /// member does not exceed the bounds of the enclosing pointer. + static ExprResult + CheckFlexibleArrayMemberSize(Sema &S, SourceLocation Loc, BoundsCheckKind BCK, + Expr *FAMPtr, CopyExpr *DeclReplacer = nullptr); + static ExprResult + CheckFlexibleArrayMemberSizeWithOVEs(Sema &S, SourceLocation Loc, + BoundsCheckKind BCK, Expr *FAMPtr, + SmallVectorImpl &OVEs, + CopyExpr *DeclReplacer = nullptr); + +private: + static ExprResult + CheckFlexibleArrayMemberSizeImpl(Sema &S, SourceLocation Loc, + BoundsCheckKind BCK, Expr *FAMPtr, + SmallVectorImpl &OVEs, + Expr *GuardedValue, CopyExpr *DeclReplacer); + + ExprResult BuildImplicitPointerArith(Sema &S, BinaryOperatorKind Opc, Expr *LHS, Expr *RHS); +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + struct SkipBodyInfo { SkipBodyInfo() = default; bool ShouldSkip = false; @@ -794,7 +935,9 @@ class Sema final : public SemaBase { ExprResult ImpCastExprToType( Expr *E, QualType Type, CastKind CK, ExprValueKind VK = VK_PRValue, const CXXCastPath *BasePath = nullptr, - CheckedConversionKind CCK = CheckedConversionKind::Implicit); + CheckedConversionKind CCK = CheckedConversionKind::Implicit, + // TO_UPSTREAM(BoundsSafety) + bool DiagnoseBoundsSafetyIncompleteArrayPromotion = true); /// ScalarTypeToBooleanCastKind - Returns the cast kind corresponding /// to the conversion from scalar type ScalarTy to the Boolean type. @@ -1447,6 +1590,18 @@ class Sema final : public SemaBase { /// Source location for newly created implicit MSInheritanceAttrs SourceLocation ImplicitMSInheritanceAttrLoc; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Default BoundsSafety pointer attributes for ABI-visible pointers. Applied to + /// ABI-visible pointers which do not have explicit BoundsSafety attributes. Can + /// be controlled with \#pragma clang abi_ptr_attr set(*ATTR*). Defaults to + /// __single in user code and __unsafe_indexable in system headers. + BoundsSafetyPointerAttributes CurPointerAbi; + + /// \return true iff `-Wunsafe-buffer-usage` is enabled for `Loc` and Bounds + /// Safety attribute-only mode is on. + bool isCXXSafeBuffersBoundsSafetyInteropEnabledAt(SourceLocation Loc) const; + /* TO_UPSTREAM(BoundsSafety) OFF */ + struct PragmaClangSection { std::string SectionName; bool Valid = false; @@ -2055,8 +2210,16 @@ class Sema final : public SemaBase { /// Issue any -Wunguarded-availability warnings in \c FD void DiagnoseUnguardedAvailabilityViolations(Decl *FD); + void DiagnoseUnguardedFeatureAvailabilityViolations(Decl *FD); + void handleDelayedAvailabilityCheck(sema::DelayedDiagnostic &DD, Decl *Ctx); + void handleDelayedFeatureAvailabilityCheck(sema::DelayedDiagnostic &DD, + Decl *Ctx); + + void DiagnoseFeatureAvailabilityOfDecl(NamedDecl *D, + ArrayRef Locs); + /// Retrieve the current function, if any, that should be analyzed for /// potential availability violations. sema::FunctionScopeInfo *getCurFunctionAvailabilityContext(); @@ -2144,6 +2307,80 @@ class Sema final : public SemaBase { /// /// \returns True iff no diagnostic where emitted, false otherwise. bool BoundsSafetyCheckUseOfCountAttrPtr(const Expr *E); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Perform Bounds Safety semantic checks on function parameters on a function + /// definition. This only performs checks that can be made by looking at + /// \param PVD in isolation (i.e. not looking at other parameters in the + /// function definition). + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckParamForFunctionDef(const ParmVarDecl *PVD); + + /// Perform Bounds Safety semantic check for CallExpr \param Call. + /// + /// \param FDecl For direct calls this should be the Function being called + /// by \param Call. This can be null. + /// \param Call The CallExpr to be checked. This cannot be null. + /// \param ProtoType For indirect calls this should be the FunctionProtoType + /// used to type check the call. This can be null. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckResolvedCall(FunctionDecl *FDecl, CallExpr *Call, + const FunctionProtoType *ProtoType); + + /// Perform Bounds Safety semantic checks on a function definition's return + /// type. + /// + /// \param FD The FunctionDecl whose return type will be checked. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckReturnTyForFunctionDef(FunctionDecl *FD); + + /// Perform Bounds Safety semantic checks on variable declaration \param VD. + /// + /// \param VD The VarDecl to check + /// \param CheckTentativeDefinitions If false and \param VD is a tentative + /// definition then checking is skipping and + /// the function returns true. If true then + /// checking of tentative definitions will + /// not be skipped. + /// + /// \returns True iff no diagnostic where emitted, false otherwise. + bool BoundsSafetyCheckVarDecl(const VarDecl *VD, + bool CheckTentativeDefinitions); + + // TODO: This can be moved back into SemaExpr.cpp as a static function once + // support for -fno-bounds-safety-bringup-missing-checks=indirect_count_update + // is removed (rdar://135833598). + bool BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp( + const CountAttributedType *CATTy, Expr *Operand, + std::variant OpInfo); + + + /// Used to record or retrieve if a VarDecl/FieldDecl has a BoundsSafety FixIt + /// emitted on it. The primary use case for this is preventing emitting + /// multiple FixIts on the same VarDecl/FieldDecl which can result in invalid + /// code. + /// + /// Note: This is not linked to the DiagnosticEngine in anyway. So callers + /// are required to manually maintain this information. + /// + /// \param DD - the VarDecl/FieldDecl to record or retrieve information about. + /// \param Set - If `Set` is `false` then this is a retrieval operation. The + /// method will return `true` if the `DD` already had a FixIt emitted and + /// `false` otherwise.. If `Set` is `true` then this is method records that + /// the `DD` had a FixIt emitted against it. The returned value will + /// always be `true` in this case. + /// + /// \returns `true` iff it was recorded that a FixIt was emitted for the + /// VarDecl `VD`. + /// + bool BoundsSafetyFixItWasEmittedFor(const DeclaratorDecl *DD, + bool Set = false); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + ///@} // @@ -2179,9 +2416,86 @@ class Sema final : public SemaBase { ExprResult Operand, SourceLocation RParenLoc); + /* TO_UPSTREAM(BoundsSafety) ON */ ExprResult BuildBuiltinBitCastExpr(SourceLocation KWLoc, TypeSourceInfo *TSI, Expr *Operand, SourceLocation RParenLoc); + ExprResult ActOnForgeBidiIndexable(SourceLocation KWLoc, Expr *Addr, + Expr *Size, SourceLocation RParenLoc); + + ExprResult ActOnForgeSingle(SourceLocation KWLoc, Expr *Addr, + SourceLocation RParenLoc); + + ExprResult ActOnForgeTerminatedBy(SourceLocation KWLoc, Expr *Addr, + Expr *Terminator, SourceLocation RParenLoc); + + ExprResult ActOnBoundsSafetyCall(ExprResult Call); + + ExprResult BuildForgePtrExpr(SourceLocation KWLoc, SourceLocation RParenLoc, + QualType ResultType, Expr *Addr, + Expr *Size = nullptr, Expr *Term = nullptr); + + /// Build a bounds check based on individual components. Each value of + /// Bounds is sequentially compared to be <= the next one. + ExprResult BuildBoundsCheckExpr(Expr *GuardedExpr, Expr *Cond, + ArrayRef CommonExprs); + + ExprResult BuildPredefinedBoundsCheckExpr(Expr *GuardedExpr, + BoundsCheckKind Kind, + ArrayRef CheckArgs); + + /// Build an expression that resolves to the lower bound of the pointer + /// represented by SubExpr. + ExprResult BuildLowerBoundExpr(Expr *SubExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc = SourceLocation(), + bool RawPointer = false); + + ExprResult ActOnGetLowerBound(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + /// Build an expression that resolves to the upper bound of the pointer + /// represented by SubExpr. + ExprResult BuildUpperBoundExpr(Expr *SubExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc = SourceLocation(), + bool RawPointer = false); + + ExprResult ActOnGetUpperBound(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult BuildMaterializeSequenceExpr(Expr *WrappedExpr, + ArrayRef Values); + + ExprResult BuildTerminatedByToIndexableExpr(Expr *PointerExpr, + Expr *TerminatorExpr, + bool IncludeTerminator, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult ActOnTerminatedByToIndexable(Expr *PointerExpr, + Expr *TerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult ActOnUnsafeTerminatedByToIndexable(Expr *PointerExpr, + Expr *TerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult BuildTerminatedByFromIndexableExpr(Expr *TerminatorExpr, + Expr *PointerExpr, + Expr *PointerToTerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + + ExprResult ActOnUnsafeTerminatedByFromIndexable(Expr *TerminatorExpr, + Expr *PointerExpr, + Expr *PointerToTerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc); + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Checks that reinterpret casts don't have undefined behavior. void CheckCompatibleReinterpretCast(QualType SrcType, QualType DestType, bool IsDereference, SourceRange Range); @@ -2776,6 +3090,9 @@ class Sema final : public SemaBase { /// Adds an expression to the set of gathered misaligned members. void AddPotentialMisalignedMembers(Expr *E, RecordDecl *RD, ValueDecl *MD, CharUnits Alignment); + + // TO_UPSTREAM(BoundsSafety) + llvm::SmallPtrSet BoundsSafetyDeclsWithFixIts; ///@} // @@ -4375,6 +4692,13 @@ class Sema final : public SemaBase { void deduceOpenCLAddressSpace(ValueDecl *decl); void deduceHLSLAddressSpace(VarDecl *decl); + /* TO_UPSTREAM(BoundsSafety) ON*/ + void deduceBoundsSafetyPointerTypes(ValueDecl *decl); + void deduceBoundsSafetyFunctionTypes(FunctionDecl *decl); + + QualType deduceCastPointerAttributes(QualType OrigTy, QualType OpTy); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Adjust the \c DeclContext for a function or variable that might be a /// function-local external declaration. static bool adjustContextForLocalExternDecl(DeclContext *&DC); @@ -4650,6 +4974,11 @@ class Sema final : public SemaBase { bool CheckAttrTarget(const ParsedAttr &CurrAttr); bool CheckAttrNoArgs(const ParsedAttr &CurrAttr); + void copyFeatureAvailability(Decl *Dst, Decl *Src); + + void copyFeatureAvailabilityCheck(Decl *Dst, NamedDecl *Src, + bool Redeclaration = false); + AvailabilityAttr *mergeAvailabilityAttr( NamedDecl *D, const AttributeCommonInfo &CI, IdentifierInfo *Platform, bool Implicit, VersionTuple Introduced, VersionTuple Deprecated, @@ -6945,6 +7274,11 @@ class Sema final : public SemaBase { bool UseArgumentDependentLookup(const CXXScopeSpec &SS, const LookupResult &R, bool HasTrailingLParen); + /* TO_UPSTREAM(BoundsSafety) ON*/ + ExprResult InstantiateDeclRefField(Expr *BaseExpr, bool IsArrow, + Expr *FieldRef); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// BuildQualifiedDeclarationNameExpr - Build a C++ qualified /// declaration name, generally during template instantiation. /// There's a large number of things which don't need to be done along @@ -7696,18 +8030,30 @@ class Sema final : public SemaBase { // DefaultFunctionArrayConversion - converts functions and arrays // to their respective pointers (C99 6.3.2.1). - ExprResult DefaultFunctionArrayConversion(Expr *E, bool Diagnose = true); + ExprResult DefaultFunctionArrayConversion( + Expr *E, bool Diagnose = true, + /* TO_UPSTREAM(BoundsSafety) ON */ + bool DiagnoseBoundsSafetyIncompleteArrayPromotion = true, + bool DisableFlexibleArrayPromotion = false); + /* TO_UPSTREAM(BoundsSafety) OFF */ // DefaultFunctionArrayLvalueConversion - converts functions and // arrays to their respective pointers and performs the // lvalue-to-rvalue conversion. - ExprResult DefaultFunctionArrayLvalueConversion(Expr *E, - bool Diagnose = true); + ExprResult DefaultFunctionArrayLvalueConversion( + Expr *E, bool Diagnose = true, + /* TO_UPSTREAM(BoundsSafety) ON */ + bool DiagnoseBoundsSafetyIncompleteArrayPromotion = true, + bool DisableFlexibleArrayPromotion = false); + /* TO_UPSTREAM(BoundsSafety) ON */ // DefaultLvalueConversion - performs lvalue-to-rvalue conversion on // the operand. This function is a no-op if the operand has a function type // or an array type. - ExprResult DefaultLvalueConversion(Expr *E); + ExprResult + DefaultLvalueConversion(Expr *E, + // TO_UPSTREAM(BoundsSafety) + bool DisableFlexibleArrayPromotion = false); // DefaultArgumentPromotion (C99 6.5.2.2p6). Used for function calls that // do not have a prototype. Integer promotions are performed on each @@ -7787,6 +8133,66 @@ class Sema final : public SemaBase { /// accept as an extension. IntToPointer, + /* TO_UPSTREAM(BoundsSafety) ON */ + /// IncompatibleIntToSafePointer - The assignment converts an int to a + /// -fbounds-safety safe pointer, which is not permitted by -fbounds-safety. + IncompatibleIntToSafePointer, + + /// IncompatibleUnsafeToSafePointer - The assignment converts an unsafe + /// pointer to a safe pointer (single or counted_by), which -fbounds-safety + /// doesn't allow. + IncompatibleUnsafeToSafePointer, + + /// IncompatibleUnsafeToIndexablePointer - The assignment converts an unsafe + /// pointer to an indexable pointer (indexable or bidi_indexable), + /// which -fbounds-safety doesn't allow and may not know how to create an upper + /// bound for. + IncompatibleUnsafeToIndexablePointer, + + // FIXME: Turn it into an error (rdar://85587619) + /// IncompleteSingleToIndexablePointer - The assignment converts an single + /// pointer with opaque element type to an indexable pointer, for which + /// -fbounds-safety + /// doesn't know how to create an upper bound. + IncompleteSingleToIndexablePointer, + + // CompatibleSingleToIndexablePointer - The assignment converts a single + // pointer to an explicitly indexable pointer. This is allowed but is likely + // a mistake. + CompatibleSingleToExplicitIndexablePointer, + + /// IncompatibleStringLiteralToValueTerminatedPointer - The assignment of a + /// string literal to value-terminated pointer with a terminator different + /// than NUL. + IncompatibleStringLiteralToValueTerminatedPointer, + + /// IncompatibleValueTerminatedTerminators - The assignment is between + /// value terminated pointers with incompatible terminators. + IncompatibleValueTerminatedTerminators, + + /// IncompatibleValueTerminatedToNonValueTerminatedPointer - The assignment + /// of a value-terminated pointer to a non-value-terminated pointer. + /// BoundsSafety doesn't allow it, __terminated_by_to_indexable() should be + /// used instead. + IncompatibleValueTerminatedToNonValueTerminatedPointer, + + /// IncompatibleNonValueTerminatedToValueTerminatedPointer - The assignment + /// of a non-value-terminated pointer to a value-terminated pointer. + /// BoundsSafety doesn't allow it, __unsafe_terminated_by_from_wide() should + /// be used instead. + IncompatibleNonValueTerminatedToValueTerminatedPointer, + + /// IncompatibleNestedValueTerminatedToNonValueTerminatedPointer - The + /// assignment is between two nested pointer types, where at a nested + /// level the source is value-terminated type, but the destination isn't. + IncompatibleNestedValueTerminatedToNonValueTerminatedPointer, + + /// IncompatibleNestedNonValueTerminatedToValueTerminatedPointer - The + /// assignment is between two nested pointer types, where at a nested + /// level the destination is value-terminated type, but the source isn't. + IncompatibleNestedNonValueTerminatedToValueTerminatedPointer, + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// FunctionVoidPointer - The assignment is between a function pointer and /// void*, which the standard doesn't allow, but we accept as an extension. FunctionVoidPointer, @@ -7833,6 +8239,18 @@ class Sema final : public SemaBase { /// extension. IncompatibleNestedPointerQualifiers, + /* TO_UPSTREAM(BoundsSafety) ON */ + /// IncompatibleNestedBoundsSafetyPointerAttributes - The assignment is + /// between two nested pointer types with different -fbounds-safety pointer + /// attributes. + IncompatibleNestedBoundsSafetyPointerAttributes, + + /// IncompatibleBoundsSafetyFunctionPointer - The assignment is + /// between two function pointer types with incompatible -fbounds-safety pointer + /// attributes. + IncompatibleBoundsSafetyFunctionPointer, + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// IncompatibleVectors - The assignment is between two vector types that /// have the same size, which we accept as an extension. IncompatibleVectors, @@ -7859,6 +8277,69 @@ class Sema final : public SemaBase { Incompatible }; + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// @brief Check whether it's ABI safe to convert between two types that would + /// otherwise be forbidden by -fbounds-safety. Returns false if \c ConvTy ... + /// - is not an incompatibility + /// - is not a -fbounds-safety specific incompatibility + /// - represents an incompatible conversion between pointers of different + /// widths + /// Otherwise returns true. + bool isCompatibleBoundsUnsafeAssignment(AssignConvertType ConvTy) const; + + /// @brief Check whether the conditions justify making an exception to + /// -fbounds-safety safety rules in system headers (since the threshold to fixing + /// errors is much higher there). In short the error should be in a system + /// header that hasn't (fully) adopted -fbounds-safety. NB: + /// If using this function to make the actual decision of ignoring the type + /// clash then \c isCompatibleBoundsUnsafeAssignment should be called first. + /// If using this function after type checking (e.g. to determine whether to + /// skip bounds checks) the only remaining cases where it returns true are + /// then ones that have previously passed \c + /// isCompatibleBoundsUnsafeAssignment during type checking, so that need not + /// be called again. + /// @param DestTy Type declared at LHS of variable assignment or function to + /// be called + /// @param SourceValue RHS of variable assignment, or function argument + /// @param AssignmentLoc Location of the actual assignment or function call + /// @return true when the -fbounds-safety rules can be skipped + bool allowBoundsUnsafePointerAssignment(const QualType DestTy, + const Expr *SourceValue, + SourceLocation AssignmentLoc) const; + + /// @brief Helper function wrapping allowBoundsUnsafePointerAssignment. + /// Only for function calls, not for assignments or returns. + bool allowBoundsUnsafeFunctionArg(const CallExpr *CallE, + unsigned ParamIdx) const; + + /// @brief Check whether the conditions justify making an exception to + /// -fbounds-safety safety rules in system headers (since the threshold to fixing + /// errors is much higher there). In short the error should be in a system + /// header that hasn't (fully) adopted -fbounds-safety. NB: + /// If determining whether a pointer assignment should be allowed, use \c + /// allowBoundsUnsafePointerAssignment (which will call this function, in + /// addition to other checks). This function can be called directly for + /// determining whether unsafe count parameter assignments should be allowed. + /// @param AssignmentLoc Location of an expression assigning a new value to + /// a bounds safe variable. + bool allowBoundsUnsafeAssignment(SourceLocation AssignmentLoc) const; + + void TryFixAssigningNullTerminatedToImplicitBidiIndexablePtr( + const ValueDecl *Assignee, Expr *SrcExpr, QualType DstType, AssignmentAction Action); + void + TryFixAssigningImplicitBidiIndexableToNullTerminatedPtr(Expr *SrcExpr, + QualType DstType); + void TryFixAssigningBidiIndexableExprToNullTerminated(Expr *SrcExpr, + QualType DstType); + void TryFixAssigningNullTerminatedToBidiIndexableExpr(Expr *SrcExpr, + QualType DstType); + void TryFixAssigningSinglePtrToNullTerminated(Expr *SrcExpr, QualType SrcType, + QualType DstType); + void TryFixAssigningConstArrayToNullTerminated(const Expr *SrcExpr, + QualType SrcType, + QualType DstType); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + bool IsAssignConvertCompatible(AssignConvertType ConvTy) { switch (ConvTy) { default: @@ -7877,7 +8358,9 @@ class Sema final : public SemaBase { bool DiagnoseAssignmentResult(AssignConvertType ConvTy, SourceLocation Loc, QualType DstType, QualType SrcType, Expr *SrcExpr, AssignmentAction Action, - bool *Complained = nullptr); + bool *Complained = nullptr, + // TO_UPSTREAM(BoundsSafety) + const ValueDecl *Assignee = nullptr); /// CheckAssignmentConstraints - Perform type checking for assignment, /// argument passing, variable initialization, and function return values. @@ -7916,6 +8399,25 @@ class Sema final : public SemaBase { AssignConvertType CheckTransparentUnionArgumentConstraints(QualType ArgType, ExprResult &RHS); + /* TO_UPSTREAM(BoundsSafety) ON*/ + bool CheckDynamicBoundVariableEscape(QualType LHSType, Expr *RHSExp); + + using DependentValuesMap = + llvm::DenseMap>; + bool CheckDynamicCountSizeForAssignment(QualType LHSTy, Expr *RHSExpr, + AssignmentAction Action, + SourceLocation Loc, + const Twine &Designator, + DependentValuesMap &DependentValues, + Expr *LHSMemberBase = nullptr); + + void DiagnoseSingleToWideLosingBounds(QualType LHSType, QualType RHSType, + const Expr *RHSExp); + bool DiagnoseDynamicCountVarZeroInit(VarDecl *VD); + Sema::AssignConvertType + CheckValueTerminatedAssignmentConstraints(QualType LHSType, Expr *RHSExpr); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// the following "Check" methods will return a valid/converted QualType /// or a null QualType (indicating an error diagnostic was issued). @@ -7940,6 +8442,9 @@ class Sema final : public SemaBase { BinaryOperatorKind Opc, QualType *CompLHSTy = nullptr); QualType CheckSubtractionOperands( // C99 6.5.6 ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, + /*TO_UPSTREAM(BoundsSafety) ON*/ + BinaryOperatorKind Opc, + /*TO_UPSTREAM(BoundsSafety) OFF*/ QualType *CompLHSTy = nullptr); QualType CheckShiftOperands( // C99 6.5.7 ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, @@ -12387,6 +12892,10 @@ class Sema final : public SemaBase { /// \param A the argument type. bool isSameOrCompatibleFunctionType(QualType Param, QualType Arg); + /* TO_UPSTREAM(BoundsSafety) ON*/ + void CheckValueTerminatedUninitialized(const VarDecl *VD); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Allocate a TemplateArgumentLoc where all locations have /// been initialized to the given location. /// @@ -14880,8 +15389,8 @@ class Sema final : public SemaBase { /// /// \returns A suitable pointer type, if there are no /// errors. Otherwise, returns a NULL type. - QualType BuildPointerType(QualType T, SourceLocation Loc, - DeclarationName Entity); + QualType BuildPointerType(QualType T, BoundsSafetyPointerAttributes A, + SourceLocation Loc, DeclarationName Entity); /// Build a reference type. /// @@ -15185,6 +15694,54 @@ class Sema final : public SemaBase { QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind, SourceLocation Loc); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Lifetime check for variables with function scope. + enum class LifetimeCheckKind { + // Do not check. + None, + + // Dependent variable must be a non-static local variable. + NonStaticLocal, + + // Dependent variable must be a static local variable. + StaticLocal, + + // Dependent variable must be an extern variable including extern local + // variable declaration. + Extern, + + // Dependent variable must be a private extern variable. + PrivateExtern, + + // Dependent variable must be a static global variable. + StaticGlobal, + + // Dependent variable must be a global variable definition. + // Technically, this doesn't tell the lifetime difference compared to + // Extern, + // but we keep this kind to track globals defined in this translation unit. + GlobalDefinition, + }; + + static LifetimeCheckKind getLifetimeCheckKind(const VarDecl *VD); + + /// Attach \c DependerDeclsAttr to declarations referred to by \c counted_by + /// or \c sized_by attributes. This doesn't apply to \c ended_by because it + /// adds a type sugar (i.e., \c DynamicRangePointerType) instead for its + /// dependent declaration. + void AttachDependerDeclsAttr(ValueDecl *NewDepender, + const CountAttributedType *NewDependerCountTy, + unsigned Level); + + QualType BuildCountAttributedType(QualType PointerTy, Expr *CountExpr, + bool CountInBytes = false, + bool OrNull = false, + bool ScopeCheck = false); + + QualType BuildDynamicRangePointerType(QualType PointerTy, Expr *StartPtr, + Expr *EndPtr, bool ScopeCheck = false); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Ensure that the type T is a literal type. /// /// This routine checks whether the type @p T is a literal type. If @p T is an diff --git a/clang/include/clang/Sema/SemaFixItUtils.h b/clang/include/clang/Sema/SemaFixItUtils.h index df9bc42976943..da1ecb901289e 100644 --- a/clang/include/clang/Sema/SemaFixItUtils.h +++ b/clang/include/clang/Sema/SemaFixItUtils.h @@ -86,5 +86,80 @@ struct ConversionFixItGenerator { } }; +/* TO_UPSTREAM(BoundsSafety) ON */ +class BoundsSafetyFixItUtils { +public: + /// Try to find the SourceLocation where a bounds-safety attribute could + /// be inserted on a pointer. Note this method does not check if there is an + /// attribute already present. Clients should handle this themselves. + /// + /// + /// \param TL - TypeLoc that the attribute could be added to + /// \param S - Sema instance + /// + /// \return a tuple of the SourceLocation where insertion could be performed + /// and a boolean that is true iff a space should be inserted after the + /// inserted attribute. If the returned SourceLocation is invalid no insertion + /// point could be found. + static std::tuple + FindPointerAttrInsertPoint(const TypeLoc TL, Sema &S); + + /// Try to create a FixItHint that adds the provided bounds-safety attribute + /// as a new attribute to the variable declaration. Note this method does + /// not check for existing attributes. Clients should have this themselves. + /// + /// \param VD - Variable Declaration to suggest FixIt for. This Variable + /// should have a pointer type. + /// \param Attribute - The string representation of the Attribute to add. + /// \param S - Sema instance + /// + /// \return A FixIt hint that adds the supplied Attribute to the type + /// specifier on the variable declaration. If creating the FixIt fails the + /// returned FixIt will be invalid. + static FixItHint + CreateAnnotatePointerDeclFixIt(const VarDecl *VD, + const llvm::StringRef Attribute, Sema &S); + + /// Try to create a FixItHint that adds the provided bounds-safety attribute + /// as a new attribute to the field declaration. Note this method does + /// not check for existing attributes. Clients should have this themselves. + /// + /// \param VD - Field Declaration to suggest FixIt for. This field + /// should have a pointer type. + /// \param Attribute - The string representation of the Attribute to add. + /// \param S - Sema instance + /// + /// \return A FixIt hint that adds the supplied Attribute to the type + /// specifier on the field declaration. If creating the FixIt fails the + /// returned FixIt will be invalid. + static FixItHint + CreateAnnotatePointerDeclFixIt(const FieldDecl *FD, + const llvm::StringRef Attribute, Sema &S); + + /// Try to create a FixItHint that adds the provided bounds-safety attribute + /// as a new attribute to the variable declaration and all of its previous + /// declarations. Note only global variables may have previous declarations. + /// Note this method does not check for existing attributes. + /// Clients should have this themselves. + /// + /// \param VD - Variable Declaration to suggest FixIt for. This Variable + /// should have a pointer type. + /// \param Attribute - The string representation of the Attribute to add. + /// \param S - Sema instance + /// \param FixIts - A SmallVector of (FixIts hint, VarDecl) tuples. Every + /// valid FixHit that can be created will be added to this SmallVector. Each + /// FixIt hint adds the supplied Attribute to the type specifier on each of + /// the variable declarations. + static void CreateAnnotateAllPointerDeclsFixIts( + const VarDecl *VD, const llvm::StringRef Attribute, Sema &S, + llvm::SmallVectorImpl> + &FixIts); + +private: + static FixItHint CreateAnnotateVarDeclOrFieldDeclFixIt( + const DeclaratorDecl *VD, const llvm::StringRef Attribute, Sema &S); +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + } // endof namespace clang #endif diff --git a/clang/include/clang/Sema/SemaSwift.h b/clang/include/clang/Sema/SemaSwift.h index 8d8f1467054ac..b33d6025acbb4 100644 --- a/clang/include/clang/Sema/SemaSwift.h +++ b/clang/include/clang/Sema/SemaSwift.h @@ -39,6 +39,7 @@ class SemaSwift : public SemaBase { void handleAsyncError(Decl *D, const ParsedAttr &AL); void handleName(Decl *D, const ParsedAttr &AL); void handleAsyncName(Decl *D, const ParsedAttr &AL); + void handleTransparentStepping(Decl *D, const ParsedAttr &AL); void handleNewType(Decl *D, const ParsedAttr &AL); /// Do a check to make sure \p Name looks like a legal argument for the diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 5cb9998126a85..10c7b376a25b0 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -375,6 +375,17 @@ enum ControlRecordTypes { /// Record code for the module build directory. MODULE_DIRECTORY, + + /// Record code for the (optional) \c ActionCache key for this module. + MODULE_CACHE_KEY, + + /// Record code for the (optional) CAS filesystem root ID for implicit modules + /// built with the dependency scanner. + CASFS_ROOT_ID, + + /// Record code for the (optional) include-tree ID for implicit modules + /// built with the dependency scanner. + CAS_INCLUDE_TREE_ID, }; /// Record types that occur within the options block inside @@ -391,9 +402,6 @@ enum OptionsRecordTypes { /// Record code for the target options table. TARGET_OPTIONS, - /// Record code for the filesystem options table. - FILE_SYSTEM_OPTIONS, - /// Record code for the headers search options table. HEADER_SEARCH_OPTIONS, @@ -415,6 +423,9 @@ enum UnhashedControlBlockRecordTypes { /// Record code for the headers search paths. HEADER_SEARCH_PATHS, + /// Record code for the filesystem options table. + FILE_SYSTEM_OPTIONS, + /// Record code for \#pragma diagnostic mappings. DIAG_PRAGMA_MAPPINGS, @@ -742,6 +753,9 @@ enum ASTRecordTypes { UPDATE_MODULE_LOCAL_VISIBLE = 76, UPDATE_TU_LOCAL_VISIBLE = 77, + + /// Record code for availability domain table. + AVAILABILITY_DOMAIN_TABLE = 78, }; /// Record types used within a source manager block. @@ -1928,6 +1942,19 @@ enum StmtCode { // OpenCL EXPR_ASTYPE, // AsTypeExpr + /* TO_UPSTREAM(BoundsSafety) ON */ + // BoundsSafety + EXPR_ASSUMPTION, // AssumptionExpr + EXPR_BOUNDS_SAFETY_POINTER_PROMOTION, // BoundsSafetyPointerPromotionExpr + EXPR_FORGE_PTR, // ForgePtrExpr + EXPR_GET_BOUND, // GetBoundExpr + EXPR_PREDEFINED_BOUNDS_CHECK, // PredefinedBoundsCheckExpr + EXPR_BOUNDS_CHECK, // BoundsCheckExpr + EXPR_MATERIALIZE_SEQUENCE, // MaterializeSequenceExpr + EXPR_TERMINATED_BY_TO_INDEXABLE, // TerminatedByToIndexableExpr + EXPR_TERMINATED_BY_FROM_INDEXABLE, // TerminatedByFromIndexableExpr + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Microsoft EXPR_CXX_PROPERTY_REF_EXPR, // MSPropertyRefExpr EXPR_CXX_PROPERTY_SUBSCRIPT_EXPR, // MSPropertySubscriptExpr diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 2765c827ece2b..3af34d5fc1655 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -257,6 +257,26 @@ class ASTReaderListener { /// AST file imported by this AST file. virtual void visitImport(StringRef ModuleName, StringRef Filename) {} + /// Called for each CAS filesystem root ID. + /// + /// \returns true to indicate \p RootID is invalid, or false otherwise. + virtual bool readCASFileSystemRootID(StringRef RootID, bool Complain) { + return false; + } + + /// Called for each CAS include-tree root ID. + /// + /// \returns true to indicate \p RootID is invalid, or false otherwise. + virtual bool readIncludeTreeID(StringRef ID, bool Complain) { return false; } + + /// Called for each module cache key. + /// + /// \returns true to indicate the key cannot be loaded. + virtual bool readModuleCacheKey(StringRef ModuleName, StringRef Filename, + StringRef CacheKey) { + return false; + } + /// Indicates that a particular module file extension has been read. virtual void readModuleFileExtension( const ModuleFileExtensionMetadata &Metadata) {} @@ -306,6 +326,10 @@ class ChainedASTReaderListener : public ASTReaderListener { serialization::ModuleKind Kind) override; bool visitInputFile(StringRef Filename, bool isSystem, bool isOverridden, bool isExplicitModule) override; + bool readCASFileSystemRootID(StringRef RootID, bool Complain) override; + bool readIncludeTreeID(StringRef ID, bool Complain) override; + bool readModuleCacheKey(StringRef ModuleName, StringRef Filename, + StringRef CacheKey) override; void readModuleFileExtension( const ModuleFileExtensionMetadata &Metadata) override; }; @@ -871,13 +895,6 @@ class ASTReader /// added to the global preprocessing entity ID to produce a local ID. GlobalPreprocessedEntityMapType GlobalPreprocessedEntityMap; - using GlobalSkippedRangeMapType = - ContinuousRangeMap; - - /// Mapping from global skipped range base IDs to the module in which - /// the skipped ranges reside. - GlobalSkippedRangeMapType GlobalSkippedRangeMap; - /// \name CodeGen-relevant special data /// Fields containing data that is relevant to CodeGen. //@{ @@ -1554,6 +1571,7 @@ class ASTReader bool AllowCompatibleConfigurationMismatch, ASTReaderListener *Listener, bool ValidateDiagnosticOptions); + Decl *getAvailabilityDomainDecl(StringRef DomainName) override; llvm::Error ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities); llvm::Error ReadExtensionBlock(ModuleFile &F); void ReadModuleOffsetMap(ModuleFile &F) const; @@ -2008,9 +2026,6 @@ class ASTReader std::optional isPreprocessedEntityInFileID(unsigned Index, FileID FID) override; - /// Read a preallocated skipped range from the external source. - SourceRange ReadSkippedRange(unsigned Index) override; - /// Read the header file information for the given file entry. HeaderFileInfo GetHeaderFileInfo(FileEntryRef FE) override; diff --git a/clang/include/clang/Serialization/ASTRecordReader.h b/clang/include/clang/Serialization/ASTRecordReader.h index 141804185083f..81e6951f12912 100644 --- a/clang/include/clang/Serialization/ASTRecordReader.h +++ b/clang/include/clang/Serialization/ASTRecordReader.h @@ -171,6 +171,12 @@ class ASTRecordReader return Qualifiers::fromOpaqueValue(readInt()); } + /* TO_UPSTREAM(BoundsSafety) ON */ + BoundsSafetyPointerAttributes readBoundsSafetyPointerAttributes() { + return BoundsSafetyPointerAttributes::fromOpaqueValue(readUInt32()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Read a type from the current position in the record. QualType readType() { return Reader->readType(*F, Record, Idx); diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index 9f0570eddc34e..adc8fff2e993a 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -611,6 +611,7 @@ class ASTWriter : public ASTDeserializationListener, uint64_t &VisibleBlockOffset, uint64_t &ModuleLocalBlockOffset, uint64_t &TULocalBlockOffset); + void writeAvailabilityDomainDecls(ASTContext &Context); void WriteTypeDeclOffsets(); void WriteFileDeclIDsMap(); void WriteComments(ASTContext &Context); @@ -956,6 +957,10 @@ class ASTWriter : public ASTDeserializationListener, void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) override; + /* TO_UPSTREAM(BoundsSafety) ON*/ + void LazyAttributeToDecl(const Attr *Attr, + const Decl *D) override; + /* TO_UPSTREAM(BoundsSafety) OFF*/ void EnteringModulePurview() override; void AddedManglingNumber(const Decl *D, unsigned) override; void AddedStaticLocalNumbers(const Decl *D, unsigned) override; diff --git a/clang/include/clang/Serialization/ModuleFile.h b/clang/include/clang/Serialization/ModuleFile.h index f20cb2f9f35ae..4eeed6a65dad1 100644 --- a/clang/include/clang/Serialization/ModuleFile.h +++ b/clang/include/clang/Serialization/ModuleFile.h @@ -144,6 +144,17 @@ class ModuleFile { /// The file name of the module file. std::string FileName; + /// The \c ActionCache key for this module, or empty. + std::string ModuleCacheKey; + + /// The CAS filesystem root ID for implicit modules built with the dependency + /// scanner, or empty. + std::string CASFileSystemRootID; + + /// The include-tree root ID for implicit modules built with the dependency + /// scanner, or empty. + std::string IncludeTreeID; + /// The name of the module. std::string ModuleName; @@ -378,12 +389,6 @@ class ModuleFile { const PPEntityOffset *PreprocessedEntityOffsets = nullptr; unsigned NumPreprocessedEntities = 0; - /// Base ID for preprocessed skipped ranges local to this module. - unsigned BasePreprocessedSkippedRangeID = 0; - - const PPSkippedRange *PreprocessedSkippedRangeOffsets = nullptr; - unsigned NumPreprocessedSkippedRanges = 0; - // === Header search information === /// The number of local HeaderFileInfo structures. @@ -400,6 +405,10 @@ class ModuleFile { /// the header files. void *HeaderFileInfoTable = nullptr; + /// The on-disk hash table that contains information about availability + /// domains. + void *AvailabilityDomainTable = nullptr; + // === Submodule information === /// The number of submodules in this module. diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def index 3c78b87805010..cf020e4c86444 100644 --- a/clang/include/clang/Serialization/TypeBitCodes.def +++ b/clang/include/clang/Serialization/TypeBitCodes.def @@ -68,5 +68,9 @@ TYPE_BIT_CODE(PackIndexing, PACK_INDEXING, 56) TYPE_BIT_CODE(CountAttributed, COUNT_ATTRIBUTED, 57) TYPE_BIT_CODE(ArrayParameter, ARRAY_PARAMETER, 58) TYPE_BIT_CODE(HLSLAttributedResource, HLSLRESOURCE_ATTRIBUTED, 59) +/* TO_UPSTREAM(BoundsSafety) ON */ +TYPE_BIT_CODE(DynamicRangePointer, DYNAMIC_END_POINTER, 60) +TYPE_BIT_CODE(ValueTerminated, VALUE_TERMINATED, 61) +/* TO_UPSTREAM(BoundsSafety) OFF */ #undef TYPE_BIT_CODE diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningCASFilesystem.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningCASFilesystem.h new file mode 100644 index 0000000000000..88fe5f39c2a8e --- /dev/null +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningCASFilesystem.h @@ -0,0 +1,117 @@ +//===- DependencyScanningCASFilesystem.h - clang-scan-deps fs ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGCASFILESYSTEM_H +#define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGCASFILESYSTEM_H + +#include "clang/Basic/LLVM.h" +#include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/CAS/ActionCache.h" +#include "llvm/CAS/CASID.h" +#include "llvm/CAS/CASReference.h" +#include "llvm/CAS/ThreadSafeFileSystem.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/VirtualFileSystem.h" +#include + +namespace llvm { +namespace cas { +class CachingOnDiskFileSystem; +} // namespace cas +} // namespace llvm + +namespace clang { +namespace tooling { +namespace dependencies { + +class DependencyScanningCASFilesystem : public llvm::cas::ThreadSafeFileSystem { +public: + DependencyScanningCASFilesystem( + IntrusiveRefCntPtr WorkerFS, + llvm::cas::ActionCache &Cache); + + ~DependencyScanningCASFilesystem(); + + // FIXME: Make a templated version of ProxyFileSystem with a configurable + // base class. + llvm::vfs::directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override { + return FS->dir_begin(Dir, EC); + } + llvm::ErrorOr getCurrentWorkingDirectory() const override { + return FS->getCurrentWorkingDirectory(); + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { + return FS->setCurrentWorkingDirectory(Path); + } + std::error_code getRealPath(const Twine &Path, + SmallVectorImpl &Output) override { + return FS->getRealPath(Path, Output); + } + std::error_code isLocal(const Twine &Path, bool &Result) override { + return FS->isLocal(Path, Result); + } + + IntrusiveRefCntPtr + createThreadSafeProxyFS() override; + + llvm::ErrorOr status(const Twine &Path) override; + bool exists(const Twine &Path) override; + llvm::ErrorOr> + openFileForRead(const Twine &Path) override; + + /// \returns The scanned preprocessor directive tokens of the file that are + /// used to speed up preprocessing, if available. + std::optional> + getDirectiveTokens(const Twine &Path); + +private: + IntrusiveRefCntPtr FS; + + struct FileEntry { + std::error_code EC; // If non-zero, caches a stat failure. + std::optional Buffer; + SmallVector DepTokens; + SmallVector DepDirectives; + llvm::vfs::Status Status; + std::optional CASContents; + }; + llvm::BumpPtrAllocator EntryAlloc; + llvm::StringMap Entries; + + struct LookupPathResult { + const FileEntry *Entry = nullptr; + + // Only filled if the Entry is nullptr. + llvm::ErrorOr Status; + }; + void scanForDirectives( + llvm::cas::ObjectRef InputDataID, StringRef Identifier, + SmallVectorImpl &DepTokens, + SmallVectorImpl &DepDirectives); + + Expected getOriginal(llvm::cas::CASID InputDataID); + + LookupPathResult lookupPath(const Twine &Path); + + llvm::cas::CachingOnDiskFileSystem &getCachingFS(); + + llvm::cas::ObjectStore &CAS; + llvm::cas::ActionCache &Cache; + std::optional ClangFullVersionID; + std::optional DepDirectivesID; + std::optional EmptyBlobID; +}; + +} // end namespace dependencies +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGCASFILESYSTEM_H diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h index a20a89a4c2b76..884088b225621 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h @@ -13,6 +13,7 @@ #include "clang/Lex/DependencyDirectivesScanner.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" +#include "llvm/CAS/CASReference.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/VirtualFileSystem.h" @@ -29,12 +30,16 @@ using DependencyDirectivesTy = /// Contents and directive tokens of a cached file entry. Single instance can /// be shared between multiple entries. struct CachedFileContents { - CachedFileContents(std::unique_ptr Contents) - : Original(std::move(Contents)), DepDirectives(nullptr) {} + CachedFileContents(std::unique_ptr Contents, + std::optional CASContents) + : Original(std::move(Contents)), CASContents(std::move(CASContents)), + DepDirectives(nullptr) {} /// Owning storage for the original contents. std::unique_ptr Original; + std::optional CASContents; + /// The mutex that must be locked before mutating directive tokens. std::mutex ValueLock; SmallVector DepDirectiveTokens; @@ -87,6 +92,13 @@ class CachedFileSystemEntry { return Contents->Original->getBuffer(); } + std::optional getObjectRefForContent() const { + assert(!isError() && "error"); + assert(!MaybeStat->isDirectory() && "not a file"); + assert(Contents && "contents not initialized"); + return Contents->CASContents; + } + /// \returns The scanned preprocessor directive tokens of the file that are /// used to speed up preprocessing, if available. std::optional> @@ -194,7 +206,8 @@ class DependencyScanningFilesystemSharedCache { /// with the unique ID and returns the result. const CachedFileSystemEntry & getOrEmplaceEntryForUID(llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat, - std::unique_ptr Contents); + std::unique_ptr Contents, + std::optional CASContents); /// Returns entry associated with the filename if there is some. Otherwise, /// associates the given entry with the filename and returns it. @@ -326,6 +339,9 @@ class EntryRef { } StringRef getContents() const { return Entry.getOriginalContents(); } + std::optional getObjectRefForContent() const { + return Entry.getObjectRefForContent(); + } std::optional> getDirectiveTokens() const { @@ -408,10 +424,13 @@ class DependencyScanningWorkerFilesystem struct TentativeEntry { llvm::vfs::Status Status; std::unique_ptr Contents; + std::optional CASContents; TentativeEntry(llvm::vfs::Status Status, - std::unique_ptr Contents = nullptr) - : Status(std::move(Status)), Contents(std::move(Contents)) {} + std::unique_ptr Contents = nullptr, + std::optional CASContents = std::nullopt) + : Status(std::move(Status)), Contents(std::move(Contents)), + CASContents(std::move(CASContents)) {} }; /// Reads file at the given path. Enforces consistency between the file size diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h index 5e8b37e791383..7509da6caa295 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h @@ -9,9 +9,12 @@ #ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGSERVICE_H #define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNINGSERVICE_H +#include "clang/CAS/CASOptions.h" +#include "clang/Tooling/DependencyScanning/DependencyScanningCASFilesystem.h" #include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h" #include "clang/Tooling/DependencyScanning/InProcessModuleCache.h" #include "llvm/ADT/BitmaskEnum.h" +#include "llvm/CAS/ActionCache.h" namespace clang { namespace tooling { @@ -41,6 +44,19 @@ enum class ScanningOutputFormat { /// explicitly building modules. Full, + /// This emits the CAS ID of the scanned files. + Tree, + + /// This emits the full dependency graph but with CAS tree embedded as file + /// dependency. + FullTree, + + /// This emits the CAS ID of the include tree. + IncludeTree, + + /// This emits the full dependency graph but with include tree. + FullIncludeTree, + /// This outputs the dependency graph for standard c++ modules in P1689R5 /// format. P1689, @@ -72,7 +88,9 @@ enum class ScanningOptimizations { // The build system needs to be aware that the current working // directory is ignored. Without a good way of notifying the build // system, it is less risky to default to off. - Default = All & (~IgnoreCWD) + Default = All & (~IgnoreCWD), + + FullIncludeTreeIrrelevant = HeaderSearch | VFS, }; #undef DSS_LAST_BITMASK_ENUM @@ -82,7 +100,10 @@ enum class ScanningOptimizations { class DependencyScanningService { public: DependencyScanningService( - ScanningMode Mode, ScanningOutputFormat Format, + ScanningMode Mode, ScanningOutputFormat Format, CASOptions CASOpts, + std::shared_ptr CAS, + std::shared_ptr Cache, + IntrusiveRefCntPtr SharedFS, ScanningOptimizations OptimizeArgs = ScanningOptimizations::Default, bool EagerLoadModules = false, bool TraceVFS = false); @@ -97,22 +118,39 @@ class DependencyScanningService { bool shouldTraceVFS() const { return TraceVFS; } DependencyScanningFilesystemSharedCache &getSharedCache() { - return SharedCache; + assert(!SharedFS && "Expected not to have a CASFS"); + assert(SharedCache && "Expected a shared cache"); + return *SharedCache; } + const CASOptions &getCASOpts() const { return CASOpts; } + + std::shared_ptr getCAS() const { return CAS; } + std::shared_ptr getCache() const { return Cache; } + + llvm::cas::CachingOnDiskFileSystem &getSharedFS() { return *SharedFS; } + + bool useCASFS() const { return (bool)SharedFS; } + ModuleCacheMutexes &getModuleCacheMutexes() { return ModCacheMutexes; } private: const ScanningMode Mode; const ScanningOutputFormat Format; + CASOptions CASOpts; + std::shared_ptr CAS; + std::shared_ptr Cache; /// Whether to optimize the modules' command-line arguments. - const ScanningOptimizations OptimizeArgs; + ScanningOptimizations OptimizeArgs; /// Whether to set up command-lines to load PCM files eagerly. const bool EagerLoadModules; /// Whether to trace VFS accesses. const bool TraceVFS; + /// Shared CachingOnDiskFileSystem. Set to nullptr to not use CAS dependency + /// scanning. + IntrusiveRefCntPtr SharedFS; /// The global file system cache. - DependencyScanningFilesystemSharedCache SharedCache; + std::optional SharedCache; /// The global module cache mutexes. ModuleCacheMutexes ModCacheMutexes; }; diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h index cb5d7e36d21c9..9a394e743bbcd 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h @@ -12,15 +12,27 @@ #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" +#include "clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h" #include "clang/Tooling/JSONCompilationDatabase.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/MapVector.h" +#include "llvm/CAS/CASID.h" +#include "llvm/Support/PrefixMapper.h" #include #include #include #include +namespace llvm { +namespace cas { +class ObjectProxy; +} // namespace cas +} // namespace llvm + namespace clang { +namespace cas { +class IncludeTreeRoot; +} namespace tooling { namespace dependencies { @@ -56,6 +68,12 @@ struct TranslationUnitDeps { /// determined that the differences are benign for this compilation. std::vector ClangModuleDeps; + /// The CASID for input file dependency tree. + std::optional CASFileSystemRootID; + + /// The include-tree for input file dependency tree. + std::optional IncludeTreeID; + /// The sequence of commands required to build the translation unit. Commands /// should be executed in order. /// @@ -120,6 +138,35 @@ class DependencyScanningTool { MakeformatOutputPath); } + /// Collect dependency tree. + llvm::Expected + getDependencyTree(const std::vector &CommandLine, StringRef CWD); + + /// If \p DiagGenerationAsCompilation is true it will generate error + /// diagnostics same way as the normal compilation, with "N errors generated" + /// message and the serialized diagnostics file emitted if the + /// \p DiagOpts.DiagnosticSerializationFile setting is set for the invocation. + llvm::Expected + getDependencyTreeFromCompilerInvocation( + std::shared_ptr Invocation, StringRef CWD, + DiagnosticConsumer &DiagsConsumer, raw_ostream *VerboseOS, + bool DiagGenerationAsCompilation); + + Expected + getIncludeTree(cas::ObjectStore &DB, + const std::vector &CommandLine, StringRef CWD, + LookupModuleOutputCallback LookupModuleOutput); + + /// If \p DiagGenerationAsCompilation is true it will generate error + /// diagnostics same way as the normal compilation, with "N errors generated" + /// message and the serialized diagnostics file emitted if the + /// \p DiagOpts.DiagnosticSerializationFile setting is set for the invocation. + Expected getIncludeTreeFromCompilerInvocation( + cas::ObjectStore &DB, std::shared_ptr Invocation, + StringRef CWD, LookupModuleOutputCallback LookupModuleOutput, + DiagnosticConsumer &DiagsConsumer, raw_ostream *VerboseOS, + bool DiagGenerationAsCompilation); + /// Given a Clang driver command-line for a translation unit, gather the /// modular dependencies and return the information needed for explicit build. /// @@ -153,6 +200,31 @@ class DependencyScanningTool { llvm::vfs::FileSystem &getWorkerVFS() const { return Worker.getVFS(); } + ScanningOutputFormat getScanningFormat() const { + return Worker.getScanningFormat(); + } + + const CASOptions &getCASOpts() const { return Worker.getCASOpts(); } + + CachingOnDiskFileSystemPtr getCachingFileSystem() { + return Worker.getCASFS(); + } + + /// If \p DependencyScanningService enabled sharing of \p FileManager this + /// will return the same instance, otherwise it will create a new one for + /// each invocation. + llvm::IntrusiveRefCntPtr getOrCreateFileManager() const { + return Worker.getOrCreateFileManager(); + } + + static std::unique_ptr + createActionController(DependencyScanningWorker &Worker, + LookupModuleOutputCallback LookupModuleOutput); + +private: + std::unique_ptr + createActionController(LookupModuleOutputCallback LookupModuleOutput); + private: DependencyScanningWorker Worker; }; @@ -188,6 +260,14 @@ class FullDependencyConsumer : public DependencyConsumer { ContextHash = std::move(Hash); } + void handleCASFileSystemRootID(std::string ID) override { + CASFileSystemRootID = std::move(ID); + } + + void handleIncludeTreeID(std::string ID) override { + IncludeTreeID = std::move(ID); + } + TranslationUnitDeps takeTranslationUnitDeps(); ModuleDepsGraph takeModuleGraphDeps(); @@ -198,6 +278,8 @@ class FullDependencyConsumer : public DependencyConsumer { std::vector DirectModuleDeps; std::vector Commands; std::string ContextHash; + std::optional CASFileSystemRootID; + std::optional IncludeTreeID; std::vector OutputPaths; const llvm::DenseSet &AlreadySeen; }; diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h index 3e232c79397ce..29416aa16714f 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h @@ -15,6 +15,7 @@ #include "clang/Frontend/PCHContainerOperations.h" #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" +#include "llvm/CAS/CachingOnDiskFileSystem.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBufferRef.h" @@ -28,6 +29,9 @@ class DependencyOutputOptions; namespace tooling { namespace dependencies { +using CachingOnDiskFileSystemPtr = + llvm::IntrusiveRefCntPtr; + class DependencyScanningWorkerFilesystem; /// A command-line tool invocation that is part of building a TU. @@ -36,6 +40,9 @@ class DependencyScanningWorkerFilesystem; struct Command { std::string Executable; std::vector Arguments; + + /// The \c ActionCache key for this translation unit, if any. + std::optional TUCacheKey; }; class DependencyConsumer { @@ -60,6 +67,10 @@ class DependencyConsumer { virtual void handleDirectModuleDependency(ModuleID MD) = 0; virtual void handleContextHash(std::string Hash) = 0; + + virtual void handleCASFileSystemRootID(std::string ID) {} + + virtual void handleIncludeTreeID(std::string ID) {} }; /// Dependency scanner callbacks that are used during scanning to influence the @@ -70,6 +81,36 @@ class DependencyActionController { virtual std::string lookupModuleOutput(const ModuleDeps &MD, ModuleOutputKind Kind) = 0; + + virtual llvm::Error initialize(CompilerInstance &ScanInstance, + CompilerInvocation &NewInvocation) { + return llvm::Error::success(); + } + + virtual llvm::Error finalize(CompilerInstance &ScanInstance, + CompilerInvocation &NewInvocation) { + return llvm::Error::success(); + } + + virtual std::optional + getCacheKey(const CompilerInvocation &NewInvocation) { + return std::nullopt; + } + + virtual llvm::Error + initializeModuleBuild(CompilerInstance &ModuleScanInstance) { + return llvm::Error::success(); + } + + virtual llvm::Error + finalizeModuleBuild(CompilerInstance &ModuleScanInstance) { + return llvm::Error::success(); + } + + virtual llvm::Error finalizeModuleInvocation(CowCompilerInvocation &CI, + const ModuleDeps &MD) { + return llvm::Error::success(); + } }; /// An individual dependency scanning worker that is able to run on its own @@ -134,6 +175,28 @@ class DependencyScanningWorker { DependencyActionController &Controller, StringRef ModuleName); + /// Scan from a compiler invocation. + /// If \p DiagGenerationAsCompilation is true it will generate error + /// diagnostics same way as the normal compilation, with "N errors generated" + /// message and the serialized diagnostics file emitted if the + /// \p DiagOpts.DiagnosticSerializationFile setting is set for the invocation. + void computeDependenciesFromCompilerInvocation( + std::shared_ptr Invocation, + StringRef WorkingDirectory, DependencyConsumer &Consumer, + DependencyActionController &Controller, DiagnosticConsumer &DiagsConsumer, + raw_ostream *VerboseOS, bool DiagGenerationAsCompilation); + + ScanningOutputFormat getScanningFormat() const { return Service.getFormat(); } + + CachingOnDiskFileSystemPtr getCASFS() { return CacheFS; } + const CASOptions &getCASOpts() const { return CASOpts; } + std::shared_ptr getCAS() const { return CAS; } + + /// If \p DependencyScanningService enabled sharing of \p FileManager this + /// will return the same instance, otherwise it will create a new one for + /// each invocation. + llvm::IntrusiveRefCntPtr getOrCreateFileManager() const; + llvm::vfs::FileSystem &getVFS() const { return *BaseFS; } private: @@ -149,6 +212,13 @@ class DependencyScanningWorker { /// (passed in the constructor). llvm::IntrusiveRefCntPtr DepFS; + /// The caching file system. + CachingOnDiskFileSystemPtr CacheFS; + /// The CAS Dependency Filesytem. This is not set at the sametime as DepFS; + llvm::IntrusiveRefCntPtr DepCASFS; + CASOptions CASOpts; + std::shared_ptr CAS; + /// Private helper functions. bool scanDependencies(StringRef WorkingDirectory, const std::vector &CommandLine, diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h index d2d0d56e5212c..a3e2f3f157d32 100644 --- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h +++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h @@ -21,6 +21,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringSet.h" +#include "llvm/CAS/CASID.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -40,11 +41,13 @@ struct PrebuiltModuleDep { std::string ModuleName; std::string PCMFile; std::string ModuleMapFile; + std::optional ModuleCacheKey; explicit PrebuiltModuleDep(const Module *M) : ModuleName(M->getTopLevelModuleName()), PCMFile(M->getASTFile()->getName()), - ModuleMapFile(M->PresumedModuleMapFile) {} + ModuleMapFile(M->PresumedModuleMapFile), + ModuleCacheKey(M->getModuleCacheKey()) {} }; /// Attributes loaded from AST files of prebuilt modules collected prior to @@ -156,6 +159,9 @@ struct ModuleDeps { /// Whether this is a "system" module. bool IsSystem; + /// Whether current working directory is ignored. + bool IgnoreCWD; + /// Whether this module is fully composed of file & module inputs from /// locations likely to stay the same across the active development and build /// cycle. For example, when all those input paths only resolve in Sysroot. @@ -185,6 +191,15 @@ struct ModuleDeps { /// determined that the differences are benign for this compilation. std::vector ClangModuleDeps; + /// The CASID for the module input dependency tree, if any. + std::optional CASFileSystemRootID; + + /// The CASID for the module include-tree, if any. + std::optional IncludeTreeID; + + /// The \c ActionCache key for this module, if any. + std::optional ModuleCacheKey; + /// The set of libraries or frameworks to link against when /// an entity from this module is used. llvm::SmallVector LinkLibraries; diff --git a/clang/include/clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h b/clang/include/clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h new file mode 100644 index 0000000000000..f885266d50766 --- /dev/null +++ b/clang/include/clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h @@ -0,0 +1,69 @@ +//===--- ScanAndUpdateArgs.h - Util for CC1 Dependency Scanning -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_DRIVER_SCANANDUPDATEARGS_H +#define LLVM_CLANG_DRIVER_SCANANDUPDATEARGS_H + +#include "clang/Basic/LLVM.h" +#include "clang/Frontend/CompileJobCacheKey.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include + +namespace llvm { +class StringSaver; +class PrefixMapper; + +namespace cas { +class ObjectStore; +class CASID; +} // namespace cas +} // namespace llvm + +namespace clang { + +class CASOptions; +class CompilerInvocation; +class DiagnosticConsumer; + +namespace tooling { +namespace dependencies { +class DependencyScanningTool; + +/// Apply CAS inputs for compilation caching to the given invocation, if +/// enabled. +void configureInvocationForCaching(CompilerInvocation &CI, CASOptions CASOpts, + std::string InputID, + CachingInputKind InputKind, + std::string WorkingDir); + +struct DepscanPrefixMapping { + /// Add path mappings to the \p Mapper. + static void configurePrefixMapper(const CompilerInvocation &Invocation, + llvm::PrefixMapper &Mapper); + + /// Add path mappings to the \p Mapper. + static void configurePrefixMapper(ArrayRef PathPrefixMappings, + llvm::PrefixMapper &Mapper); + + /// Apply the mappings from \p Mapper to \p Invocation. + static void remapInvocationPaths(CompilerInvocation &Invocation, + llvm::PrefixMapper &Mapper); +}; +} // namespace dependencies +} // namespace tooling + +Expected scanAndUpdateCC1InlineWithTool( + tooling::dependencies::DependencyScanningTool &Tool, + DiagnosticConsumer &DiagsConsumer, raw_ostream *VerboseOS, + CompilerInvocation &Invocation, StringRef WorkingDirectory, + llvm::cas::ObjectStore &DB); + +} // end namespace clang + +#endif diff --git a/clang/include/clang/Tooling/Refactor/IndexerQuery.h b/clang/include/clang/Tooling/Refactor/IndexerQuery.h new file mode 100644 index 0000000000000..0c0001352eb86 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/IndexerQuery.h @@ -0,0 +1,311 @@ +//===--- IndexerQuery.h - A set of indexer query interfaces ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the base indexer queries that can be used with +// refactoring continuations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_INDEXER_QUERY_H +#define LLVM_CLANG_TOOLING_REFACTOR_INDEXER_QUERY_H + +#include "clang/Tooling/Refactor/RefactoringOperationState.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Error.h" +#include + +namespace clang { +namespace tooling { +namespace indexer { + +/// Represents an abstract indexer query. +class IndexerQuery { +public: + const char *BaseUID; + const char *NameUID; + + IndexerQuery(const char *BaseUID, const char *NameUID) + : BaseUID(BaseUID), NameUID(NameUID) {} + virtual ~IndexerQuery() {} + + virtual void invalidateTUSpecificState() = 0; + + /// Checks if this query was satisfied. Returns true if it wasn't and reports + /// appropriate errors. + virtual bool verify(ASTContext &) { return false; } + + // Mainly used for testing. + static llvm::Error loadResultsFromYAML(StringRef Source, + ArrayRef Queries); + + static bool classof(const IndexerQuery *) { return true; } +}; + +/// An abstract AST query that can produce an AST unit in which the refactoring +/// continuation will run. +class ASTProducerQuery : public IndexerQuery { + static const char *BaseUIDString; + +public: + /// Deriving AST producer queries can redefine this type to generate custom + /// results that are then passed into the refactoring continuations. + using ResultTy = void; + + ASTProducerQuery(const char *NameUID) + : IndexerQuery(BaseUIDString, NameUID) {} + + static bool classof(const IndexerQuery *Q) { + return Q->BaseUID == BaseUIDString; + } +}; + +/// A query that finds a file that contains/should contain the implementation of +/// some declaration. +class ASTUnitForImplementationOfDeclarationQuery final + : public ASTProducerQuery { + static const char *NameUIDString; + + const Decl *D; + PersistentFileID Result; + +public: + ASTUnitForImplementationOfDeclarationQuery(const Decl *D) + : ASTProducerQuery(NameUIDString), D(D), Result("") {} + + using ResultTy = FileID; + + const Decl *getDecl() const { return D; } + + void invalidateTUSpecificState() override { D = nullptr; } + + void setResult(PersistentFileID File) { Result = std::move(File); } + + bool verify(ASTContext &Context) override; + + const PersistentFileID &getResult() const { return Result; } + + static bool classof(const IndexerQuery *D) { + return D->NameUID == NameUIDString; + } +}; + +/// Returns an indexer query that will allow a refactoring continuation to run +/// in an AST unit that contains a file that should contain the implementation +/// of the given declaration \p D. +/// +/// The continuation function will receive \c FileID that corresponds to the +/// implementation file. The indexer can decide which file should be used as an +/// implementation of a declaration based on a number of different heuristics. +/// It does not guarantee that the file will actually have any declarations that +/// correspond to the implementation of \p D yet, as the indexer may decide to +/// point to a file that it thinks will have the implementation declarations in +/// the future. +std::unique_ptr +fileThatShouldContainImplementationOf(const Decl *D); + +/// A declaration predicate operates. +struct DeclPredicate { + const char *Name; + + DeclPredicate(const char *Name) : Name(Name) {} + + bool operator==(const DeclPredicate &P) const { + return StringRef(Name) == P.Name; + } + bool operator!=(const DeclPredicate &P) const { + return StringRef(Name) != P.Name; + } +}; + +/// Represents a declaration predicate that will evaluate to either 'true' or +/// 'false' in an indexer query. +struct BoolDeclPredicate { + DeclPredicate Predicate; + bool IsInverted; + + BoolDeclPredicate(DeclPredicate Predicate, bool IsInverted = false) + : Predicate(Predicate), IsInverted(IsInverted) {} + + BoolDeclPredicate operator!() const { + return BoolDeclPredicate(Predicate, /*IsInverted=*/!IsInverted); + } +}; + +namespace detail { + +/// AST-like representation for decl predicates. +class DeclPredicateNode { +public: + const char *NameUID; + DeclPredicateNode(const char *NameUID) : NameUID(NameUID) {} + + virtual ~DeclPredicateNode() { } + + static std::unique_ptr + create(const DeclPredicate &Predicate); + static std::unique_ptr + create(const BoolDeclPredicate &Predicate); + + static bool classof(const DeclPredicateNode *) { return true; } +}; + +class DeclPredicateNodePredicate : public DeclPredicateNode { + static const char *NameUIDString; + + DeclPredicate Predicate; + +public: + DeclPredicateNodePredicate(const DeclPredicate &Predicate) + : DeclPredicateNode(NameUIDString), Predicate(Predicate) {} + + const DeclPredicate &getPredicate() const { return Predicate; } + + static bool classof(const DeclPredicateNode *P) { + return P->NameUID == NameUIDString; + } +}; + +class DeclPredicateNotPredicate : public DeclPredicateNode { + static const char *NameUIDString; + + std::unique_ptr Child; + +public: + DeclPredicateNotPredicate(std::unique_ptr Child) + : DeclPredicateNode(NameUIDString), Child(std::move(Child)) {} + + const DeclPredicateNode &getChild() const { return *Child; } + + static bool classof(const DeclPredicateNode *P) { + return P->NameUID == NameUIDString; + } +}; + +} // end namespace detail + +enum class QueryBoolResult { + Unknown, + Yes, + No, +}; + +// FIXME: Check that 'T' is either a PersistentDeclRef<> or a Decl *. +template struct Indexed { + T Decl; + // FIXME: Generalize better in the new refactoring engine. + QueryBoolResult IsNotDefined; + + Indexed(T Decl, QueryBoolResult IsNotDefined = QueryBoolResult::Unknown) + : Decl(Decl), IsNotDefined(IsNotDefined) {} + + Indexed(Indexed &&Other) = default; + Indexed &operator=(Indexed &&Other) = default; + Indexed(const Indexed &Other) = default; + Indexed &operator=(const Indexed &Other) = default; + + /// True iff the declaration is not defined in the entire project. + bool isNotDefined() const { + // FIXME: This is hack. Need a better system in the new engine. + return IsNotDefined == QueryBoolResult::Yes; + } +}; + +/// Transforms one set of declarations into another using some predicate. +class DeclarationsQuery : public IndexerQuery { + static const char *BaseUIDString; + + std::vector Input; + std::unique_ptr Predicate; + +protected: + std::vector>> Output; + +public: + DeclarationsQuery(std::vector Input, + std::unique_ptr Predicate) + : IndexerQuery(BaseUIDString, nullptr), Input(std::move(Input)), + Predicate(std::move(Predicate)) { + assert(!this->Input.empty() && "empty declarations list!"); + } + + ArrayRef getInputs() const { return Input; } + + void invalidateTUSpecificState() override { Input.clear(); } + + bool verify(ASTContext &Context) override; + + void setOutput(std::vector>> Output) { + this->Output = Output; + } + + const detail::DeclPredicateNode &getPredicateNode() const { + return *Predicate; + } + + static bool classof(const IndexerQuery *Q) { + return Q->BaseUID == BaseUIDString; + } +}; + +/// The \c DeclEntity class acts as a proxy for the entity that represents a +/// declaration in the indexer. It defines a set of declaration predicates that +/// can be used in indexer queries. +struct DeclEntity { + /// The indexer will evaluate this predicate to 'true' when a certain + /// declaration has a corresponding definition. + BoolDeclPredicate isDefined() const { + return BoolDeclPredicate("decl.isDefined"); + } +}; + +template +class ManyToManyDeclarationsQuery final + : public std::enable_if::value, + DeclarationsQuery>::type { +public: + ManyToManyDeclarationsQuery( + ArrayRef Input, + std::unique_ptr Predicate) + : DeclarationsQuery(std::vector(Input.begin(), Input.end()), + std::move(Predicate)) {} + + std::vector>> getOutput() const { + std::vector>> Results; + for (const auto &Ref : DeclarationsQuery::Output) + Results.push_back(Indexed>( + PersistentDeclRef(Ref.Decl.USR), Ref.IsNotDefined)); + return Results; + } +}; + +/// Returns an indexer query that will pass a filtered list of declarations to +/// a refactoring continuation. +/// +/// The filtering is done based on predicates that are available on the \c +/// DeclEntity types. For example, you can use the following invocation to +/// find a set of declarations that are defined in the entire project: +/// +/// \code +/// filter({ MyDeclA, MyDeclB }, [] (const DeclEntity &D) { return D.isDefined() +/// }) +/// \endcode +template +std::unique_ptr> +filter(ArrayRef Declarations, + BoolDeclPredicate (*Fn)(const DeclEntity &), + typename std::enable_if::value>::type * = + nullptr) { + return std::make_unique>( + Declarations, detail::DeclPredicateNode::create(Fn(DeclEntity()))); +} + +} // end namespace indexer +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_INDEXER_QUERY_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringActionFinder.h b/clang/include/clang/Tooling/Refactor/RefactoringActionFinder.h new file mode 100644 index 0000000000000..126c3f0bc3a85 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringActionFinder.h @@ -0,0 +1,59 @@ +//===--- RefactoringActionFinder.h - Clang refactoring library ------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Provides methods to find the refactoring actions that can be +/// performed at specific locations / source ranges in a translation unit. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_FINDER_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_FINDER_H + +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Refactor/RefactoringActions.h" +#include "llvm/ADT/StringSet.h" +#include + +namespace clang { + +class NamedDecl; +class ASTContext; + +namespace tooling { + +/// Contains a set of a refactoring actions. +struct RefactoringActionSet { + /// A set of refactoring actions that can be performed at some specific + /// location in a source file. + /// + /// The actions in the action set are ordered by their priority: most + /// important actions are placed before the less important ones. + std::vector Actions; + + RefactoringActionSet() {} + + RefactoringActionSet(RefactoringActionSet &&) = default; + RefactoringActionSet &operator=(RefactoringActionSet &&) = default; +}; + +/// \brief Returns a \c RefactoringActionSet that contains the set of actions +/// that can be performed at the given location. +RefactoringActionSet findActionSetAt(SourceLocation Loc, + SourceRange SelectionRange, + ASTContext &Context); + +/// \brief Returns a set of USRs that correspond to the given declaration. +llvm::StringSet<> findSymbolsUSRSet(const NamedDecl *FoundDecl, + ASTContext &Context); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_FINDER_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringActions.def b/clang/include/clang/Tooling/Refactor/RefactoringActions.def new file mode 100644 index 0000000000000..a6f9da97d8d6a --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringActions.def @@ -0,0 +1,61 @@ +//===--- RefactoringActions.def - The list of refactoring actions --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef REFACTORING_ACTION +#define REFACTORING_ACTION(Name, Spelling) +#endif + +#ifndef REFACTORING_SUB_ACTION +#define REFACTORING_SUB_ACTION(Name, Parent, Spelling) \ + REFACTORING_ACTION(Parent##_##Name, Spelling) +#endif + +#ifndef REFACTORING_OPERATION_ACTION +#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command)\ + REFACTORING_ACTION(Name, Spelling) +#endif + +#ifndef REFACTORING_OPERATION_SUB_ACTION +#define REFACTORING_OPERATION_SUB_ACTION(Name, Parent, Spelling, Command)\ + REFACTORING_SUB_ACTION(Name, Parent, Spelling) +#endif + +REFACTORING_ACTION(Rename, "Rename") +REFACTORING_SUB_ACTION(Local, Rename, "Rename") + +REFACTORING_OPERATION_ACTION(Extract, "Extract Function", "extract") +REFACTORING_OPERATION_SUB_ACTION(Method, Extract, "Extract Method", + "extract-method") +REFACTORING_OPERATION_SUB_ACTION(Expression, Extract, "Extract Expression", + "extract-expression") + +REFACTORING_OPERATION_ACTION(IfSwitchConversion, "Convert to Switch", + "if-switch-conversion") +REFACTORING_OPERATION_ACTION(FillInEnumSwitchCases, "Add Missing Switch Cases", + "fill-in-enum-switch-cases") +REFACTORING_OPERATION_ACTION(FillInMissingProtocolStubs, + "Add Missing Protocol Requirements", + "fill-in-missing-protocol-stubs") +REFACTORING_OPERATION_ACTION(LocalizeObjCStringLiteral, + "Wrap in NSLocalizedString", + "localize-objc-string-literal") +REFACTORING_OPERATION_ACTION(ExtractRepeatedExpressionIntoVariable, + "Extract Repeated Expression", + "extract-repeated-expr-into-var") +REFACTORING_OPERATION_ACTION(FillInMissingMethodStubsFromAbstractClasses, + "Add Missing Abstract Class Overrides", + "fill-in-missing-abstract-methods") + // FIXME: For ObjC this should say 'Methods': +REFACTORING_OPERATION_ACTION(ImplementDeclaredMethods, + "Generate Missing Function Definitions", + "implement-declared-methods") + +#undef REFACTORING_OPERATION_SUB_ACTION +#undef REFACTORING_OPERATION_ACTION +#undef REFACTORING_SUB_ACTION +#undef REFACTORING_ACTION diff --git a/clang/include/clang/Tooling/Refactor/RefactoringActions.h b/clang/include/clang/Tooling/Refactor/RefactoringActions.h new file mode 100644 index 0000000000000..fbc0bd87dea6e --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringActions.h @@ -0,0 +1,33 @@ +//===--- RefactoringActions.h - Clang refactoring library -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Contains a list of all the supported refactoring actions. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTIONS_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTIONS_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace tooling { + +enum class RefactoringActionType { +#define REFACTORING_ACTION(Name, Spelling) Name, +#include "clang/Tooling/Refactor/RefactoringActions.def" +}; + +StringRef getRefactoringActionTypeName(RefactoringActionType Action); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTIONS_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringOperation.h b/clang/include/clang/Tooling/Refactor/RefactoringOperation.h new file mode 100644 index 0000000000000..1cf66c2a41143 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringOperation.h @@ -0,0 +1,159 @@ +//===--- RefactoringOperations.h - Defines a refactoring operation --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_H + +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Refactor/RefactoringActions.h" +#include "clang/Tooling/Refactor/RefactoringOptionSet.h" +#include "clang/Tooling/Refactor/RefactoringReplacement.h" +#include "clang/Tooling/Refactor/SymbolOperation.h" +#include "llvm/Support/Error.h" +#include +#include +#include + +namespace clang { + +class ASTContext; +class Decl; +class Preprocessor; +class Stmt; + +namespace tooling { + +class RefactoringContinuation; + +/// A refactoring result contains the source replacements produced by the +/// refactoring operation and the optional refactoring continuation. +struct RefactoringResult { + std::vector Replacements; + std::vector> + AssociatedSymbols; + std::unique_ptr Continuation; + + RefactoringResult( + std::vector Replacements, + std::unique_ptr Continuation = nullptr) + : Replacements(std::move(Replacements)), + Continuation(std::move(Continuation)) {} + + RefactoringResult(std::unique_ptr Continuation) + : Replacements(), Continuation(std::move(Continuation)) {} + + RefactoringResult(RefactoringResult &&) = default; + RefactoringResult &operator=(RefactoringResult &&) = default; +}; + +namespace indexer { + +class IndexerQuery; +class ASTProducerQuery; + +} // end namespace indexer + +/// Refactoring continuations allow refactoring operations to run in external +/// AST units with some results that were obtained after querying the indexer. +/// +/// The state of the refactoring operation is automatically managed by the +/// refactoring engine: +/// - Declaration references are converted to declaration references in +/// an external translation unit. +class RefactoringContinuation { +public: + virtual ~RefactoringContinuation() {} + + virtual indexer::ASTProducerQuery *getASTUnitIndexerQuery() = 0; + + virtual std::vector + getAdditionalIndexerQueries() = 0; + + /// Converts the TU-specific state in the continuation to a TU-independent + /// state. + /// + /// This function is called before the initiation AST unit is freed. + virtual void persistTUSpecificState() = 0; + + /// Invokes the continuation with the indexer query results and the state + /// values in the context of another AST unit. + virtual llvm::Expected + runInExternalASTUnit(ASTContext &Context) = 0; +}; + +// TODO: Remove in favour of diagnostics. +class RefactoringOperationError + : public llvm::ErrorInfo { +public: + static char ID; + StringRef FailureReason; + + RefactoringOperationError(StringRef FailureReason) + : FailureReason(FailureReason) {} + + void log(raw_ostream &OS) const override; + + std::error_code convertToErrorCode() const override; +}; + +/// Represents an abstract refactoring operation. +class RefactoringOperation { +public: + virtual ~RefactoringOperation() {} + + virtual const Stmt *getTransformedStmt() const { return nullptr; } + + virtual const Stmt *getLastTransformedStmt() const { return nullptr; } + + virtual const Decl *getTransformedDecl() const { return nullptr; } + + virtual const Decl *getLastTransformedDecl() const { return nullptr; } + + virtual std::vector getRefactoringCandidates() { return {}; } + + virtual std::vector getAvailableSubActions() { + return {}; + } + + virtual llvm::Expected + perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex = 0) = 0; +}; + +/// A wrapper around a unique pointer to a \c RefactoringOperation or \c +/// SymbolOperation that determines if the operation was successfully initiated +/// or not, even if the operation itself wasn't created. +struct RefactoringOperationResult { + std::unique_ptr RefactoringOp; + std::unique_ptr SymbolOp; + bool Initiated; + StringRef FailureReason; + + RefactoringOperationResult() : Initiated(false) {} + RefactoringOperationResult(std::nullopt_t) : Initiated(false) {} + explicit RefactoringOperationResult(StringRef FailureReason) + : Initiated(false), FailureReason(FailureReason) {} +}; + +/// Initiate a specific refactoring operation. +RefactoringOperationResult initiateRefactoringOperationAt( + SourceLocation Location, SourceRange SelectionRange, ASTContext &Context, + RefactoringActionType ActionType, bool CreateOperation = true); + +/// Initiate a specific refactoring operation on a declaration that corresponds +/// to the given \p DeclUSR. +RefactoringOperationResult +initiateRefactoringOperationOnDecl(StringRef DeclUSR, ASTContext &Context, + RefactoringActionType ActionType); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringOperationState.h b/clang/include/clang/Tooling/Refactor/RefactoringOperationState.h new file mode 100644 index 0000000000000..2e49f1f89fdbb --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringOperationState.h @@ -0,0 +1,65 @@ +//===--- RefactoringOperationState.h - Serializable operation state -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the refactoring operation state types that represent the +// TU-independent state that is used for refactoring continuations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_STATE_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_STATE_H + +#include "clang/AST/Decl.h" +#include "clang/Basic/LLVM.h" +#include "clang/Tooling/Refactor/USRFinder.h" +#include +#include + +namespace clang { +namespace tooling { + +namespace detail { + +struct PersistentDeclRefBase {}; + +} // end namespace detail + +/// Declaration references are persisted across translation units by using +/// USRs. +template +struct PersistentDeclRef : std::enable_if::value, + detail::PersistentDeclRefBase>::type { + std::string USR; + // FIXME: We can improve the efficiency of conversion to Decl * by storing the + // decl kind. + + PersistentDeclRef(std::string USR) : USR(std::move(USR)) {} + PersistentDeclRef(PersistentDeclRef &&Other) = default; + PersistentDeclRef &operator=(PersistentDeclRef &&Other) = default; + PersistentDeclRef(const PersistentDeclRef &Other) = default; + PersistentDeclRef &operator=(const PersistentDeclRef &Other) = default; + + static PersistentDeclRef create(const Decl *D) { + // FIXME: Move the getUSRForDecl method somewhere else. + return PersistentDeclRef(rename::getUSRForDecl(D)); + } +}; + +/// FileIDs are persisted across translation units by using filenames. +struct PersistentFileID { + std::string Filename; + + PersistentFileID(std::string Filename) : Filename(std::move(Filename)) {} + PersistentFileID(PersistentFileID &&Other) = default; + PersistentFileID &operator=(PersistentFileID &&Other) = default; +}; + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_STATE_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringOptionSet.h b/clang/include/clang/Tooling/Refactor/RefactoringOptionSet.h new file mode 100644 index 0000000000000..39168f587e1fa --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringOptionSet.h @@ -0,0 +1,79 @@ +//===--- RefactoringOptionSet.h - A container for the refactoring options -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTION_SET_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTION_SET_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace yaml { +class IO; +} // end namespace yaml +} // end namespace llvm + +namespace clang { +namespace tooling { + +struct OldRefactoringOption { + virtual ~OldRefactoringOption() = default; + + struct SerializationContext { + llvm::yaml::IO &IO; + + SerializationContext(llvm::yaml::IO &IO) : IO(IO) {} + }; + + virtual void serialize(const SerializationContext &Context); +}; + +/// \brief A set of refactoring options that can be given to a refactoring +/// operation. +class RefactoringOptionSet final { + llvm::StringMap> Options; + +public: + RefactoringOptionSet() {} + template RefactoringOptionSet(const T &Option) { add(Option); } + + RefactoringOptionSet(RefactoringOptionSet &&) = default; + RefactoringOptionSet &operator=(RefactoringOptionSet &&) = default; + + RefactoringOptionSet(const RefactoringOptionSet &) = delete; + RefactoringOptionSet &operator=(const RefactoringOptionSet &) = delete; + + template void add(const T &Option) { + auto It = Options.try_emplace(StringRef(T::Name), nullptr); + if (It.second) + It.first->getValue().reset(new T(Option)); + } + + template const T *get() const { + auto It = Options.find(StringRef(T::Name)); + if (It == Options.end()) + return nullptr; + return static_cast(It->getValue().get()); + } + + template const T &get(const T &Default) const { + const auto *Ptr = get(); + return Ptr ? *Ptr : Default; + } + + void print(llvm::raw_ostream &OS) const; + + static llvm::Expected parse(StringRef Source); +}; + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTION_SET_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringOptions.h b/clang/include/clang/Tooling/Refactor/RefactoringOptions.h new file mode 100644 index 0000000000000..5a8c68b9b90d7 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringOptions.h @@ -0,0 +1,58 @@ +//===--- RefactoringOptions.h - A set of all the refactoring options ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a set of all possible refactoring options that can be +// given to the refactoring operations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_OLD_REFACTORING_OPTIONS_H +#define LLVM_CLANG_TOOLING_REFACTOR_OLD_REFACTORING_OPTIONS_H + +#include "clang/AST/DeclBase.h" +#include "clang/Basic/LLVM.h" +#include "clang/Tooling/Refactor/RefactoringOptionSet.h" + +namespace clang { +namespace tooling { +namespace option { + +namespace detail { + +struct BoolOptionBase : OldRefactoringOption { +protected: + bool Value = false; + void serializeImpl(const SerializationContext &Context, const char *Name); + +public: + operator bool() const { return Value; } +}; + +template struct BoolOption : BoolOptionBase { + void serialize(const SerializationContext &Context) override { + serializeImpl(Context, Option::Name); + } + + static Option getTrue() { + Option Result; + Result.Value = true; + return Result; + } +}; + +} // end namespace detail + +struct AvoidTextualMatches final : detail::BoolOption { + static constexpr const char *Name = "rename.avoid.textual.matches"; +}; + +} // end namespace option +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_OLD_REFACTORING_OPTIONS_H diff --git a/clang/include/clang/Tooling/Refactor/RefactoringReplacement.h b/clang/include/clang/Tooling/Refactor/RefactoringReplacement.h new file mode 100644 index 0000000000000..ed87aafed23cc --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RefactoringReplacement.h @@ -0,0 +1,84 @@ +//===--- RefactoringReplacement.h - ------------------------*- C++ -*------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_REPLACEMENT_H +#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_REPLACEMENT_H + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Refactoring/Rename/SymbolName.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringSet.h" +#include + +namespace clang { +namespace tooling { + +/// \brief Represent a symbol that can be used for an additional refactoring +/// action that associated. +class RefactoringResultAssociatedSymbol { + SymbolName Name; + +public: + RefactoringResultAssociatedSymbol(SymbolName Name) : Name(std::move(Name)) {} + + const SymbolName &getName() const { return Name; } +}; + +/// \brief A replacement range. +class RefactoringReplacement { +public: + SourceRange Range; + std::string ReplacementString; + + /// \brief Represents a symbol that is contained in the replacement string + /// of this replacement. + struct AssociatedSymbolLocation { + /// These offsets point into the ReplacementString. + llvm::SmallVector Offsets; + bool IsDeclaration; + + AssociatedSymbolLocation(ArrayRef Offsets, + bool IsDeclaration = false) + : Offsets(Offsets.begin(), Offsets.end()), + IsDeclaration(IsDeclaration) {} + }; + llvm::SmallDenseMap + SymbolLocations; + + RefactoringReplacement(SourceRange Range) : Range(Range) {} + + RefactoringReplacement(SourceRange Range, StringRef ReplacementString) + : Range(Range), ReplacementString(ReplacementString.str()) {} + RefactoringReplacement(SourceRange Range, std::string ReplacementString) + : Range(Range), ReplacementString(std::move(ReplacementString)) {} + + RefactoringReplacement(SourceRange Range, StringRef ReplacementString, + const RefactoringResultAssociatedSymbol *Symbol, + const AssociatedSymbolLocation &Loc) + : Range(Range), ReplacementString(ReplacementString.str()) { + SymbolLocations.insert(std::make_pair(Symbol, Loc)); + } + + RefactoringReplacement(const FixItHint &Hint) { + Range = Hint.RemoveRange.getAsRange(); + ReplacementString = Hint.CodeToInsert; + } + + RefactoringReplacement(RefactoringReplacement &&) = default; + RefactoringReplacement &operator=(RefactoringReplacement &&) = default; +}; + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_REPLACEMENT_H diff --git a/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h b/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h new file mode 100644 index 0000000000000..3645bc272245c --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RenameIndexedFile.h @@ -0,0 +1,109 @@ +//===--- RenameIndexedFile.h - -----------------------------*- C++ -*------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_RENAME_INDEXED_FILE_H +#define LLVM_CLANG_TOOLING_REFACTOR_RENAME_INDEXED_FILE_H + +#include "clang/Basic/LLVM.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Tooling/Refactor/RenamedSymbol.h" +#include "clang/Tooling/Refactoring/Rename/SymbolName.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Mutex.h" + +namespace clang { +namespace tooling { + +class RefactoringOptionSet; + +namespace rename { + +/// An already known occurrence of the symbol that's being renamed. +struct IndexedOccurrence { + /// The location of this occurrence in the indexed file. + unsigned Line, Column; + enum OccurrenceKind { + IndexedSymbol, + IndexedObjCMessageSend, + InclusionDirective + }; + OccurrenceKind Kind; +}; + +struct IndexedSymbol { + SymbolName Name; + std::vector IndexedOccurrences; + /// Whether this symbol is an Objective-C selector. + bool IsObjCSelector; + /// If true, indexed file renamer will look for matching textual occurrences + /// in string literal tokens. + bool SearchForStringLiteralOccurrences; + + IndexedSymbol(SymbolName Name, + std::vector IndexedOccurrences, + bool IsObjCSelector, + bool SearchForStringLiteralOccurrences = false) + : Name(std::move(Name)), + IndexedOccurrences(std::move(IndexedOccurrences)), + IsObjCSelector(IsObjCSelector), + SearchForStringLiteralOccurrences(SearchForStringLiteralOccurrences) {} + IndexedSymbol(IndexedSymbol &&Other) = default; + IndexedSymbol &operator=(IndexedSymbol &&Other) = default; +}; + +/// Consumes the \c SymbolOccurrences found by \c IndexedFileOccurrenceProducer. +class IndexedFileOccurrenceConsumer { +public: + virtual ~IndexedFileOccurrenceConsumer() {} + virtual void handleOccurrence(const OldSymbolOccurrence &Occurrence, + SourceManager &SM, + const LangOptions &LangOpts) = 0; +}; + +/// Guards against thread unsafe parts of ClangTool::run. +class IndexedFileRenamerLock { + llvm::sys::Mutex &Lock; + bool IsUnlocked = false; + +public: + IndexedFileRenamerLock(llvm::sys::Mutex &Lock) : Lock(Lock) { Lock.lock(); } + + void unlock() { + Lock.unlock(); + IsUnlocked = true; + } + + ~IndexedFileRenamerLock() { + if (!IsUnlocked) + Lock.unlock(); + } +}; + +/// Finds the renamed \c SymbolOccurrences in an already indexed files. +class IndexedFileOccurrenceProducer final : public PreprocessorFrontendAction { + bool IsMultiPiece; + ArrayRef Symbols; + IndexedFileOccurrenceConsumer &Consumer; + IndexedFileRenamerLock &Lock; + const RefactoringOptionSet *Options; + +public: + IndexedFileOccurrenceProducer(ArrayRef Symbols, + IndexedFileOccurrenceConsumer &Consumer, + IndexedFileRenamerLock &Lock, + const RefactoringOptionSet *Options = nullptr); + +private: + void ExecuteAction() override; +}; + +} // end namespace rename +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAME_INDEXED_FILE_H diff --git a/clang/include/clang/Tooling/Refactor/RenamedSymbol.h b/clang/include/clang/Tooling/Refactor/RenamedSymbol.h new file mode 100644 index 0000000000000..054a7153f854d --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RenamedSymbol.h @@ -0,0 +1,145 @@ +//===--- RenamedSymbol.h - ---------------------------------*- C++ -*------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_RENAMED_SYMBOL_H +#define LLVM_CLANG_TOOLING_REFACTOR_RENAMED_SYMBOL_H + +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Refactoring/Rename/SymbolName.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringSet.h" + +namespace clang { + +class NamedDecl; + +namespace tooling { +namespace rename { + +/// \brief A symbol that has to be renamed. +class Symbol { +public: + SymbolName Name; + /// The index of this symbol in a \c SymbolOperation. + unsigned SymbolIndex; + /// The declaration that was used to initiate a refactoring operation for this + /// symbol. May not be the most canonical declaration. + const NamedDecl *FoundDecl; + /// An optional Objective-C selector. + std::optional ObjCSelector; + + Symbol(const NamedDecl *FoundDecl, unsigned SymbolIndex, + const LangOptions &LangOpts); + + Symbol(Symbol &&) = default; + Symbol &operator=(Symbol &&) = default; +}; + +/// \brief An occurrence of a renamed symbol. +/// +/// Provides information about an occurrence of symbol that helps renaming tools +/// determine if they can rename this symbol automatically and which source +/// ranges they have to replace. +/// +/// A single occurrence of a symbol can span more than one source range to +/// account for things like Objective-C selectors. +// TODO: Rename +class OldSymbolOccurrence { + /// The source locations that correspond to the occurence of the symbol. + SmallVector Locations; + +public: + enum OccurrenceKind { + /// \brief This occurrence is an exact match and can be renamed + /// automatically. + MatchingSymbol, + + /// \brief This is an occurrence of a matching selector. It can't be renamed + /// automatically unless the indexer proves that this selector refers only + /// to the declarations that correspond to the renamed symbol. + MatchingSelector, + + /// \brief This is an occurrence of an implicit property that uses the + /// renamed method. + MatchingImplicitProperty, + + /// \brief This is a textual occurrence of a symbol in a comment. + MatchingComment, + + /// \brief This is a textual occurrence of a symbol in a doc comment. + MatchingDocComment, + + /// \brief This is an occurrence of a symbol in an inclusion directive. + MatchingFilename, + + /// \brief This is a textual occurrence of a symbol in a string literal. + MatchingStringLiteral + }; + + OccurrenceKind Kind; + /// Whether or not this occurrence is inside a macro. When this is true, the + /// locations of the occurrence contain just one location that points to + /// the location of the macro expansion. + bool IsMacroExpansion; + /// The index of the symbol stored in a \c SymbolOperation which matches this + /// occurrence. + unsigned SymbolIndex; + + OldSymbolOccurrence() + : Kind(MatchingSymbol), IsMacroExpansion(false), SymbolIndex(0) {} + + OldSymbolOccurrence(OccurrenceKind Kind, bool IsMacroExpansion, + unsigned SymbolIndex, ArrayRef Locations) + : Locations(Locations.begin(), Locations.end()), Kind(Kind), + IsMacroExpansion(IsMacroExpansion), SymbolIndex(SymbolIndex) { + assert(!Locations.empty() && "Renamed occurence without locations!"); + } + + OldSymbolOccurrence(OldSymbolOccurrence &&) = default; + OldSymbolOccurrence &operator=(OldSymbolOccurrence &&) = default; + + ArrayRef locations() const { + if (Kind == MatchingImplicitProperty && Locations.size() == 2) + return ArrayRef(Locations).drop_back(); + return Locations; + } + + /// Return the source range that corresponds to an individual source location + /// in this occurrence. + SourceRange getLocationRange(SourceLocation Loc, size_t OldNameSize) const { + SourceLocation EndLoc; + // Implicit property references might store the end as the second location + // to take into account the match for 'prop' when the old name is 'setProp'. + if (Kind == MatchingImplicitProperty && Locations.size() == 2) { + assert(Loc == Locations[0] && "invalid loc"); + EndLoc = Locations[1]; + } else + EndLoc = IsMacroExpansion ? Loc : Loc.getLocWithOffset(OldNameSize); + return SourceRange(Loc, EndLoc); + } + + friend bool operator<(const OldSymbolOccurrence &LHS, + const OldSymbolOccurrence &RHS); + friend bool operator==(const OldSymbolOccurrence &LHS, + const OldSymbolOccurrence &RHS); +}; + +/// \brief Less-than operator between the two renamed symbol occurrences. +bool operator<(const OldSymbolOccurrence &LHS, const OldSymbolOccurrence &RHS); + +/// \brief Equal-to operator between the two renamed symbol occurrences. +bool operator==(const OldSymbolOccurrence &LHS, const OldSymbolOccurrence &RHS); + +} // end namespace rename +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAMED_SYMBOL_H diff --git a/clang/include/clang/Tooling/Refactor/RenamingOperation.h b/clang/include/clang/Tooling/Refactor/RenamingOperation.h new file mode 100644 index 0000000000000..b9763886b5412 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/RenamingOperation.h @@ -0,0 +1,42 @@ +//===--- RenamingOperation.h - -----------------------------*- C++ -*------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_RENAMING_OPERATION_H +#define LLVM_CLANG_TOOLING_REFACTOR_RENAMING_OPERATION_H + +#include "clang/Basic/LLVM.h" +#include "clang/Tooling/Refactoring/Rename/SymbolName.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang { + +class IdentifierTable; + +namespace tooling { + +class SymbolOperation; + +namespace rename { + +/// Return true if the new name is a valid language identifier. +bool isNewNameValid(const SymbolName &NewName, bool IsSymbolObjCSelector, + IdentifierTable &IDs, const LangOptions &LangOpts); +bool isNewNameValid(const SymbolName &NewName, const SymbolOperation &Operation, + IdentifierTable &IDs, const LangOptions &LangOpts); + +/// \brief Finds the set of new names that apply to the symbols in the given +/// \c SymbolOperation. +void determineNewNames(SymbolName NewName, const SymbolOperation &Operation, + SmallVectorImpl &NewNames, + const LangOptions &LangOpts); + +} // end namespace rename +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAMING_OPERATION_H diff --git a/clang/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h b/clang/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h new file mode 100644 index 0000000000000..f5f7e3801c09d --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h @@ -0,0 +1,36 @@ +//===--- SymbolOccurrenceFinder.h - Clang refactoring library -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Provides functionality for finding all occurrences of a USR in a +/// given AST. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OCCURRENCE_FINDER_H +#define LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OCCURRENCE_FINDER_H + +#include "clang/AST/AST.h" +#include "clang/Tooling/Refactor/SymbolOperation.h" +#include "llvm/ADT/StringRef.h" +#include +#include + +namespace clang { +namespace tooling { +namespace rename { + +// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree! +std::vector +findSymbolOccurrences(const SymbolOperation &Operation, Decl *Decl); + +} // end namespace rename +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OCCURRENCE_FINDER_H diff --git a/clang/include/clang/Tooling/Refactor/SymbolOperation.h b/clang/include/clang/Tooling/Refactor/SymbolOperation.h new file mode 100644 index 0000000000000..71ca55b766c45 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/SymbolOperation.h @@ -0,0 +1,90 @@ +//===--- SymbolOperation.h - -------------------------------*- C++ -*------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OPERATION_H +#define LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OPERATION_H + +#include "clang/Basic/LLVM.h" +#include "clang/Tooling/Refactor/RenamedSymbol.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" + +namespace clang { + +class ASTContext; +class NamedDecl; + +namespace tooling { + +/// \brief A refactoring operation that deals with occurrences of symbols. +class SymbolOperation { + /// Contains the symbols that are required for this operation. + SmallVector Symbols; + + /// Maps from a USR to an index in the \c Symbol array. + /// Contains all of the USRs that correspond to the declarations which use + /// the symbols in this operation. + llvm::StringMap USRToSymbol; + + /// True if all the symbols in this operation occur only in the translation + /// unit that defines them. + bool IsLocal; + + /// The declaration whose implementation is needed for the correct initiation + /// of a symbol operation. + const NamedDecl *DeclThatRequiresImplementationTU; + +public: + SymbolOperation(const NamedDecl *FoundDecl, ASTContext &Context); + + SymbolOperation(SymbolOperation &&) = default; + SymbolOperation &operator=(SymbolOperation &&) = default; + + /// Return the symbol that corresponds to the given USR, or null if this USR + /// isn't interesting from the perspective of this operation. + const rename::Symbol *getSymbolForUSR(StringRef USR) const { + auto It = USRToSymbol.find(USR); + if (It != USRToSymbol.end()) + return &Symbols[It->getValue()]; + return nullptr; + } + + /// The symbols that this operation is working on. + /// + /// Symbol operations, like rename, usually just work on just one symbol. + /// However, there are certain language constructs that require more than + /// one symbol in order for them to be renamed correctly. Property + /// declarations in Objective-C are the perfect example: in addition to the + /// actual property, renaming has to rename the corresponding getters and + /// setters, as well as the backing ivar. + ArrayRef symbols() const { return Symbols; } + + /// True if all the symbols in this operation occur only in the translation + /// unit that defines them. + bool isLocal() const { return IsLocal; } + + /// True if the declaration that was found in the initial TU needs to be + /// examined in the TU that implemented it. + bool requiresImplementationTU() const { + return DeclThatRequiresImplementationTU; + } + + /// Returns the declaration whose implementation is needed for the correct + /// initiation of a symbol operation. + const NamedDecl *declThatRequiresImplementationTU() const { + return DeclThatRequiresImplementationTU; + } +}; + +/// Return true if the given declaration corresponds to a local symbol. +bool isLocalSymbol(const NamedDecl *D, const LangOptions &LangOpts); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OPERATION_H diff --git a/clang/include/clang/Tooling/Refactor/USRFinder.h b/clang/include/clang/Tooling/Refactor/USRFinder.h new file mode 100644 index 0000000000000..a63cc75ecd1e4 --- /dev/null +++ b/clang/include/clang/Tooling/Refactor/USRFinder.h @@ -0,0 +1,84 @@ +//===--- USRFinder.h - Clang refactoring library --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Methods for determining the USR of a symbol at a location in source +/// code. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_USR_FINDER_H +#define LLVM_CLANG_TOOLING_REFACTOR_USR_FINDER_H + +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include +#include + +namespace clang { + +class ASTContext; +class Decl; +class SourceLocation; +class NamedDecl; + +namespace tooling { +namespace rename { + +using llvm::StringRef; +using namespace clang::ast_matchers; + +// Given an AST context and a point, returns a NamedDecl identifying the symbol +// at the point. Returns null if nothing is found at the point. +const NamedDecl *getNamedDeclAt(const ASTContext &Context, + SourceLocation Point); + +/// Returns a \c NamedDecl that corresponds to the given \p USR in the given +/// AST context. Returns null if there's no declaration that matches the given +/// \p USR. +const NamedDecl *getNamedDeclWithUSR(const ASTContext &Context, StringRef USR); + +// Converts a Decl into a USR. +std::string getUSRForDecl(const Decl *Decl); + +// FIXME: Implement RecursiveASTVisitor::VisitNestedNameSpecifier instead. +class NestedNameSpecifierLocFinder : public MatchFinder::MatchCallback { +public: + explicit NestedNameSpecifierLocFinder(ASTContext &Context) + : Context(Context) {} + + ArrayRef getNestedNameSpecifierLocations() { + addMatchers(); + Finder.matchAST(Context); + return Locations; + } + +private: + void addMatchers() { + const auto NestedNameSpecifierLocMatcher = + nestedNameSpecifierLoc().bind("nestedNameSpecifierLoc"); + Finder.addMatcher(NestedNameSpecifierLocMatcher, this); + } + + void run(const MatchFinder::MatchResult &Result) override { + const auto *NNS = Result.Nodes.getNodeAs( + "nestedNameSpecifierLoc"); + Locations.push_back(*NNS); + } + + ASTContext &Context; + std::vector Locations; + MatchFinder Finder; +}; + +} // end namespace rename +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_USR_FINDER_H diff --git a/clang/include/clang/Tooling/Refactoring/Rename/RenamingAction.h b/clang/include/clang/Tooling/Refactoring/Rename/RenamingAction.h index 43a8d56e4e717..a1f0a7c03e5fe 100644 --- a/clang/include/clang/Tooling/Refactoring/Rename/RenamingAction.h +++ b/clang/include/clang/Tooling/Refactoring/Rename/RenamingAction.h @@ -19,6 +19,7 @@ #include "clang/Tooling/Refactoring/RefactoringActionRules.h" #include "clang/Tooling/Refactoring/RefactoringOptions.h" #include "clang/Tooling/Refactoring/Rename/SymbolOccurrences.h" +#include "clang/Tooling/Syntax/Tokens.h" #include "llvm/Support/Error.h" namespace clang { @@ -116,6 +117,26 @@ class QualifiedRenamingAction { std::map &FileToReplaces; }; +enum class ObjCSymbolSelectorKind { + /// The rename location is an Objective-C method call, eg. `[self add: 1]`. + MessageSend, + + /// The rename location is an Objective-C method definition, eg. + /// ` - (void)add:(int)theValue` + MethodDecl, + + /// It is unknown if the renamed location is a method call or declaration. + /// + /// The selector kind is being used to improve error recovery, passing unknown + /// does not lead to correctness issues. + Unknown +}; + +llvm::Error findObjCSymbolSelectorPieces( + ArrayRef Tokens, const SourceManager &SrcMgr, + SourceLocation RenameLoc, const SymbolName &OldName, + ObjCSymbolSelectorKind Kind, SmallVectorImpl &Result); + } // end namespace tooling } // end namespace clang diff --git a/clang/include/clang/Tooling/Refactoring/Rename/SymbolName.h b/clang/include/clang/Tooling/Refactoring/Rename/SymbolName.h index 6c28d40f3679c..887ab0929445d 100644 --- a/clang/include/clang/Tooling/Refactoring/Rename/SymbolName.h +++ b/clang/include/clang/Tooling/Refactoring/Rename/SymbolName.h @@ -9,12 +9,16 @@ #ifndef LLVM_CLANG_TOOLING_REFACTORING_RENAME_SYMBOLNAME_H #define LLVM_CLANG_TOOLING_REFACTORING_RENAME_SYMBOLNAME_H +#include "clang/AST/DeclarationName.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" namespace clang { + +class LangOptions; + namespace tooling { /// A name of a symbol. @@ -27,19 +31,45 @@ namespace tooling { /// // ^~ string 0 ~~~~~ ^~ string 1 ~~~~~ /// \endcode class SymbolName { + llvm::SmallVector NamePieces; + public: - explicit SymbolName(StringRef Name) { - // While empty symbol names are valid (Objective-C selectors can have empty - // name pieces), occurrences Objective-C selectors are created using an - // array of strings instead of just one string. - assert(!Name.empty() && "Invalid symbol name!"); - this->Name.push_back(Name.str()); - } + SymbolName(); + + /// Create a new \c SymbolName with the specified pieces. + explicit SymbolName(ArrayRef NamePieces); + explicit SymbolName(ArrayRef NamePieces); + + explicit SymbolName(const DeclarationName &Name); - ArrayRef getNamePieces() const { return Name; } + /// Creates a \c SymbolName from the given string representation. + /// + /// For Objective-C symbol names, this splits a selector into multiple pieces + /// on `:`. For all other languages the name is used as the symbol name. + SymbolName(StringRef Name, bool IsObjectiveCSelector); + SymbolName(StringRef Name, const LangOptions &LangOpts); -private: - llvm::SmallVector Name; + ArrayRef getNamePieces() const { return NamePieces; } + + /// If this symbol consists of a single piece return it, otherwise return + /// `None`. + /// + /// Only symbols in Objective-C can consist of multiple pieces, so this + /// function always returns a value for non-Objective-C symbols. + std::optional getSinglePiece() const; + + /// Returns a human-readable version of this symbol name. + /// + /// If the symbol consists of multiple pieces (aka. it is an Objective-C + /// selector/method name), the pieces are separated by `:`, otherwise just an + /// identifier name. + std::string getAsString() const; + + void print(raw_ostream &OS) const; + + bool operator==(const SymbolName &Other) const { + return NamePieces == Other.NamePieces; + } }; } // end namespace tooling diff --git a/clang/include/clang/Tooling/Syntax/Tokens.h b/clang/include/clang/Tooling/Syntax/Tokens.h index f71b8d67bfea4..3db601ba3eb19 100644 --- a/clang/include/clang/Tooling/Syntax/Tokens.h +++ b/clang/include/clang/Tooling/Syntax/Tokens.h @@ -145,6 +145,19 @@ class Token { /// For debugging purposes. Equivalent to a call to Token::str(). llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Token &T); +/// A list of tokens as lexed from the input file, without expanding +/// preprocessor macros. +class UnexpandedTokenBuffer { + std::vector Tokens; + std::unique_ptr SrcMgr; + +public: + UnexpandedTokenBuffer(StringRef Code, const LangOptions &LangOpts); + + ArrayRef tokens() const { return Tokens; } + const SourceManager &sourceManager() const { return SrcMgr->get(); } +}; + /// A list of tokens obtained by preprocessing a text buffer and operations to /// map between the expanded and spelled tokens, i.e. TokenBuffer has /// information about two token streams: diff --git a/clang/include/indexstore/IndexStoreCXX.h b/clang/include/indexstore/IndexStoreCXX.h new file mode 100644 index 0000000000000..f288cabd27745 --- /dev/null +++ b/clang/include/indexstore/IndexStoreCXX.h @@ -0,0 +1,524 @@ +//===--- IndexStoreCXX.h - C++ wrapper for the Index Store C API. ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Header-only C++ wrapper for the Index Store C API. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEXSTORE_INDEXSTORECXX_H +#define LLVM_CLANG_INDEXSTORE_INDEXSTORECXX_H + +#include "indexstore/indexstore.h" +#include "clang/Basic/PathRemapper.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" + +namespace indexstore { + using llvm::ArrayRef; + using llvm::StringRef; + +static inline StringRef stringFromIndexStoreStringRef(indexstore_string_ref_t str) { + return StringRef(str.data, str.length); +} + +template +static inline Ret functionPtrFromFunctionRef(void *ctx, Params ...params) { + auto fn = (llvm::function_ref *)ctx; + return (*fn)(std::forward(params)...); +} + +class IndexRecordSymbol { + indexstore_symbol_t obj; + friend class IndexRecordReader; + +public: + IndexRecordSymbol(indexstore_symbol_t obj) : obj(obj) {} + + indexstore_symbol_language_t getLanguage() { + return indexstore_symbol_get_language(obj); + } + indexstore_symbol_kind_t getKind() { return indexstore_symbol_get_kind(obj); } + indexstore_symbol_subkind_t getSubKind() { return indexstore_symbol_get_subkind(obj); } + uint64_t getProperties() { + return indexstore_symbol_get_properties(obj); + } + uint64_t getRoles() { return indexstore_symbol_get_roles(obj); } + uint64_t getRelatedRoles() { return indexstore_symbol_get_related_roles(obj); } + StringRef getName() { return stringFromIndexStoreStringRef(indexstore_symbol_get_name(obj)); } + StringRef getUSR() { return stringFromIndexStoreStringRef(indexstore_symbol_get_usr(obj)); } + StringRef getCodegenName() { return stringFromIndexStoreStringRef(indexstore_symbol_get_codegen_name(obj)); } +}; + +class IndexSymbolRelation { + indexstore_symbol_relation_t obj; + +public: + IndexSymbolRelation(indexstore_symbol_relation_t obj) : obj(obj) {} + + uint64_t getRoles() { return indexstore_symbol_relation_get_roles(obj); } + IndexRecordSymbol getSymbol() { return indexstore_symbol_relation_get_symbol(obj); } +}; + +class IndexRecordOccurrence { + indexstore_occurrence_t obj; + +public: + IndexRecordOccurrence(indexstore_occurrence_t obj) : obj(obj) {} + + IndexRecordSymbol getSymbol() { return indexstore_occurrence_get_symbol(obj); } + uint64_t getRoles() { return indexstore_occurrence_get_roles(obj); } + + bool foreachRelation(llvm::function_ref receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_occurrence_relations_apply(obj, ^bool(indexstore_symbol_relation_t sym_rel) { + return receiver(sym_rel); + }); +#else + return indexstore_occurrence_relations_apply_f(obj, &receiver, functionPtrFromFunctionRef); +#endif + } + + std::pair getLineCol() { + unsigned line, col; + indexstore_occurrence_get_line_col(obj, &line, &col); + return std::make_pair(line, col); + } +}; + +class IndexStore; +typedef std::shared_ptr IndexStoreRef; + +class IndexStore { + indexstore_t obj; + friend class IndexRecordReader; + friend class IndexUnitReader; + +public: + IndexStore(StringRef path, const clang::PathRemapper &remapper, + std::string &error) { + llvm::SmallString<64> buf = path; + indexstore_error_t c_err = nullptr; + indexstore_creation_options_t options = indexstore_creation_options_create(); + for (const auto &Mapping : remapper.getMappings()) + indexstore_creation_options_add_prefix_mapping(options, Mapping.first.c_str(), Mapping.second.c_str()); + + obj = indexstore_store_create_with_options(buf.c_str(), options, &c_err); + indexstore_creation_options_dispose(options); + if (c_err) { + error = indexstore_error_get_description(c_err); + indexstore_error_dispose(c_err); + } + } + + IndexStore(IndexStore &&other) : obj(other.obj) { + other.obj = nullptr; + } + + ~IndexStore() { + indexstore_store_dispose(obj); + } + + static IndexStoreRef create(StringRef path, clang::PathRemapper remapper, + std::string &error) { + auto storeRef = std::make_shared(path, remapper, error); + if (storeRef->isInvalid()) + return nullptr; + return storeRef; + } + + static unsigned formatVersion() { + return indexstore_format_version(); + } + + static unsigned version() { + return indexstore_version(); + } + + bool isValid() const { return obj; } + bool isInvalid() const { return !isValid(); } + explicit operator bool() const { return isValid(); } + + bool foreachUnit(bool sorted, llvm::function_ref receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_store_units_apply(obj, sorted, ^bool(indexstore_string_ref_t unit_name) { + return receiver(stringFromIndexStoreStringRef(unit_name)); + }); +#else + return indexstore_store_units_apply_f(obj, sorted, &receiver, functionPtrFromFunctionRef); +#endif + } + + class UnitEvent { + indexstore_unit_event_t obj; + public: + UnitEvent(indexstore_unit_event_t obj) : obj(obj) {} + + enum class Kind { + Removed, + Modified, + DirectoryDeleted, + Failure + }; + Kind getKind() const { + indexstore_unit_event_kind_t c_k = indexstore_unit_event_get_kind(obj); + Kind K; + switch (c_k) { + case INDEXSTORE_UNIT_EVENT_REMOVED: K = Kind::Removed; break; + case INDEXSTORE_UNIT_EVENT_MODIFIED: K = Kind::Modified; break; + case INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED: K = Kind::DirectoryDeleted; break; + case INDEXSTORE_UNIT_EVENT_FAILURE: K = Kind::Failure; break; + } + return K; + } + + StringRef getUnitName() const { + return stringFromIndexStoreStringRef(indexstore_unit_event_get_unit_name(obj)); + } + }; + + class UnitEventNotification { + indexstore_unit_event_notification_t obj; + public: + UnitEventNotification(indexstore_unit_event_notification_t obj) : obj(obj) {} + + bool isInitial() const { return indexstore_unit_event_notification_is_initial(obj); } + size_t getEventsCount() const { return indexstore_unit_event_notification_get_events_count(obj); } + UnitEvent getEvent(size_t index) const { return indexstore_unit_event_notification_get_event(obj, index); } + }; + + typedef std::function UnitEventHandler; + + void setUnitEventHandler(UnitEventHandler handler) { +#if INDEXSTORE_HAS_BLOCKS + if (!handler) { + indexstore_store_set_unit_event_handler(obj, nullptr); + return; + } + + indexstore_store_set_unit_event_handler(obj, ^(indexstore_unit_event_notification_t evt_note) { + handler(UnitEventNotification(evt_note)); + }); +#else + if (!handler) { + indexstore_store_set_unit_event_handler_f(obj, nullptr, nullptr, nullptr); + return; + } + + auto fnPtr = new UnitEventHandler(handler); + indexstore_store_set_unit_event_handler_f(obj, fnPtr, event_handler, event_handler_finalizer); +#endif + } + +private: + static void event_handler(void *ctx, indexstore_unit_event_notification_t evt) { + auto fnPtr = (UnitEventHandler*)ctx; + (*fnPtr)(evt); + } + static void event_handler_finalizer(void *ctx) { + auto fnPtr = (UnitEventHandler*)ctx; + delete fnPtr; + } + +public: + bool startEventListening(bool waitInitialSync, std::string &error) { + indexstore_unit_event_listen_options_t opts; + opts.wait_initial_sync = waitInitialSync; + indexstore_error_t c_err = nullptr; + bool ret = indexstore_store_start_unit_event_listening(obj, &opts, sizeof(opts), &c_err); + if (c_err) { + error = indexstore_error_get_description(c_err); + indexstore_error_dispose(c_err); + } + return ret; + } + + void stopEventListening() { + return indexstore_store_stop_unit_event_listening(obj); + } + + void discardUnit(StringRef UnitName) { + llvm::SmallString<64> buf = UnitName; + indexstore_store_discard_unit(obj, buf.c_str()); + } + + void discardRecord(StringRef RecordName) { + llvm::SmallString<64> buf = RecordName; + indexstore_store_discard_record(obj, buf.c_str()); + } + + void getUnitNameFromOutputPath(StringRef outputPath, llvm::SmallVectorImpl &nameBuf) { + llvm::SmallString<256> buf = outputPath; + llvm::SmallString<64> unitName; + unitName.resize(64); + size_t nameLen = indexstore_store_get_unit_name_from_output_path(obj, buf.c_str(), unitName.data(), unitName.size()); + if (nameLen+1 > unitName.size()) { + unitName.resize(nameLen+1); + indexstore_store_get_unit_name_from_output_path(obj, buf.c_str(), unitName.data(), unitName.size()); + } + nameBuf.append(unitName.begin(), unitName.begin()+nameLen); + } + + void purgeStaleData() { + indexstore_store_purge_stale_data(obj); + } +}; + +class IndexRecordReader { + indexstore_record_reader_t obj; + +public: + IndexRecordReader(IndexStore &store, StringRef recordName, std::string &error) { + llvm::SmallString<64> buf = recordName; + indexstore_error_t c_err = nullptr; + obj = indexstore_record_reader_create(store.obj, buf.c_str(), &c_err); + if (c_err) { + error = indexstore_error_get_description(c_err); + indexstore_error_dispose(c_err); + } + } + + IndexRecordReader(IndexRecordReader &&other) : obj(other.obj) { + other.obj = nullptr; + } + + ~IndexRecordReader() { + indexstore_record_reader_dispose(obj); + } + + bool isValid() const { return obj; } + bool isInvalid() const { return !isValid(); } + explicit operator bool() const { return isValid(); } + + /// Goes through and passes record decls, after filtering using a \c Checker + /// function. + /// + /// Resulting decls can be used as filter for \c foreachOccurrence. This + /// allows allocating memory only for the record decls that the caller is + /// interested in. + bool searchSymbols(llvm::function_ref filter, + llvm::function_ref receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_search_symbols(obj, ^bool(indexstore_symbol_t symbol, bool *stop) { + return filter(symbol, *stop); + }, ^(indexstore_symbol_t symbol) { + receiver(symbol); + }); +#else + return indexstore_record_reader_search_symbols_f(obj, &filter, functionPtrFromFunctionRef, + &receiver, functionPtrFromFunctionRef); +#endif + } + + bool foreachSymbol(bool noCache, llvm::function_ref receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_symbols_apply(obj, noCache, ^bool(indexstore_symbol_t sym) { + return receiver(sym); + }); +#else + return indexstore_record_reader_symbols_apply_f(obj, noCache, &receiver, functionPtrFromFunctionRef); +#endif + } + + /// \param DeclsFilter if non-empty indicates the list of decls that we want + /// to get occurrences for. An empty array indicates that we want occurrences + /// for all decls. + /// \param RelatedDeclsFilter Same as \c DeclsFilter but for related decls. + bool foreachOccurrence(ArrayRef symbolsFilter, + ArrayRef relatedSymbolsFilter, + llvm::function_ref receiver) { + llvm::SmallVector c_symbolsFilter; + c_symbolsFilter.reserve(symbolsFilter.size()); + for (IndexRecordSymbol sym : symbolsFilter) { + c_symbolsFilter.push_back(sym.obj); + } + llvm::SmallVector c_relatedSymbolsFilter; + c_relatedSymbolsFilter.reserve(relatedSymbolsFilter.size()); + for (IndexRecordSymbol sym : relatedSymbolsFilter) { + c_relatedSymbolsFilter.push_back(sym.obj); + } +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_occurrences_of_symbols_apply(obj, + c_symbolsFilter.data(), c_symbolsFilter.size(), + c_relatedSymbolsFilter.data(), + c_relatedSymbolsFilter.size(), + ^bool(indexstore_occurrence_t occur) { + return receiver(occur); + }); +#else + return indexstore_record_reader_occurrences_of_symbols_apply_f(obj, + c_symbolsFilter.data(), c_symbolsFilter.size(), + c_relatedSymbolsFilter.data(), + c_relatedSymbolsFilter.size(), + &receiver, functionPtrFromFunctionRef); +#endif + } + + bool foreachOccurrence( + llvm::function_ref receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_occurrences_apply(obj, ^bool(indexstore_occurrence_t occur) { + return receiver(occur); + }); +#else + return indexstore_record_reader_occurrences_apply_f(obj, &receiver, functionPtrFromFunctionRef); +#endif + } + + bool foreachOccurrenceInLineRange(unsigned lineStart, unsigned lineEnd, + llvm::function_ref receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_record_reader_occurrences_in_line_range_apply(obj, + lineStart, + lineEnd, + ^bool(indexstore_occurrence_t occur) { + return receiver(occur); + }); +#else + return indexstore_record_reader_occurrences_in_line_range_apply_f(obj, + lineStart, + lineEnd, + &receiver, functionPtrFromFunctionRef); +#endif + } +}; + +class IndexUnitDependency { + indexstore_unit_dependency_t obj; + friend class IndexUnitReader; + +public: + IndexUnitDependency(indexstore_unit_dependency_t obj) : obj(obj) {} + + enum class DependencyKind { + Unit, + Record, + File, + }; + DependencyKind getKind() { + switch (indexstore_unit_dependency_get_kind(obj)) { + case INDEXSTORE_UNIT_DEPENDENCY_UNIT: return DependencyKind::Unit; + case INDEXSTORE_UNIT_DEPENDENCY_RECORD: return DependencyKind::Record; + case INDEXSTORE_UNIT_DEPENDENCY_FILE: return DependencyKind::File; + } + } + bool isSystem() { return indexstore_unit_dependency_is_system(obj); } + StringRef getName() { return stringFromIndexStoreStringRef(indexstore_unit_dependency_get_name(obj)); } + StringRef getFilePath() { return stringFromIndexStoreStringRef(indexstore_unit_dependency_get_filepath(obj)); } + StringRef getModuleName() { return stringFromIndexStoreStringRef(indexstore_unit_dependency_get_modulename(obj)); } + +}; + +class IndexUnitInclude { + indexstore_unit_include_t obj; + friend class IndexUnitReader; + +public: + IndexUnitInclude(indexstore_unit_include_t obj) : obj(obj) {} + + StringRef getSourcePath() { + return stringFromIndexStoreStringRef(indexstore_unit_include_get_source_path(obj)); + } + StringRef getTargetPath() { + return stringFromIndexStoreStringRef(indexstore_unit_include_get_target_path(obj)); + } + unsigned getSourceLine() { + return indexstore_unit_include_get_source_line(obj); + } +}; + +class IndexUnitReader { + indexstore_unit_reader_t obj; + +public: + IndexUnitReader(IndexStore &store, StringRef unitName, std::string &error) { + llvm::SmallString<64> buf = unitName; + indexstore_error_t c_err = nullptr; + obj = indexstore_unit_reader_create(store.obj, buf.c_str(), &c_err); + if (c_err) { + error = indexstore_error_get_description(c_err); + indexstore_error_dispose(c_err); + } + } + + IndexUnitReader(IndexUnitReader &&other) : obj(other.obj) { + other.obj = nullptr; + } + + ~IndexUnitReader() { + indexstore_unit_reader_dispose(obj); + } + + bool isValid() const { return obj; } + bool isInvalid() const { return !isValid(); } + explicit operator bool() const { return isValid(); } + + StringRef getProviderIdentifier() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_provider_identifier(obj)); + } + StringRef getProviderVersion() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_provider_version(obj)); + } + + timespec getModificationTime() { + int64_t seconds, nanoseconds; + indexstore_unit_reader_get_modification_time(obj, &seconds, &nanoseconds); + timespec ts; + ts.tv_sec = seconds; + ts.tv_nsec = nanoseconds; + return ts; + } + + bool isSystemUnit() { return indexstore_unit_reader_is_system_unit(obj); } + bool isModuleUnit() { return indexstore_unit_reader_is_module_unit(obj); } + bool isDebugCompilation() { return indexstore_unit_reader_is_debug_compilation(obj); } + bool hasMainFile() { return indexstore_unit_reader_has_main_file(obj); } + + StringRef getMainFilePath() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_main_file(obj)); + } + StringRef getModuleName() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_module_name(obj)); + } + StringRef getWorkingDirectory() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_working_dir(obj)); + } + StringRef getOutputFile() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_output_file(obj)); + } + StringRef getSysrootPath() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_sysroot_path(obj)); + } + StringRef getTarget() { + return stringFromIndexStoreStringRef(indexstore_unit_reader_get_target(obj)); + } + + bool foreachDependency(llvm::function_ref receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_unit_reader_dependencies_apply(obj, ^bool(indexstore_unit_dependency_t dep) { + return receiver(dep); + }); +#else + return indexstore_unit_reader_dependencies_apply_f(obj, &receiver, functionPtrFromFunctionRef); +#endif + } + + bool foreachInclude(llvm::function_ref receiver) { +#if INDEXSTORE_HAS_BLOCKS + return indexstore_unit_reader_includes_apply(obj, ^bool(indexstore_unit_include_t inc) { + return receiver(inc); + }); +#else + return indexstore_unit_reader_includes_apply_f(obj, &receiver, functionPtrFromFunctionRef); +#endif + } +}; + +} // namespace indexstore + +#endif diff --git a/clang/include/indexstore/indexstore.h b/clang/include/indexstore/indexstore.h new file mode 100644 index 0000000000000..328b3c22c0a7a --- /dev/null +++ b/clang/include/indexstore/indexstore.h @@ -0,0 +1,611 @@ +/*===-- indexstore/indexstore.h - Index Store C API ----------------- C -*-===*\ +|* *| +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| +|* Exceptions. *| +|* See https://llvm.org/LICENSE.txt for license information. *| +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This header provides a C API for the index store. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_C_INDEXSTORE_INDEXSTORE_H +#define LLVM_CLANG_C_INDEXSTORE_INDEXSTORE_H + +#include +#include +#include +#include + +/** + * \brief The version constants for the Index Store C API. + * INDEXSTORE_VERSION_MINOR should increase when there are API additions. + * INDEXSTORE_VERSION_MAJOR is intended for "major" source/ABI breaking changes. + */ +#define INDEXSTORE_VERSION_MAJOR 0 +#define INDEXSTORE_VERSION_MINOR 15 /* added Swift init accessor sub-symbol */ + +#define INDEXSTORE_VERSION_ENCODE(major, minor) ( \ + ((major) * 10000) \ + + ((minor) * 1)) + +#define INDEXSTORE_VERSION INDEXSTORE_VERSION_ENCODE( \ + INDEXSTORE_VERSION_MAJOR, \ + INDEXSTORE_VERSION_MINOR ) + +#define INDEXSTORE_VERSION_STRINGIZE_(major, minor) \ + #major"."#minor +#define INDEXSTORE_VERSION_STRINGIZE(major, minor) \ + INDEXSTORE_VERSION_STRINGIZE_(major, minor) + +#define INDEXSTORE_VERSION_STRING INDEXSTORE_VERSION_STRINGIZE( \ + INDEXSTORE_VERSION_MAJOR, \ + INDEXSTORE_VERSION_MINOR) + +#ifdef __cplusplus +# define INDEXSTORE_BEGIN_DECLS extern "C" { +# define INDEXSTORE_END_DECLS } +#else +# define INDEXSTORE_BEGIN_DECLS +# define INDEXSTORE_END_DECLS +#endif + +#ifndef INDEXSTORE_PUBLIC +# ifdef _WIN32 +# ifdef IndexStore_EXPORTS +# define INDEXSTORE_PUBLIC __declspec(dllexport) +# else +# define INDEXSTORE_PUBLIC __declspec(dllimport) +# endif +# else +# define INDEXSTORE_PUBLIC +# endif +#endif + +#ifndef __has_feature +# define __has_feature(x) 0 +#endif + +#ifndef __has_attribute +# define __has_attribute(x) 0 +#endif + +#if __has_feature(blocks) +# define INDEXSTORE_HAS_BLOCKS 1 +#else +# define INDEXSTORE_HAS_BLOCKS 0 +#endif + +#if __has_attribute(noescape) +# define INDEXSTORE_NOESCAPE __attribute__((noescape)) +#else +# define INDEXSTORE_NOESCAPE +#endif + +#if __has_attribute(flag_enum) +# define INDEXSTORE_FLAG_ENUM_ATTR __attribute__((flag_enum)) +#else +# define INDEXSTORE_FLAG_ENUM_ATTR +#endif + +#if __has_attribute(enum_extensibility) +# define INDEXSTORE_OPEN_ENUM_ATTR __attribute__((enum_extensibility(open))) +#else +# define INDEXSTORE_OPEN_ENUM_ATTR +#endif + +#define INDEXSTORE_OPTIONS_ATTRS INDEXSTORE_OPEN_ENUM_ATTR INDEXSTORE_FLAG_ENUM_ATTR + +#if __has_feature(objc_fixed_enum) +#ifdef __cplusplus +# define INDEXSTORE_OPTIONS(_type, _name) enum INDEXSTORE_OPTIONS_ATTRS _name : _type +#else +# define INDEXSTORE_OPTIONS(_type, _name) enum _name : _type; typedef enum _name _name; enum INDEXSTORE_OPTIONS_ATTRS _name : _type +#endif +#endif + +#ifndef INDEXSTORE_OPTIONS +# define INDEXSTORE_OPTIONS(_type, _name) typedef _type _name; enum INDEXSTORE_OPTIONS_ATTRS +#endif + +INDEXSTORE_BEGIN_DECLS + +typedef void *indexstore_error_t; + +INDEXSTORE_PUBLIC const char * +indexstore_error_get_description(indexstore_error_t); + +INDEXSTORE_PUBLIC void +indexstore_error_dispose(indexstore_error_t); + +typedef struct { + const char *data; + size_t length; +} indexstore_string_ref_t; + +INDEXSTORE_PUBLIC unsigned +indexstore_format_version(void); + +INDEXSTORE_PUBLIC unsigned indexstore_version(void); + +typedef void *indexstore_t; +typedef void *indexstore_creation_options_t; + +INDEXSTORE_PUBLIC indexstore_creation_options_t +indexstore_creation_options_create(void); + +INDEXSTORE_PUBLIC void +indexstore_creation_options_dispose(indexstore_creation_options_t); + +/// Adds a remapping from \c path_prefix to \c remapped_path_prefix. +/// +/// This should be used to convert hermetic or remote paths embedded in the index data to the +/// equivalent paths on the local machine. +INDEXSTORE_PUBLIC void +indexstore_creation_options_add_prefix_mapping(indexstore_creation_options_t options, + const char *path_prefix, + const char *remapped_path_prefix); + +INDEXSTORE_PUBLIC indexstore_t +indexstore_store_create(const char *store_path, indexstore_error_t *error); + + +/// Open the indexstore at the specified path using the specified options, which may be NULL. +INDEXSTORE_PUBLIC indexstore_t +indexstore_store_create_with_options(const char *store_path, indexstore_creation_options_t options, + indexstore_error_t *error); + +INDEXSTORE_PUBLIC void +indexstore_store_dispose(indexstore_t); + +#if INDEXSTORE_HAS_BLOCKS +INDEXSTORE_PUBLIC bool +indexstore_store_units_apply(indexstore_t, unsigned sorted, + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_string_ref_t unit_name)); +#endif + +INDEXSTORE_PUBLIC bool +indexstore_store_units_apply_f(indexstore_t, unsigned sorted, + void *context, + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_string_ref_t unit_name)); + +typedef void *indexstore_unit_event_notification_t; +typedef void *indexstore_unit_event_t; + +INDEXSTORE_PUBLIC size_t +indexstore_unit_event_notification_get_events_count(indexstore_unit_event_notification_t); + +INDEXSTORE_PUBLIC indexstore_unit_event_t +indexstore_unit_event_notification_get_event(indexstore_unit_event_notification_t, size_t index); + +INDEXSTORE_PUBLIC bool +indexstore_unit_event_notification_is_initial(indexstore_unit_event_notification_t); + +typedef enum { + INDEXSTORE_UNIT_EVENT_REMOVED = 2, + INDEXSTORE_UNIT_EVENT_MODIFIED = 3, + INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED = 4, + INDEXSTORE_UNIT_EVENT_FAILURE = 5, +} indexstore_unit_event_kind_t; + +INDEXSTORE_PUBLIC indexstore_unit_event_kind_t +indexstore_unit_event_get_kind(indexstore_unit_event_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_event_get_unit_name(indexstore_unit_event_t); + +#if INDEXSTORE_HAS_BLOCKS +typedef void (^indexstore_unit_event_handler_t)(indexstore_unit_event_notification_t); + +INDEXSTORE_PUBLIC void +indexstore_store_set_unit_event_handler(indexstore_t, + indexstore_unit_event_handler_t handler); +#endif + +INDEXSTORE_PUBLIC void +indexstore_store_set_unit_event_handler_f(indexstore_t, void *context, + void(*handler)(void *context, indexstore_unit_event_notification_t), + void(*finalizer)(void *context)); + +typedef struct { + /// If true, \c indexstore_store_start_unit_event_listening will block until + /// the initial set of units is passed to the unit event handler, otherwise + /// the function will return and the initial set will be passed asynchronously. + bool wait_initial_sync; +} indexstore_unit_event_listen_options_t; + +INDEXSTORE_PUBLIC bool +indexstore_store_start_unit_event_listening(indexstore_t, + indexstore_unit_event_listen_options_t *, + size_t listen_options_struct_size, + indexstore_error_t *error); + +INDEXSTORE_PUBLIC void +indexstore_store_stop_unit_event_listening(indexstore_t); + +INDEXSTORE_PUBLIC void +indexstore_store_discard_unit(indexstore_t, const char *unit_name); + +INDEXSTORE_PUBLIC void +indexstore_store_discard_record(indexstore_t, const char *record_name); + +INDEXSTORE_PUBLIC void +indexstore_store_purge_stale_data(indexstore_t); + +/// Determines the unit name from the \c output_path and writes it out in the +/// \c name_buf buffer. It doesn't write more than \c buf_size. +/// \returns the length of the name. If this is larger than \c buf_size, the +/// caller should call the function again with a buffer of the appropriate size. +INDEXSTORE_PUBLIC size_t +indexstore_store_get_unit_name_from_output_path(indexstore_t store, + const char *output_path, + char *name_buf, + size_t buf_size); + +/// \returns true if an error occurred, false otherwise. +INDEXSTORE_PUBLIC bool +indexstore_store_get_unit_modification_time(indexstore_t store, + const char *unit_name, + int64_t *seconds, + int64_t *nanoseconds, + indexstore_error_t *error); + +typedef void *indexstore_symbol_t; + +typedef enum { + INDEXSTORE_SYMBOL_KIND_UNKNOWN = 0, + INDEXSTORE_SYMBOL_KIND_MODULE = 1, + INDEXSTORE_SYMBOL_KIND_NAMESPACE = 2, + INDEXSTORE_SYMBOL_KIND_NAMESPACEALIAS = 3, + INDEXSTORE_SYMBOL_KIND_MACRO = 4, + INDEXSTORE_SYMBOL_KIND_ENUM = 5, + INDEXSTORE_SYMBOL_KIND_STRUCT = 6, + INDEXSTORE_SYMBOL_KIND_CLASS = 7, + INDEXSTORE_SYMBOL_KIND_PROTOCOL = 8, + INDEXSTORE_SYMBOL_KIND_EXTENSION = 9, + INDEXSTORE_SYMBOL_KIND_UNION = 10, + INDEXSTORE_SYMBOL_KIND_TYPEALIAS = 11, + INDEXSTORE_SYMBOL_KIND_FUNCTION = 12, + INDEXSTORE_SYMBOL_KIND_VARIABLE = 13, + INDEXSTORE_SYMBOL_KIND_FIELD = 14, + INDEXSTORE_SYMBOL_KIND_ENUMCONSTANT = 15, + INDEXSTORE_SYMBOL_KIND_INSTANCEMETHOD = 16, + INDEXSTORE_SYMBOL_KIND_CLASSMETHOD = 17, + INDEXSTORE_SYMBOL_KIND_STATICMETHOD = 18, + INDEXSTORE_SYMBOL_KIND_INSTANCEPROPERTY = 19, + INDEXSTORE_SYMBOL_KIND_CLASSPROPERTY = 20, + INDEXSTORE_SYMBOL_KIND_STATICPROPERTY = 21, + INDEXSTORE_SYMBOL_KIND_CONSTRUCTOR = 22, + INDEXSTORE_SYMBOL_KIND_DESTRUCTOR = 23, + INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION = 24, + INDEXSTORE_SYMBOL_KIND_PARAMETER = 25, + INDEXSTORE_SYMBOL_KIND_USING = 26, + INDEXSTORE_SYMBOL_KIND_CONCEPT = 27, + + INDEXSTORE_SYMBOL_KIND_COMMENTTAG = 1000, +} indexstore_symbol_kind_t; + +typedef enum { + INDEXSTORE_SYMBOL_SUBKIND_NONE = 0, + INDEXSTORE_SYMBOL_SUBKIND_CXXCOPYCONSTRUCTOR = 1, + INDEXSTORE_SYMBOL_SUBKIND_CXXMOVECONSTRUCTOR = 2, + INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER = 3, + INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER = 4, + INDEXSTORE_SYMBOL_SUBKIND_USINGTYPENAME = 5, + INDEXSTORE_SYMBOL_SUBKIND_USINGVALUE = 6, + INDEXSTORE_SYMBOL_SUBKIND_USINGENUM = 7, + + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORWILLSET = 1000, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORDIDSET = 1001, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORADDRESSOR = 1002, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMUTABLEADDRESSOR = 1003, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFSTRUCT = 1004, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFCLASS = 1005, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFENUM = 1006, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFPROTOCOL = 1007, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTPREFIXOPERATOR = 1008, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTPOSTFIXOPERATOR = 1009, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTINFIXOPERATOR = 1010, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTSUBSCRIPT = 1011, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTASSOCIATEDTYPE = 1012, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTGENERICTYPEPARAM = 1013, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORREAD = 1014, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMODIFY = 1015, + INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORINIT = 1016, +} indexstore_symbol_subkind_t; + +INDEXSTORE_OPTIONS(uint64_t, indexstore_symbol_property_t) { + INDEXSTORE_SYMBOL_PROPERTY_GENERIC = 1 << 0, + INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION = 1 << 1, + INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION = 1 << 2, + INDEXSTORE_SYMBOL_PROPERTY_UNITTEST = 1 << 3, + INDEXSTORE_SYMBOL_PROPERTY_IBANNOTATED = 1 << 4, + INDEXSTORE_SYMBOL_PROPERTY_IBOUTLETCOLLECTION = 1 << 5, + INDEXSTORE_SYMBOL_PROPERTY_GKINSPECTABLE = 1 << 6, + INDEXSTORE_SYMBOL_PROPERTY_LOCAL = 1 << 7, + INDEXSTORE_SYMBOL_PROPERTY_PROTOCOL_INTERFACE = 1 << 8, + + INDEXSTORE_SYMBOL_PROPERTY_SWIFT_ASYNC = 1 << 16, +}; + +typedef enum { + INDEXSTORE_SYMBOL_LANG_C = 0, + INDEXSTORE_SYMBOL_LANG_OBJC = 1, + INDEXSTORE_SYMBOL_LANG_CXX = 2, + + INDEXSTORE_SYMBOL_LANG_SWIFT = 100, +} indexstore_symbol_language_t; + +INDEXSTORE_OPTIONS(uint64_t, indexstore_symbol_role_t) { + INDEXSTORE_SYMBOL_ROLE_DECLARATION = 1 << 0, + INDEXSTORE_SYMBOL_ROLE_DEFINITION = 1 << 1, + INDEXSTORE_SYMBOL_ROLE_REFERENCE = 1 << 2, + INDEXSTORE_SYMBOL_ROLE_READ = 1 << 3, + INDEXSTORE_SYMBOL_ROLE_WRITE = 1 << 4, + INDEXSTORE_SYMBOL_ROLE_CALL = 1 << 5, + INDEXSTORE_SYMBOL_ROLE_DYNAMIC = 1 << 6, + INDEXSTORE_SYMBOL_ROLE_ADDRESSOF = 1 << 7, + INDEXSTORE_SYMBOL_ROLE_IMPLICIT = 1 << 8, + INDEXSTORE_SYMBOL_ROLE_UNDEFINITION = 1 << 19, + INDEXSTORE_SYMBOL_ROLE_NAMEREFERENCE = 1 << 20, + + // Relation roles. + INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF = 1 << 9, + INDEXSTORE_SYMBOL_ROLE_REL_BASEOF = 1 << 10, + INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF = 1 << 11, + INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY = 1 << 12, + INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY = 1 << 13, + INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY = 1 << 14, + INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF = 1 << 15, + INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY = 1 << 16, + INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF = 1 << 17, + INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF = 1 << 18, +}; + +INDEXSTORE_PUBLIC indexstore_symbol_language_t +indexstore_symbol_get_language(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_symbol_kind_t +indexstore_symbol_get_kind(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_symbol_subkind_t +indexstore_symbol_get_subkind(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_symbol_property_t +indexstore_symbol_get_properties(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_symbol_role_t +indexstore_symbol_get_roles(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_symbol_role_t +indexstore_symbol_get_related_roles(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_symbol_get_name(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_symbol_get_usr(indexstore_symbol_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_symbol_get_codegen_name(indexstore_symbol_t); + +typedef void *indexstore_symbol_relation_t; + +INDEXSTORE_PUBLIC indexstore_symbol_role_t +indexstore_symbol_relation_get_roles(indexstore_symbol_relation_t); + +INDEXSTORE_PUBLIC indexstore_symbol_t +indexstore_symbol_relation_get_symbol(indexstore_symbol_relation_t); + +typedef void *indexstore_occurrence_t; + +INDEXSTORE_PUBLIC indexstore_symbol_t +indexstore_occurrence_get_symbol(indexstore_occurrence_t); + +#if INDEXSTORE_HAS_BLOCKS +INDEXSTORE_PUBLIC bool +indexstore_occurrence_relations_apply(indexstore_occurrence_t, + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_symbol_relation_t symbol_rel)); +#endif + +INDEXSTORE_PUBLIC bool +indexstore_occurrence_relations_apply_f(indexstore_occurrence_t, + void *context, + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_symbol_relation_t symbol_rel)); + +INDEXSTORE_PUBLIC indexstore_symbol_role_t +indexstore_occurrence_get_roles(indexstore_occurrence_t); + +INDEXSTORE_PUBLIC void +indexstore_occurrence_get_line_col(indexstore_occurrence_t, + unsigned *line, unsigned *column); + +typedef void *indexstore_record_reader_t; + +INDEXSTORE_PUBLIC indexstore_record_reader_t +indexstore_record_reader_create(indexstore_t store, const char *record_name, + indexstore_error_t *error); + +INDEXSTORE_PUBLIC void +indexstore_record_reader_dispose(indexstore_record_reader_t); + +#if INDEXSTORE_HAS_BLOCKS +/// Goes through the symbol data and passes symbols to \c receiver, for the +/// symbol data that \c filter returns true on. +/// +/// This allows allocating memory only for the record symbols that the caller is +/// interested in. +INDEXSTORE_PUBLIC bool +indexstore_record_reader_search_symbols(indexstore_record_reader_t, + INDEXSTORE_NOESCAPE bool(^filter)(indexstore_symbol_t symbol, bool *stop), + INDEXSTORE_NOESCAPE void(^receiver)(indexstore_symbol_t symbol)); + +/// \param nocache if true, avoids allocating memory for the symbols. +/// Useful when the caller does not intend to keep \c indexstore_record_reader_t +/// for more queries. +INDEXSTORE_PUBLIC bool +indexstore_record_reader_symbols_apply(indexstore_record_reader_t, + bool nocache, + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_symbol_t symbol)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_apply(indexstore_record_reader_t, + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_occurrence_t occur)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_in_line_range_apply(indexstore_record_reader_t, + unsigned line_start, + unsigned line_count, + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_occurrence_t occur)); + +/// \param symbols if non-zero \c symbols_count, indicates the list of symbols +/// that we want to get occurrences for. An empty array indicates that we want +/// occurrences for all symbols. +/// \param related_symbols Same as \c symbols but for related symbols. +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_of_symbols_apply(indexstore_record_reader_t, + indexstore_symbol_t *symbols, size_t symbols_count, + indexstore_symbol_t *related_symbols, size_t related_symbols_count, + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_occurrence_t occur)); +#endif + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_search_symbols_f(indexstore_record_reader_t, + void *filter_ctx, + INDEXSTORE_NOESCAPE bool(*filter)(void *filter_ctx, indexstore_symbol_t symbol, bool *stop), + void *receiver_ctx, + INDEXSTORE_NOESCAPE void(*receiver)(void *receiver_ctx, indexstore_symbol_t symbol)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_symbols_apply_f(indexstore_record_reader_t, + bool nocache, + void *context, + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_symbol_t symbol)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_apply_f(indexstore_record_reader_t, + void *context, + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_occurrence_t occur)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_in_line_range_apply_f(indexstore_record_reader_t, + unsigned line_start, + unsigned line_count, + void *context, + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_occurrence_t occur)); + +INDEXSTORE_PUBLIC bool +indexstore_record_reader_occurrences_of_symbols_apply_f(indexstore_record_reader_t, + indexstore_symbol_t *symbols, size_t symbols_count, + indexstore_symbol_t *related_symbols, size_t related_symbols_count, + void *context, + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_occurrence_t occur)); + +typedef void *indexstore_unit_reader_t; + +INDEXSTORE_PUBLIC indexstore_unit_reader_t +indexstore_unit_reader_create(indexstore_t store, const char *unit_name, + indexstore_error_t *error); + +INDEXSTORE_PUBLIC void +indexstore_unit_reader_dispose(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_provider_identifier(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_provider_version(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC void +indexstore_unit_reader_get_modification_time(indexstore_unit_reader_t, + int64_t *seconds, + int64_t *nanoseconds); + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_is_system_unit(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_is_module_unit(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_is_debug_compilation(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_has_main_file(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_main_file(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_module_name(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_working_dir(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_output_file(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_sysroot_path(indexstore_unit_reader_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_reader_get_target(indexstore_unit_reader_t); + +typedef void *indexstore_unit_dependency_t; +typedef void *indexstore_unit_include_t; + +typedef enum { + INDEXSTORE_UNIT_DEPENDENCY_UNIT = 1, + INDEXSTORE_UNIT_DEPENDENCY_RECORD = 2, + INDEXSTORE_UNIT_DEPENDENCY_FILE = 3, +} indexstore_unit_dependency_kind_t; + +INDEXSTORE_PUBLIC indexstore_unit_dependency_kind_t +indexstore_unit_dependency_get_kind(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC bool +indexstore_unit_dependency_is_system(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_dependency_get_filepath(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_dependency_get_modulename(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_dependency_get_name(indexstore_unit_dependency_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_include_get_source_path(indexstore_unit_include_t); + +INDEXSTORE_PUBLIC indexstore_string_ref_t +indexstore_unit_include_get_target_path(indexstore_unit_include_t); + +INDEXSTORE_PUBLIC unsigned +indexstore_unit_include_get_source_line(indexstore_unit_include_t); + +#if INDEXSTORE_HAS_BLOCKS +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_dependencies_apply(indexstore_unit_reader_t, + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_unit_dependency_t)); + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_includes_apply(indexstore_unit_reader_t, + INDEXSTORE_NOESCAPE bool(^applier)(indexstore_unit_include_t)); +#endif + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_dependencies_apply_f(indexstore_unit_reader_t, + void *context, + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_unit_dependency_t)); + +INDEXSTORE_PUBLIC bool +indexstore_unit_reader_includes_apply_f(indexstore_unit_reader_t, + void *context, + INDEXSTORE_NOESCAPE bool(*applier)(void *context, indexstore_unit_include_t)); + +INDEXSTORE_END_DECLS + +#endif diff --git a/clang/include/module.modulemap b/clang/include/module.modulemap index 8489619832a47..59d96f65bcf8e 100644 --- a/clang/include/module.modulemap +++ b/clang/include/module.modulemap @@ -202,6 +202,9 @@ module Clang_Tooling { // importing the AST matchers library gives a link dependency on the AST // matchers (and thus the AST), which clang-format should not have. exclude header "clang/Tooling/RefactoringCallbacks.h" + exclude header "clang/Tooling/Refactor/USRFinder.h" + + textual header "clang/Tooling/Refactor/RefactoringActions.def" } module Clang_ToolingCore { diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 646eabd2a5ecd..d5eb03b1a54ba 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -681,6 +681,9 @@ class APINotesReader::Implementation { // which this API notes file was created, if known. std::optional> SourceFileSizeAndModTime; + /// Various options and attributes for the module + ModuleOptions ModuleOpts; + using SerializedIdentifierTable = llvm::OnDiskIterableChainedHashTable; @@ -895,6 +898,7 @@ bool APINotesReader::Implementation::readControlBlock( break; case control_block::MODULE_OPTIONS: + ModuleOpts.SwiftInferImportAsMember = (Scratch.front() & 1) != 0; break; case control_block::SOURCE_FILE: @@ -2034,6 +2038,14 @@ APINotesReader::Create(std::unique_ptr InputBuffer, return Reader; } +llvm::StringRef APINotesReader::getModuleName() const { + return Implementation->ModuleName; +} + +ModuleOptions APINotesReader::getModuleOptions() const { + return Implementation->ModuleOpts; +} + template APINotesReader::VersionedInfo::VersionedInfo( llvm::VersionTuple Version, diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp index 1aae07bbdd30e..ff9f2a505b1f2 100644 --- a/clang/lib/APINotes/APINotesWriter.cpp +++ b/clang/lib/APINotes/APINotesWriter.cpp @@ -35,6 +35,8 @@ class APINotesWriter::Implementation { /// Mapping from strings to identifier IDs. llvm::StringMap IdentifierIDs; + bool SwiftInferImportAsMember = false; + /// Information about contexts (Objective-C classes or protocols or C++ /// namespaces). /// @@ -278,6 +280,11 @@ void APINotesWriter::Implementation::writeControlBlock( control_block::ModuleNameLayout ModuleName(Stream); ModuleName.emit(Scratch, this->ModuleName); + if (SwiftInferImportAsMember) { + control_block::ModuleOptionsLayout moduleOptions(Stream); + moduleOptions.emit(Scratch, SwiftInferImportAsMember); + } + if (SourceFile) { control_block::SourceFileLayout SourceFile(Stream); SourceFile.emit(Scratch, this->SourceFile->getSize(), @@ -1561,5 +1568,9 @@ void APINotesWriter::addTypedef(std::optional Ctx, SingleDeclTableKey Key(Ctx, TypedefID); Implementation->Typedefs[Key].push_back({SwiftVersion, Info}); } + +void APINotesWriter::addModuleOptions(ModuleOptions opts) { + Implementation->SwiftInferImportAsMember = opts.SwiftInferImportAsMember; +} } // namespace api_notes } // namespace clang diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp index 414a59a4f12d0..bdcf99fa67b7c 100644 --- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp @@ -29,6 +29,119 @@ using namespace clang; using namespace api_notes; +/* + + YAML Format specification. + + Nullability should be expressed using one of the following values: + O - Optional (or Nullable) + N - Not Optional + S - Scalar + U - Unknown + Note, the API is considered 'audited' when at least the return value or a + parameter has a nullability value. For 'audited' APIs, we assume the default + nullability for any underspecified type. + +--- + Name: AppKit # The name of the framework + + Availability: OSX # Optional: Specifies which platform the API is + # available on. [OSX / iOS / none/ + # available / nonswift] + + AvailabilityMsg: "" # Optional: Custom availability message to display to + # the user, when API is not available. + + Classes: # List of classes + ... + Protocols: # List of protocols + ... + Functions: # List of functions + ... + Globals: # List of globals + ... + Enumerators: # List of enumerators + ... + Tags: # List of tags (struct/union/enum/C++ class) + ... + Typedefs: # List of typedef-names and C++11 type aliases + ... + + Each class and protocol is defined as following: + + - Name: NSView # The name of the class + + AuditedForNullability: false # Optional: Specifies if the whole class + # has been audited for nullability. + # If yes, we assume all the methods and + # properties of the class have default + # nullability unless it is overwritten by + # a method/property specific info below. + # This applies to all classes, extensions, + # and categories of the class defined in + # the current framework/module. + # (false/true) + + Availability: OSX + + AvailabilityMsg: "" + + Methods: + - Selector: "setSubviews:" # Full name + + MethodKind: Instance # [Class/Instance] + + Nullability: [N, N, O, S] # The nullability of parameters in + # the signature. + + NullabilityOfRet: O # The nullability of the return value. + + Availability: OSX + + AvailabilityMsg: "" + + DesignatedInit: false # Optional: Specifies if this method is a + # designated initializer (false/true) + + Required: false # Optional: Specifies if this method is a + # required initializer (false/true) + + Properties: + - Name: window + + Nullability: O + + Availability: OSX + + AvailabilityMsg: "" + + The protocol definition format is the same as the class definition. + + Each function definition is of the following form: + + - Name: "myGlobalFunction" # Full name + + Nullability: [N, N, O, S] # The nullability of parameters in + # the signature. + + NullabilityOfRet: O # The nullability of the return value. + + Availability: OSX + + AvailabilityMsg: "" + +Each global variable definition is of the following form: + + - Name: MyGlobalVar + + Nullability: O + + Availability: OSX + + AvailabilityMsg: "" + +*/ + namespace { enum class APIAvailability { Available = 0, diff --git a/clang/lib/APINotes/CMakeLists.txt b/clang/lib/APINotes/CMakeLists.txt index dc83edd911ce2..fdc9e6563428d 100644 --- a/clang/lib/APINotes/CMakeLists.txt +++ b/clang/lib/APINotes/CMakeLists.txt @@ -8,5 +8,7 @@ add_clang_library(clangAPINotes APINotesTypes.cpp APINotesWriter.cpp APINotesYAMLCompiler.cpp + Types.cpp + LINK_LIBS clangBasic) diff --git a/clang/lib/APINotes/Types.cpp b/clang/lib/APINotes/Types.cpp new file mode 100644 index 0000000000000..956ab233b4c51 --- /dev/null +++ b/clang/lib/APINotes/Types.cpp @@ -0,0 +1,42 @@ +//===--- Types.cpp - API Notes Data Types ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines data types used in the representation of API notes data. +// +//===----------------------------------------------------------------------===// +#include "clang/APINotes/Types.h" + +void clang::api_notes::ObjCMethodInfo::mergePropInfoIntoSetter( + const ObjCPropertyInfo &pInfo) { + // Set the type of the first argument of the the setter or check that the + // value we have is consistent with the property. + // TODO: Can we provide proper error handling here? + if (auto pNullability = pInfo.getNullability()) { + if (!NullabilityAudited) { + addParamTypeInfo(0, *pNullability); + assert(NumAdjustedNullable == 2); + } else { + assert(getParamTypeInfo(0) == *pNullability); + } + } +} + +void clang::api_notes::ObjCMethodInfo::mergePropInfoIntoGetter( + const ObjCPropertyInfo &pInfo) { + // Set the return type of the getter or check that the value we have is + // consistent with the property. + // TODO: Can we provide proper error handling here? + if (auto pNullability = pInfo.getNullability()) { + if (!NullabilityAudited) { + addReturnTypeInfo(*pNullability); + assert(NumAdjustedNullable == 1); + } else { + assert(getReturnTypeInfo() == *pNullability); + } + } +} diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp index 7c33d3a165a08..6fa07dba99676 100644 --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -44,14 +44,64 @@ APValue::LValueBase::LValueBase(const ValueDecl *P, unsigned I, unsigned V) APValue::LValueBase::LValueBase(const Expr *P, unsigned I, unsigned V) : Ptr(P), Local{I, V} {} +// Separated definitions of these template functions to avoid nasty build issues +// caused by header dependency. +template +APValue::LValueBase::EnableIfDAOrForged +APValue::LValueBase::is() const { + if (!isa(Ptr)) + return false; + DynamicAllocOrForgedPtrLValue::BaseTy DAOrForged = + cast(Ptr); + return isa(DAOrForged); +} + +template APValue::LValueBase::EnableIfDAOrForged +APValue::LValueBase::is() const; +template APValue::LValueBase::EnableIfDAOrForged +APValue::LValueBase::is() const; + +template +APValue::LValueBase::EnableIfDAOrForged APValue::LValueBase::get() const { + DynamicAllocOrForgedPtrLValue::BaseTy DAOrForged = + cast(Ptr); + return cast(DAOrForged); +} + +template APValue::LValueBase::EnableIfDAOrForged +APValue::LValueBase::get() const; +template APValue::LValueBase::EnableIfDAOrForged +APValue::LValueBase::get() const; + +template +APValue::LValueBase::EnableIfDAOrForged +APValue::LValueBase::dyn_cast() const { + if (is()) + return cast(Ptr).dyn_cast(); + return T(); +} + +template APValue::LValueBase::EnableIfDAOrForged +APValue::LValueBase::dyn_cast() const; +template APValue::LValueBase::EnableIfDAOrForged +APValue::LValueBase::dyn_cast() const; + APValue::LValueBase APValue::LValueBase::getDynamicAlloc(DynamicAllocLValue LV, QualType Type) { LValueBase Base; - Base.Ptr = LV; + Base.Ptr = DynamicAllocOrForgedPtrLValue(LV); Base.DynamicAllocType = Type.getAsOpaquePtr(); return Base; } +APValue::LValueBase APValue::LValueBase::getForgedPtr(ForgedPtrLValue LV, + QualType Type) { + LValueBase Base; + Base.Ptr = DynamicAllocOrForgedPtrLValue(LV); + Base.ForgedPtrAsArrayType = Type.getAsOpaquePtr(); + return Base; +} + APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV, QualType TypeInfo) { LValueBase Base; @@ -61,6 +111,11 @@ APValue::LValueBase APValue::LValueBase::getTypeInfo(TypeInfoLValue LV, } QualType APValue::LValueBase::getType() const { + // BOUNDS-SAFETY: forged ptr's base is null, when it's constant but it can + // still have a valid type information. + if (is()) + return getForgedPtrAsArrayType(); + if (!*this) return QualType(); if (const ValueDecl *D = dyn_cast()) { // FIXME: It's unclear where we're supposed to take the type from, and @@ -106,12 +161,17 @@ QualType APValue::LValueBase::getType() const { } unsigned APValue::LValueBase::getCallIndex() const { - return (is() || is()) ? 0 - : Local.CallIndex; + return (is() || is() || + is()) + ? 0 + : Local.CallIndex; } unsigned APValue::LValueBase::getVersion() const { - return (is() || is()) ? 0 : Local.Version; + return (is() || is() || + is()) + ? 0 + : Local.Version; } QualType APValue::LValueBase::getTypeInfoType() const { @@ -124,9 +184,23 @@ QualType APValue::LValueBase::getDynamicAllocType() const { return QualType::getFromOpaquePtr(DynamicAllocType); } +QualType APValue::LValueBase::getForgedPtrAsArrayType() const { + assert(is() && "not a forged ptr lvalue"); + return QualType::getFromOpaquePtr(ForgedPtrAsArrayType); +} + +const ValueDecl *APValue::LValueBase::getValueDecl() const { + if (const ValueDecl *D = dyn_cast()) + return D; + if (ForgedPtrLValue LV = dyn_cast()) + return LV.getBaseValueDecl(); + return nullptr; +} + void APValue::LValueBase::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(Ptr.getOpaqueValue()); - if (is() || is()) + if (is() || is() || + is()) return; ID.AddInteger(Local.CallIndex); ID.AddInteger(Local.Version); @@ -137,7 +211,8 @@ bool operator==(const APValue::LValueBase &LHS, const APValue::LValueBase &RHS) { if (LHS.Ptr != RHS.Ptr) return false; - if (LHS.is() || LHS.is()) + if (LHS.is() || LHS.is() || + LHS.is()) return true; return LHS.Local.CallIndex == RHS.Local.CallIndex && LHS.Local.Version == RHS.Local.Version; @@ -162,16 +237,6 @@ QualType APValue::LValuePathSerializationHelper::getType() { return QualType::getFromOpaquePtr(Ty); } -namespace { - struct LVBase { - APValue::LValueBase Base; - CharUnits Offset; - unsigned PathLength; - bool IsNullPtr : 1; - bool IsOnePastTheEnd : 1; - }; -} - void *APValue::LValueBase::getOpaqueValue() const { return Ptr.getOpaqueValue(); } @@ -181,6 +246,9 @@ bool APValue::LValueBase::isNull() const { } APValue::LValueBase::operator bool () const { + if (is()) { + return static_cast(get()); + } return static_cast(Ptr); } @@ -200,7 +268,8 @@ llvm::DenseMapInfo::getTombstoneKey() { namespace clang { llvm::hash_code hash_value(const APValue::LValueBase &Base) { - if (Base.is() || Base.is()) + if (Base.is() || Base.is() || + Base.is()) return llvm::hash_value(Base.getOpaqueValue()); return llvm::hash_combine(Base.getOpaqueValue(), Base.getCallIndex(), Base.getVersion()); @@ -219,6 +288,7 @@ bool llvm::DenseMapInfo::isEqual( } struct APValue::LV : LVBase { + static_assert(DataSize >= sizeof(LVBase) && "LVBase too big"); static const unsigned InlinePathSpace = (DataSize - sizeof(LVBase)) / sizeof(LValuePathEntry); @@ -349,6 +419,20 @@ APValue::APValue(const APValue &RHS) else setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), NoLValuePath(), RHS.isNullPointer()); + if (RHS.isLValueForgeSingle()) { + assert(!isNullPointer() && !isLValueForgeBidi() && + !isLValueForgeTerminatedBy()); + setLValueForgedSingle(RHS.getLValueForgedOffset()); + } else if (RHS.isLValueForgeBidi()) { + assert(!isNullPointer() && !isLValueForgeSingle() && + !isLValueForgeTerminatedBy()); + setLValueForgedBidi(RHS.getLValueForgedSize(), + RHS.getLValueForgedOffset()); + } else if (RHS.isLValueForgeTerminatedBy()) { + assert(!isNullPointer() && !isLValueForgeSingle() && + !isLValueForgeBidi()); + setLValueForgedTerminatedBy(RHS.getLValueForgedOffset()); + } break; case Array: MakeArray(RHS.getArrayInitializedElts(), RHS.getArraySize()); @@ -616,6 +700,8 @@ void APValue::Profile(llvm::FoldingSetNodeID &ID) const { for (LValuePathEntry E : getLValuePath()) E.Profile(ID); } + ID.AddInteger(getLValueForgedOffset().getQuantity()); + ID.AddInteger(getLValueForgedSize().getQuantity()); return; case MemberPointer: @@ -700,6 +786,34 @@ static bool TryPrintAsStringLiteral(raw_ostream &Out, return true; } +static void printForgedPtrLValue(raw_ostream &Out, const PrintingPolicy &Policy, + const ASTContext *Ctx, const APValue &V, + ForgedPtrLValue FP, QualType Ty) { + Out << "__unsafe_forge_" + << (V.isLValueForgeSingle() + ? "single(" + : (V.isLValueForgeTerminatedBy() ? "terminated_by(" + : "bidi_indexable(")); + QualType(Ty->getPointeeOrArrayElementType(), 0).print(Out, Policy); + Out << ", "; + + if (auto BaseVD = FP.getBaseValueDecl()) { + Out << *BaseVD << " + "; + } + // LValueOffset + ForgedOffset + Out << V.getUnwrappedLValueOffset().getQuantity(); + + if (V.isLValueForgeSingle()) { + Out << ")"; + } else if (V.isLValueForgeTerminatedBy()) { + Out << ", " << Ty->getAs()->getTerminatorValue(*Ctx) + << ")"; + } else { + assert(V.isLValueForgeBidi()); + Out << ", " << V.getLValueForgedSize().getQuantity() << ")"; + } +} + void APValue::printPretty(raw_ostream &Out, const ASTContext &Ctx, QualType Ty) const { printPretty(Out, Ctx.getPrintingPolicy(), Ty, &Ctx); @@ -767,17 +881,17 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy, Out << (Policy.Nullptr ? "nullptr" : "0"); } else if (IsReference) { Out << "*(" << InnerTy.stream(Policy) << "*)" - << getLValueOffset().getQuantity(); + << getUnwrappedLValueOffset().getQuantity(); } else { Out << "(" << Ty.stream(Policy) << ")" - << getLValueOffset().getQuantity(); + << getUnwrappedLValueOffset().getQuantity(); } return; } if (!hasLValuePath()) { // No lvalue path: just print the offset. - CharUnits O = getLValueOffset(); + CharUnits O = getUnwrappedLValueOffset(); CharUnits S = Ctx ? Ctx->getTypeSizeInCharsIfKnown(InnerTy).value_or( CharUnits::Zero()) : CharUnits::Zero(); @@ -801,6 +915,8 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy, Out << "{*new " << Base.getDynamicAllocType().stream(Policy) << "#" << DA.getIndex() << "}"; + } else if (ForgedPtrLValue FP = Base.dyn_cast()) { + printForgedPtrLValue(Out, Policy, Ctx, *this, FP, Ty); } else { assert(Base.get() != nullptr && "Expecting non-null Expr"); @@ -829,6 +945,9 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy, } else if (DynamicAllocLValue DA = Base.dyn_cast()) { Out << "{*new " << Base.getDynamicAllocType().stream(Policy) << "#" << DA.getIndex() << "}"; + } else if (ForgedPtrLValue FP = Base.dyn_cast()) { + printForgedPtrLValue(Out, Policy, Ctx, *this, FP, Ty); + return; } else { const Expr *E = Base.get(); assert(E != nullptr && "Expecting non-null Expr"); @@ -973,7 +1092,7 @@ bool APValue::toIntegralConstant(APSInt &Result, QualType SrcTy, } if (isLValue() && !getLValueBase()) { - Result = Ctx.MakeIntValue(getLValueOffset().getQuantity(), SrcTy); + Result = Ctx.MakeIntValue(getUnwrappedLValueOffset().getQuantity(), SrcTy); return true; } @@ -995,6 +1114,22 @@ CharUnits &APValue::getLValueOffset() { return ((LV *)(void *)&Data)->Offset; } +CharUnits &APValue::getLValueForgedSize() { + assert(isLValue() && "Invalid accessor"); + return ((LV *)(void *)&Data)->ForgedSize; +} + +CharUnits &APValue::getLValueForgedOffset() { + assert(isLValue() && "Invalid accessor"); + return ((LV *)(void *)&Data)->ForgedOffset; +} + +CharUnits APValue::getUnwrappedLValueOffset() const { + if (isLValueForge()) + return getLValueOffset() + getLValueForgedOffset(); + return getLValueOffset(); +} + bool APValue::hasLValuePath() const { assert(isLValue() && "Invalid accessor"); return ((const LV *)(const char *)&Data)->hasPath(); @@ -1021,6 +1156,27 @@ bool APValue::isNullPointer() const { return ((const LV *)(const char *)&Data)->IsNullPtr; } +bool APValue::isLValueForgeBidi() const { + assert(isLValue() && "Invalid usage"); + return ((const LV *)(const char *)&Data)->IsForgeBidi; +} + +bool APValue::isLValueForgeSingle() const { + assert(isLValue() && "Invalid usage"); + return ((const LV *)(const char *)&Data)->IsForgeSingle; +} + +bool APValue::isLValueForgeTerminatedBy() const { + assert(isLValue() && "Invalid usage"); + return ((const LV *)(const char *)&Data)->IsForgeTerminatedBy; +} + +bool APValue::isLValueForge() const { + assert(isLValue() && "Invalid usage"); + return isLValueForgeBidi() || isLValueForgeSingle() || + isLValueForgeTerminatedBy(); +} + void APValue::setLValue(LValueBase B, const CharUnits &O, NoLValuePath, bool IsNullPtr) { assert(isLValue() && "Invalid accessor"); @@ -1030,6 +1186,10 @@ void APValue::setLValue(LValueBase B, const CharUnits &O, NoLValuePath, LVal.Offset = O; LVal.resizePath((unsigned)-1); LVal.IsNullPtr = IsNullPtr; + LVal.IsForgeBidi = false; + LVal.IsForgeSingle = false; + LVal.IsForgeTerminatedBy = false; + LVal.ForgedSize = CharUnits::Zero(); } MutableArrayRef @@ -1042,9 +1202,47 @@ APValue::setLValueUninit(LValueBase B, const CharUnits &O, unsigned Size, LVal.Offset = O; LVal.IsNullPtr = IsNullPtr; LVal.resizePath(Size); + LVal.IsForgeBidi = false; + LVal.IsForgeSingle = false; + LVal.IsForgeTerminatedBy = false; + LVal.ForgedSize = CharUnits::Zero(); return {LVal.getPath(), Size}; } +void APValue::setLValueForgedBidi(const CharUnits &Size, + const CharUnits &Offset) { + assert(isLValue() && "Invalid accessor"); + LV &LVal = *((LV *)(char *)&Data); + // For the purposes of constant evaluation, forged lvalues are never NULL, + // even if their address is 0. + LVal.IsNullPtr = false; + LVal.IsForgeBidi = true; + LVal.ForgedSize = Size; + LVal.IsForgeSingle = false; + LVal.IsForgeTerminatedBy = false; + LVal.ForgedOffset = Offset; +} + +void APValue::setLValueForgedSingle(const CharUnits &Offset) { + assert(isLValue() && "Invalid accessor"); + LV &LVal = *((LV *)(char *)&Data); + LVal.IsNullPtr = false; + LVal.IsForgeBidi = false; + LVal.IsForgeSingle = true; + LVal.IsForgeTerminatedBy = false; + LVal.ForgedOffset = Offset; +} + +void APValue::setLValueForgedTerminatedBy(const CharUnits &Offset) { + assert(isLValue() && "Invalid accessor"); + LV &LVal = *((LV *)(char *)&Data); + LVal.IsNullPtr = false; + LVal.IsForgeBidi = false; + LVal.IsForgeSingle = false; + LVal.IsForgeTerminatedBy = true; + LVal.ForgedOffset = Offset; +} + void APValue::setLValue(LValueBase B, const CharUnits &O, ArrayRef Path, bool IsOnePastTheEnd, bool IsNullPtr) { @@ -1192,6 +1390,12 @@ LinkageInfo LinkageComputer::getLVForValue(const APValue &V, return LinkageInfo::internal(); if (MergeLV(getLVForDecl(MTE->getExtendingDecl(), computation))) break; + } else if (const auto FP = V.getLValueBase().dyn_cast()) { + if (const auto *VD = FP.getBaseValueDecl()) { + MergeLV(getLVForDecl(VD, computation)); + } + // Otherwise, it's null or absolute address, which is considered external. + break; } else { assert(V.getLValueBase().is() && "unexpected LValueBase kind"); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index c95e733f30494..b1bc1a9b2c0aa 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -36,6 +36,7 @@ #include "clang/AST/Mangle.h" #include "clang/AST/MangleNumberingContext.h" #include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/ObjCMethodReferenceInfo.h" #include "clang/AST/ParentMapContext.h" #include "clang/AST/RawCommentList.h" #include "clang/AST/RecordLayout.h" @@ -44,6 +45,7 @@ #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" +#include "clang/AST/TypeVisitor.h" #include "clang/AST/UnresolvedSet.h" #include "clang/AST/VTableBuilder.h" #include "clang/Basic/AddressSpaces.h" @@ -63,6 +65,7 @@ #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetCXXABI.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" #include "clang/Basic/XRayLists.h" #include "llvm/ADT/APFixedPoint.h" #include "llvm/ADT/APInt.h" @@ -81,8 +84,10 @@ #include "llvm/Support/Capacity.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/SipHash.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/AArch64TargetParser.h" @@ -941,7 +946,8 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, DependentTypeOfExprTypes(this_()), DependentDecltypeTypes(this_()), TemplateSpecializationTypes(this_()), DependentTemplateSpecializationTypes(this_()), - DependentBitIntTypes(this_()), SubstTemplateTemplateParmPacks(this_()), + DependentBitIntTypes(this_()), ValueTerminatedTypes(this_()), + SubstTemplateTemplateParmPacks(this_()), DeducedTemplates(this_()), ArrayParameterTypes(this_()), CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts), NoSanitizeL(new NoSanitizeList(LangOpts.NoSanitizeFiles, SM)), @@ -956,6 +962,102 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, addTranslationUnitDecl(); } +ASTContext::AvailabilityDomainInfo +ASTContext::getFeatureAvailInfo(StringRef FeatureName) const { + FeatureAvailKind Kind = getLangOpts().getFeatureAvailability(FeatureName); + + if (Kind != FeatureAvailKind::None) + return {Kind}; + + Decl *D = nullptr; + auto I = AvailabilityDomainMap.find(FeatureName); + if (I != AvailabilityDomainMap.end()) + D = I->second; + else if (auto *Source = getExternalSource()) + D = Source->getAvailabilityDomainDecl(FeatureName); + + if (D) + return getFeatureAvailInfo(D).second; + + return {}; +} + +std::pair +ASTContext::getFeatureAvailInfo(Decl *D) const { + auto *VD = cast(D); + auto *Attr = VD->getAttr(); + if (!Attr) + return {}; + StringRef Name = Attr->getName()->getName(); + auto *Init = cast(VD->getInit()); + Expr::EvalResult Result; + FeatureAvailKind Kind; + + if (!Init->getInit(0)->IgnoreParenImpCasts()->EvaluateAsInt(Result, *this)) + llvm_unreachable("initializer doesn't evaluate to integer"); + llvm::APSInt Res = Result.Val.getInt(); + unsigned Val = Res.getExtValue(); + switch (Val) { + case 0: + Kind = FeatureAvailKind::Available; + break; + case 1: + Kind = FeatureAvailKind::Unavailable; + break; + case 2: + Kind = FeatureAvailKind::Dynamic; + break; + default: + llvm_unreachable("invalid feature kind"); + } + + ASTContext::AvailabilityDomainInfo Info{Kind, D, nullptr}; + + if (Kind == FeatureAvailKind::Dynamic) { + Expr *FnExpr = Init->getInit(1); + auto *Call = CallExpr::Create(*this, FnExpr, {}, IntTy, VK_PRValue, + SourceLocation(), FPOptionsOverride()); + Info.Call = + ImplicitCastExpr::Create(*this, BoolTy, CK_IntegralToBoolean, Call, + nullptr, VK_PRValue, FPOptionsOverride()); + } + + return {Name, Info}; +} + +std::pair +ASTContext::checkNewFeatureAvailability(Decl *D, StringRef NewDomainName, + bool NewUnavailable) { + assert(!NewDomainName.empty()); + + for (auto *AA : D->specific_attrs()) { + if (AA->getDomain() != NewDomainName) + continue; + if (AA->getUnavailable() == NewUnavailable) + return {AA, true}; + return {AA, false}; + } + + return {nullptr, true}; +} + +bool ASTContext::hasFeatureAvailabilityAttr(const Decl *D) const { + return !D->specific_attrs().empty(); +} + +bool ASTContext::hasUnavailableFeature(const Decl *D) const { + for (auto *AA : D->specific_attrs()) { + auto FeatureName = AA->getDomain(); + auto FeatureInfo = getFeatureAvailInfo(FeatureName); + if (FeatureInfo.Kind == (AA->getUnavailable() + ? FeatureAvailKind::Available + : FeatureAvailKind::Unavailable)) + return true; + } + + return false; +} + void ASTContext::cleanup() { // Release the DenseMaps associated with DeclContext objects. // FIXME: Is this the ideal solution? @@ -2339,11 +2441,17 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { Width = Target->getPointerWidth(AS); Align = Target->getPointerAlign(AS); break; - case Type::Pointer: - AS = cast(T)->getPointeeType().getAddressSpace(); - Width = Target->getPointerWidth(AS); + case Type::Pointer: { + const auto *PT = cast(T); + AS = PT->getPointeeType().getAddressSpace(); + uint64_t RawPtrWidth = Width = Target->getPointerWidth(AS); Align = Target->getPointerAlign(AS); + if (PT->getPointerAttributes().hasUpperBound()) + Width += RawPtrWidth; + if (PT->getPointerAttributes().hasLowerBound()) + Width += RawPtrWidth; break; + } case Type::MemberPointer: { const auto *MPT = cast(T); CXXABI::MemberPointerInfo MPI = ABI->getMemberPointerInfo(MPT); @@ -2466,6 +2574,15 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { case Type::CountAttributed: return getTypeInfo(cast(T)->desugar().getTypePtr()); + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: + return getTypeInfo( + cast(T)->desugar().getTypePtr()); + + case Type::ValueTerminated: + return getTypeInfo(cast(T)->desugar().getTypePtr()); + /* TO_UPSTREAM(BoundsSafety) OFF */ + case Type::BTFTagAttributed: return getTypeInfo( cast(T)->getWrappedType().getTypePtr()); @@ -3499,7 +3616,6 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx, return; } } - uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) { assert(!T->isDependentType() && "cannot compute type discriminator of a dependent type"); @@ -3578,11 +3694,497 @@ QualType ASTContext::getObjCGCQualType(QualType T, return getExtQualType(TypeNode, Quals); } +/* TO_UPSTREAM(BoundsSafety) ON */ +QualType ASTContext::getDynamicRangePointerType( + QualType PointerTy, Expr *StartPtr, Expr *EndPtr, + ArrayRef StartPtrDecls, + ArrayRef EndPtrDecls) const { + assert(PointerTy->isPointerType()); + + llvm::FoldingSetNodeID ID; + DynamicRangePointerType::Profile(ID, PointerTy, StartPtr, EndPtr, + StartPtrDecls.size(), EndPtrDecls.size()); + + void *InsertPos = nullptr; + DynamicRangePointerType *DRPTy = + DynamicRangePointerTypes.FindNodeOrInsertPos(ID, InsertPos); + if (DRPTy) + return QualType(DRPTy, 0); + + QualType CanonPTy = getCanonicalType(PointerTy); + size_t Size = + DynamicRangePointerType::totalSizeToAlloc( + EndPtrDecls.size() + StartPtrDecls.size()); + DRPTy = (DynamicRangePointerType *)Allocate(Size, TypeAlignment); + new (DRPTy) DynamicRangePointerType(PointerTy, CanonPTy, StartPtr, EndPtr, + StartPtrDecls, EndPtrDecls); + Types.push_back(DRPTy); + DynamicRangePointerTypes.InsertNode(DRPTy, InsertPos); + + return QualType(DRPTy, 0); +} + +QualType ASTContext::getValueTerminatedType(QualType T, + Expr *TerminatorExpr) const { + assert(T->isConstantArrayType() || T->isIncompleteArrayType() || + T->isPointerType()); + assert(!T->isValueTerminatedType()); + + SplitQualType Split = T.getSplitUnqualifiedType(); + + llvm::FoldingSetNodeID ID; + ValueTerminatedType::Profile(ID, *this, QualType(Split.Ty, 0), + TerminatorExpr); + + void *InsertPos = nullptr; + ValueTerminatedType *VTT = + ValueTerminatedTypes.FindNodeOrInsertPos(ID, InsertPos); + if (VTT) + return getQualifiedType(VTT, Split.Quals); + + QualType CanonTy = Split.Ty->getCanonicalTypeInternal(); + VTT = new (*this, TypeAlignment) + ValueTerminatedType(QualType(Split.Ty, 0), CanonTy, TerminatorExpr); + Types.push_back(VTT); + ValueTerminatedTypes.InsertNode(VTT, InsertPos); + + return getQualifiedType(VTT, Split.Quals); +} + +QualType ASTContext::getBoundsSafetyPointerType(QualType PointerTy, + BoundsSafetyPointerAttributes Attr) { + const auto *PTy = PointerTy->getAs(); + if (!PTy || PTy->getPointerAttributes() == Attr || + PointerTy->isBoundsAttributedType()) + return PointerTy; + + std::function + getBoundsSafetyPointerTypeRecurse = [&](QualType Ty) -> QualType { + if (const auto *AT = Ty->getAs()) { + auto ModifiedTy = + getBoundsSafetyPointerTypeRecurse(AT->getModifiedType()); + auto EquivalentTy = + getBoundsSafetyPointerTypeRecurse(AT->getEquivalentType()); + + auto QualsOnT = Ty.getQualifiers(); + auto QualsOnModifTy = AT->getModifiedType().getQualifiers(); + Ty = getAttributedType(AT->getAttrKind(), ModifiedTy, + EquivalentTy); + + Qualifiers::removeCommonQualifiers(QualsOnT, QualsOnModifTy); + if (!QualsOnT.empty()) { + QualifierCollector QC(QualsOnT); + Ty = QC.apply(*this, Ty); + } + return Ty; + } + assert(PointerTy->isPointerType()); + auto QualsOnT = PointerTy.getQualifiers(); + Ty = getPointerType(PointerTy->getPointeeType(), Attr); + if (!QualsOnT.empty()) { + QualifierCollector QC(QualsOnT); + Ty = QC.apply(*this, Ty); + } + return Ty; + }; + + return getBoundsSafetyPointerTypeRecurse(PointerTy); +} + +/// Copies bounds Type node from \c SrcTy to \c DestTy if it does +/// not already have the corresponding Type. +static QualType assureMandatorySugarTypesRemain(ASTContext &Ctx, + QualType DestTy, + QualType SrcTy) { + if (auto VTT = SrcTy->getAs()) { + if (!DestTy->getAs()) { + return Ctx.getValueTerminatedType(DestTy, VTT->getTerminatorExpr()); + } + } else if (auto DCPT = SrcTy->getAs()) { + if (!DestTy->getAs()) { + return Ctx.getCountAttributedType(DestTy, DCPT->getCountExpr(), + DCPT->isCountInBytes(), DCPT->isOrNull(), + DCPT->getCoupledDecls()); + } + } else if (auto DRPT = SrcTy->getAs()) { + if (!DestTy->getAs()) { + return Ctx.getDynamicRangePointerType( + DestTy, DRPT->getStartPointer(), DRPT->getEndPointer(), + DRPT->getStartPtrDecls(), DRPT->getEndPtrDecls()); + } + } + + return DestTy; +} + +QualType ASTContext::mergeBoundsSafetyPointerTypes( + QualType DstTy, QualType SrcTy, + std::function &MergeFunctor, + QualType OrigDstTy) { + if (!DstTy->isPointerType()) + return DstTy; + + if (OrigDstTy.isNull()) + OrigDstTy = DstTy; + + // FIXME: a brittle hack to avoid skipping ValueTerminatedType outside + // this PtrAutoAttr AttributedType. + bool RecoverPtrAuto = false; + if (const auto *VT = dyn_cast(DstTy)) { + const auto *AT = DstTy->getAs(); + if (AT && VT->hasAttr(attr::PtrAutoAttr)) { + RecoverPtrAuto = true; + } + } + + const auto *AT = DstTy->getAs(); + if (AT && !RecoverPtrAuto) { + auto ModifiedTy = mergeBoundsSafetyPointerTypes( + AT->getModifiedType(), SrcTy, MergeFunctor, OrigDstTy); + if (ModifiedTy.isNull()) + return QualType(); + auto EquivalentTy = mergeBoundsSafetyPointerTypes( + AT->getEquivalentType(), SrcTy, MergeFunctor, OrigDstTy); + + auto QualsOnT = DstTy.getQualifiers(); + auto QualsOnModifTy = AT->getModifiedType().getQualifiers(); + auto NewDstTy = + getAttributedType(AT->getAttrKind(), ModifiedTy, EquivalentTy); + + Qualifiers::removeCommonQualifiers(QualsOnT, QualsOnModifTy); + if (!QualsOnT.empty()) { + QualifierCollector QC(QualsOnT); + NewDstTy = QC.apply(*this, NewDstTy); + } + // Make sure we don't lose ValueTerminatedType + NewDstTy = assureMandatorySugarTypesRemain(*this, NewDstTy, DstTy); + return NewDstTy; + } + + QualType DstPointeeTy = DstTy->getPointeeType(); + QualType SrcPointeeTy = + !SrcTy.isNull() ? SrcTy->getPointeeType() : QualType(); + QualType MergePointeeTy = + mergeBoundsSafetyPointerTypes(DstPointeeTy, SrcPointeeTy, MergeFunctor); + QualType MergeTy = MergeFunctor(DstTy, SrcTy, MergePointeeTy, OrigDstTy); + if (DstTy != MergeTy && !MergeTy.isNull()) { + if (RecoverPtrAuto) { + MergeTy = getAttributedType(attr::PtrAutoAttr, MergeTy, MergeTy); + } + auto QualsOnT = DstTy.getQualifiers(); + if (!QualsOnT.empty()) { + QualifierCollector QC(QualsOnT); + MergeTy = QC.apply(*this, MergeTy); + } + } + return MergeTy; +} + +namespace { + +class MakeAutoPointer final + : public TypeVisitor> { +private: + using RetTy = std::pair; + using BaseClass = TypeVisitor; + + ASTContext &Ctx; + const BoundsSafetyPointerAttributes AbiFAttr; + std::optional AutoPtrAttr; + bool IsInsideValueTerminated = false; + + QualType copyQuals(QualType OldTy, QualType NewTy) const { + Qualifiers Qs = OldTy.getLocalQualifiers(); + if (const auto *AT = OldTy->getAs()) { + Qualifiers ModifiedTyQs = AT->getModifiedType().getQualifiers(); + Qualifiers::removeCommonQualifiers(Qs, ModifiedTyQs); + } + QualifierCollector QC(Qs); + return QC.apply(Ctx, NewTy); + } + +public: + explicit MakeAutoPointer( + ASTContext &Ctx, BoundsSafetyPointerAttributes AbiFAttr, + std::optional AutoPtrAttr) + : Ctx(Ctx), AbiFAttr(AbiFAttr), AutoPtrAttr(AutoPtrAttr) {} + + QualType Visit(QualType T) { + // We don't apply an implicit -fbounds-safety attribute for va_list, + // leaving it unsafe. + if (T->isAnyVaListType(Ctx)) + return T; + + QualType NewTy; + bool AddPtrAutoAttr; + std::tie(NewTy, AddPtrAutoAttr) = BaseClass::Visit(T.getTypePtr()); + + if (NewTy.isNull()) + return QualType(); + + NewTy = copyQuals(T, NewTy); + + if (AddPtrAutoAttr) + NewTy = Ctx.getAttributedType(attr::PtrAutoAttr, NewTy, NewTy); + + return NewTy; + } + + RetTy VisitType(const Type *T) { + QualType NewTy(T, 0); + return {NewTy, false}; + } + + RetTy VisitParenType(const ParenType *T) { + QualType InnerTy = Visit(T->getInnerType()); + QualType NewTy = Ctx.getParenType(InnerTy); + return {NewTy, false}; + } + + RetTy VisitAtomicType(const AtomicType *T) { + QualType ValueTy = Visit(T->getValueType()); + QualType NewTy = Ctx.getAtomicType(ValueTy); + return {NewTy, false}; + } + + RetTy VisitAttributedType(const AttributedType *T) { + auto SavedAutoPtrAttr = AutoPtrAttr; + QualType ModifiedTy = Visit(T->getModifiedType()); + AutoPtrAttr = SavedAutoPtrAttr; + QualType EquivalentTy = Visit(T->getEquivalentType()); + QualType NewTy = + Ctx.getAttributedType(T->getAttrKind(), ModifiedTy, EquivalentTy); + return {NewTy, false}; + } + + RetTy VisitTypedefType(const TypedefType *T) { + // If the underlying type after auto-bounding doesn't change, we retain the + // typedef type. However, if auto-bounding adds an attribute to the + // underlying type, we drop the typedef type, since otherwise we would have + // multiple inconsistent definitions of the typedef. + QualType UnderlyingTy = T->desugar(); + QualType NewUnderlyingTy = Visit(UnderlyingTy); + if (NewUnderlyingTy == UnderlyingTy) { + QualType NewTy = Ctx.getTypedefType(T->getDecl(), UnderlyingTy); + return {NewTy, false}; + } + return {NewUnderlyingTy, false}; + } + + RetTy VisitMacroQualifiedType(const MacroQualifiedType *T) { + QualType UnderlyingTy = T->desugar(); + QualType NewUnderlyingTy = Visit(UnderlyingTy); + QualType NewTy = + Ctx.getMacroQualifiedType(NewUnderlyingTy, T->getMacroIdentifier()); + return {NewTy, false}; + } + + RetTy VisitElaboratedType(const ElaboratedType *T) { + QualType NamedTy = T->getNamedType(); + QualType NewNamedTy = Visit(NamedTy); + if (NewNamedTy == NamedTy) { + return {QualType(T, 0), false}; + } + return {NewNamedTy, false}; + } + + RetTy VisitDecayedType(const DecayedType *T) { + // Replace the decayed type by an equivalent pointer type. + // TODO(pstefanski): We could retain the decayed type after some + // modifications to ASTContext. + auto SavedAutoPtrAttr = AutoPtrAttr; + AutoPtrAttr = std::nullopt; + QualType PointeeTy = Visit(T->getPointeeType()); + const auto *PtrTy = + cast(Ctx.getPointerType(PointeeTy).getTypePtr()); + AutoPtrAttr = SavedAutoPtrAttr; + return VisitPointerType(PtrTy); + } + + RetTy VisitConstantArrayType(const ConstantArrayType *T) { + AutoPtrAttr = std::nullopt; + QualType EltTy = Visit(T->getElementType()); + QualType NewTy = Ctx.getConstantArrayType( + EltTy, T->getSize(), T->getSizeExpr(), T->getSizeModifier(), + T->getIndexTypeCVRQualifiers()); + return {NewTy, false}; + } + + RetTy VisitDependentSizedArrayType(const DependentSizedArrayType *T) { + AutoPtrAttr = std::nullopt; + QualType EltTy = Visit(T->getElementType()); + QualType NewTy = Ctx.getDependentSizedArrayType( + EltTy, T->getSizeExpr(), T->getSizeModifier(), + T->getIndexTypeCVRQualifiers()); + return {NewTy, false}; + } + + RetTy VisitIncompleteArrayType(const IncompleteArrayType *T) { + AutoPtrAttr = std::nullopt; + QualType EltTy = Visit(T->getElementType()); + QualType NewTy = Ctx.getIncompleteArrayType(EltTy, T->getSizeModifier(), + T->getIndexTypeCVRQualifiers()); + return {NewTy, false}; + } + + RetTy VisitVariableArrayType(const VariableArrayType *T) { + AutoPtrAttr = std::nullopt; + QualType EltTy = Visit(T->getElementType()); + QualType NewTy = Ctx.getVariableArrayType( + EltTy, T->getSizeExpr(), T->getSizeModifier(), + T->getIndexTypeCVRQualifiers()); + return {NewTy, false}; + } + + RetTy VisitPointerType(const PointerType *T) { + // Should we add PtrAutoAttr to this pointer upon returning? + bool AddPtrAutoAttr = false; + + auto PtrAttr = AutoPtrAttr; + + QualType PointeeTy; + { + AutoPtrAttr = std::nullopt; + llvm::SaveAndRestore IsInsideValueTerminatedSAR( + IsInsideValueTerminated, false); + PointeeTy = Visit(T->getPointeeType()); + } + + BoundsSafetyPointerAttributes Attr = T->getPointerAttributes(); + if (Attr.isUnspecified()) { + AddPtrAutoAttr = true; + // The following rules are to make function pointers and non-ABI visible + // pointers to have appropriate safe pointers (i.e., __single and + // __bidi_indexable, respectively). However, we shouldn't apply this rule + // when the default ABI-visible attribute is unsafe, as that can + // automatically create both unsafe and safe pointers in the same + // function, which are not compatible, leading to undesirable diagnostic + // errors on the pointer casts. Therefore, when the default ABI-visible + // pointer attribute is unsafe, we apply the same attribute to non-ABI + // visible pointers. + // + // Function pointers, which don't have a size, are unconditionally + // __single. + if (PointeeTy->isFunctionType() && !AbiFAttr.isUnsafeOrUnspecified()) + Attr.setSingle(); + else if (PtrAttr.has_value() && + (IsInsideValueTerminated || !AbiFAttr.isUnsafeOrUnspecified())) { + // Auto-bounding the outer-most pointer types that are not ABI-visible. + // FIXME: Should be set to AutoBound when we unlock the tree transform + // for escaping locals to implicitly transform them to __single. + // rdar://69452444 + Attr = *PtrAttr; + } else { + // ABI-visible pointers get the default ABI-visible BoundsSafety + // attributes. This is unsafe indexable in system headers and single + // otherwise. + Attr.copyBoundsAttr(AbiFAttr); + } + } + + QualType NewTy = Ctx.getPointerType(PointeeTy, Attr); + + bool IsPointeeChar = PointeeTy.getTypePtr() == Ctx.CharTy.getTypePtr(); + bool IsPointeeWChar = false; + if (const auto *TT = PointeeTy->getAs()) + IsPointeeWChar = TT->getDecl()->getName() == "wchar_t"; + if (AddPtrAutoAttr && Attr.isSingle() && PointeeTy.isConstQualified() && + (IsPointeeChar || IsPointeeWChar) && !IsInsideValueTerminated) { + unsigned IntSize = Ctx.getTargetInfo().getIntWidth(); + auto *ZeroLit = IntegerLiteral::Create(Ctx, llvm::APInt(IntSize, 0), + Ctx.IntTy, SourceLocation()); + auto *TermExpr = ImplicitCastExpr::Create( + Ctx, PointeeTy, CK_IntegralCast, ZeroLit, + /*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride()); + NewTy = Ctx.getValueTerminatedType(NewTy, TermExpr); + NewTy = + Ctx.getAttributedType(attr::PtrAutoNullTerminatedAttr, NewTy, NewTy); + } + + return {NewTy, AddPtrAutoAttr}; + } + + RetTy VisitBoundsAttributedType(const BoundsAttributedType *T) { + AutoPtrAttr = std::nullopt; + QualType DesugaredTy = T->desugar(); + QualType PtrTy = Visit(DesugaredTy); + if (PtrTy == DesugaredTy) + return {QualType(T, 0), false}; + QualType NewTy; + if (const auto *DCPT = dyn_cast(T)) { + NewTy = Ctx.getCountAttributedType( + PtrTy, DCPT->getCountExpr(), DCPT->isCountInBytes(), DCPT->isOrNull(), + DCPT->getCoupledDecls()); + } else if (const auto *DRPT = dyn_cast(T)) { + NewTy = Ctx.getDynamicRangePointerType( + PtrTy, DRPT->getStartPointer(), DRPT->getEndPointer(), + DRPT->getStartPtrDecls(), DRPT->getEndPtrDecls()); + } else { + llvm_unreachable("Unknown BoundsAttributedType"); + } + return {NewTy, false}; + } + + RetTy VisitValueTerminatedType(const ValueTerminatedType *T) { + // Make value-terminated pointers __single by default. + AutoPtrAttr = BoundsSafetyPointerAttributes::single(); + llvm::SaveAndRestore IsInsideValueTerminatedSAR( + IsInsideValueTerminated, true); + QualType DesugaredTy = T->desugar(); + // There shouldn't be nested value-terminated types. + assert(!DesugaredTy->getAs()); + QualType NewDesugaredTy = Visit(DesugaredTy); + assert(!NewDesugaredTy->getAs()); + QualType NewTy = + Ctx.getValueTerminatedType(NewDesugaredTy, T->getTerminatorExpr()); + return {NewTy, false}; + } + + RetTy VisitFunctionProtoType(const FunctionProtoType *T) { + AutoPtrAttr = std::nullopt; + QualType ReturnTy = Visit(T->getReturnType()); + SmallVector ParamTypes; + ParamTypes.reserve(T->getNumParams()); + for (QualType ParamTy : T->getParamTypes()) + ParamTypes.push_back(Visit(ParamTy)); + QualType NewTy = + Ctx.getFunctionType(ReturnTy, ParamTypes, T->getExtProtoInfo()); + return {NewTy, false}; + } + + RetTy VisitFunctionNoProtoType(const FunctionNoProtoType *T) { + AutoPtrAttr = std::nullopt; + QualType ReturnTy = Visit(T->getReturnType()); + QualType NewTy = Ctx.getFunctionNoProtoType(ReturnTy, T->getExtInfo()); + return {NewTy, false}; + } +}; + +} // namespace + +QualType ASTContext::getBoundsSafetyAutoPointerType( + QualType T, BoundsSafetyPointerAttributes AbiFAttr, bool ShouldAutoBound) { + // XXX: We don't apply an implicit -fbounds-safety attribute for va_list, which + // leaves va_list unsafe + if (T->isAnyVaListType(*this)) + return T; + + std::optional AutoPtrAttr; + if (ShouldAutoBound) + AutoPtrAttr = BoundsSafetyPointerAttributes::bidiIndexable(); + MakeAutoPointer MAP(*this, AbiFAttr, AutoPtrAttr); + return MAP.Visit(T); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + QualType ASTContext::removePtrSizeAddrSpace(QualType T) const { if (const PointerType *Ptr = T->getAs()) { QualType Pointee = Ptr->getPointeeType(); if (isPtrSizeAddressSpace(Pointee.getAddressSpace())) { - return getPointerType(removeAddrSpaceQualType(Pointee)); + return getPointerType(removeAddrSpaceQualType(Pointee), + Ptr->getPointerAttributes()); } } return T; @@ -3820,11 +4422,12 @@ QualType ASTContext::getComplexType(QualType T) const { /// getPointerType - Return the uniqued reference to the type for a pointer to /// the specified type. -QualType ASTContext::getPointerType(QualType T) const { +QualType ASTContext::getPointerType(QualType T, + BoundsSafetyPointerAttributes A) const { // Unique pointers, to guarantee there is only one pointer of a particular // structure. llvm::FoldingSetNodeID ID; - PointerType::Profile(ID, T); + PointerType::Profile(ID, T, A); void *InsertPos = nullptr; if (PointerType *PT = PointerTypes.FindNodeOrInsertPos(ID, InsertPos)) @@ -3834,13 +4437,13 @@ QualType ASTContext::getPointerType(QualType T) const { // so fill in the canonical type field. QualType Canonical; if (!T.isCanonical()) { - Canonical = getPointerType(getCanonicalType(T)); + Canonical = getPointerType(getCanonicalType(T), A); // Get the new insert position for the node we care about. PointerType *NewIP = PointerTypes.FindNodeOrInsertPos(ID, InsertPos); assert(!NewIP && "Shouldn't be in the map!"); (void)NewIP; } - auto *New = new (*this, alignof(PointerType)) PointerType(T, Canonical); + auto *New = new (*this, alignof(PointerType)) PointerType(T, Canonical, A); Types.push_back(New); PointerTypes.InsertNode(New, InsertPos); return QualType(New, 0); @@ -6073,10 +6676,6 @@ ASTContext::applyObjCProtocolQualifiers(QualType type, bool allowOnPointerType) const { hasError = false; - if (const auto *objT = dyn_cast(type.getTypePtr())) { - return getObjCTypeParamType(objT->getDecl(), protocols); - } - // Apply protocol qualifiers to ObjCObjectPointerType. if (allowOnPointerType) { if (const auto *objPtr = @@ -6173,11 +6772,13 @@ void ASTContext::adjustObjCTypeParamBoundType(const ObjCTypeParamDecl *Orig, ObjCTypeParamDecl *New) const { New->setTypeSourceInfo(getTrivialTypeSourceInfo(Orig->getUnderlyingType())); // Update TypeForDecl after updating TypeSourceInfo. - auto NewTypeParamTy = cast(New->getTypeForDecl()); - SmallVector protocols; - protocols.append(NewTypeParamTy->qual_begin(), NewTypeParamTy->qual_end()); - QualType UpdatedTy = getObjCTypeParamType(New, protocols); - New->setTypeForDecl(UpdatedTy.getTypePtr()); + if (const Type *TypeForDecl = New->getTypeForDecl()) { + auto NewTypeParamTy = cast(TypeForDecl); + SmallVector protocols; + protocols.append(NewTypeParamTy->qual_begin(), NewTypeParamTy->qual_end()); + QualType UpdatedTy = getObjCTypeParamType(New, protocols); + New->setTypeForDecl(UpdatedTy.getTypePtr()); + } } /// ObjCObjectAdoptsQTypeProtocols - Checks that protocols in IC's @@ -6937,6 +7538,53 @@ bool ASTContext::hasCvrSimilarType(QualType T1, QualType T2) { } } +bool ASTContext::hasSameBoundsSafetyPointerLayout(QualType T1, QualType T2) { + return hasCompatibleBoundsSafetyPointerLayout(T1, T2, /*ExactCheck*/ true); +} + +bool ASTContext::hasCompatibleBoundsSafetyPointerLayout(QualType T1, QualType T2, + bool ExactCheck) { + while (true) { + if (ExactCheck && (T1->getAs() + != T2->getAs())) + return false; + Qualifiers Quals; + T1 = getUnqualifiedArrayType(T1, Quals); + T2 = getUnqualifiedArrayType(T2, Quals); + + if (hasSameType(T1, T2)) + return true; + + const auto *PT1 = T1->getBaseElementTypeUnsafe()->getAs(); + const auto *PT2 = T2->getBaseElementTypeUnsafe()->getAs(); + + if (!PT1 && !PT2) + return true; + + if (PT1 && PT2) { + if (!ExactCheck) { + if (!BoundsSafetyPointerAttributes::areCompatible( + PT1->getPointerAttributes(), PT2->getPointerAttributes())) + return false; + } else if (PT1->getPointerAttributes() != PT2->getPointerAttributes()) + return false; + } + + if (PT1) { + if (!PT2 && PT1->isSafePointer()) + return false; + + T1 = PT1->getPointeeType(); + } + + if (PT2) { + if (!PT1 && PT2->isSafePointer()) + return false; + T2 = PT2->getPointeeType(); + } + } +} + DeclarationNameInfo ASTContext::getNameForTemplate(TemplateName Name, SourceLocation NameLoc) const { @@ -9625,6 +10273,136 @@ static TypedefDecl *CreateVoidPtrBuiltinVaListDecl(const ASTContext *Context) { return Context->buildImplicitTypedef(T, "__builtin_va_list"); } +bool ASTContext::isObjCMsgSendUsageFileSpecified() const { + if (!ObjCMsgSendUsageFile) { + if (const char *Filename = + std::getenv("CLANG_COMPILER_OBJC_MESSAGE_TRACE_PATH")) + ObjCMsgSendUsageFile = Filename; + else + return false; + } + + return true; +} + +std::string ASTContext::getObjCMsgSendUsageFilename() const { + if (isObjCMsgSendUsageFileSpecified()) + return *ObjCMsgSendUsageFile; + return ""; +} + +void ASTContext::recordObjCMsgSendUsage(const ObjCMethodDecl *Method) { + ObjCMsgSendUsage.push_back(Method); +} + +static StringRef selectMethodKey(const clang::ObjCMethodDecl *clangD) { + assert(clangD); + if (clangD->isInstanceMethod()) + return "instance_method"; + assert(clangD->isClassMethod() && "must be a class method"); + return "class_method"; +} + +static std::array, 2> +selectMethodOwnerKey(const clang::NamedDecl *clangD) { + assert(clangD); + if (isa(clangD)) + return {{{"interface_type", clangD->getName()}, {}}}; + if (isa(clangD)) + return {{{"implementation_type", clangD->getName()}, {}}}; + if (auto *Cat = dyn_cast(clangD)) + return {{{"interface_type", Cat->getClassInterface()->getName()}, + {"category_type", Cat->getName()}}}; + if (auto *Cat = dyn_cast(clangD)) + return {{{"interface_type", + Cat->getCategoryDecl()->getClassInterface()->getName()}, + {"category_implementation_type", Cat->getName()}}}; + if (isa(clangD)) + return {{{"protocol_type", clangD->getName()}, {}}}; + llvm_unreachable("unknown method owner"); +} + +void clang::serializeObjCMethodReferencesAsJson( + const clang::ObjCMethodReferenceInfo &Info, llvm::raw_ostream &OS) { + llvm::json::OStream Out(OS, /*IndentSize=*/4); + Out.object([&] { + Out.attribute(Info.ToolName, Info.ToolVersion); + Out.attribute("format-version", Info.FormatVersion); + Out.attribute("target", Info.Target); + if (!Info.TargetVariant.empty()) + Out.attribute("target-variant", Info.TargetVariant); + Out.attributeArray("references", [&] { + for (auto &Ref : Info.References) { + unsigned FileID = Ref.first; + for (const clang::ObjCMethodDecl *clangD : Ref.second) { + auto &SM = clangD->getASTContext().getSourceManager(); + clang::SourceLocation Loc = clangD->getLocation(); + if (!Loc.isValid()) + continue; + Out.object([&] { + if (auto *parent = + dyn_cast_or_null(clangD->getParent())) { + std::array, 2> OwnerInfo = + selectMethodOwnerKey(parent); + for (auto I : OwnerInfo) + if (I.first.data()) + Out.attribute(I.first, I.second); + } + + std::string MangledMethodName; + llvm::raw_string_ostream Out2(MangledMethodName); + std::unique_ptr Mangler( + clangD->getASTContext().createMangleContext()); + Mangler->mangleObjCMethodName(clangD, Out2, + /*includePrefixByte=*/false, true); + Out.attribute(selectMethodKey(clangD), MangledMethodName); + Out.attribute("declared_at", Loc.printToString(SM)); + Out.attribute("referenced_at_file_id", FileID); + }); + } + } + }); + + Out.attributeArray("fileMap", [&] { + for (unsigned I = 0, N = Info.FilePaths.size(); I != N; I++) { + Out.object([&] { + Out.attribute("file_id", I + 1); + Out.attribute("file_path", Info.FilePaths[I]); + }); + } + }); + }); +} + +void ASTContext::writeObjCMsgSendUsages(const std::string &Filename) { + std::error_code EC; + auto FDS = std::make_unique( + Filename, EC, llvm::sys::fs::OF_Text | llvm::sys::fs::OF_Append); + if (EC) { + unsigned ID = getDiagnostics().getCustomDiagID( + DiagnosticsEngine::Error, + "couldn't open objc message send tracing file %0"); + getDiagnostics().Report(ID) << Filename; + return; + } + + if (auto L = FDS->lock()) { + clang::ObjCMethodReferenceInfo Info; + OptionalFileEntryRef FE = + SourceMgr.getFileEntryRefForID(SourceMgr.getMainFileID()); + SmallString<256> MainFile(FE->getName()); + SourceMgr.getFileManager().makeAbsolutePath(MainFile); + Info.ToolName = "clang-compiler-version"; + Info.ToolVersion = getClangFullVersion(); + Info.Target = getTargetInfo().getTriple().str(); + if (auto *VariantTriple = getTargetInfo().getDarwinTargetVariantTriple()) + Info.TargetVariant = VariantTriple->str(); + Info.FilePaths.push_back(MainFile.c_str()); + Info.References[1] = ObjCMsgSendUsage; + serializeObjCMethodReferencesAsJson(Info, *FDS); + } +} + static TypedefDecl * CreateAArch64ABIBuiltinVaListDecl(const ASTContext *Context) { // struct __va_list @@ -11463,6 +12241,15 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, if (LHSRefTy || RHSRefTy) return {}; + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (const auto *RProto = RHS->getAs()) { + // Do not merge bounds attributed return types since they are referring to + // different decls. + if (RProto->getReturnType()->isBoundsAttributedType()) + return {}; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (Unqualified) { LHS = LHS.getUnqualifiedType(); RHS = RHS.getUnqualifiedType(); @@ -11590,8 +12377,18 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, case Type::Pointer: { // Merge two pointer types, while trying to preserve typedef info - QualType LHSPointee = LHS->castAs()->getPointeeType(); - QualType RHSPointee = RHS->castAs()->getPointeeType(); + const PointerType *LHSPointer = LHS->castAs(); + const PointerType *RHSPointer = RHS->castAs(); + BoundsSafetyPointerAttributes LHSFA = LHSPointer->getPointerAttributes(); + BoundsSafetyPointerAttributes RHSFA = RHSPointer->getPointerAttributes(); + if (!BoundsSafetyPointerAttributes::areCompatible(LHSFA, RHSFA)) + return {}; + BoundsSafetyPointerAttributes BestFA = LHSFA; + if (BestFA.isUnspecified()) + BestFA = RHSFA; + + QualType LHSPointee = LHSPointer->getPointeeType(); + QualType RHSPointee = RHSPointer->getPointeeType(); if (Unqualified) { LHSPointee = LHSPointee.getUnqualifiedType(); RHSPointee = RHSPointee.getUnqualifiedType(); @@ -11600,11 +12397,16 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer, Unqualified); if (ResultType.isNull()) return {}; + QualType BestType; if (getCanonicalType(LHSPointee) == getCanonicalType(ResultType)) - return LHS; - if (getCanonicalType(RHSPointee) == getCanonicalType(ResultType)) - return RHS; - return getPointerType(ResultType); + BestType = LHS; + else if (getCanonicalType(RHSPointee) == getCanonicalType(ResultType)) + BestType = RHS; + else + BestType = getPointerType(ResultType, BestFA); + if (BestType->getAs()->getPointerAttributes() != BestFA) + BestType = getBoundsSafetyPointerType(BestType, BestFA); + return BestType; } case Type::BlockPointer: { @@ -13909,7 +14711,15 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X, } case Type::Pointer: { const auto *PX = cast(X), *PY = cast(Y); - return Ctx.getPointerType(getCommonPointeeType(Ctx, PX, PY)); + // Bounds safety: pointer attributes are not sugar but they are part of + // canonical types. Hence, the attributes must be same and this function + // must preserve them. + auto PXFAttr = PX->getPointerAttributes(); + auto PYFAttr = PY->getPointerAttributes(); + (void)PYFAttr; + assert(PXFAttr == PYFAttr || + PXFAttr.isUnsafeOrUnspecified() == PYFAttr.isUnsafeOrUnspecified()); + return Ctx.getPointerType(getCommonPointeeType(Ctx, PX, PY), PXFAttr); } case Type::BlockPointer: { const auto *PX = cast(X), *PY = cast(Y); @@ -14377,18 +15187,47 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X, return Ctx.getCountAttributedType(Ctx.getQualifiedType(Underlying), CEX, DX->isCountInBytes(), DX->isOrNull(), CDX); + /* TO_UPSTREAM(BoundsSafety) ON */ if (!CEX->isIntegerConstantExpr(Ctx) || !CEY->isIntegerConstantExpr(Ctx)) - return QualType(); + llvm_unreachable("this should have been caught by canMergeTypeBounds"); + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Two declarations with the same integer constant may still differ in their // expression pointers, so we need to evaluate them. llvm::APSInt VX = *CEX->getIntegerConstantExpr(Ctx); llvm::APSInt VY = *CEY->getIntegerConstantExpr(Ctx); + /* TO_UPSTREAM(BoundsSafety) ON */ if (VX != VY) - return QualType(); + llvm_unreachable("this should have been caught by canMergeTypeBounds"); + /* TO_UPSTREAM(BoundsSafety) OFF */ + return Ctx.getCountAttributedType(Ctx.getQualifiedType(Underlying), CEX, DX->isCountInBytes(), DX->isOrNull(), CDX); } + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: { + const auto *DX = cast(X), + *DY = cast(Y); + if (!Ctx.hasSameExpr(DX->getStartPointer(), DY->getStartPointer()) || + !Ctx.hasSameExpr(DX->getEndPointer(), DY->getEndPointer())) + llvm_unreachable("this should have been caught by canMergeTypeBounds"); + assert(DX->getNumStartPtrDecls() == DY->getNumStartPtrDecls()); + assert(DX->getNumEndPtrDecls() == DY->getNumEndPtrDecls()); + return Ctx.getDynamicRangePointerType( + Ctx.getQualifiedType(Underlying), DX->getStartPointer(), + DX->getEndPointer(), DX->getStartPtrDecls(), DX->getEndPtrDecls()); + } + case Type::ValueTerminated: { + const auto *VX = cast(X), + *VY = cast(Y); + if (!Ctx.hasSameExpr(VX->getTerminatorExpr(), VY->getTerminatorExpr()) && + VX->getTerminatorValue(Ctx) != VY->getTerminatorValue(Ctx)) + llvm_unreachable("this should have been caught by canMergeTypeBounds"); + return Ctx.getValueTerminatedType(Ctx.getQualifiedType(Underlying), + VX->getTerminatorExpr()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ } llvm_unreachable("Unhandled Type Class"); } @@ -14406,9 +15245,169 @@ static auto unwrapSugar(SplitQualType &T, Qualifiers &QTotal) { return R; } +/* TO_UPSTREAM(BoundsSafety) ON */ +ASTContext::BoundsSafePointerTypeMergeKind +ASTContext::canMergeFunctionTypeBounds(const FunctionProtoType *LHSTy, + const FunctionProtoType *RHSTy) const{ + if (LHSTy->getNumParams() != RHSTy->getNumParams()) + return ASTContext::BSPTMK_FunctionTypeMismatch; + for (unsigned i = 0; i < LHSTy->getNumParams(); i++) { + if (!canMergeInnerTypeBounds(LHSTy->getParamTypes()[i], + RHSTy->getParamTypes()[i])) + return ASTContext::BSPTMK_FunctionTypeMismatch; + } + if (canMergeInnerTypeBounds(LHSTy->getReturnType(), RHSTy->getReturnType()) != + ASTContext::BSPTMK_CanMerge) + return ASTContext::BSPTMK_FunctionTypeMismatch; + return ASTContext::BSPTMK_CanMerge; +} + +ASTContext::BoundsSafePointerTypeMergeKind +ASTContext::canMergeInnerTypeBounds(QualType LHSTy, QualType RHSTy) const { + assert(!!LHSTy->getAs() == !!RHSTy->getAs()); + + if (LHSTy->isFunctionProtoType()) { + return canMergeFunctionTypeBounds(LHSTy->getAs(), + RHSTy->getAs()); + } + + if (!LHSTy->getAs()) + return ASTContext::BSPTMK_CanMerge; + + while (LHSTy->getTypeClass() != Type::Pointer) { + switch (LHSTy->getTypeClass()) { + case Type::CountAttributed: { + auto LTy = cast(LHSTy); + auto RTy = RHSTy->getAs(); + if (!RTy) + return ASTContext::BSPTMK_NestedBoundsMismatch; + if (LTy->isCountInBytes() != RTy->isCountInBytes()) + return ASTContext::BSPTMK_NestedBoundsMismatch; + if (LTy->isOrNull() != RTy->isOrNull()) + return ASTContext::BSPTMK_NestedBoundsMismatch; + const auto CEL = LTy->getCountExpr(); + const auto CER = RTy->getCountExpr(); + if (!hasSameExpr(CEL, CER) && (!CEL->isIntegerConstantExpr(*this) || + !CER->isIntegerConstantExpr(*this) || + CEL->getIntegerConstantExpr(*this) != + CER->getIntegerConstantExpr(*this))) + return ASTContext::BSPTMK_NestedBoundsMismatch; + break; + } + case Type::DynamicRangePointer: { + auto LTy = cast(LHSTy); + auto RTy = RHSTy->getAs(); + if (!RTy) + return ASTContext::BSPTMK_NestedBoundsMismatch; + if (!hasSameExpr(LTy->getStartPointer(), RTy->getStartPointer()) || + !hasSameExpr(LTy->getEndPointer(), RTy->getEndPointer())) + return ASTContext::BSPTMK_NestedBoundsMismatch; + break; + } + case Type::ValueTerminated: { + if (checkTerminatedByMismatch(LHSTy, RHSTy) != BSPTMK_CanMerge) + return ASTContext::BSPTMK_NestedBoundsMismatch; + break; + } + default: + // Non-bounds sugar: don't iterate RHS + auto LHSNext = LHSTy->getLocallyUnqualifiedSingleStepDesugaredType(); + assert(LHSNext != LHSTy); + LHSTy = LHSNext; + continue; + } + auto LHSNext = LHSTy->getLocallyUnqualifiedSingleStepDesugaredType(); + assert(LHSNext != LHSTy); + auto CurrentTypeClass = LHSTy->getTypeClass(); + LHSTy = LHSNext; + + auto RHSNext = RHSTy->getLocallyUnqualifiedSingleStepDesugaredType(); + // Iterate until the RHSTy is at the same bounds sugar type as the + // LHS was, and then once more to match in the next iteration + while (RHSTy->getTypeClass() != CurrentTypeClass) { + assert(RHSNext != RHSTy); + auto RHSTypeClass = RHSTy->getTypeClass(); + if (RHSTypeClass == Type::CountAttributed || + RHSTypeClass == Type::DynamicRangePointer || + RHSTypeClass == Type::ValueTerminated) + return ASTContext::BSPTMK_NestedBoundsMismatch; // We found bounds + // sugar on the RHS that + // we haven't seen on + // the LHS + RHSTy = RHSNext; + RHSNext = RHSTy->getLocallyUnqualifiedSingleStepDesugaredType(); + } + RHSTy = RHSNext; + } + + // We reached the LHS raw pointer type without reaching any mismatching + // bounds sugar nodes. Make sure the RHS doesn't have any bounds sugar + // left. + while (RHSTy->getTypeClass() != Type::Pointer) { + switch (RHSTy->getTypeClass()) { + case Type::CountAttributed: + case Type::DynamicRangePointer: + case Type::ValueTerminated: + return ASTContext::BSPTMK_NestedBoundsMismatch; + default: + auto Next = RHSTy->getLocallyUnqualifiedSingleStepDesugaredType(); + assert(Next != RHSTy); + RHSTy = Next; + } + } + + // Check next layer in case of deeply nested pointer types + return canMergeInnerTypeBounds(cast(LHSTy)->getPointeeType(), + cast(RHSTy)->getPointeeType()); +} + +ASTContext::BoundsSafePointerTypeMergeKind +ASTContext::checkTerminatedByMismatch(QualType LHSTy, QualType RHSTy) const { + auto LVTTy = LHSTy->getAs(); + auto RVTTy = RHSTy->getAs(); + if (!!LVTTy != !!RVTTy) + return ASTContext::BSPTMK_TerminatedByMismatch; + + if (LVTTy && RVTTy) { + if (LVTTy->getTerminatorValue(*this) != RVTTy->getTerminatorValue(*this)) + return ASTContext::BSPTMK_TerminatedByMismatch; + } + + return BSPTMK_CanMerge; +} + +ASTContext::BoundsSafePointerTypeMergeKind +ASTContext::canMergeTypeBounds(QualType LHSTy, QualType RHSTy) const { + assert(getCanonicalType(LHSTy).getUnqualifiedType() == + getCanonicalType(RHSTy).getUnqualifiedType()); + assert(getLangOpts().BoundsSafety); + + auto TermCheck = checkTerminatedByMismatch(LHSTy, RHSTy); + if (TermCheck != BSPTMK_CanMerge) + return TermCheck; + + auto LHSPointerTy = LHSTy->getAs(); + auto RHSPointerTy = RHSTy->getAs(); + assert(!!LHSPointerTy == !!RHSPointerTy); + if (LHSPointerTy) { + // We don't need to handle outermost layer of pointer type annotations; + // they may be bounds checked dynamically while this is only for static type + // merging. + LHSTy = LHSPointerTy->getPointeeType(); + RHSTy = RHSPointerTy->getPointeeType(); + } + + return canMergeInnerTypeBounds(LHSTy, RHSTy); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + QualType ASTContext::getCommonSugaredType(QualType X, QualType Y, bool Unqualified) { assert(Unqualified ? hasSameUnqualifiedType(X, Y) : hasSameType(X, Y)); + /* TO_UPSTREAM(BoundsSafety) ON */ + assert(Unqualified || !getLangOpts().BoundsSafety || + canMergeTypeBounds(X, Y) == ASTContext::BSPTMK_CanMerge); + /* TO_UPSTREAM(BoundsSafety) OFF */ if (X == Y) return X; if (!Unqualified) { @@ -14482,6 +15481,14 @@ QualType ASTContext::getCommonSugaredType(QualType X, QualType Y, // with the sugar nodes we could not unify. QualType R = getQualifiedType(SX.Ty, QX); assert(Unqualified ? hasSameUnqualifiedType(R, X) : hasSameType(R, X)); + + // Other sugar types failing to unify can prevent bounds safety sugar from + // unifying also. Losing this sugar affects semantics, so force it back on. + R = assureMandatorySugarTypesRemain(*this, R, X); + assert(R->isValueTerminatedType() == Y->isValueTerminatedType()); + assert(R->isCountAttributedType() == Y->isCountAttributedType()); + assert(R->isDynamicRangePointerType() == Y->isDynamicRangePointerType()); + return R; } diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp index 6cb09b0492ac9..74dd72549e12a 100644 --- a/clang/lib/AST/ASTDiagnostic.cpp +++ b/clang/lib/AST/ASTDiagnostic.cpp @@ -210,7 +210,8 @@ break; \ // FIXME: Handle other pointer-like types. if (const PointerType *Ty = QT->getAs()) { QT = Context.getPointerType( - desugarForDiagnostic(Context, Ty->getPointeeType(), ShouldAKA)); + desugarForDiagnostic(Context, Ty->getPointeeType(), ShouldAKA), + Ty->getPointerAttributes()); } else if (const auto *Ty = QT->getAs()) { QT = Context.getObjCObjectPointerType( desugarForDiagnostic(Context, Ty->getPointeeType(), ShouldAKA)); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 00628602e61fa..da067483d1c1c 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1206,7 +1206,8 @@ ExpectedType ASTNodeImporter::VisitPointerType(const PointerType *T) { if (!ToPointeeTypeOrErr) return ToPointeeTypeOrErr.takeError(); - return Importer.getToContext().getPointerType(*ToPointeeTypeOrErr); + return Importer.getToContext().getPointerType(*ToPointeeTypeOrErr, + T->getPointerAttributes()); } ExpectedType ASTNodeImporter::VisitBlockPointerType(const BlockPointerType *T) { @@ -1559,7 +1560,9 @@ ExpectedType ASTNodeImporter::VisitInjectedClassNameType( } ExpectedType ASTNodeImporter::VisitRecordType(const RecordType *T) { + // getCanonicalDecl in order to not trigger redeclaration completion Expected ToDeclOrErr = import(T->getDecl()); + if (!ToDeclOrErr) return ToDeclOrErr.takeError(); @@ -1609,6 +1612,20 @@ ASTNodeImporter::VisitCountAttributedType(const CountAttributedType *T) { ArrayRef(CoupledDecls.data(), CoupledDecls.size())); } +/* TO_UPSTREAM(BoundsSafety) ON */ +ExpectedType clang::ASTNodeImporter::VisitDynamicRangePointerType( + const clang::DynamicRangePointerType *T) { + // FIXME: Unsupported AST node. + return VisitType(T); +} + +ExpectedType clang::ASTNodeImporter::VisitValueTerminatedType( + const clang::ValueTerminatedType *T) { + // FIXME: Unsupported AST node. + return VisitType(T); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + ExpectedType ASTNodeImporter::VisitTemplateTypeParmType( const TemplateTypeParmType *T) { Expected ToDeclOrErr = import(T->getDecl()); @@ -1997,8 +2014,8 @@ Error ASTNodeImporter::ImportDefinitionIfNeeded(Decl *FromD, Decl *ToD) { if (RecordDecl *FromRecord = dyn_cast(FromD)) { if (RecordDecl *ToRecord = cast(ToD)) { - if (FromRecord->getDefinition() && FromRecord->isCompleteDefinition() && - !ToRecord->getDefinition()) { + if (FromRecord->getDefinition() && !ToRecord->getDefinition() && + FromRecord->isCompleteDefinition()) { if (Error Err = ImportDefinition(FromRecord, ToRecord)) return Err; } @@ -2099,6 +2116,7 @@ ASTNodeImporter::ImportDeclContext(DeclContext *FromDC, bool ForceImport) { ImportedOrErr.takeError()); continue; } + FieldDecl *FieldFrom = dyn_cast_or_null(From); Decl *ImportedDecl = *ImportedOrErr; FieldDecl *FieldTo = dyn_cast_or_null(ImportedDecl); @@ -2786,6 +2804,9 @@ ASTNodeImporter::VisitTypedefNameDecl(TypedefNameDecl *D, bool IsAlias) { !hasSameVisibilityContextAndLinkage(FoundR, FromR)) continue; } + + if (Importer.isMinimalImport()) + return Importer.MapImported(D, FoundTypedef); // If the "From" context has a complete underlying type but we // already have a complete underlying type then return with that. if (!FromUT->isIncompleteType() && !FoundUT->isIncompleteType()) @@ -3059,7 +3080,7 @@ ExpectedDecl ASTNodeImporter::VisitEnumDecl(EnumDecl *D) { // Import the definition if (D->isCompleteDefinition()) - if (Error Err = ImportDefinition(D, D2)) + if (Error Err = ImportDefinition(D, D2, IDK_Default)) return std::move(Err); return D2; @@ -6403,7 +6424,7 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateSpecializationDecl( } if (D->isCompleteDefinition()) - if (Error Err = ImportDefinition(D, D2)) + if (Error Err = ImportDefinition(D, D2, IDK_Default)) return std::move(Err); return D2; diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index b8db635a2b890..c0255cec11bdb 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -864,12 +864,20 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return false; break; - case Type::Pointer: - if (!IsStructurallyEquivalent(Context, - cast(T1)->getPointeeType(), - cast(T2)->getPointeeType())) + case Type::Pointer: { + /* TO_UPSTREAM(BoundsSafety) ON*/ + const PointerType *PT1 = cast(T1); + const PointerType *PT2 = cast(T2); + + if (!IsStructurallyEquivalent(Context, PT1->getPointeeType(), + PT2->getPointeeType())) + return false; + if (!BoundsSafetyPointerAttributes::areEquivalentLayouts( + PT1->getPointerAttributes(), PT2->getPointerAttributes())) return false; break; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + } case Type::BlockPointer: if (!IsStructurallyEquivalent(Context, @@ -1100,6 +1108,22 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return false; break; + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: + if (!IsStructurallyEquivalent( + Context, cast(T1)->desugar(), + cast(T2)->desugar())) + return false; + break; + + case Type::ValueTerminated: + if (!IsStructurallyEquivalent(Context, + cast(T1)->desugar(), + cast(T2)->desugar())) + return false; + break; + /* TO_UPSTREAM(BoundsSafety) OFF */ + case Type::BTFTagAttributed: if (!IsStructurallyEquivalent( Context, cast(T1)->getWrappedType(), diff --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp index fefb8f55a9ee2..c0ca9d0722ee0 100644 --- a/clang/lib/AST/AttrImpl.cpp +++ b/clang/lib/AST/AttrImpl.cpp @@ -270,6 +270,13 @@ unsigned AlignedAttr::getAlignment(ASTContext &Ctx) const { return Ctx.getTargetDefaultAlignForAttributeAligned(); } +std::string DomainAvailabilityAttr::getFeatureAttributeStr() const { + assert(!getDomain().empty() && "cannot be called if domain is empty"); + std::string S = "__attribute__((availability(domain:"; + S += getDomain().str() + ", " + (getUnavailable() ? '1' : '0') + ")))"; + return S; +} + StringLiteral *FormatMatchesAttr::getFormatString() const { return cast(getExpectedFormat()); } diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index fd2eefa1cf076..7fe95028fcf35 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -21,6 +21,20 @@ using namespace clang; +/* TO_UPSTREAM(BoundsSafety) ON*/ +namespace { + template + ExprDependence SumDependence(Range R) { + auto Result = ExprDependence::None; + for (const auto *E : R) { + if (E) + Result |= cast(E)->getDependence(); + } + return Result; + } +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + ExprDependence clang::computeDependence(FullExpr *E) { return E->getSubExpr()->getDependence(); } @@ -885,6 +899,37 @@ ExprDependence clang::computeDependence(MaterializeTemporaryExpr *E) { return E->getSubExpr()->getDependence(); } +ExprDependence clang::computeDependence(MaterializeSequenceExpr *E) { + // All opaque values managed by MaterializeSequenceExpr should be used by the + // main sub-expression, therefore it is the only visitation root we need to + // respect. + return E->getWrappedExpr()->getDependence(); +} + +ExprDependence clang::computeDependence(PredefinedBoundsCheckExpr *E) { + return SumDependence(E->children()); +} + +ExprDependence clang::computeDependence(BoundsCheckExpr *E) { + return SumDependence(E->children()); +} + +ExprDependence clang::computeDependence(AssumptionExpr *E) { + return SumDependence(E->children()); +} + +ExprDependence clang::computeDependence(BoundsSafetyPointerPromotionExpr *E) { + return SumDependence(E->children()); +} + +ExprDependence clang::computeDependence(GetBoundExpr *E) { + return E->getSubExpr()->getDependence(); +} + +ExprDependence clang::computeDependence(ForgePtrExpr *E) { + return SumDependence(E->children()); +} + ExprDependence clang::computeDependence(CXXFoldExpr *E) { auto D = ExprDependence::TypeValueInstantiation; for (const auto *C : {E->getLHS(), E->getRHS()}) { diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 61d497999b669..6fe5bdcb81190 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -203,6 +203,22 @@ static bool isExplicitMemberSpecialization(const RedeclarableTemplateDecl *D) { return D->isMemberSpecialization(); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +// A BoundsSafety helper function. +bool clang::IsConstOrLateConst(const Decl *D) { + if (D->hasAttr()) + return true; + + auto const *VD = dyn_cast(D); + if (!VD) + return false; + + if (VD->getType().isConstQualified()) + return true; + return false; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Given a visibility attribute, return the explicit visibility /// associated with it. template @@ -1640,6 +1656,7 @@ Module *Decl::getOwningModuleForLinkage() const { switch (M->Kind) { case Module::ModuleMapModule: + case Module::IncludeTreeModuleMap: // Module map modules have no special linkage semantics. return nullptr; @@ -5154,6 +5171,21 @@ bool RecordDecl::isOrContainsUnion() const { return false; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +bool RecordDecl::isParentStructOf(const Decl *Inner) const { + auto DeclCtx = Inner->getDeclContext(); + if (DeclCtx == this) + return true; + for (auto FD : fields()) { + if (auto InnerStructTy = FD->getType()->getAs()) { + if (cast(InnerStructTy->getDecl())->isParentStructOf(Inner)) + return true; + } + } + return false; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + RecordDecl::field_iterator RecordDecl::field_begin() const { if (hasExternalLexicalStorage() && !hasLoadedFieldsFromExternalStorage()) LoadFieldsFromExternalStorage(); @@ -5470,6 +5502,36 @@ bool ValueDecl::isParameterPack() const { return isa_and_nonnull(getType().getTypePtrOrNull()); } +/* TO_UPSTREAM(BoundsSafety) ON */ +bool ValueDecl::isDependentParamOfReturnType( + const BoundsAttributedType **RetType, + const TypeCoupledDeclRefInfo **Info) const { + const auto *PVD = dyn_cast(this); + if (!PVD) + return false; + + const auto *FD = dyn_cast(PVD->getDeclContext()); + if (!FD) + return false; + + const auto *BATy = FD->getReturnType()->getAs(); + if (!BATy) + return false; + + auto *It = std::find_if( + BATy->dependent_decl_begin(), BATy->dependent_decl_end(), + [PVD](const TypeCoupledDeclRefInfo &I) { return I.getDecl() == PVD; }); + if (It == BATy->dependent_decl_end()) + return false; + + if (RetType) + *RetType = BATy; + if (Info) + *Info = &*It; + return true; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void ImplicitParamDecl::anchor() {} ImplicitParamDecl *ImplicitParamDecl::Create(ASTContext &C, DeclContext *DC, diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index fead99c5f28a9..56e96241c1fc6 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -859,6 +859,12 @@ bool Decl::isWeakImported() const { if (CheckAvailability(getASTContext(), Availability, nullptr, VersionTuple()) == AR_NotYetIntroduced) return true; + } else if (const auto *DA = dyn_cast(A)) { + auto DomainName = DA->getDomain(); + auto FeatureInfo = getASTContext().getFeatureAvailInfo(DomainName); + if (FeatureInfo.Kind == FeatureAvailKind::Dynamic) + return true; + continue; } } diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index 5c107325df30c..a0245809a967b 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -1476,12 +1476,8 @@ ObjCTypeParamDecl *ObjCTypeParamDecl::Create(ASTContext &ctx, DeclContext *dc, IdentifierInfo *name, SourceLocation colonLoc, TypeSourceInfo *boundInfo) { - auto *TPDecl = - new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, - nameLoc, name, colonLoc, boundInfo); - QualType TPType = ctx.getObjCTypeParamType(TPDecl, {}); - TPDecl->setTypeForDecl(TPType.getTypePtr()); - return TPDecl; + return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index, + nameLoc, name, colonLoc, boundInfo); } ObjCTypeParamDecl *ObjCTypeParamDecl::CreateDeserialized(ASTContext &ctx, @@ -1544,7 +1540,7 @@ ObjCInterfaceDecl *ObjCInterfaceDecl::Create( auto *Result = new (C, DC) ObjCInterfaceDecl(C, DC, atLoc, Id, typeParamList, ClassLoc, PrevDecl, isInternal); - Result->Data.setInt(!C.getLangOpts().Modules); + Result->Data.setInt(!C.getExternalSource()); C.getObjCInterfaceType(Result, PrevDecl); return Result; } @@ -1554,7 +1550,7 @@ ObjCInterfaceDecl *ObjCInterfaceDecl::CreateDeserialized(const ASTContext &C, auto *Result = new (C, ID) ObjCInterfaceDecl(C, nullptr, SourceLocation(), nullptr, nullptr, SourceLocation(), nullptr, false); - Result->Data.setInt(!C.getLangOpts().Modules); + Result->Data.setInt(!C.getExternalSource()); return Result; } @@ -1943,7 +1939,7 @@ ObjCProtocolDecl *ObjCProtocolDecl::Create(ASTContext &C, DeclContext *DC, ObjCProtocolDecl *PrevDecl) { auto *Result = new (C, DC) ObjCProtocolDecl(C, DC, Id, nameLoc, atStartLoc, PrevDecl); - Result->Data.setInt(!C.getLangOpts().Modules); + Result->Data.setInt(!C.getExternalSource()); return Result; } @@ -1952,7 +1948,7 @@ ObjCProtocolDecl *ObjCProtocolDecl::CreateDeserialized(ASTContext &C, ObjCProtocolDecl *Result = new (C, ID) ObjCProtocolDecl(C, nullptr, nullptr, SourceLocation(), SourceLocation(), nullptr); - Result->Data.setInt(!C.getLangOpts().Modules); + Result->Data.setInt(!C.getExternalSource()); return Result; } @@ -2329,18 +2325,19 @@ raw_ostream &clang::operator<<(raw_ostream &OS, void ObjCCompatibleAliasDecl::anchor() {} -ObjCCompatibleAliasDecl * -ObjCCompatibleAliasDecl::Create(ASTContext &C, DeclContext *DC, - SourceLocation L, - IdentifierInfo *Id, - ObjCInterfaceDecl* AliasedClass) { - return new (C, DC) ObjCCompatibleAliasDecl(DC, L, Id, AliasedClass); +ObjCCompatibleAliasDecl *ObjCCompatibleAliasDecl::Create( + ASTContext &C, DeclContext *DC, SourceLocation NameLoc, IdentifierInfo *Id, + ObjCInterfaceDecl *AliasedClass, SourceLocation AliasedClassLoc, + SourceLocation AtLoc) { + return new (C, DC) ObjCCompatibleAliasDecl(DC, NameLoc, Id, AliasedClass, + AliasedClassLoc, AtLoc); } ObjCCompatibleAliasDecl * ObjCCompatibleAliasDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { - return new (C, ID) ObjCCompatibleAliasDecl(nullptr, SourceLocation(), - nullptr, nullptr); + return new (C, ID) + ObjCCompatibleAliasDecl(nullptr, SourceLocation(), nullptr, nullptr, + SourceLocation(), SourceLocation()); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 22da5bf251ecd..8d87a6371193c 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -691,13 +691,15 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { CXXConversionDecl *ConversionDecl = dyn_cast(D); CXXDeductionGuideDecl *GuideDecl = dyn_cast(D); if (!Policy.SuppressSpecifiers) { - switch (D->getStorageClass()) { - case SC_None: break; - case SC_Extern: Out << "extern "; break; - case SC_Static: Out << "static "; break; - case SC_PrivateExtern: Out << "__private_extern__ "; break; - case SC_Auto: case SC_Register: - llvm_unreachable("invalid for functions"); + if (!Policy.SupressStorageClassSpecifiers) { + switch (D->getStorageClass()) { + case SC_None: break; + case SC_Extern: Out << "extern "; break; + case SC_Static: Out << "static "; break; + case SC_PrivateExtern: Out << "__private_extern__ "; break; + case SC_Auto: case SC_Register: + llvm_unreachable("invalid for functions"); + } } if (D->isInlineSpecified()) Out << "inline "; diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 59c0e47c7c195..61d479e8a6ba9 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1876,6 +1876,11 @@ bool CastExpr::CastConsistency() const { } goto CheckNoBasePath; + case CK_BoundsSafetyPointerCast: + assert(getType()->isPointerType()); + assert(getSubExpr()->getType()->isPointerType()); + goto CheckNoBasePath; + case CK_AnyPointerToBlockPointerCast: assert(getType()->isBlockPointerType()); assert(getSubExpr()->getType()->isAnyPointerType() && @@ -1981,6 +1986,53 @@ const char *CastExpr::getCastKindName(CastKind CK) { } namespace { + /* TO_UPSTREAM(BoundsSafety) ON */ + const Expr *skipImplicitTemporary( + const Expr *E, + llvm::SmallPtrSetImpl &BoundValues) { + const Expr *Prev; + do { + Prev = E; + // Skip through reference binding to temporary. + if (auto *Materialize = dyn_cast(E)) + E = Materialize->getSubExpr(); + + // Skip any temporary bindings; they're implicit. + if (auto *Binder = dyn_cast(E)) + E = Binder->getSubExpr(); + + if (auto *MSE = dyn_cast(E)) { + if (!MSE->isUnbinding()) { + BoundValues.insert(MSE->opaquevalues_begin(), + MSE->opaquevalues_end()); + } + E = MSE->getWrappedExpr(); + } + + if (auto *Opaque = dyn_cast(E)) + if (BoundValues.count(Opaque) != 0) + E = Opaque->getSourceExpr(); + + if (auto *BCE = dyn_cast(E)) { + BoundValues.insert(BCE->opaquevalues_begin(), BCE->opaquevalues_end()); + E = BCE->getGuardedExpr(); + } + + if (auto *FPPE = dyn_cast(E)) + E = FPPE->getSubExpr(); + + if (auto *AE = dyn_cast(E)) + E = AE->getWrappedExpr(); + } while (Prev != E); + return E; + } + + const Expr *skipImplicitTemporary(const Expr *E) { + llvm::SmallPtrSet BoundValues; + return skipImplicitTemporary(E, BoundValues); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Skip over implicit nodes produced as part of semantic analysis. // Designed for use with IgnoreExprNodes. static Expr *ignoreImplicitSemaNodes(Expr *E) { @@ -2152,6 +2204,175 @@ CStyleCastExpr *CStyleCastExpr::CreateEmpty(const ASTContext &C, return new (Buffer) CStyleCastExpr(EmptyShell(), PathSize, HasFPFeatures); } +AssumptionExpr::AssumptionExpr(Expr *ResultExpr, + llvm::ArrayRef Assumptions) + : Expr(AssumptionExprClass, ResultExpr->getType(), + ResultExpr->getValueKind(), ResultExpr->getObjectKind()) { + AssumptionExprBits.NumExprs = Assumptions.size() + 1; + auto ExprPtr = getTrailingExprs(); + *ExprPtr = ResultExpr; + for (Expr *E : Assumptions) { + ++ExprPtr; + *ExprPtr = E; + } + setDependence(computeDependence(this)); +} + +AssumptionExpr *AssumptionExpr::Create(const ASTContext &Ctx, Expr *Result, + ArrayRef Assumptions) { + const unsigned NumExprs = Assumptions.size() + 1; + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumExprs), + alignof(AssumptionExpr)); + return new (Mem) AssumptionExpr(Result, Assumptions); +} + +AssumptionExpr *AssumptionExpr::CreateEmpty(const ASTContext &Ctx, + unsigned NumExprs) { + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumExprs), + alignof(AssumptionExpr)); + return new (Mem) AssumptionExpr(EmptyShell(), NumExprs); +} + +Stmt **BoundsSafetyPointerPromotionExpr::getPointerPtr() { + return getTrailingObjects(); +} + +Stmt **BoundsSafetyPointerPromotionExpr::getLowerBoundPtr() { + auto FA = getType()->getAs()->getPointerAttributes(); + if (!FA.hasLowerBound()) + return nullptr; + return getPointerPtr() + 2; +} + +Stmt **BoundsSafetyPointerPromotionExpr::getUpperBoundPtr() { + return getPointerPtr() + 1; +} + +BoundsSafetyPointerPromotionExpr *BoundsSafetyPointerPromotionExpr::CreateEmpty( + const ASTContext &Context, unsigned SubExprCount) { + assert(SubExprCount >= 1 && "Missing Ptr SubExpr"); + void *Buffer = Context.Allocate(totalSizeToAlloc(SubExprCount), + alignof(BoundsSafetyPointerPromotionExpr)); + auto *FPPE = new (Buffer) BoundsSafetyPointerPromotionExpr(EmptyShell()); + return FPPE; +} + +BoundsSafetyPointerPromotionExpr * +BoundsSafetyPointerPromotionExpr::Create(const ASTContext &Context, QualType QT, + Expr *Ptr, Expr *UpperBound, + Expr *LowerBound, bool NullCheck) { + auto PtrTy = QT->getAs(); + auto FA = PtrTy->getPointerAttributes(); + assert(FA.hasUpperBound()); + unsigned Count = 1 + FA.hasLowerBound() + FA.hasUpperBound(); + void *Buffer = Context.Allocate(totalSizeToAlloc(Count), + alignof(BoundsSafetyPointerPromotionExpr)); + auto *FPPE = new (Buffer) + BoundsSafetyPointerPromotionExpr(QT, Ptr, UpperBound, LowerBound, NullCheck); + return FPPE; +} + +Expr *BoundsSafetyPointerPromotionExpr::getSubExprAsWritten() { + const Expr *SubExpr = skipImplicitTemporary(getSubExpr()); + while (const auto *E = dyn_cast(SubExpr)) { + SubExpr = skipImplicitTemporary(E->getSubExpr()); + } + return const_cast(SubExpr); +} + +PredefinedBoundsCheckExpr::PredefinedBoundsCheckExpr(Expr *GuardedExpr, + BoundsCheckKind Kind, + ArrayRef CheckArgs) + : Expr(PredefinedBoundsCheckExprClass, GuardedExpr->getType(), + GuardedExpr->getValueKind(), GuardedExpr->getObjectKind()) { + PredefinedBoundsCheckExprBits.Kind = static_cast(Kind); + PredefinedBoundsCheckExprBits.NumChildren = + CheckArgs.size() + PredefinedBoundsCheckExpr::getCheckArgsOffset(); + + setGuardedExpr(GuardedExpr); + std::copy(CheckArgs.begin(), CheckArgs.end(), + getSubExprs() + PredefinedBoundsCheckExpr::getCheckArgsOffset()); + setDependence(computeDependence(this)); +} + +PredefinedBoundsCheckExpr * +PredefinedBoundsCheckExpr::Create(ASTContext &Ctx, Expr *GuardedExpr, + BoundsCheckKind Kind, + ArrayRef CheckArgs) { + const unsigned NumExprs = CheckArgs.size() + getCheckArgsOffset(); + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumExprs), + alignof(PredefinedBoundsCheckExpr)); + return new (Mem) PredefinedBoundsCheckExpr(GuardedExpr, Kind, CheckArgs); +} + +PredefinedBoundsCheckExpr * +PredefinedBoundsCheckExpr::CreateEmpty(ASTContext &Ctx, unsigned NumChildren) { + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumChildren), + alignof(PredefinedBoundsCheckExpr)); + return new (Mem) PredefinedBoundsCheckExpr(EmptyShell(), NumChildren); +} + +StringRef PredefinedBoundsCheckExpr::getKindName() const { + switch (getKind()) { + case BoundsCheckKind::FlexibleArrayCountDeref: + return "FlexibleArrayCountDeref(BasePtr, FamPtr, Count)"; + case BoundsCheckKind::FlexibleArrayCountCast: + return "FlexibleArrayCountCast(BasePtr, FamPtr, Count)"; + case BoundsCheckKind::FlexibleArrayCountAssign: + return "FlexibleArrayCountAssign(BasePtr, FamPtr, Count)"; + } + llvm_unreachable("Unexpected PredefinedBoundsCheckKind"); +} + +BoundsCheckExpr::BoundsCheckExpr(Expr *GuardedExpr, Expr *Cond, + ArrayRef CommonExprs) + : Expr(BoundsCheckExprClass, GuardedExpr->getType(), + GuardedExpr->getValueKind(), GuardedExpr->getObjectKind()) { + BoundsCheckExprBits.NumChildren = CommonExprs.size() + 2; + + setGuardedExpr(GuardedExpr); + setCond(Cond); + std::copy(CommonExprs.begin(), CommonExprs.end(), getSubExprs() + 2); + setDependence(computeDependence(this)); +} + +BoundsCheckExpr *BoundsCheckExpr::Create(ASTContext &Ctx, Expr *GuardedExpr, + Expr *Cond, + ArrayRef CommonExprs) { + const unsigned NumExprs = CommonExprs.size() + 2; + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumExprs), + alignof(BoundsCheckExpr)); + return new (Mem) BoundsCheckExpr(GuardedExpr, Cond, CommonExprs); +} + +BoundsCheckExpr *BoundsCheckExpr::CreateEmpty(ASTContext &Ctx, + unsigned NumChildren) { + unsigned SizeOfTrailingObjects = NumChildren * sizeof(Stmt *); + void *Mem = Ctx.Allocate(sizeof(BoundsCheckExpr) + SizeOfTrailingObjects, + alignof(BoundsCheckExpr)); + return new (Mem) BoundsCheckExpr(EmptyShell(), NumChildren); +} + +MaterializeSequenceExpr *MaterializeSequenceExpr::CreateEmpty(const ASTContext &Ctx, + unsigned NumExprs) { + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumExprs), + alignof(MaterializeSequenceExpr)); + + return new (Mem) MaterializeSequenceExpr(EmptyShell(), NumExprs); +} + +MaterializeSequenceExpr *MaterializeSequenceExpr::Create(const ASTContext &Ctx, + Expr *WrappedExpr, + ArrayRef Values, + bool Unbind) { + const unsigned NumExprs = Values.size() + 1; + + void *Mem = Ctx.Allocate(totalSizeToAlloc(NumExprs), + alignof(MaterializeSequenceExpr)); + + return new (Mem) MaterializeSequenceExpr(WrappedExpr, Values, Unbind); +} + /// getOpcodeStr - Turn an Opcode enum value into the punctuation char it /// corresponds to, e.g. "<<=". StringRef BinaryOperator::getOpcodeStr(Opcode Op) { @@ -2629,9 +2850,20 @@ bool Expr::isReadIfDiscardedInCPlusPlus11() const { /// be warned about if the result is unused. If so, fill in Loc and Ranges /// with location to warn on and the source range[s] to report with the /// warning. +/* TO_UPSTREAM(BoundsSafety) ON*/ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, SourceRange &R1, SourceRange &R2, ASTContext &Ctx) const { + llvm::SmallPtrSet BoundExprs; + return isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + +bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, + SourceRange &R1, SourceRange &R2, + ASTContext &Ctx, + // TO_UPSTREAM(BoundsSafety) + llvm::SmallPtrSetImpl &BoundExprs) const { // Don't warn if the expr is type dependent. The type could end up // instantiating to void. if (isTypeDependent()) @@ -2647,17 +2879,17 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, return true; case ParenExprClass: return cast(this)->getSubExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); case GenericSelectionExprClass: return cast(this)->getResultExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); case CoawaitExprClass: case CoyieldExprClass: return cast(this)->getResumeExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); case ChooseExprClass: return cast(this)->getChosenSubExpr()-> - isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); case UnaryOperatorClass: { const UnaryOperator *UO = cast(this); @@ -2685,7 +2917,8 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, return false; break; case UO_Extension: - return UO->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return UO->getSubExpr()->isUnusedResultAWarning( + WarnE, Loc, R1, R2, Ctx, BoundExprs); } WarnE = this; Loc = UO->getOperatorLoc(); @@ -2706,12 +2939,15 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, dyn_cast(BO->getRHS()->IgnoreParens())) if (IE->getValue() == 0) return false; - return BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return BO->getRHS()->isUnusedResultAWarning( + WarnE, Loc, R1, R2, Ctx, BoundExprs); // Consider '||', '&&' to have side effects if the LHS or RHS does. case BO_LAnd: case BO_LOr: - if (!BO->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx) || - !BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)) + if (!BO->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs) || + !BO->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs)) return false; break; } @@ -2733,12 +2969,15 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, // be being used for control flow. Only warn if both the LHS and // RHS are warnings. const auto *Exp = cast(this); - return Exp->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx) && - Exp->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return Exp->getLHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs) && + Exp->getRHS()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs); } case BinaryConditionalOperatorClass: { const auto *Exp = cast(this); - return Exp->getFalseExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return Exp->getFalseExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs); } case MemberExprClass: @@ -2910,10 +3149,10 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, const CompoundStmt *CS = cast(this)->getSubStmt(); if (!CS->body_empty()) { if (const Expr *E = dyn_cast(CS->body_back())) - return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); if (const LabelStmt *Label = dyn_cast(CS->body_back())) if (const Expr *E = dyn_cast(Label->getSubStmt())) - return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return E->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); } if (getType()->isVoidType()) @@ -2949,7 +3188,8 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, if (SubE->getType()->isArrayType()) return false; - return SubE->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return SubE->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs); } return false; } @@ -2957,7 +3197,8 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, // If this is a cast to a constructor conversion, check the operand. // Otherwise, the result of the cast is unused. if (CE->getCastKind() == CK_ConstructorConversion) - return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return CE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs); if (CE->getCastKind() == CK_Dependent) return false; @@ -2973,6 +3214,27 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, } return true; } + case BoundsSafetyPointerPromotionExprClass: + return cast(this) + ->getPointer() + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); + case AssumptionExprClass: + return cast(this) + ->getWrappedExpr() + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); + case BoundsCheckExprClass: { + const auto *BCE = cast(this); + BoundExprs.insert(BCE->opaquevalues_begin(), BCE->opaquevalues_end()); + return BCE->getGuardedExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, + Ctx, BoundExprs); + } + case MaterializeSequenceExprClass: { + const auto *MSE = cast(this); + if (!MSE->isUnbinding()) + BoundExprs.insert(MSE->opaquevalues_begin(), MSE->opaquevalues_end()); + return MSE->getWrappedExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, + Ctx, BoundExprs); + } case ImplicitCastExprClass: { const CastExpr *ICE = cast(this); @@ -2981,14 +3243,17 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, ICE->getSubExpr()->getType().isVolatileQualified()) return false; - return ICE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + return ICE->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs); } case CXXDefaultArgExprClass: return (cast(this) - ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); + ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs)); case CXXDefaultInitExprClass: return (cast(this) - ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx)); + ->getExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, + BoundExprs)); case CXXNewExprClass: // FIXME: In theory, there might be new expressions that don't have side @@ -2998,16 +3263,16 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, case MaterializeTemporaryExprClass: return cast(this) ->getSubExpr() - ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); case CXXBindTemporaryExprClass: return cast(this)->getSubExpr() - ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); case ExprWithCleanupsClass: return cast(this)->getSubExpr() - ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx); + ->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx, BoundExprs); case OpaqueValueExprClass: return cast(this)->getSourceExpr()->isUnusedResultAWarning( - WarnE, Loc, R1, R2, Ctx); + WarnE, Loc, R1, R2, Ctx, BoundExprs); } } @@ -3083,32 +3348,65 @@ QualType Expr::findBoundMemberType(const Expr *expr) { } Expr *Expr::IgnoreImpCasts() { - return IgnoreExprNodes(this, IgnoreImplicitCastsSingleStep); + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; + return IgnoreExprNodes(this, IgnoreImplicitCastsSingleStep, + IgnoreBoundsSafetyImplicit); } Expr *Expr::IgnoreCasts() { - return IgnoreExprNodes(this, IgnoreCastsSingleStep); + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; + return IgnoreExprNodes(this, IgnoreCastsSingleStep, IgnoreBoundsSafetyImplicit); } Expr *Expr::IgnoreImplicit() { - return IgnoreExprNodes(this, IgnoreImplicitSingleStep); + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; + return IgnoreExprNodes(this, IgnoreImplicitSingleStep, + IgnoreBoundsSafetyImplicit); } Expr *Expr::IgnoreImplicitAsWritten() { - return IgnoreExprNodes(this, IgnoreImplicitAsWrittenSingleStep); + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; + return IgnoreExprNodes(this, IgnoreImplicitAsWrittenSingleStep, + IgnoreBoundsSafetyImplicit); } Expr *Expr::IgnoreParens() { return IgnoreExprNodes(this, IgnoreParensSingleStep); } -Expr *Expr::IgnoreParenImpCasts() { +Expr *Expr::IgnoreParenImpCasts(llvm::SmallPtrSetImpl &BoundValues) { + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; return IgnoreExprNodes(this, IgnoreParensSingleStep, - IgnoreImplicitCastsExtraSingleStep); + IgnoreImplicitCastsExtraSingleStep, + IgnoreBoundsSafetyImplicit); +} + +Expr *Expr::IgnoreParenImpCasts() { + llvm::SmallPtrSet BoundValues; + return IgnoreParenImpCasts(BoundValues); } Expr *Expr::IgnoreParenCasts() { - return IgnoreExprNodes(this, IgnoreParensSingleStep, IgnoreCastsSingleStep); + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; + return IgnoreExprNodes(this, IgnoreParensSingleStep, IgnoreCastsSingleStep, + IgnoreBoundsSafetyImplicit); } Expr *Expr::IgnoreConversionOperatorSingleStep() { @@ -3120,8 +3418,13 @@ Expr *Expr::IgnoreConversionOperatorSingleStep() { } Expr *Expr::IgnoreParenLValueCasts() { + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; return IgnoreExprNodes(this, IgnoreParensSingleStep, - IgnoreLValueCastsSingleStep); + IgnoreLValueCastsSingleStep, + IgnoreBoundsSafetyImplicit); } Expr *Expr::IgnoreParenBaseCasts() { @@ -3156,6 +3459,10 @@ Expr *Expr::IgnoreParenNoopCasts(const ASTContext &Ctx) { } Expr *Expr::IgnoreUnlessSpelledInSource() { + llvm::SmallPtrSet BoundValues; + auto IgnoreBoundsSafetyImplicit = [&](Expr *E) { + return IgnoreBoundsSafetyImplicitImpl(E, BoundValues); + }; auto IgnoreImplicitConstructorSingleStep = [](Expr *E) { if (auto *Cast = dyn_cast(E)) { auto *SE = Cast->getSubExpr(); @@ -3194,7 +3501,7 @@ Expr *Expr::IgnoreUnlessSpelledInSource() { return IgnoreExprNodes( this, IgnoreImplicitSingleStep, IgnoreImplicitCastsExtraSingleStep, IgnoreParensOnlySingleStep, IgnoreImplicitConstructorSingleStep, - IgnoreImplicitMemberCallSingleStep); + IgnoreImplicitMemberCallSingleStep, IgnoreBoundsSafetyImplicit); } bool Expr::isDefaultArgument() const { @@ -3330,6 +3637,17 @@ bool Expr::hasAnyTypeDependentArguments(ArrayRef Exprs) { return false; } +namespace { +const ValueDecl *getUnderlyingDecl(ASTContext &Ctx, const Expr *E) { + E = E->IgnoreParenCasts(); + if (const auto *ME = dyn_cast(E)) + return ME->getMemberDecl(); + if (const auto *DRE = dyn_cast(E)) + return DRE->getDecl(); + return nullptr; +} +} // namespace + bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, const Expr **Culprit) const { assert(!isValueDependent() && @@ -3399,6 +3717,16 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, return DIUE->getBase()->isConstantInitializer(Ctx, false, Culprit) && DIUE->getUpdater()->isConstantInitializer(Ctx, false, Culprit); } + case ForgePtrExprClass: { + const ForgePtrExpr *FPE = cast(this); + if (!FPE->getAddr()->isConstantInitializer(Ctx, false, Culprit)) + return false; + if (FPE->ForgesBidiIndexablePointer()) + return FPE->getSize()->isConstantInitializer(Ctx, false, Culprit); + if (FPE->ForgesTerminatedByPointer()) + assert(FPE->getTerminator()->isConstantInitializer(Ctx, false, Culprit)); + return true; + } case InitListExprClass: { // C++ [dcl.init.aggr]p2: // The elements of an aggregate are: @@ -3453,6 +3781,32 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, return false; } } else { + // BoundsSafety : Check if a static initializer for RecordType with + // dynamic count fields can be statically evaluated without runtime checks. + if (Field->getType()->isCountAttributedType()) { + auto Dcl = getUnderlyingDecl(Ctx, Elt); + if (Dcl && Dcl->getType()->isConstantArrayType()) { + if (Ctx.getTypeSizeInCharsIfKnown(Dcl->getType()).has_value()) { + // Strip the pointer cast and let it fall through. + Elt = Elt->IgnoreParens(); + if (auto ICE = dyn_cast(Elt)) { + if (ICE->getCastKind() == CK_BoundsSafetyPointerCast) + Elt = ICE->getSubExpr(); + } else if (auto FPPE = + dyn_cast(Elt)) { + Elt = FPPE->getPointer(); + } + } + } + } + + // FIXME: rdar://81135826 + if (Field->getType()->isDynamicRangePointerType()) { + if (Culprit) + *Culprit = Elt; + return false; + } + bool RefType = Field->getType()->isReferenceType(); if (!Elt->isConstantInitializer(Ctx, RefType, Culprit)) return false; @@ -3747,6 +4101,15 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case ShuffleVectorExprClass: case ConvertVectorExprClass: case AsTypeExprClass: + case BoundsSafetyPointerPromotionExprClass: + case AssumptionExprClass: + case ForgePtrExprClass: + case GetBoundExprClass: + case PredefinedBoundsCheckExprClass: + case BoundsCheckExprClass: + case MaterializeSequenceExprClass: + case TerminatedByToIndexableExprClass: + case TerminatedByFromIndexableExprClass: case CXXParenListInitExprClass: // These have a side-effect if any subexpression does. break; @@ -3971,6 +4334,30 @@ bool Expr::hasNonTrivialCall(const ASTContext &Ctx) const { return Finder.hasNonTrivialCall(); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +Expr::NullPointerConstantKind Expr::isNullPointerConstantIgnoreCastsAndOVEs( + ASTContext &Ctx, NullPointerConstantValueDependence NPC) const { + // Expr::isNullPointerConstant only handles explict `(void*)` casts so it + // won't detect something like `(int*)0` as a null pointer constant. Try to + // compensate for that here. + + const Expr *EPrevious = nullptr; + const Expr *ECurrent = this; + while (EPrevious != ECurrent) { + EPrevious = ECurrent; + + // Walk through parens and implicit/explicit casts + ECurrent = ECurrent->IgnoreParenCasts(); + + // Walk through OVEs + if (const auto *OVE = dyn_cast(ECurrent)) { + ECurrent = OVE->getSourceExpr(); + } + } + return ECurrent->isNullPointerConstant(Ctx, NPC); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + /// isNullPointerConstant - C99 6.3.2.3p3 - Return whether this is a null /// pointer constant or not, as well as the specific kind of constant detected. /// Null pointer constants can be integer constant expressions with the @@ -4978,6 +5365,24 @@ UnaryOperator *UnaryOperator::Create(const ASTContext &C, Expr *input, UnaryOperator(C, input, opc, type, VK, OK, l, CanOverflow, FPFeatures); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +OpaqueValueExpr *OpaqueValueExpr::Wrap(const ASTContext &Context, Expr *E) { + assert(!isa(E)); + return new (Context) OpaqueValueExpr( + E->getExprLoc(), E->getType(), E->getValueKind(), E->getObjectKind(), E); +} + +OpaqueValueExpr *OpaqueValueExpr::EnsureWrapped( + const ASTContext &Ctx, Expr *E, SmallVectorImpl &OVEs) { + auto *OVE = dyn_cast(E); + if (!OVE) { + OVE = Wrap(Ctx, E); + OVEs.push_back(OVE); + } + return OVE; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + const OpaqueValueExpr *OpaqueValueExpr::findInCopyConstruct(const Expr *e) { if (const ExprWithCleanups *ewc = dyn_cast(e)) e = ewc->getSubExpr(); diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 3f37d06cc8f3a..124782c082c78 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -217,8 +217,16 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::SourceLocExprClass: case Expr::ConceptSpecializationExprClass: case Expr::RequiresExprClass: + case Expr::BoundsSafetyPointerPromotionExprClass: + case Expr::ForgePtrExprClass: + case Expr::GetBoundExprClass: + case Expr::TerminatedByToIndexableExprClass: + case Expr::TerminatedByFromIndexableExprClass: return Cl::CL_PRValue; + case Expr::AssumptionExprClass: + return ClassifyInternal(Ctx, cast(E)->getWrappedExpr()); + case Expr::EmbedExprClass: // Nominally, this just goes through as a PRValue until we actually expand // it and check it. @@ -272,6 +280,18 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { ? Cl::CL_PRValue : Cl::CL_LValue; return ClassifyDecl(Ctx, cast(E)->getDecl()); + case Expr::BoundsCheckExprClass: + // Simply look through. + return ClassifyInternal(Ctx, cast(E)->getGuardedExpr()); + + case Expr::PredefinedBoundsCheckExprClass: + // Simply look through. + return ClassifyInternal(Ctx, cast(E)->getGuardedExpr()); + + // Simply look through. + case Expr::MaterializeSequenceExprClass: + return ClassifyInternal(Ctx, cast(E)->getWrappedExpr()); + // Member access is complex. case Expr::MemberExprClass: return ClassifyMemberExpr(Ctx, cast(E)); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index f2e49b9ea669e..228f64fdb5d63 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -76,6 +76,11 @@ using llvm::APSInt; using llvm::APFloat; using llvm::FixedPointSemantics; +/* TO_UPSTREAM(BoundsSafety) ON */ +static uint64_t evaluateIncompleteArraySize(const ASTContext &C, + QualType T); +/* TO_UPSTREAM(BoundsSafety) OFF */ + namespace { struct LValue; class CallStackFrame; @@ -182,6 +187,13 @@ namespace { llvm_unreachable("unknown ConstantExprKind"); } + /* TO_UPSTREAM(BoundsSafety) ON */ + static CharUnits getTypeSizeWithUnknownAsOne(const ASTContext &Ctx, + QualType Ty) { + return Ctx.getTypeSizeInCharsIfKnown(Ty).value_or(CharUnits::One()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// The bound to claim that an array of unknown bound has. /// The value in MostDerivedArraySize is undefined in this case. So, set it /// to an arbitrary value that's likely to loudly break things if it's used. @@ -213,6 +225,10 @@ namespace { if (auto *CAT = dyn_cast(AT)) { ArraySize = CAT->getZExtSize(); + /* TO_UPSTREAM(BoundsSafety) ON */ + } else if (auto NumElts = evaluateIncompleteArraySize(Ctx, Type)) { + ArraySize = NumElts; + /* TO_UPSTREAM(BoundsSafety) OFF */ } else { assert(I == 0 && "unexpected unsized array designator"); FirstEntryIsUnsizedArray = true; @@ -448,6 +464,35 @@ namespace { MostDerivedPathLength = Entries.size(); } + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Adjust type of forged pointer and scale up/down the array size and index. + void adjustForgedObjectTypeAndSize(QualType NewElemTy, + uint64_t OldElemSize, uint64_t NewElemSize) { + if (Invalid) + return; + assert(MostDerivedIsArrayElement && + MostDerivedPathLength == Entries.size()); + MostDerivedType = NewElemTy; + if (OldElemSize == NewElemSize) + return; + + const unsigned numBits = sizeof(uint64_t) * CHAR_BIT; + APInt APArraySize(numBits, MostDerivedArraySize); + APInt APOldElemSize(numBits, OldElemSize); + bool Overflow = false; + APArraySize = APArraySize.umul_ov(APOldElemSize, Overflow); + assert(!Overflow); + assert(NewElemSize != 0); + MostDerivedArraySize = APArraySize.getZExtValue() / NewElemSize; + + APInt APArrayIndex(numBits, Entries.back().getAsArrayIndex()); + APArrayIndex = APArrayIndex.umul_ov(APOldElemSize, Overflow); + assert(!Overflow); + uint64_t ArrayIndex = APArrayIndex.getZExtValue() / NewElemSize; + Entries.back() = PathEntry::ArrayIndex(ArrayIndex); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void addVectorElementUnchecked(QualType EltTy, uint64_t Size, uint64_t Idx) { Entries.push_back(PathEntry::ArrayIndex(Idx)); @@ -1632,10 +1677,15 @@ namespace { APValue::LValueBase Base; CharUnits Offset; SubobjectDesignator Designator; + CharUnits ForgedSize; + CharUnits ForgedOffset; bool IsNullPtr : 1; bool InvalidBase : 1; // P2280R4 track if we have an unknown reference or pointer. bool AllowConstexprUnknown = false; + bool IsForgeBidi : 1; + bool IsForgeSingle : 1; + bool IsForgeTerminatedBy : 1; const APValue::LValueBase getLValueBase() const { return Base; } bool allowConstexprUnknown() const { return AllowConstexprUnknown; } @@ -1644,6 +1694,18 @@ namespace { SubobjectDesignator &getLValueDesignator() { return Designator; } const SubobjectDesignator &getLValueDesignator() const { return Designator;} bool isNullPointer() const { return IsNullPtr;} + bool isForgeBidi() const { return IsForgeBidi; } + bool isForgeSingle() const { return IsForgeSingle; } + bool isForgeTerminatedBy() const { return IsForgeTerminatedBy; } + CharUnits getLValueForgedSize() { return ForgedSize; } + const CharUnits &getLValueForgedSize() const { return ForgedSize; } + CharUnits getLValueForgedOffset() { return ForgedOffset; } + const CharUnits &getLValueForgedOffset() const { return ForgedOffset; } + CharUnits getUnwrappedLValueOffset() const { + if (isForgeBidi() || isForgeSingle() || isForgeTerminatedBy()) + return Offset + ForgedOffset; + return Offset; + } unsigned getLValueCallIndex() const { return Base.getCallIndex(); } unsigned getLValueVersion() const { return Base.getVersion(); } @@ -1658,6 +1720,12 @@ namespace { } if (AllowConstexprUnknown) V.setConstexprUnknown(); + if (IsForgeBidi) + V.setLValueForgedBidi(ForgedSize, ForgedOffset); + if (IsForgeSingle) + V.setLValueForgedSingle(ForgedOffset); + if (IsForgeTerminatedBy) + V.setLValueForgedTerminatedBy(ForgedOffset); } void setFrom(ASTContext &Ctx, const APValue &V) { assert(V.isLValue() && "Setting LValue from a non-LValue?"); @@ -1667,6 +1735,11 @@ namespace { Designator = SubobjectDesignator(Ctx, V); IsNullPtr = V.isNullPointer(); AllowConstexprUnknown = V.allowConstexprUnknown(); + IsForgeBidi = V.isLValueForgeBidi(); + ForgedSize = V.getLValueForgedSize(); + ForgedOffset = V.getLValueForgedOffset(); + IsForgeSingle = V.isLValueForgeSingle(); + IsForgeTerminatedBy = V.isLValueForgeTerminatedBy(); } void set(APValue::LValueBase B, bool BInvalid = false) { @@ -1685,6 +1758,11 @@ namespace { Designator = SubobjectDesignator(getType(B)); IsNullPtr = false; AllowConstexprUnknown = false; + IsForgeBidi = false; + IsForgeSingle = false; + IsForgeTerminatedBy = false; + ForgedSize = CharUnits::Zero(); + ForgedOffset = CharUnits::Zero(); } void setNull(ASTContext &Ctx, QualType PointerTy) { @@ -1695,6 +1773,26 @@ namespace { Designator = SubobjectDesignator(PointerTy->getPointeeType()); IsNullPtr = true; AllowConstexprUnknown = false; + IsForgeBidi = false; + IsForgeSingle = false; + IsForgeTerminatedBy = false; + ForgedSize = CharUnits::Zero(); + ForgedOffset = CharUnits::Zero(); + } + + void setForgedBidi(APValue::LValueBase B, QualType T, CharUnits Size, + CharUnits ForgedOfs, bool IsNullP) { + if (B.is() || B.is()) { + ForgedPtrLValue FP(B.getValueDecl()); + set(APValue::LValueBase::getForgedPtr(FP, T)); + } else { + Designator.setInvalid(); + } + AllowConstexprUnknown = false; + IsForgeBidi = true; + ForgedSize = Size; + ForgedOffset = ForgedOfs; + IsNullPtr = IsNullP; } void setInvalid(APValue::LValueBase B, unsigned I = 0) { @@ -1744,6 +1842,38 @@ namespace { Designator.checkSubobject(Info, E, CSK); } + /* TO_UPSTREAM(BoundsSafety) ON */ + CharUnits getLValueSize(const ASTContext &Ctx) const { + if (IsForgeBidi) + return ForgedSize; + + if (IsForgeSingle || IsForgeTerminatedBy) { + QualType Ty = getType(Base); + // When forging a pointer from a constant there's no ValueDecl to + // provide an actual pointee type in consteval context. Since we cannot + // dereference a constant during consteval, the element size is + // effectively 0 (the only caller is validUpperAdjustment()). + if (!Ty.isNull()) + return getTypeSizeWithUnknownAsOne( + Ctx, QualType(Ty->getPointeeOrArrayElementType(), 0)); + } + + if (const auto *VD = Base.dyn_cast()) { + assert(!VD->getType()->isVoidType()); + return Ctx.getTypeSizeInChars(VD->getType()); + } + return CharUnits::Zero(); + } + + // Return the remaining lvalue object size. This is similar to + // SubobjectDesignator::validIndexAdjustments() but we can't always + // rely on it because SubobjectDesignator is easy to be invalid + // (due to bitcast, etc.). LValueOffset is more reliable. + CharUnits validUpperAdjustment(const ASTContext &Ctx) const { + return getLValueSize(Ctx) - getLValueOffset(); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void addDecl(EvalInfo &Info, const Expr *E, const Decl *D, bool Virtual = false) { if (checkSubobject(Info, E, isa(D) ? CSK_Field : CSK_Base)) @@ -1769,6 +1899,28 @@ namespace { if (checkSubobject(Info, E, Imag ? CSK_Imag : CSK_Real)) Designator.addComplexUnchecked(EltTy, Imag); } + void adjustForgedObjectTypeAndSize(const ASTContext &Ctx, + QualType NewElemTy) { + ForgedPtrLValue FP = Base.get(); + const auto *CAT = + Ctx.getAsConstantArrayType(Base.getForgedPtrAsArrayType()); + assert(CAT); + QualType OldElemTy = CAT->getElementType(); + uint64_t OldElemSize = + getTypeSizeWithUnknownAsOne(Ctx, OldElemTy).getQuantity(); + uint64_t NewElemSize = + getTypeSizeWithUnknownAsOne(Ctx, NewElemTy).getQuantity(); + + bool Overflow = false; + APInt APOldElemSize(CAT->getSize().getBitWidth(), OldElemSize); + APInt APNewArraySize = CAT->getSize().umul_ov(APOldElemSize, Overflow); + assert(!Overflow); + APNewArraySize = APNewArraySize.udiv(NewElemSize); + Base = APValue::LValueBase::getForgedPtr( + FP, Ctx.getConstantArrayType(NewElemTy, APNewArraySize, nullptr, + ArraySizeModifier::Normal, 0)); + Designator.adjustForgedObjectTypeAndSize(NewElemTy, OldElemSize, NewElemSize); + } void addVectorElement(EvalInfo &Info, const Expr *E, QualType EltTy, uint64_t Size, uint64_t Idx) { if (checkSubobject(Info, E, CSK_VectorElement)) @@ -1923,6 +2075,8 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result); static bool EvaluateBuiltinStrLen(const Expr *E, uint64_t &Result, EvalInfo &Info, std::string *StringResult = nullptr); +static bool EvaluateTerminatorElement(const Expr *E, APValue &Result, + EvalInfo &Info); /// Evaluate an integer or fixed point expression into an APResult. static bool EvaluateFixedPointOrInteger(const Expr *E, APFixedPoint &Result, @@ -1946,6 +2100,28 @@ static void negateAsSigned(APSInt &Int) { Int = -Int; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +static uint64_t evaluateIncompleteArraySize(const ASTContext &C, QualType T) { + if (!C.getLangOpts().BoundsSafety) + return 0; + + const auto *DCPT = T->getAs(); + if (!T->isIncompleteArrayType() || !DCPT) + return 0; + + APSInt Result; + Expr::EvalStatus Status; + EvalInfo Info(C, Status, EvalInfo::EM_ConstantExpression); + if (!EvaluateInteger(DCPT->getCountExpr(), Result, Info)) + return 0; + + if (Result.isNegative() || Result.ugt(std::numeric_limits::max())) + return 0; + + return Result.getLimitedValue(); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + template APValue &CallStackFrame::createTemporary(const KeyT *Key, QualType T, ScopeKind Scope, LValue &LV) { @@ -2107,7 +2283,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) { if (!B) return true; - if (const ValueDecl *D = B.dyn_cast()) { + // TO_UPSTREAM(BoundsSafety): getValueDecl + if (const ValueDecl *D = B.getValueDecl()) { // ... the address of an object with static storage duration, if (const VarDecl *VD = dyn_cast(D)) return VD->hasGlobalStorage(); @@ -8125,6 +8302,30 @@ class ExprEvaluatorBase return static_cast(this)->VisitCastExpr(E); } + bool VisitForgePtrExpr(const ForgePtrExpr *E) { + if (!StmtVisitorTy::Visit(E->getAddr())) + return false; + + if (E->ForgesBidiIndexablePointer()) + return StmtVisitorTy::Visit(E->getSize()); + if (E->ForgesTerminatedByPointer()) + assert(StmtVisitorTy::Visit(E->getTerminator())); + return true; + } + + bool VisitAssumptionExpr(const AssumptionExpr *E) { + return StmtVisitorTy::Visit(E->getWrappedExpr()); + } + + bool VisitGetBoundExpr(const GetBoundExpr *E) { + return static_cast(this)->Visit(E->getSubExpr()); + } + + bool VisitBoundsCheckExpr(const BoundsCheckExpr *E) { + // FIXME: C++ support + return false; + } + bool VisitBinaryOperator(const BinaryOperator *E) { switch (E->getOpcode()) { default: @@ -8152,6 +8353,13 @@ class ExprEvaluatorBase } bool VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) { + /* TO_UPSTREAM(BoundsSafety) ON*/ + // The same binary conditional operator might occur multiple times in one + // bounds check expression. Don't create a temporary if it already exists. + if (Info.CurrentCall->getCurrentTemporary(E->getOpaqueValue())) + return HandleConditionalOperator(E); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Evaluate and cache the common expression. We treat it as a temporary, // even though it's not quite the same thing. LValue CommonLV; @@ -9483,6 +9691,8 @@ class PointerExprEvaluator bool VisitBinaryOperator(const BinaryOperator *E); bool VisitCastExpr(const CastExpr* E); bool VisitUnaryAddrOf(const UnaryOperator *E); + bool VisitGetBoundExpr(const GetBoundExpr *E); + bool VisitForgePtrExpr(const ForgePtrExpr *E); bool VisitObjCStringLiteral(const ObjCStringLiteral *E) { return Success(E); } bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E) { @@ -9619,6 +9829,82 @@ bool PointerExprEvaluator::VisitUnaryAddrOf(const UnaryOperator *E) { return evaluateLValue(E->getSubExpr(), Result); } +bool PointerExprEvaluator::VisitGetBoundExpr(const GetBoundExpr *E) { + bool Res = evaluatePointer(E->getSubExpr(), Result); + if (!Res) + return false; + + auto &SubObj = Result.getLValueDesignator(); + auto maxAdjustments = SubObj.validIndexAdjustments(); + if (maxAdjustments.first == 0 && maxAdjustments.second == 0) { + Info.CCEDiag(E, diag::err_bounds_safety_evaluate_no_bounds) + << (int)E->getBoundKind(); + return false; + } + + APSInt Adjustment(64, true); // defaults to 0 + if (E->getBoundKind() == GetBoundExpr::BK_Lower) + (APInt &)Adjustment -= maxAdjustments.first; + else + (APInt &)Adjustment += maxAdjustments.second; + + // `E->getType()->getPointeeType() != MostDerivedType` when it's bitcast to + // `void *` and `Offset` must be calculated based on `MostDerivedType` of + // `LValueDesignator`. + return HandleLValueArrayAdjustment( + Info, E, Result, SubObj.MostDerivedType, + Adjustment); +} + +bool PointerExprEvaluator::VisitForgePtrExpr(const ForgePtrExpr *E) { + const ASTContext &Ctx = Info.Ctx; + const Expr *Addr = E->getAddr(); + if (Addr->getType()->isPointerType()) { + if (!evaluatePointer(Addr, Result)) + return false; + } else { + APSInt Value; + if (!EvaluateInteger(Addr, Value, Info)) + return false; + + unsigned TypeSize = Ctx.getTypeSize(E->getType()); + uint64_t Offset = Value.extOrTrunc(TypeSize).getZExtValue(); + Result.Base = (const ValueDecl *)nullptr; + Result.InvalidBase = false; + Result.Offset = CharUnits::fromQuantity(Offset); + Result.Designator.setInvalid(); + Result.IsNullPtr = Value.isZero(); + } + + if (E->ForgesBidiIndexablePointer()) { + APSInt Size; + if (!EvaluateInteger(E->getSize(), Size, Info)) + return false; + + QualType T = Ctx.getConstantArrayType(Ctx.UnsignedCharTy, Size, nullptr, + ArraySizeModifier::Normal, 0); + + // We keep the base offset since the forged pointer starts from there, yet + // the designator index shall start from 0. + // Pretend the value isn't NULL when the size isn't 0. This allows various + // constant operations to work on a forged NULL pointer. + auto CUSize = CharUnits::fromQuantity(Size.getZExtValue()); + Result.setForgedBidi(Result.getLValueBase(), T, CUSize, + Result.getUnwrappedLValueOffset(), + Result.IsNullPtr && CUSize.isZero()); + Result.addArray(Info, E, cast(T)); + } else { + assert(E->ForgesSinglePointer() != E->ForgesTerminatedByPointer()); + Result.IsForgeBidi = false; + Result.IsForgeSingle = E->ForgesSinglePointer(); + Result.IsForgeTerminatedBy = E->ForgesTerminatedByPointer(); + Result.ForgedOffset = Result.getUnwrappedLValueOffset(); + Result.Offset = CharUnits::Zero(); + } + + return true; +} + // Is the provided decl 'std::source_location::current'? static bool IsDeclSourceLocationCurrent(const FunctionDecl *FD) { if (!FD) @@ -9648,10 +9934,24 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_AddressSpaceConversion: if (!Visit(SubExpr)) return false; - // Bitcasts to cv void* are static_casts, not reinterpret_casts, so are - // permitted in constant expressions in C++11. Bitcasts from cv void* are - // also static_casts, but we disallow them as a resolution to DR1312. - if (!E->getType()->isVoidPointerType()) { + + /* TO_UPSTREAM(BoundsSafety) ON */ + // BoundsSafety: __builtin_unsafe_forge_* always returns void *, leading to + // a bitcast from void* to the assignee type. We special case such + // bitcasts so it doesn't invalidate the SubobjectDesignator. + if (!E->getType()->isVoidPointerType() && E->getCastKind() == CK_BitCast && + !Result.InvalidBase && !Result.Designator.Invalid && + (Result.IsForgeSingle || Result.IsForgeTerminatedBy || + Result.IsForgeBidi) && + SubExpr->getType()->isVoidPointerType()) { + if (Result.IsForgeBidi) + Result.adjustForgedObjectTypeAndSize(Info.Ctx, + E->getType()->getPointeeType()); + /* TO_UPSTREAM(BoundsSafety) OFF */ + } else if (!E->getType()->isVoidPointerType()) { + // Bitcasts to cv void* are static_casts, not reinterpret_casts, so are + // permitted in constant expressions in C++11. Bitcasts from cv void* are + // also static_casts, but we disallow them as a resolution to DR1312. // In some circumstances, we permit casting from void* to cv1 T*, when the // actual pointee object is actually a cv2 T. bool HasValidResult = !Result.InvalidBase && !Result.Designator.Invalid && @@ -9692,6 +9992,22 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { Result.Designator.setInvalid(); } } + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety: Conversion from 'void *__single' to 'T *__single' is allowed + // in the bounds-only profile. + // FIXME: check '__counted_by' and friends. + if (E->getCastKind() == CK_BitCast && E->getType()->isSinglePointerType() && + SubExpr->getType()->isSinglePointerType() && + !SubExpr->getType()->getPointeeType()->isIncompleteOrSizelessType()) { + auto DstElemSize = + Info.Ctx.getTypeSizeInChars(E->getType()->getPointeeType()); + auto SrcElemSize = + Info.Ctx.getTypeSizeInChars(SubExpr->getType()->getPointeeType()); + if (SrcElemSize < DstElemSize) + return false; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (E->getCastKind() == CK_AddressSpaceConversion && Result.IsNullPtr) ZeroInitialization(E); return true; @@ -9757,6 +10073,98 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { } } + /* TO_UPSTREAM(BoundsSafety) ON */ + case CK_BoundsSafetyPointerCast: { + // XXX: instead BoundsSafetyPointerCast doing heroics, we should constant- + // evaluate BoundsCheckExpr and fail if the bounds check fails or isn't + // constant. + + // Conversion rules. YES: always converts, CHECK: converts if static check + // passes, NO: never converts + // + // To | Bidi | Indexable | Single | Counted | Unsafe + // From -----+------+-----------+--------+---------+------- + // ----------+------+-----------+--------+---------+------- + // Bidi | YES | CHECK | CHECK | CHECK | YES + // Indexable | YES | YES | CHECK | CHECK | YES + // Single | YES | YES | CHECK | CHECK | YES + // Unsafe | NO | NO | NO | NO | YES + // + // In other words: "to unsafe" always works; else "from unsafe" never works; + // else "to counted" needs a check; else it works. ("To single" is a special + // case of "to counted" where the count is 1.) + // There never is a case where you convert from a counted pointer to + // something else because no lvalue turns into a counted pointer. + + auto ResultPtrTy = E->getType()->getAs(); + auto ResultFA = ResultPtrTy->getPointerAttributes(); + + // Evaluate the sub-expression and bail out if that didn't work. We do + // this first because NULL pointers are always valid constants. + if (!evaluatePointer(E->getSubExpr(), Result)) + return false; + + auto &SubObj = Result.getLValueDesignator(); + // "from unsafe or unspecified" only works if the destination is also + // unsafe; we can bail out early if that's not the case + auto InputPtrTy = E->getSubExpr()->getType()->getAs(); + auto InputFA = InputPtrTy->getPointerAttributes(); + auto InputIsUnsafe = InputFA.isUnsafeOrUnspecified() && !Result.IsNullPtr; + if (InputIsUnsafe && !ResultFA.isUnsafeOrUnspecified()) + return false; + + // "to unsafe" and "to bidi indexable" require no other checks. + if (ResultFA.isUnsafeOrUnspecified() || ResultFA.isBidiIndexable()) + return true; + + // "to indexable" requires that the pointer value is not less than the lower + // bound. Since a pointer's value cannot be reduced, a pointer above its + // upper bound is useless. This gives us a good reason to reject it, which + // makes the test easy: if the sub-object is invalid, prevent constant + // evaluation. + if (ResultFA.isIndexable()) { + return Result.IsNullPtr || SubObj.isValidSubobject(); + } + + if (E->getType()->isDynamicRangePointerType()) { + // rdar://81135826 + return false; + } + + // Otherwise, this is a __counted_by pointer or a plain __single pointer. + // A __single pointer tolerates NULL as a sentinel value; otherwise, it's + // the same as a __counted_by pointer with a length of 1. + APSInt ItemCount; + if (auto DCP = E->getType()->getAs()) { + if (DCP->isOrNull() && Result.IsNullPtr) + return true; + if (!EvaluateInteger(DCP->getCountExpr(), ItemCount, Info)) { + return false; + } + } else { + // Plain __single pointer. NULL always works. + if (Result.IsNullPtr) + return true; + + QualType PointeeTy = ResultPtrTy->getPointeeType(); + + if (PointeeTy->isIncompleteType()) + return true; + + // To zero-sized type also always works. + auto ObjSize = Info.Ctx.getTypeSizeInChars(PointeeTy); + if (ObjSize.getQuantity() == 0) + return true; + + ItemCount = APInt(64, 1); + } + + // ensure that there is room for at least that many elements + auto MaxAdjustment = SubObj.validIndexAdjustments().second; + return ItemCount <= MaxAdjustment; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + case CK_ArrayToPointerDecay: { if (SubExpr->isGLValue()) { if (!evaluateLValue(SubExpr, Result)) @@ -9769,9 +10177,15 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) { } // The result is a pointer to the first element of the array. auto *AT = Info.Ctx.getAsArrayType(SubExpr->getType()); - if (auto *CAT = dyn_cast(AT)) + if (auto *CAT = dyn_cast(AT)) { Result.addArray(Info, E, CAT); - else + } else if (uint64_t IATEltCount = evaluateIncompleteArraySize(Info.Ctx, SubExpr->getType())) { + llvm::APInt EltCount(64, IATEltCount); + QualType CAT = + Info.Ctx.getConstantArrayType(AT->getElementType(), EltCount, nullptr, + ArraySizeModifier::Normal, 0); + Result.addArray(Info, E, cast(CAT)); + } else Result.addUnsizedArray(Info, E, AT->getElementType()); return true; } @@ -12081,6 +12495,30 @@ class IntExprEvaluator return Success(E->getValue(), E); } + bool VisitObjCAvailabilityCheckExpr(const ObjCAvailabilityCheckExpr *E) { + if (!E->hasDomainName()) + return false; + auto FeatureName = E->getDomainName(); + auto FeatureInfo = Info.Ctx.getFeatureAvailInfo(FeatureName); + unsigned ResultInt; + + switch (FeatureInfo.Kind) { + case FeatureAvailKind::Available: + ResultInt = 1; + break; + case FeatureAvailKind::Unavailable: + ResultInt = 0; + break; + case FeatureAvailKind::Dynamic: + return false; + case FeatureAvailKind::None: + llvm_unreachable("unexpected feature kind"); + } + + return Success(APSInt(APInt(Info.Ctx.getIntWidth(E->getType()), ResultInt)), + E); + } + bool CheckReferencedDecl(const Expr *E, const Decl *D); bool VisitDeclRefExpr(const DeclRefExpr *E) { if (CheckReferencedDecl(E, E->getDecl())) @@ -12561,6 +12999,8 @@ static QualType getObjectType(APValue::LValueBase B) { return B.getTypeInfoType(); } else if (B.is()) { return B.getDynamicAllocType(); + } else if (B.is()) { + return B.getForgedPtrAsArrayType(); } return QualType(); @@ -15134,6 +15574,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_IntegralToFixedPoint: case CK_MatrixCast: case CK_HLSLAggregateSplatCast: + case CK_BoundsSafetyPointerCast: llvm_unreachable("invalid cast kind for integral value"); case CK_BitCast: @@ -15274,7 +15715,13 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { // FIXME: Allow a larger integer size than the pointer size, and allow // narrowing back down to pointer width in subsequent integral casts. // FIXME: Check integer type's active bits, not its type size. - if (Info.Ctx.getTypeSize(DestType) != Info.Ctx.getTypeSize(SrcType)) + if (Info.Ctx.getTypeSize(DestType) != Info.Ctx.getTypeSize(SrcType) + /* TO_UPSTREAM(BoundsSafety) ON */ + && (!SrcType->isPointerTypeWithBounds() || + Info.Ctx.getTypeSize(DestType) != + Info.Ctx.getTypeSize(Info.Ctx.getIntPtrType())) + /* TO_UPSTREAM(BoundsSafety) OFF */ + ) return Error(E); LV.Designator.setInvalid(); @@ -16001,6 +16448,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_FixedPointToIntegral: case CK_IntegralToFixedPoint: case CK_MatrixCast: + case CK_BoundsSafetyPointerCast: case CK_HLSLVectorTruncation: case CK_HLSLElementwiseCast: case CK_HLSLAggregateSplatCast: @@ -17346,11 +17794,22 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::CoawaitExprClass: case Expr::DependentCoawaitExprClass: case Expr::CoyieldExprClass: + case Expr::BoundsSafetyPointerPromotionExprClass: + case Expr::ForgePtrExprClass: + case Expr::GetBoundExprClass: + case Expr::PredefinedBoundsCheckExprClass: + case Expr::BoundsCheckExprClass: + case Expr::MaterializeSequenceExprClass: + case Expr::TerminatedByToIndexableExprClass: + case Expr::TerminatedByFromIndexableExprClass: case Expr::SYCLUniqueStableNameExprClass: case Expr::CXXParenListInitExprClass: case Expr::HLSLOutArgExprClass: return ICEDiag(IK_NotICE, E->getBeginLoc()); + case Expr::AssumptionExprClass: + return CheckICE(cast(E)->getWrappedExpr(), Ctx); + case Expr::InitListExprClass: { // C++03 [dcl.init]p13: If T is a scalar type, then a declaration of the // form "T x = { a };" is equivalent to "T x = a;". @@ -18085,6 +18544,107 @@ bool Expr::tryEvaluateStrLen(uint64_t &Result, ASTContext &Ctx) const { return EvaluateBuiltinStrLen(this, Result, Info); } +static bool +EvaluateTerminatorElementFromCondOpIgnoringCond(const Expr *E, APValue &Result, + const ASTContext &Ctx) { + const auto *CondOp = dyn_cast(E); + if (!CondOp) + return false; + + const auto *TrueE = CondOp->getTrueExpr(); + const auto *FalseE = CondOp->getFalseExpr(); + + Expr::EvalResult TrueRes; + if (!TrueE->tryEvaluateTerminatorElement(TrueRes, Ctx) || + !TrueRes.Val.isInt()) + return false; + + Expr::EvalResult FalseRes; + if (!FalseE->tryEvaluateTerminatorElement(FalseRes, Ctx) || + !FalseRes.Val.isInt()) + return false; + + if (!llvm::APSInt::isSameValue(TrueRes.Val.getInt(), FalseRes.Val.getInt())) + return false; + + Result = TrueRes.Val; + return true; +} + +static bool EvaluateTerminatorElement(const Expr *E, APValue &Result, + EvalInfo &Info) { + if (!E->getType()->hasPointerRepresentation()) + return false; + + QualType PointeeTy = E->getType()->getPointeeType(); + for (;;) { + E = E->IgnoreParens(); + const auto *ICE = dyn_cast(E); + if (!ICE) + break; + CastKind K = ICE->getCastKind(); + if (!(K == CK_BitCast || K == CK_BoundsSafetyPointerCast)) + break; + const auto *SubE = ICE->getSubExpr(); + if (!Info.Ctx.hasSameType(SubE->getType()->getPointeeType(), PointeeTy)) + break; + E = SubE; + } + + if (const auto *VTT = E->getType()->getAs()) { + Result = APValue(VTT->getTerminatorValue(Info.Ctx)); + return true; + } + + if (EvaluateTerminatorElementFromCondOpIgnoringCond(E, Result, Info.Ctx)) + return true; + + LValue Array; + if (!EvaluatePointer(E, Array, Info)) + return false; + + QualType EltTy = E->getType()->getPointeeType(); + CharUnits EltSz; + if (!HandleSizeof(Info, E->getExprLoc(), EltTy, EltSz)) + return false; + + size_t MaxIndex = Array.Designator.validIndexAdjustments().second; + if (MaxIndex == 0) + return false; + + size_t LastIndex = MaxIndex - 1; + if (!HandleLValueArrayAdjustment(Info, E, Array, EltTy, LastIndex)) + return false; + + return handleLValueToRValueConversion(Info, E, EltTy, Array, Result); +} + +bool Expr::tryEvaluateTerminatorElement(EvalResult &Result, + const ASTContext &Ctx) const { + Expr::EvalStatus Status; + EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold); + return EvaluateTerminatorElement(this, Result.Val, Info); +} + +bool Expr::EvaluateAsTerminatorValue( + llvm::APSInt &Result, const ASTContext &Ctx, + Expr::SideEffectsKind AllowSideEffects) const { + assert(!isValueDependent() && + "Expression evaluator can't be called on a dependent expression."); + + QualType Ty = getType(); + if (!Ty->isIntegralOrEnumerationType() && !Ty->isPointerType()) + return false; + + EvalResult ExprResult; + if (!EvaluateAsRValue(ExprResult, Ctx) || + hasUnacceptableSideEffect(ExprResult, AllowSideEffects)) { + return false; + } + + return ExprResult.Val.toIntegralConstant(Result, Ty, Ctx); +} + namespace { struct IsWithinLifetimeHandler { EvalInfo &Info; diff --git a/clang/lib/AST/ExprObjC.cpp b/clang/lib/AST/ExprObjC.cpp index 79b5db301d414..7cc5d0455bc6c 100644 --- a/clang/lib/AST/ExprObjC.cpp +++ b/clang/lib/AST/ExprObjC.cpp @@ -20,6 +20,7 @@ #include #include #include +#include using namespace clang; @@ -348,3 +349,26 @@ StringRef ObjCBridgedCastExpr::getBridgeKindName() const { llvm_unreachable("Invalid BridgeKind!"); } + +ObjCAvailabilityCheckExpr *ObjCAvailabilityCheckExpr::CreateEmpty( + const ASTContext &C, Stmt::EmptyShell Empty, size_t FeaturesLen) { + ObjCAvailabilityCheckExpr *E = (ObjCAvailabilityCheckExpr *)C.Allocate( + totalSizeToAlloc(FeaturesLen + 1), + alignof(ObjCAvailabilityCheckExpr)); + new (E) ObjCAvailabilityCheckExpr(Empty); + memset(E->getTrailingObjects(), 0, FeaturesLen + 1); + return E; +} + +ObjCAvailabilityCheckExpr * +ObjCAvailabilityCheckExpr::CreateAvailabilityFeatureCheck(SourceLocation AtLoc, + SourceLocation RParen, + QualType Ty, + StringRef DomainName, + const ASTContext &C) { + ObjCAvailabilityCheckExpr *E = (ObjCAvailabilityCheckExpr *)C.Allocate( + totalSizeToAlloc(DomainName.size() + 1), + alignof(ObjCAvailabilityCheckExpr)); + new (E) ObjCAvailabilityCheckExpr(AtLoc, RParen, Ty, DomainName); + return E; +} diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 33a8728728574..62db7138ac5dd 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2474,6 +2474,10 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, case Type::BitInt: case Type::DependentBitInt: case Type::CountAttributed: + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: + case Type::ValueTerminated: + /* TO_UPSTREAM(BoundsSafety) OFF */ llvm_unreachable("type is illegal as a nested name specifier"); case Type::SubstTemplateTypeParmPack: @@ -3928,6 +3932,7 @@ void CXXNameMangler::mangleType(const SubstTemplateTypeParmPackType *T) { // ::= P # pointer-to void CXXNameMangler::mangleType(const PointerType *T) { + // FIXME: add a mangling rule for BoundsSafetyPointerKind. Out << 'P'; mangleType(T->getPointeeType()); } @@ -5002,7 +5007,16 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, case Expr::AtomicExprClass: case Expr::SourceLocExprClass: case Expr::EmbedExprClass: - case Expr::BuiltinBitCastExprClass: { + case Expr::BuiltinBitCastExprClass: + case Expr::BoundsSafetyPointerPromotionExprClass: + case Expr::AssumptionExprClass: + case Expr::ForgePtrExprClass: + case Expr::GetBoundExprClass: + case Expr::PredefinedBoundsCheckExprClass: + case Expr::BoundsCheckExprClass: + case Expr::MaterializeSequenceExprClass: + case Expr::TerminatedByToIndexableExprClass: + case Expr::TerminatedByFromIndexableExprClass: { NotPrimaryExpr(); if (!NullOut) { // As bad as this diagnostic is, it's better than crashing. diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index add737b762ccc..a8d5cc9405440 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -3381,6 +3381,7 @@ void MicrosoftCXXNameMangler::mangleType(const SubstTemplateTypeParmPackType *T, // # the E is required for 64-bit non-static pointers void MicrosoftCXXNameMangler::mangleType(const PointerType *T, Qualifiers Quals, SourceRange Range) { + // FIXME: implement a mangling for BoundsSafetyPointerKind QualType PointeeType = T->getPointeeType(); manglePointerCVQualifiers(Quals); manglePointerExtQualifiers(Quals, PointeeType); diff --git a/clang/lib/AST/NestedNameSpecifier.cpp b/clang/lib/AST/NestedNameSpecifier.cpp index bd0fe36d781da..963dd7e4a9196 100644 --- a/clang/lib/AST/NestedNameSpecifier.cpp +++ b/clang/lib/AST/NestedNameSpecifier.cpp @@ -699,3 +699,34 @@ NestedNameSpecifierLocBuilder::getWithLocInContext(ASTContext &Context) const { memcpy(Mem, Buffer, BufferSize); return NestedNameSpecifierLoc(Representation, Mem); } + +NestedNameSpecifier *NestedNameSpecifier::getRequiredQualification( + ASTContext &Context, const DeclContext *CurContext, + const DeclContext *TargetContext) { + SmallVector TargetParents; + + for (const DeclContext *CommonAncestor = TargetContext; + CommonAncestor && !CommonAncestor->Encloses(CurContext); + CommonAncestor = CommonAncestor->getLookupParent()) { + if (CommonAncestor->isTransparentContext() || + CommonAncestor->isFunctionOrMethod()) + continue; + + TargetParents.push_back(CommonAncestor); + } + + NestedNameSpecifier *Result = nullptr; + while (!TargetParents.empty()) { + const DeclContext *Parent = TargetParents.pop_back_val(); + + if (const NamespaceDecl *Namespace = dyn_cast(Parent)) { + if (!Namespace->getIdentifier()) + continue; + + Result = NestedNameSpecifier::Create(Context, Result, Namespace); + } else if (const TagDecl *TD = dyn_cast(Parent)) + Result = NestedNameSpecifier::Create( + Context, Result, Context.getTypeDeclType(TD).getTypePtr()); + } + return Result; +} diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp index aacc079f2521d..580ee34998553 100644 --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -1852,6 +1852,9 @@ void ItaniumRecordLayoutBuilder::LayoutBitField(const FieldDecl *D) { void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D, bool InsertExtraPadding) { + if (Context.hasUnavailableFeature(D)) + return; + auto *FieldClass = D->getType()->getAsCXXRecordDecl(); bool IsOverlappingEmptyField = D->isPotentiallyOverlapping() && FieldClass->isEmpty(); @@ -3552,6 +3555,8 @@ uint64_t ASTContext::lookupFieldBitOffset(const ObjCInterfaceDecl *OID, for (const ObjCIvarDecl *IVD = Container->all_declared_ivar_begin(); IVD; IVD = IVD->getNextIvar()) { + if (hasUnavailableFeature(IVD)) + continue; if (Ivar == IVD) break; ++Index; diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index be4ba6878bf0c..e4f991401f850 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -1049,6 +1049,13 @@ bool IfStmt::isObjCAvailabilityCheck() const { return isa(getCond()); } +bool IfStmt::isObjCAvailabilityCheckWithDomainName() const { + if (auto *ACE = dyn_cast(getCond()); + ACE && ACE->hasDomainName()) + return true; + return false; +} + std::optional IfStmt::getNondiscardedCase(const ASTContext &Ctx) { if (!isConstexpr() || getCond()->isValueDependent()) return std::nullopt; diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index c6c49c6c1ba4d..252b7544428c9 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2933,6 +2933,84 @@ void StmtPrinter::VisitAsTypeExpr(AsTypeExpr *Node) { OS << ")"; } +void StmtPrinter::VisitGetBoundExpr(GetBoundExpr *Node) { + OS << "__builtin_get_pointer_"; + OS << (Node->getBoundKind() == GetBoundExpr::BK_Lower ? "lower" : "upper"); + OS << "_bound("; + PrintExpr(Node->getSubExpr()); + OS << ")"; +} + +void StmtPrinter::VisitBoundsSafetyPointerPromotionExpr( + BoundsSafetyPointerPromotionExpr *Node) { + PrintExpr(Node->getPointer()); +} + +void StmtPrinter::VisitAssumptionExpr(AssumptionExpr *E) { + PrintExpr(E->getWrappedExpr()); +} + +void StmtPrinter::VisitForgePtrExpr(ForgePtrExpr *Node) { + if (Node->ForgesBidiIndexablePointer()) { + OS << "__builtin_unsafe_forge_bidi_indexable("; + PrintExpr(Node->getAddr()); + OS << ", "; + PrintExpr(Node->getSize()); + OS << ")"; + } else if (Node->ForgesTerminatedByPointer()) { + OS << "__builtin_unsafe_forge_terminated_by("; + PrintExpr(Node->getAddr()); + OS << ", "; + PrintExpr(Node->getTerminator()); + OS << ")"; + } else { + OS << "__builtin_unsafe_forge_single("; + PrintExpr(Node->getAddr()); + OS << ")"; + } +} + +void StmtPrinter::VisitBoundsCheckExpr(BoundsCheckExpr *Node) { + PrintExpr(Node->getGuardedExpr()); +} + +void StmtPrinter::VisitPredefinedBoundsCheckExpr( + PredefinedBoundsCheckExpr *Node) { + PrintExpr(Node->getGuardedExpr()); +} + +void StmtPrinter::VisitMaterializeSequenceExpr(MaterializeSequenceExpr *Node) { + PrintExpr(Node->getWrappedExpr()); +} + +void StmtPrinter::VisitTerminatedByToIndexableExpr( + TerminatedByToIndexableExpr *Node) { + const char *Name = Node->includesTerminator() + ? "__builtin_unsafe_terminated_by_to_indexable" + : "__builtin_terminated_by_to_indexable"; + OS << Name << '('; + PrintExpr(Node->getPointer()); + if (auto *Terminator = Node->getTerminator()) { + OS << ", "; + PrintExpr(Terminator); + } + OS << ")"; +} + +void StmtPrinter::VisitTerminatedByFromIndexableExpr( + TerminatedByFromIndexableExpr *Node) { + const auto *VTT = cast(Node->getType()); + OS << "__builtin_unsafe_terminated_by_from_indexable("; + PrintExpr(VTT->getTerminatorExpr()); + OS << ", "; + PrintExpr(Node->getPointer()); + if (auto *PointerToTerminator = Node->getPointerToTerminator()) { + OS << ", "; + PrintExpr(PointerToTerminator); + } + OS << ")"; +} + void StmtPrinter::VisitHLSLOutArgExpr(HLSLOutArgExpr *Node) { PrintExpr(Node->getArgLValue()); } diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 83d54da9be7e5..28f60d60ff5c3 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -2512,6 +2512,51 @@ void StmtProfiler::VisitTemplateArgument(const TemplateArgument &Arg) { } } +/* TO_UPSTREAM(BoundsSafety) ON */ +void StmtProfiler::VisitBoundsSafetyPointerPromotionExpr( + const BoundsSafetyPointerPromotionExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitAssumptionExpr(const AssumptionExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitForgePtrExpr(const ForgePtrExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitGetBoundExpr(const GetBoundExpr *S) { + VisitExpr(S); + ID.AddInteger(S->getBoundKind()); +} + +void StmtProfiler::VisitBoundsCheckExpr(const BoundsCheckExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitPredefinedBoundsCheckExpr( + const PredefinedBoundsCheckExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitMaterializeSequenceExpr(const MaterializeSequenceExpr *S) { + VisitExpr(S); + ID.AddBoolean(S->isUnbinding()); +} + +void StmtProfiler::VisitTerminatedByToIndexableExpr( + const TerminatedByToIndexableExpr *S) { + VisitExpr(S); + ID.AddBoolean(S->includesTerminator()); +} + +void StmtProfiler::VisitTerminatedByFromIndexableExpr( + const TerminatedByFromIndexableExpr *S) { + VisitExpr(S); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + namespace { class OpenACCClauseProfiler : public OpenACCClauseVisitor { diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 51f7d17be4158..e112023a31c81 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1927,6 +1927,26 @@ void TextNodeDumper::VisitOMPIteratorExpr(const OMPIteratorExpr *Node) { } } +void TextNodeDumper::VisitGetBoundExpr(const GetBoundExpr *Expr) { + OS << (Expr->getBoundKind() == GetBoundExpr::BK_Lower ? " lower" : " upper"); +} + +void TextNodeDumper::VisitMaterializeSequenceExpr( + const MaterializeSequenceExpr *Node) { + OS << (Node->isUnbinding() ? " " : " "); +} + +void TextNodeDumper::VisitPredefinedBoundsCheckExpr( + const PredefinedBoundsCheckExpr *Node) { + OS << " <" << Node->getKindName() << ">"; +} + +void TextNodeDumper::VisitBoundsCheckExpr(const BoundsCheckExpr *Node) { + OS << " '"; + Node->getCond()->printPretty(OS, nullptr, PrintPolicy, 0, "\n", Context); + OS << "'"; +} + void TextNodeDumper::VisitConceptSpecializationExpr( const ConceptSpecializationExpr *Node) { OS << " "; @@ -2957,6 +2977,8 @@ void TextNodeDumper::VisitObjCCompatibleAliasDecl( const ObjCCompatibleAliasDecl *D) { dumpName(D); dumpDeclRef(D->getClassInterface()); + OS << " "; + dumpLocation(D->getClassInterfaceLoc()); } void TextNodeDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index d298f1cff73cf..32dbe2ef6c146 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -464,6 +464,41 @@ void CountAttributedType::Profile(llvm::FoldingSetNodeID &ID, ID.AddPointer(CountExpr); } +/* TO_UPSTREAM(BoundsSafety) ON */ +void DynamicRangePointerType::Profile(llvm::FoldingSetNodeID &ID, + QualType PointerTy, + Expr *StartPtr, + Expr *EndPtr, + unsigned NumStartDecls, + unsigned NumEndDecls) { + ID.AddPointer(PointerTy.getAsOpaquePtr()); + // We profile it as a pointer as the StmtProfiler considers parameter + // expressions on function declaration and function definition as the + // same, resulting in count expression being evaluated with ParamDecl + // not in the function scope. + ID.AddPointer(StartPtr); + ID.AddPointer(EndPtr); + ID.AddInteger(NumStartDecls); + ID.AddInteger(NumEndDecls); +} + +llvm::APSInt +ValueTerminatedType::getTerminatorValue(const ASTContext &Ctx) const { + llvm::APSInt Result; + bool Ok = TerminatorExpr->EvaluateAsTerminatorValue(Result, Ctx); + (void)Ok; + assert(Ok); + return Result; +} + +void ValueTerminatedType::Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &Ctx, QualType OriginalTy, + const Expr *TerminatorExpr) { + ID.AddPointer(OriginalTy.getAsOpaquePtr()); + TerminatorExpr->Profile(ID, Ctx, true); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// getArrayElementTypeNoTypeQual - If this is an array type, return the /// element type of the array, potentially with type qualifiers missing. /// This method should never be used when type qualifiers are meaningful. @@ -642,6 +677,16 @@ template <> const CountAttributedType *Type::getAs() const { return getAsSugar(this); } +/* TO_UPSTREAM(BoundsSafety) ON */ +template <> const DynamicRangePointerType *Type::getAs() const { + return getAsSugar(this); +} + +template <> const ValueTerminatedType *Type::getAs() const { + return getAsSugar(this); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// getUnqualifiedDesugaredType - Pull any qualifiers and syntactic /// sugar off the given type. This should produce an object of the /// same dynamic type as the canonical type. @@ -738,6 +783,66 @@ bool Type::isCountAttributedType() const { return getAs(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +bool Type::isAnyVaListType(ASTContext &Ctx) const { + bool MaybeMS = (Ctx.getTargetInfo().hasBuiltinMSVaList() && + Ctx.getTargetInfo().getBuiltinVaListKind() != TargetInfo::CharPtrBuiltinVaList); + + const Type *VaListType = Ctx.getBuiltinVaListType().getTypePtr(); + const Type *MSVaListType = MaybeMS ? Ctx.getBuiltinMSVaListType().getTypePtr() : nullptr; + + auto isVaListTypeSingle = [&](const Type *T) -> bool { + return T == VaListType || (MaybeMS && T == MSVaListType); + }; + + const Type *Cur = this; + if (const auto *AT = dyn_cast(this)) { + Cur = AT->getOriginalType().getTypePtr(); + } + if (!Ctx.hasSameType(VaListType, Cur) && + MaybeMS && !Ctx.hasSameType(MSVaListType, Cur)) + return false; + + while (true) { + if (isVaListTypeSingle(Cur)) + return true; + switch (Cur->getTypeClass()) { +#define ABSTRACT_TYPE(Class, Parent) +#define TYPE(Class, Parent) \ + case Type::Class: { \ + const auto *Ty = cast(Cur); \ + if (!Ty->isSugared()) \ + return 0; \ + Cur = Ty->desugar().getTypePtr(); \ + break; \ + } +#include "clang/AST/TypeNodes.inc" + } + } + return false; +} + +bool Type::isDynamicRangePointerType() const { + return getAs(); +} + +bool Type::isBoundsAttributedType() const { + return getAs(); +} + +bool Type::isValueTerminatedType() const { + return getAs(); +} + +bool Type::isImplicitlyNullTerminatedType(const ASTContext &Ctx) const { + const auto *VT = getAs(); + if (!VT || !VT->getTerminatorValue(Ctx).isZero()) + return false; + return hasAttr(attr::PtrAutoNullTerminatedAttr); +} + +/* TO_UPSTREAM(BoundsSafety) OFF */ + const ComplexType *Type::getAsComplexIntegerType() const { if (const auto *Complex = getAs()) if (Complex->getElementType()->isIntegerType()) @@ -1393,54 +1498,49 @@ struct SubstObjCTypeArgsVisitor ObjCSubstitutionContext context) : BaseType(ctx), TypeArgs(typeArgs), SubstContext(context) {} - QualType VisitObjCTypeParamType(const ObjCTypeParamType *OTPTy) { + QualType VisitTypedefType(const TypedefType *typedefTy) { // Replace an Objective-C type parameter reference with the corresponding // type argument. - ObjCTypeParamDecl *typeParam = OTPTy->getDecl(); - // If we have type arguments, use them. - if (!TypeArgs.empty()) { - QualType argType = TypeArgs[typeParam->getIndex()]; - if (OTPTy->qual_empty()) + if (auto *typeParam = dyn_cast(typedefTy->getDecl())) { + // If we have type arguments, use them. + if (!TypeArgs.empty()) { + // FIXME: Introduce SubstObjCTypeParamType ? + QualType argType = TypeArgs[typeParam->getIndex()]; return argType; + } - // Apply protocol lists if exists. - bool hasError; - SmallVector protocolsVec; - protocolsVec.append(OTPTy->qual_begin(), OTPTy->qual_end()); - ArrayRef protocolsToApply = protocolsVec; - return Ctx.applyObjCProtocolQualifiers( - argType, protocolsToApply, hasError, true/*allowOnPointerType*/); - } - - switch (SubstContext) { - case ObjCSubstitutionContext::Ordinary: - case ObjCSubstitutionContext::Parameter: - case ObjCSubstitutionContext::Superclass: - // Substitute the bound. - return typeParam->getUnderlyingType(); - - case ObjCSubstitutionContext::Result: - case ObjCSubstitutionContext::Property: { - // Substitute the __kindof form of the underlying type. - const auto *objPtr = - typeParam->getUnderlyingType()->castAs(); - - // __kindof types, id, and Class don't need an additional - // __kindof. - if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType()) + switch (SubstContext) { + case ObjCSubstitutionContext::Ordinary: + case ObjCSubstitutionContext::Parameter: + case ObjCSubstitutionContext::Superclass: + // Substitute the bound. return typeParam->getUnderlyingType(); - // Add __kindof. - const auto *obj = objPtr->getObjectType(); - QualType resultTy = Ctx.getObjCObjectType( - obj->getBaseType(), obj->getTypeArgsAsWritten(), obj->getProtocols(), - /*isKindOf=*/true); - - // Rebuild object pointer type. - return Ctx.getObjCObjectPointerType(resultTy); - } + case ObjCSubstitutionContext::Result: + case ObjCSubstitutionContext::Property: { + // Substitute the __kindof form of the underlying type. + const auto *objPtr = + typeParam->getUnderlyingType()->castAs(); + + // __kindof types, id, and Class don't need an additional + // __kindof. + if (objPtr->isKindOfType() || objPtr->isObjCIdOrClassType()) + return typeParam->getUnderlyingType(); + + // Add __kindof. + const auto *obj = objPtr->getObjectType(); + QualType resultTy = Ctx.getObjCObjectType(obj->getBaseType(), + obj->getTypeArgsAsWritten(), + obj->getProtocols(), + /*isKindOf=*/true); + + // Rebuild object pointer type. + return Ctx.getObjCObjectPointerType(resultTy); + } + } + llvm_unreachable("Unexpected ObjCSubstitutionContext!"); } - llvm_unreachable("Unexpected ObjCSubstitutionContext!"); + return BaseType::VisitTypedefType(typedefTy); } QualType VisitFunctionType(const FunctionType *funcType) { @@ -2620,6 +2720,21 @@ bool Type::isSveVLSBuiltinType() const { return false; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +bool Type::isSizeMeaningless() const { + if (isIncompleteType()) + return true; + if (isSizelessType()) + return true; + if (isFunctionType()) + return true; + if (auto *RT = getAs()) + if (RT->getDecl()->hasFlexibleArrayMember()) + return true; + return false; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + QualType Type::getSizelessVectorEltType(const ASTContext &Ctx) const { assert(isSizelessVectorType() && "Must be sizeless vector type"); // Currently supports SVE and RVV @@ -3925,12 +4040,19 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, getExtProtoInfo(), Ctx, isCanonicalUnqualified()); } -TypeCoupledDeclRefInfo::TypeCoupledDeclRefInfo(ValueDecl *D, bool Deref) - : Data(D, Deref << DerefShift) {} +TypeCoupledDeclRefInfo::TypeCoupledDeclRefInfo(ValueDecl *D, bool Deref, + // TO_UPSTREAM(BoundsSafety) + bool Member) + : Data(D, (Deref << DerefShift) | (Member << MemberShift)) {} +/* TO_UPSTREAM(BoundsSafety) ON*/ bool TypeCoupledDeclRefInfo::isDeref() const { - return Data.getInt() & DerefMask; + return (Data.getInt() >> DerefShift) & DerefMask; } +bool TypeCoupledDeclRefInfo::isMember() const { + return (Data.getInt() >> MemberShift) & MemberMask; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ ValueDecl *TypeCoupledDeclRefInfo::getDecl() const { return Data.getPointer(); } unsigned TypeCoupledDeclRefInfo::getInt() const { return Data.getInt(); } void *TypeCoupledDeclRefInfo::getOpaqueValue() const { @@ -3987,6 +4109,27 @@ StringRef CountAttributedType::getAttributeName(bool WithMacroPrefix) const { #undef ENUMERATE_ATTRS } +/* TO_UPSTREAM(BoundsSafety) ON*/ +DynamicRangePointerType::DynamicRangePointerType( + QualType PointerTy, QualType CanPointerTy, Expr *StartPtr, Expr *EndPtr, + ArrayRef StartPtrDecls, + ArrayRef EndPtrDecls) + : BoundsAttributedType(DynamicRangePointer, PointerTy, CanPointerTy), + StartPtr(StartPtr), EndPtr(EndPtr) { + assert(EndPtrDecls.size() < (1 << 16)); + assert(StartPtrDecls.size() < (1 << 16)); + DynamicRangePointerTypeBits.NumEndPtrDecls = EndPtrDecls.size(); + DynamicRangePointerTypeBits.NumStartPtrDecls = StartPtrDecls.size(); + auto *DeclSlot = getTrailingObjects(); + Decls = llvm::ArrayRef(DeclSlot, + EndPtrDecls.size() + StartPtrDecls.size()); + for (unsigned i = 0; i != StartPtrDecls.size(); ++i) + *(DeclSlot++) = StartPtrDecls[i]; + for (unsigned i = 0; i != EndPtrDecls.size(); ++i) + *(DeclSlot++) = EndPtrDecls[i]; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + TypedefType::TypedefType(TypeClass tc, const TypedefNameDecl *D, QualType Underlying, QualType can) : Type(tc, can, toSemanticDependence(can->getDependence())), diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index 3d1b5ca966b66..02e726af10867 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -525,6 +525,12 @@ SourceRange CountAttributedTypeLoc::getLocalSourceRange() const { return getCountExpr() ? getCountExpr()->getSourceRange() : SourceRange(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +SourceRange DynamicRangePointerTypeLoc::getLocalSourceRange() const { + return getEndPointer() ? getEndPointer()->getSourceRange() : SourceRange(); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + SourceRange BTFTagAttributedTypeLoc::getLocalSourceRange() const { return getAttr() ? getAttr()->getRange() : SourceRange(); } diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index cba1a2d98d660..d5b86b8ec4dda 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -109,11 +109,27 @@ class ElaboratedTypePolicyRAII { } }; +class DelayedArrayQualPolicyRAII { + PrintingPolicy &Policy; + std::string DelayedArrayQual; + +public: + explicit DelayedArrayQualPolicyRAII(PrintingPolicy &Policy, const std::string &Qual) : Policy(Policy) { + DelayedArrayQual = Policy.DelayedArrayQual; + Policy.DelayedArrayQual = Qual; + } + + ~DelayedArrayQualPolicyRAII() { + Policy.DelayedArrayQual = std::move(DelayedArrayQual); + } +}; + class TypePrinter { PrintingPolicy Policy; unsigned Indentation; bool HasEmptyPlaceHolder = false; bool InsideCCAttribute = false; + bool IgnoreFunctionProtoTypeConstQual = false; public: explicit TypePrinter(const PrintingPolicy &Policy, unsigned Indentation = 0) @@ -288,6 +304,10 @@ bool TypePrinter::canPrefixQualifiers(const Type *T, case Type::SubstTemplateTypeParm: case Type::MacroQualified: case Type::CountAttributed: + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: + case Type::ValueTerminated: + /* TO_UPSTREAM(BoundsSafety) OFF */ CanPrefixQualifiers = false; break; @@ -401,15 +421,30 @@ void TypePrinter::printComplexAfter(const ComplexType *T, raw_ostream &OS) { printAfter(T->getElementType(), OS); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +static bool shouldPrintParenForPointer(const PointerType *T) { + QualType PointeeTy = T->getPointeeType(); + if (auto *DCPTy = PointeeTy->getAs()) + PointeeTy = DCPTy->desugar(); + + return isa(PointeeTy); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + void TypePrinter::printPointerBefore(const PointerType *T, raw_ostream &OS) { IncludeStrongLifetimeRAII Strong(Policy); SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); printBefore(T->getPointeeType(), OS); // Handle things like 'int (*A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. - if (isa(T->getPointeeType())) + /* TO_UPSTREAM(BoundsSafety) ON */ + // It checks `isa(T->getPointeeType())` in upstream. + if (shouldPrintParenForPointer(T)) + /* TO_UPSTREAM(BoundsSafety) OFF */ OS << '('; OS << '*'; + + T->getPointerAttributes().print(OS); } void TypePrinter::printPointerAfter(const PointerType *T, raw_ostream &OS) { @@ -417,7 +452,10 @@ void TypePrinter::printPointerAfter(const PointerType *T, raw_ostream &OS) { SaveAndRestore NonEmptyPH(HasEmptyPlaceHolder, false); // Handle things like 'int (*A)[4];' correctly. // FIXME: this should include vectors, but vectors use attributes I guess. - if (isa(T->getPointeeType())) + /* TO_UPSTREAM(BoundsSafety) ON */ + // It checks `isa(T->getPointeeType())` in upstream. + if (shouldPrintParenForPointer(T)) + /* TO_UPSTREAM(BoundsSafety) OFF */ OS << ')'; printAfter(T->getPointeeType(), OS); } @@ -551,7 +589,7 @@ void TypePrinter::printIncompleteArrayBefore(const IncompleteArrayType *T, void TypePrinter::printIncompleteArrayAfter(const IncompleteArrayType *T, raw_ostream &OS) { - OS << "[]"; + OS << '[' << Policy.DelayedArrayQual << ']'; printAfter(T->getElementType(), OS); } @@ -1021,8 +1059,11 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, printFunctionAfter(Info, OS); - if (!T->getMethodQuals().empty()) - OS << " " << T->getMethodQuals().getAsString(); + Qualifiers quals = T->getMethodQuals(); + if (IgnoreFunctionProtoTypeConstQual) + quals.removeConst(); + if (!quals.empty()) + OS << " " << quals.getAsString(); switch (T->getRefQualifier()) { case RQ_None: @@ -1229,6 +1270,14 @@ void TypePrinter::printUsingBefore(const UsingType *T, raw_ostream &OS) { void TypePrinter::printUsingAfter(const UsingType *T, raw_ostream &OS) {} void TypePrinter::printTypedefBefore(const TypedefType *T, raw_ostream &OS) { + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (StringRef MaybeBoundsSafetyName = T->getDecl()->getName(); + MaybeBoundsSafetyName.starts_with("__bounds_safety")) { + this->print(T->desugar(), OS, ""); + return; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + printTypeSpec(T->getDecl(), OS); } @@ -1497,6 +1546,13 @@ void TypePrinter::printTag(TagDecl *D, raw_ostream &OS) { else if (TypedefNameDecl *Typedef = D->getTypedefNameForAnonDecl()) { assert(Typedef->getIdentifier() && "Typedef without identifier?"); OS << Typedef->getIdentifier()->getName(); + } else if (Policy.UseStdFunctionForLambda && isa(D) && + cast(D)->isLambda()) { + OS << "std::function<"; + QualType T = cast(D)->getLambdaCallOperator()->getType(); + SaveAndRestore NoConst(IgnoreFunctionProtoTypeConstQual, true); + print(T, OS, ""); + OS << '>'; } else { // Make an unambiguous representation for anonymous types, e.g. // (anonymous enum at /usr/include/string.h:120:9) @@ -1818,7 +1874,13 @@ void TypePrinter::printPackExpansionAfter(const PackExpansionType *T, static void printCountAttributedImpl(const CountAttributedType *T, raw_ostream &OS, const PrintingPolicy &Policy) { - OS << ' '; + /* TO_UPSTREAM(BoundsSafety) ON */ + // FIXME: The upstream version doesn't have this conditional check for adding + // a leading space and instead always adds it. + if (!T->isArrayType() || !Policy.CountedByInArrayBracket) + OS << ' '; + /* TO_UPSTREAM(BoundsSafety) OFF */ + if (T->isCountInBytes() && T->isOrNull()) OS << "__sized_by_or_null("; else if (T->isCountInBytes()) @@ -1841,11 +1903,85 @@ void TypePrinter::printCountAttributedBefore(const CountAttributedType *T, void TypePrinter::printCountAttributedAfter(const CountAttributedType *T, raw_ostream &OS) { + /* TO_UPSTREAM(BoundsSafety) ON */ + if (Policy.CountedByInArrayBracket && T->isArrayType()) { + std::string DelayedQual; + llvm::raw_string_ostream DOS(DelayedQual); + printCountAttributedImpl(T, DOS, Policy); + DelayedArrayQualPolicyRAII DP(Policy, DOS.str()); + printAfter(T->desugar(), OS); + return; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + printAfter(T->desugar(), OS); if (T->isArrayType()) printCountAttributedImpl(T, OS, Policy); } +/* TO_UPSTREAM(BoundsSafety) ON */ +void TypePrinter::printDynamicRangePointerBefore( + const DynamicRangePointerType *T, raw_ostream &OS) { + printBefore(T->desugar(), OS); + const Expr *StartPtr = T->getStartPointer(); + const Expr *EndPtr = T->getEndPointer(); + if (EndPtr) { + OS << " __ended_by("; + EndPtr->printPretty(OS, nullptr, Policy); + OS << ')'; + } + + if (StartPtr) { + OS << " /* __started_by("; + StartPtr->printPretty(OS, nullptr, Policy); + OS << ") */ "; + } +} + +void TypePrinter::printDynamicRangePointerAfter( + const DynamicRangePointerType *T, raw_ostream &OS) { + printAfter(T->desugar(), OS); +} + +static void printTerminatedByAttr(const ValueTerminatedType *T, raw_ostream &OS, + const PrintingPolicy &Policy) { + OS << "__terminated_by("; + T->getTerminatorExpr()->printPretty(OS, nullptr, Policy); + OS << ')'; +} + +void TypePrinter::printValueTerminatedBefore(const ValueTerminatedType *T, + raw_ostream &OS) { + QualType DesugaredTy = T->desugar(); + assert(DesugaredTy->isArrayType() || DesugaredTy->isPointerType()); + printBefore(DesugaredTy, OS); + if (DesugaredTy->isPointerType()) { + OS << ' '; + printTerminatedByAttr(T, OS, Policy); + } +} + +void TypePrinter::printValueTerminatedAfter(const ValueTerminatedType *T, + raw_ostream &OS) { + QualType DesugaredTy = T->desugar(); + if (DesugaredTy->isPointerType()) { + printAfter(DesugaredTy, OS); + } else { + assert(DesugaredTy->isArrayType()); + std::string Str; + llvm::raw_string_ostream SS(Str); + printAfter(DesugaredTy, SS); + SS.flush(); + assert(Str.size() >= 2 && Str[0] == '['); + OS << '['; + printTerminatedByAttr(T, OS, Policy); + if (Str[1] != ']') + OS << ' '; + OS << Str.c_str() + 1; + } +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void TypePrinter::printAttributedBefore(const AttributedType *T, raw_ostream &OS) { // FIXME: Generate this with TableGen. @@ -1937,6 +2073,23 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, return; } + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Don't print implicit type attribute PtrAutoAttr + if (T->getAttrKind() == attr::PtrAutoAttr) + return; + if (T->getAttrKind() == attr::PtrAutoNullTerminatedAttr) + return; + + if (T->getAttrKind() == attr::PtrSingle) { + OS << "__single"; + return; + } + if (T->getAttrKind() == attr::PtrUnsafeIndexable) { + OS << "__unsafe_indexable"; + return; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // The printing of the address_space attribute is handled by the qualifier // since it is still stored in the qualifier. Return early to prevent printing // this twice. @@ -1998,6 +2151,19 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, // AttributedType nodes for them. break; + /* TO_UPSTREAM(BoundsSafety) ON */ + case attr::ArrayDecayDiscardsCountInParameters: + OS << "decay_discards_count_in_parameters"; + break; + + case attr::PtrBidiIndexable: + case attr::PtrSingle: + case attr::PtrIndexable: + case attr::PtrUnsafeIndexable: + case attr::PtrEndedBy: + case attr::PtrTerminatedBy: + /* TO_UPSTREAM(BoundsSafety) OFF */ + case attr::CountedBy: case attr::CountedByOrNull: case attr::SizedBy: @@ -2008,6 +2174,8 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::TypeNullable: case attr::TypeNullableResult: case attr::TypeNullUnspecified: + case attr::PtrAutoAttr: + case attr::PtrAutoNullTerminatedAttr: case attr::ObjCGC: case attr::ObjCInertUnsafeUnretained: case attr::ObjCKindOf: diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index d03a0a544b016..3b9347e1847ca 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -651,6 +651,15 @@ class CFGBuilder { CFGBlock *VisitUnaryOperator(UnaryOperator *U, AddStmtChoice asc); CFGBlock *VisitWhileStmt(WhileStmt *W); CFGBlock *VisitArrayInitLoopExpr(ArrayInitLoopExpr *A, AddStmtChoice asc); + /* TO_UPSTREAM(BoundsSafety) ON */ + CFGBlock *VisitBoundsCheckExpr(BoundsCheckExpr *BCE, AddStmtChoice asc); + CFGBlock *VisitPredefinedBoundsCheckExpr(PredefinedBoundsCheckExpr *BCE, + AddStmtChoice asc); + CFGBlock *VisitMaterializeSequenceExpr(MaterializeSequenceExpr *MSE, + AddStmtChoice asc); + CFGBlock *VisitBoundsSafetyPointerPromotionExpr(BoundsSafetyPointerPromotionExpr *E, + AddStmtChoice asc); + /* TO_UPSTREAM(BoundsSafety) OFF */ CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd, bool ExternallyDestructed = false); @@ -2397,6 +2406,23 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc, case Stmt::OpaqueValueExprClass: return Block; + /* TO_UPSTREAM(BoundsSafety) ON */ + case Stmt::MaterializeSequenceExprClass: + return VisitMaterializeSequenceExpr(cast(S), + asc); + + case Stmt::BoundsCheckExprClass: + return VisitBoundsCheckExpr(cast(S), asc); + + case Stmt::PredefinedBoundsCheckExprClass: + return VisitPredefinedBoundsCheckExpr(cast(S), + asc); + + case Stmt::BoundsSafetyPointerPromotionExprClass: + return VisitBoundsSafetyPointerPromotionExpr( + cast(S), asc); + /* TO_UPSTREAM(BoundsSafety) OFF */ + case Stmt::PseudoObjectExprClass: return VisitPseudoObjectExpr(cast(S)); @@ -4966,6 +4992,48 @@ CFGBlock *CFGBuilder::VisitImplicitCastExpr(ImplicitCastExpr *E, return Visit(E->getSubExpr(), AddStmtChoice()); } +/* TO_UPSTREAM(BoundsSafety) ON */ +CFGBlock *CFGBuilder::VisitBoundsCheckExpr(BoundsCheckExpr *E, + AddStmtChoice asc) { + CFGBlock *Ret = Visit(E->getGuardedExpr(), asc); + + for (Expr *subExpr : llvm::reverse(E->opaquevalues())) { + auto *ove = cast(subExpr); + Ret = Visit(ove->getSourceExpr(), asc); + } + return Ret; +} + +CFGBlock * +CFGBuilder::VisitPredefinedBoundsCheckExpr(PredefinedBoundsCheckExpr *E, + AddStmtChoice asc) { + return Visit(E->getGuardedExpr(), asc); +} + +CFGBlock *CFGBuilder::VisitMaterializeSequenceExpr(MaterializeSequenceExpr *E, + AddStmtChoice asc) { + CFGBlock *Ret = Visit(E->getWrappedExpr(), asc); + + if (E->isBinding()) { + for (Expr *subExpr : llvm::reverse(E->opaquevalues())) { + auto *ove = cast(subExpr); + Ret = Visit(ove->getSourceExpr(), asc); + } + } + return Ret; +} + +CFGBlock *CFGBuilder::VisitBoundsSafetyPointerPromotionExpr( + BoundsSafetyPointerPromotionExpr *E, + AddStmtChoice asc) { + if (asc.alwaysAdd(*this, E)) { + autoCreateBlock(); + appendStmt(Block, E); + } + return Visit(E->getSubExpr(), asc); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + CFGBlock *CFGBuilder::VisitConstantExpr(ConstantExpr *E, AddStmtChoice asc) { return Visit(E->getSubExpr(), AddStmtChoice()); } @@ -5863,6 +5931,10 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, } else if (const CastExpr *CE = dyn_cast(S)) { OS << " (" << CE->getStmtClassName() << ", " << CE->getCastKindName() << ", " << CE->getType() << ")"; + /* TO_UPSTREAM(BoundsSafety) ON */ + } else if (const auto *PE = dyn_cast(S)) { + OS << " (" << PE->getStmtClassName() << ", " << PE->getType() << ")"; + /* TO_UPSTREAM(BoundsSafety) OFF */ } // Expressions need a newline. diff --git a/clang/lib/Analysis/ReachableCode.cpp b/clang/lib/Analysis/ReachableCode.cpp index dd81c8e0a3d54..60450f57e5d20 100644 --- a/clang/lib/Analysis/ReachableCode.cpp +++ b/clang/lib/Analysis/ReachableCode.cpp @@ -308,6 +308,13 @@ static bool shouldTreatSuccessorsAsReachable(const CFGBlock *B, if (const auto *IS = dyn_cast(Term); IS != nullptr && IS->isConstexpr()) return true; + + // Do not treat successors of `if (@available(domain:))` as unreachable. + if (const auto *IS = dyn_cast(Term)) + if (const auto *AC = dyn_cast( + IS->getCond()->IgnoreParenImpCasts()); + AC && AC->hasDomainName()) + return true; } const Stmt *Cond = B->getTerminatorCondition(/* stripParens */ false); diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp index 5b72382ca9772..9a9b9e4c26234 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -15,7 +15,9 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DynamicRecursiveASTVisitor.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/FormatString.h" +#include "clang/AST/OperationKinds.h" #include "clang/AST/ParentMapContext.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtVisitor.h" @@ -31,6 +33,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" +#include #include #include #include @@ -102,6 +105,10 @@ class FastMatcher { class MatchResult { public: + MatchResult() = default; + + MatchResult(StringRef ID, const DynTypedNode &Node) { Nodes[ID] = Node; } + template const T *getNodeAs(StringRef ID) const { auto It = Nodes.find(ID); if (It == Nodes.end()) { @@ -115,6 +122,9 @@ class MatchResult { private: llvm::StringMap Nodes; }; + +using MatchResults = SmallVector; + } // namespace // A `RecursiveASTVisitor` that traverses all descendants of a given node "n" @@ -250,6 +260,10 @@ static bool hasPointerType(const Expr &E) { return isa(E.getType().getCanonicalType()); } +static bool isSinglePointerType(QualType Ty) { + return Ty->isSinglePointerType(); +} + static bool hasArrayType(const Expr &E) { return isa(E.getType().getCanonicalType()); } @@ -283,13 +297,6 @@ ignoreUnsafeBufferInContainer(const Stmt &Node, return Handler->ignoreUnsafeBufferInContainer(Node.getBeginLoc()); } -static bool ignoreUnsafeLibcCall(const ASTContext &Ctx, const Stmt &Node, - const UnsafeBufferUsageHandler *Handler) { - if (Ctx.getLangOpts().CPlusPlus) - return Handler->ignoreUnsafeBufferInLibcCall(Node.getBeginLoc()); - return true; /* Only warn about libc calls for C++ */ -} - // Finds any expression 'e' such that `OnResult` // matches 'e' and 'e' is in an Unspecified Lvalue Context. static void findStmtsInUnspecifiedLvalueContext( @@ -392,6 +399,705 @@ static void findStmtsInUnspecifiedUntypedContext( // FIXME: Handle loop bodies. } +namespace { + +/* TO_UPSTREAM(BoundsSafetyInterop) ON */ +// Finds the argument that is passed as dependent count. +const Expr *findCountArg(const Expr *Count, const CallExpr *Call) { + const auto *DRE = dyn_cast(Count->IgnoreParenImpCasts()); + if (!DRE) + return nullptr; + + const auto *PVD = dyn_cast(DRE->getDecl()); + if (!PVD) + return nullptr; + + unsigned Index = PVD->getFunctionScopeIndex(); + if (Index >= Call->getNumArgs()) + return nullptr; + + return Call->getArg(Index); +} + +// Mapping: dependent decl -> value. +using DependentValuesTy = llvm::DenseMap; + +// Given the call expr, find the mapping from the dependent parameter to the +// argument that is passed to that parameter. +std::optional +getDependentValuesFromCall(const CountAttributedType *CAT, + const CallExpr *Call) { + DependentValuesTy Values; + for (const auto &DI : CAT->dependent_decls()) { + const auto *PVD = dyn_cast(DI.getDecl()); + if (!PVD) + return std::nullopt; + + unsigned Index = PVD->getFunctionScopeIndex(); + if (Index >= Call->getNumArgs()) + return std::nullopt; + + const Expr *Arg = Call->getArg(Index); + [[maybe_unused]] bool Inserted = Values.insert({PVD, Arg}).second; + assert(Inserted); + } + return {std::move(Values)}; +} + +// Impl of `isCompatibleWithCountExpr`. See `isCompatibleWithCountExpr` for +// high-level document. +// +// This visitor compares two expressions in a tiny subset of the language, +// including DRE of VarDecls, constants, binary operators, subscripts, +// dereferences, member accesses, and member function calls. +// +// - For constants, they are literal constants and expressions have +// compile-time constant values. +// - For a supported dereference expression, it can be either of the forms '*e' +// or '*&e', where 'e' is a supported expression. +// - For a subscript expression, it can be either an array subscript or +// overloaded subscript operator. +// - For a member function call, it must be a call to a 'const' (non-static) +// member function with zero argument. This is to ensure side-effect free. +// Other kinds of function calls are not supported, so an expression of the +// form `f(...)` is not supported. +struct CompatibleCountExprVisitor + : public ConstStmtVisitor { + // The third 'bool' type parameter for each visit method indicates whether the + // current visiting expression is the result of the formal parameter to actual + // argument substitution. Since the argument expression may contain DREs + // referencing to back to those parameters (in cases of recursive calls), the + // analysis may hit an infinite loop if not knowing whether the substitution + // has happened. A typical example that could introduce infinite loop without + // this knowledge is shown below. + // ``` + // void f(int * __counted_by(n) p, size_t n) { + // f(p, n); + // } + // ``` + using BaseVisitor = + ConstStmtVisitor; + + const Expr *MemberBase; + const DependentValuesTy *DependentValues; + ASTContext &Ctx; + + // If `Deref` has the form `*&e`, return `e`; otherwise return nullptr. + const Expr *trySimplifyDerefAddressof(const UnaryOperator *Deref, + bool hasBeenSubstituted) { + const Expr *DerefOperand = Deref->getSubExpr()->IgnoreParenImpCasts(); + + if (const auto *UO = dyn_cast(DerefOperand)) + if (UO->getOpcode() == UO_AddrOf) + return UO->getSubExpr(); + if (const auto *DRE = dyn_cast(DerefOperand)) { + if (!DependentValues || hasBeenSubstituted) + return nullptr; + + auto I = DependentValues->find(DRE->getDecl()); + + if (I != DependentValues->end()) + if (const auto *UO = dyn_cast(I->getSecond())) + if (UO->getOpcode() == UO_AddrOf) + return UO->getSubExpr(); + } + return nullptr; + } + + explicit CompatibleCountExprVisitor(const Expr *MemberBase, + const DependentValuesTy *DependentValues, + ASTContext &Ctx) + : MemberBase(MemberBase), DependentValues(DependentValues), Ctx(Ctx) {} + + bool VisitStmt(const Stmt *S, const Expr *E, bool hasBeenSubstituted) { + return false; + } + + bool VisitImplicitCastExpr(const ImplicitCastExpr *SelfICE, const Expr *Other, + bool hasBeenSubstituted) { + return Visit(SelfICE->getSubExpr(), Other, hasBeenSubstituted); + } + + bool VisitParenExpr(const ParenExpr *SelfPE, const Expr *Other, + bool hasBeenSubstituted) { + return Visit(SelfPE->getSubExpr(), Other, hasBeenSubstituted); + } + + bool VisitIntegerLiteral(const IntegerLiteral *SelfIL, const Expr *Other, + bool hasBeenSubstituted) { + if (const auto *IntLit = + dyn_cast(Other->IgnoreParenImpCasts())) { + return SelfIL == IntLit || + llvm::APInt::isSameValue(SelfIL->getValue(), IntLit->getValue()); + } + return false; + } + + bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Self, + const Expr *Other, + bool hasBeenSubstituted) { + // If `Self` is a `sizeof` expression, try to evaluate and compare the two + // expressions as constants: + if (Self->getKind() == UnaryExprOrTypeTrait::UETT_SizeOf) { + Expr::EvalResult ER; + + if (Self->EvaluateAsConstantExpr(ER, Ctx)) { + APInt SelfVal = ER.Val.getInt(); + + if (Other->getType()->isIntegerType()) + if (Other->EvaluateAsConstantExpr(ER, Ctx)) + return APInt::isSameValue(SelfVal, ER.Val.getInt()); + } + } + return false; + } + + bool VisitCXXThisExpr(const CXXThisExpr *SelfThis, const Expr *Other, + bool hasBeenSubstituted) { + return isa(Other->IgnoreParenImpCasts()); + } + + bool VisitDeclRefExpr(const DeclRefExpr *SelfDRE, const Expr *Other, + bool hasBeenSubstituted) { + const ValueDecl *SelfVD = SelfDRE->getDecl(); + + if (DependentValues && !hasBeenSubstituted) { + const auto It = DependentValues->find(SelfVD); + if (It != DependentValues->end()) + return Visit(It->second, Other, true); + } + + const auto *O = Other->IgnoreParenImpCasts(); + + if (const auto *OtherDRE = dyn_cast(O)) { + // Both SelfDRE and OtherDRE can be transformed from member expressions: + if (OtherDRE->getDecl() == SelfVD) + return true; + return false; + } + + const auto *OtherME = dyn_cast(O); + if (MemberBase && OtherME) { + return OtherME->getMemberDecl() == SelfVD && + Visit(OtherME->getBase(), MemberBase, hasBeenSubstituted); + } + + return false; + } + + bool VisitMemberExpr(const MemberExpr *Self, const Expr *Other, + bool hasBeenSubstituted) { + // Even though we don't support member expression in counted-by, actual + // arguments can be member expressions. + if (Self == Other) + return true; + if (const auto *DRE = dyn_cast(Other->IgnoreParenImpCasts())) + return MemberBase && Self->getMemberDecl() == DRE->getDecl() && + Visit(Self->getBase(), MemberBase, hasBeenSubstituted); + if (const auto *OtherME = + dyn_cast(Other->IgnoreParenImpCasts())) { + return Self->getMemberDecl() == OtherME->getMemberDecl() && + Visit(Self->getBase(), OtherME->getBase(), hasBeenSubstituted); + } + return false; + } + + bool VisitUnaryOperator(const UnaryOperator *SelfUO, const Expr *Other, + bool hasBeenSubstituted) { + if (SelfUO->getOpcode() != UO_Deref) + return false; // We don't support any other unary operator + + if (const auto *OtherUO = + dyn_cast(Other->IgnoreParenImpCasts())) { + if (SelfUO->getOpcode() == OtherUO->getOpcode()) + return Visit(SelfUO->getSubExpr(), OtherUO->getSubExpr(), + hasBeenSubstituted); + } + // If `Other` is not a dereference expression, try to simplify `SelfUO`: + if (const auto *SimplifiedSelf = + trySimplifyDerefAddressof(SelfUO, hasBeenSubstituted)) { + return Visit(SimplifiedSelf, Other, hasBeenSubstituted); + } + return false; + } + + bool VisitBinaryOperator(const BinaryOperator *SelfBO, const Expr *Other, + bool hasBeenSubstituted) { + const auto *OtherBO = + dyn_cast(Other->IgnoreParenImpCasts()); + if (OtherBO && OtherBO->getOpcode() == SelfBO->getOpcode()) { + return Visit(SelfBO->getLHS(), OtherBO->getLHS(), hasBeenSubstituted) && + Visit(SelfBO->getRHS(), OtherBO->getRHS(), hasBeenSubstituted); + } + + return false; + } + + // Support any overloaded operator[] so long as it is a const method. + bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *SelfOpCall, + const Expr *Other, bool hasBeenSubstituted) { + if (SelfOpCall->getOperator() != OverloadedOperatorKind::OO_Subscript) + return false; + + const auto *MD = dyn_cast(SelfOpCall->getCalleeDecl()); + + if (!MD || !MD->isConst()) + return false; + if (const auto *OtherOpCall = + dyn_cast(Other->IgnoreParenImpCasts())) + if (SelfOpCall->getOperator() == OtherOpCall->getOperator()) { + return Visit(SelfOpCall->getArg(0), OtherOpCall->getArg(0), + hasBeenSubstituted) && + Visit(SelfOpCall->getArg(1), OtherOpCall->getArg(1), + hasBeenSubstituted); + } + return false; + } + + // Support array/pointer subscript. Even though these operators are generally + // considered unsafe, they can be safely used on constant arrays with + // known-safe literal indexes. + bool VisitArraySubscriptExpr(const ArraySubscriptExpr *SelfAS, + const Expr *Other, bool hasBeenSubstituted) { + if (const auto *OtherAS = + dyn_cast(Other->IgnoreParenImpCasts())) + return Visit(SelfAS->getLHS(), OtherAS->getLHS(), hasBeenSubstituted) && + Visit(SelfAS->getRHS(), OtherAS->getRHS(), hasBeenSubstituted); + return false; + } + + // Support non-static member call: + bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *SelfCall, + const Expr *Other, bool hasBeenSubstituted) { + const CXXMethodDecl *MD = SelfCall->getMethodDecl(); + + // The callee member function must be a const function with no parameter: + if (MD->isConst() && MD->param_empty()) { + if (auto *OtherCall = + dyn_cast(Other->IgnoreParenImpCasts())) { + return OtherCall->getMethodDecl() == MD && + Visit(SelfCall->getImplicitObjectArgument(), + OtherCall->getImplicitObjectArgument(), + hasBeenSubstituted); + } + } + return false; + } +}; + +// TL'DR: +// Checks if `E` is the same as `ExpectedCountExpr` modulo implicit casts and +// parens. +// +// Lengthy description: +// `E`: represents the actual count/size associated to a +// pointer. +// `ExpectedCountExpr`: represents the counted-by expression of a counted-by +// type attribute. +// `MemberBase`: represents "this" member base. A MemberExpr +// representing `this->f` in both `E` and +// `ExpectedCountExpr` may be transformed to a DRE +// representing just `f`. Therefore we need to keep track +// of the base for them in that case. +// `DependentValues`: is a mapping from parameter DREs to actual argument +// expressions. It serves as a "state" where +// `ExpectedCountExpr` is "interpreted". +// +// This function then checks for a pointer with a known count `E` that `E` is +// equivalent to the count `ExpectedCountExpr` of a counted-by type attribute at +// the state `DependentValues`. +// +// For example, suppose there is a call to a function `foo(int *__counted_by(n) +// p, size_t n)`: +// +// foo(x, y+z); +// +// We check that the count associated to the pointer 'x' is same as the +// expected count expression 'n' with the mapping (state) '{n -> y+z}'. The +// count of 'x' is determined by pre-defined knowledge, e.g., if 'x' has the +// form of 'span.data()', its' count is 'span.size()', etc. At this point, we +// already know that `E` is the count of 'x'. So we just need to compare `E` +// to 'n' with 'n' being interpreted under '{n -> y+z}'. That is, this function +// will return true iff `E` is same as 'y+z'. +bool isCompatibleWithCountExpr(const Expr *E, const Expr *ExpectedCountExpr, + const Expr *MemberBase, + const DependentValuesTy *DependentValues, + ASTContext &Ctx) { + CompatibleCountExprVisitor Visitor(MemberBase, DependentValues, Ctx); + return Visitor.Visit(ExpectedCountExpr, E, /* hasBeenSubstituted*/ false); +} + +// Returns true iff `C` is a C++ nclass method call to the function +// `'ClassName'::'MethodName'` or `'ClassName'::Operator'MethodName'`: +static bool matchCXXMethodByName(const CallExpr *C, StringRef ClassName, + StringRef MethodName) { + const Decl *Callee = C->getCalleeDecl(); + + if (const auto *MethodDecl = dyn_cast(Callee)) { + if (!MethodDecl->getDeclName().isIdentifier() && + !MethodDecl->isOverloadedOperator()) + return false; + + if (MethodDecl->getDeclName().isIdentifier() && + MethodDecl->getName() != MethodName) + return false; + + if (MethodDecl->isOverloadedOperator()) { + StringRef Spelling = + getOperatorSpelling(MethodDecl->getOverloadedOperator()); + + if (Spelling != MethodName) + return false; + } + + if (const auto *RD = dyn_cast(MethodDecl->getParent())) { + if (!RD->getDeclName().isIdentifier()) + return false; + return RD->getQualifiedNameAsString() == ClassName; + } + } + return false; +} + +// Returns if a pair of expressions contain method calls to .data()/.c_str() and +// .size()/.size_bytes()/.length() that form a valid range. +static bool isValidContainerRange(ASTContext &Context, const Expr *Data, + const Expr *Size, bool ArgInBytes, + bool ParamInBytes) { + const auto *DataCall = + dyn_cast(Data->IgnoreParenImpCasts()); + + if (!(DataCall && + (matchCXXMethodByName(DataCall, "std::array", "data") || + matchCXXMethodByName(DataCall, "std::basic_string", "c_str") || + matchCXXMethodByName(DataCall, "std::basic_string", "data") || + matchCXXMethodByName(DataCall, "std::basic_string_view", "data") || + matchCXXMethodByName(DataCall, "std::span", "data") || + matchCXXMethodByName(DataCall, "std::vector", "data")))) + return false; + + const auto *SizeCall = + dyn_cast(Size->IgnoreParenImpCasts()); + + if (!(SizeCall && + (matchCXXMethodByName(SizeCall, "std::array", "size") || + matchCXXMethodByName(SizeCall, "std::basic_string", "length") || + matchCXXMethodByName(SizeCall, "std::basic_string", "size") || + matchCXXMethodByName(SizeCall, "std::basic_string_view", "length") || + matchCXXMethodByName(SizeCall, "std::basic_string_view", "size") || + matchCXXMethodByName(SizeCall, "std::span", "size") || + matchCXXMethodByName(SizeCall, "std::span", "size_bytes") || + matchCXXMethodByName(SizeCall, "std::vector", "size")))) + return false; + + const Expr *DataObj = DataCall->getImplicitObjectArgument(); + const Expr *SizeObj = SizeCall->getImplicitObjectArgument(); + if (!DataObj || !SizeObj) + return false; + + bool AllowSizeBytes = ParamInBytes; + bool RequireSizeBytes = !ArgInBytes && ParamInBytes; + bool IsSizeBytes = SizeCall->getDirectCallee()->getName() == "size_bytes"; + if (!AllowSizeBytes && IsSizeBytes) + return false; + if (RequireSizeBytes && !IsSizeBytes) + return false; + + // Check if the base of data and size is the same. + const auto *DataDRE = dyn_cast(DataObj->IgnoreImpCasts()); + const auto *SizeDRE = dyn_cast(SizeObj->IgnoreImpCasts()); + return DataDRE && SizeDRE && DataDRE->getDecl() == SizeDRE->getDecl(); +} + +// Extract the extent `X` from `sp.first(X).data()` and friends. +const Expr *extractExtentFromSubviewDataCall(ASTContext &Context, + const Expr *E) { + auto ExtentMatcher = [](const CXXMemberCallExpr *MCE, StringRef Name, + unsigned N) -> const Expr * { + if (matchCXXMethodByName(MCE, "std::span", Name) && MCE->getNumArgs() > N) + return MCE->getArg(N); + return nullptr; + }; + + if (const auto *MCE = dyn_cast(E->IgnoreParenImpCasts())) { + if (!matchCXXMethodByName(MCE, "std::span", "data")) + return nullptr; + if (const auto *DataObj = MCE->getImplicitObjectArgument()) + if (const auto *DataObjMCE = + dyn_cast(DataObj->IgnoreParenImpCasts())) { + if (const auto *Extent = ExtentMatcher(DataObjMCE, "first", 0)) + return Extent; + if (const auto *Extent = ExtentMatcher(DataObjMCE, "last", 0)) + return Extent; + if (const auto *Extent = ExtentMatcher(DataObjMCE, "subspan", 1)) + return Extent; + } + } + return nullptr; +} + +// Returns true iff `E` evaluates to `Val`. +static bool hasIntegeralConstant(const Expr *E, uint64_t Val, ASTContext &Ctx) { + Expr::EvalResult ER; + + if (E->EvaluateAsInt(ER, Ctx)) { + APInt Eval = ER.Val.getInt(); + + return APInt::isSameValue(Eval, APInt(Eval.getBitWidth(), Val)); + } + return false; +} + +// Return `DRE` if `E` matches the form `&DRE`: +static const DeclRefExpr *tryGetAddressofDRE(const Expr *E) { + if (const auto *UO = dyn_cast(E->IgnoreParenImpCasts())) { + if (UO->getOpcode() != UnaryOperator::Opcode::UO_AddrOf) + return nullptr; + return dyn_cast(UO->getSubExpr()->IgnoreParenImpCasts()); + } + return nullptr; +} + +// Checks if the argument passed to count-attributed pointer is one of the +// following forms: +// 0. `NULL/nullptr`, if the argument to dependent count/size is `0`. +// 1. `&var`, if `var` is a variable identifier and (1.a.) the dependent count +// is `1`; or (1.b.) the counted_by expression is constant `1` +// 2. `&var`, if `var` is a variable identifier and the dependent size is +// equivalent to `sizeof(var)`. +// 3. `sp.data()` if the argument to dependent count is `sp.size()`. +// 4. `sp.first(extent).data()` if `extent` is compatible with the `count`. +// 5. `p` if `p` is __counted_by(c) pointer and `c` is compatible with the +// `count` of the param type. +// 6. `constant-array` if the argument to dependent count is the length of +// `constant-array`. +// 7. `(T*) constant-array` if the size of `T` equals to one byte and the +// argument to dependent size is equivalent to `sizeof(constant-array)`. +// +// This function is generalized for two different cases: +// (a). `__counted_by(e)` attributes are annotated for the function being +// called; +// (b). The function being called has no annotations but this analysis +// hardcodes `__counted_by(n)/__sized_by(n)` relation for a pair of +// parameters--pointer `p` and count/size `n`. +// (E.g., the first two parameters of snprintf). +// +// \param PtrArg the argument to the parameter representing a counted_by +// pointer. +// \param CountArg the argument to the parameter representing count/size. Or +// NULL if the count/size cannot be represented by one argument. +// `CountArg` is never NULL for case (b). +// \param CountedByExpr +// the counted_by expression in the annotated bounds attribute +// for case (a); Or NULL for case (b). This parameter indicates +// whether it is case (a) or (b). +// \param CountedByPointerTy +// the pointer type of a counted_by parameter. Note +// this type is NOT necessarily a `CountAttributedType` in case +// (b) because the counted_by relation comes from hardcoded +// knowledge. +// \param isSizedBy +// true iff the counted_by relation is bytewise. +// \param isOrNull +// true iff the counted_by attribute is __counted_by_or_null +// \param DependentValueMap +// A map from variables in the counted_by expression to actual +// arguments in case (a); or NULL for case (b) or failure in +// obtatining the info. +static bool isCountAttributedPointerArgumentSafeImpl( + ASTContext &Context, const Expr *PtrArg, + const Expr * /* Nullable */ CountArg, const Type *CountedByPointerTy, + const Expr * /* Nullable */ CountedByExpr, bool isSizedBy, bool isOrNull, + const DependentValuesTy * /* Nullable */ DependentValueMap = nullptr) { + assert((CountedByExpr || CountArg) && + "Only one of them can be null. If the __counted_by information is " + "hardcoded, there must be an argument representing the actual " + "count."); + assert((CountedByExpr || !DependentValueMap) && + "If the __counted_by information is hardcoded, there is no " + "dependent value map."); + const Expr *PtrArgNoImp = PtrArg->IgnoreParenImpCasts(); + + // check form 0: + if (PtrArgNoImp->getType()->isNullPtrType()) { + if (CountArg) + return hasIntegeralConstant(CountArg, 0, Context); + // When there is no argument representing the count/size, it is safe iff + // the annotation is `__counted_by(0)`. + return hasIntegeralConstant(CountedByExpr, 0, Context); + } + + // check form 1-2: + if (auto *DRE = tryGetAddressofDRE(PtrArgNoImp)) { + if (CountArg) { + if (!isSizedBy) // form 1.a.: + return hasIntegeralConstant(CountArg, 1, Context); + // form 2: + if (auto TySize = Context.getTypeSizeInCharsIfKnown(DRE->getType())) + return hasIntegeralConstant(CountArg, TySize->getQuantity(), Context); + } else + // form 1.b.: when there is no argument representing the count/size, it is + // safe iff the annotation is `__counted_by(1)`. + return !isSizedBy && hasIntegeralConstant(CountedByExpr, 1, Context); + return false; + } + + auto getTypeSize = [&](QualType Ty) -> std::optional { + if (Context.hasSameUnqualifiedType(Ty, Context.VoidTy)) + return {CharUnits::One()}; + return Context.getTypeSizeInCharsIfKnown(Ty); + }; + + std::optional ArgTypeSize = getTypeSize( + QualType(PtrArgNoImp->getType()->getPointeeOrArrayElementType(), 0)); + std::optional ParamTypeSize = + getTypeSize(CountedByPointerTy->getPointeeType()); + + bool ArgInBytes = ArgTypeSize.has_value() && ArgTypeSize->isOne(); + bool ParamInBytes = + isSizedBy || (ParamTypeSize.has_value() && ParamTypeSize->isOne()); + + // If there is only one dependent count, check for the form 3. + if (CountArg && isValidContainerRange(Context, PtrArg, CountArg, ArgInBytes, + ParamInBytes)) + return true; + + // Check forms 4-7. + + if (ArgInBytes != ParamInBytes) + return false; + + if (!DependentValueMap && CountedByExpr) + // Bail if the map is not available for case (a). Because + // we need the map to substitute parameters to arguments in case (a) but + // not case(b). + return false; + + // the actual count of the pointer inferred through patterns below: + const Expr *ActualCount = nullptr; + const Expr *MemberBase = nullptr; + + if (const auto *ME = dyn_cast(PtrArgNoImp)) + MemberBase = ME->getBase(); + + if (const Expr *ExtentExpr = + extractExtentFromSubviewDataCall(Context, PtrArg)) { + // Form 4. + ActualCount = ExtentExpr; + } else if (const auto *ArgCAT = + PtrArgNoImp->getType()->getAs()) { + // Form 5. + bool areBoundsAttributesCompatible = + (ArgCAT->isCountInBytes() == isSizedBy || (ArgInBytes && ParamInBytes)); + + if (ArgCAT->isOrNull() == isOrNull && areBoundsAttributesCompatible) + ActualCount = ArgCAT->getCountExpr(); + } else { + // Form 6-7. + const Expr *ArrArg = PtrArgNoImp; + + if (ArgInBytes) + if (auto *CE = dyn_cast(ArrArg)) + // In case of ArgInBytes, we know the destination type is a pointer to + // char: + ArrArg = CE->getSubExpr()->IgnoreParenImpCasts(); + if (const auto *ATy = Context.getAsConstantArrayType(ArrArg->getType())) { + APInt TySize = ATy->getSize(); + + if (isSizedBy) + if (auto TySizeInChar = Context.getTypeSizeInCharsIfKnown(ATy)) + TySize = APInt(TySize.getBitWidth(), TySizeInChar->getQuantity()); + ActualCount = IntegerLiteral::Create( + Context, TySize, Context.getSizeType(), SourceLocation()); + } + } + if (!ActualCount) + return false; + if (!CountedByExpr) + // In case (b), the actual count of the pointer must match the argument + // passed to the parameter representing the count: + CountedByExpr = CountArg; + return isCompatibleWithCountExpr(ActualCount, CountedByExpr, MemberBase, + DependentValueMap, Context); +} + +// Checks if the argument passed to a count-attributed pointer is safe. This +// function is for the case where the pointer has bounds attributes. This is +// case (a) in `isCountAttributedPointerArgumentSafeImpl`. +static bool isCountAttributedPointerArgumentSafe(ASTContext &Context, + const CountAttributedType *CAT, + const CallExpr *Call, + const Expr *Arg) { + const Expr * /* Nullable */ CountArg = + findCountArg(CAT->getCountExpr(), Call); + auto ValuesOpt = getDependentValuesFromCall(CAT, Call); + + return isCountAttributedPointerArgumentSafeImpl( + Context, Arg, CountArg, CAT, CAT->getCountExpr(), CAT->isCountInBytes(), + CAT->isOrNull(), ValuesOpt.has_value() ? &*ValuesOpt : nullptr); +} + +// Checks if arguments passed to a function, which has no annotation but +// hardcoded counted_by knowledge, is safe. This is case (b) in +// `isCountAttributedPointerArgumentSafeImpl`. +static bool isHardcodedCountedByPointerArgumentSafe( + ASTContext &Context, const Expr *PtrArg, const Expr *CountArg, + const Type *PtrParmType, bool isSizedBy, bool isOrNull) { + return isCountAttributedPointerArgumentSafeImpl( + Context, PtrArg, CountArg, PtrParmType, nullptr, isSizedBy, isOrNull); +} + +// Checks if the argument passed to __single pointer is one of the following +// forms: +// 0. `nullptr`. +// 1. `&var`, if `var` is a variable identifier. +// 2. `&C[_]`, if `C` is a hardened container/view. +// 3. `sp.first(1).data()` and friends. +bool isSinglePointerArgumentSafe(ASTContext &Context, const Expr *Arg) { + const Expr *ArgNoImp = Arg->IgnoreParenImpCasts(); + + // Check form 0: + if (ArgNoImp->getType()->isNullPtrType()) + return true; + + // Check form 1: + { + if (tryGetAddressofDRE(ArgNoImp)) + return true; + } + + // Check form 2: + { + if (const auto *UO = dyn_cast(ArgNoImp)) + if (UO->getOpcode() == UnaryOperator::Opcode::UO_AddrOf) { + const Expr *Operand = UO->getSubExpr()->IgnoreParenImpCasts(); + + if (const auto *OPCall = dyn_cast(Operand)) + // TODO: Add more classes. + if (matchCXXMethodByName(OPCall, "std::array", "[]") || + matchCXXMethodByName(OPCall, "std::basic_string", "[]") || + matchCXXMethodByName(OPCall, "std::basic_string_view", "[]") || + matchCXXMethodByName(OPCall, "std::span", "[]") || + matchCXXMethodByName(OPCall, "std::vector", "[]")) + return true; + } + } + + // Check form 3: + if (const Expr *ExtentExpr = + extractExtentFromSubviewDataCall(Context, ArgNoImp)) { + std::optional ExtentVal = + ExtentExpr->getIntegerConstantExpr(Context); + if (ExtentVal.has_value() && ExtentVal->isOne()) + return true; + } + + return false; +} +/* TO_UPSTREAM(BoundsSafetyInterop) OFF */ +} // namespace + // Returns true iff integer E1 is equivalent to integer E2. // // For now we only support such expressions: @@ -459,12 +1165,20 @@ static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx) { // 4. `std::span{a, n}`, where `a` is of an array-of-T with constant size // `n` // 5. `std::span{any, 0}` -// 6. `std::span{ (char *)f(args), args[N] * arg*[M]}`, where +// 6. `std::span{p, strlen(p)}` or `std::span{p, wcslen(p)}` +// 7. `std::span{ (char *)f(args), args[N] * arg*[M]}`, where // `f` is a function with attribute `alloc_size(N, M)`; // `args` represents the list of arguments; // `N, M` are parameter indexes to the allocating element number and size. // Sometimes, there is only one parameter index representing the total // size. +// TO_UPSTREAM(BoundsSafetyInterop) ON +// Interop Pattern: +// `std::span{p, n}`, where `p` is a __counted_by(`n`)/__sized_by(`n`) +// pointer OR `std::span{(char*)p, n}`, where `p` is a +// __sized_by(`n`) pointer. (This pattern is not in upstream, so try it +// last to avoid possible conflicts.) +// TO_UPSTREAM(BoundsSafetyInterop) OFF static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node, ASTContext &Ctx) { assert(Node.getNumArgs() == 2 && @@ -525,7 +1239,7 @@ static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node, break; } - QualType Arg0Ty = Arg0->IgnoreImplicit()->getType(); + QualType Arg0Ty = Arg0->getType(); if (auto *ConstArrTy = Ctx.getAsConstantArrayType(Arg0Ty)) { const APSInt ConstArrSize = APSInt(ConstArrTy->getSize()); @@ -533,7 +1247,23 @@ static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node, // Check form 4: return Arg1CV && APSInt::compareValues(ConstArrSize, *Arg1CV) == 0; } + // Check form 6: + if (const auto *Call = dyn_cast(Arg1); + Call && Call->getNumArgs() == 1) { + if (const FunctionDecl *FD = Call->getDirectCallee(); + FD && FD->getIdentifier()) { + StringRef FName = FD->getName(); + const Expr *CallArg = Call->getArg(0)->IgnoreParenImpCasts(); + + // TODO: we can re-use `LibcFunNamePrefixSuffixParser` to support more + // variants, e.g., `strlen_s` + return (FName == "strlen" || FName == "wcslen") && + AreSameDRE(Arg0, CallArg); + } + } + + // Check form 7: if (auto CCast = dyn_cast(Arg0)) { if (!CCast->getType()->isPointerType()) return false; @@ -562,6 +1292,53 @@ static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node, } } } + + /* TO_UPSTREAM(BoundsSafetyInterop) ON */ + // Check interop pattern: + bool isArg0CastToBytePtrType = false; + + if (auto *CE = dyn_cast(Arg0)) { + if (auto DestTySize = + Ctx.getTypeSizeInCharsIfKnown(Arg0Ty->getPointeeType())) { + if (!DestTySize->isOne()) + return false; // If the destination pointee type is NOT of one byte + // size, pattern match fails. + Arg0 = CE->getSubExpr()->IgnoreParenImpCasts(); + Arg0Ty = Arg0->getType(); + isArg0CastToBytePtrType = true; + } + } + // Check pointer and count/size with respect to the count-attribute: + if (const auto *CAT = Arg0Ty->getAs()) { + // For the pattern of `std::span{(char *) p, n}`, p must NOT be a + // __counted_by pointer. + if (!CAT->isCountInBytes() && isArg0CastToBytePtrType) + return false; + // If `Arg0` is not a cast and is a sized_by pointer, its pointee type size + // must be one byte: + if (CAT->isCountInBytes() && !isArg0CastToBytePtrType) { + std::optional SizeOpt = + Ctx.getTypeSizeInCharsIfKnown(CAT->getPointeeType()); + if (!SizeOpt.has_value() || !SizeOpt->isOne()) + return false; + } + + const Expr *MemberBase = nullptr; + if (const auto *ME = dyn_cast(Arg0)) + MemberBase = ME->getBase(); + + if (const auto *Call = dyn_cast(Arg0)) { + auto ValuesOpt = getDependentValuesFromCall(CAT, Call); + if (!ValuesOpt.has_value()) + return false; + return isCompatibleWithCountExpr(Arg1, CAT->getCountExpr(), MemberBase, + &*ValuesOpt, Ctx); + } + + return isCompatibleWithCountExpr(Arg1, CAT->getCountExpr(), MemberBase, + /*DependentValues=*/nullptr, Ctx); + } + /* TO_UPSTREAM(BoundsSafetyInterop) OFF */ return false; } @@ -619,6 +1396,43 @@ static bool isSafeArraySubscript(const ArraySubscriptExpr &Node, return false; } +static bool hasNumArgs(const CallExpr *CE, unsigned NumArgs) { + return CE->getNumArgs() == NumArgs; +} + +static bool hasAnyBoundsAttributes(const FunctionDecl *FD) { + bool RetTyHasBoundsAttr = FD->getReturnType()->isBoundsAttributedType() || + FD->getReturnType()->isValueTerminatedType(); + return RetTyHasBoundsAttr || + llvm::any_of(FD->parameters(), [](const ParmVarDecl *PVD) { + return PVD->getType()->isBoundsAttributedType() || + PVD->getType()->isValueTerminatedType(); + }); +} + +// A pointer type expression is known to be null-terminated, if +// 1. it is a string literal or `PredefinedExpr` (e.g., `__func__`); +// 2. it has the form: E.c_str(), for any expression E of `std::string` type; +// 3. it has `__null_terminated` type +static bool isNullTermPointer(const Expr *Ptr, ASTContext &Ctx) { + if (isa(Ptr->IgnoreParenImpCasts())) + return true; + if (isa(Ptr->IgnoreParenImpCasts())) + return true; + if (auto *MCE = dyn_cast(Ptr->IgnoreParenImpCasts())) { + const CXXMethodDecl *MD = MCE->getMethodDecl(); + const CXXRecordDecl *RD = MCE->getRecordDecl()->getCanonicalDecl(); + + if (MD && RD && RD->isInStdNamespace()) + if (MD->getName() == "c_str" && RD->getName() == "basic_string") + return true; + } + if (auto *VTT = Ptr->getType().getTypePtr()->getAs()) { + return VTT->getTerminatorValue(Ctx).isZero(); + } + return false; +} + namespace libc_func_matchers { // Under `libc_func_matchers`, define a set of matchers that match unsafe // functions in libc and unsafe calls to them. @@ -664,24 +1478,6 @@ struct LibcFunNamePrefixSuffixParser { } }; -// A pointer type expression is known to be null-terminated, if it has the -// form: E.c_str(), for any expression E of `std::string` type. -static bool isNullTermPointer(const Expr *Ptr) { - if (isa(Ptr->IgnoreParenImpCasts())) - return true; - if (isa(Ptr->IgnoreParenImpCasts())) - return true; - if (auto *MCE = dyn_cast(Ptr->IgnoreParenImpCasts())) { - const CXXMethodDecl *MD = MCE->getMethodDecl(); - const CXXRecordDecl *RD = MCE->getRecordDecl()->getCanonicalDecl(); - - if (MD && RD && RD->isInStdNamespace() && MD->getIdentifier()) - if (MD->getName() == "c_str" && RD->getName() == "basic_string") - return true; - } - return false; -} - // Return true iff at least one of following cases holds: // 1. Format string is a literal and there is an unsafe pointer argument // corresponding to an `s` specifier; @@ -698,11 +1494,12 @@ static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg, const CallExpr *Call; unsigned FmtArgIdx; const Expr *&UnsafeArg; + ASTContext &Ctx; public: StringFormatStringHandler(const CallExpr *Call, unsigned FmtArgIdx, - const Expr *&UnsafeArg) - : Call(Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg) {} + const Expr *&UnsafeArg, ASTContext &Ctx) + : Call(Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg), Ctx(Ctx) {} bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, @@ -713,7 +1510,7 @@ static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg, unsigned ArgIdx = FS.getPositionalArgIndex() + FmtArgIdx; if (0 < ArgIdx && ArgIdx < Call->getNumArgs()) - if (!isNullTermPointer(Call->getArg(ArgIdx))) { + if (!isNullTermPointer(Call->getArg(ArgIdx), Ctx)) { UnsafeArg = Call->getArg(ArgIdx); // output // returning false stops parsing immediately return false; @@ -735,7 +1532,7 @@ static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg, else goto CHECK_UNSAFE_PTR; - StringFormatStringHandler Handler(Call, FmtArgIdx, UnsafeArg); + StringFormatStringHandler Handler(Call, FmtArgIdx, UnsafeArg, Ctx); return analyze_format_string::ParsePrintfString( Handler, FmtStr.begin(), FmtStr.end(), Ctx.getLangOpts(), @@ -747,8 +1544,8 @@ static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg, // (including the format argument) is unsafe pointer. return llvm::any_of( llvm::make_range(Call->arg_begin() + FmtArgIdx, Call->arg_end()), - [&UnsafeArg](const Expr *Arg) -> bool { - if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg)) { + [&UnsafeArg, &Ctx](const Expr *Arg) -> bool { + if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg, Ctx)) { UnsafeArg = Arg; return true; } @@ -1000,7 +1797,7 @@ static bool hasUnsafePrintfStringArg(const CallExpr &Node, ASTContext &Ctx, // We don't really recognize this "normal" printf, the only thing we // can do is to require all pointers to be null-terminated: for (const auto *Arg : Node.arguments()) - if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg)) { + if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg, Ctx)) { Result.addNode(Tag, DynTypedNode::create(*Arg)); return true; } @@ -1023,8 +1820,7 @@ static bool hasUnsafePrintfStringArg(const CallExpr &Node, ASTContext &Ctx, // ptr := Constant-Array-DRE; // size:= any expression that has compile-time constant value equivalent to // sizeof (Constant-Array-DRE) -static bool hasUnsafeSnprintfBuffer(const CallExpr &Node, - const ASTContext &Ctx) { +static bool hasUnsafeSnprintfBuffer(const CallExpr &Node, ASTContext &Ctx) { const FunctionDecl *FD = Node.getDirectCallee(); assert(FD && "It should have been checked that FD is non-null."); @@ -1044,55 +1840,53 @@ static bool hasUnsafeSnprintfBuffer(const CallExpr &Node, !Size->getType()->isIntegerType()) return false; // not an snprintf call - // Pattern 1: - static StringRef SizedObjs[] = {"span", "array", "vector", - "basic_string_view", "basic_string"}; - Buf = Buf->IgnoreParenImpCasts(); - Size = Size->IgnoreParenImpCasts(); - if (auto *MCEPtr = dyn_cast(Buf)) - if (auto *MCESize = dyn_cast(Size)) { - auto *DREOfPtr = dyn_cast( - MCEPtr->getImplicitObjectArgument()->IgnoreParenImpCasts()); - auto *DREOfSize = dyn_cast( - MCESize->getImplicitObjectArgument()->IgnoreParenImpCasts()); - - if (!DREOfPtr || !DREOfSize) - return true; // not in safe pattern - if (DREOfPtr->getDecl() != DREOfSize->getDecl()) - return true; // not in safe pattern - if (MCEPtr->getMethodDecl()->getName() != "data") - return true; // not in safe pattern - - if (MCESize->getMethodDecl()->getName() == "size_bytes" || - // Note here the pointer must be a pointer-to-char type unless there - // is explicit casting. If there is explicit casting, this branch - // is unreachable. Thus, at this branch "size" and "size_bytes" are - // equivalent as the pointer is a char pointer: - MCESize->getMethodDecl()->getName() == "size") - for (StringRef SizedObj : SizedObjs) - if (MCEPtr->getRecordDecl()->isInStdNamespace() && - MCEPtr->getRecordDecl()->getCanonicalDecl()->getName() == - SizedObj) - return false; // It is in fact safe - } + if (std::optional NumChars = + Ctx.getTypeSizeInCharsIfKnown(FirstPteTy)) { + Buf = Buf->IgnoreParenImpCasts(); + Size = Size->IgnoreParenImpCasts(); + return !isHardcodedCountedByPointerArgumentSafe( + Ctx, Buf, Size, FirstParmTy.getTypePtr(), NumChars->isOne(), false); + } + return false; +} +} // namespace libc_func_matchers - // Pattern 2: - if (auto *DRE = dyn_cast(Buf->IgnoreParenImpCasts())) { - if (auto *CAT = Ctx.getAsConstantArrayType(DRE->getType())) { - Expr::EvalResult ER; - // The array element type must be compatible with `char` otherwise an - // explicit cast will be needed, which will make this check unreachable. - // Therefore, the array extent is same as its' bytewise size. - if (Size->EvaluateAsInt(ER, Ctx)) { - APSInt EVal = ER.Val.getInt(); // Size must have integer type +// Matches each argument passed to a `__counted_by(count)` pointer that is +// unsafe, i.e. that is NOT in one of the following forms: +// 1. `sp.data()` if the argument to dependent count is `sp.size()`. +// 2. `sp.first(extent).data()` if `extent` is compatible with the `count`. +static bool forEachUnsafeCountAttributedPointerArgument(const CallExpr *Call, + MatchResults &Result, + StringRef ArgTag, + StringRef CallTag, + ASTContext &Ctx) { + const FunctionDecl *FD = Call->getDirectCallee(); + if (!FD) + return false; - return APSInt::compareValues(EVal, APSInt(CAT->getSize(), true)) != 0; - } + const auto *FPT = FD->getType()->getAs(); + if (!FPT) + return false; + + const unsigned Num = std::min(Call->getNumArgs(), FPT->getNumParams()); + + bool Matched = false; + for (unsigned I = 0; I < Num; ++I) { + const auto *CAT = FPT->getParamType(I)->getAs(); + if (!CAT) + continue; + + const Expr *Arg = Call->getArg(I); + if (!isCountAttributedPointerArgumentSafe(Ctx, CAT, Call, Arg)) { + MatchResult &MR = Result.emplace_back(ArgTag, DynTypedNode::create(*Arg)); + + MR.addNode(CallTag, DynTypedNode::create(*Call)); + Matched = true; } } - return true; // ptr and size are not in safe pattern + + return Matched; } -} // namespace libc_func_matchers namespace { // Because the analysis revolves around variables and their types, we'll need to @@ -1864,48 +2658,72 @@ class UnsafeLibcFunctionCallGadget : public WarningGadget { WarnedFunKind = VA_LIST; } - static bool matches(const Stmt *S, ASTContext &Ctx, + static bool matches(const Stmt *Stmt, ASTContext &Ctx, const UnsafeBufferUsageHandler *Handler, MatchResult &Result) { - if (ignoreUnsafeLibcCall(Ctx, *S, Handler)) + // When this warning interops with bounds attributes, we suppress the + // warning for most of the libc functions except for + // 1. "normal printf" (see `libc_func_matchers::isNormalPrintfFunc`), + // because we still can check its string arguments and take advantage of + // the '__null_terminated' attribute; + // 2. `v*printf/sprintf` functions because these functions cannot be + // completely safe even with bounds attributes + if (!Ctx.getLangOpts().CPlusPlus /* Warn about libc ONLY in C++ */ || + Handler->ignoreUnsafeBufferInLibcCall(Stmt->getBeginLoc())) return false; - auto *CE = dyn_cast(S); - if (!CE || !CE->getDirectCallee()) + + const CallExpr *Call = dyn_cast(Stmt); + + if (!Call) return false; - const auto *FD = dyn_cast(CE->getDirectCallee()); - if (!FD) + + const FunctionDecl *CalleeDecl = + dyn_cast_or_null(Call->getCalleeDecl()); + + if (!CalleeDecl) return false; - auto isSingleStringLiteralArg = false; - if (CE->getNumArgs() == 1) { - isSingleStringLiteralArg = - isa(CE->getArg(0)->IgnoreParenImpCasts()); - } - if (!isSingleStringLiteralArg) { - // (unless the call has a sole string literal argument): - if (libc_func_matchers::isPredefinedUnsafeLibcFunc(*FD)) { - Result.addNode(Tag, DynTypedNode::create(*CE)); - return true; - } - if (libc_func_matchers::isUnsafeVaListPrintfFunc(*FD)) { - Result.addNode(Tag, DynTypedNode::create(*CE)); - Result.addNode(UnsafeVaListTag, DynTypedNode::create(*FD)); - return true; - } - if (libc_func_matchers::isUnsafeSprintfFunc(*FD)) { - Result.addNode(Tag, DynTypedNode::create(*CE)); - Result.addNode(UnsafeSprintfTag, DynTypedNode::create(*FD)); + // If the call has a sole null-terminated argument, e.g., strlen, + // printf, atoi, we consider it safe: + if (hasNumArgs(Call, 1) && isNullTermPointer(Call->getArg(0), Ctx)) + return false; + if (!hasAnyBoundsAttributes(CalleeDecl)) { + // Match a predefined unsafe libc function: + if (libc_func_matchers::isPredefinedUnsafeLibcFunc(*CalleeDecl)) { + Result.addNode(Tag, DynTypedNode::create(*Call)); return true; } + } // v*printf and sprintf functions are always unsafe regardless of whether + // they have bounds annotations + + // Match a call to one of the `v*printf` functions taking va-list, which + // cannot be checked at compile-time: + if (libc_func_matchers::isUnsafeVaListPrintfFunc(*CalleeDecl)) { + Result.addNode(UnsafeVaListTag, DynTypedNode::create(*CalleeDecl)); + Result.addNode(Tag, DynTypedNode::create(*Call)); + return true; } - if (libc_func_matchers::isNormalPrintfFunc(*FD)) { - if (libc_func_matchers::hasUnsafeSnprintfBuffer(*CE, Ctx)) { - Result.addNode(Tag, DynTypedNode::create(*CE)); - Result.addNode(UnsafeSizedByTag, DynTypedNode::create(*CE)); + + // Match a call to a `sprintf` function, which is never safe: + if (libc_func_matchers::isUnsafeSprintfFunc(*CalleeDecl)) { + Result.addNode(UnsafeSprintfTag, DynTypedNode::create(*CalleeDecl)); + Result.addNode(Tag, DynTypedNode::create(*Call)); + return true; + } + + if (libc_func_matchers::isNormalPrintfFunc(*CalleeDecl)) { + // Match a call to an `snprintf` function. And first two arguments of the + // call (that describe a buffer) are not in safe patterns: + if (!hasAnyBoundsAttributes(CalleeDecl) && + libc_func_matchers::hasUnsafeSnprintfBuffer(*Call, Ctx)) { + Result.addNode(UnsafeSizedByTag, DynTypedNode::create(*Call)); + Result.addNode(Tag, DynTypedNode::create(*Call)); return true; } - if (libc_func_matchers::hasUnsafePrintfStringArg(*CE, Ctx, Result, + // Match a call to a `printf` function, which can be safe if + // all arguments are null-terminated: + if (libc_func_matchers::hasUnsafePrintfStringArg(*Call, Ctx, Result, UnsafeStringTag)) { - Result.addNode(Tag, DynTypedNode::create(*CE)); + Result.addNode(Tag, DynTypedNode::create(*Call)); return true; } } @@ -2380,6 +3198,114 @@ class DerefSimplePtrArithFixableGadget : public FixableGadget { } }; +// Represents an argument that is being passed to a __counted_by() pointer. +class CountAttributedPointerArgumentGadget : public WarningGadget { +private: + static constexpr const char *const CallTag = + "CountAttributedPointerArgument_Call"; + static constexpr const char *const ArgTag = + "CountAttributedPointerArgument_Arg"; + const CallExpr *Call; + const Expr *Arg; + +public: + explicit CountAttributedPointerArgumentGadget(const MatchResult &Result) + : WarningGadget(Kind::CountAttributedPointerArgument), + Call(Result.getNodeAs(CallTag)), + Arg(Result.getNodeAs(ArgTag)) { + assert(Call != nullptr && "Expecting a non-null matching result"); + assert(Arg != nullptr && "Expecting a non-null matching result"); + } + + static bool classof(const Gadget *G) { + return G->getKind() == Kind::CountAttributedPointerArgument; + } + + static bool matches(const Stmt *Stmt, ASTContext &Ctx, + MatchResults &Results) { + if (const auto *Call = dyn_cast(Stmt)) + if (forEachUnsafeCountAttributedPointerArgument(Call, Results, ArgTag, + CallTag, Ctx)) + return true; + return false; + } + + void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, + bool IsRelatedToDecl, + ASTContext &Ctx) const override { + Handler.handleUnsafeCountAttributedPointerArgument(Call, Arg, + IsRelatedToDecl, Ctx); + } + + SourceLocation getSourceLoc() const override { return Arg->getBeginLoc(); } + + virtual DeclUseList getClaimedVarUseSites() const override { + if (const auto *DRE = dyn_cast(Arg)) { + return {DRE}; + } + return {}; + } + + SmallVector getUnsafePtrs() const override { + return {}; + } +}; + +// Represents an argument that is being passed to a __single pointer. +class SinglePointerArgumentGadget : public WarningGadget { +private: + static constexpr const char *const ArgTag = "SinglePointerArgument_Arg"; + const Expr *Arg; + +public: + explicit SinglePointerArgumentGadget(const MatchResult &Result) + : WarningGadget(Kind::SinglePointerArgument), + Arg(Result.getNodeAs(ArgTag)) { + assert(Arg != nullptr && "Expecting a non-null matching result"); + } + + static bool classof(const Gadget *G) { + return G->getKind() == Kind::SinglePointerArgument; + } + + static bool matches(const Stmt *Stmt, ASTContext &Ctx, + MatchResults &Results) { + bool Found = false; + + if (const auto *Call = dyn_cast(Stmt)) { + ast_matchers::matchEachArgumentWithParamType( + *Call, [&Results, &Ctx, &Found](QualType QT, const Expr *Arg) { + if (isSinglePointerType(QT) && + !isSinglePointerArgumentSafe(Ctx, Arg)) { + Results.emplace_back(ArgTag, DynTypedNode::create(*Arg)); + Found = true; + } + }); + } + return Found; + } + + void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler, + bool IsRelatedToDecl, + ASTContext &Ctx) const override { + Handler.handleUnsafeSinglePointerArgument(Arg, IsRelatedToDecl, Ctx); + } + + SourceLocation getSourceLoc() const override { return Arg->getBeginLoc(); } + + virtual DeclUseList getClaimedVarUseSites() const override { + if (const auto *DRE = dyn_cast(Arg)) { + return {DRE}; + } + return {}; + } + + SmallVector getUnsafePtrs() const override { + return {}; + } +}; + +/// Scan the function and return a list of gadgets found with provided kits. class WarningGadgetMatcher : public FastMatcher { public: @@ -2399,6 +3325,16 @@ class WarningGadgetMatcher : public FastMatcher { WarningGadgets.push_back(std::make_unique(Result)); \ return true; \ } +#define WARNING_BOUNDS_SAFETY_GADGET(name) \ + { \ + MatchResults MultiResults; \ + if (name##Gadget::matches(S, Ctx, MultiResults) && \ + notInSafeBufferOptOut(*S, &Handler)) { \ + for (auto &Result : MultiResults) \ + WarningGadgets.push_back(std::make_unique(Result)); \ + return true; \ + } \ + } #define WARNING_OPTIONAL_GADGET(name) \ if (name##Gadget::matches(S, Ctx, &Handler, Result) && \ notInSafeBufferOptOut(*S, &Handler)) { \ diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp index 538c1d18a8ac1..616beb80b5998 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -67,6 +67,14 @@ const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB, return DB; } +const StreamingDiagnostic & +clang::operator<<(const StreamingDiagnostic &DB, + const llvm::format_object_base &Fmt) { + SmallString<32> Buf; + llvm::raw_svector_ostream(Buf) << Fmt; + return DB << Buf; +} + static void DummyArgToStringFn(DiagnosticsEngine::ArgumentKind AK, intptr_t QT, StringRef Modifier, StringRef Argument, ArrayRef PrevArgs, diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index e2d940c0d39e9..58632f8b65aef 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -137,6 +137,7 @@ VALIDATE_DIAG_SIZE(SEMA) VALIDATE_DIAG_SIZE(ANALYSIS) VALIDATE_DIAG_SIZE(REFACTORING) VALIDATE_DIAG_SIZE(INSTALLAPI) +VALIDATE_DIAG_SIZE(CAS) #undef VALIDATE_DIAG_SIZE #undef STRINGIFY_NAME @@ -169,6 +170,7 @@ const StaticDiagInfoRec StaticDiagInfo[] = { #include "clang/Basic/DiagnosticAnalysisKinds.inc" #include "clang/Basic/DiagnosticRefactoringKinds.inc" #include "clang/Basic/DiagnosticInstallAPIKinds.inc" +#include "clang/Basic/DiagnosticCASKinds.inc" // clang-format on #undef DIAG }; @@ -212,6 +214,7 @@ CATEGORY(SEMA, CROSSTU) CATEGORY(ANALYSIS, SEMA) CATEGORY(REFACTORING, ANALYSIS) CATEGORY(INSTALLAPI, REFACTORING) +CATEGORY(CAS, INSTALLAPI) #undef CATEGORY // Avoid out of bounds reads. @@ -270,6 +273,10 @@ class CustomDiagInfo { return Diags->second; return {}; } + + unsigned getMaxCustomDiagID() const { + return DIAG_UPPER_LIMIT + DiagInfo.size(); + } }; } // namespace diag @@ -388,6 +395,18 @@ unsigned DiagnosticIDs::getCustomDiagID(CustomDiagDesc Diag) { return CustomDiagInfo->getOrCreateDiagID(Diag); } +std::optional DiagnosticIDs::getMaxCustomDiagID() const { + if (CustomDiagInfo) + return CustomDiagInfo->getMaxCustomDiagID(); + return std::nullopt; +} + +const DiagnosticIDs::CustomDiagDesc & +DiagnosticIDs::getCustomDiagDesc(unsigned DiagID) const { + assert(IsCustomDiag(DiagID)); + return CustomDiagInfo->getDescription(DiagID); +} + bool DiagnosticIDs::isWarningOrExtension(unsigned DiagID) const { return DiagID < diag::DIAG_UPPER_LIMIT ? getDiagClass(DiagID) != CLASS_ERROR diff --git a/clang/lib/Basic/FileManager.cpp b/clang/lib/Basic/FileManager.cpp index 86fe352df0461..9fddaa82ce6c5 100644 --- a/clang/lib/Basic/FileManager.cpp +++ b/clang/lib/Basic/FileManager.cpp @@ -21,6 +21,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Statistic.h" +#include "llvm/CAS/CASReference.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" @@ -464,7 +465,7 @@ OptionalFileEntryRef FileManager::getBypassFile(FileEntryRef VF) { // If we've already bypassed just use the existing one. auto Insertion = SeenBypassFileEntries->insert( - {VF.getName(), std::errc::no_such_file_or_directory}); + {VF.getNameAsRequested(), std::errc::no_such_file_or_directory}); if (!Insertion.second) return FileEntryRef(*Insertion.first); @@ -519,7 +520,8 @@ void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) { llvm::ErrorOr> FileManager::getBufferForFile(FileEntryRef FE, bool isVolatile, bool RequiresNullTerminator, - std::optional MaybeLimit, bool IsText) { + std::optional MaybeLimit, bool IsText, + std::optional *CASContents) { const FileEntry *Entry = &FE.getFileEntry(); // If the content is living on the file entry, return a reference to it. if (Entry->Content) @@ -540,27 +542,45 @@ FileManager::getBufferForFile(FileEntryRef FE, bool isVolatile, if (Entry->File) { auto Result = Entry->File->getBuffer(Filename, FileSize, RequiresNullTerminator, isVolatile); + if (CASContents) { + auto CASRef = Entry->File->getObjectRefForContent(); + if (!CASRef) + return CASRef.getError(); + *CASContents = *CASRef; + } Entry->closeFile(); return Result; } // Otherwise, open the file. return getBufferForFileImpl(Filename, FileSize, isVolatile, - RequiresNullTerminator, IsText); + RequiresNullTerminator, IsText, CASContents); } llvm::ErrorOr> FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize, bool isVolatile, bool RequiresNullTerminator, - bool IsText) const { + bool IsText, + std::optional *CASContents) const { if (FileSystemOpts.WorkingDir.empty()) return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator, - isVolatile, IsText); + isVolatile, IsText, CASContents); SmallString<128> FilePath(Filename); FixupRelativePath(FilePath); return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator, - isVolatile, IsText); + isVolatile, IsText, CASContents); +} + +llvm::ErrorOr> +FileManager::getObjectRefForFileContent(const Twine &Filename) { + if (FileSystemOpts.WorkingDir.empty()) + return FS->getObjectRefForFileContent(Filename); + + SmallString<128> FilePath; + Filename.toVector(FilePath); + FixupRelativePath(FilePath); + return FS->getObjectRefForFileContent(FilePath); } /// getStatValue - Get the 'stat' information for the specified path, diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp index 16151c94464f9..c0a679dd3b67d 100644 --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -111,10 +111,14 @@ enum TokenKey : unsigned { KEYNOZOS = 0x4000000, KEYHLSL = 0x8000000, KEYFIXEDPOINT = 0x10000000, - KEYMAX = KEYFIXEDPOINT, // The maximum key + KEYBOUNDSSAFETY = 0x20000000, + KEYBOUNDSSAFETYATTRIBUTES = 0x40000000, + KEYMAX = KEYBOUNDSSAFETYATTRIBUTES, // The maximum key KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20, KEYALL = (KEYMAX | (KEYMAX - 1)) & ~KEYNOMS18 & ~KEYNOOPENCL & ~KEYNOZOS // KEYNOMS18, KEYNOOPENCL, KEYNOZOS are excluded. + // Exclude bounds-safety. + & ~KEYBOUNDSSAFETY & ~KEYBOUNDSSAFETYATTRIBUTES }; /// How a keyword is treated in the selected standard. This enum is ordered @@ -216,6 +220,12 @@ static KeywordStatus getKeywordStatusHelper(const LangOptions &LangOpts, return KS_Unknown; case KEYFIXEDPOINT: return LangOpts.FixedPoint ? KS_Enabled : KS_Disabled; + /* TO_UPSTREAM(BoundsSafety) ON*/ + case KEYBOUNDSSAFETY: + return LangOpts.BoundsSafety ? KS_Enabled : KS_Unknown; + case KEYBOUNDSSAFETYATTRIBUTES: + return LangOpts.BoundsSafetyAttributes ? KS_Enabled : KS_Unknown; + /* TO_UPSTREAM(BoundsSafety) OFF*/ default: llvm_unreachable("Unknown KeywordStatus flag"); } diff --git a/clang/lib/Basic/LangOptions.cpp b/clang/lib/Basic/LangOptions.cpp index e3037ec819add..26fa7f33205eb 100644 --- a/clang/lib/Basic/LangOptions.cpp +++ b/clang/lib/Basic/LangOptions.cpp @@ -240,3 +240,25 @@ LLVM_DUMP_METHOD void FPOptionsOverride::dump() { #include "clang/Basic/FPOptions.def" llvm::errs() << "\n"; } + +/* TO_UPSTREAM(BoundsSafety) ON*/ +LangOptionsBase::BoundsSafetyNewChecksMaskIntTy +LangOptionsBase::getBoundsSafetyNewChecksMaskForGroup(StringRef GroupName) { + const uint64_t Batch0 = BS_CHK_AccessSize | BS_CHK_IndirectCountUpdate | + BS_CHK_ReturnSize | BS_CHK_EndedByLowerBound | + BS_CHK_CompoundLiteralInit | BS_CHK_LibCAttributes | + BS_CHK_ArraySubscriptAgg; + + // Currently "all" and "batch_0" are the same + if (GroupName == "all" || GroupName == "batch_0") + return Batch0; + + // Invalid GroupName + return BS_CHK_None; +} + +LangOptionsBase::BoundsSafetyNewChecksMaskIntTy +LangOptionsBase::getDefaultBoundsSafetyNewChecksMask() { + return BS_CHK_None; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ diff --git a/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp index 6330bfe9418e8..fcfd7fb721fa7 100644 --- a/clang/lib/Basic/Module.cpp +++ b/clang/lib/Basic/Module.cpp @@ -42,9 +42,15 @@ Module::Module(ModuleConstructorTag, StringRef Name, HasIncompatibleModuleFile(false), IsAvailable(true), IsFromModuleFile(false), IsFramework(IsFramework), IsExplicit(IsExplicit), IsSystem(false), IsExternC(false), IsInferred(false), + IsInferredMissingFromUmbrellaHeader(false), InferSubmodules(false), InferExplicitSubmodules(false), InferExportWildcard(false), ConfigMacrosExhaustive(false), NoUndeclaredIncludes(false), ModuleMapIsPrivate(false), + + // SWIFT-SPECIFIC FIELDS HERE. Handling them separately helps avoid merge + // conflicts. + IsSwiftInferImportAsMember(false), + NamedModuleHasInit(true), NameVisibility(Hidden) { if (Parent) { IsAvailable = Parent->isAvailable(); @@ -305,6 +311,13 @@ bool Module::directlyUses(const Module *Requested) { if (!Requested->Parent && Requested->Name == "ptrauth") return true; + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Darwin is allowed is to use our builtin 'ptrcheck.h' and its accompanying + // module. + if (!Requested->Parent && Requested->Name == "ptrcheck") + return true; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (NoUndeclaredIncludes) UndeclaredUses.insert(Requested); @@ -476,6 +489,8 @@ void Module::print(raw_ostream &OS, unsigned Indent, bool Dump) const { OS << " [system]"; if (IsExternC) OS << " [extern_c]"; + if (IsSwiftInferImportAsMember) + OS << " [swift_infer_import_as_member]"; } OS << " {\n"; diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index ab13c32f6943e..421efc7b78c77 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -162,6 +162,7 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) { AllowAMDGPUUnsafeFPAtomics = false; HasUnalignedAccess = false; ARMCDECoprocMask = 0; + PointerAuthSupported = false; // Default to no types using fpret. RealTypeUsesObjCFPRetMask = 0; diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp index 3633bab6e0df9..4611b4f4b122d 100644 --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -275,6 +275,9 @@ AArch64TargetInfo::AArch64TargetInfo(const llvm::Triple &Triple, else if (Triple.getOS() == llvm::Triple::UnknownOS) this->MCountName = Opts.EABIVersion == llvm::EABI::GNU ? "\01_mcount" : "mcount"; + + if (Triple.getArchName() == "arm64e") + PointerAuthSupported = true; } StringRef AArch64TargetInfo::getABI() const { return ABI; } diff --git a/clang/lib/Basic/Targets/OSTargets.cpp b/clang/lib/Basic/Targets/OSTargets.cpp index e744e84a5b079..926c52b7d2664 100644 --- a/clang/lib/Basic/Targets/OSTargets.cpp +++ b/clang/lib/Basic/Targets/OSTargets.cpp @@ -11,6 +11,7 @@ #include "OSTargets.h" #include "clang/Basic/MacroBuilder.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" using namespace clang; @@ -29,6 +30,13 @@ void getAppleMachODefines(MacroBuilder &Builder, const LangOptions &Opts, if (Opts.Sanitize.has(SanitizerKind::Address)) Builder.defineMacro("_FORTIFY_SOURCE", "0"); + if (Opts.PointerAuthIntrinsics) + Builder.defineMacro("__PTRAUTH_INTRINSICS__"); + + if (Opts.PointerAuthABIVersionEncoded) + Builder.defineMacro("__ptrauth_abi_version__", + llvm::utostr(Opts.PointerAuthABIVersion)); + // Apple defines __weak, __strong, and __unsafe_unretained even in C mode. if (!Opts.ObjC) { // __weak is always defined, for use in blocks and with objc pointers. diff --git a/clang/lib/Basic/Targets/OSTargets.h b/clang/lib/Basic/Targets/OSTargets.h index d148b38d03c7c..769d525cf8263 100644 --- a/clang/lib/Basic/Targets/OSTargets.h +++ b/clang/lib/Basic/Targets/OSTargets.h @@ -353,6 +353,9 @@ class LLVM_LIBRARY_VISIBILITY LinuxTargetInfo : public OSTargetInfo { } else { Builder.defineMacro("__gnu_linux__"); } + + if (Triple.isMusl()) + Builder.defineMacro("__musl__", "1"); if (Opts.POSIXThreads) Builder.defineMacro("_REENTRANT"); if (Opts.CPlusPlus) diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index bc8d60f592548..1ac9d9944f042 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -406,12 +406,11 @@ class LLVM_LIBRARY_VISIBILITY X86TargetInfo : public TargetInfo { case CC_C: case CC_PreserveMost: case CC_Swift: + case CC_SwiftAsync: case CC_X86Pascal: case CC_IntelOclBicc: case CC_OpenCLKernel: return CCCR_OK; - case CC_SwiftAsync: - return CCCR_Error; default: return CCCR_Warning; } diff --git a/clang/lib/Basic/Version.cpp b/clang/lib/Basic/Version.cpp index 4823f566bd773..d56522785e2e0 100644 --- a/clang/lib/Basic/Version.cpp +++ b/clang/lib/Basic/Version.cpp @@ -125,4 +125,6 @@ std::string getClangFullCPPVersion() { return buf; } +unsigned getClangMajorVersionNumber() { return CLANG_VERSION_MAJOR; } + } // end namespace clang diff --git a/clang/lib/CAS/CASOptions.cpp b/clang/lib/CAS/CASOptions.cpp new file mode 100644 index 0000000000000..a2d30095424ac --- /dev/null +++ b/clang/lib/CAS/CASOptions.cpp @@ -0,0 +1,121 @@ +//===- CASOptions.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/CAS/CASOptions.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticCAS.h" +#include "llvm/CAS/ActionCache.h" +#include "llvm/CAS/BuiltinUnifiedCASDatabases.h" +#include "llvm/CAS/ObjectStore.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Path.h" + +using namespace clang; +using namespace llvm::cas; + +std::pair, + std::shared_ptr> +CASOptions::getOrCreateDatabases(DiagnosticsEngine &Diags, + bool CreateEmptyDBsOnFailure) const { + if (Cache.Config.IsFrozen) + return {Cache.CAS, Cache.AC}; + + if (auto E = initCache()) + Diags.Report(diag::err_cas_cannot_be_initialized) << toString(std::move(E)); + + if (!Cache.CAS && CreateEmptyDBsOnFailure) + Cache.CAS = llvm::cas::createInMemoryCAS(); + if (!Cache.AC && CreateEmptyDBsOnFailure) + Cache.AC = llvm::cas::createInMemoryActionCache(); + return {Cache.CAS, Cache.AC}; +} + +llvm::Expected, + std::shared_ptr>> +CASOptions::getOrCreateDatabases() const { + if (auto E = initCache()) + return std::move(E); + return std::pair{Cache.CAS, Cache.AC}; +} + +void CASOptions::freezeConfig(DiagnosticsEngine &Diags) { + if (Cache.Config.IsFrozen) + return; + + // Make sure the cache is initialized. + if (auto E = initCache()) + Diags.Report(diag::err_cas_cannot_be_initialized) << toString(std::move(E)); + + // Freeze the CAS and wipe out the visible config to hide it from future + // accesses. For example, future diagnostics cannot see this. Something that + // needs direct access to the CAS configuration will need to be + // scheduled/executed at a level that has access to the configuration. + auto &CurrentConfig = static_cast(*this); + CurrentConfig = CASConfiguration(); + CurrentConfig.IsFrozen = Cache.Config.IsFrozen = true; + + if (Cache.CAS) { + // Set the CASPath to the hash schema, since that leaks through CASContext's + // API and is observable. + CurrentConfig.CASPath = + Cache.CAS->getContext().getHashSchemaIdentifier().str(); + CurrentConfig.PluginPath.clear(); + CurrentConfig.PluginOptions.clear(); + } +} + +void CASOptions::ensurePersistentCAS() { + assert(!IsFrozen && "Expected to check for a persistent CAS before freezing"); + switch (getKind()) { + case UnknownCAS: + llvm_unreachable("Cannot ensure persistent CAS if it's unknown / frozen"); + case InMemoryCAS: + CASPath = "auto"; + break; + case OnDiskCAS: + break; + } +} + +llvm::Error CASOptions::initCache() const { + auto &CurrentConfig = static_cast(*this); + if (CurrentConfig == Cache.Config && Cache.CAS && Cache.AC) + return llvm::Error::success(); + + Cache.Config = CurrentConfig; + StringRef CASPath = Cache.Config.CASPath; + + if (!PluginPath.empty()) { + std::pair, std::shared_ptr> DBs; + if (llvm::Error E = + createPluginCASDatabases(PluginPath, CASPath, PluginOptions) + .moveInto(DBs)) { + return E; + } + std::tie(Cache.CAS, Cache.AC) = std::move(DBs); + return llvm::Error::success(); + } + + if (CASPath.empty()) { + Cache.CAS = llvm::cas::createInMemoryCAS(); + Cache.AC = llvm::cas::createInMemoryActionCache(); + return llvm::Error::success(); + } + + SmallString<256> PathBuf; + if (CASPath == "auto") { + getDefaultOnDiskCASPath(PathBuf); + CASPath = PathBuf; + } + std::pair, std::unique_ptr> DBs; + if (llvm::Error E = createOnDiskUnifiedCASDatabases(CASPath).moveInto(DBs)) + return E; + + std::tie(Cache.CAS, Cache.AC) = std::move(DBs); + return llvm::Error::success(); +} diff --git a/clang/lib/CAS/CMakeLists.txt b/clang/lib/CAS/CMakeLists.txt new file mode 100644 index 0000000000000..7f417c538d851 --- /dev/null +++ b/clang/lib/CAS/CMakeLists.txt @@ -0,0 +1,13 @@ +set(LLVM_LINK_COMPONENTS + Support + CAS + ) + +add_clang_library(clangCAS + CASOptions.cpp + IncludeTree.cpp + + LINK_LIBS + clangBasic + ) + diff --git a/clang/lib/CAS/IncludeTree.cpp b/clang/lib/CAS/IncludeTree.cpp new file mode 100644 index 0000000000000..b22b121c1ece9 --- /dev/null +++ b/clang/lib/CAS/IncludeTree.cpp @@ -0,0 +1,1101 @@ +//===- IncludeTree.cpp - Include-tree CAS graph -----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/CAS/IncludeTree.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallBitVector.h" +#include "llvm/CAS/ObjectStore.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/Error.h" +#include + +using namespace clang; +using namespace clang::cas; + +template +Expected IncludeTreeBase::create(ObjectStore &DB, + ArrayRef Refs, + ArrayRef Data) { + // Using 4 chars for less chance that it will randomly match a wrong node and + // makes the buffer 4 bytes "aligned". + static_assert(NodeT::getNodeKind().size() == 4, + "getNodeKind() should return 4 characters"); + SmallString<256> Buf{NodeT::getNodeKind()}; + Buf.reserve(Data.size() + NodeT::getNodeKind().size()); + Buf.append(Data.begin(), Data.end()); + auto Node = DB.store(Refs, Buf); + if (!Node) + return Node.takeError(); + auto Proxy = DB.getProxy(*Node); + if (!Proxy) + return Proxy.takeError(); + return NodeT(*Proxy); +} + +Expected +IncludeTree::SpuriousImport::create(ObjectStore &DB, ObjectRef ImportRef, + ObjectRef TreeRef) { + return IncludeTreeBase::create(DB, {ImportRef, TreeRef}, {}); +} + +Expected IncludeTree::File::create(ObjectStore &DB, + StringRef Filename, + ObjectRef Contents) { + auto PathRef = DB.storeFromString({}, Filename); + if (!PathRef) + return PathRef.takeError(); + std::array Refs{*PathRef, Contents}; + return IncludeTreeBase::create(DB, Refs, {}); +} + +Expected IncludeTree::getBaseFile() { + auto Node = getCAS().getProxy(getBaseFileRef()); + if (!Node) + return Node.takeError(); + return File(std::move(*Node)); +} + +Expected IncludeTree::getBaseFileInfo() { + auto File = getBaseFile(); + if (!File) + return File.takeError(); + return File->getFileInfo(); +} + +llvm::Error IncludeTree::forEachInclude( + llvm::function_ref)> Callback) { + size_t RefI = 0; + const size_t IncludeEnd = getNumIncludes(); + return forEachReference([&](ObjectRef Ref) -> llvm::Error { + if (RefI == 0) { + ++RefI; + return llvm::Error::success(); + } + size_t IncludeI = RefI - 1; + if (IncludeI >= IncludeEnd) + return llvm::Error::success(); + ++RefI; + auto Include = getIncludeNode(Ref, getIncludeKind(IncludeI)); + if (!Include) + return Include.takeError(); + return Callback({*Include, getIncludeOffset(IncludeI)}); + }); +} + +/// Write the bitset \p Bits to \p Writer, filling the final byte with zeros for +/// any unused values. Note: this does not store the size of the bitset. +static void writeBitSet(llvm::support::endian::Writer &Writer, + const llvm::SmallBitVector &Bits) { + uintptr_t Store; + ArrayRef BitWords = Bits.getData(Store); + size_t RemainingBitsCount = Bits.size(); + while (RemainingBitsCount > 0) { + if (BitWords.size() > 1) { + Writer.write(BitWords.front()); + BitWords = BitWords.drop_front(); + RemainingBitsCount -= sizeof(uintptr_t) * CHAR_BIT; + continue; + } + assert(RemainingBitsCount <= sizeof(uintptr_t) * CHAR_BIT); + uintptr_t LastWord = BitWords.front(); + unsigned BytesNum = RemainingBitsCount / CHAR_BIT; + if (RemainingBitsCount % CHAR_BIT != 0) + ++BytesNum; + while (BytesNum--) { + Writer.write(static_cast(LastWord & 0xFF)); + LastWord >>= CHAR_BIT; + } + break; + } +} + +Expected IncludeTree::create( + ObjectStore &DB, SrcMgr::CharacteristicKind FileCharacteristic, + ObjectRef BaseFile, ArrayRef Includes, + std::optional SubmoduleName, llvm::SmallBitVector Checks) { + // The data buffer is composed of + // 1. 1 byte for `CharacteristicKind` and IsSubmodule + // 2. `uint32_t` offset and `uint8_t` kind for each includes + // 3. variable number of bitset bytes for `Checks`. + + char Kind = FileCharacteristic; + assert(Kind == FileCharacteristic && (Kind & IsSubmoduleBit) == 0 && + "SrcMgr::CharacteristicKind too big!"); + if (SubmoduleName) + Kind |= IsSubmoduleBit; + + assert(File::isValid(DB, BaseFile)); + SmallVector Refs; + Refs.reserve(Includes.size() + 2); + Refs.push_back(BaseFile); + SmallString<64> Buffer; + Buffer.reserve(Includes.size() * sizeof(uint32_t) + 1); + + Buffer += Kind; + + llvm::raw_svector_ostream BufOS(Buffer); + llvm::support::endian::Writer Writer(BufOS, llvm::endianness::little); + + for (const auto &Include : Includes) { + assert((Include.Kind == NodeKind::Tree && + IncludeTree::isValid(DB, Include.Ref)) || + (Include.Kind == NodeKind::ModuleImport && + ModuleImport::isValid(DB, Include.Ref)) || + (Include.Kind == NodeKind::SpuriousImport && + SpuriousImport::isValid(DB, Include.Ref))); + Refs.push_back(Include.Ref); + Writer.write(Include.Offset); + static_assert(sizeof(uint8_t) == sizeof(Kind)); + Writer.write(static_cast(Include.Kind)); + } + + if (SubmoduleName) + Refs.push_back(*SubmoduleName); + + writeBitSet(Writer, Checks); + + return IncludeTreeBase::create(DB, Refs, Buffer); +} + +Expected IncludeTree::get(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) + return Node.takeError(); + if (!isValid(*Node)) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "not a IncludeTree node kind"); + return IncludeTree(std::move(*Node)); +} + +IncludeTree::NodeKind IncludeTree::getIncludeKind(size_t I) const { + assert(I < getNumIncludes()); + StringRef Data = dataSkippingFlags(); + assert(Data.size() >= (I + 1) * (sizeof(uint32_t) + 1)); + uint8_t K = *(Data.data() + I * (sizeof(uint32_t) + 1) + sizeof(uint32_t)); + return NodeKind(K); +} + +uint32_t IncludeTree::getIncludeOffset(size_t I) const { + assert(I < getNumIncludes()); + StringRef Data = dataSkippingFlags(); + assert(Data.size() >= (I + 1) * sizeof(uint32_t)); + uint32_t Offset = + llvm::support::endian::read( + Data.data() + I * (sizeof(uint32_t) + 1)); + return Offset; +} + +bool IncludeTree::getCheckResult(size_t I) const { + // Skip include offsets and CharacteristicKind. + StringRef Data = dataSkippingIncludes(); + unsigned ByteIndex = I / CHAR_BIT; + size_t RemainingIndex = I % CHAR_BIT; + uint8_t Bits = Data[ByteIndex]; + return Bits & (1 << RemainingIndex); +} + +Expected IncludeTree::getIncludeNode(size_t I) { + return getIncludeNode(getIncludeRef(I), getIncludeKind(I)); +} + +Expected IncludeTree::getIncludeNode(ObjectRef Ref, + NodeKind K) { + auto N = getCAS().getProxy(Ref); + if (!N) + return N.takeError(); + return Node(std::move(*N), K); +} + +bool IncludeTree::isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + if (Base.getNumReferences() == 0 || Base.getData().empty()) + return false; + unsigned NumIncludes = Base.getNumReferences() - 1; + if (Base.getData().front() & IsSubmoduleBit) + NumIncludes -= 1; + return Base.getData().size() >= NumIncludes * (sizeof(uint32_t) + 1) + 1; +} + +Expected +IncludeTree::ModuleImport::create(ObjectStore &DB, StringRef ModuleName, + bool VisibilityOnly) { + SmallString<64> Buffer; + Buffer.push_back((char)VisibilityOnly); + Buffer.append(ModuleName); + return IncludeTreeBase::create(DB, {}, Buffer); +} + +size_t IncludeTree::FileList::getNumFilesCurrentList() const { + return llvm::support::endian::read( + getData().data()); +} + +IncludeTree::FileList::FileSizeTy +IncludeTree::FileList::getFileSize(size_t I) const { + assert(I < getNumFilesCurrentList()); + StringRef Data = getData().drop_front(sizeof(uint32_t)); + assert(Data.size() >= (I + 1) * sizeof(FileSizeTy)); + return llvm::support::endian::read( + Data.data() + I * sizeof(FileSizeTy)); +} + +llvm::Error IncludeTree::FileList::forEachFileImpl( + llvm::DenseSet &Seen, + llvm::function_ref Callback) { + size_t Next = 0; + size_t FileCount = getNumFilesCurrentList(); + return forEachReference([&](ObjectRef Ref) -> llvm::Error { + size_t Index = Next++; + if (!Seen.insert(Ref).second) + return llvm::Error::success(); + + if (Index < FileCount) { + auto Include = File::get(getCAS(), Ref); + if (!Include) + return Include.takeError(); + return Callback(std::move(*Include), getFileSize(Index)); + } + + // Otherwise, it's a chained FileList. + auto Proxy = getCAS().getProxy(Ref); + if (!Proxy) + return Proxy.takeError(); + FileList FL(std::move(*Proxy)); + return FL.forEachFileImpl(Seen, Callback); + }); +} + +llvm::Error IncludeTree::FileList::forEachFile( + llvm::function_ref Callback) { + llvm::DenseSet Seen; + return forEachFileImpl(Seen, Callback); +} + +Expected +IncludeTree::FileList::create(ObjectStore &DB, ArrayRef Files, + ArrayRef FileLists) { + SmallVector Refs; + Refs.reserve(Files.size() + FileLists.size()); + SmallString<256> Buffer; + Buffer.reserve(sizeof(uint32_t) + Files.size() * sizeof(FileSizeTy)); + + llvm::raw_svector_ostream BufOS(Buffer); + llvm::support::endian::Writer Writer(BufOS, llvm::endianness::little); + Writer.write(static_cast(Files.size())); + + for (const FileEntry &Entry : Files) { + assert(File::isValid(DB, Entry.FileRef)); + Refs.push_back(Entry.FileRef); + Writer.write(Entry.Size); + } + + Refs.append(FileLists.begin(), FileLists.end()); + + return IncludeTreeBase::create(DB, Refs, Buffer); +} + +Expected IncludeTree::FileList::get(ObjectStore &DB, + ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) + return Node.takeError(); + if (!isValid(*Node)) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "not a IncludeFileList node kind"); + return FileList(std::move(*Node)); +} + +bool IncludeTree::FileList::isValid(const ObjectProxy &Node) { + if (!IncludeTreeBase::isValid(Node)) + return false; + IncludeTreeBase Base(Node); + StringRef Data = Base.getData(); + if (Data.size() < sizeof(uint32_t)) + return false; + unsigned NumFiles = + llvm::support::endian::read(Data.data()); + return NumFiles != 0 && NumFiles <= Base.getNumReferences() && + Data.size() == sizeof(uint32_t) + NumFiles * sizeof(FileSizeTy); +} + +static constexpr uint16_t ModuleFlagFramework = 1 << 0; +static constexpr uint16_t ModuleFlagExplicit = 1 << 1; +static constexpr uint16_t ModuleFlagExternC = 1 << 2; +static constexpr uint16_t ModuleFlagSystem = 1 << 3; +static constexpr uint16_t ModuleFlagInferSubmodules = 1 << 4; +static constexpr uint16_t ModuleFlagInferExplicitSubmodules = 1 << 5; +static constexpr uint16_t ModuleFlagInferInferExportWildcard = 1 << 6; +static constexpr uint16_t ModuleFlagHasExports = 1 << 7; +static constexpr uint16_t ModuleFlagHasLinkLibraries = 1 << 8; +static constexpr uint16_t ModuleFlagUseExportAsModuleLinkName = 1 << 9; + +IncludeTree::Module::ModuleFlags IncludeTree::Module::getFlags() const { + uint16_t Raw = rawFlags(); + ModuleFlags Flags; + Flags.IsFramework = Raw & ModuleFlagFramework; + Flags.IsExplicit = Raw & ModuleFlagExplicit; + Flags.IsExternC = Raw & ModuleFlagExternC; + Flags.IsSystem = Raw & ModuleFlagSystem; + Flags.InferSubmodules = Raw & ModuleFlagInferSubmodules; + Flags.InferExplicitSubmodules = Raw & ModuleFlagInferExplicitSubmodules; + Flags.InferExportWildcard = Raw & ModuleFlagInferInferExportWildcard; + Flags.UseExportAsModuleLinkName = Raw & ModuleFlagUseExportAsModuleLinkName; + return Flags; +} + +size_t IncludeTree::Module::getNumSubmodules() const { + size_t Count = getNumReferences(); + if (hasExports()) + Count -= 1; + if (hasLinkLibraries()) + Count -= 1; + return Count; +} + +llvm::Error IncludeTree::Module::forEachSubmodule( + llvm::function_ref CB) { + size_t Count = getNumSubmodules(); + return forEachReference([&](ObjectRef Ref) -> llvm::Error { + if (Count == 0) + return llvm::Error::success(); + Count -= 1; + auto Node = getCAS().getProxy(Ref); + if (!Node) + return Node.takeError(); + return CB(Module(*Node)); + }); +} + +Expected +IncludeTree::Module::create(ObjectStore &DB, StringRef ModuleName, + StringRef ExportAs, ModuleFlags Flags, + ArrayRef Submodules, + std::optional ExportList, + std::optional LinkLibraries) { + // Data: + // - 2 bytes for Flags + // - ModuleName (String, null-terminated) + // - (optional) ExportAsModule + // Refs: + // - Submodules (IncludeTreeModule) + // - (optional) ExportList + // - (optional) LinkLibaryList + + uint16_t RawFlags = 0; + if (Flags.IsFramework) + RawFlags |= ModuleFlagFramework; + if (Flags.IsExplicit) + RawFlags |= ModuleFlagExplicit; + if (Flags.IsExternC) + RawFlags |= ModuleFlagExternC; + if (Flags.IsSystem) + RawFlags |= ModuleFlagSystem; + if (Flags.InferSubmodules) + RawFlags |= ModuleFlagInferSubmodules; + if (Flags.InferExplicitSubmodules) + RawFlags |= ModuleFlagInferExplicitSubmodules; + if (Flags.InferExportWildcard) + RawFlags |= ModuleFlagInferInferExportWildcard; + if (ExportList) + RawFlags |= ModuleFlagHasExports; + if (LinkLibraries) + RawFlags |= ModuleFlagHasLinkLibraries; + if (Flags.UseExportAsModuleLinkName) + RawFlags |= ModuleFlagUseExportAsModuleLinkName; + + SmallString<64> Buffer; + llvm::raw_svector_ostream BufOS(Buffer); + llvm::support::endian::Writer Writer(BufOS, llvm::endianness::little); + Writer.write(RawFlags); + + Buffer.append(ModuleName); + Buffer.append(StringRef("\0", 1)); + Buffer.append(ExportAs); + + SmallVector Refs(Submodules); + if (ExportList) + Refs.push_back(*ExportList); + if (LinkLibraries) + Refs.push_back(*LinkLibraries); + + return IncludeTreeBase::create(DB, Refs, Buffer); +} + +uint16_t IncludeTree::Module::rawFlags() const { + return llvm::support::endian::read( + getData().data()); +} + +bool IncludeTree::Module::hasExports() const { + return rawFlags() & ModuleFlagHasExports; +} +bool IncludeTree::Module::hasLinkLibraries() const { + return rawFlags() & ModuleFlagHasLinkLibraries; +} + +std::optional IncludeTree::Module::getExportsIndex() const { + if (hasExports()) + return getNumReferences() - (hasLinkLibraries() ? 2 : 1); + return std::nullopt; +} +std::optional IncludeTree::Module::getLinkLibrariesIndex() const { + if (hasLinkLibraries()) + return getNumReferences() - 1; + return std::nullopt; +} + +Expected> +IncludeTree::Module::getExports() { + if (auto Ref = getExportsRef()) { + auto N = getCAS().getProxy(*Ref); + if (!N) + return N.takeError(); + return ExportList(std::move(*N)); + } + return std::nullopt; +} + +/// The list of modules that this submodule re-exports. +Expected> +IncludeTree::Module::getLinkLibraries() { + if (auto Ref = getLinkLibrariesRef()) { + auto N = getCAS().getProxy(*Ref); + if (!N) + return N.takeError(); + return LinkLibraryList(std::move(*N)); + } + return std::nullopt; +} + +bool IncludeTree::Module::ExportList::hasGlobalWildcard() const { + // The bit after explicit exports is global. + return exportHasWildcard(getNumExplicitExports()); +} +bool IncludeTree::Module::ExportList::exportHasWildcard(size_t I) const { + assert(I < getNumExplicitExports() + 1); + unsigned ByteIndex = I / CHAR_BIT; + size_t RemainingIndex = I % CHAR_BIT; + uint8_t Bits = getData()[ByteIndex]; + return Bits & (1 << RemainingIndex); +} +Expected +IncludeTree::Module::ExportList::getExplicitExport(size_t I) { + Expected Name = getCAS().getProxy(getReference(I)); + if (!Name) + return Name.takeError(); + return Export{Name->getData(), exportHasWildcard(I)}; +} +llvm::Error IncludeTree::Module::ExportList::forEachExplicitExport( + llvm::function_ref CB) { + size_t ExportI = 0; + return forEachReference([&](ObjectRef Ref) { + Expected Name = getCAS().getProxy(Ref); + if (!Name) + return Name.takeError(); + return CB(Export{Name->getData(), exportHasWildcard(ExportI)}); + }); +} +Expected +IncludeTree::Module::ExportList::create(ObjectStore &DB, + ArrayRef Exports, + bool GlobalWildcard) { + // Data: + // - 1 bit per explicit export for wildcard + // - 1 bit for global wildcard + // Refs: export names + SmallString<64> Buffer; + llvm::raw_svector_ostream BufOS(Buffer); + llvm::support::endian::Writer Writer(BufOS, llvm::endianness::little); + SmallVector Refs; + llvm::SmallBitVector WildcardBits; + for (Export E : Exports) { + auto Ref = DB.storeFromString({}, E.ModuleName); + if (!Ref) + return Ref.takeError(); + Refs.push_back(*Ref); + WildcardBits.push_back(E.Wildcard); + } + WildcardBits.push_back(GlobalWildcard); + writeBitSet(Writer, WildcardBits); + + return IncludeTreeBase::create(DB, Refs, Buffer); +} + +bool IncludeTree::Module::LinkLibraryList::isFramework(size_t I) const { + assert(I < getNumLibraries()); + unsigned ByteIndex = I / CHAR_BIT; + size_t RemainingIndex = I % CHAR_BIT; + uint8_t Bits = getData()[ByteIndex]; + return Bits & (1 << RemainingIndex); +} +llvm::Error IncludeTree::Module::LinkLibraryList::forEachLinkLibrary( + llvm::function_ref CB) { + size_t I = 0; + return forEachReference([&](ObjectRef Ref) { + auto Name = getCAS().getProxy(getLibraryNameRef(I)); + if (!Name) + return Name.takeError(); + return CB({Name->getData(), isFramework(I++)}); + }); +} +Expected +IncludeTree::Module::LinkLibraryList::create(ObjectStore &DB, + ArrayRef Libraries) { + // Data: + // - 1 bit per library for IsFramework + // Refs: library names + SmallString<64> Buffer; + llvm::raw_svector_ostream BufOS(Buffer); + llvm::support::endian::Writer Writer(BufOS, llvm::endianness::little); + SmallVector Refs; + llvm::SmallBitVector FrameworkBits; + for (LinkLibrary L : Libraries) { + auto Ref = DB.storeFromString({}, L.Library); + if (!Ref) + return Ref.takeError(); + Refs.push_back(*Ref); + FrameworkBits.push_back(L.IsFramework); + } + writeBitSet(Writer, FrameworkBits); + + return IncludeTreeBase::create(DB, Refs, Buffer); +} + +Expected +IncludeTree::ModuleMap::create(ObjectStore &DB, ArrayRef Modules) { + return IncludeTreeBase::create(DB, Modules, {}); +} + +llvm::Error IncludeTree::ModuleMap::forEachModule( + llvm::function_ref CB) { + return forEachReference([&](ObjectRef Ref) { + auto N = getCAS().getProxy(Ref); + if (!N) + return N.takeError(); + return CB(Module(std::move(*N))); + }); +} + +static constexpr char HasPCH = 1; +static constexpr char HasModuleMap = 1 << 1; +static constexpr char HasAPINotes = 1 << 2; + +Expected +IncludeTreeRoot::create(ObjectStore &DB, ObjectRef MainFileTree, + ObjectRef FileList, std::optional PCHRef, + std::optional ModuleMapRef, + std::optional APINotesRef) { + assert(IncludeTree::isValid(DB, MainFileTree)); + assert(IncludeTree::FileList::isValid(DB, FileList)); + assert(!ModuleMapRef || IncludeTree::ModuleMap::isValid(DB, *ModuleMapRef)); + + std::array Data = {0}; + if (PCHRef) + Data[0] |= HasPCH; + if (ModuleMapRef) + Data[0] |= HasModuleMap; + if (APINotesRef) + Data[0] |= HasAPINotes; + + SmallVector Refs = {MainFileTree, FileList}; + if (PCHRef) + Refs.push_back(*PCHRef); + if (ModuleMapRef) + Refs.push_back(*ModuleMapRef); + if (APINotesRef) + Refs.push_back(*APINotesRef); + + return IncludeTreeBase::create(DB, Refs, Data); +} + +Expected IncludeTreeRoot::get(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) + return Node.takeError(); + if (!isValid(*Node)) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "not a IncludeTreeRoot node kind"); + return IncludeTreeRoot(std::move(*Node)); +} + +std::optional IncludeTreeRoot::getPCHRefIndex() const { + if (getData()[0] & HasPCH) + return 2; + return std::nullopt; +} +std::optional IncludeTreeRoot::getModuleMapRefIndex() const { + if (getData()[0] & HasModuleMap) + return (getData()[0] & HasPCH) ? 3u : 2u; + return std::nullopt; +} +std::optional IncludeTreeRoot::getAPINotesRefIndex() const { + if (getData()[0] & HasAPINotes) + return 2 + (getPCHRefIndex() ? 1 : 0) + (getModuleMapRefIndex() ? 1 : 0); + return std::nullopt; +} + +llvm::Error IncludeTree::File::print(llvm::raw_ostream &OS, unsigned Indent) { + auto Filename = getFilename(); + if (!Filename) + return Filename.takeError(); + OS.indent(Indent) << Filename->getData() << ' '; + getCAS().getID(getContentsRef()).print(OS); + OS << '\n'; + return llvm::Error::success(); +} + +llvm::Error IncludeTree::print(llvm::raw_ostream &OS, unsigned Indent) { + auto IncludeBy = getBaseFile(); + if (!IncludeBy) + return IncludeBy.takeError(); + if (llvm::Error E = IncludeBy->print(OS)) + return E; + + auto Submodule = getSubmoduleName(); + if (!Submodule) + return Submodule.takeError(); + if (*Submodule) + OS.indent(Indent) << "Submodule: " << **Submodule << '\n'; + + llvm::SourceMgr SM; + auto Blob = IncludeBy->getContents(); + if (!Blob) + return Blob.takeError(); + auto MemBuf = llvm::MemoryBuffer::getMemBuffer(Blob->getData()); + unsigned BufID = SM.AddNewSourceBuffer(std::move(MemBuf), llvm::SMLoc()); + + return forEachInclude([&](std::pair Include) -> llvm::Error { + llvm::SMLoc Loc = llvm::SMLoc::getFromPointer( + SM.getMemoryBuffer(BufID)->getBufferStart() + Include.second); + auto LineCol = SM.getLineAndColumn(Loc); + OS.indent(Indent) << LineCol.first << ':' << LineCol.second << ' '; + return Include.first.print(OS, Indent + 2); + }); +} + +llvm::Error IncludeTree::FileList::print(llvm::raw_ostream &OS, + unsigned Indent) { + return forEachFile([&](File File, FileSizeTy) -> llvm::Error { + return File.print(OS, Indent); + }); +} + +llvm::Error IncludeTree::ModuleImport::print(llvm::raw_ostream &OS, + unsigned Indent, char End) { + if (visibilityOnly()) + OS << "(Module for visibility only) "; + else + OS << "(Module) "; + OS << getModuleName() << End; + return llvm::Error::success(); +} + +llvm::Error IncludeTree::SpuriousImport::print(llvm::raw_ostream &OS, + unsigned Indent) { + auto MI = getModuleImport(); + if (!MI) + return MI.takeError(); + auto IT = getIncludeTree(); + if (!IT) + return IT.takeError(); + OS << "(Spurious import) "; + if (llvm::Error E = MI->print(OS, Indent, /*End=*/' ')) + return E; + if (llvm::Error E = IT->print(OS, Indent)) + return E; + return llvm::Error::success(); +} + +llvm::Error IncludeTree::Node::print(llvm::raw_ostream &OS, unsigned Indent) { + switch (K) { + case NodeKind::Tree: + return getIncludeTree().print(OS, Indent); + case NodeKind::ModuleImport: + return getModuleImport().print(OS, Indent); + case NodeKind::SpuriousImport: + return getSpuriousImport().print(OS, Indent); + } +} + +llvm::Error IncludeTree::Module::print(llvm::raw_ostream &OS, unsigned Indent) { + OS.indent(Indent) << getName(); + ModuleFlags Flags = getFlags(); + if (Flags.IsFramework) + OS << " (framework)"; + if (Flags.IsExplicit) + OS << " (explicit)"; + if (Flags.IsExternC) + OS << " (extern_c)"; + if (Flags.IsSystem) + OS << " (system)"; + OS << '\n'; + auto ExportAs = getExportAsModule(); + if (!ExportAs.empty()) + OS << " export_as " << ExportAs << "\n"; + if (Flags.InferSubmodules) { + if (Flags.InferExplicitSubmodules) + OS << " explicit module *"; + else + OS << " module *"; + if (Flags.InferExportWildcard) + OS << " { export * }"; + OS << '\n'; + } + auto ExportList = getExports(); + if (!ExportList) + return ExportList.takeError(); + if (*ExportList) + if (llvm::Error E = (*ExportList)->print(OS, Indent + 2)) + return E; + auto LinkLibraries = getLinkLibraries(); + if (!LinkLibraries) + return LinkLibraries.takeError(); + if (*LinkLibraries) + if (llvm::Error E = (*LinkLibraries)->print(OS, Indent + 2)) + return E; + return forEachSubmodule( + [&](Module Sub) { return Sub.print(OS, Indent + 2); }); +} +llvm::Error IncludeTree::Module::ExportList::print(llvm::raw_ostream &OS, + unsigned Indent) { + if (hasGlobalWildcard()) + OS.indent(Indent) << "export *\n"; + return forEachExplicitExport([&](Export E) { + OS.indent(Indent) << "export " << E.ModuleName; + if (E.Wildcard) + OS << ".*"; + OS << '\n'; + return llvm::Error::success(); + }); +} + +llvm::Error IncludeTree::Module::LinkLibraryList::print(llvm::raw_ostream &OS, + unsigned Indent) { + return forEachLinkLibrary([&](LinkLibrary E) { + OS.indent(Indent) << "link " << E.Library; + if (E.IsFramework) + OS << " (framework)"; + OS << '\n'; + return llvm::Error::success(); + }); +} + +llvm::Error IncludeTree::ModuleMap::print(llvm::raw_ostream &OS, + unsigned Indent) { + return forEachModule([&](Module M) { return M.print(OS, Indent); }); +} + +llvm::Expected +IncludeTree::APINotes::create(ObjectStore &DB, + ArrayRef APINoteList) { + assert(APINoteList.size() < 2 && "Too many APINotes added"); + return IncludeTreeBase::create(DB, APINoteList, {}); +} + +llvm::Expected +IncludeTree::APINotes::get(ObjectStore &DB, ObjectRef Ref) { + auto Node = DB.getProxy(Ref); + if (!Node) + return Node.takeError(); + if (!isValid(*Node)) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "not an APINodes node kind"); + return APINotes(std::move(*Node)); +} + +llvm::Error IncludeTree::APINotes::print(llvm::raw_ostream &OS, + unsigned Indent) { + return forEachReference([&](ObjectRef Ref) -> llvm::Error { + auto Node = getCAS().getProxy(Ref); + if (!Node) + return Node.takeError(); + OS.indent(Indent) << Node->getID() << "\n"; + OS.indent(Indent) << Node->getData() << "\n"; + return llvm::Error::success(); + }); +} + +llvm::Error IncludeTree::APINotes::forEachAPINotes( + llvm::function_ref CB) { + return forEachReference([&](ObjectRef Ref) { + auto N = getCAS().getProxy(Ref); + if (!N) + return N.takeError(); + return CB(N->getData()); + }); +} + +llvm::Error IncludeTreeRoot::print(llvm::raw_ostream &OS, unsigned Indent) { + std::optional PCH; + if (llvm::Error E = getPCH().moveInto(PCH)) + return E; + if (PCH) { + OS.indent(Indent) << "(PCH) "; + if (llvm::Error E = PCH->print(OS)) + return E; + } + std::optional MainTree; + if (llvm::Error E = getMainFileTree().moveInto(MainTree)) + return E; + if (llvm::Error E = MainTree->print(OS.indent(Indent), Indent)) + return E; + std::optional ModuleMap; + if (llvm::Error E = getModuleMap().moveInto(ModuleMap)) + return E; + if (ModuleMap) { + OS.indent(Indent) << "Module Map:\n"; + if (llvm::Error E = ModuleMap->print(OS, Indent)) + return E; + } + OS.indent(Indent) << "Files:\n"; + std::optional List; + if (llvm::Error E = getFileList().moveInto(List)) + return E; + std::optional APINotes; + if (llvm::Error E = getAPINotes().moveInto(APINotes)) + return E; + if (APINotes) { + OS.indent(Indent) << "APINotes:\n"; + if (llvm::Error E = APINotes->print(OS, Indent)) + return E; + } + return List->print(OS, Indent); +} + +namespace { +/// An implementation of a \p vfs::FileSystem that supports the simple queries +/// of the preprocessor, for creating \p FileEntries using a file path, while +/// "replaying" an \p IncludeTreeRoot. It is not intended to be a complete +/// implementation of a file system. +class IncludeTreeFileSystem : public llvm::vfs::FileSystem { + llvm::cas::ObjectStore &CAS; + +public: + class IncludeTreeFile : public llvm::vfs::File { + llvm::vfs::Status Stat; + StringRef Contents; + cas::ObjectRef ContentsRef; + + public: + IncludeTreeFile(llvm::vfs::Status Stat, StringRef Contents, + cas::ObjectRef ContentsRef) + : Stat(std::move(Stat)), Contents(Contents), + ContentsRef(std::move(ContentsRef)) {} + + llvm::ErrorOr status() override { return Stat; } + + llvm::ErrorOr> + getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, + bool IsVolatile) override { + SmallString<256> NameBuf; + return llvm::MemoryBuffer::getMemBuffer(Contents, + Name.toStringRef(NameBuf)); + } + + llvm::ErrorOr> + getObjectRefForContent() override { + return ContentsRef; + } + + std::error_code close() override { return std::error_code(); } + }; + + explicit IncludeTreeFileSystem(llvm::cas::ObjectStore &CAS) : CAS(CAS) {} + + struct FileEntry { + cas::ObjectRef ContentsRef; + IncludeTree::FileList::FileSizeTy Size; + llvm::sys::fs::UniqueID UniqueID; + }; + + struct MaterializedFile : FileEntry { + StringRef Contents; + + MaterializedFile(StringRef Contents, FileEntry FE) + : FileEntry(std::move(FE)), Contents(Contents) {} + }; + + llvm::BumpPtrAllocator Alloc; + llvm::StringMap Files{Alloc}; + llvm::StringMap + Directories{Alloc}; + + llvm::ErrorOr status(const Twine &Path) override { + SmallString<128> Filename; + getPath(Path, Filename); + auto FileEntry = Files.find(Filename); + if (FileEntry != Files.end()) { + return makeStatus(Filename, FileEntry->second.Size, + FileEntry->second.UniqueID, + llvm::sys::fs::file_type::regular_file); + } + + // Also check whether this is a parent directory status query. + auto DirEntry = Directories.find(Filename); + if (DirEntry != Directories.end()) { + return makeStatus(Filename, /*Size*/ 0, DirEntry->second, + llvm::sys::fs::file_type::directory_file); + } + + return llvm::errorToErrorCode(fileError(Filename)); + } + + llvm::ErrorOr> + openFileForRead(const Twine &Path) override { + SmallString<128> Filename; + getPath(Path, Filename); + auto MaterializedFile = materialize(Filename); + if (!MaterializedFile) + return llvm::errorToErrorCode(MaterializedFile.takeError()); + llvm::vfs::Status Stat = makeStatus( + Filename, MaterializedFile->Contents.size(), MaterializedFile->UniqueID, + llvm::sys::fs::file_type::regular_file); + return std::make_unique( + std::move(Stat), MaterializedFile->Contents, + std::move(MaterializedFile->ContentsRef)); + } + + Expected materialize(StringRef Filename) { + auto Entry = Files.find(Filename); + if (Entry == Files.end()) + return fileError(Filename); + auto ContentsBlob = CAS.getProxy(Entry->second.ContentsRef); + if (!ContentsBlob) + return ContentsBlob.takeError(); + + return MaterializedFile{ContentsBlob->getData(), Entry->second}; + } + + /// Produce a filename compatible with our StringMaps. See comment in + /// \c createIncludeTreeFileSystem. + void getPath(const Twine &Path, SmallVectorImpl &Out) { + Path.toVector(Out); + // Strip dots, but do not eliminate a path consisting only of '.' + if (Out.size() != 1) + llvm::sys::path::remove_dots(Out); + } + + static llvm::vfs::Status makeStatus(StringRef Filename, uint64_t Size, + llvm::sys::fs::UniqueID UniqueID, + llvm::sys::fs::file_type Type) { + const llvm::sys::fs::perms Permissions = + llvm::sys::fs::perms::all_read | llvm::sys::fs::perms::owner_write; + return llvm::vfs::Status(Filename, UniqueID, llvm::sys::TimePoint<>(), + /*User=*/0, + /*Group=*/0, Size, Type, Permissions); + } + + static llvm::Error fileError(StringRef Filename) { + return llvm::createFileError( + Filename, + llvm::createStringError(std::errc::no_such_file_or_directory, + "filename not part of include tree list")); + } + + llvm::vfs::directory_iterator dir_begin(const Twine &Dir, + std::error_code &EC) override { + // Return no_such_file_or_directory so llvm::vfs::OverlayFileSystem can + // ignore this layer when iterating directories. + EC = llvm::errc::no_such_file_or_directory; + return llvm::vfs::directory_iterator(); + } + llvm::ErrorOr getCurrentWorkingDirectory() const override { + return llvm::errc::operation_not_permitted; + } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override { + return llvm::errc::operation_not_permitted; + } +}; +} // namespace + +static llvm::Error diagnoseFileChange(IncludeTree::File F, ObjectRef Content) { + auto FilenameBlob = F.getFilename(); + if (!FilenameBlob) + return FilenameBlob.takeError(); + cas::ObjectStore &DB = F.getCAS(); + std::string Filename(FilenameBlob->getData()); + std::string OldID = DB.getID(Content).toString(); + std::string NewID = DB.getID(F.getContentsRef()).toString(); + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "file '%s' changed during build; include-tree " + "contents changed from %s to %s", + Filename.c_str(), OldID.c_str(), + NewID.c_str()); +} + +Expected> +cas::createIncludeTreeFileSystem(IncludeTreeRoot &Root) { + auto FileList = Root.getFileList(); + if (!FileList) + return FileList.takeError(); + + std::vector Files; + Files.reserve(FileList->getNumReferences()); + + if (auto Err = FileList->forEachFile( + [&](IncludeTree::File File, IncludeTree::FileList::FileSizeTy Size) { + Files.push_back({File.getRef(), Size}); + return llvm::Error::success(); + })) + return std::move(Err); + + return createIncludeTreeFileSystem(Root.getCAS(), Files); +} + +Expected> +cas::createIncludeTreeFileSystem( + llvm::cas::ObjectStore &CAS, + llvm::ArrayRef List) { + // Map from FilenameRef to ContentsRef. + llvm::DenseMap SeenContents; + + IntrusiveRefCntPtr IncludeTreeFS = + new IncludeTreeFileSystem(CAS); + + for (auto &Entry : List) { + auto File = IncludeTree::File::get(CAS, Entry.FileRef); + + if (!File) + return File.takeError(); + + auto InsertPair = SeenContents.insert( + std::make_pair(File->getFilenameRef(), File->getContentsRef())); + if (!InsertPair.second) { + if (InsertPair.first->second != File->getContentsRef()) + return diagnoseFileChange(*File, InsertPair.first->second); + continue; + } + + auto FilenameBlob = File->getFilename(); + if (!FilenameBlob) + return FilenameBlob.takeError(); + + SmallString<128> Filename(FilenameBlob->getData()); + // Strip './' in the filename to match the behaviour of ASTWriter; we + // also strip './' in IncludeTreeFileSystem::getPath. + assert(Filename != "."); + llvm::sys::path::remove_dots(Filename); + + StringRef DirName = llvm::sys::path::parent_path(Filename); + if (DirName.empty()) + DirName = "."; + auto &DirEntry = IncludeTreeFS->Directories[DirName]; + if (DirEntry == llvm::sys::fs::UniqueID()) { + DirEntry = llvm::vfs::getNextVirtualUniqueID(); + } + + IncludeTreeFS->Files.insert(std::make_pair( + Filename, + IncludeTreeFileSystem::FileEntry{File->getContentsRef(), Entry.Size, + llvm::vfs::getNextVirtualUniqueID()})); + } + + return IncludeTreeFS; +} diff --git a/clang/lib/CMakeLists.txt b/clang/lib/CMakeLists.txt index 4f2218b583e41..ccbcbd73779d4 100644 --- a/clang/lib/CMakeLists.txt +++ b/clang/lib/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(Headers) add_subdirectory(Basic) +add_subdirectory(CAS) add_subdirectory(APINotes) add_subdirectory(Lex) add_subdirectory(Parse) @@ -19,6 +20,7 @@ add_subdirectory(FrontendTool) add_subdirectory(Tooling) add_subdirectory(DirectoryWatcher) add_subdirectory(Index) +add_subdirectory(IndexDataStore) add_subdirectory(IndexSerialization) add_subdirectory(InstallAPI) add_subdirectory(StaticAnalyzer) diff --git a/clang/lib/CodeGen/ABIInfo.cpp b/clang/lib/CodeGen/ABIInfo.cpp index d981d69913632..59b2b17788380 100644 --- a/clang/lib/CodeGen/ABIInfo.cpp +++ b/clang/lib/CodeGen/ABIInfo.cpp @@ -283,6 +283,15 @@ bool SwiftABIInfo::shouldPassIndirectly(ArrayRef ComponentTys, return occupiesMoreThan(ComponentTys, /*total=*/4); } +bool SwiftABIInfo::shouldReturnTypedErrorIndirectly( + ArrayRef ComponentTys) const { + for (llvm::Type *type : ComponentTys) { + if (!type->isIntegerTy() && !type->isPointerTy()) + return true; + } + return shouldPassIndirectly(ComponentTys, /*AsReturnValue=*/true); +} + bool SwiftABIInfo::isLegalVectorType(CharUnits VectorSize, llvm::Type *EltTy, unsigned NumElts) const { // The default implementation of this assumes that the target guarantees diff --git a/clang/lib/CodeGen/ABIInfo.h b/clang/lib/CodeGen/ABIInfo.h index 9c7029c99bd44..b253696f502d8 100644 --- a/clang/lib/CodeGen/ABIInfo.h +++ b/clang/lib/CodeGen/ABIInfo.h @@ -162,6 +162,9 @@ class SwiftABIInfo { /// Returns true if swifterror is lowered to a register by the target ABI. bool isSwiftErrorInRegister() const { return SwiftErrorInRegister; }; + + virtual bool + shouldReturnTypedErrorIndirectly(ArrayRef ComponentTys) const; }; } // end namespace CodeGen } // end namespace clang diff --git a/clang/lib/CodeGen/BackendConsumer.h b/clang/lib/CodeGen/BackendConsumer.h index ad3adfca36785..57549c866739b 100644 --- a/clang/lib/CodeGen/BackendConsumer.h +++ b/clang/lib/CodeGen/BackendConsumer.h @@ -33,7 +33,9 @@ class BackendConsumer : public ASTConsumer { const CodeGenOptions &CodeGenOpts; const TargetOptions &TargetOpts; const LangOptions &LangOpts; + const CASOptions &CASOpts; // MCCAS std::unique_ptr AsmOutStream; + std::unique_ptr CasIDStream; ASTContext *Context = nullptr; IntrusiveRefCntPtr FS; @@ -72,9 +74,11 @@ class BackendConsumer : public ASTConsumer { public: BackendConsumer(CompilerInstance &CI, BackendAction Action, IntrusiveRefCntPtr VFS, + const CASOptions &CASOpts, llvm::LLVMContext &C, SmallVector LinkModules, StringRef InFile, std::unique_ptr OS, CoverageSourceInfo *CoverageInfo, + std::unique_ptr CasIDOS = nullptr, llvm::Module *CurLinkModule = nullptr); llvm::Module *getModule() const; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index c9ceb49ce5ceb..6030cd12eb68a 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -13,6 +13,7 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/TargetOptions.h" +#include "clang/CAS/CASOptions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/Utils.h" #include "clang/Lex/HeaderSearchOptions.h" @@ -76,6 +77,7 @@ #include "llvm/Transforms/Instrumentation/PGOInstrumentation.h" #include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" #include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h" +#include "llvm/Transforms/Instrumentation/SoftPointerAuth.h" #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/Instrumentation/TypeSanitizer.h" @@ -149,6 +151,7 @@ class EmitAssemblyHelper { const CodeGenOptions &CodeGenOpts; const clang::TargetOptions &TargetOpts; const LangOptions &LangOpts; + const CASOptions &CASOpts; // MCCAS llvm::Module *TheModule; IntrusiveRefCntPtr VFS; @@ -177,7 +180,8 @@ class EmitAssemblyHelper { /// /// \return True on success. bool AddEmitPasses(legacy::PassManager &CodeGenPasses, BackendAction Action, - raw_pwrite_stream &OS, raw_pwrite_stream *DwoOS); + raw_pwrite_stream &OS, raw_pwrite_stream *DwoOS, + raw_pwrite_stream *CasIDOS = nullptr); std::unique_ptr openOutputFile(StringRef Path) { std::error_code EC; @@ -195,7 +199,8 @@ class EmitAssemblyHelper { std::unique_ptr &ThinLinkOS, BackendConsumer *BC); void RunCodegenPipeline(BackendAction Action, std::unique_ptr &OS, - std::unique_ptr &DwoOS); + std::unique_ptr &DwoOS, + std::unique_ptr &CasIDOS); /// Check whether we should emit a module summary for regular LTO. /// The module summary should be emitted by default for regular LTO @@ -217,10 +222,12 @@ class EmitAssemblyHelper { public: EmitAssemblyHelper(CompilerInstance &CI, CodeGenOptions &CGOpts, + const CASOptions &CASOpts, // MCCAS llvm::Module *M, IntrusiveRefCntPtr VFS) : CI(CI), Diags(CI.getDiagnostics()), CodeGenOpts(CGOpts), TargetOpts(CI.getTargetOpts()), LangOpts(CI.getLangOpts()), + CASOpts(CASOpts), // MCCAS TheModule(M), VFS(std::move(VFS)), TargetTriple(TheModule->getTargetTriple()) {} @@ -233,7 +240,8 @@ class EmitAssemblyHelper { // Emit output using the new pass manager for the optimization pipeline. void emitAssembly(BackendAction Action, std::unique_ptr OS, - BackendConsumer *BC); + std::unique_ptr CasIDOS = nullptr, + BackendConsumer *BC = nullptr); }; } // namespace @@ -363,7 +371,9 @@ static std::string flattenClangCommandLine(ArrayRef Args, static bool initTargetOptions(const CompilerInstance &CI, DiagnosticsEngine &Diags, - llvm::TargetOptions &Options) { + llvm::TargetOptions &Options, + const CASOptions &CASOpts // MCCAS + ) { const auto &CodeGenOpts = CI.getCodeGenOpts(); const auto &TargetOpts = CI.getTargetOpts(); const auto &LangOpts = CI.getLangOpts(); @@ -492,6 +502,12 @@ static bool initTargetOptions(const CompilerInstance &CI, break; } + // BEGIN MCCAS + Options.UseCASBackend = CodeGenOpts.UseCASBackend; // MCCAS + Options.MCOptions.CAS = CASOpts.getOrCreateDatabases(Diags).first; + Options.MCOptions.ResultCallBack = CodeGenOpts.MCCallBack; + Options.MCOptions.CASObjMode = CodeGenOpts.getCASObjMode(); + // END MCCAS Options.MCOptions.SplitDwarfFile = CodeGenOpts.SplitDwarfFile; Options.MCOptions.EmitDwarfUnwind = CodeGenOpts.getEmitDwarfUnwind(); Options.MCOptions.EmitCompactUnwindNonCanonical = @@ -613,7 +629,7 @@ void EmitAssemblyHelper::CreateTargetMachine(bool MustCreateTM) { CodeGenOptLevel OptLevel = *OptLevelOrNone; llvm::TargetOptions Options; - if (!initTargetOptions(CI, Diags, Options)) + if (!initTargetOptions(CI, Diags, Options, CASOpts)) return; TM.reset(TheTarget->createTargetMachine(Triple, TargetOpts.CPU, FeaturesStr, Options, RM, CM, OptLevel)); @@ -624,7 +640,8 @@ void EmitAssemblyHelper::CreateTargetMachine(bool MustCreateTM) { bool EmitAssemblyHelper::AddEmitPasses(legacy::PassManager &CodeGenPasses, BackendAction Action, raw_pwrite_stream &OS, - raw_pwrite_stream *DwoOS) { + raw_pwrite_stream *DwoOS, + raw_pwrite_stream *CasIDOS) { // Add LibraryInfo. std::unique_ptr TLII( llvm::driver::createTLII(TargetTriple, CodeGenOpts.getVecLib())); @@ -635,7 +652,8 @@ bool EmitAssemblyHelper::AddEmitPasses(legacy::PassManager &CodeGenPasses, CodeGenFileType CGFT = getCodeGenFileType(Action); if (TM->addPassesToEmitFile(CodeGenPasses, OS, DwoOS, CGFT, - /*DisableVerify=*/!CodeGenOpts.VerifyModule)) { + /*DisableVerify=*/!CodeGenOpts.VerifyModule, + nullptr, CasIDOS)) { Diags.Report(diag::err_fe_unable_to_interface_with_target); return false; } @@ -1021,6 +1039,9 @@ void EmitAssemblyHelper::RunOptimizationPipeline( const bool PrepareForThinLTO = CodeGenOpts.PrepareForThinLTO; const bool PrepareForLTO = CodeGenOpts.PrepareForLTO; + // -f[no-]split-cold-code + PB.setEnableHotColdSplitting(CodeGenOpts.SplitColdCode); + if (LangOpts.ObjCAutoRefCount) { PB.registerPipelineStartEPCallback( [](ModulePassManager &MPM, OptimizationLevel Level) { @@ -1091,6 +1112,11 @@ void EmitAssemblyHelper::RunOptimizationPipeline( addKCFIPass(TargetTriple, LangOpts, PB); } + if (LangOpts.SoftPointerAuth) + PB.registerOptimizerLastEPCallback( + [](ModulePassManager &MPM, OptimizationLevel Level, + ThinOrFullLTOPhase) { MPM.addPass(SoftPointerAuthPass()); }); + if (std::optional Options = getGCOVOptions(CodeGenOpts, LangOpts)) PB.registerPipelineStartEPCallback( @@ -1216,7 +1242,8 @@ void EmitAssemblyHelper::RunOptimizationPipeline( void EmitAssemblyHelper::RunCodegenPipeline( BackendAction Action, std::unique_ptr &OS, - std::unique_ptr &DwoOS) { + std::unique_ptr &DwoOS, + std::unique_ptr &CasIDOS) { // We still use the legacy PM to run the codegen pipeline since the new PM // does not work with the codegen pipeline. // FIXME: make the new PM work with the codegen pipeline. @@ -1235,7 +1262,7 @@ void EmitAssemblyHelper::RunCodegenPipeline( return; } if (!AddEmitPasses(CodeGenPasses, Action, *OS, - DwoOS ? &DwoOS->os() : nullptr)) + DwoOS ? &DwoOS->os() : nullptr, CasIDOS.get())) // FIXME: Should we handle this error differently? return; break; @@ -1266,6 +1293,7 @@ void EmitAssemblyHelper::RunCodegenPipeline( void EmitAssemblyHelper::emitAssembly(BackendAction Action, std::unique_ptr OS, + std::unique_ptr CasIDOS, BackendConsumer *BC) { setCommandLineOpts(CodeGenOpts); @@ -1282,7 +1310,7 @@ void EmitAssemblyHelper::emitAssembly(BackendAction Action, std::unique_ptr ThinLinkOS, DwoOS; RunOptimizationPipeline(Action, OS, ThinLinkOS, BC); - RunCodegenPipeline(Action, OS, DwoOS); + RunCodegenPipeline(Action, OS, DwoOS, CasIDOS); if (ThinLinkOS) ThinLinkOS->keep(); @@ -1292,7 +1320,9 @@ void EmitAssemblyHelper::emitAssembly(BackendAction Action, static void runThinLTOBackend(CompilerInstance &CI, ModuleSummaryIndex *CombinedIndex, - llvm::Module *M, std::unique_ptr OS, + llvm::Module *M, + const CASOptions &CASOpts, // MCCAS + std::unique_ptr OS, std::string SampleProfile, std::string ProfileRemapping, BackendAction Action) { DiagnosticsEngine &Diags = CI.getDiagnostics(); @@ -1335,7 +1365,7 @@ runThinLTOBackend(CompilerInstance &CI, ModuleSummaryIndex *CombinedIndex, assert(OptLevelOrNone && "Invalid optimization level!"); Conf.CGOptLevel = *OptLevelOrNone; Conf.OptLevel = CGOpts.OptimizationLevel; - initTargetOptions(CI, Diags, Conf.Options); + initTargetOptions(CI, Diags, Conf.Options, CASOpts/* MCCAS */); Conf.SampleProfile = std::move(SampleProfile); Conf.PTO.LoopUnrolling = CGOpts.UnrollLoops; Conf.PTO.LoopInterchange = CGOpts.InterchangeLoops; @@ -1400,10 +1430,12 @@ runThinLTOBackend(CompilerInstance &CI, ModuleSummaryIndex *CombinedIndex, } void clang::emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts, + const CASOptions &CASOpts, // MCCAS StringRef TDesc, llvm::Module *M, BackendAction Action, IntrusiveRefCntPtr VFS, std::unique_ptr OS, + std::unique_ptr CasIDOS, BackendConsumer *BC) { llvm::TimeTraceScope TimeScope("Backend"); DiagnosticsEngine &Diags = CI.getDiagnostics(); @@ -1429,7 +1461,8 @@ void clang::emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts, // of an error). if (CombinedIndex) { if (!CombinedIndex->skipModuleByDistributedBackend()) { - runThinLTOBackend(CI, CombinedIndex.get(), M, std::move(OS), + runThinLTOBackend(CI, CombinedIndex.get(), M, CASOpts/* MCCAS */, + std::move(OS), CGOpts.SampleProfileFile, CGOpts.ProfileRemappingFile, Action); return; @@ -1446,8 +1479,8 @@ void clang::emitBackendOutput(CompilerInstance &CI, CodeGenOptions &CGOpts, } } - EmitAssemblyHelper AsmHelper(CI, CGOpts, M, VFS); - AsmHelper.emitAssembly(Action, std::move(OS), BC); + EmitAssemblyHelper AsmHelper(CI, CGOpts, CASOpts/* MCCAS */, M, VFS); + AsmHelper.emitAssembly(Action, std::move(OS), std::move(CasIDOS), BC); // Verify clang's TargetInfo DataLayout against the LLVM TargetMachine's // DataLayout. diff --git a/clang/lib/CodeGen/BoundsSafetyOptRemarks.cpp b/clang/lib/CodeGen/BoundsSafetyOptRemarks.cpp new file mode 100644 index 0000000000000..63aa2506c807d --- /dev/null +++ b/clang/lib/CodeGen/BoundsSafetyOptRemarks.cpp @@ -0,0 +1,47 @@ +//===- BoundsSafetyOptRemarks.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "BoundsSafetyOptRemarks.h" +#include "BoundsSafetyTraps.h" +#include "llvm/IR/Instruction.h" +#include "llvm/Support/ErrorHandling.h" + +namespace clang { +namespace CodeGen { + +llvm::StringRef GetBoundsSafetyOptRemarkString(BoundsSafetyOptRemarkKind kind) { + switch (kind) { + case BNS_OR_NONE: + return ""; +#define BOUNDS_SAFETY_OR(SUFFIX, ANNOTATION_STR) \ + case BNS_OR_##SUFFIX: \ + return ANNOTATION_STR; +#include "BoundsSafetyOptRemarks.def" +#undef BOUNDS_SAFETY_OR + } + llvm_unreachable("Unhandled BoundsSafetyOptRemarkKind"); +} + +BoundsSafetyOptRemarkKind GetBoundsSafetyOptRemarkForTrap(BoundsSafetyTrapKind kind) { + switch (kind) { + case BNS_TRAP_NONE: + return BNS_OR_NONE; +#define BOUNDS_SAFETY_TRAP_CTX(SUFFIX, ANNOTATION_STR) \ + case BNS_TRAP_##SUFFIX: \ + return BNS_OR_##SUFFIX; +#define BOUNDS_SAFETY_TRAP(SUFFIX, ANNOTATION_STR, TRAP_MSG) \ + case BNS_TRAP_##SUFFIX: \ + return BNS_OR_##SUFFIX; +#include "BoundsSafetyTraps.def" +#undef BOUNDS_SAFETY_TRAP +#undef BOUNDS_SAFETY_TRAP_CTX + } + llvm_unreachable("Unhandled BoundsSafetyTrapKind"); +} + +} // namespace CodeGen +} // namespace clang diff --git a/clang/lib/CodeGen/BoundsSafetyOptRemarks.def b/clang/lib/CodeGen/BoundsSafetyOptRemarks.def new file mode 100644 index 0000000000000..65dcfcbe0422e --- /dev/null +++ b/clang/lib/CodeGen/BoundsSafetyOptRemarks.def @@ -0,0 +1,26 @@ +//===- BoundsSafetyOptRemarks.def ----------------------------------0----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This file declares the different types of BoundsSafety opt-remarks that can +// emitted. The opt-remarks added here should be independent of any BoundsSafety +// trap. If the opt-remark is tied to a particular trap you should declare it +// in `BoundsSafetyTraps.def` instead. +//===----------------------------------------------------------------------===// +#ifndef BOUNDS_SAFETY_OR +#error BOUNDS_SAFETY_OR must be defined +#endif + +// BOUNDS_SAFETY_OR(, ) + +BOUNDS_SAFETY_OR(PTR_NEQ_NULL, "bounds-safety-check-ptr-neq-null") + +// Include opt-remarks from BoundsSafety traps +#define BOUNDS_SAFETY_TRAP_CTX(SUFFIX, ANNOTATION_STR) BOUNDS_SAFETY_OR(SUFFIX, ANNOTATION_STR) +#define BOUNDS_SAFETY_TRAP(SUFFIX, ANNOTATION_STR, _) BOUNDS_SAFETY_OR(SUFFIX, ANNOTATION_STR) +#include "BoundsSafetyTraps.def" +#undef BOUNDS_SAFETY_TRAP +#undef BOUNDS_SAFETY_TRAP_CTX diff --git a/clang/lib/CodeGen/BoundsSafetyOptRemarks.h b/clang/lib/CodeGen/BoundsSafetyOptRemarks.h new file mode 100644 index 0000000000000..077a34ee46bf0 --- /dev/null +++ b/clang/lib/CodeGen/BoundsSafetyOptRemarks.h @@ -0,0 +1,30 @@ +//===- BoundsSafetyOptRemarks.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_LIB_CODEGEN_BOUNDS_SAFETY_OPT_REMARKS_H +#define LLVM_CLANG_LIB_CODEGEN_BOUNDS_SAFETY_OPT_REMARKS_H +#include "BoundsSafetyTraps.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/Instruction.h" + +namespace clang { +namespace CodeGen { + +enum BoundsSafetyOptRemarkKind { + BNS_OR_NONE, ///< Special value representing no opt-remark. +#define BOUNDS_SAFETY_OR(SUFFIX, ANNOTATION_STR) BNS_OR_##SUFFIX, +#include "BoundsSafetyOptRemarks.def" +#undef BOUNDS_SAFETY_OR +}; + +llvm::StringRef GetBoundsSafetyOptRemarkString(BoundsSafetyOptRemarkKind kind); +BoundsSafetyOptRemarkKind GetBoundsSafetyOptRemarkForTrap(BoundsSafetyTrapKind kind); + +} // namespace CodeGen +} // namespace clang + +#endif diff --git a/clang/lib/CodeGen/BoundsSafetyTraps.cpp b/clang/lib/CodeGen/BoundsSafetyTraps.cpp new file mode 100644 index 0000000000000..5ef672ea9da99 --- /dev/null +++ b/clang/lib/CodeGen/BoundsSafetyTraps.cpp @@ -0,0 +1,82 @@ +//===- BoundsSafetyTraps.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "BoundsSafetyTraps.h" +#include "BoundsSafetyOptRemarks.h" +#include "llvm/IR/Instruction.h" +#include "llvm/Support/ErrorHandling.h" + +namespace clang { +namespace CodeGen { + +llvm::StringRef GetBoundsSafetyTrapMessagePrefix() { + return "Bounds check failed"; +} + +llvm::StringRef +GetBoundsSafetyTrapMessageSuffixWithContext(BoundsSafetyTrapKind kind, + BoundsSafetyTrapCtx::Kind TrapCtx) { + assert(TrapCtx != BoundsSafetyTrapCtx::UNKNOWN); + switch (kind) { + case BNS_TRAP_PTR_LT_LOWER_BOUND: + switch (TrapCtx) { + case BoundsSafetyTrapCtx::DEREF: + return "Dereferencing below bounds"; + case BoundsSafetyTrapCtx::CAST: + return "Pointer below bounds while casting"; + case BoundsSafetyTrapCtx::ASSIGN: + return "Pointer below bounds while assigning"; + case BoundsSafetyTrapCtx::ADDR_OF_STRUCT_MEMBER: + return "Pointer to struct below bounds while taking address of struct " + "member"; + case BoundsSafetyTrapCtx::TERMINATED_BY_FROM_INDEXABLE: + default: + llvm_unreachable("Invalid TrapCtx"); + } + case BNS_TRAP_PTR_GT_UPPER_BOUND: + case BNS_TRAP_PTR_GE_UPPER_BOUND: + switch (TrapCtx) { + case BoundsSafetyTrapCtx::DEREF: + return "Dereferencing above bounds"; + case BoundsSafetyTrapCtx::CAST: + return "Pointer above bounds while casting"; + case BoundsSafetyTrapCtx::ASSIGN: + return "Pointer above bounds while assigning"; + case BoundsSafetyTrapCtx::ADDR_OF_STRUCT_MEMBER: + return "Pointer to struct above bounds while taking address of struct " + "member"; + case BoundsSafetyTrapCtx::TERMINATED_BY_FROM_INDEXABLE: + return "Pointer above bounds while converting __indexable to " + "__terminated_by"; + default: + llvm_unreachable("Invalid TrapCtx"); + } + default: + llvm_unreachable("Unhandled BoundsSafetyTrapKind"); + } +} + +llvm::StringRef GetBoundsSafetyTrapMessageSuffix(BoundsSafetyTrapKind kind, + BoundsSafetyTrapCtx::Kind TrapCtx) { + switch (kind) { + case BNS_TRAP_NONE: + return ""; +#define BOUNDS_SAFETY_TRAP_CTX(SUFFIX, ANNOTATION_STR) \ + case BNS_TRAP_##SUFFIX: \ + return GetBoundsSafetyTrapMessageSuffixWithContext(kind, TrapCtx); +#define BOUNDS_SAFETY_TRAP(SUFFIX, ANNOTATION_STR, TRAP_MSG) \ + case BNS_TRAP_##SUFFIX: \ + return TRAP_MSG; +#include "BoundsSafetyTraps.def" +#undef BOUNDS_SAFETY_TRAP +#undef BOUNDS_SAFETY_TRAP_CTX + } + llvm_unreachable("Unhandled BoundsSafetyTrapKind"); +} + +} // namespace CodeGen +} // namespace clang diff --git a/clang/lib/CodeGen/BoundsSafetyTraps.def b/clang/lib/CodeGen/BoundsSafetyTraps.def new file mode 100644 index 0000000000000..bd122b87d84f8 --- /dev/null +++ b/clang/lib/CodeGen/BoundsSafetyTraps.def @@ -0,0 +1,102 @@ +//===- BoundsSafetyTraps.def ----------------------------------0----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This file declares the different types of BoundsSafety traps that can emitted +// along with properties of the trap +//===----------------------------------------------------------------------===// + +#ifndef BOUNDS_SAFETY_TRAP +#error BOUNDS_SAFETY_TRAP must be defined +#endif +#ifndef BOUNDS_SAFETY_TRAP_CTX +#error BOUNDS_SAFETY_TRAP_CTX must be defined +#endif + +// BOUNDS_SAFETY_TRAP( +// , +// , +// ) +// +// - The name of the enum field (without the prefix). This +// should be a valid enum identifier. The name of this field should indicate (if +// possible) the condition that is true when the trap fires. +// +// - The string is used to annotate instructions +// (that might lead to the trap) with opt remarks. The naming convention +// used here is that the string should (if possible) describe the condition +// that the annotated branch instruction checks. More specifically it is the +// condition that is true when execution should continue (i.e. not trap). +// NOTE THIS NAMING CONVENTION IS THE OPPOSITE TO WHAT IS USED FOR THE OTHER +// FIELDS. It is setup this way for historical reasons. +// +// - The string used to describe the type of trap to the user. If +// possible the string should describe the condition that was checked for, but +// failed to hold. +// +// ----------------------------------------------------------------------------- +// +// BOUNDS_SAFETY_TRAP_CTX(, ) +// +// Same as `BOUNDS_SAFETY_TRAP` but does not take a `` because the +// message depends on context so it is specified elsewhere. + +// The general trap message is deliberately empty because we have nothing useful +// to tell the user other that it's a BoundsSafety trap which is already handled by +// the prefix (`GetBoundsSafetyTrapMessagePrefix()`) that is emitted for all +// BoundsSafety traps. +BOUNDS_SAFETY_TRAP( + GENERAL, "bounds-safety-generic", "") + +BOUNDS_SAFETY_TRAP_CTX(PTR_GT_UPPER_BOUND, "bounds-safety-check-ptr-le-upper-bound") +BOUNDS_SAFETY_TRAP_CTX(PTR_GE_UPPER_BOUND, "bounds-safety-check-ptr-lt-upper-bound") +BOUNDS_SAFETY_TRAP_CTX(PTR_LT_LOWER_BOUND, "bounds-safety-check-ptr-ge-lower-bound") + +BOUNDS_SAFETY_TRAP( + BIDI_TO_INDEXABLE_PTR_LT_LOWER_BOUND, + "bounds-safety-check-bidi-to-indexable-ptr-ge-lower-bound", + "Converted __indexable pointer is below bounds") +BOUNDS_SAFETY_TRAP( + TERMINATED_BY_PTR_ARITH, "bounds-safety-check-terminated-by-ptr-arith", + "Arithmetic on __terminated_by pointer one-past-the-end of the terminator") +BOUNDS_SAFETY_TRAP( + TERMINATED_BY_TERM_ASSIGN, "bounds-safety-check-terminated-by-term-assign", + "The terminator cannot be assigned") + +BOUNDS_SAFETY_TRAP(TERMINATED_BY_FROM_INDEXABLE_PTR_GT_TERM_PTR, + "bounds-safety-check-terminated-by-from-indexable-ptr-le-term-ptr", + "Terminator pointer below bounds" +) +BOUNDS_SAFETY_TRAP(TERMINATED_BY_FROM_INDEXABLE_TERM_PTR_OVERFLOW, + "bounds-safety-check-terminated-by-from-indexable-term-ptr-no-overflow", + "Terminator pointer overflows address space" +) +BOUNDS_SAFETY_TRAP(TERMINATED_BY_FROM_INDEXABLE_TERM_PTR_PLUS_ONE_GT_UPPER_BOUND, + "bounds-safety-check-terminated-by-from-indexable-term-ptr-plus-one-le-upper-bound", + "Terminator pointer above bounds" +) + +BOUNDS_SAFETY_TRAP( + TERMINATED_BY_FROM_INDEXABLE_TERM, + "bounds-safety-check-terminated-by-from-indexable-term", + "Cannot find the terminator when converting to __terminated_by pointer from an __indexable pointer") +BOUNDS_SAFETY_TRAP( + INDEXABLE_PTR_NEW_LT_OLD, + "bounds-safety-check-new-indexable-ptr-ge-old", + "New lower bound less than old lower bound" +) +BOUNDS_SAFETY_TRAP( + COUNT_NEGATIVE, + "bounds-safety-check-count-negative", + "Count is negative") +BOUNDS_SAFETY_TRAP( + FLEX_COUNT_GT_BOUNDS, + "bounds-safety-check-flexible-count-gt-bounds", + "Count for flexible array member is too big") +BOUNDS_SAFETY_TRAP( + PTR_PAST_END_OVERFLOW, + "bounds-safety-check-one-past-end-overflow", + "Pointer to one past the end overflows address space") \ No newline at end of file diff --git a/clang/lib/CodeGen/BoundsSafetyTraps.h b/clang/lib/CodeGen/BoundsSafetyTraps.h new file mode 100644 index 0000000000000..5a96709326826 --- /dev/null +++ b/clang/lib/CodeGen/BoundsSafetyTraps.h @@ -0,0 +1,49 @@ +//===- BoundsSafetyTraps.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_LIB_CODEGEN_BOUNDS_SAFETY_TRAPS_H +#define LLVM_CLANG_LIB_CODEGEN_BOUNDS_SAFETY_TRAPS_H +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace CodeGen { + +enum BoundsSafetyTrapKind { + BNS_TRAP_NONE, ///< Special value representing no trap. +#define BOUNDS_SAFETY_TRAP_CTX(SUFFIX, ANNOTATION_STR) BNS_TRAP_##SUFFIX, +#define BOUNDS_SAFETY_TRAP(SUFFIX, ANNOTATION_STR, TRAP_MSG) BNS_TRAP_##SUFFIX, +#include "BoundsSafetyTraps.def" + +#undef BOUNDS_SAFETY_TRAP +#undef BOUNDS_SAFETY_TRAP_CTX +}; + +llvm::StringRef GetBoundsSafetyTrapMessagePrefix(); + +// BoundsSafetyTrapCtx provides an enum and helper methods for describing the +// context where a trap happens (i.e. the operation we are guarding against with +// a trap). +struct BoundsSafetyTrapCtx { + enum Kind { + UNKNOWN, ///< Unknown + DEREF, ///< Pointer/Array dereference + ASSIGN, ///< Assign + CAST, ///< Cast + ADDR_OF_STRUCT_MEMBER, ///< Take address of struct member + TERMINATED_BY_FROM_INDEXABLE ///< Check during call to + ///< `__unsafe_terminated_by_from_indexable`. + }; +}; + +llvm::StringRef GetBoundsSafetyTrapMessageSuffix( + BoundsSafetyTrapKind kind, + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN); + +} // namespace CodeGen +} // namespace clang + +#endif diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp index ba0d87fdc5d43..c78b1688fef49 100644 --- a/clang/lib/CodeGen/CGBlocks.cpp +++ b/clang/lib/CodeGen/CGBlocks.cpp @@ -188,13 +188,16 @@ static llvm::Constant *buildBlockDescriptor(CodeGenModule &CGM, // Optional copy/dispose helpers. bool hasInternalHelper = false; if (blockInfo.NeedsCopyDispose) { + auto &schema = + CGM.getCodeGenOpts().PointerAuth.BlockHelperFunctionPointers; + // copy_func_helper_decl llvm::Constant *copyHelper = buildCopyHelper(CGM, blockInfo); - elements.add(copyHelper); + elements.addSignedPointer(copyHelper, schema, GlobalDecl(), QualType()); // destroy_func_decl llvm::Constant *disposeHelper = buildDisposeHelper(CGM, blockInfo); - elements.add(disposeHelper); + elements.addSignedPointer(disposeHelper, schema, GlobalDecl(), QualType()); if (cast(copyHelper->stripPointerCasts()) ->hasInternalLinkage() || @@ -868,11 +871,26 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { llvm::ConstantInt::get(IntTy, blockInfo.BlockAlign.getQuantity()), getIntSize(), "block.align"); } - addHeaderField(blockFn, GenVoidPtrSize, "block.invoke"); - if (!IsOpenCL) + + if (!IsOpenCL) { + llvm::Value *blockFnPtr = llvm::ConstantExpr::getBitCast(InvokeFn, VoidPtrTy); + auto blockFnPtrAddr = projectField(index, "block.invoke"); + if (auto &schema = + CGM.getCodeGenOpts().PointerAuth.BlockInvocationFunctionPointers) { + QualType type = blockInfo.getBlockExpr()->getType() + ->castAs()->getPointeeType(); + auto authInfo = EmitPointerAuthInfo( + schema, blockFnPtrAddr.emitRawPointer(*this), GlobalDecl(), type); + blockFnPtr = EmitPointerAuthSign(authInfo, blockFnPtr); + } + Builder.CreateStore(blockFnPtr, blockFnPtrAddr); + offset += getPointerSize(); + index++; + addHeaderField(descriptor, getPointerSize(), "block.descriptor"); - else if (auto *Helper = - CGM.getTargetCodeGenInfo().getTargetOpenCLBlockHelper()) { + } else if (auto *Helper = + CGM.getTargetCodeGenInfo().getTargetOpenCLBlockHelper()) { + addHeaderField(blockFn, GenVoidPtrSize, "block.invoke"); for (auto I : Helper->getCustomFieldValues(*this, blockInfo)) { addHeaderField( I.first, @@ -880,7 +898,8 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const CGBlockInfo &blockInfo) { CGM.getDataLayout().getTypeAllocSize(I.first->getType())), I.second); } - } + } else + addHeaderField(blockFn, GenVoidPtrSize, "block.invoke"); } // Finally, capture all the values into the block. @@ -1151,6 +1170,8 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr *E, ASTContext &Ctx = getContext(); CallArgList Args; + llvm::Value *FuncPtr = nullptr; + if (getLangOpts().OpenCL) { // For OpenCL, BlockPtr is already casted to generic block literal. @@ -1170,7 +1191,7 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr *E, if (!isa(E->getCalleeDecl())) Func = CGM.getOpenCLRuntime().getInvokeFunction(E->getCallee()); else { - llvm::Value *FuncPtr = Builder.CreateStructGEP(GenBlockTy, BlockPtr, 2); + FuncPtr = Builder.CreateStructGEP(GenBlockTy, BlockPtr, 2); Func = Builder.CreateAlignedLoad(GenericVoidPtrTy, FuncPtr, getPointerAlign()); } @@ -1179,7 +1200,7 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr *E, BlockPtr = Builder.CreatePointerCast(BlockPtr, UnqualPtrTy, "block.literal"); // Get pointer to the block invoke function - llvm::Value *FuncPtr = Builder.CreateStructGEP(GenBlockTy, BlockPtr, 3); + FuncPtr = Builder.CreateStructGEP(GenBlockTy, BlockPtr, 3); // First argument is a block literal casted to a void pointer BlockPtr = Builder.CreatePointerCast(BlockPtr, VoidPtrTy); @@ -1196,7 +1217,14 @@ RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr *E, CGM.getTypes().arrangeBlockFunctionCall(Args, FuncTy); // Prepare the callee. - CGCallee Callee(CGCalleeInfo(), Func); + CGPointerAuthInfo PointerAuth; + if (auto &AuthSchema = + CGM.getCodeGenOpts().PointerAuth.BlockInvocationFunctionPointers) { + assert(FuncPtr != nullptr && "Missing function pointer for AuthInfo"); + PointerAuth = EmitPointerAuthInfo(AuthSchema, FuncPtr, + GlobalDecl(), FnType); + } + CGCallee Callee(CGCalleeInfo(), Func, PointerAuth); // And call the block. return EmitCall(FnInfo, Callee, ReturnValue, Args, CallOrInvoke); @@ -1298,14 +1326,25 @@ static llvm::Constant *buildGlobalBlock(CodeGenModule &CGM, // Reserved fields.addInt(CGM.IntTy, 0); + + // Function + if (auto &schema = + CGM.getCodeGenOpts().PointerAuth.BlockInvocationFunctionPointers) { + QualType fnType = blockInfo.getBlockExpr() + ->getType() + ->castAs() + ->getPointeeType(); + fields.addSignedPointer(blockFn, schema, GlobalDecl(), fnType); + } else { + fields.add(blockFn); + } } else { fields.addInt(CGM.IntTy, blockInfo.BlockSize.getQuantity()); fields.addInt(CGM.IntTy, blockInfo.BlockAlign.getQuantity()); + // Function + fields.add(blockFn); } - // Function - fields.add(blockFn); - if (!IsOpenCL) { // Descriptor fields.add(buildBlockDescriptor(CGM, blockInfo)); @@ -2713,8 +2752,16 @@ void CodeGenFunction::emitByrefStructureInit(const AutoVarEmission &emission) { unsigned nextHeaderIndex = 0; CharUnits nextHeaderOffset; auto storeHeaderField = [&](llvm::Value *value, CharUnits fieldSize, - const Twine &name) { + const Twine &name, bool isFunction = false) { auto fieldAddr = Builder.CreateStructGEP(addr, nextHeaderIndex, name); + if (isFunction) { + if (auto &schema = CGM.getCodeGenOpts().PointerAuth + .BlockByrefHelperFunctionPointers) { + auto pointerAuth = EmitPointerAuthInfo( + schema, fieldAddr.emitRawPointer(*this), GlobalDecl(), QualType()); + value = EmitPointerAuthSign(pointerAuth, value); + } + } Builder.CreateStore(value, fieldAddr); nextHeaderIndex++; @@ -2798,9 +2845,9 @@ void CodeGenFunction::emitByrefStructureInit(const AutoVarEmission &emission) { if (helpers) { storeHeaderField(helpers->CopyHelper, getPointerSize(), - "byref.copyHelper"); + "byref.copyHelper", /*function*/ true); storeHeaderField(helpers->DisposeHelper, getPointerSize(), - "byref.disposeHelper"); + "byref.disposeHelper", /*function*/ true); } if (ByRefHasLifetime && HasByrefExtendedLayout) { diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index c7fbbbc6fd40d..030d19647900f 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2025,6 +2025,14 @@ static void getTrivialDefaultFunctionAttributes( FuncAttrs.addAttribute("stackrealign"); if (CodeGenOpts.Backchain) FuncAttrs.addAttribute("backchain"); + if (CodeGenOpts.PointerAuth.ReturnAddresses) + FuncAttrs.addAttribute("ptrauth-returns"); + if (CodeGenOpts.PointerAuth.FunctionPointers) + FuncAttrs.addAttribute("ptrauth-calls"); + if (CodeGenOpts.PointerAuth.IndirectGotos) + FuncAttrs.addAttribute("ptrauth-indirect-gotos"); + if (CodeGenOpts.PointerAuth.AuthTraps) + FuncAttrs.addAttribute("ptrauth-auth-traps"); if (CodeGenOpts.EnableSegmentedStacks) FuncAttrs.addAttribute("split-stack"); @@ -5903,7 +5911,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (CGM.getLangOpts().ObjCAutoRefCount) AddObjCARCExceptionMetadata(CI); - // Set tail call kind if necessary. + // Adjust tail call behavior based on TargetDecl's attributes and CallInfo. if (llvm::CallInst *Call = dyn_cast(CI)) { if (TargetDecl && TargetDecl->hasAttr()) Call->setTailCallKind(llvm::CallInst::TCK_NoTail); diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h index 0b4e3f9cb0365..26bfd1f75aefb 100644 --- a/clang/lib/CodeGen/CGCall.h +++ b/clang/lib/CodeGen/CGCall.h @@ -110,8 +110,7 @@ class CGCallee { /// Construct a callee. Call this constructor directly when this /// isn't a direct call. CGCallee(const CGCalleeInfo &abstractInfo, llvm::Value *functionPtr, - /* FIXME: make parameter pointerAuthInfo mandatory */ - const CGPointerAuthInfo &pointerAuthInfo = CGPointerAuthInfo()) + const CGPointerAuthInfo &pointerAuthInfo) : KindOrFunctionPointer( SpecialKind(reinterpret_cast(functionPtr))) { OrdinaryInfo.AbstractInfo = abstractInfo; @@ -136,12 +135,12 @@ class CGCallee { static CGCallee forDirect(llvm::Constant *functionPtr, const CGCalleeInfo &abstractInfo = CGCalleeInfo()) { - return CGCallee(abstractInfo, functionPtr); + return CGCallee(abstractInfo, functionPtr, CGPointerAuthInfo()); } static CGCallee forDirect(llvm::FunctionCallee functionPtr, const CGCalleeInfo &abstractInfo = CGCalleeInfo()) { - return CGCallee(abstractInfo, functionPtr.getCallee()); + return CGCallee(abstractInfo, functionPtr.getCallee(), CGPointerAuthInfo()); } static CGCallee forVirtual(const CallExpr *CE, GlobalDecl MD, Address Addr, diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index f3ec498d4064b..0b5ddaa4e1ede 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -78,6 +78,12 @@ static uint32_t getDeclAlignIfRequired(const Decl *D, const ASTContext &Ctx) { return D->hasAttr() ? D->getMaxAlignment() : 0; } +static bool getIsTransparentStepping(const Decl *D) { + if (!D) + return false; + return D->hasAttr(); +} + /// Returns true if \ref VD is a a holding variable (aka a /// VarDecl retrieved using \ref BindingDecl::getHoldingVar). static bool IsDecomposedVarDecl(VarDecl const *VD) { @@ -1124,6 +1130,30 @@ llvm::DIType *CGDebugInfo::CreateType(const ObjCObjectPointerType *Ty, llvm::DIType *CGDebugInfo::CreateType(const PointerType *Ty, llvm::DIFile *Unit) { + /* TO_UPSTREAM(BoundsSafety) ON*/ + // If we heve a -fbounds-safety pointer then we should grab the wide_ptr it + // maps to from the ASTContext and generate that. + if (!Ty->hasRawPointerLayout()) { + llvm::StructType *StructTy = + cast(convertTypeForMemory(CGM, QualType(Ty, 0))); + SmallVector EltTys; + QualType ElemQTy = CGM.getContext().getPointerType(Ty->getPointeeType()); + std::string MemberNames[] = {"ptr", "ub", "lb"}; + uint64_t FieldOffset = 0; + assert(StructTy->getNumElements() < 4 && "Unknown wide pointer layout"); + for (unsigned i = 0; i < StructTy->getNumElements(); ++i) { + llvm::DIType *DElemTy = CreateMemberType(Unit, ElemQTy, MemberNames[i], &FieldOffset); + EltTys.push_back(DElemTy); + } + + llvm::DICompositeType *DStructTy = DBuilder.createStructType( + Unit, StructTy->getName(), nullptr, 0, CGM.getContext().getTypeSize(Ty), + CGM.getContext().getTypeAlign(Ty), llvm::DINode::FlagZero, nullptr, + DBuilder.getOrCreateArray(EltTys)); + + return DStructTy; + } else + /* TO_UPSTREAM(BoundsSafety) OFF*/ return CreatePointerLikeType(llvm::dwarf::DW_TAG_pointer_type, Ty, Ty->getPointeeType(), Unit); } @@ -1532,6 +1562,53 @@ static llvm::DINode::DIFlags getAccessFlag(AccessSpecifier Access, llvm_unreachable("unexpected access enumerator"); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +llvm::DIType *CGDebugInfo::CreateType(const CountAttributedType *Ty, + llvm::DIFile *Unit) { + QualType DesugaredTy = Ty->desugar(); + llvm::DIType *DesugaredDITy = getOrCreateType(DesugaredTy, Unit); + SourceLocation Loc; + + // Map the pointer type into a typedef with a recognizable name so that + // debuggers may provide special formatters for them. + std::string TypedefName; + if (Ty->isCountInBytes() && Ty->isOrNull()) + TypedefName = "__bounds_safety::sized_by_or_null::"; + else if (Ty->isCountInBytes()) + TypedefName = "__bounds_safety::sized_by::"; + else if (Ty->isOrNull()) + TypedefName = "__bounds_safety::counted_by_or_null::"; + else + TypedefName = "__bounds_safety::counted_by::"; + llvm::raw_string_ostream ExprStream(TypedefName); + Ty->getCountExpr()->printPretty( + ExprStream, nullptr, PrintingPolicy(CGM.getContext().getLangOpts())); + return DBuilder.createTypedef(DesugaredDITy, TypedefName, Unit, + getLineNumber(Loc), Unit); +} + +llvm::DIType *CGDebugInfo::CreateType(const DynamicRangePointerType *Ty, + llvm::DIFile *Unit) { + QualType DesugaredTy = Ty->desugar(); + llvm::DIType *DesugaredDITy = getOrCreateType(DesugaredTy, Unit); + SourceLocation Loc; + + // Map the pointer type into a typedef with a recognizable name so that + // debuggers may provide special formatters for them. + std::string TypedefName = "__bounds_safety::dynamic_range::"; + llvm::raw_string_ostream ExprStream(TypedefName); + if (auto *StartExpr = Ty->getStartPointer()) + StartExpr->printPretty(ExprStream, nullptr, + PrintingPolicy(CGM.getContext().getLangOpts())); + ExprStream << "::"; + if (auto *ExndExpr = Ty->getEndPointer()) + ExndExpr->printPretty(ExprStream, nullptr, + PrintingPolicy(CGM.getContext().getLangOpts())); + return DBuilder.createTypedef(DesugaredDITy, TypedefName, Unit, + getLineNumber(Loc), Unit); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + llvm::DIType *CGDebugInfo::CreateType(const TypedefType *Ty, llvm::DIFile *Unit) { llvm::DIType *Underlying = @@ -2185,6 +2262,8 @@ llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction( SPFlags |= llvm::DISubprogram::SPFlagLocalToUnit; if (CGM.getLangOpts().Optimize) SPFlags |= llvm::DISubprogram::SPFlagOptimized; + if (getIsTransparentStepping(Method)) + SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping; // In this debug mode, emit type info for a class when its constructor type // info is emitted. @@ -3139,7 +3218,7 @@ llvm::DIModule *CGDebugInfo::getOrCreateModuleRef(ASTSourceDescriptor Mod, std::string IncludePath = Mod.getPath().str(); llvm::DIModule *DIMod = DBuilder.createModule(Parent, Mod.getModuleName(), ConfigMacros, - RemapPath(IncludePath)); + RemapPath(IncludePath), M ? M->APINotesFile : ""); ModuleCache[M].reset(DIMod); return DIMod; } @@ -3685,10 +3764,17 @@ static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) { case Type::Attributed: T = cast(T)->getEquivalentType(); break; + case Type::ValueTerminated: + T = cast(T)->desugar(); + break; case Type::BTFTagAttributed: T = cast(T)->getWrappedType(); break; case Type::CountAttributed: + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (C.getLangOpts().BoundsSafety) + return C.getQualifiedType(T.getTypePtr(), Quals); + /* TO_UPSTREAM(BoundsSafety) OFF*/ T = cast(T)->desugar(); break; case Type::Elaborated: @@ -3889,7 +3975,14 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) { case Type::HLSLAttributedResource: return CreateType(cast(Ty), Unit); + /* TO_UPSTREAM(BoundsSafety) ON */ case Type::CountAttributed: + if (CGM.getLangOpts().BoundsSafety) + return CreateType(cast(Ty), Unit); + break; + case Type::DynamicRangePointer: + return CreateType(cast(Ty), Unit); + /* TO_UPSTREAM(BoundsSafety) OFF */ case Type::Auto: case Type::Attributed: case Type::BTFTagAttributed: @@ -3906,6 +3999,7 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) { case Type::Decltype: case Type::PackIndexing: case Type::UnaryTransform: + case Type::ValueTerminated: break; } @@ -4202,6 +4296,8 @@ llvm::DISubprogram *CGDebugInfo::getFunctionFwdDeclOrStub(GlobalDecl GD, if (Stub) { Flags |= getCallSiteRelatedAttrs(); SPFlags |= llvm::DISubprogram::SPFlagDefinition; + if (getIsTransparentStepping(FD)) + SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping; return DBuilder.createFunction( DContext, Name, LinkageName, Unit, Line, getOrCreateFunctionType(GD.getDecl(), FnType, Unit), 0, Flags, SPFlags, @@ -4351,6 +4447,8 @@ llvm::DISubprogram *CGDebugInfo::getObjCMethodDeclaration( if (It == TypeCache.end()) return nullptr; auto *InterfaceType = cast(It->second); + if (getIsTransparentStepping(D)) + SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping; llvm::DISubprogram *FD = DBuilder.createFunction( InterfaceType, getObjCMethodName(OMD), StringRef(), InterfaceType->getFile(), LineNo, FnType, LineNo, Flags, SPFlags); @@ -4517,6 +4615,8 @@ void CGDebugInfo::emitFunctionStart(GlobalDecl GD, SourceLocation Loc, SPFlags |= llvm::DISubprogram::SPFlagLocalToUnit; if (CGM.getLangOpts().Optimize) SPFlags |= llvm::DISubprogram::SPFlagOptimized; + if (getIsTransparentStepping(D)) + SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping; llvm::DINode::DIFlags FlagsForDef = Flags | getCallSiteRelatedAttrs(); llvm::DISubprogram::DISPFlags SPFlagsForDef = @@ -4603,6 +4703,9 @@ void CGDebugInfo::EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc, llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(D); llvm::DISubroutineType *STy = getOrCreateFunctionType(D, FnType, Unit); + if (getIsTransparentStepping(D)) + SPFlags |= llvm::DISubprogram::SPFlagIsTransparentStepping; + llvm::DISubprogram *SP = DBuilder.createFunction( FDContext, Name, LinkageName, Unit, LineNo, STy, ScopeLine, Flags, SPFlags, TParamsArray.get(), nullptr, nullptr, Annotations); diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index b287ce7b92eee..c57ffa089f0b1 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -196,6 +196,10 @@ class CGDebugInfo { llvm::DIType *CreateType(const PointerType *Ty, llvm::DIFile *F); llvm::DIType *CreateType(const BlockPointerType *Ty, llvm::DIFile *F); llvm::DIType *CreateType(const FunctionType *Ty, llvm::DIFile *F); + /* TO_UPSTREAM(BoundsSafety) ON */ + llvm::DIType *CreateType(const CountAttributedType *Ty, llvm::DIFile *F); + llvm::DIType *CreateType(const DynamicRangePointerType *Ty, llvm::DIFile *F); + /* TO_UPSTREAM(BoundsSafety) OFF */ llvm::DIType *CreateType(const HLSLAttributedResourceType *Ty, llvm::DIFile *F); /// Get structure or union type. diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index db34e2738b4cf..653b0f5f5b9cb 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -16,6 +16,7 @@ #include "CGDebugInfo.h" #include "CGOpenCLRuntime.h" #include "CGOpenMPRuntime.h" +#include "CGRecordLayout.h" #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" @@ -751,12 +752,13 @@ static void drillIntoBlockVariable(CodeGenFunction &CGF, lvalue.setAddress(CGF.emitBlockByrefAddress(lvalue.getAddress(), var)); } -void CodeGenFunction::EmitNullabilityCheck(LValue LHS, llvm::Value *RHS, +// TO_UPSTREAM(BoundsSafety): LHSQTy +void CodeGenFunction::EmitNullabilityCheck(QualType LHSQTy, llvm::Value *RHS, SourceLocation Loc) { if (!SanOpts.has(SanitizerKind::NullabilityAssign)) return; - auto Nullability = LHS.getType()->getNullability(); + auto Nullability = LHSQTy->getNullability(); if (!Nullability || *Nullability != NullabilityKind::NonNull) return; @@ -765,13 +767,20 @@ void CodeGenFunction::EmitNullabilityCheck(LValue LHS, llvm::Value *RHS, SanitizerScope SanScope(this); llvm::Value *IsNotNull = Builder.CreateIsNotNull(RHS); llvm::Constant *StaticData[] = { - EmitCheckSourceLocation(Loc), EmitCheckTypeDescriptor(LHS.getType()), + EmitCheckSourceLocation(Loc), EmitCheckTypeDescriptor(LHSQTy), llvm::ConstantInt::get(Int8Ty, 0), // The LogAlignment info is unused. llvm::ConstantInt::get(Int8Ty, TCK_NonnullAssign)}; EmitCheck({{IsNotNull, SanitizerKind::SO_NullabilityAssign}}, SanitizerHandler::TypeMismatch, StaticData, RHS); } +/* TO_UPSTREAM(BoundsSafety) ON */ +void CodeGenFunction::EmitNullabilityCheck(LValue LHS, llvm::Value *RHS, + SourceLocation Loc) { + EmitNullabilityCheck(LHS.getType(), RHS, Loc); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D, LValue lvalue, bool capturedByInit) { Qualifiers::ObjCLifetime lifetime = lvalue.getObjCLifetime(); @@ -1915,6 +1924,119 @@ void CodeGenFunction::emitZeroOrPatternForAutoVarInit(QualType type, } } +/* TO_UPSTREAM(BoundsSafety) ON */ +static void boundsSafetyZeroInit(CodeGenFunction &CGF, const Address Addr, + const bool IsVolatileQualified) { + llvm::Type *ElTy = Addr.getElementType(); + llvm::Constant *constant = constWithPadding( + CGF.CGM, IsPattern::No, llvm::Constant::getNullValue(ElTy)); + + auto *Ty = constant->getType(); + uint64_t ConstantSize = CGF.CGM.getDataLayout().getTypeAllocSize(Ty); + if (!ConstantSize) + return; + + bool canDoSingleStore = Ty->isIntOrIntVectorTy() || + Ty->isPtrOrPtrVectorTy() || Ty->isFPOrFPVectorTy(); + if (canDoSingleStore) { + auto *I = CGF.Builder.CreateStore(constant, Addr, IsVolatileQualified); + I->addAnnotationMetadata("bounds-safety-zero-init"); + } else { + auto *SizeVal = llvm::ConstantInt::get(CGF.CGM.IntPtrTy, ConstantSize); + auto *I = CGF.Builder.CreateMemSet( + Addr, llvm::ConstantInt::get(CGF.CGM.Int8Ty, 0), SizeVal, + IsVolatileQualified); + I->addAnnotationMetadata("bounds-safety-zero-init"); + } +} + +template +static auto makeCachingAddressInitializer(T &Action, std::optional
&Addr) { + return [&]() { + if (!Addr) { + Addr = Action(); + } + return *Addr; + }; +} + +static void boundsSafetyRecursiveInit(CodeGenFunction &CGF, const Decl *D, + const QualType Ty, bool IsVolatileQualified, + std::function BaseAddrGen) { + const DependerDeclsAttr *Att = D ? D->getAttr() : nullptr; + auto ShouldInitializeType = [](QualType Ty) -> bool { + return Ty->isSafePointerType() || Ty->isCountAttributedType() || + Ty->isDynamicRangePointerType() || Ty->isValueTerminatedType(); + }; + // declarations referred to from _sized_by etc attributes + if (Att && Att->dependerDecls_size() > 0) { + boundsSafetyZeroInit(CGF, BaseAddrGen(), IsVolatileQualified); + } else if (ShouldInitializeType(Ty)) { + boundsSafetyZeroInit(CGF, BaseAddrGen(), IsVolatileQualified); + } else if (auto RTy = Ty->getAs()) { + auto RD = RTy->getDecl(); + assert(RD); + if (RD->isUnion()) { + for (auto F : RD->fields()) { + auto FTy = F->getType(); + if (ShouldInitializeType(FTy)) { + // zero-init the whole record + boundsSafetyZeroInit(CGF, BaseAddrGen(), IsVolatileQualified); + break; + } + } + } else { + for (auto F : RD->fields()) { + if (F->isZeroSize(CGF.getContext())) + continue; + + std::optional
NewBase; + auto FieldAddrGenAction = [&]() { + const unsigned FieldIdx = + CGF.CGM.getTypes().getCGRecordLayout(RD).getLLVMFieldNo(F); + return CGF.Builder.CreateStructGEP( + BaseAddrGen(), FieldIdx, F->getName()); + }; + auto FieldAddrGen = + makeCachingAddressInitializer(FieldAddrGenAction, NewBase); + boundsSafetyRecursiveInit(CGF, F, F->getType(), + IsVolatileQualified || F->getType().isVolatileQualified(), + FieldAddrGen); + } + } + } else if (auto CATy = dyn_cast(Ty)) { + auto ElemTy = CATy->getElementType(); + if (ShouldInitializeType(ElemTy)) { + boundsSafetyZeroInit(CGF, BaseAddrGen(), + IsVolatileQualified || ElemTy.isVolatileQualified()); + } else { + const uint64_t Size = CATy->getSize().getZExtValue(); + llvm::Constant *ZeroIdx = llvm::ConstantInt::get(CGF.IntTy, 0); + + for (uint64_t Idx = 0; Idx < Size; ++Idx) { + std::optional
NewBase; + auto ElemAddrGenAction = [&]() { + Address BaseAddr = BaseAddrGen(); + auto *CIIdx = llvm::ConstantInt::get(CGF.IntPtrTy, Idx); + CharUnits offset = dyn_cast(CIIdx)->getZExtValue() * + CGF.getContext().getTypeSizeInChars(ElemTy); + CharUnits ElemAlignment = + BaseAddr.getAlignment().alignmentAtOffset(offset); + return CGF.Builder.CreateGEP(BaseAddr, {ZeroIdx, CIIdx}, + CGF.ConvertTypeForMem(ElemTy), + ElemAlignment, ""); + }; + auto ElemAddrGen = + makeCachingAddressInitializer(ElemAddrGenAction, NewBase); + boundsSafetyRecursiveInit(CGF, nullptr, ElemTy, + IsVolatileQualified || ElemTy.isVolatileQualified(), + ElemAddrGen); + } + } + } +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { assert(emission.Variable && "emission was not valid!"); @@ -1965,6 +2087,14 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { const Address Loc = locIsByrefHeader ? emission.getObjectAddress(*this) : emission.Addr; + /* TO_UPSTREAM(BoundsSafety) ON */ + if (!Init && CGM.getLangOpts().BoundsSafety) { + auto ReturnLoc = [&]() { return Loc; }; + boundsSafetyRecursiveInit(*this, &D, D.getType(), + D.getType().isVolatileQualified(), ReturnLoc); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + auto hasNoTrivialAutoVarInitAttr = [&](const Decl *D) { return D && D->hasAttr(); }; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index bba7d1e805f3f..6b14f6b18ba6e 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -22,6 +22,8 @@ #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" +#include "BoundsSafetyOptRemarks.h" +#include "BoundsSafetyTraps.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTLambda.h" @@ -89,6 +91,231 @@ enum VariableTypeDescriptorKind : uint16_t { // Miscellaneous Helper Methods //===--------------------------------------------------------------------===// +llvm::Value *CodeGenFunction::GetWidePointerElement(Address Addr, + WPIndex Index) { + auto GetElemStr = [&]() { + switch (Index) { + case WPIndex::Pointer: + return "ptr"; + case WPIndex::Upper: + return "ub"; + case WPIndex::Lower: + return "lb"; + } + }; + + std::string Name = "wide_ptr."; + Name += GetElemStr(); + Address ElemAddr = + Builder.CreateStructGEP(Addr, (unsigned int)Index, Name + ".addr"); + return Builder.CreateLoad(ElemAddr, Name); +} + +void CodeGenFunction::EmitPtrCastLECheck(llvm::Value *LHS, llvm::Value *RHS, + BoundsSafetyTrapKind TrapKind, + BoundsSafetyTrapCtx::Kind TrapCtx) { + // TODO(dliew): rdar://109574814: opt-remarks could be added here. + { + BoundsSafetyOptRemarkScope Scope(this, GetBoundsSafetyOptRemarkForTrap(TrapKind)); + llvm::Value *Check = Builder.CreateICmpULE(LHS, RHS); + EmitBoundsSafetyTrapCheck(Check, TrapKind, TrapCtx); + } +} + +void CodeGenFunction::EmitBoundsSafetyBoundsCheck( + llvm::Type *ElemTy, llvm::Value *Ptr, llvm::Value *Upper, + llvm::Value *Lower, bool AcceptNullPtr, + BoundsSafetyTrapCtx::Kind TrapCtx) { + if (!Upper && !Lower) + return; + assert(TrapCtx != BoundsSafetyTrapCtx::UNKNOWN); + + // rdar://109574814: opt-remarks could be added here. + llvm::BranchInst *NullCheckBranch = nullptr; + if (AcceptNullPtr) { + // Test that the pointer is not NULL before testing that it's in bounds, + // if AcceptNullPtr is specified. + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_NEQ_NULL); + llvm::BasicBlock *NotNull = createBasicBlock("boundscheck.notnull"); + + llvm::Value *Null = llvm::Constant::getNullValue(Ptr->getType()); + llvm::Value *PtrIsNull = Builder.CreateICmpNE(Ptr, Null); + NullCheckBranch = Builder.CreateCondBr(PtrIsNull, NotNull, nullptr); + EmitBlock(NotNull); + } + + if (Upper) { + if (getLangOpts().hasNewBoundsSafetyCheck(LangOptions::BS_CHK_AccessSize) && + ElemTy->isSized() && CGM.getDataLayout().getTypeStoreSize(ElemTy) > 1) { + // For sized types larger than 1 byte take the size of a potential access + // into account. For something like: + // + // T* ptr; + // + // We check + // 1. `ptr.ptr + sizeof(T)` does not overflow + // 2. `ptr.ptr + sizeof(T) <= ptr.upper` + // + assert(ElemTy); + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_GT_UPPER_BOUND); + llvm::Value *OnePastTheEndPtr = + Builder.CreateGEP(ElemTy, Ptr, llvm::ConstantInt::get(SizeTy, 1)); + // Emitting the upper bound check first since it's more + // optimization-friendly. This is because the upper bound calculation (ptr + // + size) is often marked 'inbounds' if 'ptr' is '__counted_by' or an + // array decay of a sized array. This allows ConstraintElimination to use + // this information to infer subsequent pointer arithmetic (ptr + i; where + // 'i <= size') doesn't wrap. + EmitBoundsSafetyTrapCheck(Builder.CreateICmpULE(OnePastTheEndPtr, Upper), + BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx); + EmitBoundsSafetyTrapCheck(Builder.CreateICmpULE(Ptr, OnePastTheEndPtr), + BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx); + } else { + // Path where the size of the access is assumed to be 1 byte. This is used + // for + // + // * 1-byte access when `BS_CHK_AccessSize` enabled. + // * unsized types (e.g. function types) + // * Legacy bounds checks (i.e. `BS_CHK_AccessSize` is disabled). + // + // In this case for something like + // + // T* ptr; + // + // We assume that `sizeof(T)` is 1. In this case + // + // 1. The `ptr.ptr + 1` overflow check is unnecessary and so we skip + // emitting it. + // 2. The `ptr.ptr + sizeof(T) <= ptr.upper` check simplifies to + // `ptr.ptr < ptr.upper`. + // + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_GE_UPPER_BOUND); + llvm::Value *Check = Builder.CreateICmpULT(Ptr, Upper); + EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_PTR_GE_UPPER_BOUND, TrapCtx); + } + } + + if (Lower) { + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_LT_LOWER_BOUND); + llvm::Value *Check = Builder.CreateICmpUGE(Ptr, Lower); + EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_PTR_LT_LOWER_BOUND, TrapCtx); + } + if (NullCheckBranch) + NullCheckBranch->setSuccessor(1, Builder.GetInsertBlock()); +} + +void CodeGenFunction::EmitBoundsSafetyRangeCheck(llvm::Value *LowerBound, + llvm::Value *LowerAccess, + llvm::Value *UpperAccess, + llvm::Value *UpperBound, + BoundsSafetyTrapCtx::Kind TrapCtx) { + // rdar://109574814: opt-remarks could be added here. + + // LowerBound <= LowerAccess <= UpperAccess <= UpperBound. + // This function assumes that LowerAccess <= UpperAccess and + // LowerBound <= UpperBound, so in practice it only checks + // LowerBound <= LowerAccess && UpperAccess <= UpperBound. + if (UpperAccess != UpperBound) { + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_GE_UPPER_BOUND); + llvm::Value *UpperCheck = Builder.CreateICmpULE(UpperAccess, UpperBound); + assert(TrapCtx != BoundsSafetyTrapCtx::UNKNOWN); + EmitBoundsSafetyTrapCheck(UpperCheck, BNS_TRAP_PTR_GE_UPPER_BOUND, TrapCtx); + } + + if (LowerBound != LowerAccess) { + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_LT_LOWER_BOUND); + llvm::Value *LowerCheck = Builder.CreateICmpULE(LowerBound, LowerAccess); + assert(TrapCtx != BoundsSafetyTrapCtx::UNKNOWN); + EmitBoundsSafetyTrapCheck(LowerCheck, BNS_TRAP_PTR_LT_LOWER_BOUND, TrapCtx); + } +} + +llvm::Value *CodeGenFunction::EmitWideToRawPtr(const Expr *E, bool BoundsCheck, + BoundsSafetyTrapCtx::Kind TrapCtx, + bool LowerOnlyCheck, + llvm::Value **UpperPtr, + llvm::Value **LowerPtr, + bool IsNullAllowed) { + auto PT = E->getType()->getAs(); + assert(PT && !PT->hasRawPointerLayout() && "Expected wide pointer type"); + RValue PtrRV = EmitAnyExpr(E); + Address PtrAddress = PtrRV.getAggregateAddress(); + llvm::Value *Ptr = GetWidePointerElement(PtrAddress, WPIndex::Pointer); + llvm::Value *Upper = GetWidePointerElement(PtrAddress, WPIndex::Upper); + llvm::Value *Lower = PT->isBidiIndexable() + ? GetWidePointerElement(PtrAddress, WPIndex::Lower) + : nullptr; + // XXX: Lower bound check only for wide to counted_by + if (BoundsCheck || LowerOnlyCheck) + EmitBoundsSafetyBoundsCheck(ConvertTypeForMem(PT->getPointeeType()), Ptr, + LowerOnlyCheck ? nullptr : Upper, Lower, + IsNullAllowed, TrapCtx); + if (UpperPtr) + *UpperPtr = Upper; + if (LowerPtr) + *LowerPtr = Lower; + return Ptr; +} + +llvm::Value *CodeGenFunction::EmitTerminator(const llvm::APSInt &Terminator, + llvm::Type *Ty) { + if (Terminator.isZero()) + return llvm::Constant::getNullValue(Ty); + if (Ty->isPointerTy()) { + auto *IntPtrTy = CGM.getDataLayout().getIntPtrType(Ty); + auto *C = llvm::ConstantInt::get(IntPtrTy, Terminator.getSExtValue()); + return Builder.CreateIntToPtr(C, Ty); + } + if (Terminator.isSigned()) + return llvm::ConstantInt::getSigned(Ty, Terminator.getSExtValue()); + return llvm::ConstantInt::get(Ty, Terminator.getZExtValue()); +} + +void CodeGenFunction::EmitValueTerminatedPointerArithmeticCheck( + QualType PointerType, llvm::Value *Ptr) { + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_TERMINATED_BY_PTR_ARITH); + assert(PointerType->isPointerType()); + QualType PointeeType = PointerType->getPointeeType(); + + CharUnits Alignment = getContext().getTypeAlignInChars(PointeeType); + Address PtrAddr = Address(Ptr, ConvertTypeForMem(PointeeType), Alignment); + llvm::Value *Val = Builder.CreateLoad(PtrAddr); + + const auto *VTT = PointerType->getAs(); + llvm::APSInt TermVal = VTT->getTerminatorValue(getContext()); + llvm::Value *Term = EmitTerminator(TermVal, Val->getType()); + + llvm::Value *Check = Builder.CreateICmpNE(Val, Term); + EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_TERMINATED_BY_PTR_ARITH); +} + +void CodeGenFunction::EmitValueTerminatedAssignmentCheck( + const BinaryOperator *E, LValue Value) { + BoundsSafetyOptRemarkScope Scope(this, BNS_OR_TERMINATED_BY_TERM_ASSIGN); + const Expr *LHS = E->getLHS()->IgnoreParenCasts(); + const Expr *PtrE = nullptr; + if (const auto *UnOp = dyn_cast(LHS)) { + if (UnOp->getOpcode() == UO_Deref) + PtrE = UnOp->getSubExpr(); + } else if (const auto *ASE = dyn_cast(LHS)) { + PtrE = ASE->getBase(); + } + if (!PtrE) + return; + + const auto *VTT = PtrE->getType()->getAs(); + if (!VTT) + return; + + llvm::Value *Val = EmitLoadOfLValue(Value, E->getExprLoc()).getScalarVal(); + + llvm::APSInt TermVal = VTT->getTerminatorValue(getContext()); + llvm::Value *Term = EmitTerminator(TermVal, Val->getType()); + + llvm::Value *Check = Builder.CreateICmpNE(Val, Term); + EmitBoundsSafetyTrapCheck(Check, BNS_TRAP_TERMINATED_BY_TERM_ASSIGN); +} + /// CreateTempAlloca - This creates a alloca and inserts it into the entry /// block. RawAddress @@ -215,6 +442,15 @@ llvm::Value *CodeGenFunction::EvaluateExprAsBool(const Expr *E) { QualType BoolTy = getContext().BoolTy; SourceLocation Loc = E->getExprLoc(); CGFPOptionsRAII FPOptsRAII(*this, E); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + auto *PT = E->getType()->getAs(); + if (PT && !PT->hasRawPointerLayout()) + return EmitScalarConversion( + EmitWideToRawPtr(E), getContext().getPointerType(PT->getPointeeType()), + BoolTy, Loc); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (!E->getType()->isAnyComplexType()) return EmitScalarConversion(EmitScalarExpr(E), E->getType(), BoolTy, Loc); @@ -1249,6 +1485,162 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound, SanitizerHandler::OutOfBounds, StaticData, Index); } +static BoundsSafetyTrapCtx::Kind +GetTrapCtxFromPredefinedBoundsCheckExpr(const PredefinedBoundsCheckExpr *E) { + switch (E->getKind()) { + case BoundsCheckKind::FlexibleArrayCountAssign: + return BoundsSafetyTrapCtx::ASSIGN; + case BoundsCheckKind::FlexibleArrayCountCast: + return BoundsSafetyTrapCtx::CAST; + case BoundsCheckKind::FlexibleArrayCountDeref: + return BoundsSafetyTrapCtx::DEREF; + } + llvm_unreachable("Unhandled BoundsCheckKind"); +} + +static llvm::Value *getPtrSubDivisor(CodeGenFunction &CGF, + QualType elementType) { + assert(!elementType->isVariableArrayType()); + CharUnits elementSize; + // Handle GCC extension for pointer arithmetic on void* and + // function pointer types. + if (elementType->isVoidType() || elementType->isFunctionType()) + elementSize = CharUnits::One(); + else + elementSize = CGF.getContext().getTypeSizeInChars(elementType); + + // Don't even emit the divide for element size of 1. + if (elementSize.isOne()) + return nullptr; + + return CGF.CGM.getSize(elementSize); +} + +/// @brief This function performs the check to ensure `Count <= Upper - FamPtr` +/// and other bounds checks related to the count and the pointers, e.g., checks +/// to ensure `Count >= 0` and `Upper >= FamPtr`. +void CodeGenFunction::EmitFlexibleArrayCountCheck( + const PredefinedBoundsCheckExpr *E) { + const Expr *BasePtr = E->getFamBasePtr(); + const Expr *FamPtr = E->getFamPtr(); + const Expr *Count = E->getFamCount(); + + assert(BasePtr && FamPtr && Count); + + llvm::Value *Upper; + llvm::Value *Lower; + BoundsSafetyTrapCtx::Kind TrapCtx = GetTrapCtxFromPredefinedBoundsCheckExpr(E); + // Necessary bounds checks are performed later. + llvm::Value *Ptr = EmitWideToRawPtr(BasePtr, /*BoundsCheck*/ false, TrapCtx, + /*LowerOnlyCheck*/ false, &Upper, &Lower); + + llvm::BranchInst *NullCheckBranch = nullptr; + // Deref doesn't survive with null. + if (E->getKind() != BoundsCheckKind::FlexibleArrayCountDeref) { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(this, BNS_OR_PTR_NEQ_NULL); + + llvm::BasicBlock *NonnullBB = createBasicBlock("flex.base.nonnull"); + + llvm::Value *Null = llvm::Constant::getNullValue(Ptr->getType()); + llvm::Value *PtrNonnull = + Builder.CreateICmpNE(Ptr, Null, "flex.base.null.check"); + NullCheckBranch = Builder.CreateCondBr(PtrNonnull, NonnullBB, nullptr); + EmitBlock(NonnullBB); + } + + // `FamPtr` is essentially `Ptr + FamOffset` and we need an overflow check + // for this calculation. Evaluating `FamPtr` would be emitted as StructGEP + // which always gets `inbounds` which will make `Ptr <= Ptr + FamOffset` be + // considered true. Hence we do the pointer arithmetic without `inbouds` here. + // We don't have the exact FamOffset here so we actually do `Ptr <= Ptr + + // sizeof(*Ptr)` instead but this should cover the overflow check because + // `sizeof(*Ptr) <= FamOffset`. + llvm::Type *BaseElemTy = + ConvertTypeForMem(BasePtr->getType()->getPointeeType()); + llvm::Constant *One = llvm::ConstantInt::get(SizeTy, 1); + llvm::Value *PtrPlusSize = Builder.CreateGEP(BaseElemTy, Ptr, One, ""); + EmitPtrCastLECheck(Ptr, PtrPlusSize, BNS_TRAP_PTR_PAST_END_OVERFLOW); + + // XXX: Some redundant bounds checks are performed on the struct base pointer + // in `EmitMemberExpr` in order to emit FamPtr. This is not a big problem + // because redundant checks go away in optimized builds so we leave it as is + // for now. rdar://104875616 + llvm::Value *FamPtrVal = EmitScalarExpr(FamPtr); + llvm::Value *UserProvidedCount = EmitScalarExpr(Count); + + // Ensure `Count >= 0`. + if (Count->getType()->isSignedIntegerOrEnumerationType()) { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(this, BNS_OR_COUNT_NEGATIVE); + llvm::Value *Zero = llvm::ConstantInt::get(UserProvidedCount->getType(), 0); + llvm::Value *ZeroLE = + Builder.CreateICmpSLE(Zero, UserProvidedCount, "flex.count.minus"); + EmitBoundsSafetyTrapCheck(ZeroLE, BNS_TRAP_COUNT_NEGATIVE, TrapCtx); + } + + { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(this, + BNS_OR_FLEX_COUNT_GT_BOUNDS); + + // Ensure `FamPtr (Ptr + FamOffset) <= Upper`. + // This is necessary because we later do `Upper - FamPtr` to get the + // avaiable count. + EmitPtrCastLECheck(FamPtrVal, Upper, BNS_TRAP_PTR_GT_UPPER_BOUND, TrapCtx); + + // Ensure Lower <= Ptr. + EmitBoundsSafetyBoundsCheck(nullptr, Ptr, nullptr, Lower, + /*AcceptNullPtr*/ false, TrapCtx); + + QualType ElemTy = FamPtr->getType()->getPointeeType(); + + llvm::Value *UpperIntPtr = + Builder.CreatePtrToInt(Upper, PtrDiffTy, "upper.intptr"); + llvm::Value *FamIntPtr = + Builder.CreatePtrToInt(FamPtrVal, PtrDiffTy, "fam.intptr"); + // `diffInChars = Upper - FamPtrVal`. We can do this subtraction because + // we ensure `FamPtrVal <= Upper` in the previous bounds check in the + // function. + llvm::Value *diffInChars = Builder.CreateSub( + UpperIntPtr, FamIntPtr, "flex.avail.count", /*HasNUW*/ true); + llvm::Value *PointerDerivedCount = diffInChars; + // `PointerDerivedCount = diffInChars / sizeof(Fam[0])`. In other words, we + // get the element count by dividing the byte count with the element type + // size. + if (llvm::Value *divisor = getPtrSubDivisor(*this, ElemTy)) { + PointerDerivedCount = + Builder.CreateExactSDiv(diffInChars, divisor, "flex.avail.count.div"); + } + assert(PointerDerivedCount->getType() == IntPtrTy); + // It's safe to zero extend the count because we previously ensured `Count + // >= 0` in this function. + UserProvidedCount = + Builder.CreateIntCast(UserProvidedCount, IntPtrTy, + /*isSigned*/ false, "flex.count.intptr"); + // Ensure `Count <= PointerDerivedCount (Upper - FamPtr)`. + llvm::Value *CountCheck = Builder.CreateICmpULE( + UserProvidedCount, PointerDerivedCount, "flex.count.check"); + EmitBoundsSafetyTrapCheck(CountCheck, BNS_TRAP_FLEX_COUNT_GT_BOUNDS); + } + + if (NullCheckBranch) + NullCheckBranch->setSuccessor(1, Builder.GetInsertBlock()); +} + +void CodeGenFunction::EmitBoundsSafetyTrapCheck(const Expr *Cond, + BoundsSafetyTrapKind kind) { + auto OptRemark = GetBoundsSafetyOptRemarkForTrap(kind); + assert(BoundsSafetyOptRemarkScope::InScope(this, OptRemark)); + llvm::Value *CondVal = EvaluateExprAsBool(Cond); + + // The result of `EvaluateExprAsBool()` might be a phi instruction + // which is not created via `Builder` which means BoundsSafetyOptRemarkScope + // won't annotate it. Annotate the instruction explicitly. + if (auto *I = llvm::dyn_cast(CondVal)) { + I->addAnnotationMetadata(GetBoundsSafetyOptRemarkString(OptRemark)); + } + + EmitBoundsSafetyTrapCheck(CondVal, kind); +} + CodeGenFunction::ComplexPairTy CodeGenFunction:: EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV, bool isInc, bool isPre) { @@ -1301,6 +1693,8 @@ void CodeGenModule::EmitExplicitCastExprType(const ExplicitCastExpr *E, static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, TBAAAccessInfo *TBAAInfo, + llvm::Value **UpperPtr, + llvm::Value **LowerPtr, KnownNonNull_t IsKnownNonNull, CodeGenFunction &CGF) { // We allow this with ObjC object pointers because of fragile ABIs. @@ -1325,7 +1719,8 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, LValueBaseInfo InnerBaseInfo; TBAAAccessInfo InnerTBAAInfo; Address Addr = CGF.EmitPointerWithAlignment( - CE->getSubExpr(), &InnerBaseInfo, &InnerTBAAInfo, IsKnownNonNull); + CE->getSubExpr(), &InnerBaseInfo, &InnerTBAAInfo, UpperPtr, + LowerPtr, IsKnownNonNull); if (BaseInfo) *BaseInfo = InnerBaseInfo; if (TBAAInfo) *TBAAInfo = InnerTBAAInfo; @@ -1355,6 +1750,58 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, CE->getBeginLoc()); } + /* TO_UPSTREAM(BoundsSafety) ON */ + if (auto DestPtTy = E->getType()->getAs()) { + if (!DestPtTy->hasRawPointerLayout()) { + QualType ETy = DestPtTy->getPointeeType(); + llvm::Type *DestPointeeType = CGF.ConvertTypeForMem(ETy); + if (DestPointeeType->isVoidTy()) + DestPointeeType = llvm::Type::getInt8Ty(CGF.getLLVMContext()); + + unsigned AS = DestPointeeType->isFunctionTy() + ? CGF.CGM.getDataLayout().getProgramAddressSpace() + : CGF.getTypes().getTargetAddressSpace(ETy); + llvm::Type *DestTy = llvm::PointerType::get(DestPointeeType, AS); + + auto RemoveBoundsSafetyAttrs = [&] (const QualType& QT) { + if (auto PtrTy = QT->getAs()) { + if (!PtrTy->hasRawPointerLayout()) { + const auto SrcPointeeTy = PtrTy->getPointeeType(); + const auto SrcPtrRawTy = + CGF.CGM.getContext().getPointerType(SrcPointeeTy); + return QualType(SrcPtrRawTy.getTypePtr(), QT.getQualifiers().getAsOpaqueValue()); + } + } + return QT; + }; + + const QualType DestPtrTyForAuth = RemoveBoundsSafetyAttrs(CE->getType()); + const QualType SrcPtrTyForAuth = RemoveBoundsSafetyAttrs(CE->getSubExpr()->getType()); + + auto CastBoundPtr = [&CGF, &DestTy, &SrcPtrTyForAuth, + &DestPtrTyForAuth, &CE](llvm::Value **Bound) { + if (Bound && *Bound) { + *Bound = CE->getCastKind() != CK_AddressSpaceConversion + ? CGF.Builder.CreateBitCast(*Bound, DestTy) + : CGF.Builder.CreateAddrSpaceCast(*Bound, DestTy); + *Bound = CGF.authPointerToPointerCast(*Bound, SrcPtrTyForAuth, + DestPtrTyForAuth); + } + }; + + CastBoundPtr(UpperPtr); + CastBoundPtr(LowerPtr); + + Address Result = + CE->getCastKind() != CK_AddressSpaceConversion + ? Addr.withElementType(DestPointeeType) + : CGF.Builder.CreateAddrSpaceCast(Addr, DestTy, + DestPointeeType); + return CGF.authPointerToPointerCast( + Result, CE->getSubExpr()->getType(), CE->getType()); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF */ llvm::Type *ElemTy = CGF.ConvertTypeForMem(E->getType()->getPointeeType()); Addr = Addr.withElementType(ElemTy); @@ -1379,7 +1826,7 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, if (TBAAInfo) *TBAAInfo = CGF.CGM.getTBAAAccessInfo(E->getType()); Address Addr = CGF.EmitPointerWithAlignment( - CE->getSubExpr(), BaseInfo, nullptr, + CE->getSubExpr(), BaseInfo, nullptr, nullptr, nullptr, (KnownNonNull_t)(IsKnownNonNull || CE->getCastKind() == CK_UncheckedDerivedToBase)); auto Derived = CE->getSubExpr()->getType()->getPointeeCXXRecordDecl(); @@ -1423,9 +1870,20 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, // TODO: conditional operators, comma. + /*TO_UPSTREAM(BoundsSafety) ON*/ + // Extract the pointer from wide pointer + auto PT = E->getType()->getAs(); + llvm::Value *Base = + (PT && !PT->hasRawPointerLayout()) + ? CGF.EmitWideToRawPtr(E, /*BoundsCheck=*/false, + /*TrapCtx=*/BoundsSafetyTrapCtx::UNKNOWN, + /*LowerOnlyCheck=*/false, UpperPtr, LowerPtr) + : CGF.EmitScalarExpr(E); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + // Otherwise, use the alignment of the type. return CGF.makeNaturalAddressForPointer( - CGF.EmitScalarExpr(E), E->getType()->getPointeeType(), CharUnits(), + Base, E->getType()->getPointeeType(), CharUnits(), /*ForPointeeType=*/true, BaseInfo, TBAAInfo, IsKnownNonNull); } @@ -1433,9 +1891,10 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, /// derive a more accurate bound on the alignment of the pointer. Address CodeGenFunction::EmitPointerWithAlignment( const Expr *E, LValueBaseInfo *BaseInfo, TBAAAccessInfo *TBAAInfo, + llvm::Value **UpperPtr, llvm::Value **LowerPtr, KnownNonNull_t IsKnownNonNull) { - Address Addr = - ::EmitPointerWithAlignment(E, BaseInfo, TBAAInfo, IsKnownNonNull, *this); + Address Addr = ::EmitPointerWithAlignment(E, BaseInfo, TBAAInfo, UpperPtr, + LowerPtr, IsKnownNonNull, *this); if (IsKnownNonNull && !Addr.isKnownNonNull()) Addr.setKnownNonNull(); return Addr; @@ -1514,7 +1973,12 @@ bool CodeGenFunction::IsWrappedCXXThis(const Expr *Obj) { LValue CodeGenFunction::EmitCheckedLValue(const Expr *E, TypeCheckKind TCK) { LValue LV; - if (SanOpts.has(SanitizerKind::ArrayBounds) && isa(E)) + auto *ASE = dyn_cast(E); + if (ASE && (SanOpts.has(SanitizerKind::ArrayBounds) || + /*TO_UPSTREAM(BoundsSafety) ON*/ + + ASE->getBase()->getType()->isPointerTypeWithBounds())) + /*TO_UPSTREAM(BoundsSafety) OFF*/ LV = EmitArraySubscriptExpr(cast(E), /*Accessed*/true); else LV = EmitLValue(E); @@ -1665,6 +2129,22 @@ LValue CodeGenFunction::EmitLValueHelper(const Expr *E, CXXDefaultInitExprScope Scope(*this, DIE); return EmitLValue(DIE->getExpr(), IsKnownNonNull); } + + case Expr::BoundsCheckExprClass: + return EmitBoundsCheckExprLValue(cast(E)); + case Expr::PredefinedBoundsCheckExprClass: + return EmitPredefinedBoundsCheckExprLValue( + cast(E)); + case Expr::AssumptionExprClass: + return EmitAssumptionExprLValue(cast(E)); + case Expr::ForgePtrExprClass: + return EmitForgePtrExprLValue(cast(E)); + case Expr::BoundsSafetyPointerPromotionExprClass: + return EmitBoundsSafetyPointerPromotionExprLValue( + cast(E)); + case Expr::MaterializeSequenceExprClass: + return EmitMaterializeSequenceExprLValue(cast(E)); + case Expr::CXXTypeidExprClass: return EmitCXXTypeidLValue(cast(E)); @@ -1842,8 +2322,11 @@ CodeGenFunction::tryEmitAsConstant(const DeclRefExpr *RefExpr) { } // Emit as a constant. - llvm::Constant *C = ConstantEmitter(*this).emitAbstract( - RefExpr->getLocation(), result.Val, resultType); + // Try to emit as a constant. + llvm::Constant *C = + ConstantEmitter(*this).tryEmitAbstract(result.Val, resultType); + if (!C) + return ConstantEmission(); // Make sure we emit a debug reference to the global variable. // This should probably fire even for @@ -2996,6 +3479,18 @@ llvm::Constant *CodeGenModule::getRawFunctionPointer(GlobalDecl GD, } llvm::Constant *V = GetAddrOfFunction(GD, Ty); + if (!FD->hasPrototype()) { + if (const FunctionProtoType *Proto = + FD->getType()->getAs()) { + // Ugly case: for a K&R-style definition, the type of the definition + // isn't the same as the type of a use. Correct for this with a + // bitcast. + QualType NoProtoType = + getContext().getFunctionNoProtoType(Proto->getReturnType()); + NoProtoType = getContext().getPointerType(NoProtoType); + V = llvm::ConstantExpr::getBitCast(V,getTypes().ConvertType(NoProtoType)); + } + } return V; } @@ -3318,9 +3813,19 @@ LValue CodeGenFunction::EmitUnaryOpLValue(const UnaryOperator *E) { LValueBaseInfo BaseInfo; TBAAAccessInfo TBAAInfo; - Address Addr = EmitPointerWithAlignment(E->getSubExpr(), &BaseInfo, - &TBAAInfo); - LValue LV = MakeAddrLValue(Addr, T, BaseInfo, TBAAInfo); + /*TO_UPSTREAM(BoundsSafety) ON*/ + LValue LV; + const auto *PT = E->getSubExpr()->getType()->getAs(); + if (PT && !PT->hasRawPointerLayout()) { + llvm::Value *Ptr = EmitWideToRawPtr(E->getSubExpr(), /*BoundsCheck=*/true, + /*TrapCtx=*/BoundsSafetyTrapCtx::DEREF); + LV = MakeAddrLValue(Ptr, T, getContext().getTypeAlignInChars(T)); + } else { + /*TO_UPSTREAM(BoundsSafety) OFF*/ + Address Addr = + EmitPointerWithAlignment(E->getSubExpr(), &BaseInfo, &TBAAInfo); + LV = MakeAddrLValue(Addr, T, BaseInfo, TBAAInfo); + } LV.getQuals().setAddressSpace(ExprTy.getAddressSpace()); // We should not generate __weak write barrier on indirect reference @@ -3333,6 +3838,13 @@ LValue CodeGenFunction::EmitUnaryOpLValue(const UnaryOperator *E) { LV.setNonGC(!E->isOBJCGCCandidate(getContext())); return LV; } + /*TO_UPSTREAM(BoundsSafety) ON*/ + case UO_AddrOf: { + if (!E->getType()->isPointerTypeWithBounds()) + llvm_unreachable("Unknown unary operator lvalue!"); + return EmitAggExprToLValue(E); + } + /*TO_UPSTREAM(BoundsSafety) ON*/ case UO_Real: case UO_Imag: { LValue LV = EmitLValue(E->getSubExpr()); @@ -3992,7 +4504,11 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) { void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID, - bool NoMerge) { + bool NoMerge, + /*TO_UPSTREAM(BoundsSafety) ON*/ + StringRef Annotation, + StringRef TrapMessage) { + /*TO_UPSTREAM(BoundsSafety) OFF*/ llvm::BasicBlock *Cont = createBasicBlock("cont"); // If we're optimizing, collapse all calls to trap down to just one per @@ -4002,43 +4518,116 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID]; + /*TO_UPSTREAM(BoundsSafety) ON*/ + llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation(); + if (CheckHandlerID == SanitizerHandler::BoundsSafety && getDebugInfo()) { + TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor( + TrapLocation, GetBoundsSafetyTrapMessagePrefix(), TrapMessage); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + NoMerge = NoMerge || !CGM.getCodeGenOpts().OptimizationLevel || (CurCodeDecl && CurCodeDecl->hasAttr()); llvm::MDBuilder MDHelper(getLLVMContext()); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + NoMerge |= CGM.getCodeGenOpts().TrapFuncReturns; + NoMerge |= CGM.getCodeGenOpts().UniqueTrapBlocks; + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (TrapBB && !NoMerge) { auto Call = TrapBB->begin(); assert(isa(Call) && "Expected call in trap BB"); - Call->applyMergedLocation(Call->getDebugLoc(), - Builder.getCurrentDebugLocation()); - Builder.CreateCondBr(Checked, Cont, TrapBB, + Call->applyMergedLocation(Call->getDebugLoc(), TrapLocation); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Merge the debug info on the unreachable too so its debug info is not + // stale. + auto Unreachable = ++TrapBB->begin(); + assert(isa(Unreachable) && + "Expected unreachable instruction in trap BB"); + Unreachable->applyMergedLocation(Unreachable->getDebugLoc(), TrapLocation); + + if (!Annotation.empty()) { + Call->addAnnotationMetadata(Annotation); + Unreachable->addAnnotationMetadata(Annotation); + } + auto *CondBrInst = Builder.CreateCondBr(Checked, Cont, TrapBB, MDHelper.createLikelyBranchWeights()); + + if (!Annotation.empty()) + CondBrInst->addAnnotationMetadata(Annotation); + /* TO_UPSTREAM(BoundsSafety) OFF*/ } else { - TrapBB = createBasicBlock("trap"); - Builder.CreateCondBr(Checked, Cont, TrapBB, + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (CGM.getCodeGenOpts().UniqueTrapBlocks && + !CGM.getCodeGenOpts().TrapFuncReturns) + TrapBB = createUnmergeableBasicBlock("trap"); + else + TrapBB = createBasicBlock("trap"); + auto *BrInst = Builder.CreateCondBr(Checked, Cont, TrapBB, MDHelper.createLikelyBranchWeights()); - EmitBlock(TrapBB); - llvm::CallInst *TrapCall = - Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::ubsantrap), - llvm::ConstantInt::get(CGM.Int8Ty, CheckHandlerID)); + if (!Annotation.empty()) + BrInst->addAnnotationMetadata(Annotation); + /*TO_UPSTREAM(BoundsSafety) OFF*/ - if (!CGM.getCodeGenOpts().TrapFuncName.empty()) { - auto A = llvm::Attribute::get(getLLVMContext(), "trap-func-name", - CGM.getCodeGenOpts().TrapFuncName); - TrapCall->addFnAttr(A); + EmitBlock(TrapBB); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + ApplyDebugLocation applyTrapDI(*this, TrapLocation); + llvm::CallInst *TrapCall = nullptr; + if (CGM.getCodeGenOpts().TrapFuncReturns) { + auto *TrapID = llvm::ConstantInt::get(CGM.Int8Ty, CheckHandlerID); + llvm::FunctionType *FnType = + llvm::FunctionType::get(CGM.VoidTy, {TrapID->getType()}, false); + auto TrapFunc = CGM.CreateRuntimeFunction( + FnType, CGM.getCodeGenOpts().TrapFuncName); + TrapCall = EmitNounwindRuntimeCall(TrapFunc, {TrapID}); + Builder.CreateBr(Cont); + } else { + TrapCall = Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::ubsantrap), + llvm::ConstantInt::get(CGM.Int8Ty, CheckHandlerID)); + + if (!CGM.getCodeGenOpts().TrapFuncName.empty()) { + auto A = llvm::Attribute::get(getLLVMContext(), "trap-func-name", + CGM.getCodeGenOpts().TrapFuncName); + TrapCall->addFnAttr(A); + } + if (NoMerge) + TrapCall->addFnAttr(llvm::Attribute::NoMerge); + TrapCall->setDoesNotReturn(); + TrapCall->setDoesNotThrow(); + auto Unreachable = Builder.CreateUnreachable(); + if (!Annotation.empty()) + Unreachable->addAnnotationMetadata(Annotation); } - if (NoMerge) - TrapCall->addFnAttr(llvm::Attribute::NoMerge); - TrapCall->setDoesNotReturn(); - TrapCall->setDoesNotThrow(); - Builder.CreateUnreachable(); + + if (!Annotation.empty()) + TrapCall->addAnnotationMetadata(Annotation.str()); } + /*TO_UPSTREAM(BoundsSafety) OFF*/ EmitBlock(Cont); } +void CodeGenFunction::EmitBoundsSafetyTrapCheck(llvm::Value *Checked, + BoundsSafetyTrapKind kind, + BoundsSafetyTrapCtx::Kind TrapCtx) { + auto OptRemark = GetBoundsSafetyOptRemarkForTrap(kind); + assert(BoundsSafetyOptRemarkScope::InScope(this, OptRemark)); + + // We still need to pass `OptRemark` because not all emitted instructions + // can be covered by BoundsSafetyOptRemarkScope. This is because EmitTrapCheck + // caches basic blocks that contain instructions that need annotating. + EmitTrapCheck(Checked, SanitizerHandler::BoundsSafety, false, + GetBoundsSafetyOptRemarkString(OptRemark), + GetBoundsSafetyTrapMessageSuffix(kind, TrapCtx)); +} + llvm::CallInst *CodeGenFunction::EmitTrapCall(llvm::Intrinsic::ID IntrID) { llvm::CallInst *TrapCall = Builder.CreateCall(CGM.getIntrinsic(IntrID)); @@ -4056,7 +4645,9 @@ llvm::CallInst *CodeGenFunction::EmitTrapCall(llvm::Intrinsic::ID IntrID) { Address CodeGenFunction::EmitArrayToPointerDecay(const Expr *E, LValueBaseInfo *BaseInfo, - TBAAAccessInfo *TBAAInfo) { + TBAAAccessInfo *TBAAInfo, + // TO_UPSTREAM(BoundsSafety) + TBAAAccessInfo *EltTBAAInfo) { assert(E->getType()->isArrayType() && "Array to pointer decay must have array source type!"); @@ -4085,6 +4676,10 @@ Address CodeGenFunction::EmitArrayToPointerDecay(const Expr *E, QualType EltType = E->getType()->castAsArrayTypeUnsafe()->getElementType(); if (BaseInfo) *BaseInfo = LV.getBaseInfo(); if (TBAAInfo) *TBAAInfo = CGM.getTBAAAccessInfo(EltType); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (EltTBAAInfo) + *EltTBAAInfo = CGM.getTBAAInfoForSubobject(LV, EltType); + /* TO_UPSTREAM(BoundsSafety) OFF*/ return Addr.withElementType(ConvertTypeForMem(EltType)); } @@ -4274,6 +4869,116 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr, return Address(eltPtr, CGF.ConvertTypeForMem(eltType), eltAlign); } +Address CodeGenFunction::EmitArrayToWidePointerDecay( + const Expr *E, llvm::Value *&Upper, LValueBaseInfo *BaseInfo, + TBAAAccessInfo *TBAAInfo, TBAAAccessInfo *EltTBAAInfo) { + assert(E->getType()->isArrayType()); + Address Addr = EmitArrayToPointerDecay(E, BaseInfo, TBAAInfo, EltTBAAInfo); + llvm::Value *Ptr = Addr.getBasePointer(); + llvm::Value *Count = nullptr; + QualType OrigSrcTy = E->getType(); + QualType SrcTy = OrigSrcTy.getDesugaredType(getContext()); + if (auto CAT = dyn_cast(SrcTy)) + Count = llvm::ConstantInt::get(getLLVMContext(), CAT->getSize()); + else if (auto VAT = dyn_cast(SrcTy)) + Count = getVLASize(VAT).NumElts; + else if (isa(SrcTy)) { + if (const auto *DCPTy = OrigSrcTy->getAs()) { + const Expr *CountExpr = DCPTy->getCountExpr(); + assert(!isa(E->IgnoreParenCasts())); + Count = EmitScalarExpr(CountExpr); + assert(CountExpr->getType()->isIntegralOrEnumerationType()); + Count = CountExpr->getType()->isSignedIntegerOrEnumerationType() + ? Builder.CreateSExt(Count, IntPtrTy) + : Builder.CreateZExt(Count, IntPtrTy); + + // Propagate the assumption that unsigned count is non-negative because + // the optimizer doesn't have the concept of signess. + if (CountExpr->getType()->isUnsignedIntegerOrEnumerationType()) + Builder.CreateAssumption(Builder.CreateICmpSGE( + Count, llvm::Constant::getNullValue(Count->getType()), + "count.positive")); + } else + Count = llvm::ConstantInt::get(IntPtrTy, 0); + } else + llvm_unreachable("Unexpected array type"); + + // We assume here that the upper bounds have been checked for overflow at + // initialization. + Upper = Builder.CreateInBoundsGEP(Addr.getElementType(), Ptr, Count, "upper"); + + return Addr; +} + +LValue +CodeGenFunction::EmitWidePtrArraySubscriptExpr(const ArraySubscriptExpr *E, + bool Accessed) { + const auto *PT = E->getBase()->getType()->getAs(); + assert(PT && !PT->hasRawPointerLayout()); + + LValueBaseInfo BaseInfo; + TBAAAccessInfo TBAAInfo; + llvm::Value *Upper = nullptr; + llvm::Value *Lower = nullptr; + Address WidePtrAddr = Address::invalid(); + Address Addr = Address::invalid(); + QualType ArrayTy{}; + if (auto *Array = isSimpleArrayDecayOperand(E->getBase())) { + ArrayTy = Array->getType(); + // We need the element's TBAA info for array subscript, not the array's + // TBAA. + Addr = EmitArrayToWidePointerDecay(Array, Upper, &BaseInfo, + /*TBAAInfo*/ nullptr, + /*EltTBAAInfo*/ &TBAAInfo); + Lower = Addr.getBasePointer(); + } else { + RValue BaseRV = EmitAnyExpr(E->getBase()); + WidePtrAddr = BaseRV.getAggregateAddress(); + Addr = makeNaturalAddressForPointer( + GetWidePointerElement(WidePtrAddr, WPIndex::Pointer), E->getType(), + CGM.getNaturalTypeAlignment(E->getType(), &BaseInfo, &TBAAInfo)); + } + + llvm::Value *Idx = EmitScalarExpr(E->getIdx()); + QualType IdxTy = E->getIdx()->getType(); + bool IdxSigned = IdxTy->isSignedIntegerOrEnumerationType(); + + // Extend or truncate the index type to 32 or 64-bits. + if (Idx->getType() != IntPtrTy) + Idx = Builder.CreateIntCast(Idx, IntPtrTy, IdxSigned, "idxprom"); + + // For VLAs, the memory type resolves to the innermost element type. + // Hence, multiplying by the VLA size emulates the behavior of GEP over + // a constant array. + if (const VariableArrayType *vla = + getContext().getAsVariableArrayType(E->getType())) { + llvm::Value *numElements = getVLASize(vla).NumElts; + // Do not use `CreateNSWMul` because With -fbounds-safety GEP doesn't + // get 'inbounds' by default. So, this also emulates the behavior of + // GEP on a constant array indexed with a non-constant value. + Idx = Builder.CreateMul(Idx, numElements); + } + + QualType *ArrayTyPtr = ArrayTy.isNull() ? nullptr : &ArrayTy; + Addr = emitArraySubscriptGEP(*this, Addr, Idx, E->getType(), + /*inbounds*/ false, IdxSigned, E->getExprLoc(), + ArrayTyPtr, E->getBase()); + if (Accessed) { + // __indexable pointers should have been cast to __bidi_indexable in Sema. + assert(E->getBase()->getType()->isBidiIndexablePointerType()); + if (WidePtrAddr.isValid()) { + Upper = GetWidePointerElement(WidePtrAddr, WPIndex::Upper); + Lower = GetWidePointerElement(WidePtrAddr, WPIndex::Lower); + } + assert(!!Upper && !!Lower); + llvm::Type *ElemTy = ConvertTypeForMem(E->getType()); + EmitBoundsSafetyBoundsCheck(ElemTy, Addr.getBasePointer(), Upper, Lower, + /*AcceptNullPtr=*/false, + /*TrapCtx=*/BoundsSafetyTrapCtx::DEREF); + } + return MakeAddrLValue(Addr, E->getType(), BaseInfo, TBAAInfo); +} + /// The offset of a field from the beginning of the record. static bool getFieldOffsetInBits(CodeGenFunction &CGF, const RecordDecl *RD, const FieldDecl *Field, int64_t &Offset) { @@ -4331,6 +5036,9 @@ static std::optional getOffsetDifferenceInBits(CodeGenFunction &CGF, LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E, bool Accessed) { + const auto *PT = E->getBase()->getType()->getAs(); + if (PT && !PT->hasRawPointerLayout()) + return EmitWidePtrArraySubscriptExpr(E, Accessed); // The index must always be an integer, which is not an aggregate. Emit it // in lexical order (this complexity is, sadly, required by C++17). llvm::Value *IdxPre = @@ -4838,15 +5546,30 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) { // If this is s.x, emit s as an lvalue. If it is s->x, emit s as a scalar. LValue BaseLV; if (E->isArrow()) { + llvm::Value *Upper = nullptr; + llvm::Value *Lower = nullptr; LValueBaseInfo BaseInfo; TBAAAccessInfo TBAAInfo; - Address Addr = EmitPointerWithAlignment(BaseExpr, &BaseInfo, &TBAAInfo); + Address Addr = EmitPointerWithAlignment(BaseExpr, &BaseInfo, &TBAAInfo, + &Upper, &Lower); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (Upper) { + llvm::Value *Ptr = Addr.getBasePointer(); + llvm::Value *OnePastTheEndPtr = Builder.CreateGEP(Addr.getElementType(), Ptr, + llvm::ConstantInt::get(SizeTy, 1)); + EmitBoundsSafetyRangeCheck(Lower ? Lower : Ptr, Ptr, OnePastTheEndPtr, Upper, + BoundsSafetyTrapCtx::DEREF); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + QualType PtrTy = BaseExpr->getType()->getPointeeType(); SanitizerSet SkippedChecks; bool IsBaseCXXThis = IsWrappedCXXThis(BaseExpr); if (IsBaseCXXThis) SkippedChecks.set(SanitizerKind::Alignment, true); - if (IsBaseCXXThis || isa(BaseExpr)) + if (IsBaseCXXThis || isa(BaseExpr) || + isa(Addr.emitRawPointer(*this))) SkippedChecks.set(SanitizerKind::Null, true); EmitTypeCheck(TCK_MemberAccess, E->getExprLoc(), Addr, PtrTy, /*Alignment=*/CharUnits::Zero(), SkippedChecks); @@ -5401,13 +6124,7 @@ LValue CodeGenFunction::EmitConditionalOperatorLValue( LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { switch (E->getCastKind()) { case CK_ToVoid: - case CK_BitCast: - case CK_LValueToRValueBitCast: - case CK_ArrayToPointerDecay: case CK_FunctionToPointerDecay: - case CK_NullToMemberPointer: - case CK_NullToPointer: - case CK_IntegralToPointer: case CK_PointerToIntegral: case CK_PointerToBoolean: case CK_IntegralCast: @@ -5451,6 +6168,18 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { case CK_HLSLAggregateSplatCast: return EmitUnsupportedLValue(E, "unexpected cast lvalue"); + /*TO_UPSTREAM(BoundsSafety) ON*/ + case CK_LValueToRValueBitCast: + case CK_ArrayToPointerDecay: + case CK_NullToMemberPointer: + case CK_NullToPointer: + case CK_IntegralToPointer: { + if (!E->getType()->isPointerTypeWithBounds()) + return EmitUnsupportedLValue(E, "unexpected cast lvalue"); + return EmitAggExprToLValue(E); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + case CK_Dependent: llvm_unreachable("dependent cast kind in IR gen!"); @@ -5495,6 +6224,22 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { return LV; } + /*TO_UPSTREAM(BoundsSafety) ON*/ + case CK_BitCast: { + auto PT = E->getType()->getAs(); + if (PT->hasRawPointerLayout()) + return EmitUnsupportedLValue(E, "unexpected cast lvalue"); + return EmitAggExprToLValue(E); + } + + case CK_BoundsSafetyPointerCast: { + auto PT = E->getType()->getAs(); + if (PT->hasRawPointerLayout()) + return EmitLValue(E->getSubExpr()); + return EmitAggExprToLValue(E); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + case CK_UncheckedDerivedToBase: case CK_DerivedToBase: { const auto *DerivedClassTy = @@ -5820,6 +6565,69 @@ static GlobalDecl getGlobalDeclForDirectCall(const FunctionDecl *FD) { return GlobalDecl(FD); } +static unsigned getPointerAuthKeyValue(const ASTContext &Context, + const Expr *key) { + Expr::EvalResult result; + bool success = key->EvaluateAsInt(result, Context); + assert(success && "pointer auth key wasn't a constant?"); (void) success; + return result.Val.getInt().getZExtValue(); +} + +static bool isFunctionPointerAuth(CodeGenModule &CGM, const Expr *key, + const Expr *discriminator) { + // Verify that the ABI uses function-pointer signing at all. + auto &authSchema = CGM.getCodeGenOpts().PointerAuth.FunctionPointers; + if (!authSchema.isEnabled()) + return false; + + // Verify that the key matches the ABI's key. + if (authSchema.getKey() != getPointerAuthKeyValue(CGM.getContext(), key)) + return false; + + // If the ABI uses weird discrimination for function pointers, just give up. + assert(!authSchema.isAddressDiscriminated()); + if (authSchema.getOtherDiscrimination() + != PointerAuthSchema::Discrimination::None) { + return false; + } + + if (discriminator->getType()->isPointerType()) { + return discriminator->isNullPointerConstant(CGM.getContext(), + Expr::NPC_NeverValueDependent); + } else { + assert(discriminator->getType()->isIntegerType()); + Expr::EvalResult result; + return (discriminator->EvaluateAsInt(result, CGM.getContext()) && + result.Val.getInt() == 0); + } +} + +/// Given an expression for a function pointer that's been signed with +/// a variant scheme, and given a constant expression for the key value +/// and an expression for the discriminator, produce a callee for the +/// function pointer using that scheme. +static CGCallee EmitSignedFunctionPointerCallee(CodeGenFunction &CGF, + const Expr *functionPointerExpr, + const Expr *keyExpr, + const Expr *discriminatorExpr) { + llvm::Value *calleePtr = CGF.EmitScalarExpr(functionPointerExpr); + auto key = getPointerAuthKeyValue(CGF.getContext(), keyExpr); + auto discriminator = CGF.EmitScalarExpr(discriminatorExpr); + + if (discriminator->getType()->isPointerTy()) + discriminator = CGF.Builder.CreatePtrToInt(discriminator, CGF.IntPtrTy); + + auto functionType = + functionPointerExpr->getType()->castAs()->getPointeeType(); + CGCalleeInfo calleeInfo(functionType->getAs()); + CGPointerAuthInfo pointerAuth(key, PointerAuthenticationMode::SignAndAuth, + /* isaIsaPointer */ false, + /* authenticatesNullValues */ false, + discriminator); + CGCallee callee(calleeInfo, calleePtr, pointerAuth); + return callee; +} + CGCallee CodeGenFunction::EmitCallee(const Expr *E) { E = E->IgnoreParens(); @@ -5870,6 +6678,36 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) { // Treat pseudo-destructor calls differently. } else if (auto PDE = dyn_cast(E)) { return CGCallee::forPseudoDestructor(PDE); + + // Peephole specific builtin calls. + } else if (auto CE = dyn_cast(E)) { + if (unsigned builtin = CE->getBuiltinCallee()) { + // If the callee is a __builtin_ptrauth_sign_unauthenticated to the + // ABI function-pointer signing schema, perform an unauthenticated call. + if (builtin == Builtin::BI__builtin_ptrauth_sign_unauthenticated && + isFunctionPointerAuth(CGM, CE->getArg(1), CE->getArg(2))) { + CGCallee callee = EmitCallee(CE->getArg(0)); + if (callee.isOrdinary()) + callee.setPointerAuthInfo(CGPointerAuthInfo()); + return callee; + } + + // If the callee is a __builtin_ptrauth_auth_and_resign to the + // ABI function-pointer signing schema, avoid the intermediate resign. + if (builtin == Builtin::BI__builtin_ptrauth_auth_and_resign && + isFunctionPointerAuth(CGM, CE->getArg(3), CE->getArg(4))) { + return EmitSignedFunctionPointerCallee(*this, CE->getArg(0), + CE->getArg(1), CE->getArg(2)); + + // If the callee is a __builtin_ptrauth_auth when ABI function pointer + // signing is disabled, we need to promise to use the unattackable + // OperandBundle code pattern. + } else if (builtin == Builtin::BI__builtin_ptrauth_auth && + !CGM.getCodeGenOpts().PointerAuth.FunctionPointers.isEnabled()) { + return EmitSignedFunctionPointerCallee(*this, CE->getArg(0), + CE->getArg(1), CE->getArg(2)); + } + } } // Otherwise, we have an indirect reference. @@ -5907,6 +6745,10 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) { E->getOpcode() == BO_PtrMemI) return EmitPointerToDataMemberBinaryExpr(E); + if (E->getType()->isPointerTypeWithBounds()) { + return EmitAggExprToLValue(E); + } + assert(E->getOpcode() == BO_Assign && "unexpected binary l-value"); // Note that in all of these cases, __block variables need the RHS @@ -6063,6 +6905,79 @@ CodeGenFunction::EmitCXXBindTemporaryLValue(const CXXBindTemporaryExpr *E) { return MakeAddrLValue(Slot.getAddress(), E->getType(), AlignmentSource::Decl); } +LValue CodeGenFunction::EmitBoundsCheckExprLValue(const BoundsCheckExpr *E) { + const auto *BoundsCheck = cast(E); + typedef CodeGenFunction::OpaqueValueMappingData OVMD; + SmallVector bindings; + for (auto *Common : BoundsCheck->opaquevalues()) { + const OpaqueValueExpr *ov = cast(Common); + OVMD opaqueData = OVMD::bind(*this, ov, ov->getSourceExpr()); + bindings.push_back(opaqueData); + } + + { + RAIIDisableUBSanChecks DisableChecks(*this); + // TODO(dliew): We should have a more specific opt-remark. + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(this, BNS_OR_GENERAL); + // TODO(dliew): We should have a more specific trap reason. + EmitBoundsSafetyTrapCheck(BoundsCheck->getCond(), BNS_TRAP_GENERAL); + } + LValue Res = EmitLValue(BoundsCheck->getGuardedExpr()); + for (auto &opaqueData : bindings) { + opaqueData.unbind(*this); + } + return Res; +} + +LValue CodeGenFunction::EmitPredefinedBoundsCheckExprLValue( + const PredefinedBoundsCheckExpr *E) { + return EmitAggExprToLValue(E); +} + +LValue CodeGenFunction::EmitMaterializeSequenceExprLValue( + const MaterializeSequenceExpr *MSE) { + if (MSE->isBinding()) { + for (auto *OVE : MSE->opaquevalues()) { + if (CodeGenFunction::OpaqueValueMappingData::shouldBindAsLValue(OVE)) { + RValue PtrRV = EmitAnyExpr(OVE->getSourceExpr()); + LValue LV = MakeAddrLValue(PtrRV.getAggregateAddress(), OVE->getType()); + CodeGenFunction::OpaqueValueMappingData::bind(*this, OVE, LV); + } else { + CodeGenFunction::OpaqueValueMappingData::bind( + *this, OVE, OVE->getSourceExpr()); + } + } + } + + LValue LV = EmitLValue(MSE->getWrappedExpr()); + + if (MSE->isUnbinding()) { + for (auto *OVE : MSE->opaquevalues()) + CodeGenFunction::OpaqueValueMappingData::unbind(*this, OVE); + } + + return LV; +} + +LValue CodeGenFunction::EmitBoundsSafetyPointerPromotionExprLValue( + const BoundsSafetyPointerPromotionExpr *E) { + return EmitAggExprToLValue(E); +} + +LValue CodeGenFunction::EmitForgePtrExprLValue(const ForgePtrExpr *E) { + if (!E->getType()->isPointerTypeWithBounds()) + return EmitUnsupportedLValue(E, "l-value expression"); + return EmitAggExprToLValue(E); +} + +LValue CodeGenFunction::EmitAssumptionExprLValue(const AssumptionExpr *E) { + llvm::Function *FnAssume = CGM.getIntrinsic(llvm::Intrinsic::assume); + for (auto *Assumption : E->assumptions()) { + Builder.CreateCall(FnAssume, EvaluateExprAsBool(Assumption)); + } + return EmitLValue(E->getWrappedExpr()); +} + LValue CodeGenFunction::EmitObjCMessageExprLValue(const ObjCMessageExpr *E) { RValue RV = EmitObjCMessageExpr(E); diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 87b2a73fb0c03..46d7526d647dc 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -82,7 +82,9 @@ class AggExprEmitter : public StmtVisitor { /// EmitAggLoadOfLValue - Given an expression with aggregate type that /// represents a value lvalue, this method emits the address of the lvalue, /// then loads the result into DestPtr. - void EmitAggLoadOfLValue(const Expr *E); + /* TO_UPSTREAM(BoundsSafety) ON */ + void EmitAggLoadOfLValue(const Expr *E, bool Checked = false); + /* TO_UPSTREAM(BoundsSafety) OFF */ /// EmitFinalDestCopy - Perform the final copy to DestPtr, if desired. /// SrcIsRValue is true if source comes from an RValue. @@ -152,10 +154,29 @@ class AggExprEmitter : public StmtVisitor { void VisitDeclRefExpr(DeclRefExpr *E) { EmitAggLoadOfLValue(E); } void VisitMemberExpr(MemberExpr *ME) { EmitAggLoadOfLValue(ME); } void VisitUnaryDeref(UnaryOperator *E) { EmitAggLoadOfLValue(E); } + /* TO_UPSTREAM(BoundsSafety) ON */ + void VisitUnaryAddrOf(UnaryOperator *E); + void VisitUnaryPostDec(UnaryOperator *E) { + EmitWidePtrPrePostIncDec(E, /*IsSub*/true, /*IsPre*/false); + } + void VisitUnaryPostInc(UnaryOperator *E) { + EmitWidePtrPrePostIncDec(E, /*IsSub*/false, /*IsPre*/false); + } + void VisitUnaryPreDec(UnaryOperator *E) { + EmitWidePtrPrePostIncDec(E, /*IsSub*/true, /*IsPre*/true); + } + void VisitUnaryPreInc(UnaryOperator *E) { + EmitWidePtrPrePostIncDec(E, /*IsSub*/false, /*IsPre*/true); + } + void EmitWidePtrPrePostIncDec(UnaryOperator *E, bool IsSub, bool IsPre); + /* TO_UPSTREAM(BoundsSafety) OFF */ + void VisitStringLiteral(StringLiteral *E) { EmitAggLoadOfLValue(E); } void VisitCompoundLiteralExpr(CompoundLiteralExpr *E); void VisitArraySubscriptExpr(ArraySubscriptExpr *E) { - EmitAggLoadOfLValue(E); + /* TO_UPSTREAM(BoundsSafety) ON */ + EmitAggLoadOfLValue(E, E->getBase()->getType()->isPointerTypeWithBounds()); + /* TO_UPSTREAM(BoundsSafety) OFF */ } void VisitPredefinedExpr(const PredefinedExpr *E) { EmitAggLoadOfLValue(E); @@ -167,9 +188,13 @@ class AggExprEmitter : public StmtVisitor { void VisitStmtExpr(const StmtExpr *E); void VisitBinaryOperator(const BinaryOperator *BO); void VisitPointerToDataMemberBinaryOperator(const BinaryOperator *BO); + // TO_UPSTREAM(BoundsSafety) + void VisitBoundPointerArithmetic(const BinaryOperator *BO); void VisitBinAssign(const BinaryOperator *E); void VisitBinComma(const BinaryOperator *E); void VisitBinCmp(const BinaryOperator *E); + // TO_UPSTREAM(BoundsSafety) + void VisitCompoundAssignOperator(CompoundAssignOperator *E); void VisitCXXRewrittenBinaryOperator(CXXRewrittenBinaryOperator *E) { Visit(E->getSemanticForm()); } @@ -232,6 +257,20 @@ class AggExprEmitter : public StmtVisitor { void VisitCXXParenListOrInitListExpr(Expr *ExprToVisit, ArrayRef Args, Expr *ArrayFiller); + /* TO_UPSTREAM(BoundsSafety) ON */ + void VisitGetBoundExpr(GetBoundExpr *E) { + CGF.EmitAggExpr(E->getSubExpr(), Dest); + if (Dest.isIgnored()) + return; + Address Addr = Dest.getAddress(); + WPIndex SrcIndex = E->getBoundKind() == GetBoundExpr::BK_Lower + ? WPIndex::Lower : WPIndex::Upper; + llvm::Value *Component = CGF.GetWidePointerElement(Addr, SrcIndex); + Address DstAddr = Builder.CreateStructGEP(Addr, (unsigned)WPIndex::Pointer); + Builder.CreateStore(Component, DstAddr); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void EmitInitializationToLValue(Expr *E, LValue Address); void EmitNullInitializationToLValue(LValue Address); // case Expr::ChooseExprClass: @@ -243,6 +282,86 @@ class AggExprEmitter : public StmtVisitor { void VisitPackIndexingExpr(PackIndexingExpr *E) { Visit(E->getSelectedExpr()); } + + /* TO_UPSTREAM(BoundsSafety) ON */ + // BoundsSafety: handle wide pointer operations. + using WidePointerElemCallback = std::function; + static WidePointerElemCallback DefaultElemCallback; + + void EmitWidePointer( + LValue DestLV, llvm::Value *Ptr, llvm::Value *Upper, + llvm::Value *Lower = nullptr, + WidePointerElemCallback PtrElemCallback = DefaultElemCallback); + void EmitWidePointerToDest( + QualType DestTy, llvm::Value *Ptr, llvm::Value *Upper, + llvm::Value *Lower = nullptr, + WidePointerElemCallback PtrElemCallback = DefaultElemCallback) { + // Skip it if the dest is ignored. + if (Dest.isIgnored()) + return; + LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), DestTy); + return EmitWidePointer(DestLV, Ptr, Upper, Lower, PtrElemCallback); + } + void EmitBoundsSafetyPointerConversion(CastExpr *E); + void EmitNullToBoundPointer(CastExpr *E); + void EmitArrayToBoundPointerDecay(CastExpr *E); + void EmitWidePointerBitCast(CastExpr *E); + + void EmitCheckedBoundPointerArithmetic(LValue DestLV, const Expr *E, + const Expr *Base, llvm::Value *Idx, + QualType IdxType, bool IsSub = false); + void EmitCheckedBoundPointerArithmetic(LValue DestLV, const Expr *E, + const Expr *Base, const Expr *Idx, + QualType IdxType, bool IsSub = false); + void EmitCheckedBoundPointerArithmetic(LValue DestLV, const Expr *E, + const Expr *Base, unsigned int Idx, + ASTContext &Ctx, bool IsSub) { + llvm::Value *Index = llvm::ConstantInt::get(CGF.Int32Ty, Idx); + QualType Int32Ty = Ctx.getIntTypeForBitwidth(32, true); + + EmitCheckedBoundPointerArithmetic(DestLV, E, Base, Index, Int32Ty, IsSub); + } + + void EmitBoundPointerArithmetic(LValue DestLV, const Expr *Base, llvm::Value *Idx, + bool IsIdxSigned, bool IsSub); + void EmitBoundPointerArithmetic(LValue DestLV, LValue BaseLV, llvm::Value *Idx, + bool IsIdxSigned, bool IsSub); + void EmitBoundPointerArithmetic(LValue DestLV, const Expr *Base, const Expr *Idx, + bool IsSub = false) { + llvm::Value *Index = CGF.EmitAnyExpr(Idx).getScalarVal(); + const bool IsSigned = Idx->getType()->isSignedIntegerOrEnumerationType(); + + EmitBoundPointerArithmetic(DestLV, Base, Index, IsSigned, IsSub); + } + void VisitAssumptionExpr(AssumptionExpr *E); + void VisitForgePtrExpr(ForgePtrExpr *E); + void VisitBoundsCheckExpr(BoundsCheckExpr *E) { + typedef CodeGenFunction::OpaqueValueMappingData OVMD; + SmallVector bindings; + for (auto *Common : E->opaquevalues()) { + const OpaqueValueExpr *ov = cast(Common); + OVMD opaqueData = OVMD::bind(CGF, ov, ov->getSourceExpr()); + bindings.push_back(opaqueData); + } + { + RAIIDisableUBSanChecks DisableChecks(CGF); + // TODO(dliew): We should have a more specific opt-remark. + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(CGF, BNS_OR_GENERAL); + // TODO(dliew): We should have a more specific trap reason. + CGF.EmitBoundsSafetyTrapCheck(E->getCond(), BNS_TRAP_GENERAL); + } + Visit(E->getGuardedExpr()); + for (auto &opaqueData : bindings) { + opaqueData.unbind(CGF); + } + } + void VisitPredefinedBoundsCheckExpr(PredefinedBoundsCheckExpr *E); + void VisitBoundsSafetyPointerPromotionExpr(BoundsSafetyPointerPromotionExpr *E); + + void VisitMaterializeSequenceExpr(MaterializeSequenceExpr *MSE); + + void VisitTerminatedByToIndexableExpr(TerminatedByToIndexableExpr *E); + /* TO_UPSTREAM(BoundsSafety) OFF */ }; } // end anonymous namespace. @@ -250,11 +369,39 @@ class AggExprEmitter : public StmtVisitor { // Utilities //===----------------------------------------------------------------------===// +/* TO_UPSTREAM(BoundsSafety) ON */ +AggExprEmitter::WidePointerElemCallback AggExprEmitter::DefaultElemCallback = + [](llvm::Value *V) { return V; }; +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// EmitAggLoadOfLValue - Given an expression with aggregate type that /// represents a value lvalue, this method emits the address of the lvalue, /// then loads the result into DestPtr. -void AggExprEmitter::EmitAggLoadOfLValue(const Expr *E) { - LValue LV = CGF.EmitLValue(E); +void AggExprEmitter::EmitAggLoadOfLValue(const Expr *E, bool Checked) { + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (CGF.getLangOpts().hasNewBoundsSafetyCheck( + clang::LangOptionsBase::BS_CHK_ArraySubscriptAgg)) { + // TODO(dliew): Modifying `Checked` should probably be removed. + // Calling `EmitCheckedLValue` does two things: + // + // 1. If `E` is an ArraySubscriptExpr then emits bound checks if UBSan is + // on or if the base pointer has bounds information + // 2. Calls `CodeGenFunction::EmitTypeCheck()` in some cases. + // + // (1.) is already handled by `AggExprEmitter::VisitArraySubscriptExpr()` so + // modifying `Checked` isn't need for that. However, (2.) adds UBSan type + // checks. + // + // We should audit this interaction with UBSan to see if its safe to remove + // setting `Checked`. (rdar://145257962). + Checked |= E->getType()->isPointerTypeWithBounds(); + } else { + // Preserve old buggy behavior + Checked = E->getType()->isPointerTypeWithBounds(); + } + LValue LV = Checked ? CGF.EmitCheckedLValue(E, CodeGenFunction::TCK_Load) + : CGF.EmitLValue(E); + /*TO_UPSTREAM(BoundsSafety) OFF*/ // If the type of the l-value is atomic, then do an atomic load. if (LV.getType()->isAtomicType() || CGF.LValueIsSuitableForInlineAtomic(LV)) { @@ -265,6 +412,546 @@ void AggExprEmitter::EmitAggLoadOfLValue(const Expr *E) { EmitFinalDestCopy(E->getType(), LV); } +/*TO_UPSTREAM(BoundsSafety) ON*/ +void AggExprEmitter::EmitNullToBoundPointer(CastExpr *E) { + EnsureDest(E->getType()); + LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType()); + EmitNullInitializationToLValue(DestLV); +} + +void AggExprEmitter::EmitArrayToBoundPointerDecay(CastExpr *E) { + assert(E->getType()->isPointerTypeWithBounds() && + E->getType()->getAs()->isBidiIndexable()); + + llvm::Value *Upper = nullptr; + Address Addr = CGF.EmitArrayToWidePointerDecay(E->getSubExpr(), Upper); + llvm::Value *Ptr = Addr.getBasePointer(); + EmitWidePointerToDest(E->getType(), Ptr, Upper, Ptr); +} + +void AggExprEmitter::EmitWidePointerBitCast(CastExpr *E) { + QualType RawPointerTy = + CGF.getContext().getPointerType(E->getType()->getPointeeType()); + + WidePointerElemCallback ElemBitCast = + [&](llvm::Value *PtrVal) -> llvm::Value * { + return Builder.CreateBitCast(PtrVal, CGF.ConvertType(RawPointerTy)); + }; + + RValue SrcRV = CGF.EmitAnyExpr(E->getSubExpr()); + assert(SrcRV.isAggregate()); + Address SrcAddr = SrcRV.getAggregateAddress(); + EmitWidePointerToDest(E->getType(), + CGF.GetWidePointerElement(SrcAddr, WPIndex::Pointer), + CGF.GetWidePointerElement(SrcAddr, WPIndex::Upper), + E->getType()->isBidiIndexablePointerType() + ? CGF.GetWidePointerElement(SrcAddr, WPIndex::Lower) + : nullptr, + ElemBitCast); +} + +void AggExprEmitter::EmitWidePointer(LValue DestLV, llvm::Value *Ptr, + llvm::Value *Upper, llvm::Value *Lower, + WidePointerElemCallback PtrElemCallback) { + + auto *PT = DestLV.getType()->getAs(); + unsigned Idx = 0; + auto InitPtrField = [&](llvm::Value *Val) { + Address Addr = Builder.CreateStructGEP(DestLV.getAddress(), Idx++); + Builder.CreateStore(PtrElemCallback(Val), Addr); + }; + + // Initialize ptr field of the wide pointer record + InitPtrField(Ptr); + + if (PT->getPointerAttributes().hasUpperBound()) + InitPtrField(Upper); + + // Initialize lower bound if exist + if (PT->getPointerAttributes().hasLowerBound()) { + assert(Lower); + InitPtrField(Lower); + } +} + +void AggExprEmitter::VisitForgePtrExpr(ForgePtrExpr *E) { + RValue AddrRV = CGF.EmitAnyExpr(E->getAddr()); + llvm::Value *Ptr; + if (AddrRV.isScalar()) + Ptr = AddrRV.getScalarVal(); + else { + assert(AddrRV.isAggregate()); + Ptr = CGF.GetWidePointerElement(AddrRV.getAggregateAddress(), WPIndex::Pointer); + } + llvm::Value *ByteSize = CGF.EmitScalarExpr(E->getSize()); + // XXX: bitcast can be removed upon the full transition to opaque pointer types + // (rdar://92073883). + Ptr = Builder.CreateBitOrPointerCast(Ptr, CGF.VoidPtrTy); + QualType PointeeType = E->getType()->getPointeeType(); + llvm::Type *ElemTy = CGF.ConvertTypeForMem(PointeeType); + llvm::Value *Upper = Builder.CreateGEP(ElemTy, Ptr, ByteSize); + llvm::Type *RawPtrTy = + CGF.ConvertTypeForMem(CGF.getContext().getPointerType(PointeeType)); + WidePointerElemCallback Callback = [&](llvm::Value *V) -> llvm::Value * { + return Builder.CreateBitCast(V, RawPtrTy); + }; + EmitWidePointerToDest(E->getType(), Ptr, Upper, Ptr, Callback); +} + +void AggExprEmitter::VisitAssumptionExpr(AssumptionExpr *E) { + llvm::Function *FnAssume = CGF.CGM.getIntrinsic(llvm::Intrinsic::assume); + for (Expr *Assumption : E->assumptions()) { + Builder.CreateCall(FnAssume, CGF.EvaluateExprAsBool(Assumption)); + } + return Visit(E->getWrappedExpr()); +} + +void AggExprEmitter:: +VisitBoundsSafetyPointerPromotionExpr(BoundsSafetyPointerPromotionExpr *E) { + llvm::Value *Ptr; + RValue PtrRV = CGF.EmitAnyExpr(E->getPointer()); + if (PtrRV.isScalar()) + Ptr = PtrRV.getScalarVal(); + else { + assert(PtrRV.isAggregate()); + Ptr = CGF.GetWidePointerElement(PtrRV.getAggregateAddress(), WPIndex::Pointer); + } + + llvm::BranchInst *NullCheckBranch = nullptr; + if (E->getNullCheck()) { + // Test that the pointer is not NULL before testing that it's in bounds, + // if AcceptNullPtr is specified. + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(CGF, BNS_OR_PTR_NEQ_NULL); + llvm::BasicBlock *NotNull = CGF.createBasicBlock("boundscheck.notnull"); + + llvm::Value *Null = llvm::Constant::getNullValue(Ptr->getType()); + llvm::Value *PtrIsNull = Builder.CreateICmpNE(Ptr, Null); + NullCheckBranch = Builder.CreateCondBr(PtrIsNull, NotNull, nullptr); + CGF.EmitBlock(NotNull); + } + + llvm::Value *Lower; + if (auto *LowerE = E->getLowerBound()) { + RValue LowerRV = CGF.EmitAnyExpr(LowerE); + if (LowerRV.isScalar()) + Lower = LowerRV.getScalarVal(); + else { + assert(LowerRV.isAggregate()); + Lower = CGF.GetWidePointerElement(LowerRV.getAggregateAddress(), WPIndex::Pointer); + } + } else { + Lower = Ptr; + } + + llvm::Value *Upper; + { + RAIIDisableUBSanChecks DisableUBSanChecks(CGF); + RValue UpperRV = CGF.EmitAnyExpr(E->getUpperBound()); + if (UpperRV.isScalar()) + Upper = UpperRV.getScalarVal(); + else { + assert(UpperRV.isAggregate()); + Upper = CGF.GetWidePointerElement(UpperRV.getAggregateAddress(), + WPIndex::Pointer); + } + } + + auto *PT = E->getType()->getAs(); + llvm::Type *RawPtrTy = + CGF.ConvertType(CGF.getContext().getPointerType(PT->getPointeeType())); + WidePointerElemCallback Callback = [&](llvm::Value *V) -> llvm::Value * { + return Builder.CreateBitCast(V, RawPtrTy); + }; + EmitWidePointerToDest(E->getType(), Ptr, Upper, Lower, Callback); + + // Creating the following control flow graph. + // nullcheck: + // br cond nonnull, null + // nonnull: + // promotion operations + // br cont + // null: + // fill result with null + // br cont + // cont: + if (NullCheckBranch) { + llvm::BasicBlock *ContBlock = CGF.createBasicBlock("boundscheck.cont"); + Builder.CreateBr(ContBlock); + + llvm::BasicBlock *NullBlock = CGF.createBasicBlock("boundscheck.null"); + NullCheckBranch->setSuccessor(1, NullBlock); + CGF.EmitBlock(NullBlock); + llvm::Value *Zero = llvm::Constant::getNullValue(Ptr->getType()); + EmitWidePointerToDest(E->getType(), Zero, Zero, Zero, Callback); + CGF.EmitBlock(ContBlock); + } +} + +void AggExprEmitter::VisitPredefinedBoundsCheckExpr( + PredefinedBoundsCheckExpr *E) { + CGF.EmitFlexibleArrayCountCheck(E); + Visit(E->getGuardedExpr()); +} + +void AggExprEmitter::VisitMaterializeSequenceExpr(MaterializeSequenceExpr *MSE) { + if (MSE->isBinding()) { + for (auto *OVE : MSE->opaquevalues()) { + if (CodeGenFunction::OpaqueValueMappingData::shouldBindAsLValue(OVE)) { + RValue PtrRV = CGF.EmitAnyExpr(OVE->getSourceExpr()); + LValue LV = CGF.MakeAddrLValue(PtrRV.getAggregateAddress(), OVE->getType()); + CodeGenFunction::OpaqueValueMappingData::bind(CGF, OVE, LV); + } else { + CodeGenFunction::OpaqueValueMappingData::bind(CGF, OVE, OVE->getSourceExpr()); + } + } + } + + Visit(MSE->getWrappedExpr()); + + if (MSE->isUnbinding()) { + for (auto *OVE : MSE->opaquevalues()) + CodeGenFunction::OpaqueValueMappingData::unbind(CGF, OVE); + } +} + +static llvm::Value *EmitTerminatedByToIndexableStrlen(CodeGenFunction &CGF, + RValue PtrRV, + QualType PointeeTy, + SourceLocation Loc) { + ASTContext &C = CGF.getContext(); + + const char *Name; + unsigned BuiltinID; + QualType CTy; + if (PointeeTy->isCharType()) { + Name = "__builtin_strlen"; + BuiltinID = Builtin::BI__builtin_strlen; + CTy = C.CharTy; + } else { + Name = "__builtin_wcslen"; + BuiltinID = Builtin::BI__builtin_wcslen; + CTy = C.WCharTy; + } + + const IdentifierInfo *II = &C.Idents.get(Name); + + QualType ReturnTy = C.getSizeType(); + QualType StrTy = CTy; + StrTy.addConst(); + StrTy = C.getPointerType(StrTy); + SmallVector ArgTys{StrTy}; + QualType FunctionTy = C.getFunctionType(ReturnTy, ArgTys, {}); + + FunctionDecl *FD = FunctionDecl::Create( + C, C.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), II, + FunctionTy, /*TInfo=*/nullptr, SC_Extern, /*UsesFPIntrin=*/false, + /*hasWrittenPrototype=*/false); + + auto *Parm = ParmVarDecl::Create( + C, FD, SourceLocation(), SourceLocation(), nullptr, StrTy, + C.getTrivialTypeSourceInfo(StrTy, SourceLocation()), SC_None, + /*DefArg=*/nullptr); + FD->setParams({Parm}); + + llvm::Constant *FnPtr = CGF.CGM.getBuiltinLibFunction(FD, BuiltinID); + CGCallee Callee = CGCallee::forDirect(FnPtr, GlobalDecl(FD)); + + CallArgList Args; + Args.add(PtrRV, StrTy); + const CGFunctionInfo &FnInfo = CGF.CGM.getTypes().arrangeBuiltinFunctionCall( + ReturnTy, Args); + + llvm::CallBase *CallOrInvoke = nullptr; + RValue Call = + CGF.EmitCall(FnInfo, Callee, ReturnValueSlot(), Args, &CallOrInvoke, + /*IsMustTail=*/false, Loc); + return Call.getScalarVal(); +} + +static llvm::Value * +EmitTerminatedByToIndexableLoop(CodeGenFunction &CGF, RValue PtrRV, + QualType PointeeTy, + const llvm::APSInt &TermVal) { + ASTContext &C = CGF.getContext(); + auto &Builder = CGF.Builder; + + llvm::Value *Ptr = PtrRV.getScalarVal(); + llvm::Type *ElemTy = CGF.ConvertTypeForMem(PointeeTy); + + llvm::BasicBlock *Cond = CGF.createBasicBlock("terminated_by.loop_cond"); + llvm::BasicBlock *Cont = CGF.createBasicBlock("terminated_by.loop_cont"); + llvm::BasicBlock *End = CGF.createBasicBlock("terminated_by.loop_end"); + + RawAddress LenAllocaAddr = + CGF.CreateTempAlloca(CGF.SizeTy, CGF.getSizeAlign(), "terminated_by.len"); + llvm::Constant *Null = llvm::Constant::getNullValue(CGF.SizeTy); + Builder.CreateStore(Null, LenAllocaAddr); + + llvm::Value *Term = CGF.EmitTerminator(TermVal, ElemTy); + + CGF.EmitBlock(Cond); + + llvm::Value *Len = Builder.CreateLoad(LenAllocaAddr, "terminated_by.len"); + llvm::Value *GEP = Builder.CreateInBoundsGEP(ElemTy, Ptr, Len); + Address ElemAddr = Address(GEP, ElemTy, C.getTypeAlignInChars(PointeeTy)); + llvm::Value *Elem = Builder.CreateLoad(ElemAddr, "terminated_by.elem"); + llvm::Value *TermCheck = + Builder.CreateICmpEQ(Elem, Term, "terminted_by.check_terminator"); + Builder.CreateCondBr(TermCheck, End, Cont); + + CGF.EmitBlock(Cont); + + llvm::Constant *One = llvm::ConstantInt::get(CGF.SizeTy, 1); + llvm::Value *NewLen = Builder.CreateAdd(Len, One, "terminated_by.new_len"); + Builder.CreateStore(NewLen, LenAllocaAddr); + Builder.CreateBr(Cond); + + CGF.EmitBlock(End); + + return Builder.CreateLoad(LenAllocaAddr); +} + +void AggExprEmitter::VisitTerminatedByToIndexableExpr( + TerminatedByToIndexableExpr *E) { + ASTContext &C = CGF.getContext(); + + RValue PtrRV = CGF.EmitAnyExpr(E->getPointer()); + assert(PtrRV.isScalar()); + llvm::Value *Ptr = PtrRV.getScalarVal(); + llvm::Type *ElemTy = CGF.ConvertTypeForMem(E->getType()->getPointeeType()); + + const auto *VTT = E->getPointer()->getType()->getAs(); + llvm::APSInt TermVal = VTT->getTerminatorValue(C); + + // Arrays are decayed to pointers, thus we expect a pointer here. + assert(VTT->isPointerType()); + QualType PointeeTy = VTT->getPointeeType(); + + // Try to call a builtin to get the length, if available. If not, we emit a + // loop to get the position of the terminator. + llvm::Value *Length; + if (!CGF.getLangOpts().Freestanding && TermVal.isZero() && + (PointeeTy->isCharType() || PointeeTy->isWideCharType())) { + Length = EmitTerminatedByToIndexableStrlen(CGF, PtrRV, PointeeTy, + E->getExprLoc()); + } else { + Length = EmitTerminatedByToIndexableLoop(CGF, PtrRV, PointeeTy, TermVal); + } + + if (E->includesTerminator()) { + llvm::Constant *One = llvm::ConstantInt::get(CGF.SizeTy, 1); + Length = Builder.CreateAdd(Length, One); + } + + llvm::Value *Upper = + Builder.CreateInBoundsGEP(ElemTy, Ptr, Length, "terminated_by.upper"); + EmitWidePointerToDest(E->getType(), Ptr, Upper, /*Lower=*/Ptr); +} + +void AggExprEmitter::VisitUnaryAddrOf(UnaryOperator *E) { + // Get bounds of underlying object + Expr *SubExpr = E->getSubExpr()->IgnoreParens(); + + // C99 6.5.3.2p3, Address and indirection operators + // [...] If the operand is the result of a unary * operator, neither + // that operator nor the & operator is evaluated and the result is as + // if both were omitted, except that the constraints on the operators + // still apply and the result is not an lvalue. + // In BoundsSafety, this translates into that bounds of '&*ptr' should be + // the same as those of 'ptr'. + int32_t DerefLevel = 0; + Expr *UnderlyingSubExpr = SubExpr; + while (auto *UO = dyn_cast(UnderlyingSubExpr)) { + bool StopSubExpr = false; + switch (UO->getOpcode()) { + case UO_Deref: + DerefLevel++; + break; + case UO_AddrOf: + DerefLevel--; + break; + default: + StopSubExpr = true; + break; + } + if (StopSubExpr) + break; + UnderlyingSubExpr = UO->getSubExpr()->IgnoreParens(); + } + if (DerefLevel == 1 && UnderlyingSubExpr->IgnoreImpCasts()->isLValue()) { + return Visit(UnderlyingSubExpr); + } + + EnsureDest(E->getType()); + LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType()); + + // Array subscripts, member expressions are common enough that we have a + // peephole. However, we bail out for rarer cases that we handle better by + // taking the address of EmitLValue. + if (auto *ASE = dyn_cast(SubExpr)) + return EmitCheckedBoundPointerArithmetic( + DestLV, ASE, ASE->getBase(), ASE->getIdx(), ASE->getIdx()->getType()); + + // In the case of member expressions, we only peephole struct field accesses; + // unions go through the inefficient path for now. + if (auto *ME = dyn_cast(SubExpr)) { + auto *Member = dyn_cast(ME->getMemberDecl()); + if (Member && Member->getParent()->getTagKind() == TagTypeKind::Struct) { + auto Base = ME->getBase(); + llvm::Value *Ptr = nullptr; + RecordDecl *Rec = cast(Member->getParent()); + unsigned MemberIdx = + CGF.CGM.getTypes().getCGRecordLayout(Rec).getLLVMFieldNo(Member); + if (ME->isArrow()) { + if (Base->getType()->getAs()->hasRawPointerLayout()) { + Ptr = CGF.EmitScalarExpr(Base); + } else { + Ptr = CGF.EmitWideToRawPtr( + Base, /*BoundsCheck=*/true, + /*BoundsSafetyTrapCtx=*/BoundsSafetyTrapCtx::ADDR_OF_STRUCT_MEMBER); + } + llvm::Value *GepIndices[] = { + Builder.getInt32(0), Builder.getInt32(MemberIdx) + }; + llvm::Type *BaseStructTy = + CGF.ConvertTypeForMem(Base->getType()->getPointeeType()); + Ptr = Builder.CreateInBoundsGEP(BaseStructTy, Ptr, GepIndices); + } else { + LValue BaseLV = CGF.EmitLValue(Base); + Ptr = Builder.CreateStructGEP(BaseLV.getAddress(), MemberIdx) + .getBasePointer(); + } + + QualType PointeeType = E->getType()->getPointeeType(); + QualType RawPointerType = CGF.getContext().getPointerType(PointeeType); + llvm::Type *PointeeTypeForMem = CGF.ConvertTypeForMem(PointeeType); + llvm::ConstantInt *One = llvm::ConstantInt::get(CGF.SizeTy, 1); + // We always add `inbounds` to create upper bound for addr of member + // access. This is safe because if the base is any safe pointer (__single, + // __bidi_indexable, etc.), the preceding member access being successfully + // executed means the base is in bounds and it's safe to emit gep + // inbounds. And if the base is __unsafe_indexable, it's already unsafe + // and we don't care about bounds checking being optimized. If the base is + // not a pointer, this is essentially address of a single object (i.e., + // &s.member) and should also be in bounds. + llvm::Value *Upper = + Builder.CreateInBoundsGEP(PointeeTypeForMem, Ptr, One); + + return EmitWidePointer(DestLV, Ptr, Upper, Ptr, [&](llvm::Value *PtrVal) { + // XXX: bitcast can be removed upon the full transition to opaque pointer types + // (rdar://92073883). + return Builder.CreateBitCast(PtrVal, + CGF.ConvertTypeForMem(RawPointerType)); + }); + } + } + + LValue SubExprLV = CGF.EmitLValue(SubExpr); + llvm::Value *Ptr = SubExprLV.getPointer(CGF); + llvm::Type *ElemTy = SubExprLV.getAddress().getElementType(); + llvm::Value *Upper = Builder.CreateGEP(ElemTy, Ptr, + llvm::ConstantInt::get(CGF.SizeTy, 1)); + EmitWidePointer(DestLV, Ptr, Upper, /*Lower*/ Ptr); +} + +void AggExprEmitter::EmitWidePtrPrePostIncDec(UnaryOperator *E, bool IsSub, bool IsPre) { + assert(E->getType()->isPointerTypeWithBounds()); + assert((!IsSub || E->getType()->isBidiIndexablePointerType()) && + "decrease for array type cannot pass the semantic check"); + LValue LV = CGF.EmitLValue(E->getSubExpr()); + if (!IsPre) + EmitFinalDestCopy(E->getType(), LV); + + EmitCheckedBoundPointerArithmetic(LV, E, E->getSubExpr(), 1, CGF.getContext(), + IsSub); + if (IsPre) + EmitFinalDestCopy(E->getType(), LV); +} + +void AggExprEmitter::EmitBoundsSafetyPointerConversion(CastExpr *E) { + const PointerType *DstTy = E->getType()->getAs(); + const PointerType *SrcTy = E->getSubExpr()->getType()->getAs(); + + assert(DstTy); + assert(SrcTy); + assert(DstTy->getPointerAttributes() != SrcTy->getPointerAttributes()); + assert(CGF.getContext().hasSameType(DstTy->getPointeeType(), + SrcTy->getPointeeType())); + assert(DstTy->isIndexable() || DstTy->isBidiIndexable()); + + EnsureDest(E->getType()); + LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType()); + + unsigned Idx = 0; + auto InitPtrField = [&](llvm::Value *Val) { + Address Addr = Builder.CreateStructGEP(DestLV.getAddress(), Idx++); + // For instance, this may be true when an end pointer has a different + // type than its start pointer. + if (Val->getType() != Addr.getElementType()) + Val = Builder.CreateBitOrPointerCast(Val, Addr.getElementType()); + Builder.CreateStore(Val, Addr); + }; + + llvm::Value *Ptr, *Upper; + if (SrcTy->hasRawPointerLayout()) { + Ptr = CGF.EmitScalarExpr(E->getSubExpr()); + Upper = Ptr; + // Simple thin to wide pointer + bool GEPNeedsVoidPtrCast = SrcTy->getPointeeType()->isFunctionType(); + + // Create a zero bounds pointer if the pointee is incomplete or sizeless + // type. + QualType SrcPointeeType = SrcTy->getPointeeType(); + if (!SrcPointeeType->isIncompleteOrSizelessType()) { + llvm::Value *Count = llvm::ConstantInt::get(CGF.IntPtrTy, 1); + // We assume here that the upper bounds have been checked for overflow + // at initialization. + Upper = Builder.CreateInBoundsGEP(CGF.ConvertTypeForMem(SrcPointeeType), + Upper, Count, "upper"); + if (GEPNeedsVoidPtrCast) { + llvm::Type *PtrTy = Ptr->getType(); + Upper = Builder.CreatePointerCast(Upper, PtrTy); + } + } + } else { + assert((DstTy->isBidiIndexable() && SrcTy->isIndexable()) || + (DstTy->isIndexable() && SrcTy->isBidiIndexable())); + + RValue PtrRV = CGF.EmitAnyExpr(E->getSubExpr()); + Address PtrAddr = PtrRV.getAggregateAddress(); + Ptr = CGF.GetWidePointerElement(PtrAddr, WPIndex::Pointer); + Upper = CGF.GetWidePointerElement(PtrAddr, WPIndex::Upper); + + if (SrcTy->isBidiIndexable()) { + // We need to ensure that we don't create an __indexable pointer with + // { .ptr = null, .ub = non-null }, which allows arbitrary access. + // Thus, if the source pointer is null, we cannot just copy the upper + // bound, since it is possible to create __bidi_indexable pointers with + // { .ptr = null, .lb = non-null, .ub = non-null }. + // We always emit a lower bound check, which should work regardless if the + // source pointer is null or not. This will, however, trap for null + // __bidi_indexable pointer with non-null bounds (such pointers can be + // obtained with pointer arithmetic). + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + CGF, BNS_OR_BIDI_TO_INDEXABLE_PTR_LT_LOWER_BOUND); + llvm::Value *Lower = CGF.GetWidePointerElement(PtrAddr, WPIndex::Lower); + llvm::Value *Check = Builder.CreateICmpUGE(Ptr, Lower); + CGF.EmitBoundsSafetyTrapCheck(Check, + BNS_TRAP_BIDI_TO_INDEXABLE_PTR_LT_LOWER_BOUND); + } + } + + InitPtrField(Ptr); + InitPtrField(Upper); + + // Initialize lower bound if exist + if (DstTy->getPointerAttributes().hasLowerBound()) { + assert(!SrcTy->getPointerAttributes().hasLowerBound()); + InitPtrField(Ptr); + } +} +/*TO_UPSTREAM(BoundsSafety) OFF*/ + /// True if the given aggregate type requires special GC API calls. bool AggExprEmitter::TypeRequiresGCollection(QualType T) { // Only record types have members that might require garbage collection. @@ -962,6 +1649,18 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { RValue rvalue = RValue::getAggregate(valueAddr, atomicSlot.isVolatile()); return EmitFinalDestCopy(valueType, rvalue); } + + /*TO_UPSTREAM(BoundsSafety) ON*/ + case CK_BoundsSafetyPointerCast: + return EmitBoundsSafetyPointerConversion(E); + + case CK_NullToPointer: + return EmitNullToBoundPointer(E); + + case CK_ArrayToPointerDecay: + return EmitArrayToBoundPointerDecay(E); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + case CK_AddressSpaceConversion: return Visit(E->getSubExpr()); @@ -1024,6 +1723,25 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { case CK_NoOp: case CK_UserDefinedConversion: case CK_ConstructorConversion: + /* TO_UPSTREAM(BoundsSafety) ON */ + // BoundsSafety : With -fbounds-safety a pointer can be an aggregate type and + // when that's the case, the pointee may still have different CVR + // qualifiers, meaning the pointers themselves may have different canonical + // types. + if (E->getType()->isPointerTypeWithBounds()) { + const auto *LPTy = E->getType()->getAs(); + const auto *RPTy = E->getSubExpr()->getType()->getAs(); + (void)LPTy; + (void)RPTy; + assert(LPTy && RPTy && + LPTy->getPointerAttributes() == RPTy->getPointerAttributes() && + CGF.getContext().hasSameUnqualifiedType(RPTy->getPointeeType(), + RPTy->getPointeeType()) && + "Implicit cast types must be compatible"); + Visit(E->getSubExpr()); + break; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ assert(CGF.getContext().hasSameUnqualifiedType(E->getSubExpr()->getType(), E->getType()) && "Implicit cast types must be compatible"); @@ -1033,11 +1751,14 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { case CK_LValueBitCast: llvm_unreachable("should not be emitting lvalue bitcast as rvalue"); - case CK_Dependent: case CK_BitCast: - case CK_ArrayToPointerDecay: + /* TO_UPSTREAM(BoundsSafety) ON */ + if (E->getType()->isPointerType()) + return EmitWidePointerBitCast(E); + LLVM_FALLTHROUGH; + /* TO_UPSTREAM(BoundsSafety) OFF */ + case CK_Dependent: case CK_FunctionToPointerDecay: - case CK_NullToPointer: case CK_NullToMemberPointer: case CK_BaseToDerivedMemberPointer: case CK_DerivedToBaseMemberPointer: @@ -1256,16 +1977,150 @@ void AggExprEmitter::VisitBinCmp(const BinaryOperator *E) { void AggExprEmitter::VisitBinaryOperator(const BinaryOperator *E) { if (E->getOpcode() == BO_PtrMemD || E->getOpcode() == BO_PtrMemI) VisitPointerToDataMemberBinaryOperator(E); + /* TO_UPSTREAM(BoundsSafety) ON */ + else if (E->getType()->isPointerType()) + VisitBoundPointerArithmetic(E); + /* TO_UPSTREAM(BoundsSafety) OFF */ else CGF.ErrorUnsupported(E, "aggregate binary expression"); } +/* TO_UPSTREAM(BoundsSafety) ON */ +void AggExprEmitter::VisitCompoundAssignOperator(CompoundAssignOperator *E) { + bool IsSub = false; + if (E->getOpcode() != BO_AddAssign) { + if (E->getOpcode() != BO_SubAssign) + return CGF.ErrorUnsupported(E, "aggregate binary expression"); + IsSub = true; + } + assert(E->getType()->isPointerTypeWithBounds()); + LValue LV = CGF.EmitLValue(E->getLHS()); + EmitCheckedBoundPointerArithmetic(LV, E, E->getLHS(), E->getRHS(), + E->getRHS()->getType(), IsSub); + EmitFinalDestCopy(E->getType(), LV); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void AggExprEmitter::VisitPointerToDataMemberBinaryOperator( const BinaryOperator *E) { LValue LV = CGF.EmitPointerToDataMemberBinaryExpr(E); EmitFinalDestCopy(E->getType(), LV); } +/* TO_UPSTREAM(BoundsSafety) ON */ +void AggExprEmitter::EmitCheckedBoundPointerArithmetic( + LValue DestLV, const Expr *E, const Expr *Base, llvm::Value *Idx, + QualType IdxType, bool IsSub) { + const bool IsSigned = IdxType->isSignedIntegerOrEnumerationType(); + LValue BaseLV = CGF.EmitAggExprToLValue(Base); + EmitBoundPointerArithmetic(DestLV, BaseLV, Idx, IsSigned, IsSub); + + if (CGF.SanOpts.has(SanitizerKind::ArrayBounds)) + CGF.EmitBoundsCheck(E, Base, Idx, IdxType, + /*Accessed*/ false); + + if (CGF.SanOpts.has(SanitizerKind::PointerOverflow)) { + Address PtrAddr = Builder.CreateStructGEP(BaseLV.getAddress(), 0); + llvm::Value *Ptr = Builder.CreateLoad(PtrAddr); + + QualType PointeeType = Base->getType()->getPointeeType(); + CGF.EmitCheckedInBoundsGEP(CGF.ConvertTypeForMem(PointeeType), Ptr, Idx, + IsSigned, IsSub, E->getExprLoc(), "add.ptr"); + } +} + +void AggExprEmitter::EmitCheckedBoundPointerArithmetic( + LValue DestLV, const Expr *E, const Expr *Base, const Expr *Idx, + QualType IdxType, bool IsSub) { + llvm::Value *IdxVal = CGF.EmitScalarExpr(Idx); + EmitCheckedBoundPointerArithmetic(DestLV, E, Base, IdxVal, IdxType, IsSub); +} + +void AggExprEmitter::EmitBoundPointerArithmetic(LValue DestLV, const Expr *Base, + llvm::Value *Idx, bool IsSigned, bool IsSub) { + LValue BaseLV = CGF.EmitAggExprToLValue(Base); + EmitBoundPointerArithmetic(DestLV, BaseLV, Idx, IsSigned, IsSub); +} + +void AggExprEmitter::EmitBoundPointerArithmetic(LValue DestLV, LValue BaseLV, llvm::Value *Idx, + bool IsIdxSigned, bool IsSub) { + // TODO(dliew): rdar://109574814: opt-remarks could be added here. + Address PtrAddr = Builder.CreateStructGEP(BaseLV.getAddress(), 0); + llvm::Value *Ptr = Builder.CreateLoad(PtrAddr); + + const PointerType *BasePtrTy = BaseLV.getType()->getAs(); + const PointerType *DestPtrTy = DestLV.getType()->getAs(); + + llvm::Value *OldPtr; + if (BasePtrTy->isIndexable()) + OldPtr = Builder.CreatePtrToInt(Ptr, CGF.SizeTy, "bound.ptr.arith.old"); + + if (auto IntIdxTy = dyn_cast(Idx->getType())) { + if (IntIdxTy->getBitWidth() != CGF.IntPtrTy->getBitWidth()) + Idx = Builder.CreateIntCast(Idx, CGF.IntPtrTy, IsIdxSigned, "idxprom"); + } + + if (IsSub) + Idx = Builder.CreateNeg(Idx, "idx.neg"); + + QualType BaseType = BaseLV.getType(); + assert(BaseType->isPointerType()); + QualType BasePointeeType = BaseType->getPointeeType(); + Ptr = Builder.CreateGEP(CGF.ConvertTypeForMem(BasePointeeType), Ptr, Idx, + "bound.ptr.arith"); + unsigned I = 0; + auto InitPtrField = [&](llvm::Value *Val) { + Address DstAddr = Builder.CreateStructGEP(DestLV.getAddress(), I++); + Builder.CreateStore(Val, DstAddr); + }; + + InitPtrField(Ptr); + // Load and store upper bound + Address SrcAddr = Builder.CreateStructGEP(BaseLV.getAddress(), I); + InitPtrField(Builder.CreateLoad(SrcAddr)); + if (DestPtrTy->getPointerAttributes().hasLowerBound()) { + if (BasePtrTy->getPointerAttributes().hasLowerBound()) { + SrcAddr = Builder.CreateStructGEP(BaseLV.getAddress(), I); + InitPtrField(Builder.CreateLoad(SrcAddr)); + } else { + // converting from an indexable pointer, for which the lower bound is the + // same as the original pointer value + InitPtrField(Ptr); + } + } + + if (BasePtrTy->isIndexable()) { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + CGF, BNS_OR_INDEXABLE_PTR_NEW_LT_OLD); + llvm::Value *NewPtr = + Builder.CreatePtrToInt(Ptr, CGF.SizeTy, "bound.ptr.arith.new"); + llvm::Value *NewGEOld = Builder.CreateICmpUGE(NewPtr, OldPtr); + CGF.EmitBoundsSafetyTrapCheck( + NewGEOld, BoundsSafetyTrapKind::BNS_TRAP_INDEXABLE_PTR_NEW_LT_OLD); + } +} + +void AggExprEmitter::VisitBoundPointerArithmetic(const BinaryOperator *E) { + Expr *LHS = E->getLHS(); + Expr *RHS = E->getRHS(); + + EnsureDest(E->getType()); + LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType()); + + if (E->getOpcode() == BO_Add) { + if (RHS->getType()->isPointerType()) + std::swap(LHS, RHS); + if (!RHS->getType()->isPointerType()) + return EmitCheckedBoundPointerArithmetic(DestLV, E, LHS, RHS, + RHS->getType()); + } else if (E->getOpcode() == BO_Sub) + return EmitCheckedBoundPointerArithmetic(DestLV, E, LHS, RHS, + RHS->getType(), /* IsSub */ true); + + return CGF.ErrorUnsupported(E, "aggregate binary expression"); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// Is the value of the given expression possibly a reference to or /// into a __block variable? static bool isBlockVarRef(const Expr *E) { @@ -1368,7 +2223,8 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { return; } - LValue LHS = CGF.EmitLValue(E->getLHS()); + // TO_UPSTREAM(BoundsSafety) + LValue LHS = CGF.EmitCheckedLValue(E->getLHS(), CodeGenFunction::TCK_Store); // If we have an atomic type, evaluate into the destination and then // do an atomic copy. @@ -1391,6 +2247,13 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { CGF.EmitAggExpr(E->getRHS(), LHSSlot); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (CGF.SanOpts.has(SanitizerKind::NullabilityAssign) && E->getLHS()->getType()->isPointerTypeWithBounds()) { + llvm::Value* RHSValue = CGF.GetWidePointerElement(LHSSlot.getAddress(), WPIndex::Pointer); + CGF.EmitNullabilityCheck(E->getLHS()->getType(), RHSValue, E->getExprLoc()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Copy into the destination if the assignment isn't ignored. EmitFinalDestCopy(E->getType(), LHS); @@ -1590,6 +2453,10 @@ static bool castPreservesZero(const CastExpr *CE) { case CK_MatrixCast: case CK_NonAtomicToAtomic: case CK_AtomicToNonAtomic: + /* TO_UPSTREAM(BoundsSafety) ON */ + // BoundsSafety: null pointer becomes pointers with null bounds + case CK_BoundsSafetyPointerCast: + /* TO_UPSTREAM(BoundsSafety) OFF */ case CK_HLSLVectorTruncation: case CK_HLSLElementwiseCast: case CK_HLSLAggregateSplatCast: diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 3e8c42d5a6f4b..524d200299d3e 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -458,7 +458,8 @@ CodeGenFunction::EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E, // Emit the 'this' pointer. Address This = Address::invalid(); if (BO->getOpcode() == BO_PtrMemI) - This = EmitPointerWithAlignment(BaseExpr, nullptr, nullptr, KnownNonNull); + This = EmitPointerWithAlignment(BaseExpr, nullptr, nullptr, nullptr, + nullptr, KnownNonNull); else This = EmitLValue(BaseExpr, KnownNonNull).getAddress(); diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp index f556594f4a9ec..c96093d94d085 100644 --- a/clang/lib/CodeGen/CGExprComplex.cpp +++ b/clang/lib/CodeGen/CGExprComplex.cpp @@ -612,6 +612,8 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op, case CK_FixedPointToIntegral: case CK_IntegralToFixedPoint: case CK_MatrixCast: + // TO_UPSTREAM(BoundsSafety) + case CK_BoundsSafetyPointerCast: case CK_HLSLVectorTruncation: case CK_HLSLArrayRValue: case CK_HLSLElementwiseCast: diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index b21ebeee4bed1..9abcca05f2c77 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1114,6 +1114,64 @@ EmitArrayConstant(CodeGenModule &CGM, llvm::ArrayType *DesiredType, return llvm::ConstantStruct::get(SType, Elements); } +/* TO_UPSTREAM(BoundsSafety) ON */ +static llvm::Constant * +EmitWidePointerConstant(CodeGenModule &CGM, llvm::StructType *widePtrStructTy, + ArrayRef Elements) { + const auto numElems = widePtrStructTy->getNumElements(); + assert(numElems == 2 || numElems == 3); + + assert(widePtrStructTy->getElementType(0) == + widePtrStructTy->getElementType(1)); + if (numElems == 3) { + assert(widePtrStructTy->getElementType(0) == + widePtrStructTy->getElementType(2)); + } + + assert(Elements.size() == numElems); + return llvm::ConstantStruct::get(widePtrStructTy, Elements); +} + +static llvm::Constant * +EmitWidePointerConstant(CodeGenModule &CGM, llvm::StructType *widePtrStructTy, + llvm::Constant *Ptr, llvm::Constant *Size, + llvm::Constant *Base = nullptr) { + const auto numElems = widePtrStructTy->getNumElements(); + assert(numElems == 2 || numElems == 3); + + auto widePtrStructElemTy = widePtrStructTy->getElementType(0); + + if (!Ptr->getType()->isPointerTy()) + Ptr = llvm::ConstantExpr::getIntToPtr(Ptr, widePtrStructElemTy); + else if (Ptr->getType() != widePtrStructElemTy) + Ptr = llvm::ConstantExpr::getPointerCast(Ptr, widePtrStructElemTy); + + if (Base) { + if (!Base->getType()->isPointerTy()) + Base = llvm::ConstantExpr::getIntToPtr(Base, widePtrStructElemTy); + else if (Base->getType() != widePtrStructElemTy) + Base = llvm::ConstantExpr::getPointerCast(Base, widePtrStructElemTy); + } else { + Base = Ptr; + } + + llvm::Constant *Upper = llvm::ConstantExpr::getIntToPtr( + llvm::ConstantExpr::getAdd( + llvm::ConstantExpr::getPtrToInt(Base, CGM.IntPtrTy), + llvm::ConstantFoldIntegerCast(Size, CGM.IntPtrTy, /*IsSigned*/ false, + CGM.getDataLayout())), + widePtrStructElemTy); + + llvm::SmallVector Elems; + Elems.push_back(Ptr); + Elems.push_back(Upper); + if (numElems == 3) { + Elems.push_back(Base); + } + return llvm::ConstantStruct::get(widePtrStructTy, Elems); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + // This class only needs to handle arrays, structs and unions. Outside C++11 // mode, we don't currently constant fold those types. All other types are // handled by constant folding. @@ -1251,6 +1309,13 @@ class ConstExprEmitter case CK_ConstructorConversion: return Visit(subExpr, destType); + /* TO_UPSTREAM(BoundsSafety) ON */ + // BoundsSafety: Since we get to emit constant expression we assume + // -fbounds-safety pointer cast is side-effect free. + case CK_BoundsSafetyPointerCast: + return Emitter.tryEmitPrivate(subExpr, destType); + /* TO_UPSTREAM(BoundsSafety) OFF */ + case CK_ArrayToPointerDecay: if (const auto *S = dyn_cast(subExpr)) return CGM.GetAddrOfConstantStringFromLiteral(S).getPointer(); @@ -1512,6 +1577,15 @@ class ConstExprEmitter return Const.build(ValTy, HasFlexibleArray); } + /* TO_UPSTREAM(BoundsSafety) ON */ + // Have the evaluator handle this later as APValue. This is to make it + // consistent with how a normal C cast const-evaluated. See `VisitCastExpr` + // returning nullptr for `CK_BitCast` so the evaluater can handle it later. + llvm::Constant *VisitForgePtrExpr(const ForgePtrExpr *E, QualType Ty) { + return nullptr; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + llvm::Constant *VisitCXXConstructExpr(const CXXConstructExpr *E, QualType Ty) { if (!E->getConstructor()->isTrivial()) @@ -1695,8 +1769,37 @@ llvm::GlobalValue *ConstantEmitter::getCurrentAddrPrivate() { return global; } +static llvm::Constant *getUnfoldableValue(llvm::Constant *C) { + // Look through any constant expressions that might get folded + while (auto CE = dyn_cast(C)) { + switch (CE->getOpcode()) { + // Simple type changes. + case llvm::Instruction::BitCast: + case llvm::Instruction::IntToPtr: + case llvm::Instruction::PtrToInt: + break; + + // GEPs, if all the indices are zero. + case llvm::Instruction::GetElementPtr: + for (unsigned i = 1, e = CE->getNumOperands(); i != e; ++i) + if (!CE->getOperand(i)->isNullValue()) + return C; + break; + + default: + return C; + } + C = CE->getOperand(0); + } + return C; +} + void ConstantEmitter::registerCurrentAddrPrivate(llvm::Constant *signal, llvm::GlobalValue *placeholder) { + // Strip anything from the signal value that might get folded into other + // constant expressions in the final initializer. + signal = getUnfoldableValue(signal); + assert(!PlaceholderAddresses.empty()); assert(PlaceholderAddresses.back().first == nullptr); assert(PlaceholderAddresses.back().second == placeholder); @@ -2108,6 +2211,8 @@ class ConstantLValueEmitter : public ConstStmtVisitor emitPointerAuthDiscriminator(const Expr *E); + llvm::Constant *tryEmitConstantSignedPointer(llvm::Constant *ptr, + PointerAuthQualifier auth); bool hasNonZeroOffset() const { return !Value.getLValueOffset().isZero(); } @@ -2118,13 +2223,44 @@ class ConstantLValueEmitter : public ConstStmtVisitor(destTy) || isa(destTy)); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + auto DestPtrTy = DestType->getAs(); + const bool IsWidePtr = DestPtrTy && !DestPtrTy->hasRawPointerLayout(); + assert(isa(destTy) || isa(destTy) || + IsWidePtr); + /*TO_UPSTREAM(BoundsSafety) OFF*/ // If there's no base at all, this is a null or absolute pointer, // possibly cast back to an integer type. if (!base) { + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (IsWidePtr) { + auto widePtrStructTy = dyn_cast_or_null(destTy); + assert(widePtrStructTy && widePtrStructTy->getNumElements() > 1); + auto widePtrStructElemTy = widePtrStructTy->getElementType(0); + llvm::Constant *Size = nullptr; + if (Value.isLValueForgeBidi()) { + Size = CGM.getSize(Value.getLValueForgedSize()); + } else if (Value.isLValueForgeSingle() && + !Value.getUnwrappedLValueOffset().isZero()) { + // Single points to one valid element. + Size = CGM.getSize( + CGM.getContext().getTypeSizeInChars(DestType->getPointeeType())); + } + + if (Size) { + auto Ptr = llvm::ConstantExpr::getIntToPtr( + CGM.getSize(Value.getUnwrappedLValueOffset()), widePtrStructElemTy); + return EmitWidePointerConstant(CGM, widePtrStructTy, Ptr, Size); + } + + auto Ptr = tryEmitAbsolute(widePtrStructElemTy); + llvm::SmallVector Elems; + for (unsigned i = 0; i < widePtrStructTy->getNumElements(); ++i) + Elems.push_back(Ptr); + return EmitWidePointerConstant(CGM, widePtrStructTy, Elems); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ return tryEmitAbsolute(destTy); } @@ -2155,6 +2325,12 @@ llvm::Constant *ConstantLValueEmitter::tryEmit() { llvm::Constant *value = result.Value; if (!value) return nullptr; + /* TO_UPSTREAM(BoundsSafety) ON */ + if (Value.isLValueForge()) + value = applyForgedOffset(value); + llvm::Constant *BaseAddr = value; + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Apply the offset if necessary and not already done. if (!result.HasOffsetApplied) { value = applyOffset(value); @@ -2172,7 +2348,71 @@ llvm::Constant *ConstantLValueEmitter::tryEmit() { // an integer. FIXME: performAddrSpaceCast if (isa(destTy)) return llvm::ConstantExpr::getPointerCast(value, destTy); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (IsWidePtr) { + uint64_t UpperBoundBytes = 1; + if (Value.isLValueForgeBidi()) { + UpperBoundBytes = Value.getLValueForgedSize().getQuantity(); + } else if (Value.isLValueForgeSingle()) { + if (DestType->getPointeeType()->isIncompleteOrSizelessType()) { + UpperBoundBytes = 0; + } else { + UpperBoundBytes = CGM.getContext() + .getTypeSizeInChars(DestType->getPointeeType()) + .getQuantity(); + } + } else if (const ValueDecl *InitDecl = base.dyn_cast()) { + // In the following example, `InitDecl` picks up the first declaration, + // `extern float init[]` which is identified as an incomplete array type. + // Thus, the following code block tries to find the most recent + // declaration which will have a concrete array type if that exists. + // + // extern float init[]; + // float init[] = {1, 2, 3, 4}; + // float *__bidi_indexable f[] = {init}; + auto *Var = dyn_cast(InitDecl); + if (Var && Var->hasDefinition() == VarDecl::Definition) { + InitDecl = Var->getDefinition(); + } + QualType InitDeclTy = InitDecl->getType(); + if (auto ValTy = InitDeclTy.getTypePtrOrNull()) { + if (const auto IAT = dyn_cast(ValTy)) { + if (const auto *DCPTy = InitDeclTy->getAs()) { + const Expr *Count = DCPTy->getCountExpr(); + clang::Expr::EvalResult EvaluatedDynamicCount; + const bool DynamicCountEvalSuccess = Count->EvaluateAsInt( + EvaluatedDynamicCount, CGM.getContext(), + clang::Expr::SE_NoSideEffects, /*InConstantContext=*/false); + (void)DynamicCountEvalSuccess; + assert(DynamicCountEvalSuccess); + + const uint64_t SizeOfPointee = + CGM.GetTargetTypeStoreSize( + CGM.getTypes().ConvertType(IAT->getElementType())) + .getQuantity(); + UpperBoundBytes = + EvaluatedDynamicCount.Val.getInt().getLimitedValue() * + SizeOfPointee; + } else { + // Currently, Sema reports this case as a warning. We might want to + // turn certain cases into an error (rdar://84950239). + UpperBoundBytes = 0; + } + } else { + UpperBoundBytes = + CGM.GetTargetTypeStoreSize(CGM.getTypes().ConvertType(InitDeclTy)) + .getQuantity(); + } + } + } + auto widePtrStructTy = dyn_cast_or_null(destTy); + assert(widePtrStructTy); + return EmitWidePointerConstant( + CGM, widePtrStructTy, value, + llvm::ConstantInt::get(CGM.SizeTy, UpperBoundBytes), BaseAddr); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ return llvm::ConstantExpr::getPtrToInt(value, destTy); } @@ -2192,8 +2432,9 @@ ConstantLValueEmitter::tryEmitAbsolute(llvm::Type *destTy) { // FIXME: signedness depends on the original integer type. auto intptrTy = CGM.getDataLayout().getIntPtrType(destPtrTy); llvm::Constant *C; - C = llvm::ConstantFoldIntegerCast(getOffset(), intptrTy, /*isSigned*/ false, - CGM.getDataLayout()); + // TO_UPSTREAM(BoundsSafety): getTotalOffset + C = llvm::ConstantFoldIntegerCast(getTotalOffset(), intptrTy, + /*isSigned*/ false, CGM.getDataLayout()); assert(C && "Must have folded, as Offset is a ConstantInt"); C = llvm::ConstantExpr::getIntToPtr(C, destPtrTy); return C; @@ -2202,7 +2443,8 @@ ConstantLValueEmitter::tryEmitAbsolute(llvm::Type *destTy) { ConstantLValue ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { // Handle values. - if (const ValueDecl *D = base.dyn_cast()) { + // TO_UPSTREAM(BoundsSafety): getValueDecl + if (const ValueDecl *D = base.getValueDecl()) { // The constant always points to the canonical declaration. We want to look // at properties of the most recent declaration at the point of emission. D = cast(D->getMostRecentDecl()); @@ -2351,6 +2593,42 @@ ConstantLValueEmitter::VisitCallExpr(const CallExpr *E) { } } +/// Try to emit a constant signed pointer, given a raw pointer and the +/// destination ptrauth qualifier. +/// +/// This can fail if the qualifier needs address discrimination and the +/// emitter is in an abstract mode. +llvm::Constant * +ConstantLValueEmitter::tryEmitConstantSignedPointer( + llvm::Constant *unsignedPointer, + PointerAuthQualifier schema) { + assert(schema && "applying trivial ptrauth schema"); + auto key = schema.getKey(); + + // Create an address placeholder if we're using address discrimination. + llvm::GlobalValue *storageAddress = nullptr; + if (schema.isAddressDiscriminated()) { + // We can't do this if the emitter is in an abstract state. + if (Emitter.isAbstract()) + return nullptr; + + storageAddress = Emitter.getCurrentAddrPrivate(); + } + + // Fetch the extra discriminator. + llvm::ConstantInt *otherDiscriminator = + llvm::ConstantInt::get(CGM.IntPtrTy, schema.getExtraDiscriminator()); + + auto signedPointer = + CGM.getConstantSignedPointer(unsignedPointer, key, storageAddress, + otherDiscriminator); + + if (schema.isAddressDiscriminated()) + Emitter.registerCurrentAddrPrivate(signedPointer, storageAddress); + + return signedPointer; +} + ConstantLValue ConstantLValueEmitter::emitPointerAuthSignConstant(const CallExpr *E) { llvm::Constant *UnsignedPointer = emitPointerAuthPointer(E->getArg(0)); @@ -2719,9 +2997,14 @@ llvm::Constant *ConstantEmitter::emitNullForMemory(CodeGenModule &CGM, } llvm::Constant *CodeGenModule::EmitNullConstant(QualType T) { - if (T->getAs()) - return getNullPointer( - cast(getTypes().ConvertTypeForMem(T)), T); + if (const auto *PT = T->getAs()) { + if (PT->hasRawPointerLayout()) + return getNullPointer( + cast(getTypes().ConvertTypeForMem(T)), T); + /*TO_UPSTREAM(BoundsSafety) ON*/ + return llvm::Constant::getNullValue(getTypes().ConvertTypeForMem(T)); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + } if (getTypes().isZeroInitializable(T)) return llvm::Constant::getNullValue(getTypes().ConvertTypeForMem(T)); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 8dbbcdaef25d8..d597433dd2499 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -19,6 +19,7 @@ #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" +#include "BoundsSafetyTraps.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" @@ -393,6 +394,9 @@ class ScalarExprEmitter SourceLocation Loc, ScalarConversionOpts Opts = ScalarConversionOpts()); + // TO_UPSTREAM(BoundsSafety) + Value *EmitBoundsSafetyPointerConversion(CastExpr *E); + /// Convert between either a fixed point and other fixed point or fixed point /// and an integer. Value *EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy, @@ -579,7 +583,16 @@ class ScalarExprEmitter } Value *VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) { - VersionTuple Version = E->getVersion(); + if (E->hasDomainName()) { + auto DomainName = E->getDomainName(); + ASTContext::AvailabilityDomainInfo Info = + CGF.getContext().getFeatureAvailInfo(DomainName); + assert((Info.Kind == FeatureAvailKind::Dynamic && Info.Call) && + "ObjCAvailabilityCheckExpr should have been constant evaluated"); + return CGF.EmitScalarExpr(Info.Call); + } + + VersionTuple Version = E->getVersionAsWritten(); // If we're checking for a platform older than our minimum deployment // target, we can fold the check away. @@ -690,6 +703,35 @@ class ScalarExprEmitter return Visit(E->getSubExpr()); } + /* TO_UPSTREAM(BoundsSafety) ON */ + Value *VisitForgePtrExpr(const ForgePtrExpr *E) { + assert((E->ForgesSinglePointer() || E->ForgesTerminatedByPointer()) && + !E->getSize()); + RValue AddrRV = CGF.EmitAnyExpr(E->getAddr()); + llvm::Value *Ptr; + if (AddrRV.isScalar()) { + Ptr = AddrRV.getScalarVal(); + } else { + assert(AddrRV.isAggregate()); + Ptr = CGF.GetWidePointerElement(AddrRV.getAggregateAddress(), + WPIndex::Pointer); + } + + llvm::Type *RawPtrTy = CGF.ConvertType(E->getType()); + if (Ptr->getType() != RawPtrTy) { + Ptr = Builder.CreateBitOrPointerCast(Ptr, RawPtrTy); + } + // __builtin_unsafe_forge_* always returns `void *`, regardless of the type + // of the address argument. This is semantically similar to an explicit cast + // to `void *` (i.e., `(void*)addr`), which may require a ptrauth resign to + // match the discriminator for `(void *)` if `addr` was a function pointer + // type or type with a different key and/or discriminator. + // `AuthPointerToPointerCast` skips resign when it's not necessary. + return CGF.authPointerToPointerCast(Ptr, E->getAddr()->getType(), + E->getType()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + // C++ Value *VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E) { return EmitLoadOfLValue(E); @@ -962,6 +1004,76 @@ class ScalarExprEmitter Value *VisitPackIndexingExpr(PackIndexingExpr *E) { return Visit(E->getSelectedExpr()); } + + /* TO_UPSTREAM(BoundsSafety) ON */ + Value *VisitBoundsCheckExpr(BoundsCheckExpr *BCE) { + typedef CodeGenFunction::OpaqueValueMappingData OVMD; + SmallVector bindings; + for (auto *Common : BCE->opaquevalues()) { + const OpaqueValueExpr *ov = cast(Common); + OVMD opaqueData = OVMD::bind(CGF, ov, ov->getSourceExpr()); + bindings.push_back(opaqueData); + } + { + RAIIDisableUBSanChecks DisableChecks(CGF); + // TODO(dliew): We should have a more specific opt-remark. + CodeGenFunction::BoundsSafetyOptRemarkScope Scope(CGF, BNS_OR_GENERAL); + // TODO(dliew): We should have a more specific trap reason. + CGF.EmitBoundsSafetyTrapCheck(BCE->getCond(), BNS_TRAP_GENERAL); + } + Value *Res = Visit(BCE->getGuardedExpr()); + for (auto &opaqueData : bindings) { + opaqueData.unbind(CGF); + } + return Res; + } + + Value *VisitPredefinedBoundsCheckExpr(PredefinedBoundsCheckExpr *BCE) { + CGF.EmitFlexibleArrayCountCheck(BCE); + return Visit(BCE->getGuardedExpr()); + } + + Value *VisitAssumptionExpr(AssumptionExpr *AE) { + llvm::Function *FnAssume = CGF.CGM.getIntrinsic(llvm::Intrinsic::assume); + for (Expr *Assumption : AE->assumptions()) { + Builder.CreateCall(FnAssume, CGF.EvaluateExprAsBool(Assumption)); + } + return Visit(AE->getWrappedExpr()); + } + + Value *VisitGetBoundExpr(GetBoundExpr *E) { + RValue RV = CGF.EmitAnyExpr(E->getSubExpr()); + if (RV.isScalar()) + return RV.getScalarVal(); + + Address Addr = RV.getAggregateAddress(); + WPIndex SrcIndex = E->getBoundKind() == GetBoundExpr::BK_Lower + ? WPIndex::Lower : WPIndex::Upper; + return CGF.GetWidePointerElement(Addr, SrcIndex); + } + + Value *VisitMaterializeSequenceExpr(MaterializeSequenceExpr *MSE) { + if (MSE->isUnbinding()) { + Value *Result = Visit(MSE->getWrappedExpr()); + for (auto *OVE : MSE->opaquevalues()) + CodeGenFunction::OpaqueValueMappingData::unbind(CGF, OVE); + return Result; + } + + for (auto *OVE : MSE->opaquevalues()) { + if (OVE->getType()->isPointerTypeWithBounds()) { + RValue PtrRV = CGF.EmitAnyExpr(OVE->getSourceExpr()); + LValue LV = CGF.MakeAddrLValue(PtrRV.getAggregateAddress(), OVE->getType()); + CodeGenFunction::OpaqueValueMappingData::bind(CGF, OVE, LV); + } else { + CodeGenFunction::OpaqueValueMappingData::bind(CGF, OVE, OVE->getSourceExpr()); + } + } + return Visit(MSE->getWrappedExpr()); + } + + Value *VisitTerminatedByFromIndexableExpr(TerminatedByFromIndexableExpr *E); + /* TO_UPSTREAM(BoundsSafety) OFF */ }; } // end anonymous namespace. @@ -1310,6 +1422,27 @@ void ScalarExprEmitter::EmitIntegerSignChangeCheck(Value *Src, QualType SrcType, {Src, Dst}); } +/* TO_UPSTREAM(BoundsSafety) ON */ +Value *ScalarExprEmitter::EmitBoundsSafetyPointerConversion(CastExpr *E) { + QualType DstTy = E->getType(); + QualType SrcTy = E->getSubExpr()->getType(); + assert(!DstTy->isPointerTypeWithBounds()); + + if (SrcTy->isPointerTypeWithBounds()) { + bool NeedBoundsCheck = + DstTy->isSinglePointerType() && !DstTy->isBoundsAttributedType(); + bool TrackSize = false; + return CGF.EmitWideToRawPtr( + E->getSubExpr(), /*BoundsCheck=*/NeedBoundsCheck, + /*TrapCtx=*/BoundsSafetyTrapCtx::CAST, /*LowerOnlyCheck=*/TrackSize, + /*UpperPtr=*/nullptr, /*LowerPtr=*/nullptr, + /*IsNullAllowed=*/DstTy->isSinglePointerType()); + } + // No additional check is needed between single and unsafe pointers. + return Visit(E->getSubExpr()); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + // Should be called within CodeGenFunction::SanitizerScope RAII scope. // Returns 'i1 false' when the truncation Src -> Dst was lossy. static std::pair(E)); } + /* TO_UPSTREAM(BoundsSafety) ON */ + case CK_BoundsSafetyPointerCast: + return EmitBoundsSafetyPointerConversion(CE); + /* TO_UPSTREAM(BoundsSafety) OFF */ + case CK_BaseToDerived: { const CXXRecordDecl *DerivedClassDecl = DestTy->getPointeeCXXRecordDecl(); assert(DerivedClassDecl && "BaseToDerived arg isn't a C++ object pointer!"); @@ -2695,7 +2833,10 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { } case CK_PointerToIntegral: { assert(!DestTy->isBooleanType() && "bool should use PointerToBool"); - auto *PtrExpr = Visit(E); + /*TO_UPSTREAM(BoundsSafety) ON*/ + Value *PtrExpr = E->getType()->isPointerTypeWithBounds() ? + CGF.EmitWideToRawPtr(E) : Visit(E); + /*TO_UPSTREAM(BoundsSafety) OFF*/ if (CGF.CGM.getCodeGenOpts().StrictVTablePointers) { const QualType SrcType = E->getType(); @@ -2824,8 +2965,19 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { } case CK_IntegralToBoolean: return EmitIntToBoolConversion(Visit(E)); - case CK_PointerToBoolean: - return EmitPointerToBoolConversion(Visit(E), E->getType()); + case CK_PointerToBoolean: { + /*TO_UPSTREAM(BoundsSafety) ON*/ + Value *PtrExpr; + QualType PtrTy = E->getType(); + if (E->getType()->isPointerTypeWithBounds()) { + PtrExpr = CGF.EmitWideToRawPtr(E); + PtrTy = CGF.getContext().getPointerType(PtrTy->getPointeeType()). + withCVRQualifiers(PtrTy.getCVRQualifiers()); + } else + PtrExpr = Visit(E); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + return EmitPointerToBoolConversion(PtrExpr, PtrTy); + } case CK_FloatingToBoolean: { CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, CE); return EmitFloatToBoolConversion(Visit(E)); @@ -3161,6 +3313,14 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, } else if (const PointerType *ptr = type->getAs()) { QualType type = ptr->getPointeeType(); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (E->getSubExpr()->getType()->isValueTerminatedType()) { + assert(isInc); + CGF.EmitValueTerminatedPointerArithmeticCheck(E->getSubExpr()->getType(), + value); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + // VLA types don't have constant size. if (const VariableArrayType *vla = CGF.getContext().getAsVariableArrayType(type)) { @@ -3767,6 +3927,11 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue( // Load/convert the LHS. LValue LHSLV = EmitCheckedLValue(E->getLHS(), CodeGenFunction::TCK_Store); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (CGF.getLangOpts().BoundsSafety) + CGF.EmitValueTerminatedAssignmentCheck(E, LHSLV); + /* TO_UPSTREAM(BoundsSafety) OFF */ + llvm::PHINode *atomicPHI = nullptr; if (const AtomicType *atomicTy = LHSTy->getAs()) { QualType type = atomicTy->getValueType(); @@ -4146,6 +4311,20 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF, std::swap(pointerOperand, indexOperand); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (pointerOperand->getType()->isValueTerminatedType()) { + // The index should be an ICE, since it was checked in Sema. + llvm::APSInt IndexVal = + indexOperand->getIntegerConstantExpr(CGF.getContext()).value(); + if (!IndexVal.isZero()) { + assert((!isSubtraction && IndexVal.isOne()) || + (isSubtraction && IndexVal.isNegative() && IndexVal.isAllOnes())); + CGF.EmitValueTerminatedPointerArithmeticCheck(pointerOperand->getType(), + pointer); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + bool isSigned = indexOperand->getType()->isSignedIntegerOrEnumerationType(); unsigned width = cast(index->getType())->getBitWidth(); @@ -5038,6 +5217,12 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) { llvm::Value *RV = CGF.EmitPointerAuthQualify(PtrAuth, E->getRHS(), LV.getAddress()); CGF.EmitNullabilityCheck(LV, RV, E->getExprLoc()); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (CGF.getLangOpts().BoundsSafety) + CGF.EmitValueTerminatedAssignmentCheck(E, LV); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + CGF.EmitStoreThroughLValue(RValue::get(RV), LV); if (Ignore) @@ -5081,6 +5266,11 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) { LHS = EmitCheckedLValue(E->getLHS(), CodeGenFunction::TCK_Store); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (CGF.getLangOpts().BoundsSafety) + CGF.EmitValueTerminatedAssignmentCheck(E, LHS); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Store the value into the LHS. Bit-fields are handled specially // because the result is altered by the store, i.e., [C99 6.5.16p1] // 'An assignment expression has the value of the left operand after @@ -6096,3 +6286,164 @@ Address CodeGenFunction::EmitCheckedInBoundsGEP( IdxList, SignedIndices, IsSubtraction, Loc, Name), elementType, Align); } + +/* TO_UPSTREAM(BoundsSafety) ON */ +static llvm::Value *EmitTerminatedByFromIndexable(CodeGenFunction &CGF, + const Expr *PtrExpr, + const llvm::APSInt &TermVal) { + auto &Builder = CGF.Builder; + + llvm::Value *Upper = nullptr; + Address PtrAddr = CGF.EmitPointerWithAlignment(PtrExpr, /*BaseInfo=*/nullptr, + /*TBAAInfo=*/nullptr, &Upper); + assert(Upper); + llvm::Value *Ptr = PtrAddr.getBasePointer(); + llvm::Type *ElemTy = PtrAddr.getElementType(); + + // Emit a loop to find the terminator. + + llvm::BasicBlock *Cond = CGF.createBasicBlock("terminated_by.loop_cond"); + llvm::BasicBlock *Cont = CGF.createBasicBlock("terminated_by.loop_cont"); + llvm::BasicBlock *End = CGF.createBasicBlock("terminated_by.loop_end"); + + // Ensure that Ptr < Upper. + CGF.EmitBoundsSafetyBoundsCheck(ElemTy, Ptr, Upper, /*Lower=*/nullptr, + /*AcceptPtrAndUpperNull=*/false, + BoundsSafetyTrapCtx::TERMINATED_BY_FROM_INDEXABLE); + + RawAddress CurAllocaAddr = + CGF.CreateDefaultAlignTempAlloca(Ptr->getType(), "terminated_by.cur"); + Builder.CreateStore(Ptr, CurAllocaAddr); + + CGF.EmitBlock(Cond); + + // Ensure that Cur+1 <= Upper. The initial value of Cur is Ptr, and thus + // smaller than Upper. We assume that Cur+1 won't overflow (this can only + // happen if (void*)Upper+sizeof(ElemTy) overflows). + llvm::Constant *One = llvm::ConstantInt::get(CGF.SizeTy, 1); + llvm::Value *Cur = nullptr; + llvm::Value *OnePastCur = nullptr; + llvm::Value *AccessCheck = nullptr; + { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + CGF, BNS_OR_TERMINATED_BY_FROM_INDEXABLE_TERM); + Cur = Builder.CreateLoad(CurAllocaAddr, "terminated_by.cur"); + + OnePastCur = Builder.CreateInBoundsGEP(ElemTy, Cur, One, + "terminated_by.one_past_cur"); + AccessCheck = + Builder.CreateICmpULE(OnePastCur, Upper, "terminated_by.check_access"); + CGF.EmitBoundsSafetyTrapCheck(AccessCheck, + BNS_TRAP_TERMINATED_BY_FROM_INDEXABLE_TERM); + } + + // Check terminator. + Address ElemAddr = Address(Cur, ElemTy, PtrAddr.getAlignment()); + llvm::Value *Elem = Builder.CreateLoad(ElemAddr); + llvm::Value *Term = CGF.EmitTerminator(TermVal, ElemTy); + llvm::Value *TermCheck = + Builder.CreateICmpEQ(Elem, Term, "terminated_by.check_terminator"); + Builder.CreateCondBr(TermCheck, End, Cont); + + CGF.EmitBlock(Cont); + + Builder.CreateStore(OnePastCur, CurAllocaAddr); + Builder.CreateBr(Cond); + + CGF.EmitBlock(End); + + return Ptr; +} + +static llvm::Value *EmitTerminatedByFromIndexableWithPtrToTerm( + CodeGenFunction &CGF, const Expr *PtrExpr, const Expr *PtrToTermExpr, + const llvm::APSInt &TermVal) { + auto &Builder = CGF.Builder; + + llvm::Value *Upper = nullptr; + Address PtrAddr = CGF.EmitPointerWithAlignment(PtrExpr, /*BaseInfo=*/nullptr, + /*TBAAInfo=*/nullptr, &Upper); + assert(Upper); + llvm::Value *Ptr = PtrAddr.getBasePointer(); + llvm::Type *ElemTy = PtrAddr.getElementType(); + + Address PtrToTermAddr = CGF.EmitPointerWithAlignment(PtrToTermExpr); + llvm::Value *PtrToTerm = PtrToTermAddr.getBasePointer(); + + // Ptr <= PtrToTerm + llvm::Value *PtrCheck = nullptr; + { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + &CGF, BNS_OR_TERMINATED_BY_FROM_INDEXABLE_PTR_GT_TERM_PTR); + + PtrCheck = Builder.CreateICmpULE(Ptr, PtrToTerm, + "terminated_by.check_ptr_le_term_ptr"); + CGF.EmitBoundsSafetyTrapCheck( + PtrCheck, BNS_TRAP_TERMINATED_BY_FROM_INDEXABLE_PTR_GT_TERM_PTR); + } + + // PtrToTerm+sizeof(ElemTy) does not overflow + llvm::Value *DidNotOverflow = nullptr; + llvm::Value *OnePastPtrToTerm = nullptr; + { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + &CGF, BNS_OR_TERMINATED_BY_FROM_INDEXABLE_TERM_PTR_OVERFLOW); + + llvm::Constant *One = llvm::ConstantInt::get(CGF.SizeTy, 1); + OnePastPtrToTerm = Builder.CreateGEP(ElemTy, PtrToTerm, One, + "terminated_by.one_past_term_ptr"); + + DidNotOverflow = + Builder.CreateICmpUGT(OnePastPtrToTerm, PtrToTerm, + "terminated_by.check_one_past_term_ptr_overflow"); + CGF.EmitBoundsSafetyTrapCheck( + DidNotOverflow, BNS_TRAP_TERMINATED_BY_FROM_INDEXABLE_TERM_PTR_OVERFLOW); + } + + // PtrToTerm+sizeof(ElemTy) <= Upper + llvm::Value *OnePastPtrToTermCheck = nullptr; + { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + &CGF, + BNS_OR_TERMINATED_BY_FROM_INDEXABLE_TERM_PTR_PLUS_ONE_GT_UPPER_BOUND); + + OnePastPtrToTermCheck = + Builder.CreateICmpULE(OnePastPtrToTerm, Upper, + "terminated_by.check_one_past_term_ptr_le_upper"); + CGF.EmitBoundsSafetyTrapCheck( + OnePastPtrToTermCheck, + BNS_TRAP_TERMINATED_BY_FROM_INDEXABLE_TERM_PTR_PLUS_ONE_GT_UPPER_BOUND); + } + + // Check terminator. + llvm::Value *TermCheck = nullptr; + { + CodeGenFunction::BoundsSafetyOptRemarkScope Scope( + &CGF, BNS_OR_TERMINATED_BY_FROM_INDEXABLE_TERM); + + llvm::Value *Elem = Builder.CreateLoad(PtrToTermAddr); + llvm::Value *Term = CGF.EmitTerminator(TermVal, ElemTy); + TermCheck = + Builder.CreateICmpEQ(Elem, Term, "terminated_by.check_terminator"); + CGF.EmitBoundsSafetyTrapCheck(TermCheck, + BNS_TRAP_TERMINATED_BY_FROM_INDEXABLE_TERM); + } + + return Ptr; +} + +Value *ScalarExprEmitter::VisitTerminatedByFromIndexableExpr( + TerminatedByFromIndexableExpr *E) { + // The pointer should have been cast to an __indexable pointer in Sema. + assert(E->getPointer()->getType()->isIndexablePointerType()); + + const auto *VTT = E->getType()->getAs(); + llvm::APSInt TermVal = VTT->getTerminatorValue(CGF.getContext()); + + if (const auto *PtrToTermExpr = E->getPointerToTerminator()) { + return EmitTerminatedByFromIndexableWithPtrToTerm(CGF, E->getPointer(), + PtrToTermExpr, TermVal); + } + return EmitTerminatedByFromIndexable(CGF, E->getPointer(), TermVal); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index 27c7c2fa9cba1..a64df81d6185a 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -3678,6 +3678,15 @@ void CodeGenFunction::EmitExtendGCLifetime(llvm::Value *object) { EmitNounwindRuntimeCall(extender, object); } +/// Return 'void (void *, const void *)', which is the type of ObjC atomic +/// property copy helper functions. +static QualType getObjCAtomicPropertyCopyHelperFunctionType(ASTContext &Ctx) { + SmallVector ArgTys; + ArgTys.push_back(Ctx.VoidPtrTy); + ArgTys.push_back(Ctx.getPointerType(Ctx.VoidTy.withConst())); + return Ctx.getFunctionType(Ctx.VoidTy, ArgTys, {}); +} + /// GenerateObjCAtomicSetterCopyHelperFunction - Given a c++ object type with /// non-trivial copy assignment function, produce following helper function. /// static void copyHelper(Ty *dest, const Ty *source) { *dest = *source; } @@ -3698,6 +3707,8 @@ CodeGenFunction::GenerateObjCAtomicSetterCopyHelperFunction( CharUnits Alignment = C.getTypeAlignInChars(Ty); llvm::Constant *Fn = getNonTrivialCStructMoveAssignmentOperator( CGM, Alignment, Alignment, Ty.isVolatileQualified(), Ty); + Fn = CGM.getFunctionPointer(Fn, + getObjCAtomicPropertyCopyHelperFunctionType(C)); return Fn; } @@ -3778,7 +3789,8 @@ CodeGenFunction::GenerateObjCAtomicSetterCopyHelperFunction( EmitStmt(TheCall); FinishFunction(); - HelperFn = Fn; + HelperFn = CGM.getFunctionPointer( + Fn, getObjCAtomicPropertyCopyHelperFunctionType(C)); CGM.setAtomicSetterHelperFnMap(Ty, HelperFn); return HelperFn; } @@ -3796,6 +3808,8 @@ llvm::Constant *CodeGenFunction::GenerateObjCAtomicGetterCopyHelperFunction( CharUnits Alignment = C.getTypeAlignInChars(Ty); llvm::Constant *Fn = getNonTrivialCStructCopyConstructor( CGM, Alignment, Alignment, Ty.isVolatileQualified(), Ty); + Fn = CGM.getFunctionPointer(Fn, + getObjCAtomicPropertyCopyHelperFunctionType(C)); return Fn; } @@ -3897,7 +3911,8 @@ llvm::Constant *CodeGenFunction::GenerateObjCAtomicGetterCopyHelperFunction( AggValueSlot::IsNotAliased, AggValueSlot::DoesNotOverlap)); FinishFunction(); - HelperFn = Fn; + HelperFn = CGM.getFunctionPointer( + Fn, getObjCAtomicPropertyCopyHelperFunctionType(C)); CGM.setAtomicGetterHelperFnMap(Ty, HelperFn); return HelperFn; } diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index 33f6b0470061f..18d11735c58d0 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -2833,7 +2833,8 @@ CGObjCGNU::GenerateMessageSendSuper(CodeGenFunction &CGF, llvm::Type::getInt1Ty(VMContext), IsClassMessage))}; llvm::MDNode *node = llvm::MDNode::get(VMContext, impMD); - CGCallee callee(CGCalleeInfo(), imp); + CGPointerAuthInfo pointerAuth; // TODO + CGCallee callee(CGCalleeInfo(), imp, pointerAuth); llvm::CallBase *call; RValue msgRet = CGF.EmitCall(MSI.CallInfo, callee, Return, ActualArgs, &call); @@ -3031,7 +3032,8 @@ CGObjCGNU::GenerateMessageSend(CodeGenFunction &CGF, imp = EnforceType(Builder, imp, MSI.MessengerType); llvm::CallBase *call; - CGCallee callee(CGCalleeInfo(), imp); + CGPointerAuthInfo pointerAuth; // TODO + CGCallee callee(CGCalleeInfo(), imp, pointerAuth); RValue msgRet = CGF.EmitCall(MSI.CallInfo, callee, Return, ActualArgs, &call); if (!isDirect) call->setMetadata(msgSendMDKind, node); diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 1c23a8b4db918..d23ba774c4522 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -3100,6 +3100,8 @@ static void PushProtocolProperties( SmallVectorImpl &Properties, const ObjCProtocolDecl *Proto, bool IsClassProperty) { for (const auto *PD : Proto->properties()) { + if (Proto->getASTContext().hasUnavailableFeature(PD)) + continue; if (IsClassProperty != PD->isClassProperty()) continue; if (!PropertySet.insert(PD->getIdentifier()).second) @@ -3141,6 +3143,8 @@ llvm::Constant *CGObjCCommonMac::EmitPropertyList( if (const ObjCInterfaceDecl *OID = dyn_cast(OCD)) for (const ObjCCategoryDecl *ClassExt : OID->known_extensions()) for (auto *PD : ClassExt->properties()) { + if (CGM.getContext().hasUnavailableFeature(PD)) + continue; if (IsClassProperty != PD->isClassProperty()) continue; if (PD->isDirectProperty()) @@ -3150,6 +3154,8 @@ llvm::Constant *CGObjCCommonMac::EmitPropertyList( } for (const auto *PD : OCD->properties()) { + if (CGM.getContext().hasUnavailableFeature(PD)) + continue; if (IsClassProperty != PD->isClassProperty()) continue; // Don't emit duplicate metadata for properties that were already in a @@ -5203,6 +5209,9 @@ void IvarLayoutBuilder::visitAggregate(Iterator begin, Iterator end, for (; begin != end; ++begin) { auto field = *begin; + if (CGM.getContext().hasUnavailableFeature(field)) + continue; + // Skip over bitfields. if (field->isBitField()) { continue; @@ -6623,6 +6632,9 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { void CGObjCNonFragileABIMac::emitMethodConstant(ConstantArrayBuilder &builder, const ObjCMethodDecl *MD, bool forProtocol) { + if (CGM.getContext().hasUnavailableFeature(MD)) + return; + auto method = builder.beginStruct(ObjCTypes.MethodTy); method.add(GetMethodVarName(MD->getSelector())); method.add(GetMethodVarType(MD)); @@ -6633,7 +6645,15 @@ void CGObjCNonFragileABIMac::emitMethodConstant(ConstantArrayBuilder &builder, } else { llvm::Function *fn = GetMethodDefinition(MD); assert(fn && "no definition for method?"); - method.add(fn); + + if (const auto &schema = + CGM.getCodeGenOpts().PointerAuth.ObjCMethodListFunctionPointers) { + auto *bitcast = + llvm::ConstantExpr::getBitCast(fn, ObjCTypes.Int8PtrProgramASTy); + method.addSignedPointer(bitcast, schema, GlobalDecl(), QualType()); + } else { + method.add(fn); + } } method.finishAndAddTo(builder); @@ -6812,6 +6832,9 @@ CGObjCNonFragileABIMac::EmitIvarList(const ObjCImplementationDecl *ID) { if (!IVD->getDeclName()) continue; + if (CGM.getContext().hasUnavailableFeature(IVD)) + continue; + auto ivar = ivars.beginStruct(ObjCTypes.IvarnfABITy); ivar.add(EmitIvarOffsetVar(ID->getClassInterface(), IVD, ComputeIvarBaseOffset(CGM, ID, IVD))); @@ -6903,9 +6926,8 @@ CGObjCNonFragileABIMac::GetOrEmitProtocol(const ObjCProtocolDecl *PD) { return Entry; // Use the protocol definition, if there is one. - assert(PD->hasDefinition() && - "emitting protocol metadata without definition"); - PD = PD->getDefinition(); + if (const ObjCProtocolDecl *Def = PD->getDefinition()) + PD = Def; auto methodLists = ProtocolMethodLists::get(PD); @@ -7201,7 +7223,8 @@ RValue CGObjCNonFragileABIMac::EmitVTableMessageSend( llvm::Value *calleePtr = CGF.Builder.CreateLoad(calleeAddr, "msgSend_fn"); calleePtr = CGF.Builder.CreateBitCast(calleePtr, MSI.MessengerType); - CGCallee callee(CGCalleeInfo(), calleePtr); + CGPointerAuthInfo pointerAuth; // This code path is unsupported. + CGCallee callee(CGCalleeInfo(), calleePtr, pointerAuth); RValue result = CGF.EmitCall(MSI.CallInfo, callee, returnSlot, args); return nullReturn.complete(CGF, returnSlot, result, resultType, formalArgs, @@ -7260,7 +7283,12 @@ CGObjCNonFragileABIMac::GetClassGlobal(StringRef Name, } assert(GV->getLinkage() == L); - return GV; + + if (IsForDefinition || + GV->getValueType() == ObjCTypes.ClassnfABITy) + return GV; + + return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.ClassnfABIPtrTy); } llvm::Constant * @@ -7388,7 +7416,8 @@ llvm::Value *CGObjCNonFragileABIMac::EmitMetaClassRef( llvm::Value *CGObjCNonFragileABIMac::GetClass(CodeGenFunction &CGF, const ObjCInterfaceDecl *ID) { if (ID->isWeakImported()) { - auto ClassGV = GetClassGlobal(ID, /*metaclass*/ false, NotForDefinition); + llvm::Constant *ClassGV = GetClassGlobal(ID, /*metaclass*/ false, + NotForDefinition); (void)ClassGV; assert(!isa(ClassGV) || cast(ClassGV)->hasExternalWeakLinkage()); @@ -7676,10 +7705,18 @@ CGObjCNonFragileABIMac::GetInterfaceEHType(const ObjCInterfaceDecl *ID, } llvm::Value *VTableIdx = llvm::ConstantInt::get(CGM.Int32Ty, 2); + llvm::Constant *VTablePtr = llvm::ConstantExpr::getInBoundsGetElementPtr( + VTableGV->getValueType(), VTableGV, VTableIdx); + ConstantInitBuilder builder(CGM); auto values = builder.beginStruct(ObjCTypes.EHTypeTy); - values.add(llvm::ConstantExpr::getInBoundsGetElementPtr( - VTableGV->getValueType(), VTableGV, VTableIdx)); + + if (auto &Schema = CGM.getCodeGenOpts().PointerAuth.CXXVTablePointers) { + values.addSignedPointer(VTablePtr, Schema, GlobalDecl(), QualType()); + } else { + values.add(VTablePtr); + } + values.add(GetClassName(ClassName)); values.add(GetClassGlobal(ID, /*metaclass*/ false, NotForDefinition)); diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp index 0a183a8524c17..66d46acb496e8 100644 --- a/clang/lib/CodeGen/CGPointerAuth.cpp +++ b/clang/lib/CodeGen/CGPointerAuth.cpp @@ -13,6 +13,7 @@ #include "CodeGenFunction.h" #include "CodeGenModule.h" +#include "CGCall.h" #include "clang/CodeGen/CodeGenABITypes.h" #include "clang/CodeGen/ConstantInitBuilder.h" #include "llvm/Analysis/ValueTracking.h" @@ -21,6 +22,11 @@ using namespace clang; using namespace CodeGen; +// FIXME: Temporarily allow both ConstantPtrAuth and llvm.ptrauth emission. +static llvm::cl::opt PtrauthEmitWrapperGlobals( + "ptrauth-emit-wrapper-globals", llvm::cl::init(true), llvm::cl::Hidden, + llvm::cl::desc("Emit llvm.ptrauth globals rather than ptrauth Constants")); + /// Given a pointer-authentication schema, return a concrete "other" /// discriminator for it. llvm::ConstantInt *CodeGenModule::getPointerAuthOtherDiscriminator( @@ -46,16 +52,6 @@ llvm::ConstantInt *CodeGenModule::getPointerAuthOtherDiscriminator( llvm_unreachable("bad discrimination kind"); } -uint16_t CodeGen::getPointerAuthTypeDiscriminator(CodeGenModule &CGM, - QualType FunctionType) { - return CGM.getContext().getPointerAuthTypeDiscriminator(FunctionType); -} - -uint16_t CodeGen::getPointerAuthDeclDiscriminator(CodeGenModule &CGM, - GlobalDecl Declaration) { - return CGM.getPointerAuthDeclDiscriminator(Declaration); -} - /// Return the "other" decl-specific discriminator for the given decl. uint16_t CodeGenModule::getPointerAuthDeclDiscriminator(GlobalDecl Declaration) { @@ -90,41 +86,6 @@ CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) { Discriminator); } -llvm::Value * -CodeGenFunction::EmitPointerAuthBlendDiscriminator(llvm::Value *StorageAddress, - llvm::Value *Discriminator) { - StorageAddress = Builder.CreatePtrToInt(StorageAddress, IntPtrTy); - auto Intrinsic = CGM.getIntrinsic(llvm::Intrinsic::ptrauth_blend); - return Builder.CreateCall(Intrinsic, {StorageAddress, Discriminator}); -} - -/// Emit the concrete pointer authentication informaton for the -/// given authentication schema. -CGPointerAuthInfo CodeGenFunction::EmitPointerAuthInfo( - const PointerAuthSchema &Schema, llvm::Value *StorageAddress, - GlobalDecl SchemaDecl, QualType SchemaType) { - if (!Schema) - return CGPointerAuthInfo(); - - llvm::Value *Discriminator = - CGM.getPointerAuthOtherDiscriminator(Schema, SchemaDecl, SchemaType); - - if (Schema.isAddressDiscriminated()) { - assert(StorageAddress && - "address not provided for address-discriminated schema"); - - if (Discriminator) - Discriminator = - EmitPointerAuthBlendDiscriminator(StorageAddress, Discriminator); - else - Discriminator = Builder.CreatePtrToInt(StorageAddress, IntPtrTy); - } - - return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(), - Schema.isIsaPointer(), - Schema.authenticatesNullValues(), Discriminator); -} - CGPointerAuthInfo CodeGenFunction::EmitPointerAuthInfo(PointerAuthQualifier Qual, Address StorageAddress) { @@ -400,6 +361,123 @@ llvm::Value *CodeGenFunction::emitPointerAuthResign( return Value; } +/// We use an abstract, side-allocated cache for signed function pointers +/// because (1) most compiler invocations will not need this cache at all, +/// since they don't use signed function pointers, and (2) the +/// representation is pretty complicated (an llvm::ValueMap) and we don't +/// want to have to include that information in CodeGenModule.h. +template +static CacheTy &getOrCreateCache(void *&abstractStorage) { + auto cache = static_cast(abstractStorage); + if (cache) return *cache; + + abstractStorage = cache = new CacheTy(); + return *cache; +} + +template +static void destroyCache(void *&abstractStorage) { + delete static_cast(abstractStorage); + abstractStorage = nullptr; +} + +namespace { +struct PointerAuthConstantEntry { + unsigned Key; + llvm::Constant *OtherDiscriminator; + llvm::GlobalVariable *Global; +}; + +using PointerAuthConstantEntries = + std::vector; +using ByConstantCacheTy = + llvm::ValueMap; +using ByDeclCacheTy = + llvm::DenseMap; +} + +/// Build a global signed-pointer constant. +static llvm::GlobalVariable * +buildConstantSignedPointer(CodeGenModule &CGM, + llvm::Constant *pointer, + unsigned key, + llvm::Constant *storageAddress, + llvm::ConstantInt *otherDiscriminator) { + ConstantInitBuilder builder(CGM); + auto values = builder.beginStruct(); + values.add(pointer); + values.addInt(CGM.Int32Ty, key); + if (storageAddress) { + if (isa(storageAddress)) { + assert(!storageAddress->isNullValue() && + "expecting pointer or special address-discriminator indicator"); + values.add(storageAddress); + } else { + values.add(llvm::ConstantExpr::getPtrToInt(storageAddress, CGM.IntPtrTy)); + } + } else { + values.addInt(CGM.SizeTy, 0); + } + if (otherDiscriminator) { + assert(otherDiscriminator->getType() == CGM.SizeTy); + values.add(otherDiscriminator); + } else { + values.addInt(CGM.SizeTy, 0); + } + + auto *stripped = pointer->stripPointerCasts(); + StringRef name; + if (const auto *origGlobal = dyn_cast(stripped)) + name = origGlobal->getName(); + else if (const auto *ce = dyn_cast(stripped)) + if (ce->getOpcode() == llvm::Instruction::GetElementPtr) + name = cast(ce)->getPointerOperand()->getName(); + + auto global = values.finishAndCreateGlobal( + name + ".ptrauth", + CGM.getPointerAlign(), + /*constant*/ true, + llvm::GlobalVariable::PrivateLinkage); + global->setSection("llvm.ptrauth"); + + return global; +} + +llvm::Value * +CodeGenFunction::EmitPointerAuthBlendDiscriminator(llvm::Value *StorageAddress, + llvm::Value *Discriminator) { + StorageAddress = Builder.CreatePtrToInt(StorageAddress, IntPtrTy); + auto Intrinsic = CGM.getIntrinsic(llvm::Intrinsic::ptrauth_blend); + return Builder.CreateCall(Intrinsic, {StorageAddress, Discriminator}); +} + +/// Emit the concrete pointer authentication informaton for the +/// given authentication schema. +CGPointerAuthInfo CodeGenFunction::EmitPointerAuthInfo( + const PointerAuthSchema &Schema, llvm::Value *StorageAddress, + GlobalDecl SchemaDecl, QualType SchemaType) { + if (!Schema) + return CGPointerAuthInfo(); + + llvm::Value *Discriminator = + CGM.getPointerAuthOtherDiscriminator(Schema, SchemaDecl, SchemaType); + + if (Schema.isAddressDiscriminated()) { + assert(StorageAddress && + "address not provided for address-discriminated schema"); + + if (Discriminator) + Discriminator = + EmitPointerAuthBlendDiscriminator(StorageAddress, Discriminator); + else + Discriminator = Builder.CreatePtrToInt(StorageAddress, IntPtrTy); + } + + return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(), + Schema.isIsaPointer(), + Schema.authenticatesNullValues(), Discriminator); +} + void CodeGenFunction::EmitPointerAuthCopy(PointerAuthQualifier Qual, QualType T, Address DestAddress, Address SrcAddress) { @@ -421,6 +499,49 @@ llvm::Constant * CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key, llvm::Constant *StorageAddress, llvm::ConstantInt *OtherDiscriminator) { + if (PtrauthEmitWrapperGlobals) { + // Unique based on the underlying value, not a signing of it. + auto stripped = Pointer->stripPointerCasts(); + + PointerAuthConstantEntries *entries = nullptr; + + // We can cache this for discriminators that aren't defined in terms + // of globals. Discriminators defined in terms of globals (1) would + // require additional tracking to be safe and (2) only come up with + // address-specific discrimination, where this entry is almost certainly + // unique to the use-site anyway. + if (!StorageAddress && + (!OtherDiscriminator || + isa(OtherDiscriminator))) { + + // Get or create the cache. + auto &cache = + getOrCreateCache(ConstantSignedPointersByConstant); + + // Check for an existing entry. + entries = &cache[stripped]; + for (auto &entry : *entries) { + if (entry.Key == Key && entry.OtherDiscriminator == OtherDiscriminator) { + auto global = entry.Global; + return llvm::ConstantExpr::getBitCast(global, Pointer->getType()); + } + } + } + + // Build the constant. + auto global = + buildConstantSignedPointer(*this, stripped, Key, StorageAddress, + OtherDiscriminator); + + // Cache if applicable. + if (entries) { + entries->push_back({ Key, OtherDiscriminator, global }); + } + + // Cast to the original type. + return llvm::ConstantExpr::getBitCast(global, Pointer->getType()); + } + llvm::Constant *AddressDiscriminator; if (StorageAddress) { assert(StorageAddress->getType() == UnqualPtrTy); @@ -442,7 +563,7 @@ CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key, IntegerDiscriminator, AddressDiscriminator); } -/// Does a given PointerAuthScheme require us to sign a value +// Does a given PointerAuthScheme require us to sign a value bool CodeGenModule::shouldSignPointer(const PointerAuthSchema &Schema) { auto AuthenticationMode = Schema.getAuthenticationMode(); return AuthenticationMode == PointerAuthenticationMode::SignAndStrip || @@ -463,18 +584,47 @@ llvm::Constant *CodeGenModule::getConstantSignedPointer( OtherDiscriminator); } +void CodeGenModule::destroyConstantSignedPointerCaches() { + destroyCache(ConstantSignedPointersByConstant); + destroyCache(ConstantSignedPointersByDecl); + destroyCache(SignedThunkPointers); +} + /// If applicable, sign a given constant function pointer with the ABI rules for /// functionType. llvm::Constant *CodeGenModule::getFunctionPointer(llvm::Constant *Pointer, - QualType FunctionType) { + QualType FunctionType, + GlobalDecl GD) { assert(FunctionType->isFunctionType() || FunctionType->isFunctionReferenceType() || FunctionType->isFunctionPointerType()); - if (auto PointerAuth = getFunctionPointerAuthInfo(FunctionType)) - return getConstantSignedPointer( - Pointer, PointerAuth.getKey(), /*StorageAddress=*/nullptr, - cast_or_null(PointerAuth.getDiscriminator())); + if (auto pointerAuth = getFunctionPointerAuthInfo(FunctionType)) { + // Check a cache that, for now, just has entries for functions signed + // with the standard function-pointer scheme. + // Cache function pointers based on their decl. Anything without a decl is + // going to be a one-off that doesn't need to be cached anyway. + llvm::Constant **entry = nullptr; + if (GD) { + auto FD = cast(GD.getDecl()); + auto &cache = + getOrCreateCache(ConstantSignedPointersByDecl); + entry = &cache[FD->getCanonicalDecl()]; + if (*entry) + return llvm::ConstantExpr::getBitCast(*entry, Pointer->getType()); + } + + // If the cache misses, build a new constant. It's not a *problem* to + // have more than one of these for a particular function, but it's nice + // to avoid it. + Pointer = getConstantSignedPointer( + Pointer, pointerAuth.getKey(), nullptr, + cast_or_null(pointerAuth.getDiscriminator())); + + // Store the result back into the cache, if any. + if (entry) + *entry = Pointer; + } return Pointer; } @@ -511,12 +661,26 @@ CGPointerAuthInfo CodeGenModule::getMemberFunctionPointerAuthInfo(QualType FT) { } llvm::Constant *CodeGenModule::getMemberFunctionPointer(llvm::Constant *Pointer, - QualType FT) { - if (CGPointerAuthInfo PointerAuth = getMemberFunctionPointerAuthInfo(FT)) - return getConstantSignedPointer( + QualType FT, + const FunctionDecl *FD) { + if (CGPointerAuthInfo PointerAuth = getMemberFunctionPointerAuthInfo(FT)) { + llvm::Constant **Entry = nullptr; + if (FD) { + auto &Cache = + getOrCreateCache(SignedThunkPointers); + Entry = &Cache[FD->getCanonicalDecl()]; + if (*Entry) + return llvm::ConstantExpr::getBitCast(*Entry, Pointer->getType()); + } + + Pointer = getConstantSignedPointer( Pointer, PointerAuth.getKey(), nullptr, cast_or_null(PointerAuth.getDiscriminator())); + if (Entry) + *Entry = Pointer; + } + return Pointer; } @@ -525,7 +689,7 @@ llvm::Constant *CodeGenModule::getMemberFunctionPointer(const FunctionDecl *FD, QualType FT = FD->getType(); FT = getContext().getMemberPointerType(FT, /*Qualifier=*/nullptr, cast(FD)->getParent()); - return getMemberFunctionPointer(getRawFunctionPointer(FD, Ty), FT); + return getMemberFunctionPointer(getRawFunctionPointer(FD, Ty), FT, FD); } std::optional diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 3562b4ea22a24..ff845b02f8342 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -900,7 +900,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) { // the condition and the dead arm of the if/else. bool CondConstant; if (ConstantFoldsToSimpleInteger(S.getCond(), CondConstant, - S.isConstexpr())) { + (S.isConstexpr() || S.isObjCAvailabilityCheckWithDomainName()))) { // Figure out which block (then or else) is executed. const Stmt *Executed = S.getThen(); const Stmt *Skipped = Else; @@ -909,7 +909,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) { // If the skipped block has no labels in it, just emit the executed block. // This avoids emitting dead code and simplifies the CFG substantially. - if (S.isConstexpr() || !ContainsLabel(Skipped)) { + if ((S.isConstexpr() || S.isObjCAvailabilityCheckWithDomainName()) || !ContainsLabel(Skipped)) { if (CondConstant) incrementProfileCounter(&S); if (Executed) { diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt index a05b31f971e18..6394777793469 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -105,6 +105,8 @@ add_clang_library(clangCodeGen CodeGenSYCL.cpp CodeGenTBAA.cpp CodeGenTypes.cpp + BoundsSafetyOptRemarks.cpp + BoundsSafetyTraps.cpp ConstantInitBuilder.cpp CoverageMappingGen.cpp ItaniumCXXABI.cpp diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp index 1f5eb427b566f..969f343c68a64 100644 --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -108,15 +108,18 @@ static void reportOptRecordError(Error E, DiagnosticsEngine &Diags, BackendConsumer::BackendConsumer(CompilerInstance &CI, BackendAction Action, IntrusiveRefCntPtr VFS, + const CASOptions &CASOpts, LLVMContext &C, SmallVector LinkModules, StringRef InFile, std::unique_ptr OS, CoverageSourceInfo *CoverageInfo, + std::unique_ptr CasIDOS, llvm::Module *CurLinkModule) : CI(CI), Diags(CI.getDiagnostics()), CodeGenOpts(CI.getCodeGenOpts()), TargetOpts(CI.getTargetOpts()), LangOpts(CI.getLangOpts()), - AsmOutStream(std::move(OS)), FS(VFS), Action(Action), + CASOpts(CASOpts), AsmOutStream(std::move(OS)), + CasIDStream(std::move(CasIDOS)), FS(VFS), Action(Action), Gen(CreateLLVMCodeGen(Diags, InFile, std::move(VFS), CI.getHeaderSearchOpts(), CI.getPreprocessorOpts(), CI.getCodeGenOpts(), C, CoverageInfo)), @@ -313,9 +316,10 @@ void BackendConsumer::HandleTranslationUnit(ASTContext &C) { EmbedBitcode(getModule(), CodeGenOpts, llvm::MemoryBufferRef()); - emitBackendOutput(CI, CI.getCodeGenOpts(), + emitBackendOutput(CI, CI.getCodeGenOpts(), CASOpts, C.getTargetInfo().getDataLayoutString(), getModule(), - Action, FS, std::move(AsmOutStream), this); + Action, FS, std::move(AsmOutStream), std::move(CasIDStream), + this); Ctx.setDiagnosticHandler(std::move(OldDiagnosticHandler)); @@ -959,8 +963,23 @@ std::unique_ptr CodeGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { BackendAction BA = static_cast(Act); std::unique_ptr OS = CI.takeOutputStream(); - if (!OS) + std::unique_ptr CasIDOS = nullptr; + if (!OS) { OS = GetOutputStream(CI, InFile, BA); + auto OutputFile = StringRef(CI.getFrontendOpts().OutputFile); + if (CI.getCodeGenOpts().UseCASBackend && + CI.getCodeGenOpts().EmitCASIDFile && + CI.getCodeGenOpts().getCASObjMode() != llvm::CASBackendMode::CASID && + BA == Backend_EmitObj && OutputFile != "-") { + std::string OutputPathCASIDFile = std::string(OutputFile); + OutputPathCASIDFile.append(".casid"); + std::error_code EC; + CasIDOS = std::make_unique(OutputPathCASIDFile, EC); + if (EC) + CI.getDiagnostics().Report(diag::err_fe_unable_to_open_output) + << EC.message(); + } + } if (BA != Backend_EmitNothing && !OS) return nullptr; @@ -976,8 +995,10 @@ CodeGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { CI.getPreprocessor()); std::unique_ptr Result(new BackendConsumer( - CI, BA, &CI.getVirtualFileSystem(), *VMContext, std::move(LinkModules), - InFile, std::move(OS), CoverageInfo)); + CI, BA, &CI.getVirtualFileSystem(), + CI.getCASOpts(), // MCCAS + *VMContext, std::move(LinkModules), + InFile, std::move(OS), CoverageInfo, std::move(CasIDOS))); BEConsumer = Result.get(); // Enable generating macro debug info only when debug info is not disabled and @@ -1154,9 +1175,10 @@ void CodeGenAction::ExecuteAction() { // Set clang diagnostic handler. To do this we need to create a fake // BackendConsumer. - BackendConsumer Result(CI, BA, &CI.getVirtualFileSystem(), *VMContext, + BackendConsumer Result(CI, BA, &CI.getVirtualFileSystem(), CI.getCASOpts(), + *VMContext, std::move(LinkModules), "", nullptr, nullptr, - TheModule.get()); + nullptr, TheModule.get()); // Link in each pending link module. if (!CodeGenOpts.LinkBitcodePostopt && Result.LinkInModules(&*TheModule)) @@ -1185,6 +1207,7 @@ void CodeGenAction::ExecuteAction() { std::move(*OptRecordFileOrErr); emitBackendOutput(CI, CI.getCodeGenOpts(), + CI.getCASOpts(), // MCCAS CI.getTarget().getDataLayoutString(), TheModule.get(), BA, CI.getFileManager().getVirtualFileSystemPtr(), std::move(OS)); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 4d29ceace646f..635582ee25f4b 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -40,6 +40,8 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/FPEnv.h" +// TO_UPSTREAM(BoundsSafety) ON +#include "llvm/IR/InlineAsm.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" @@ -266,9 +268,17 @@ TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) { case Type::DeducedTemplateSpecialization: llvm_unreachable("undeduced type in IR-generation"); + /* TO_UPSTREAM(BoundsSafety) ON*/ + case Type::Pointer: { + auto const *PT = dyn_cast(type); + if (!PT->hasRawPointerLayout()) + return TEK_Aggregate; + return TEK_Scalar; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Various scalar types. case Type::Builtin: - case Type::Pointer: case Type::BlockPointer: case Type::LValueReference: case Type::RValueReference: @@ -308,6 +318,43 @@ TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) { } } +/* TO_UPSTREAM(BoundsSafety) ON*/ +llvm::BasicBlock *CodeGenFunction::createUnmergeableBasicBlock( + const Twine &name, llvm::Function *parent, llvm::BasicBlock *before) { + auto *BB = createBasicBlock(name, parent, before); + // This approach is the same approach used by Swift. + // TODO: Find a better way to do this (rdar://137627723). + + // Emit unique side-effecting inline asm calls in order to eliminate + // the possibility that an LLVM optimization or code generation pass + // will merge these blocks back together again. We emit an empty asm + // string with the side-effect flag set, and with a unique integer + // argument. + llvm::IntegerType *asmArgTy = CGM.Int64Ty; + llvm::Type *argTys = {asmArgTy}; + llvm::FunctionType *asmFnTy = + llvm::FunctionType::get(CGM.VoidTy, argTys, /* isVarArg=*/false); + // "n" is an input constraint stating that the first argument to the call + // will be an integer literal. + llvm::InlineAsm *inlineAsm = + llvm::InlineAsm::get(asmFnTy, /*AsmString=*/"", /*Constraints=*/"n", + /*hasSideEffects=*/true); + + // Use the builder so that any opt-remarks and attributes are automatically + // applied by the builder. The current state of the builder is saved so it + // can be used for creating the asm call and then the builder is reset to + // its previous state. + auto OldInsertPoint = Builder.GetInsertPoint(); + auto* OldInsertBB = Builder.GetInsertBlock(); + Builder.SetInsertPoint(BB); + Builder.CreateCall( + inlineAsm, + llvm::ConstantInt::get(asmArgTy, CGM.getAndIncrementUniqueTrapCount())); + Builder.SetInsertPoint(OldInsertBB, OldInsertPoint); + return BB; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + llvm::DebugLoc CodeGenFunction::EmitReturnBlock() { // For cleanliness, we try to avoid emitting the return block for // simple cases. @@ -2573,6 +2620,10 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) { case Type::SubstTemplateTypeParm: case Type::MacroQualified: case Type::CountAttributed: + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: + case Type::ValueTerminated: + /* TO_UPSTREAM(BoundsSafety) OFF */ // Keep walking after single level desugaring. type = type.getSingleStepDesugaredType(getContext()); break; @@ -2759,12 +2810,50 @@ CodeGenFunction::SanitizerScope::~SanitizerScope() { CGF->IsSanitizerScope = false; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +CodeGenFunction::BoundsSafetyOptRemarkScope::BoundsSafetyOptRemarkScope( + CodeGenFunction *CGF, BoundsSafetyOptRemarkKind Kind) + : CGF(CGF), Kind(Kind) { + // Check for dupes + if (InScope(CGF, Kind)) { + assert(0 && "Kind already in scope"); + return; + } + CGF->BoundsSafetyOptRemarkScopes.push_back(this); +} + +CodeGenFunction::BoundsSafetyOptRemarkScope::~BoundsSafetyOptRemarkScope() { + BoundsSafetyOptRemarkScope *Current = + CGF->BoundsSafetyOptRemarkScopes.pop_back_val(); + assert(Current == this); +} + +void CodeGenFunction::BoundsSafetyOptRemarkScope::Annotate(llvm::Instruction *I) { + I->addAnnotationMetadata(GetBoundsSafetyOptRemarkString(Kind)); +} + +bool CodeGenFunction::BoundsSafetyOptRemarkScope::InScope( + const CodeGenFunction *CGF, BoundsSafetyOptRemarkKind Kind) { + for (const auto &Scope : CGF->BoundsSafetyOptRemarkScopes) { + if (Scope->GetKind() == Kind) + return true; + } + return false; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + void CodeGenFunction::InsertHelper(llvm::Instruction *I, const llvm::Twine &Name, llvm::BasicBlock::iterator InsertPt) const { LoopStack.InsertHelper(I); if (IsSanitizerScope) I->setNoSanitizeMetadata(); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + for (const auto &Scope : BoundsSafetyOptRemarkScopes) { + Scope->Annotate(I); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ } void CGBuilderInserter::InsertHelper( @@ -3217,7 +3306,7 @@ llvm::DebugLoc CodeGenFunction::SourceLocToDebugLoc(SourceLocation Location) { llvm::Value * CodeGenFunction::emitCondLikelihoodViaExpectIntrinsic(llvm::Value *Cond, - Stmt::Likelihood LH) { + Stmt::Likelihood LH) { switch (LH) { case Stmt::LH_None: return Cond; diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 4c5e8a8a44926..8e9a8d27b01f0 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -19,6 +19,8 @@ #include "CodeGenModule.h" #include "CodeGenPGO.h" #include "EHScopeStack.h" +#include "BoundsSafetyOptRemarks.h" +#include "BoundsSafetyTraps.h" #include "VarBypassDetector.h" #include "clang/AST/CharUnits.h" #include "clang/AST/CurrentSourceLocExprScope.h" @@ -115,6 +117,8 @@ enum TypeEvaluationKind { }; // clang-format on +/// TODO: Add -fbounds-safety trap kinds + #define LIST_SANITIZER_CHECKS \ SANITIZER_CHECK(AddOverflow, add_overflow, 0) \ SANITIZER_CHECK(BuiltinUnreachable, builtin_unreachable, 0) \ @@ -149,6 +153,14 @@ enum SanitizerHandler { #undef SANITIZER_CHECK }; +/* TO_UPSTREAM(BoundsSafety) ON */ +enum class WPIndex { + Pointer = 0, + Upper = 1, + Lower = 2, +}; +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// Helper class with most of the code for saving a value for a /// conditional expression cleanup. struct DominatingLLVMValue { @@ -588,6 +600,28 @@ class CodeGenFunction : public CodeGenTypeCache { ~SanitizerScope(); }; + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// RAII object that automatically adds BoundsSafety opt-remarks to instructions + /// created with `Builder` while in scope. Multiple instances of this class + /// are allowed to be in scope, but only if they each use distinct + /// opt-remarks. + class BoundsSafetyOptRemarkScope { + CodeGenFunction *CGF; + BoundsSafetyOptRemarkKind Kind; + public: + BoundsSafetyOptRemarkScope(CodeGenFunction *CGF, BoundsSafetyOptRemarkKind Kind); + BoundsSafetyOptRemarkScope(CodeGenFunction &CGF, BoundsSafetyOptRemarkKind Kind) + : BoundsSafetyOptRemarkScope(&CGF, Kind){}; + ~BoundsSafetyOptRemarkScope(); + void Annotate(llvm::Instruction *I); + BoundsSafetyOptRemarkKind GetKind() const { return Kind; } + static bool InScope(const CodeGenFunction *CGF, + BoundsSafetyOptRemarkKind Kind); + }; + + llvm::SmallVector BoundsSafetyOptRemarkScopes; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// In C++, whether we are code generating a thunk. This controls whether we /// should emit cleanups. bool CurFuncIsThunk = false; @@ -1451,6 +1485,17 @@ class CodeGenFunction : public CodeGenTypeCache { return data; } + /* TO_UPSTREAM(BoundsSafety) ON*/ + // FIXME: This can't do 'unprotectFromPeepholes'. We need another + // mechanism to handle OpaqueValueExpr inserted by -fbounds-safety. + static void unbind(CodeGenFunction &CGF, const OpaqueValueExpr *ov) { + if (shouldBindAsLValue(ov)) + CGF.OpaqueLValues.erase(ov); + else + CGF.OpaqueRValues.erase(ov); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + bool isValid() const { return OpaqueValue != nullptr; } void clear() { OpaqueValue = nullptr; } @@ -2640,6 +2685,13 @@ class CodeGenFunction : public CodeGenTypeCache { return llvm::BasicBlock::Create(getLLVMContext(), name, parent, before); } + /* TO_UPSTREM(BoundsSafety) ON*/ + llvm::BasicBlock * + createUnmergeableBasicBlock(const Twine &name = "", + llvm::Function *parent = nullptr, + llvm::BasicBlock *before = nullptr); + /* TO_UPSTREM(BoundsSafety) OFF*/ + /// getBasicBlockForLabel - Return the LLVM basicblock that the specified /// label maps to. JumpDest getJumpDestForLabel(const LabelDecl *S); @@ -2914,6 +2966,36 @@ class CodeGenFunction : public CodeGenTypeCache { AggValueSlot::IsNotAliased, AggValueSlot::DoesNotOverlap); } + /* TO_UPSTREAM(BoundsSafety) ON */ + llvm::Value *GetWidePointerElement(Address Addr, WPIndex Index); + /// Emit a raw pointer extraction from wide pointer. + llvm::Value *EmitWideToRawPtr( + const Expr *E, bool BoundsCheck = false, + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN, + bool LowerOnlyCheck = false, llvm::Value **UpperPtr = nullptr, + llvm::Value **LowerPtr = nullptr, bool IsNullAllowed = false); + + void EmitPtrCastLECheck( + llvm::Value *LHS, llvm::Value *RHS, BoundsSafetyTrapKind TrapKind, + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN); + void EmitBoundsSafetyBoundsCheck( + llvm::Type *ElemTy, llvm::Value *Ptr, llvm::Value *Upper, + llvm::Value *Lower, bool AcceptNullPt = false, + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN); + void EmitBoundsSafetyRangeCheck( + llvm::Value *LowerBound, llvm::Value *LowerAccess, + llvm::Value *UpperAccess, llvm::Value *UpperBound, + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN); + LValue EmitWidePtrArraySubscriptExpr(const ArraySubscriptExpr *E, + bool Accessed = true); + + llvm::Value *EmitTerminator(const llvm::APSInt &Terminator, llvm::Type *Ty); + void EmitValueTerminatedPointerArithmeticCheck(QualType PointerType, + llvm::Value *Ptr); + void EmitValueTerminatedAssignmentCheck(const BinaryOperator *E, + LValue Value); + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// EvaluateExprAsBool - Perform the usual unary conversions on the specified /// expression and compare the result against zero, returning an Int1Ty value. llvm::Value *EvaluateExprAsBool(const Expr *E); @@ -3348,6 +3430,21 @@ class CodeGenFunction : public CodeGenTypeCache { llvm::Value *EmitLoadOfCountedByField(const Expr *Base, const FieldDecl *FD, const FieldDecl *CountDecl); + /*TO_UPSTREAM(BoundsSafety) ON*/ + /// Emit a check for the boolean expression \p E. The emitted code checks + /// if \p E is false. If this is the case the emitted code will call the trap + /// intrisic for -fbounds-safety. + /// + /// A BoundsSafetyOptRemarkScope using the opt-remark that corresponds to \p kind + /// must be in scope when this method is called. + void EmitBoundsSafetyTrapCheck(const Expr *E, BoundsSafetyTrapKind kind); + + void EmitFlexibleArrayCountCheck(const PredefinedBoundsCheckExpr *E); + + /// Emit a trap if any of \p Conds doesn't hold \c true. + void EmitSequentialTrapCheck(llvm::ArrayRef Conds); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, bool isInc, bool isPre); ComplexPairTy EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV, @@ -4411,7 +4508,16 @@ class CodeGenFunction : public CodeGenTypeCache { Address EmitArrayToPointerDecay(const Expr *Array, LValueBaseInfo *BaseInfo = nullptr, - TBAAAccessInfo *TBAAInfo = nullptr); + TBAAAccessInfo *TBAAInfo = nullptr, + // TO_UPSTREAM(BoundsSafety) + TBAAAccessInfo *EltTBAAInfo = nullptr); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + Address EmitArrayToWidePointerDecay(const Expr *Array, llvm::Value *&Upper, + LValueBaseInfo *BaseInfo = nullptr, + TBAAAccessInfo *TBAAInfo = nullptr, + TBAAAccessInfo *EltTBAAInfo = nullptr); + /* TO_UPSTREAM(BoundsSafety) OFF*/ class ConstantEmission { llvm::PointerIntPair ValueAndIsReference; @@ -4480,6 +4586,17 @@ class CodeGenFunction : public CodeGenTypeCache { LValue EmitCXXTypeidLValue(const CXXTypeidExpr *E); LValue EmitCXXUuidofLValue(const CXXUuidofExpr *E); + /*TO_UPSTREAM(BoundsSafety) ON*/ + LValue EmitBoundsCheckExprLValue(const BoundsCheckExpr *E); + LValue + EmitPredefinedBoundsCheckExprLValue(const PredefinedBoundsCheckExpr *E); + LValue EmitMaterializeSequenceExprLValue(const MaterializeSequenceExpr *E); + LValue EmitBoundsSafetyPointerPromotionExprLValue( + const BoundsSafetyPointerPromotionExpr *E); + LValue EmitForgePtrExprLValue(const ForgePtrExpr *E); + LValue EmitAssumptionExprLValue(const AssumptionExpr *E); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + LValue EmitObjCMessageExprLValue(const ObjCMessageExpr *E); LValue EmitObjCIvarRefLValue(const ObjCIvarRefExpr *E); LValue EmitStmtExprLValue(const StmtExpr *E); @@ -5202,6 +5319,12 @@ class CodeGenFunction : public CodeGenTypeCache { /// nonnull, if \p LHS is marked _Nonnull. void EmitNullabilityCheck(LValue LHS, llvm::Value *RHS, SourceLocation Loc); + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Given an assignment `*LHS = RHS`, emit a test that checks if \p RHS is + /// nonnull, if \p LHS is marked _Nonnull. + void EmitNullabilityCheck(QualType LHSQTy, llvm::Value *RHS, SourceLocation Loc); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// An enumeration which makes it easier to specify whether or not an /// operation is a subtraction. enum { NotSubtraction = false, IsSubtraction = true }; @@ -5276,7 +5399,19 @@ class CodeGenFunction : public CodeGenTypeCache { /// Create a basic block that will call the trap intrinsic, and emit a /// conditional branch to it, for the -ftrapv checks. void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID, - bool NoMerge = false); + bool NoMerge = false, + StringRef Annotation = "", StringRef TrapMessage = ""); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Create a basic block that will call the trap intrinsic for -fbounds-safety, and + /// emit a conditional branch to it. + /// + /// A BoundsSafetyOptRemarkScope using the opt-remark that corresponds to \p kind + /// must be in scope when this method is called. + void EmitBoundsSafetyTrapCheck( + llvm::Value *Checked, BoundsSafetyTrapKind kind, + BoundsSafetyTrapCtx::Kind TrapCtx = BoundsSafetyTrapCtx::UNKNOWN); + /* TO_UPSTREAM(BoundsSafety) OFF*/ /// Emit a call to trap or debugtrap and attach function attribute /// "trap-func-name" if specified. @@ -5450,9 +5585,17 @@ class CodeGenFunction : public CodeGenTypeCache { /// into the address of a local variable. In such a case, it's quite /// reasonable to just ignore the returned alignment when it isn't from an /// explicit source. + /// + /// TO_UPSTREAM(BoundsSafety) + /// BoundsSafety: Extract bounds information when loading a pointer from + /// wide pointer. Address EmitPointerWithAlignment(const Expr *Addr, LValueBaseInfo *BaseInfo = nullptr, TBAAAccessInfo *TBAAInfo = nullptr, + /* TO_UPSTREAM(BoundsSafety) ON*/ + llvm::Value **UpperPtr = nullptr, + llvm::Value **LowerPtr = nullptr, + /* TO_UPSTREAM(BoundsSafety) OFF*/ KnownNonNull_t IsKnownNonNull = NotKnownNonNull); /// If \p E references a parameter with pass_object_size info or a constant @@ -5508,6 +5651,23 @@ class CodeGenFunction : public CodeGenTypeCache { llvm::Value *EmitAArch64CpuSupports(ArrayRef FeatureStrs); }; +/* TO_UPSTREAM(BoundsSafety) ON*/ +class RAIIDisableUBSanChecks { +public: + explicit RAIIDisableUBSanChecks(CodeGenFunction &CGF) + : CGF(CGF), OldSanOpts(CGF.SanOpts) { + CGF.SanOpts.clear(SanitizerKind::Undefined | + SanitizerKind::Integer | + SanitizerKind::ImplicitConversion); + } + ~RAIIDisableUBSanChecks() { CGF.SanOpts = OldSanOpts; } + +private: + CodeGenFunction &CGF; + SanitizerSet OldSanOpts; +}; +/* TO_UPSTREAM(BoundsSafety) OFF*/ + inline DominatingLLVMValue::saved_type DominatingLLVMValue::save(CodeGenFunction &CGF, llvm::Value *value) { if (!needsSaving(value)) diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index f0ce4625c1921..c20fe7c976184 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -335,12 +335,14 @@ CodeGenModule::CodeGenModule(ASTContext &C, IntrusiveRefCntPtr FS, const HeaderSearchOptions &HSO, const PreprocessorOptions &PPO, - const CodeGenOptions &CGO, llvm::Module &M, + const CodeGenOptions &CGO, + const TargetInfo &CGTI, + llvm::Module &M, DiagnosticsEngine &diags, CoverageSourceInfo *CoverageInfo) : Context(C), LangOpts(C.getLangOpts()), FS(FS), HeaderSearchOpts(HSO), PreprocessorOpts(PPO), CodeGenOpts(CGO), TheModule(M), Diags(diags), - Target(C.getTargetInfo()), ABI(createCXXABI(*this)), + Target(CGTI), ABI(createCXXABI(*this)), VMContext(M.getContext()), VTables(*this), StackHandler(diags), SanitizerMD(new SanitizerMetadata(*this)), AtomicOpts(Target.getAtomicOpts()) { @@ -357,19 +359,19 @@ CodeGenModule::CodeGenModule(ASTContext &C, BFloatTy = llvm::Type::getBFloatTy(LLVMContext); FloatTy = llvm::Type::getFloatTy(LLVMContext); DoubleTy = llvm::Type::getDoubleTy(LLVMContext); - PointerWidthInBits = C.getTargetInfo().getPointerWidth(LangAS::Default); + PointerWidthInBits = Target.getPointerWidth(LangAS::Default); PointerAlignInBytes = - C.toCharUnitsFromBits(C.getTargetInfo().getPointerAlign(LangAS::Default)) + C.toCharUnitsFromBits(Target.getPointerAlign(LangAS::Default)) .getQuantity(); SizeSizeInBytes = - C.toCharUnitsFromBits(C.getTargetInfo().getMaxPointerWidth()).getQuantity(); + C.toCharUnitsFromBits(Target.getMaxPointerWidth()).getQuantity(); IntAlignInBytes = - C.toCharUnitsFromBits(C.getTargetInfo().getIntAlign()).getQuantity(); + C.toCharUnitsFromBits(Target.getIntAlign()).getQuantity(); CharTy = - llvm::IntegerType::get(LLVMContext, C.getTargetInfo().getCharWidth()); - IntTy = llvm::IntegerType::get(LLVMContext, C.getTargetInfo().getIntWidth()); + llvm::IntegerType::get(LLVMContext, Target.getCharWidth()); + IntTy = llvm::IntegerType::get(LLVMContext, Target.getIntWidth()); IntPtrTy = llvm::IntegerType::get(LLVMContext, - C.getTargetInfo().getMaxPointerWidth()); + Target.getMaxPointerWidth()); Int8PtrTy = llvm::PointerType::get(LLVMContext, C.getTargetAddressSpace(LangAS::Default)); const llvm::DataLayout &DL = M.getDataLayout(); @@ -454,7 +456,9 @@ CodeGenModule::CodeGenModule(ASTContext &C, CodeGenOpts.NumRegisterParameters); } -CodeGenModule::~CodeGenModule() {} +CodeGenModule::~CodeGenModule() { + destroyConstantSignedPointerCaches(); +} void CodeGenModule::createObjCRuntime() { // This is just isGNUFamily(), but we want to force implementors of @@ -1339,6 +1343,11 @@ void CodeGenModule::Release() { if (LangOpts.HLSL) getHLSLRuntime().finishCodeGen(); + if (LangOpts.PointerAuthABIVersionEncoded) + TheModule.setPtrAuthABIVersion( + {static_cast(LangOpts.PointerAuthABIVersion), + static_cast(LangOpts.PointerAuthKernelABIVersion)}); + if (uint32_t PLevel = Context.getLangOpts().PICLevel) { assert(PLevel < 3 && "Invalid PIC Level"); getModule().setPICLevel(static_cast(PLevel)); @@ -5374,6 +5383,9 @@ void CodeGenModule::EmitTentativeDefinition(const VarDecl *D) { return; } + if (Context.hasUnavailableFeature(D)) + return; + // The tentative definition is the only definition. EmitGlobalVarDefinition(D); } @@ -6891,6 +6903,9 @@ ConstantAddress CodeGenModule::GetAddrOfGlobalTemporary( void CodeGenModule::EmitObjCPropertyImplementations(const ObjCImplementationDecl *D) { for (const auto *PID : D->property_impls()) { + if (Context.hasUnavailableFeature(PID->getPropertyDecl())) + continue; + // Dynamic is just for type-checking. if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize) { ObjCPropertyDecl *PD = PID->getPropertyDecl(); @@ -6915,9 +6930,12 @@ void CodeGenModule::EmitObjCPropertyImplementations(const static bool needsDestructMethod(ObjCImplementationDecl *impl) { const ObjCInterfaceDecl *iface = impl->getClassInterface(); for (const ObjCIvarDecl *ivar = iface->all_declared_ivar_begin(); - ivar; ivar = ivar->getNextIvar()) + ivar; ivar = ivar->getNextIvar()) { + if (impl->getASTContext().hasUnavailableFeature(ivar)) + continue; if (ivar->getType().isDestructedType()) return true; + } return false; } @@ -7049,6 +7067,9 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { if (auto *FD = dyn_cast(D); FD && FD->isImmediateFunction()) return; + if (Context.hasUnavailableFeature(D)) + return; + switch (D->getKind()) { case Decl::CXXConversion: case Decl::CXXMethod: @@ -7179,6 +7200,8 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { } case Decl::ObjCMethod: { auto *OMD = cast(D); + if (Context.hasUnavailableFeature(OMD->getClassInterface())) + break; // If this is not a prototype, emit the body. if (OMD->getBody()) CodeGenFunction(*this).GenerateObjCMethod(OMD); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 59f400570fb7a..fd68671ea8aed 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -483,6 +483,11 @@ class CodeGenModule : public CodeGenTypeCache { // most up to date ValueDecl that will have all the inherited annotations. llvm::MapVector DeferredAnnotations; + /// Signed constant pointers. + void *ConstantSignedPointersByDecl = nullptr; + void *SignedThunkPointers = nullptr; + void *ConstantSignedPointersByConstant = nullptr; + /// Map used to get unique annotation strings. llvm::StringMap AnnotationStrings; @@ -676,13 +681,18 @@ class CodeGenModule : public CodeGenTypeCache { std::optional computeVTPointerAuthentication(const CXXRecordDecl *ThisClass); + // TO_UPSTREAM(BoundsSafety) + uint64_t UniqueTrapCount = 0; + AtomicOptions AtomicOpts; public: CodeGenModule(ASTContext &C, IntrusiveRefCntPtr FS, const HeaderSearchOptions &headersearchopts, const PreprocessorOptions &ppopts, - const CodeGenOptions &CodeGenOpts, llvm::Module &M, + const CodeGenOptions &CodeGenOpts, + const TargetInfo &CodeGenTargetInfo, + llvm::Module &M, DiagnosticsEngine &Diags, CoverageSourceInfo *CoverageInfo = nullptr); @@ -1026,15 +1036,17 @@ class CodeGenModule : public CodeGenTypeCache { /// Return the ABI-correct function pointer value for a reference /// to the given function. This will apply a pointer signature if - /// necessary. + /// necessary, but will only cache the result if \p GD is passed. llvm::Constant *getFunctionPointer(llvm::Constant *Pointer, - QualType FunctionType); + QualType FunctionType, + GlobalDecl GD = GlobalDecl()); llvm::Constant *getMemberFunctionPointer(const FunctionDecl *FD, llvm::Type *Ty = nullptr); llvm::Constant *getMemberFunctionPointer(llvm::Constant *Pointer, - QualType FT); + QualType FT, + const FunctionDecl *FD); CGPointerAuthInfo getFunctionPointerAuthInfo(QualType T); @@ -1141,6 +1153,11 @@ class CodeGenModule : public CodeGenTypeCache { /// Fetches the global unique block count. int getUniqueBlockCount() { return ++Block.GlobalUniqueCount; } + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Fetches and increments unique trap count + uint64_t getAndIncrementUniqueTrapCount() { return UniqueTrapCount++; } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Fetches the type of a generic block descriptor. llvm::Type *getBlockDescriptorType(); @@ -1989,6 +2006,8 @@ class CodeGenModule : public CodeGenTypeCache { /// function. void SimplifyPersonality(); + void destroyConstantSignedPointerCaches(); + /// Helper function for getDefaultFunctionAttributes. Builds a set of function /// attributes which can be simply added to a function. void getTrivialDefaultFunctionAttributes(StringRef Name, bool HasOptnone, diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp index d1b292f23c2d2..5a11bdfd48bea 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -618,7 +618,12 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { const PointerType *PTy = cast(Ty); QualType ETy = PTy->getPointeeType(); unsigned AS = getTargetAddressSpace(ETy); - ResultType = llvm::PointerType::get(getLLVMContext(), AS); + if (PTy->hasRawPointerLayout()) + ResultType = llvm::PointerType::get(getLLVMContext(), AS); + /* TO_UPSTREAM(BoundsSafety) ON */ + else + ResultType = ConvertBoundsSafetyPointerType(PTy); + /* TO_UPSTREAM(BoundsSafety) OFF */ break; } @@ -788,6 +793,35 @@ bool CodeGenModule::isPaddedAtomicType(const AtomicType *type) { return Context.getTypeSize(type) != Context.getTypeSize(type->getValueType()); } +/* TO_UPSTREAM(BoundsSafety) ON */ +llvm::Type *CodeGenTypes::ConvertBoundsSafetyPointerType(const PointerType *PT) { + assert(!PT->hasRawPointerLayout()); + llvm::StructType *&Entry = WidePointerTypes[PT]; + if (Entry) + return Entry; + + SmallString<256> TypeName; + llvm::raw_svector_ostream OS(TypeName); + OS << "__bounds_safety::wide_ptr."; + if (PT->isBidiIndexable()) + OS << "bidi_indexable"; + else if (PT->isIndexable()) + OS << "indexable"; + + QualType RawQTy = Context.getPointerType(PT->getPointeeType()); + llvm::Type *RawTy = ConvertType(RawQTy); + llvm::SmallVector Elems; + Elems.push_back(RawTy); + if (PT->getPointerAttributes().hasUpperBound()) + Elems.push_back(RawTy); + if (PT->getPointerAttributes().hasLowerBound()) + Elems.push_back(RawTy); + + Entry = llvm::StructType::create(getLLVMContext(), Elems, OS.str()); + return Entry; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// ConvertRecordDeclType - Lay out a tagged decl type like struct or union. llvm::StructType *CodeGenTypes::ConvertRecordDeclType(const RecordDecl *RD) { // TagDecl's are not necessarily unique, instead use the (clang) diff --git a/clang/lib/CodeGen/CodeGenTypes.h b/clang/lib/CodeGen/CodeGenTypes.h index 29f6f1ec80bc3..2b9b68ec8c5de 100644 --- a/clang/lib/CodeGen/CodeGenTypes.h +++ b/clang/lib/CodeGen/CodeGenTypes.h @@ -70,6 +70,11 @@ class CodeGenTypes { /// Contains the LLVM IR type for any converted RecordDecl. llvm::DenseMap RecordDeclTypes; + /* TO_UPSTREAM(BoundsSafety) ON */ + /// Contains the LLVM IR type for any converted wide pointer type. + llvm::DenseMap WidePointerTypes; + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Hold memoized CGFunctionInfo results. llvm::FoldingSet FunctionInfos{FunctionInfosLog2InitSize}; @@ -145,6 +150,10 @@ class CodeGenTypes { /// load/store type are the same. llvm::Type *convertTypeForLoadStore(QualType T, llvm::Type *LLVMTy = nullptr); + /* TO_UPSTREAM(BoundsSafety) ON */ + llvm::Type *ConvertBoundsSafetyPointerType(const PointerType *PT); + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// GetFunctionType - Get the LLVM function type for \arg Info. llvm::FunctionType *GetFunctionType(const CGFunctionInfo &Info); diff --git a/clang/lib/CodeGen/ConstantInitBuilder.cpp b/clang/lib/CodeGen/ConstantInitBuilder.cpp index ce1fe137c1919..340a8bef30d8f 100644 --- a/clang/lib/CodeGen/ConstantInitBuilder.cpp +++ b/clang/lib/CodeGen/ConstantInitBuilder.cpp @@ -314,3 +314,19 @@ void ConstantAggregateBuilderBase::addSignedPointer( Pointer, Schema, StorageAddress, CalleeDecl, CalleeType); add(SignedPointer); } + +/// Sign the given pointer and add it to the constant initializer +/// currently being built. +void ConstantAggregateBuilderBase::addSignedPointer( + llvm::Constant *pointer, unsigned key, + bool useAddressDiscrimination, llvm::ConstantInt *otherDiscriminator) { + llvm::Constant *storageAddress = nullptr; + if (useAddressDiscrimination) { + storageAddress = getAddrOfCurrentPosition(pointer->getType()); + } + + llvm::Constant *signedPointer = + Builder.CGM.getConstantSignedPointer(pointer, key, storageAddress, + otherDiscriminator); + add(signedPointer); +} diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp index 73811d15979d5..a37a698149f19 100644 --- a/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -2383,6 +2383,31 @@ struct CounterCoverageMappingBuilder if (OVE->isUnique()) Visit(OVE->getSourceExpr()); } + + /* TO_UPSTREAM(BoundsSafety) ON */ + // In most expression types, only visit the main expressions, as the others + // weren't spelled out and can have confusing source locations. + void VisitAssumptionExpr(const AssumptionExpr *AE) { + Visit(AE->getWrappedExpr()); + } + + void VisitBoundsSafetyPointerPromotionExpr( + const BoundsSafetyPointerPromotionExpr *FPPE) { + Visit(FPPE->getPointer()); + } + + void VisitBoundsCheckExpr(const BoundsCheckExpr *BCE) { + Visit(BCE->getGuardedExpr()); + } + + void VisitPredefinedBoundsCheckExpr(const PredefinedBoundsCheckExpr *PBCE) { + Visit(PBCE->getGuardedExpr()); + } + + void VisitMaterializeSequenceExpr(const MaterializeSequenceExpr *MSE) { + Visit(MSE->getWrappedExpr()); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ }; } // end anonymous namespace diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 70b53be7e77a3..9c7337ec64272 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -31,6 +31,7 @@ #include "clang/AST/Type.h" #include "clang/CodeGen/ConstantInitBuilder.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/GlobalPtrAuthInfo.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" @@ -878,6 +879,23 @@ llvm::Value *ItaniumCXXABI::EmitMemberDataPointerAddress( static llvm::Constant *pointerAuthResignConstant( llvm::Value *Ptr, const CGPointerAuthInfo &CurAuthInfo, const CGPointerAuthInfo &NewAuthInfo, CodeGenModule &CGM) { + std::optional Info = + llvm::GlobalPtrAuthInfo::analyze(Ptr); + + if (Info) { + if (!isa(NewAuthInfo.getDiscriminator())) + return nullptr; + + assert(Info->getKey()->getZExtValue() == CurAuthInfo.getKey() && + Info->getAddrDiscriminator()->isZeroValue() && + Info->getDiscriminator() == CurAuthInfo.getDiscriminator() && + "unexpected key or discriminators"); + + return CGM.getConstantSignedPointer( + Info->getPointer(), NewAuthInfo.getKey(), nullptr, + cast(NewAuthInfo.getDiscriminator())); + } + const auto *CPA = dyn_cast(Ptr); if (!CPA) @@ -5138,7 +5156,7 @@ ItaniumCXXABI::getSignedVirtualMemberFunctionPointer(const CXXMethodDecl *MD) { llvm::Constant *thunk = getOrCreateVirtualFunctionPointerThunk(origMD); QualType funcType = CGM.getContext().getMemberPointerType( MD->getType(), /*Qualifier=*/nullptr, MD->getParent()); - return CGM.getMemberFunctionPointer(thunk, funcType); + return CGM.getMemberFunctionPointer(thunk, funcType, MD); } void WebAssemblyCXXABI::emitBeginCatch(CodeGenFunction &CGF, diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index aa9a55ae05927..9a6d7c48b577e 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -1992,7 +1992,7 @@ CGCallee MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, VFunc = Builder.CreateAlignedLoad(Ty, VFuncPtr, CGF.getPointerAlign()); } - CGCallee Callee(GD, VFunc); + CGCallee Callee(GD, VFunc, /*unsigned*/ CGPointerAuthInfo()); return Callee; } @@ -3550,7 +3550,7 @@ CGCallee MicrosoftCXXABI::EmitLoadOfMemberFunctionPointer( ThisPtrForCall = Builder.CreateInBoundsGEP(CGF.Int8Ty, ThisPtrForCall, NonVirtualBaseAdjustment); - CGCallee Callee(FPT, FunctionPointer); + CGCallee Callee(FPT, FunctionPointer, /*unsigned*/ CGPointerAuthInfo()); return Callee; } diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp index 09a7d79ae4afb..e283777d1c092 100644 --- a/clang/lib/CodeGen/ModuleBuilder.cpp +++ b/clang/lib/CodeGen/ModuleBuilder.cpp @@ -149,21 +149,26 @@ namespace { } void Initialize(ASTContext &Context) override { + Initialize(Context, Context.getTargetInfo()); + } + + void Initialize(ASTContext &Context, const TargetInfo &CodeGenTargetInfo) override { Ctx = &Context; - M->setTargetTriple(Ctx->getTargetInfo().getTriple()); - M->setDataLayout(Ctx->getTargetInfo().getDataLayoutString()); - const auto &SDKVersion = Ctx->getTargetInfo().getSDKVersion(); + M->setTargetTriple(CodeGenTargetInfo.getTriple()); + M->setDataLayout(CodeGenTargetInfo.getDataLayoutString()); + const auto &SDKVersion = CodeGenTargetInfo.getSDKVersion(); if (!SDKVersion.empty()) M->setSDKVersion(SDKVersion); - if (const auto *TVT = Ctx->getTargetInfo().getDarwinTargetVariantTriple()) + if (const auto *TVT = CodeGenTargetInfo.getDarwinTargetVariantTriple()) M->setDarwinTargetVariantTriple(TVT->getTriple()); if (auto TVSDKVersion = - Ctx->getTargetInfo().getDarwinTargetVariantSDKVersion()) + CodeGenTargetInfo.getDarwinTargetVariantSDKVersion()) M->setDarwinTargetVariantSDKVersion(*TVSDKVersion); Builder.reset(new CodeGen::CodeGenModule(Context, FS, HeaderSearchOpts, PreprocessorOpts, CodeGenOpts, - *M, Diags, CoverageInfo)); + CodeGenTargetInfo, *M, + Diags, CoverageInfo)); for (auto &&Lib : CodeGenOpts.DependentLibraries) Builder->AddDependentLib(Lib); diff --git a/clang/lib/CodeGen/ObjectFilePCHContainerWriter.cpp b/clang/lib/CodeGen/ObjectFilePCHContainerWriter.cpp index 95971e57086e7..8b54552623872 100644 --- a/clang/lib/CodeGen/ObjectFilePCHContainerWriter.cpp +++ b/clang/lib/CodeGen/ObjectFilePCHContainerWriter.cpp @@ -49,6 +49,7 @@ class PCHContainerGenerator : public ASTConsumer { CodeGenOptions CodeGenOpts; const TargetOptions TargetOpts; LangOptions LangOpts; + const CASOptions &CASOpts; // MCCAS std::unique_ptr VMContext; std::unique_ptr M; std::unique_ptr Builder; @@ -150,6 +151,7 @@ class PCHContainerGenerator : public ASTConsumer { HeaderSearchOpts(CI.getHeaderSearchOpts()), PreprocessorOpts(CI.getPreprocessorOpts()), TargetOpts(CI.getTargetOpts()), LangOpts(CI.getLangOpts()), + CASOpts(CI.getCASOpts()), // MCCAS OS(std::move(OS)), Buffer(std::move(Buffer)) { // The debug info output isn't affected by CodeModel and // ThreadModel, but the backend expects them to be nonempty. @@ -172,6 +174,10 @@ class PCHContainerGenerator : public ASTConsumer { ~PCHContainerGenerator() override = default; void Initialize(ASTContext &Context) override { + Initialize(Context, Context.getTargetInfo()); + } + + void Initialize(ASTContext &Context, const TargetInfo &CodeGenTargetInfo) override { assert(!Ctx && "initialized multiple times"); Ctx = &Context; @@ -179,7 +185,8 @@ class PCHContainerGenerator : public ASTConsumer { M.reset(new llvm::Module(MainFileName, *VMContext)); M->setDataLayout(Ctx->getTargetInfo().getDataLayoutString()); Builder.reset(new CodeGen::CodeGenModule( - *Ctx, FS, HeaderSearchOpts, PreprocessorOpts, CodeGenOpts, *M, Diags)); + *Ctx, FS, HeaderSearchOpts, PreprocessorOpts, CodeGenOpts, + CodeGenTargetInfo, *M, Diags)); // Prepare CGDebugInfo to emit debug info for a clang module. auto *DI = Builder->getModuleDebugInfo(); @@ -322,14 +329,15 @@ class PCHContainerGenerator : public ASTConsumer { // Print the IR for the PCH container to the debug output. llvm::SmallString<0> Buffer; clang::emitBackendOutput( - CI, CodeGenOpts, Ctx.getTargetInfo().getDataLayoutString(), M.get(), + CI, CodeGenOpts, CASOpts /* MCCAS */, + Ctx.getTargetInfo().getDataLayoutString(), M.get(), BackendAction::Backend_EmitLL, FS, std::make_unique(Buffer)); llvm::dbgs() << Buffer; }); // Use the LLVM backend to emit the pch container. - clang::emitBackendOutput(CI, CodeGenOpts, + clang::emitBackendOutput(CI, CodeGenOpts, CASOpts, // MCCAS Ctx.getTargetInfo().getDataLayoutString(), M.get(), BackendAction::Backend_EmitObj, FS, std::move(OS)); diff --git a/clang/lib/CodeGen/SwiftCallingConv.cpp b/clang/lib/CodeGen/SwiftCallingConv.cpp index 10f9f20bca313..ba092e35d9b36 100644 --- a/clang/lib/CodeGen/SwiftCallingConv.cpp +++ b/clang/lib/CodeGen/SwiftCallingConv.cpp @@ -643,6 +643,27 @@ bool SwiftAggLowering::shouldPassIndirectly(bool asReturnValue) const { return getSwiftABIInfo(CGM).shouldPassIndirectly(componentTys, asReturnValue); } +bool SwiftAggLowering::shouldReturnTypedErrorIndirectly() const { + assert(Finished && "haven't yet finished lowering"); + + // Empty types don't need to be passed indirectly. + if (Entries.empty()) + return false; + + // Avoid copying the array of types when there's just a single element. + if (Entries.size() == 1) { + return getSwiftABIInfo(CGM).shouldReturnTypedErrorIndirectly( + Entries.back().Type); + } + + SmallVector componentTys; + componentTys.reserve(Entries.size()); + for (auto &entry : Entries) { + componentTys.push_back(entry.Type); + } + return getSwiftABIInfo(CGM).shouldReturnTypedErrorIndirectly(componentTys); +} + bool swiftcall::shouldPassIndirectly(CodeGenModule &CGM, ArrayRef componentTys, bool asReturnValue) { diff --git a/clang/lib/CodeGen/Targets/AVR.cpp b/clang/lib/CodeGen/Targets/AVR.cpp index 5399d12f7ce80..e632b4de9532d 100644 --- a/clang/lib/CodeGen/Targets/AVR.cpp +++ b/clang/lib/CodeGen/Targets/AVR.cpp @@ -112,7 +112,10 @@ class AVRABIInfo : public DefaultABIInfo { class AVRTargetCodeGenInfo : public TargetCodeGenInfo { public: AVRTargetCodeGenInfo(CodeGenTypes &CGT, unsigned NPR, unsigned NRR) - : TargetCodeGenInfo(std::make_unique(CGT, NPR, NRR)) {} + : TargetCodeGenInfo(std::make_unique(CGT, NPR, NRR)) { + SwiftInfo = + std::make_unique(CGT, /*SwiftErrorInRegister=*/false); + } LangAS getGlobalVarAddressSpace(CodeGenModule &CGM, const VarDecl *D) const override { diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp index b36a6e1396653..f51758778ffc9 100644 --- a/clang/lib/CodeGen/Targets/X86.cpp +++ b/clang/lib/CodeGen/Targets/X86.cpp @@ -342,9 +342,10 @@ bool X86_32ABIInfo::shouldReturnTypeInRegister(QualType Ty, // If this is a builtin, pointer, enum, complex type, member pointer, or // member function pointer it is ok. - if (Ty->getAs() || Ty->hasPointerRepresentation() || - Ty->isAnyComplexType() || Ty->isEnumeralType() || - Ty->isBlockPointerType() || Ty->isMemberPointerType()) + if ((Ty->getAs() || Ty->hasPointerRepresentation() || + Ty->isAnyComplexType() || Ty->isEnumeralType() || + Ty->isBlockPointerType() || Ty->isMemberPointerType()) && + !Ty->isPointerTypeWithBounds()) return true; // Arrays are treated like records. @@ -1852,6 +1853,28 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo, return; } + // BoundsSafety wide pointers. + if (Ty->isPointerTypeWithBounds()) { + uint64_t Size = getContext().getTypeSize(Ty); + // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger + // than eight eightbytes, ..., it has class MEMORY. + assert(Size <= 512 && "Wide pointers won't be larger than 512 bytes."); + + // This is a summarized version of the RecordType routine such that the + // result be equivalent to having RecordDecl for a wide pointer. + // In effect, '__indexable' which only has an upper bound will be passed + // in register, while '__bidi_indexable' which has both upper and lower + // bounds will be passed in memory. + if (Size > 128) { + Lo = Memory; + } else { + Lo = Integer; + Hi = Integer; + } + postMerge(Size, Lo, Hi); + return; + } + if (Ty->hasPointerRepresentation()) { Current = Integer; return; diff --git a/clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp b/clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp index b8788bae8171c..75e4ede060ec4 100644 --- a/clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp +++ b/clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp @@ -149,7 +149,6 @@ static void eventStreamCallback(ConstFSEventStreamRef Stream, // default Events.emplace_back(DirectoryWatcher::Event{ DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""}); - llvm_unreachable("Unknown FSEvent type."); } if (!Events.empty()) { diff --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp index ec09726044812..a66414a73fd59 100644 --- a/clang/lib/Driver/Action.cpp +++ b/clang/lib/Driver/Action.cpp @@ -23,6 +23,7 @@ const char *Action::getClassName(ActionClass AC) { case BindArchClass: return "bind-arch"; case OffloadClass: return "offload"; + case DepscanJobClass: return "depscan"; case PreprocessJobClass: return "preprocessor"; case PrecompileJobClass: return "precompiler"; case ExtractAPIJobClass: @@ -349,6 +350,11 @@ JobAction::JobAction(ActionClass Kind, Action *Input, types::ID Type) JobAction::JobAction(ActionClass Kind, const ActionList &Inputs, types::ID Type) : Action(Kind, Inputs, Type) {} +void DepscanJobAction::anchor() {} + +DepscanJobAction::DepscanJobAction(Action *Input, types::ID OutputType) + : JobAction(DepscanJobClass, Input, OutputType), JA(this) {} + void PreprocessJobAction::anchor() {} PreprocessJobAction::PreprocessJobAction(Action *Input, types::ID OutputType) diff --git a/clang/lib/Driver/BoundsSafetyArgs.cpp b/clang/lib/Driver/BoundsSafetyArgs.cpp new file mode 100644 index 0000000000000..6aa72ed118f63 --- /dev/null +++ b/clang/lib/Driver/BoundsSafetyArgs.cpp @@ -0,0 +1,101 @@ +//===- BoundsSafetyArgs.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "clang/Driver/BoundsSafetyArgs.h" +#include "clang/Basic/DiagnosticDriver.h" +#include "clang/Driver/Options.h" + +using namespace llvm::opt; +using namespace clang::driver::options; + +namespace clang { + +namespace driver { + +LangOptions::BoundsSafetyNewChecksMaskIntTy +ParseBoundsSafetyNewChecksMaskFromArgs(const llvm::opt::ArgList &Args, + DiagnosticsEngine *Diags) { + auto FilteredArgs = + Args.filtered(OPT_fbounds_safety_bringup_missing_checks_EQ, + OPT_fno_bounds_safety_bringup_missing_checks_EQ); + if (FilteredArgs.empty()) { + // No flags present. Use the default + return LangOptions::getDefaultBoundsSafetyNewChecksMask(); + } + + // If flags are present then start with BS_CHK_None as the initial mask and + // update the mask based on the flags. This preserves compiler behavior for + // users that adopted the `-fbounds-safety-bringup-missing-checks` flag when + // `getDefaultBoundsSafetyNewChecksMask() == BS_CHK_None`. + LangOptions::BoundsSafetyNewChecksMaskIntTy Result = LangOptions::BS_CHK_None; + // All the options will be applied as masks in the command line order, to make + // it easier to enable all but certain checks (or disable all but certain + // checks). + for (const Arg *A : FilteredArgs) { + for (const char *Value : A->getValues()) { + std::optional Mask = + llvm::StringSwitch< + std::optional>(Value) + .Case("access_size", LangOptions::BS_CHK_AccessSize) + .Case("indirect_count_update", + LangOptions::BS_CHK_IndirectCountUpdate) + .Case("return_size", LangOptions::BS_CHK_ReturnSize) + .Case("ended_by_lower_bound", + LangOptions::BS_CHK_EndedByLowerBound) + .Case("compound_literal_init", + LangOptions::BS_CHK_CompoundLiteralInit) + .Case("libc_attributes", LangOptions::BS_CHK_LibCAttributes) + .Case("array_subscript_agg", + LangOptions::BS_CHK_ArraySubscriptAgg) + .Case("all", + LangOptions::getBoundsSafetyNewChecksMaskForGroup("all")) + .Case( + "batch_0", + LangOptions::getBoundsSafetyNewChecksMaskForGroup("batch_0")) + .Case("none", LangOptions::BS_CHK_None) + .Default(std::nullopt); + if (!Mask) { + if (Diags) + Diags->Report(diag::err_drv_invalid_value) + << A->getSpelling() << Value; + break; + } + + bool IsPosFlag = + A->getOption().matches(OPT_fbounds_safety_bringup_missing_checks_EQ) + ? true + : false; + + // `-fbounds-safety-bringup-missing-checks=none` would do nothing as the + // masks are additive, which is unlikely to be intended. To disable all + // checks, `-fno-bounds-safety-bringup-missing-checks=all` should be used + // instead. Hence, "none" is not supported, triggering an error with the + // suggestion. + if (*Mask == LangOptions::BS_CHK_None) { + if (Diags) + Diags->Report(diag::err_drv_invalid_value_with_flag_suggestion) + << A->getSpelling() << Value + << (IsPosFlag ? "-fno-bounds-safety-bringup-missing-checks" + : "-fbounds-safety-bringup-missing-checks"); + break; + } + + if (IsPosFlag) { + Result |= *Mask; + } else { + assert(A->getOption().matches( + OPT_fno_bounds_safety_bringup_missing_checks_EQ)); + Result &= ~(*Mask); + } + } + } + return Result; +} + +} // namespace driver + +} // namespace clang diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt index 9dbededf1ade9..27f03aa789a7e 100644 --- a/clang/lib/Driver/CMakeLists.txt +++ b/clang/lib/Driver/CMakeLists.txt @@ -1,5 +1,6 @@ set(LLVM_LINK_COMPONENTS BinaryFormat + CAS MC Object Option @@ -16,6 +17,7 @@ endif() add_clang_library(clangDriver Action.cpp + BoundsSafetyArgs.cpp Compilation.cpp Distro.cpp Driver.cpp @@ -98,5 +100,6 @@ add_clang_library(clangDriver LINK_LIBS clangBasic + clangCAS ${system_libs} ) diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index a648cc928afdc..a50aae0db28c8 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -2129,7 +2129,9 @@ void Driver::generateCompilationDiagnostics( C.addTempFile(TempFile); // Assume associated files are based off of the first temporary file. - CrashReportInfo CrashInfo(TempFiles[0], VFS); + CrashReportInfo CrashInfo( + TempFiles[0], VFS, + C.getArgs().getLastArgValue(options::OPT_index_store_path)); llvm::SmallString<128> Script(CrashInfo.Filename); llvm::sys::path::replace_extension(Script, "sh"); @@ -4451,6 +4453,14 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, if (OffloadBuilder->addHostDependenceToDeviceActions(Current, InputArg)) break; + // FIXME: To avoid globally adding the depscan phase to all the input types, + // we try to inject depscan phase here if the first phase is preprocess and + // -fdepscan is used. + if (PL.front() == phases::Preprocess) + if (Arg *A = Args.getLastArg(options::OPT_fdepscan_EQ)) + if (A->getValue() != llvm::StringLiteral("off")) + PL.insert(PL.begin(), phases::Depscan); + for (phases::ID Phase : PL) { // Add any offload action the host action depends on. @@ -5083,6 +5093,8 @@ Action *Driver::ConstructPhaseAction( llvm_unreachable("link action invalid here."); case phases::IfsMerge: llvm_unreachable("ifsmerge action invalid here."); + case phases::Depscan: + return C.MakeAction(Input, types::TY_ResponseFile); case phases::Preprocess: { types::ID OutputTy; // -M and -MM specify the dependency file name by altering the output type, @@ -5091,7 +5103,12 @@ Action *Driver::ConstructPhaseAction( !Args.hasArg(options::OPT_MD, options::OPT_MMD)) { OutputTy = types::TY_Dependencies; } else { - OutputTy = Input->getType(); + // If the previous is action is depscan, bypass the output kind from + // depscan. + if (Input->getKind() == AssembleJobAction::DepscanJobClass) + OutputTy = Input->getInputs().front()->getType(); + else + OutputTy = Input->getType(); // For these cases, the preprocessor is only translating forms, the Output // still needs preprocessing. if (!Args.hasFlag(options::OPT_frewrite_includes, @@ -5299,11 +5316,20 @@ void Driver::BuildJobs(Compilation &C) const { /*TargetDeviceOffloadKind*/ Action::OFK_None); } + // DepScan introduces a new DepScan binding that cannot be merged so it will + // end up having 2 jobs instead of 1 but we still should run the command + // in-process if possible. + bool ShouldInProcessDepScan = + C.getJobs().size() == 2 && + isa(C.getJobs().begin()->getSource()); + // If we have more than one job, then disable integrated-cc1 for now. Do this // also when we need to report process execution statistics. - if (C.getJobs().size() > 1 || CCPrintProcessStats) + if ((C.getJobs().size() > 1 && !ShouldInProcessDepScan) || + CCPrintProcessStats) { for (auto &J : C.getJobs()) J.InProcess = false; + } if (CCPrintProcessStats) { C.setPostCallback([=](const Command &Cmd, int Res) { @@ -5674,6 +5700,14 @@ class ToolSelector final { Inputs = NewInputs; } + void combineWithDepscan(const Tool *T, const JobAction *Current, + ActionList &Inputs) { + for (Action *A : Inputs) { + if (auto *DepScan = dyn_cast(A)) + DepScan->setScanningJobAction(Current); + } + } + public: ToolSelector(const JobAction *BaseAction, const ToolChain &TC, const Compilation &C, bool SaveTemps, bool EmbedBitcode) @@ -5730,6 +5764,8 @@ class ToolSelector final { } combineWithPreprocessor(T, Inputs, CollapsedOffloadAction); + + combineWithDepscan(T, BaseAction, Inputs); return T; } }; @@ -7187,6 +7223,10 @@ llvm::Error driver::expandResponseFiles(SmallVectorImpl &Args, return llvm::Error::success(); } +bool driver::isClangCache(StringRef DriverMode) { + return DriverMode == "cache"; +} + static const char *GetStableCStr(llvm::StringSet<> &SavedStrings, StringRef S) { return SavedStrings.insert(S).first->getKeyData(); } diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index 4619b8c1887be..61f2185f95c4c 100644 --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -77,6 +77,8 @@ static bool skipArgs(const char *Flag, bool HaveCrashVFS, int &SkipNum, .Default(false); if (IsInclude) return !HaveCrashVFS; + if (StringRef(Flag).starts_with("-index-store-path")) + return true; // The remaining flags are treated as a single argument. @@ -98,6 +100,8 @@ static bool skipArgs(const char *Flag, bool HaveCrashVFS, int &SkipNum, return !HaveCrashVFS; if (FlagRef.starts_with("-fmodules-cache-path=")) return true; + if (FlagRef.starts_with("-fapinotes-cache-path=")) + return true; SkipNum = 0; return false; @@ -202,6 +206,15 @@ rewriteIncludes(const llvm::ArrayRef &Args, size_t Idx, void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo) const { + // Print the environment to display, if relevant. + if (!EnvironmentDisplay.empty()) { + OS << " env"; + for (const char *NameValue : EnvironmentDisplay) { + OS << ' '; + llvm::sys::printArg(OS, NameValue, /*Quote=*/true); + } + } + // Always quote the exe. OS << ' '; llvm::sys::printArg(OS, Executable, /*Quote=*/true); @@ -217,6 +230,7 @@ void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, } bool HaveCrashVFS = CrashInfo && !CrashInfo->VFSPath.empty(); + bool HaveIndexStorePath = CrashInfo && !CrashInfo->IndexStorePath.empty(); for (size_t i = 0, e = Args.size(); i < e; ++i) { const char *const Arg = Args[i]; @@ -281,6 +295,24 @@ void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, llvm::sys::printArg(OS, ModCachePath, Quote); } + if (CrashInfo && HaveIndexStorePath) { + SmallString<128> IndexStoreDir; + + if (HaveCrashVFS) { + IndexStoreDir = llvm::sys::path::parent_path( + llvm::sys::path::parent_path(CrashInfo->VFSPath)); + llvm::sys::path::append(IndexStoreDir, "index-store"); + } else { + IndexStoreDir = "index-store"; + } + + OS << ' '; + llvm::sys::printArg(OS, "-index-store-path", Quote); + OS << ' '; + llvm::sys::printArg(OS, IndexStoreDir.c_str(), Quote); + } + + if (ResponseFile != nullptr) { OS << "\n Arguments passed via response file:\n"; writeResponseFile(OS); @@ -306,6 +338,11 @@ void Command::setEnvironment(llvm::ArrayRef NewEnvironment) { Environment.push_back(nullptr); } +void Command::setEnvironmentDisplay(llvm::ArrayRef Display) { + EnvironmentDisplay.reserve(Display.size()); + EnvironmentDisplay.assign(Display.begin(), Display.end()); +} + void Command::setRedirectFiles( const std::vector> &Redirects) { RedirectFiles = Redirects; diff --git a/clang/lib/Driver/Phases.cpp b/clang/lib/Driver/Phases.cpp index 01598c59bd9eb..cb787e9513df3 100644 --- a/clang/lib/Driver/Phases.cpp +++ b/clang/lib/Driver/Phases.cpp @@ -14,6 +14,7 @@ using namespace clang::driver; const char *phases::getPhaseName(ID Id) { switch (Id) { + case Depscan: return "depscan"; case Preprocess: return "preprocessor"; case Precompile: return "precompiler"; case Compile: return "compiler"; diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index ff08bffdbde1f..4fc98fda753be 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -660,6 +660,9 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, Kinds |= Default; + if (TC.getTriple().isOSBinFormatMachO() && !TC.getTriple().isX86()) + Kinds &= ~SanitizerKind::Function; + // We disable the vptr sanitizer if it was enabled by group expansion but RTTI // is disabled. if ((Kinds & SanitizerKind::Vptr) && (RTTIMode == ToolChain::RM_Disabled)) { @@ -1049,7 +1052,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, AsanUseOdrIndicator = Args.hasFlag(options::OPT_fsanitize_address_use_odr_indicator, options::OPT_fno_sanitize_address_use_odr_indicator, - !TC.getTriple().isOSWindows()); + !TC.getTriple().isOSWindows() && !TC.getTriple().isOSDarwin()); if (AllAddedKinds & SanitizerKind::PointerCompare & ~AllRemove) { AsanInvalidPointerCmp = true; diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 5cd5755e01587..43b502a2c194a 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -426,6 +426,7 @@ static const DriverSuffix *FindDriverSuffix(StringRef ProgName, size_t &Pos) { // `flang-new`. This will be removed in the future. {"flang-new", "--driver-mode=flang"}, {"clang-dxc", "--driver-mode=dxc"}, + {"clang-cache", "--driver-mode=cache"}, }; for (const auto &DS : DriverSuffixes) { @@ -640,6 +641,7 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const { case Action::CompileJobClass: case Action::PrecompileJobClass: + case Action::DepscanJobClass: case Action::PreprocessJobClass: case Action::ExtractAPIJobClass: case Action::AnalyzeJobClass: diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 762f8af886920..0c6242e15fe46 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -986,7 +986,8 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, const Driver &D, const ArgList &Args, ArgStringList &CmdArgs, const InputInfo &Output, - const InputInfoList &Inputs) const { + const InputInfoList &Inputs, + std::optional &Sysroot) const { const bool IsIAMCU = getToolChain().getTriple().isOSIAMCU(); CheckPreprocessingOptions(D, Args); @@ -1025,6 +1026,7 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, } CmdArgs.push_back("-dependency-file"); CmdArgs.push_back(DepFile); + CmdArgs.push_back("-skip-unused-modulemap-deps"); } // Cmake generates dependency files using all compilation options specified // by users. Claim those not used for dependency files. @@ -1248,10 +1250,15 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA, // -isysroot to the CC1 invocation. StringRef sysroot = C.getSysRoot(); if (sysroot != "") { - if (!Args.hasArg(options::OPT_isysroot)) { + if (Arg *A = Args.getLastArg(options::OPT_isysroot)) { + Sysroot = A->getValue(); + } else { CmdArgs.push_back("-isysroot"); CmdArgs.push_back(C.getArgs().MakeArgString(sysroot)); + Sysroot = sysroot; } + } else if (Arg *A = Args.getLastArg(options::OPT_isysroot)) { + Sysroot = A->getValue(); } // Parse additional include paths from environment variables. @@ -4036,6 +4043,14 @@ static void RenderBuiltinOptions(const ToolChain &TC, const llvm::Triple &T, } } +static void RenderFeatueAvailabilityOptions(const ArgList &Args, + ArgStringList &CmdArgs) { + for (const Arg *A : Args.filtered(options::OPT_ffeature_availability_EQ)) { + A->claim(); + A->render(Args, CmdArgs); + } +} + bool Driver::getDefaultModuleCachePath(SmallVectorImpl &Result) { if (const char *Str = std::getenv("CLANG_MODULE_CACHE_PATH")) { Twine Path{Str}; @@ -5026,13 +5041,80 @@ static void ProcessVSRuntimeLibrary(const ToolChain &TC, const ArgList &Args, CmdArgs.push_back("--dependent-lib=softintrin"); } -void Clang::ConstructJob(Compilation &C, const JobAction &JA, +void Clang::AddPrefixMappingOptions(const ArgList &Args, ArgStringList &CmdArgs, + const Driver &D, + std::optional Sysroot) const { + auto IsPathApplicableAsPrefix = [](std::optional Path) { + return Path && llvm::sys::path::is_absolute(*Path) && + *Path != llvm::sys::path::root_path(*Path); + }; + + if (Arg *A = Args.getLastArg(options::OPT_fdepscan_prefix_map_sdk_EQ)) { + if (IsPathApplicableAsPrefix(Sysroot)) { + CmdArgs.push_back(Args.MakeArgString(Twine("-fdepscan-prefix-map=") + + *Sysroot + "=" + A->getValue())); + } else { + // FIXME: warning if we cannot infer sdk + } + } + + if (Arg *A = Args.getLastArg(options::OPT_fdepscan_prefix_map_toolchain_EQ)) { + StringRef ResourceDir = D.ResourceDir; + StringRef Guess = llvm::sys::path::parent_path(ResourceDir); + for (StringRef Dir : {"clang", "lib", "usr"}) { + if (llvm::sys::path::filename(Guess) != Dir) + break; + Guess = llvm::sys::path::parent_path(Guess); + } + if (IsPathApplicableAsPrefix(Guess)) { + CmdArgs.push_back(Args.MakeArgString(Twine("-fdepscan-prefix-map=") + + Guess + "=" + A->getValue())); + } else { + // FIXME: warning if we cannot infer toolchain + } + } + + for (const Arg *A : Args.filtered(options::OPT_fdepscan_prefix_map_EQ)) { + A->claim(); + StringRef Map = A->getValue(); + StringRef Prefix = Map.split('=').first; + if (Prefix.size() == Map.size() || !IsPathApplicableAsPrefix(Prefix)) { + D.Diag(diag::err_drv_invalid_argument_to_option) + << A->getValue() << A->getOption().getName(); + } else { + A->render(Args, CmdArgs); + } + } +} + +static void addCachingOptions(ArgStringList &CmdArgs) { + if (std::getenv("CLANG_CACHE_TEST_DETERMINISTIC_OUTPUTS")) + CmdArgs.push_back("-fcache-disable-replay"); + + if (std::getenv("CLANG_CACHE_REDACT_TIME_MACROS")) { + // Remove use of these macros to get reproducible outputs. This can + // accompany CLANG_CACHE_TEST_DETERMINISTIC_OUTPUTS to avoid fatal errors + // when the source uses these macros. + CmdArgs.push_back("-Wno-builtin-macro-redefined"); + CmdArgs.push_back("-D__DATE__=\"redacted\""); + CmdArgs.push_back("-D__TIMESTAMP__=\"redacted\""); + CmdArgs.push_back("-D__TIME__=\"redacted\""); + } + + if (std::getenv("CLANG_CACHE_CHECK_REPRODUCIBLE_CACHING_ISSUES")) + CmdArgs.push_back("-Werror=reproducible-caching"); +} + +void Clang::ConstructJob(Compilation &C, const JobAction &Job, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, const char *LinkingOutput) const { const auto &TC = getToolChain(); const llvm::Triple &RawTriple = TC.getTriple(); const llvm::Triple &Triple = TC.getEffectiveTriple(); const std::string &TripleStr = Triple.getTriple(); + const JobAction &JA = isa(Job) + ? cast(Job).getScanningJobAction() + : Job; bool KernelOrKext = Args.hasArg(options::OPT_mkernel, options::OPT_fapple_kext); @@ -5121,11 +5203,89 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (IsIAMCU && types::isCXX(Input.getType())) D.Diag(diag::err_drv_clang_unsupported) << "C++ for IAMCU"; + { + const OptSpecifier DepScanOpts[] = {options::OPT_fdepscan_EQ, + options::OPT_fdepscan_include_tree, + options::OPT_fdepscan_share_EQ, + options::OPT_fdepscan_share_identifier, + options::OPT_fdepscan_share_parent, + options::OPT_fdepscan_share_parent_EQ, + options::OPT_fno_depscan_share, + options::OPT_fdepscan_share_stop_EQ, + options::OPT_fdepscan_daemon_EQ}; + + // Handle depscan. + if (Job.getKind() == Action::DepscanJobClass) { + CmdArgs.push_back("-cc1depscan"); + + // Pass depscan related options to cc1depscan. + Args.addAllArgs(CmdArgs, DepScanOpts); + + assert(Output.isFilename() && "Depscan needs to have file output"); + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + CmdArgs.push_back("-cc1-args"); + } else if (Arg *A = Args.getLastArg(options::OPT_fdepscan_EQ)) { + if (A->getValue() == llvm::StringLiteral("off")) { + // Claim depscan args. + for (auto Opt : DepScanOpts) + Args.ClaimAllArgs(Opt); + } + } + } + // Invoke ourselves in -cc1 mode. // // FIXME: Implement custom jobs for internal actions. CmdArgs.push_back("-cc1"); + // Handle response file input. + if (Inputs.front().getType() == types::TY_ResponseFile) { + // Render response file input first. + assert(Inputs.size() == 1 && "Only one response file input"); + auto &II = Inputs.front(); + assert(II.isFilename() && "Should be response file"); + SmallString<128> InputName("@"); + InputName += II.getFilename(); + CmdArgs.push_back(C.getArgs().MakeArgString(InputName)); + + // FIXME: The -cc1depscan job should include the -o for the -cc1 job that + // follows it in -cc1-args, but it doesn't currently have access to that + // filename. To fix this, we need to somehow route the final output type + // into Depscan ConstructJob. + // FIXME: This intermediate output file will adds 3-4% of overhead comparing + // to passing all the cc1 args in the memory. The fix might be pin DepScan + // job in-process and pass cc1 args in memory instead of creating an actual + // file here. + if (Output.isFilename()) { + CmdArgs.push_back("-o"); + // Fix up the output file if it is a relative path to + // `-working-directory`. + SmallString<128> OutPath( + Args.getLastArgValue(options::OPT_working_directory)); + StringRef Out = Output.getFilename(); + if (llvm::sys::path::is_relative(Out) && !OutPath.empty()) { + llvm::sys::path::append(OutPath, Output.getFilename()); + Out = OutPath.str(); + } + CmdArgs.push_back(Args.MakeArgString(Out)); + } + // FIXME: Clean up this code and factor out the common logic (see the end of + // the function) + const char *Exec = D.getClangProgramPath(); + if (D.CC1Main && !D.CCGenDiagnostics) { + // Invoke the CC1 directly in this process + C.addCommand(std::make_unique( + Job, *this, ResponseFileSupport::AtFileUTF8(), Exec, CmdArgs, Inputs, + Output)); + } else { + C.addCommand(std::make_unique(Job, *this, + ResponseFileSupport::AtFileUTF8(), + Exec, CmdArgs, Inputs, Output)); + } + return; + } + // Add the "effective" target triple. CmdArgs.push_back("-triple"); CmdArgs.push_back(Args.MakeArgString(TripleStr)); @@ -5363,7 +5523,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (SymbolGraphDirArg) SymbolGraphDirArg->render(Args, CmdArgs); } else { - assert((isa(JA) || isa(JA)) && + assert((isa(JA) || isa(JA) || isa(JA)) && "Invalid action for clang tool."); if (JA.getType() == types::TY_Nothing) { CmdArgs.push_back("-fsyntax-only"); @@ -5594,7 +5754,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, } C.addCommand(std::make_unique( - JA, *this, ResponseFileSupport::AtFileUTF8(), D.getClangProgramPath(), + Job, *this, ResponseFileSupport::AtFileUTF8(), D.getClangProgramPath(), CmdArgs, Inputs, Output, D.getPrependArg())); return; } @@ -6441,12 +6601,41 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_working_directory); + if (Args.hasArg(options::OPT_index_store_path)) { + Args.AddLastArg(CmdArgs, options::OPT_index_store_path); + Args.AddLastArg(CmdArgs, options::OPT_index_ignore_system_symbols); + Args.AddLastArg(CmdArgs, options::OPT_index_record_codegen_name); + Args.AddLastArg(CmdArgs, options::OPT_index_unit_output_path); + Args.AddLastArg(CmdArgs, options::OPT_index_ignore_macros); + Args.AddLastArg(CmdArgs, options::OPT_index_ignore_pcms); + + // If '-o' is passed along with '-fsyntax-only' pass it along the cc1 + // invocation so that the index action knows what the out file is. + if (isa(JA) && JA.getType() == types::TY_Nothing) { + Args.AddLastArg(CmdArgs, options::OPT_o); + } + } + + if (const char *IdxStorePath = ::getenv("CLANG_PROJECT_INDEX_PATH")) { + CmdArgs.push_back("-index-store-path"); + CmdArgs.push_back(IdxStorePath); + CmdArgs.push_back("-index-ignore-system-symbols"); + CmdArgs.push_back("-index-record-codegen-name"); + } + // Add preprocessing options like -I, -D, etc. if we are using the // preprocessor. // // FIXME: Support -fpreprocessed + std::optional Sysroot; if (types::getPreprocessedType(InputType) != types::TY_INVALID) - AddPreprocessingOptions(C, JA, D, Args, CmdArgs, Output, Inputs); + AddPreprocessingOptions(C, JA, D, Args, CmdArgs, Output, Inputs, Sysroot); + + // Handle -fdepscan-prefix-map-* options + AddPrefixMappingOptions(Args, CmdArgs, D, Sysroot); + + // Handle compile caching options. + addCachingOptions(CmdArgs); // Don't warn about "clang -c -DPIC -fPIC test.i" because libtool.m4 assumes // that "The compiler can only warn and ignore the option if not recognized". @@ -7014,6 +7203,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_ftrapv); Args.AddLastArg(CmdArgs, options::OPT_malign_double); Args.AddLastArg(CmdArgs, options::OPT_fno_temp_file); + Args.AddLastArg(CmdArgs, options::OPT_fsuppress_conflicting_types); if (const char *Name = C.getTimeTraceFile(&JA)) { CmdArgs.push_back(Args.MakeArgString("-ftime-trace=" + Twine(Name))); @@ -7027,6 +7217,20 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, } Args.AddLastArg(CmdArgs, options::OPT_ftrap_function_EQ); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (Arg *A = Args.getLastArg(options::OPT_ftrap_function_returns, + options::OPT_fno_trap_function_returns)) + if (A->getOption().matches(options::OPT_ftrap_function_returns)) { + if (Args.getLastArg(options::OPT_ftrap_function_EQ)) + CmdArgs.push_back("-ftrap-function-returns"); + else + D.Diag(diag::err_drv_option_requires_option) + << "-ftrap-function-returns" << "-ftrap-function="; + } + + Args.addOptInFlag(CmdArgs, options::OPT_funique_traps, + options::OPT_fno_unique_traps); + /* TO_UPSTREAM(BoundsSafety) OFF*/ // Handle -f[no-]wrapv and -f[no-]strict-overflow, which are used by both // clang and flang. @@ -7177,15 +7381,23 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, } RenderBuiltinOptions(TC, RawTriple, Args, CmdArgs); + RenderFeatueAvailabilityOptions(Args, CmdArgs); + Args.addOptOutFlag(CmdArgs, options::OPT_fassume_sane_operator_new, options::OPT_fno_assume_sane_operator_new); - if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, false)) - CmdArgs.push_back("-fapinotes"); - if (Args.hasFlag(options::OPT_fapinotes_modules, - options::OPT_fno_apinotes_modules, false)) - CmdArgs.push_back("-fapinotes-modules"); - Args.AddLastArg(CmdArgs, options::OPT_fapinotes_swift_version); + if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, false) || + Args.hasFlag(options::OPT_fapinotes_modules, + options::OPT_fno_apinotes_modules, false) || + Args.hasArg(options::OPT_iapinotes_modules)) { + if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, false)) + CmdArgs.push_back("-fapinotes"); + if (Args.hasFlag(options::OPT_fapinotes_modules, + options::OPT_fno_apinotes_modules, false)) + CmdArgs.push_back("-fapinotes-modules"); + + Args.AddLastArg(CmdArgs, options::OPT_fapinotes_swift_version); + } // -fblocks=0 is default. if (Args.hasFlag(options::OPT_fblocks, options::OPT_fno_blocks, @@ -7505,6 +7717,58 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.addOptOutFlag(CmdArgs, options::OPT_fassume_sane_operator_new, options::OPT_fno_assume_sane_operator_new); + /*TO_UPSTREAM(BoundsSafety) ON*/ + // -fbounds-safety is off by default. + if (Arg *A = Args.getLastArg(options::OPT_fbounds_safety, + options::OPT_fno_bounds_safety)) { + if (A->getOption().matches(options::OPT_fbounds_safety)) { + CmdArgs.push_back("-fbounds-safety"); + auto Iter = Args.filtered(options::OPT_mllvm); + bool HasBoundsCheckOpt = std::any_of(Iter.begin(), Iter.end(), [](const Arg *A) { + return StringRef(A->getValue(0)).starts_with("-enable-constraint-elimination"); + }); + // Turn on constraint-elimination pass when -fbounds-safety is enabled when the + // pass is not explicitly enabled or disabled. + if (!HasBoundsCheckOpt) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-enable-constraint-elimination"); + } + + // This is inside the -fbounds-safety flag checking, such that the flag + // will be ignored with -Wunused-command-line-argument being triggered. + Args.addAllArgs( + CmdArgs, {options::OPT_fbounds_safety_bringup_missing_checks_EQ, + options::OPT_fno_bounds_safety_bringup_missing_checks_EQ}); + } + } + + if (Args.hasFlag(options::OPT_fexperimental_bounds_safety_attributes, + options::OPT_fno_experimental_bounds_safety_attributes, + false)) { + CmdArgs.push_back("-fexperimental-bounds-safety-attributes"); + } + + if (Args.hasFlag(options::OPT_fbounds_attributes_cxx_experimental, + options::OPT_fno_bounds_attributes_cxx_experimental, + false)) { + CmdArgs.push_back("-fbounds-attributes-cxx-experimental"); + } + + if (Args.hasFlag(options::OPT_fbounds_attributes_objc_experimental, + options::OPT_fno_bounds_attributes_objc_experimental, + false)) { + CmdArgs.push_back("-fbounds-attributes-objc-experimental"); + } + + // -fbounds-safety-relaxed-system-headers is on by default + Args.addOptOutFlag(CmdArgs, + options::OPT_fbounds_safety_relaxed_system_headers, + options::OPT_fno_bounds_safety_relaxed_system_headers); + + Args.addOptInFlag(CmdArgs, options::OPT_fbounds_safety_adoption_mode, + options::OPT_fno_bounds_safety_adoption_mode); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + // -fassume-unique-vtables is on by default. Args.addOptOutFlag(CmdArgs, options::OPT_fassume_unique_vtables, options::OPT_fno_assume_unique_vtables); @@ -7575,6 +7839,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // -fno-common is the default, set -fcommon only when that flag is set. Args.addOptInFlag(CmdArgs, options::OPT_fcommon, options::OPT_fno_common); + if (Args.hasFlag(options::OPT_fptrauth_soft, + options::OPT_fno_ptrauth_soft, false)) + CmdArgs.push_back("-fptrauth-soft"); + // -fsigned-bitfields is default, and clang doesn't yet support // -funsigned-bitfields. if (!Args.hasFlag(options::OPT_fsigned_bitfields, @@ -7813,6 +8081,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, const char *Exec = D.getClangProgramPath(); + // Check if there's a request for reproducible debug info. Pass it through + // but also use it. + bool GReproducible = + Args.hasFlag(options::OPT_greproducible, + options::OPT_gno_reproducible, false); + if (GReproducible) + CmdArgs.push_back("-greproducible"); + // Optionally embed the -cc1 level arguments into the debug info or a // section, for build analysis. // Also record command line arguments into the debug info if @@ -7820,7 +8096,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // By default, -gno-record-gcc-switches is set on and no recording. auto GRecordSwitches = false; auto FRecordSwitches = false; - if (shouldRecordCommandLine(TC, Args, FRecordSwitches, GRecordSwitches)) { + if (shouldRecordCommandLine(TC, Args, FRecordSwitches, GRecordSwitches, + GReproducible)) { auto FlagsArgString = renderEscapedCommandLine(TC, Args); if (TC.UseDwarfDebugFlags() || GRecordSwitches) { CmdArgs.push_back("-dwarf-debug-flags"); @@ -8134,11 +8411,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (D.CC1Main && !D.CCGenDiagnostics) { // Invoke the CC1 directly in this process C.addCommand(std::make_unique( - JA, *this, ResponseFileSupport::AtFileUTF8(), Exec, CmdArgs, Inputs, + Job, *this, ResponseFileSupport::AtFileUTF8(), Exec, CmdArgs, Inputs, Output, D.getPrependArg())); } else { C.addCommand(std::make_unique( - JA, *this, ResponseFileSupport::AtFileUTF8(), Exec, CmdArgs, Inputs, + Job, *this, ResponseFileSupport::AtFileUTF8(), Exec, CmdArgs, Inputs, Output, D.getPrependArg())); } @@ -8650,6 +8927,46 @@ void ClangAs::AddRISCVTargetArgs(const ArgList &Args, } } +void ClangAs::AddAArch64TargetArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + const llvm::Triple &Triple = getToolChain().getTriple(); + + // In the assembler, arm64e support mostly consists of setting an ABI version. + // It also enables various preprocessor macros, but that's done before -cc1as. + if (Triple.isArm64e()) { + // The ptrauth ABI version is 0 by default, but can be overridden. + static const constexpr unsigned DefaultPtrauthABIVersion = 0; + + unsigned PtrAuthABIVersion = DefaultPtrauthABIVersion; + const Arg *A = Args.getLastArg(options::OPT_fptrauth_abi_version_EQ, + options::OPT_fno_ptrauth_abi_version); + bool HasVersionArg = + A && A->getOption().matches(options::OPT_fptrauth_abi_version_EQ); + if (HasVersionArg) { + unsigned PtrAuthABIVersionArg; + if (StringRef(A->getValue()).getAsInteger(10, PtrAuthABIVersionArg)) + getToolChain().getDriver().Diag(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + else + PtrAuthABIVersion = PtrAuthABIVersionArg; + } + + // Pass the ABI version to -cc1, regardless of its value, if the user asked + // for it or if the user didn't explicitly disable it. + if (HasVersionArg || !Args.hasArg(options::OPT_fno_ptrauth_abi_version)) { + CmdArgs.push_back(Args.MakeArgString("-fptrauth-abi-version=" + + llvm::utostr(PtrAuthABIVersion))); + + // -f(no-)ptrauth-kernel-abi-version can override -mkernel and + // -fapple-kext + if (Args.hasArg(options::OPT_fptrauth_kernel_abi_version, + options::OPT_mkernel, options::OPT_fapple_kext) && + !Args.hasArg(options::OPT_fno_ptrauth_kernel_abi_version)) + CmdArgs.push_back("-fptrauth-kernel-abi-version"); + } + } +} + void ClangAs::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, const ArgList &Args, @@ -8860,6 +9177,7 @@ void ClangAs::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-mllvm"); CmdArgs.push_back("-aarch64-mark-bti-property"); } + AddAArch64TargetArgs(Args, CmdArgs); break; case llvm::Triple::loongarch32: diff --git a/clang/lib/Driver/ToolChains/Clang.h b/clang/lib/Driver/ToolChains/Clang.h index 18f6c5ed06a59..9b3af59255051 100644 --- a/clang/lib/Driver/ToolChains/Clang.h +++ b/clang/lib/Driver/ToolChains/Clang.h @@ -43,7 +43,8 @@ class LLVM_LIBRARY_VISIBILITY Clang : public Tool { const Driver &D, const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs, const InputInfo &Output, - const InputInfoList &Inputs) const; + const InputInfoList &Inputs, + std::optional &Sysroot) const; void RenderTargetOptions(const llvm::Triple &EffectiveTriple, const llvm::opt::ArgList &Args, bool KernelOrKext, @@ -102,6 +103,11 @@ class LLVM_LIBRARY_VISIBILITY Clang : public Tool { StringRef Dir, Compilation &C, StringRef Target, const InputInfo &Output, const InputInfo &Input, const llvm::opt::ArgList &Args) const; + void AddPrefixMappingOptions(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs, + const Driver &D, + std::optional Sysroot) const; + public: Clang(const ToolChain &TC, bool HasIntegratedBackend = true); ~Clang() override; @@ -131,6 +137,8 @@ class LLVM_LIBRARY_VISIBILITY ClangAs : public Tool { llvm::opt::ArgStringList &CmdArgs) const; void AddRISCVTargetArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const; + void AddAArch64TargetArgs(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs) const; bool hasGoodDiagnostics() const override { return true; } bool hasIntegratedAssembler() const override { return false; } bool hasIntegratedCPP() const override { return false; } diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index 0406142fa916b..a07ff4344f52d 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -3042,7 +3042,8 @@ const char *tools::renderEscapedCommandLine(const ToolChain &TC, bool tools::shouldRecordCommandLine(const ToolChain &TC, const llvm::opt::ArgList &Args, bool &FRecordCommandLine, - bool &GRecordCommandLine) { + bool &GRecordCommandLine, + const bool GReproducible) { const Driver &D = TC.getDriver(); const llvm::Triple &Triple = TC.getEffectiveTriple(); const std::string &TripleStr = Triple.getTriple(); @@ -3050,7 +3051,7 @@ bool tools::shouldRecordCommandLine(const ToolChain &TC, FRecordCommandLine = Args.hasFlag(options::OPT_frecord_command_line, options::OPT_fno_record_command_line, false); - GRecordCommandLine = + GRecordCommandLine = !GReproducible && Args.hasFlag(options::OPT_grecord_command_line, options::OPT_gno_record_command_line, false); if (FRecordCommandLine && !Triple.isOSBinFormatELF() && diff --git a/clang/lib/Driver/ToolChains/CommonArgs.h b/clang/lib/Driver/ToolChains/CommonArgs.h index 96bc0619dcbc0..04a7bb3a1c427 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.h +++ b/clang/lib/Driver/ToolChains/CommonArgs.h @@ -251,7 +251,8 @@ const char *renderEscapedCommandLine(const ToolChain &TC, bool shouldRecordCommandLine(const ToolChain &TC, const llvm::opt::ArgList &Args, bool &FRecordCommandLine, - bool &GRecordCommandLine); + bool &GRecordCommandLine, + const bool GReproducible = false); void renderCommonIntegerOverflowOptions(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs); diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index 4735dc3ad30ee..803332c77c4c6 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -13,6 +13,7 @@ #include "clang/Basic/AlignedAllocation.h" #include "clang/Basic/ObjCRuntime.h" #include "clang/Config/config.h" +#include "clang/Driver/BoundsSafetyArgs.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" @@ -569,6 +570,102 @@ static void renderRemarksOptions(const ArgList &Args, ArgStringList &CmdArgs, } } +namespace { +/// Environment. By default, same as the host application. +class VirtualEnvironment { +public: + /// Get the environment block, only if it has been overridden. This is a valid + /// environment block and includes the terminating \a nullptr. + std::optional> getEnvironmentIfOverridden() const { + if (Environment) + return ArrayRef(*Environment); + return std::nullopt; + } + + /// Get the parts of the environment that have been overridden. Not a valid + /// environment block. + ArrayRef getOverriddenEnvironment() const { + return OverriddenEnvironment; + } + + void set(StringRef Name, StringRef Value, + llvm::function_ref SaveString); + +private: + static const char **findName(const char **I, StringRef Name, + std::optional &Existing); + static const char **getHostEnvironment(); + void copyHostEnvironment(const char **I = nullptr); + std::optional> Environment; + SmallVector OverriddenEnvironment; +}; +} // end namespace + +const char **VirtualEnvironment::findName(const char **I, StringRef Name, + std::optional &Existing) { + std::string Prefix = (Name + Twine("=")).str(); + for (; *I; ++I) { + StringRef NameValue = *I; + if (!NameValue.starts_with(Prefix)) + continue; + Existing = NameValue; + break; + } + return I; +} + +extern char **environ; +const char **VirtualEnvironment::getHostEnvironment() { + return const_cast(environ); +} + +void VirtualEnvironment::copyHostEnvironment(const char **Hint) { + assert(!Environment); + const char **B = getHostEnvironment(); + const char **I = Hint ? Hint : B; + while (*I) + ++I; + + Environment.emplace(); + + // Include room for nullptr plus 1 new variable. + Environment->reserve(I - B + 2); + + // Include nullptr. + Environment->append(B, I + 1); +} + +/// Set an environment variable. +/// +/// FIXME: Not portable. +void VirtualEnvironment::set( + StringRef Name, StringRef Value, + llvm::function_ref SaveString) { + const char **B = + Environment ? Environment->data() : const_cast(environ); + std::optional Existing; + const char **I = findName(B, Name, Existing); + if (Existing && Existing->drop_front(Name.size() + 1) == Value) + return; + + SmallString<128> Storage; + const char *NameValue = + SaveString((Name + Twine("=") + Value).toStringRef(Storage)); + + // Record what has been overridden. + OverriddenEnvironment.push_back(NameValue); + + // Update the environment block. + if (!Environment) + copyHostEnvironment(I); + if (*I) { + (*Environment)[I - B] = NameValue; + return; + } + Environment->back() = NameValue; + Environment->push_back(nullptr); +} + static void AppendPlatformPrefix(SmallString<128> &Path, const llvm::Triple &T); void darwin::Linker::ConstructJob(Compilation &C, const JobAction &JA, @@ -589,6 +686,12 @@ void darwin::Linker::ConstructJob(Compilation &C, const JobAction &JA, // more information. ArgStringList CmdArgs; + Args.ClaimAllArgs(options::OPT_index_store_path); + Args.ClaimAllArgs(options::OPT_index_ignore_system_symbols); + Args.ClaimAllArgs(options::OPT_index_record_codegen_name); + Args.ClaimAllArgs(options::OPT_index_ignore_macros); + Args.ClaimAllArgs(options::OPT_index_ignore_pcms); + VersionTuple Version = getMachOToolChain().getLinkerVersion(Args); bool LinkerIsLLD; @@ -825,9 +928,27 @@ void darwin::Linker::ConstructJob(Compilation &C, const JobAction &JA, "-filelist"}; } + // Set the environment to include ZERO_AR_FLAGS. + // + // FIXME: Add a flag to the linkers to support this without an environment + // variable, and use that when the linkers are new enough. + VirtualEnvironment Environment; + if (Args.hasFlag(options::OPT_greproducible, + options::OPT_gno_reproducible, false)) + Environment.set("ZERO_AR_DATE", "1", + [&](StringRef S) { return Args.MakeArgString(S); }); + std::unique_ptr Cmd = std::make_unique( JA, *this, ResponseSupport, Exec, CmdArgs, Inputs, Output); Cmd->setInputFileList(std::move(InputFileList)); + + // Set the environment, if it has been overridden. + if (std::optional> Env = + Environment.getEnvironmentIfOverridden()) { + Cmd->setEnvironment(Env->drop_back()); + Cmd->setEnvironmentDisplay(Environment.getOverriddenEnvironment()); + } + C.addCommand(std::move(Cmd)); } @@ -1206,6 +1327,26 @@ void DarwinClang::addClangTargetOptions( Action::OffloadKind DeviceOffloadKind) const { Darwin::addClangTargetOptions(DriverArgs, CC1Args, DeviceOffloadKind); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + // When `-fbounds-safety` and new userspace Libc attributes are enabled + // set a macro that userspace Libc will look for to decide whether or not to + // enable -fbounds-safety annotations in its headers (rdar://84733153). This + // is only being set during the gradual enablement of new checks and will be + // removed once the transition is complete (rdar://137912561). + if (DriverArgs.hasFlagNoClaim(options::OPT_fbounds_safety, + options::OPT_fno_bounds_safety, false)) { + LangOptions::BoundsSafetyNewChecksMaskIntTy NewChecks = + ParseBoundsSafetyNewChecksMaskFromArgs(DriverArgs, + /*DiagnosticsEngine=*/nullptr); + if (NewChecks & LangOptions::BS_CHK_LibCAttributes) { + bool TargetWithoutUserspaceLibc = false; + if (getTriple().isOSDarwin() && !TargetWithoutUserspaceLibc) { + CC1Args.push_back("-D__LIBC_STAGED_BOUNDS_SAFETY_ATTRIBUTES"); + } + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ } /// Take a path that speculatively points into Xcode and return the @@ -3071,6 +3212,38 @@ void MachO::addClangTargetOptions(const llvm::opt::ArgList &DriverArgs, // On arm64e, enable pointer authentication (for the return address and // indirect calls), as well as usage of the intrinsics. if (getArchName() == "arm64e") { + // The ptrauth ABI version is 0 by default, but can be overridden. + static const constexpr unsigned DefaultPtrauthABIVersion = 0; + + unsigned PtrAuthABIVersion = DefaultPtrauthABIVersion; + const Arg *A = DriverArgs.getLastArg(options::OPT_fptrauth_abi_version_EQ, + options::OPT_fno_ptrauth_abi_version); + bool HasVersionArg = + A && A->getOption().matches(options::OPT_fptrauth_abi_version_EQ); + if (HasVersionArg) { + unsigned PtrAuthABIVersionArg; + if (StringRef(A->getValue()).getAsInteger(10, PtrAuthABIVersionArg)) + getDriver().Diag(diag::err_drv_invalid_value) + << A->getAsString(DriverArgs) << A->getValue(); + else + PtrAuthABIVersion = PtrAuthABIVersionArg; + } + + // Pass the ABI version to -cc1, regardless of its value, if the user asked + // for it or if the user didn't explicitly disable it. + if (HasVersionArg || + !DriverArgs.hasArg(options::OPT_fno_ptrauth_abi_version)) { + CC1Args.push_back(DriverArgs.MakeArgString( + "-fptrauth-abi-version=" + llvm::utostr(PtrAuthABIVersion))); + + // -f(no-)ptrauth-kernel-abi-version can override -mkernel and + // -fapple-kext + if (DriverArgs.hasArg(options::OPT_fptrauth_kernel_abi_version, + options::OPT_mkernel, options::OPT_fapple_kext) && + !DriverArgs.hasArg(options::OPT_fno_ptrauth_kernel_abi_version)) + CC1Args.push_back("-fptrauth-kernel-abi-version"); + } + if (!DriverArgs.hasArg(options::OPT_fptrauth_returns, options::OPT_fno_ptrauth_returns)) CC1Args.push_back("-fptrauth-returns"); @@ -3140,10 +3313,6 @@ void Darwin::addClangTargetOptions( if (!sdkSupportsBuiltinModules(SDKInfo)) CC1Args.push_back("-fbuiltin-headers-in-system-modules"); - if (!DriverArgs.hasArgNoClaim(options::OPT_fdefine_target_os_macros, - options::OPT_fno_define_target_os_macros)) - CC1Args.push_back("-fdefine-target-os-macros"); - // Disable subdirectory modulemap search on sufficiently recent SDKs. if (SDKInfo && !DriverArgs.hasFlag(options::OPT_fmodulemap_allow_subdirectory_search, @@ -3172,6 +3341,10 @@ void Darwin::addClangTargetOptions( if (!RequiresSubdirectorySearch) CC1Args.push_back("-fno-modulemap-allow-subdirectory-search"); } + + if (!DriverArgs.hasArgNoClaim(options::OPT_fdefine_target_os_macros, + options::OPT_fno_define_target_os_macros)) + CC1Args.push_back("-fdefine-target-os-macros"); } void Darwin::addClangCC1ASTargetOptions( @@ -3621,6 +3794,9 @@ SanitizerMask Darwin::getSupportedSanitizers() const { Res |= SanitizerKind::FuzzerNoLink; Res |= SanitizerKind::ObjCCast; + // Apple-Clang: Don't support LSan. rdar://problem/45841334 + Res &= ~SanitizerKind::Leak; + // Prior to 10.9, macOS shipped a version of the C++ standard library without // C++11 support. The same is true of iOS prior to version 5. These OS'es are // incompatible with -fsanitize=vptr. diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp index 1e9bd3de75f04..7ee0a11fdac3d 100644 --- a/clang/lib/Driver/ToolChains/Linux.cpp +++ b/clang/lib/Driver/ToolChains/Linux.cpp @@ -635,14 +635,21 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, // Add 'include' in the resource directory, which is similar to // GCC_INCLUDE_DIR (private headers) in GCC. Note: the include directory - // contains some files conflicting with system /usr/include. musl systems - // prefer the /usr/include copies which are more relevant. - SmallString<128> ResourceDirInclude(D.ResourceDir); - llvm::sys::path::append(ResourceDirInclude, "include"); - if (!DriverArgs.hasArg(options::OPT_nobuiltininc) && - (!getTriple().isMusl() || DriverArgs.hasArg(options::OPT_nostdlibinc))) + // contains some files conflicting with system /usr/include. + // + // Note: the include order used to be different on musl systems because + // of the expectation that that users of that C library would use the + // C library's copies of the "built-in" headers. This was a mistake; + // it's better to adapt the built-in headers to fall through in that case + // (using #include_next), since otherwise if they get pulled in through + // some other mechanism, __has_include_next(
) will fail and then + // they'll try to redefine things, which causes errors. + if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { + SmallString<128> ResourceDirInclude(D.ResourceDir); + llvm::sys::path::append(ResourceDirInclude, "include"); addSystemInclude(DriverArgs, CC1Args, ResourceDirInclude); - + } + if (DriverArgs.hasArg(options::OPT_nostdlibinc)) return; @@ -682,9 +689,6 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, addExternCSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/include")); addExternCSystemInclude(DriverArgs, CC1Args, concat(SysRoot, "/usr/include")); - - if (!DriverArgs.hasArg(options::OPT_nobuiltininc) && getTriple().isMusl()) - addSystemInclude(DriverArgs, CC1Args, ResourceDirInclude); } void Linux::addLibStdCxxIncludePaths(const llvm::opt::ArgList &DriverArgs, diff --git a/clang/lib/Edit/CMakeLists.txt b/clang/lib/Edit/CMakeLists.txt index a7fa9c28e1f61..99aff3c3aeb19 100644 --- a/clang/lib/Edit/CMakeLists.txt +++ b/clang/lib/Edit/CMakeLists.txt @@ -5,6 +5,8 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangEdit Commit.cpp EditedSource.cpp + FillInMissingProtocolStubs.cpp + FillInMissingSwitchEnumCases.cpp RewriteObjCFoundationAPI.cpp LINK_LIBS diff --git a/clang/lib/Edit/FillInMissingProtocolStubs.cpp b/clang/lib/Edit/FillInMissingProtocolStubs.cpp new file mode 100644 index 0000000000000..cc97cee32f73e --- /dev/null +++ b/clang/lib/Edit/FillInMissingProtocolStubs.cpp @@ -0,0 +1,480 @@ +//===--- FillInMissingProtocolStubs.cpp - --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements the "Add methods from protocol(s)" refactoring operation. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/NSAPI.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Edit/RefactoringFixits.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/DenseSet.h" +#include + +using namespace clang; +using namespace edit; +using namespace fillInMissingProtocolStubs; + +// FIXME: This is duplicated with the refactoring lib. +static bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2, + const SourceManager &SM) { + return !Loc1.isMacroID() && !Loc2.isMacroID() && + SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2); +} + +static bool isSemicolonAtLocation(SourceLocation TokenLoc, + const SourceManager &SM, + const LangOptions &LangOpts) { + return Lexer::getSourceText( + CharSourceRange::getTokenRange(TokenLoc, TokenLoc), SM, + LangOpts) == ";"; +} + +static SourceLocation getLocationOfPrecedingToken(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts) { + SourceLocation Result = Loc; + if (Result.isMacroID()) + Result = SM.getExpansionLoc(Result); + FileID FID = SM.getFileID(Result); + SourceLocation StartOfFile = SM.getLocForStartOfFile(FID); + if (Loc == StartOfFile) + return SourceLocation(); + return Lexer::GetBeginningOfToken(Result.getLocWithOffset(-1), SM, LangOpts); +} + +static SourceLocation +getLastLineLocationUnlessItHasOtherTokens(SourceLocation SpellingLoc, + const SourceManager &SM, + const LangOptions &LangOpts) { + assert(!SpellingLoc.isMacroID() && "Expecting a spelling location"); + SourceLocation NextTokenLoc = + Lexer::findNextTokenLocationAfterTokenAt(SpellingLoc, SM, LangOpts); + if (NextTokenLoc.isValid()) { + bool IsSameLine = areOnSameLine(SpellingLoc, NextTokenLoc, SM); + if (IsSameLine) { + // Could be a ';' on the same line, so try looking after the ';' + if (isSemicolonAtLocation(NextTokenLoc, SM, LangOpts)) + return getLastLineLocationUnlessItHasOtherTokens(NextTokenLoc, SM, + LangOpts); + } else { + SourceLocation LastLoc = SM.translateLineCol( + SM.getFileID(SpellingLoc), SM.getSpellingLineNumber(SpellingLoc), + std::numeric_limits::max()); + if (LastLoc.isValid()) + return LastLoc; + } + } + return Lexer::getLocForEndOfToken(SpellingLoc, 0, SM, LangOpts); +} + +namespace { + +struct ProtocolInfo { + /// The lower the priority, the more important this protocol is considered to + /// be. Typically protocols from the class have lower priority than protocols + /// from superclasses. + int Priority; +}; + +using ProtocolMapTy = llvm::DenseMap; + +/// Contains the set of methods from all the protocols that the class conforms +/// to. +class MethodSet { +public: + struct MethodInfo { + const ObjCMethodDecl *M; + const ObjCProtocolDecl *P; + int ProtocolPriority; + enum MethodPresenceKind { IsDeclared = 0x1, IsImplemented = 0x2 }; + unsigned PresenceKind = 0; + const ObjCMethodDecl *DeclaredOrImplementedMethod = nullptr; + + MethodInfo(const ObjCMethodDecl *M, const ObjCProtocolDecl *P, + int ProtocolPriority) + : M(M), P(P), ProtocolPriority(ProtocolPriority) {} + + bool isRequired() const { + return M->getImplementationControl() == + ObjCImplementationControl::Required; + } + void markAs(MethodPresenceKind Kind) { PresenceKind |= Kind; } + bool is(MethodPresenceKind Kind) const { + return (PresenceKind & Kind) == Kind; + } + }; + +private: + llvm::DenseMap InstanceMethods; + llvm::DenseMap ClassMethods; + + void markMethodsFrom(const ObjCContainerDecl *Container, + MethodInfo::MethodPresenceKind Kind) { + for (const ObjCMethodDecl *M : Container->methods()) { + auto &Map = M->isInstanceMethod() ? InstanceMethods : ClassMethods; + auto It = Map.find(M->getSelector()); + if (It != Map.end()) { + It->second.markAs(Kind); + if (!It->second.DeclaredOrImplementedMethod) + It->second.DeclaredOrImplementedMethod = M; + } + } + } + +public: + MethodSet() {} + MethodSet(MethodSet &&Other) = default; + MethodSet &operator=(MethodSet &&Other) = default; + + void gatherMethodsFrom(const ObjCProtocolDecl *P, int Priority) { + for (const ObjCMethodDecl *M : P->methods()) { + if (M->isImplicit()) + continue; + AvailabilityResult Availability = M->getAvailability(); + // Methods that are unavailable or not yet introduced are not considered + // to be required. + if (Availability == AR_NotYetIntroduced || Availability == AR_Unavailable) + continue; + auto &Map = M->isInstanceMethod() ? InstanceMethods : ClassMethods; + MethodInfo MI(M, P, Priority); + auto [I, Inserted] = Map.insert(std::make_pair(M->getSelector(), MI)); + if (Inserted) + continue; + + // Prefer a required method so we do not miss fixits, and the lowest + // priority so the order of fixits will be deterministic. + if (MI.isRequired() != I->second.isRequired()) { + if (MI.isRequired()) + I->second = MI; + continue; + } + if (MI.ProtocolPriority < I->second.ProtocolPriority) + I->second = MI; + } + } + + void markImplementedMethods(const ObjCContainerDecl *Container) { + assert(isa(Container) && "Not an implementation container"); + markMethodsFrom(Container, MethodInfo::IsImplemented); + if (const auto *ID = dyn_cast(Container)) { + const auto *I = ID->getClassInterface(); + // Mark declarations from super-classes as implemented to prevent + // redundant implementations. + while ((I = I->getSuperClass())) + markMethodsFrom(I, MethodInfo::IsImplemented); + } + } + + void markDeclaredMethods(const ObjCContainerDecl *Container) { + assert(!isa(Container) && "Not an interface container"); + markMethodsFrom(Container, MethodInfo::IsDeclared); + // Mark declarations from super-classes as declared to prevent redundant + // declarations. + if (const auto *I = dyn_cast(Container)) { + while ((I = I->getSuperClass())) + markMethodsFrom(I, MethodInfo::IsDeclared); + } + } + + /// Returns true if the given container has missing @required method stubs. + /// + /// For @interfaces, this method returns true when the interface is missing + /// a declaration for any @required method in all of the protocols. + /// For @implementations, this method returns true when the implementation is + /// missing an implementation of any @required method in all of the protocols. + bool hasMissingRequiredMethodStubs(const ObjCContainerDecl *Container) { + MethodInfo::MethodPresenceKind Kind = isa(Container) + ? MethodInfo::IsImplemented + : MethodInfo::IsDeclared; + for (const auto &I : InstanceMethods) { + if (!I.second.isRequired()) + continue; + if (!I.second.is(Kind)) + return true; + } + for (const auto &I : ClassMethods) { + if (!I.second.isRequired()) + continue; + if (!I.second.is(Kind)) + return true; + } + return false; + } + + std::vector + getMissingRequiredMethods(const ObjCContainerDecl *Container) { + MethodInfo::MethodPresenceKind Kind = isa(Container) + ? MethodInfo::IsImplemented + : MethodInfo::IsDeclared; + std::vector Results; + for (const auto &I : InstanceMethods) { + if (!I.second.isRequired()) + continue; + if (!I.second.is(Kind)) + Results.push_back(I.second); + } + for (const auto &I : ClassMethods) { + if (!I.second.isRequired()) + continue; + if (!I.second.is(Kind)) + Results.push_back(I.second); + } + return Results; + } + + SourceLocation findLocationForInsertionForMethodsFromProtocol( + const ObjCProtocolDecl *P, const ObjCContainerDecl *Container, + const SourceManager &SM, const LangOptions &LangOpts) { + MethodInfo::MethodPresenceKind Kind = isa(Container) + ? MethodInfo::IsImplemented + : MethodInfo::IsDeclared; + llvm::SmallVector MethodsFromProtocolInContainer; + for (const ObjCMethodDecl *M : P->methods()) { + if (M->isImplicit()) + continue; + const auto &Map = M->isInstanceMethod() ? InstanceMethods : ClassMethods; + auto It = Map.find(M->getSelector()); + if (It == Map.end()) + continue; + if (!It->second.is(Kind)) + continue; + const ObjCMethodDecl *ContainerMethod = + It->second.DeclaredOrImplementedMethod; + // Ignore method declarations from superclasses. + if (ContainerMethod->getLexicalDeclContext() != Container) + continue; + // This is a method from the given protocol that either declared or + // implemented in the container. + MethodsFromProtocolInContainer.push_back(ContainerMethod); + } + // Find the appropriate source locations by looking + if (MethodsFromProtocolInContainer.empty()) + return SourceLocation(); + SourceLocation Loc = MethodsFromProtocolInContainer[0]->getEndLoc(); + if (Loc.isMacroID()) + Loc = SM.getExpansionRange(Loc).getEnd(); + for (const ObjCMethodDecl *M : + ArrayRef(MethodsFromProtocolInContainer).drop_front()) { + SourceLocation EndLoc = M->getEndLoc(); + if (EndLoc.isMacroID()) + EndLoc = SM.getExpansionRange(EndLoc).getEnd(); + if (SM.isBeforeInTranslationUnit(Loc, EndLoc)) + Loc = EndLoc; + } + return getLastLineLocationUnlessItHasOtherTokens(Loc, SM, LangOpts); + } +}; + +} // end anonymous namespace + +namespace clang { +namespace edit { +namespace fillInMissingProtocolStubs { + +class FillInMissingProtocolStubsImpl { +public: + const ObjCContainerDecl *Container; + MethodSet Methods; +}; + +} // end namespace fillInMissingProtocolStubsImpl +} // end namespace edit +} // end namespace clang + +static void gatherProtocols( + llvm::iterator_range::iterator> Protocols, + NSAPI &API, ProtocolMapTy &Result, int &Priority) { + for (const ObjCProtocolDecl *P : Protocols) { + // Ignore the 'NSObject' protocol. + if (API.getNSClassId(NSAPI::ClassId_NSObject) == P->getIdentifier()) + continue; + gatherProtocols(P->protocols(), API, Result, Priority); + Result.insert(std::make_pair(P, ProtocolInfo{Priority++})); + } +} + +static ProtocolMapTy +gatherSuitableClassProtocols(const ObjCInterfaceDecl *I, + const ObjCContainerDecl *Container, NSAPI &API) { + ProtocolMapTy Result; + // The class of interest should use the protocols from extensions when the + // operation is initiated from the @implementation / extension. + auto ClassProtocols = + Container == I ? I->protocols() : I->all_referenced_protocols(); + int Priority = 0; + gatherProtocols(ClassProtocols, API, Result, Priority); + while ((I = I->getSuperClass())) + gatherProtocols(I->protocols(), API, Result, Priority); + return Result; +} + +static const ObjCContainerDecl * +getInterfaceOrCategory(const ObjCContainerDecl *Container) { + if (const auto *Impl = dyn_cast(Container)) + return Impl->getClassInterface(); + if (const auto *CategoryImpl = dyn_cast(Container)) + return CategoryImpl->getCategoryDecl(); + return Container; +} + +static bool initiate(FillInMissingProtocolStubsImpl &Dest, ASTContext &Context, + const ObjCContainerDecl *Container) { + const ObjCContainerDecl *ContainerProtocolSource = + getInterfaceOrCategory(Container); + if (!ContainerProtocolSource) + return false; + + // The protocols that are specified in the @interface and/or in the + // superclasses. + ProtocolMapTy Protocols; + NSAPI API(Context); + if (const auto *I = dyn_cast(ContainerProtocolSource)) { + if (!I->hasDefinition()) + return false; + Protocols = gatherSuitableClassProtocols(I, Container, API); + if (Protocols.empty()) + return false; + } else if (const auto *I = + dyn_cast(ContainerProtocolSource)) { + int Priority = 0; + gatherProtocols(I->protocols(), API, Protocols, Priority); + if (Protocols.empty()) + return false; + } + + // Check if there are missing @required methods. + for (const auto &P : Protocols) + Dest.Methods.gatherMethodsFrom(P.first, P.second.Priority); + if (isa(Container)) + Dest.Methods.markImplementedMethods(Container); + else + Dest.Methods.markDeclaredMethods(Container); + + Dest.Container = Container; + return true; +} + +FillInMissingProtocolStubs::FillInMissingProtocolStubs() {} +FillInMissingProtocolStubs::~FillInMissingProtocolStubs() {} +FillInMissingProtocolStubs::FillInMissingProtocolStubs( + FillInMissingProtocolStubs &&Other) + : Impl(std::move(Other.Impl)) {} +FillInMissingProtocolStubs &FillInMissingProtocolStubs:: +operator=(FillInMissingProtocolStubs &&Other) { + Impl = std::move(Other.Impl); + return *this; +} + +bool FillInMissingProtocolStubs::initiate(ASTContext &Context, + const ObjCContainerDecl *Container) { + Impl = std::make_unique(); + if (!::initiate(*Impl, Context, Container)) + return true; + return false; +} + +bool FillInMissingProtocolStubs::hasMissingRequiredMethodStubs() { + return Impl->Methods.hasMissingRequiredMethodStubs(Impl->Container); +} + +static void perform(MethodSet &Methods, const ObjCContainerDecl *Container, + ASTContext &Context, + llvm::function_ref Consumer) { + auto MissingMethods = Methods.getMissingRequiredMethods(Container); + // Sort the methods by grouping them into protocol clusters and then sorting + // them alphabetically within the same protocol. + std::sort(MissingMethods.begin(), MissingMethods.end(), + [](const MethodSet::MethodInfo &A, const MethodSet::MethodInfo &B) { + if (A.ProtocolPriority == B.ProtocolPriority) + return A.M->getSelector().getAsString() < + B.M->getSelector().getAsString(); + assert(A.P != B.P && "Same protocols should have same priority"); + return A.ProtocolPriority < B.ProtocolPriority; + }); + + SourceLocation InsertionLoc = + isa(Container) + ? Container->getEndLoc() + : getLocationOfPrecedingToken(Container->getEndLoc(), + Context.getSourceManager(), + Context.getLangOpts()); + if (InsertionLoc.isInvalid()) + InsertionLoc = Container->getEndLoc(); + + PrintingPolicy PP = Context.getPrintingPolicy(); + PP.PolishForDeclaration = true; + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + + std::string EndInsertionOSStr; + llvm::raw_string_ostream EndInsertionOS(EndInsertionOSStr); + + std::string InsertionGroupStr; + llvm::raw_string_ostream InsertionGroupOS(InsertionGroupStr); + + const ObjCProtocolDecl *CurrentProtocol = nullptr; + SourceLocation CurrentProtocolInsertionLoc; + bool IsImplementation = isa(Container); + for (const auto &Method : MissingMethods) { + const ObjCProtocolDecl *P = Method.P; + if (CurrentProtocol != P) { + if (!InsertionGroupOS.str().empty()) { + assert(CurrentProtocolInsertionLoc.isValid()); + Consumer(FixItHint::CreateInsertion(CurrentProtocolInsertionLoc, + InsertionGroupOS.str())); + } + InsertionGroupStr.clear(); + CurrentProtocol = P; + CurrentProtocolInsertionLoc = + Methods.findLocationForInsertionForMethodsFromProtocol( + P, Container, Context.getSourceManager(), Context.getLangOpts()); + } + bool IsInsertingAfterRelatedMethods = CurrentProtocolInsertionLoc.isValid(); + raw_ostream &OS = + IsInsertingAfterRelatedMethods ? InsertionGroupOS : EndInsertionOS; + + std::string MethodDeclStr; + llvm::raw_string_ostream MethodOS(MethodDeclStr); + Method.M->print(MethodOS, PP); + if (IsInsertingAfterRelatedMethods) + OS << "\n\n"; + OS << StringRef(MethodOS.str()).drop_back(); // Drop the ';' + if (IsImplementation) + OS << " { \n <#code#>\n}\n"; + else + OS << ";\n"; + if (!IsInsertingAfterRelatedMethods) + OS << "\n"; + } + if (!InsertionGroupOS.str().empty()) { + assert(CurrentProtocolInsertionLoc.isValid()); + Consumer(FixItHint::CreateInsertion(CurrentProtocolInsertionLoc, + InsertionGroupOS.str())); + } + if (!EndInsertionOS.str().empty()) + Consumer(FixItHint::CreateInsertion(InsertionLoc, EndInsertionOS.str())); +} + +void FillInMissingProtocolStubs::perform( + ASTContext &Context, llvm::function_ref Consumer) { + ::perform(Impl->Methods, Impl->Container, Context, Consumer); +} + +void fillInMissingProtocolStubs::addMissingProtocolStubs( + ASTContext &Context, const ObjCContainerDecl *Container, + llvm::function_ref Consumer) { + FillInMissingProtocolStubsImpl Impl; + if (initiate(Impl, Context, Container)) + perform(Impl.Methods, Impl.Container, Context, Consumer); +} diff --git a/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp new file mode 100644 index 0000000000000..37094532ace8d --- /dev/null +++ b/clang/lib/Edit/FillInMissingSwitchEnumCases.cpp @@ -0,0 +1,192 @@ +//===--- FillInMissingSwitchEnumCases.cpp - ------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Edit/RefactoringFixits.h" +#include + +using namespace clang; + +namespace { + +struct CaseInfo { + const SwitchCase *Case, *NextCase; + unsigned Index; +}; +typedef std::unordered_map CoveredEnumCasesInfoType; + +/// Return true if the ordering of the covered enum cases is similar to the +/// order of the enum case constants that are defined in the enum. +bool useCaseBasedOrdering(const ArrayRef &CoveredEnumCaseValues, + const CoveredEnumCasesInfoType &CoveredEnumCases) { + if (CoveredEnumCaseValues.empty()) + return false; + for (const auto &I : llvm::enumerate(CoveredEnumCaseValues)) { + auto It = CoveredEnumCases.find(I.value()); + if (It == CoveredEnumCases.end()) + return false; + const CaseInfo &Case = It->second; + if (Case.Index != I.index()) + return false; + } + return true; +} + +/// Determine if the inserted cases should be wrapped in braces using a simple +/// heuristic: +/// Wrap only if at least 90% of existing cases use braces. +bool useBraces(const SwitchStmt *S) { + unsigned CaseCount = 0, CompoundCasesCount = 0; + for (const SwitchCase *Case = S->getSwitchCaseList(); Case; + Case = Case->getNextSwitchCase(), ++CaseCount) { + if (!Case->getSubStmt()) + continue; + if (isa(Case->getSubStmt())) + ++CompoundCasesCount; + } + return CaseCount && float(CompoundCasesCount) / float(CaseCount) >= 0.9; +} + +} // end anonymous namespace + +void edit::fillInMissingSwitchEnumCases( + ASTContext &Context, const SwitchStmt *Switch, const EnumDecl *Enum, + const DeclContext *SwitchContext, + llvm::function_ref Consumer) { + // Compute the number of cases in the switch. + unsigned CaseCount = 0; + for (const SwitchCase *Case = Switch->getSwitchCaseList(); Case; + Case = Case->getNextSwitchCase()) + ++CaseCount; + + // Compute the set of enum values that are covered by the switch. + CoveredEnumCasesInfoType CoveredEnumCases; + const SwitchCase *DefaultCase = nullptr; + const SwitchCase *FirstCoveredEnumCase = nullptr; + const SwitchCase *NextCase = nullptr; + unsigned CaseIndex = CaseCount; + for (const SwitchCase *Case = Switch->getSwitchCaseList(); Case; + NextCase = Case, Case = Case->getNextSwitchCase()) { + // The cases in the switch are ordered back to front, so the index has + // to be reversed. + --CaseIndex; + if (isa(Case)) { + DefaultCase = Case; + continue; + } + const auto *CS = cast(Case); + if (const auto *LHS = CS->getLHS()) { + Expr::EvalResult Result; + if (!LHS->EvaluateAsInt(Result, Context)) + continue; + // Only allow constant that fix into 64 bits. + if (Result.Val.getInt().getSignificantBits() > 64) + continue; + CoveredEnumCases[Result.Val.getInt().getSExtValue()] = + CaseInfo{Case, NextCase, CaseIndex}; + // The cases in the switch are ordered back to front, so the last + // case is actually the first enum case in the switch. + FirstCoveredEnumCase = Case; + } + } + + // Wrap the inserted cases in braces using a simple heuristic: + // Wrap only if at least 90% of existing cases use braces. + bool WrapInBraces = useBraces(Switch); + auto CreateReplacementForMissingCaseGroup = + [&](ArrayRef UncoveredEnumCases, + SourceLocation InsertionLoc = SourceLocation()) { + if (UncoveredEnumCases.empty()) + return; + std::string Result; + llvm::raw_string_ostream OS(Result); + for (const auto *EnumCase : UncoveredEnumCases) { + OS << "case "; + if (SwitchContext) { + const auto *NS = NestedNameSpecifier::getRequiredQualification( + Context, SwitchContext, Enum->getLexicalDeclContext()); + if (NS) + NS->print(OS, Context.getPrintingPolicy()); + } + if (Enum->isScoped()) + OS << Enum->getName() << "::"; + OS << EnumCase->getName() << ":"; + if (WrapInBraces) + OS << " {"; + OS << "\n<#code#>\nbreak;\n"; + if (WrapInBraces) + OS << "}\n"; + } + + if (InsertionLoc.isInvalid()) { + // Insert the cases before the 'default' if it's the last case in the + // switch. + // Note: Switch cases are ordered back to front, so the last default + // case would be the first case in the switch statement. + if (DefaultCase && DefaultCase == Switch->getSwitchCaseList()) + InsertionLoc = DefaultCase->getBeginLoc(); + else + InsertionLoc = Switch->getBody()->getEndLoc(); + } + Consumer(FixItHint::CreateInsertion( + Context.getSourceManager().getSpellingLoc(InsertionLoc), OS.str())); + }; + + // Determine which enum cases are uncovered. + + llvm::SmallVector, 8> EnumCases; + llvm::SmallVector CoveredEnumCaseValues; + for (const auto *EnumCase : Enum->enumerators()) { + if (EnumCase->getInitVal().getSignificantBits() > 64) + continue; + int64_t Value = EnumCase->getInitVal().getSExtValue(); + EnumCases.push_back(std::make_pair(EnumCase, Value)); + if (CoveredEnumCases.count(Value)) + CoveredEnumCaseValues.push_back(Value); + } + + llvm::SmallVector UncoveredEnumCases; + // Figure out if the ordering of the covered enum cases is similar to the + // order of enum case values defined in the enum. + if (useCaseBasedOrdering(CoveredEnumCaseValues, CoveredEnumCases)) { + // Start inserting before the first covered case. + SourceLocation InsertionLoc = FirstCoveredEnumCase->getBeginLoc(); + + for (const auto &EnumCase : EnumCases) { + if (!CoveredEnumCases.count(EnumCase.second)) { + UncoveredEnumCases.push_back(EnumCase.first); + continue; + } + // Create the insertion source replacement for this set of uncovered + // cases. + CreateReplacementForMissingCaseGroup(UncoveredEnumCases, InsertionLoc); + UncoveredEnumCases.clear(); + // Find the insertion location for the next set of uncovered cases. + auto It = CoveredEnumCases.find(EnumCase.second); + assert(It != CoveredEnumCases.end() && "Missing enum case"); + const CaseInfo &Case = It->second; + InsertionLoc = Case.NextCase ? Case.NextCase->getBeginLoc() + : /*Insert before end*/ SourceLocation(); + } + CreateReplacementForMissingCaseGroup(UncoveredEnumCases, InsertionLoc); + } else { + // Gather all of the uncovered enum cases. + for (const auto &EnumCase : EnumCases) { + if (!CoveredEnumCases.count(EnumCase.second)) + UncoveredEnumCases.push_back(EnumCase.first); + } + assert(!UncoveredEnumCases.empty() && + "Can't fill-in enum cases in a full switch"); + CreateReplacementForMissingCaseGroup(UncoveredEnumCases); + } +} diff --git a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp index 627a1d6fb3dd5..7371ff6582d16 100644 --- a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp +++ b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp @@ -1081,6 +1081,11 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, case CK_MatrixCast: return false; + /* TO_UPSTREAM(BoundsSafety) ON */ + case CK_BoundsSafetyPointerCast: + llvm_unreachable("BoundsSafety extension is disabled for Objective-C"); + /* TO_UPSTREAM(BoundsSafety) OFF */ + case CK_BooleanToSignedIntegral: llvm_unreachable("OpenCL-specific cast in Objective-C?"); diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp index e05385d119870..aefdade165c84 100644 --- a/clang/lib/Frontend/ASTUnit.cpp +++ b/clang/lib/Frontend/ASTUnit.cpp @@ -1825,6 +1825,9 @@ std::unique_ptr ASTUnit::LoadFromCommandLine( if (ModuleFormat) CI->getHeaderSearchOpts().ModuleFormat = std::string(*ModuleFormat); + if (ForSerialization) + CI->getLangOpts().NeededByPCHOrCompilationUsesPCH = true; + // Create the AST unit. std::unique_ptr AST; AST.reset(new ASTUnit(false)); diff --git a/clang/lib/Frontend/CASDependencyCollector.cpp b/clang/lib/Frontend/CASDependencyCollector.cpp new file mode 100644 index 0000000000000..601b63f8a09c0 --- /dev/null +++ b/clang/lib/Frontend/CASDependencyCollector.cpp @@ -0,0 +1,83 @@ +//===--- CASDependencyCollector.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CASDependencyCollector.h" +#include "clang/Basic/DiagnosticCAS.h" +#include "llvm/CAS/CASOutputBackend.h" +#include "llvm/CAS/ObjectStore.h" +#include "llvm/Support/VirtualOutputBackends.h" + +using namespace clang; +using namespace clang::cas; + +/// \returns \p DependencyOutputOptions but with \p ExtraDeps cleared out. +/// +/// This is useful to avoid recording these filenames in the CAS. +static DependencyOutputOptions dropExtraDeps(DependencyOutputOptions Opts) { + Opts.ExtraDeps.clear(); + return Opts; +} + +CASDependencyCollector::CASDependencyCollector( + DependencyOutputOptions Opts, cas::ObjectStore &CAS, + std::function)> Callback) + : DependencyFileGenerator(dropExtraDeps(std::move(Opts)), + llvm::vfs::makeNullOutputBackend()), + CAS(CAS), Callback(std::move(Callback)) {} + +llvm::Error CASDependencyCollector::replay(const DependencyOutputOptions &Opts, + ObjectStore &CAS, ObjectProxy Refs, + llvm::raw_ostream &OS) { + CASDependencyCollector DC(Opts, CAS, nullptr); + + // Add the filenames from DependencyOutputOptions::ExtraDeps. These are kept + // out of the compilation cache key since they can be passed-in and added at + // the point where the dependency file is generated, without needing to affect + // the cached compilation. + for (const std::pair &Dep : Opts.ExtraDeps) { + DC.addDependency(Dep.first); + } + + auto Err = Refs.forEachReference([&](ObjectRef Ref) -> llvm::Error { + auto PathHandle = CAS.getProxy(Ref); + if (!PathHandle) + return PathHandle.takeError(); + StringRef Path = PathHandle->getData(); + // This assumes the replay has the same filtering options as when it was + // originally computed. That avoids needing to store many unnecessary paths. + // FIXME: if a prefix map is enabled, we should remap the paths to the + // invocation's environment. + DC.addDependency(Path); + return llvm::Error::success(); + }); + if (Err) + return Err; + + DC.outputDependencyFile(OS); + return llvm::Error::success(); +} + +void CASDependencyCollector::finishedMainFile(DiagnosticsEngine &Diags) { + ArrayRef Files = getDependencies(); + std::vector Refs; + Refs.reserve(Files.size()); + for (StringRef File : Files) { + auto Handle = CAS.storeFromString({}, File); + if (!Handle) { + Diags.Report({}, diag::err_cas_store) << toString(Handle.takeError()); + return; + } + Refs.push_back(*Handle); + } + + auto Handle = CAS.store(Refs, {}); + if (Handle) + Callback(*Handle); + else + Diags.Report({}, diag::err_cas_store) << toString(Handle.takeError()); +} diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt index a916667208845..4831919f5c50a 100644 --- a/clang/lib/Frontend/CMakeLists.txt +++ b/clang/lib/Frontend/CMakeLists.txt @@ -3,8 +3,12 @@ add_subdirectory(Rewrite) set(LLVM_LINK_COMPONENTS BitReader BitstreamReader + CAS + CASUtil + MCCAS Option ProfileData + RemoteCachingService Support TargetParser ) @@ -13,8 +17,13 @@ add_clang_library(clangFrontend ASTConsumers.cpp ASTMerge.cpp ASTUnit.cpp + CachedDiagnostics.cpp + CASDependencyCollector.cpp ChainedDiagnosticConsumer.cpp ChainedIncludesSource.cpp + CompileJobCache.cpp + CompileJobCacheKey.cpp + CompileJobCacheResult.cpp CompilerInstance.cpp CompilerInvocation.cpp CreateInvocationFromCommandLine.cpp @@ -25,6 +34,7 @@ add_clang_library(clangFrontend FrontendActions.cpp FrontendOptions.cpp HeaderIncludeGen.cpp + IncludeTreePPActions.cpp InitPreprocessor.cpp LayoutOverrideSource.cpp LogDiagnosticPrinter.cpp @@ -56,5 +66,6 @@ add_clang_library(clangFrontend clangLex clangParse clangSema + clangCAS clangSerialization ) diff --git a/clang/lib/Frontend/CachedDiagnostics.cpp b/clang/lib/Frontend/CachedDiagnostics.cpp new file mode 100644 index 0000000000000..194cb2137751f --- /dev/null +++ b/clang/lib/Frontend/CachedDiagnostics.cpp @@ -0,0 +1,868 @@ +//===- CachedDiagnostics.h - Serializing diagnostics for caching ----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is a mechanism for caching diagnostics with full source location +// fidelity. It serializes the subset of SourceManager state for the diagnostic +// source locations (FileIDs, macro expansions, etc.) and replays diagnostics by +// materializing their SourceLocations; that way the diagnostic consumers have +// full access to the include stack, expanded macros, etc. +// +// This allows us to only cache the diagnostic information that is essential for +// the compilation and ignore the various ways that diagnostics can be rendered. +// +//===----------------------------------------------------------------------===// + +#include "CachedDiagnostics.h" +#include "clang/Basic/DiagnosticCAS.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/Base64.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/YAMLTraits.h" +#include + +using namespace clang; +using namespace clang::cas; +using namespace llvm; + +namespace { + +namespace cached_diagnostics { + +struct Location { + unsigned SLocIdx; + unsigned Offset; +}; + +struct Range { + std::optional Begin; + std::optional End; + bool IsTokenRange; +}; + +struct FixItHint { + std::optional RemoveRange; + std::optional InsertFromRange; + std::string CodeToInsert; + bool BeforePreviousInsertions; +}; + +struct SLocEntry { + struct FileInfo { + std::string Filename; + // Using \c shared_ptr instead of \c unique_ptr to accomodate the YAML + // [de]serialization functions. + std::shared_ptr Buffer; + std::optional IncludeLoc; + + // Only used during compilation. + bool IsScratchBuffer = false; + // If this is for a scratch buffer, records the highest offset that a + // converted location resolved inside it. Only used during compilation. + unsigned HighestScratchBufferOffset = 0; + }; + + struct ExpansionInfo { + std::optional SpellingLoc; + std::optional ExpansionStartLoc; + std::optional ExpansionEndLoc; + unsigned Length; + bool IsTokenRange; + }; + + std::variant Data = FileInfo(); + + bool isFileInfo() const { return std::holds_alternative(Data); } + + FileInfo &getAsFileInfo() { + assert(isFileInfo()); + return std::get(Data); + } + const FileInfo &getAsFileInfo() const { + assert(isFileInfo()); + return std::get(Data); + } + + ExpansionInfo &getAsExpansionInfo() { + assert(!isFileInfo()); + return std::get(Data); + } + const ExpansionInfo &getAsExpansionInfo() const { + assert(!isFileInfo()); + return std::get(Data); + } +}; + +struct Diagnostic { + unsigned ID; + DiagnosticsEngine::Level Level; + std::string Message; + std::optional Loc; + std::vector Ranges; + std::vector FixIts; +}; + +struct CustomDiagDesc { + diag::Severity DefaultSeverity; + DiagnosticIDs::Class DiagClass; + bool ShowInSystemHeader; + bool ShowInSystemMacro; + std::string Description; + std::optional Group; + CustomDiagDesc() = default; + CustomDiagDesc(const DiagnosticIDs::CustomDiagDesc &Desc) + : DefaultSeverity(Desc.GetDefaultSeverity()), DiagClass(Desc.GetClass()), + ShowInSystemHeader(Desc.ShouldShowInSystemHeader()), + ShowInSystemMacro(Desc.ShouldShowInSystemMacro()), + Description(Desc.GetDescription()), Group(Desc.GetGroup()) {} + + DiagnosticIDs::CustomDiagDesc getDesc() const { + return DiagnosticIDs::CustomDiagDesc(DefaultSeverity, Description, + DiagClass, ShowInSystemHeader, + ShowInSystemMacro, Group); + } +}; + +struct Diagnostics { + std::vector SLocEntries; + std::vector Diags; + std::vector CustomDiags; + + size_t getNumDiags() const { return Diags.size(); } + + void clear() { + SLocEntries.clear(); + Diags.clear(); + CustomDiags.clear(); + } +}; + +} // namespace cached_diagnostics + +/// Converts diagnostics from/to the \p cached_diagnostics structures, and +/// de/serializes diagnostics from/to a buffer. It "materializes" source +/// locations using its own \p SourceManager. +struct CachedDiagnosticSerializer { + PrefixMapper &Mapper; + DiagnosticsEngine DiagEngine; + SourceManager SourceMgr; + /// Diagnostics either emitted during compilation or deserialized from a + /// buffer. + cached_diagnostics::Diagnostics CachedDiags; + /// Associates a \p FileID with the index of a + /// \p cached_diagnostics::SLocEntry object. + SmallDenseMap FileIDToCachedSLocIdx; + /// Associates the indices of \p cached_diagnostics::SLocEntry objects with + /// their "materialized" \p FileID. + SmallVector FileIDBySlocIdx; + /// Keeps track of \p MemoryBuffers for ownership purposes. + SmallVector> FileBuffers; + BumpPtrAllocator Alloc; + StringSaver Saver{Alloc}; + + CachedDiagnosticSerializer(PrefixMapper &Mapper, FileManager &FileMgr) + : Mapper(Mapper), + DiagEngine(new DiagnosticIDs(), new DiagnosticOptions()), + SourceMgr(DiagEngine, FileMgr) {} + + size_t getNumDiags() const { return CachedDiags.getNumDiags(); } + bool empty() const { return getNumDiags() == 0; } + void clear() { CachedDiags.clear(); } + + /// \returns the index of the created \p cached_diagnostics::Diagnostic + /// object. + unsigned addDiag(const StoredDiagnostic &Diag); + StoredDiagnostic getDiag(unsigned Idx); + + std::optional + convertLoc(const FullSourceLoc &Loc); + FullSourceLoc convertCachedLoc( + const std::optional &CachedLoc); + + std::optional + convertRange(const CharSourceRange &Range, const SourceManager &SM); + CharSourceRange convertCachedRange( + const std::optional &CachedRange); + + cached_diagnostics::FixItHint convertFixIt(const FixItHint &FixIt, + const SourceManager &SM); + FixItHint + convertCachedFixIt(const cached_diagnostics::FixItHint &CachedFixIt); + + unsigned convertFileID(FileID FID, const SourceManager &SM); + FileID convertCachedSLocEntry(unsigned Idx); + + FileID getExistingFileIDForCachedSLocEntry(unsigned Idx) const { + if (Idx >= FileIDBySlocIdx.size()) + return FileID(); + return FileIDBySlocIdx[Idx]; + } + + /// \returns a serialized buffer of the currently recorded + /// \p cached_diagnostics::Diagnostics, or \p std::nullopt if there's no + /// diagnostic. The buffer can be passed to \p deserializeCachedDiagnostics to + /// get back the same diagnostics. + /// + /// There is no stability guarantee for the format of the buffer, the + /// expectation is that the buffer will be deserialized only by the same + /// compiler version that produced it. The format can change without + /// restrictions. + /// + /// The intended use is as implementation detail of compilation caching, where + /// the diagnostic output is associated with a compilation cache key. A + /// different compiler version will create different cache keys, which ensures + /// that the diagnostics buffer will only be read by the same compiler that + /// produced it. + std::optional serializeEmittedDiagnostics(); + Error deserializeCachedDiagnostics(StringRef Buffer); + + /// Capture any custom diagnostics registerd by \p Diags so that they can be + /// later serialized. + void captureCustomDiags(const DiagnosticsEngine &Diags); +}; + +} // anonymous namespace + +unsigned CachedDiagnosticSerializer::addDiag(const StoredDiagnostic &Diag) { + cached_diagnostics::Diagnostic CachedDiag; + CachedDiag.ID = Diag.getID(); + CachedDiag.Level = Diag.getLevel(); + CachedDiag.Message = Diag.getMessage(); + CachedDiag.Loc = convertLoc(Diag.getLocation()); + if (Diag.getLocation().isValid()) { + const SourceManager &SM = Diag.getLocation().getManager(); + for (const CharSourceRange &Range : Diag.getRanges()) { + if (std::optional CachedRange = + convertRange(Range, SM)) + CachedDiag.Ranges.push_back(std::move(*CachedRange)); + } + for (const FixItHint &FixIt : Diag.getFixIts()) { + CachedDiag.FixIts.push_back(convertFixIt(FixIt, SM)); + } + } + + unsigned Idx = CachedDiags.Diags.size(); + CachedDiags.Diags.push_back(std::move(CachedDiag)); + return Idx; +} + +StoredDiagnostic CachedDiagnosticSerializer::getDiag(unsigned Idx) { + assert(Idx < getNumDiags()); + const cached_diagnostics::Diagnostic &CachedDiag = CachedDiags.Diags[Idx]; + FullSourceLoc Loc = convertCachedLoc(CachedDiag.Loc); + SmallVector Ranges; + for (const cached_diagnostics::Range &CachedRange : CachedDiag.Ranges) { + Ranges.push_back(convertCachedRange(CachedRange)); + } + SmallVector FixIts; + for (const cached_diagnostics::FixItHint &CachedFixIt : CachedDiag.FixIts) { + FixIts.push_back(convertCachedFixIt(CachedFixIt)); + } + + StoredDiagnostic Diag(CachedDiag.Level, CachedDiag.ID, CachedDiag.Message, + Loc, Ranges, FixIts); + return Diag; +} + +std::optional +CachedDiagnosticSerializer::convertLoc(const FullSourceLoc &Loc) { + if (Loc.isInvalid()) + return std::nullopt; + + FileID FID; + unsigned Offset; + std::tie(FID, Offset) = Loc.getDecomposedLoc(); + cached_diagnostics::Location CachedLoc; + CachedLoc.SLocIdx = convertFileID(FID, Loc.getManager()); + CachedLoc.Offset = Offset; + + auto &SLocEntry = CachedDiags.SLocEntries[CachedLoc.SLocIdx]; + if (SLocEntry.isFileInfo()) { + auto &FI = SLocEntry.getAsFileInfo(); + if (FI.IsScratchBuffer) { + if (FI.HighestScratchBufferOffset < Offset) + FI.HighestScratchBufferOffset = Offset; + FileID CachedScratchFID = + getExistingFileIDForCachedSLocEntry(CachedLoc.SLocIdx); + if (CachedScratchFID.isValid()) { + // Since the scratch buffer updates while parsing, use the line cache + // from the compilation's source manager, to make sure it is up-to-date. + auto LineMap = Loc.getManager() + .getSLocEntry(FID) + .getFile() + .getContentCache() + .SourceLineCache; + SourceMgr.getSLocEntry(CachedScratchFID) + .getFile() + .getContentCache() + .SourceLineCache = LineMap; + } + } + } + + return CachedLoc; +} + +FullSourceLoc CachedDiagnosticSerializer::convertCachedLoc( + const std::optional &CachedLoc) { + if (!CachedLoc) + return FullSourceLoc(); + + FileID FID = convertCachedSLocEntry(CachedLoc->SLocIdx); + SourceLocation Loc = SourceMgr.getComposedLoc(FID, CachedLoc->Offset); + return FullSourceLoc(Loc, SourceMgr); +} + +std::optional +CachedDiagnosticSerializer::convertRange(const CharSourceRange &Range, + const SourceManager &SM) { + if (Range.isInvalid()) + return std::nullopt; + + cached_diagnostics::Range CachedRange; + CachedRange.Begin = convertLoc(FullSourceLoc(Range.getBegin(), SM)); + CachedRange.End = convertLoc(FullSourceLoc(Range.getEnd(), SM)); + CachedRange.IsTokenRange = Range.isTokenRange(); + return CachedRange; +} + +CharSourceRange CachedDiagnosticSerializer::convertCachedRange( + const std::optional &CachedRange) { + if (!CachedRange) + return CharSourceRange(); + + FullSourceLoc Begin = convertCachedLoc(CachedRange->Begin); + FullSourceLoc End = convertCachedLoc(CachedRange->End); + return CharSourceRange(SourceRange(Begin, End), CachedRange->IsTokenRange); +} + +cached_diagnostics::FixItHint +CachedDiagnosticSerializer::convertFixIt(const FixItHint &FixIt, + const SourceManager &SM) { + cached_diagnostics::FixItHint CachedFixIt; + CachedFixIt.RemoveRange = convertRange(FixIt.RemoveRange, SM); + CachedFixIt.InsertFromRange = convertRange(FixIt.InsertFromRange, SM); + CachedFixIt.CodeToInsert = FixIt.CodeToInsert; + CachedFixIt.BeforePreviousInsertions = FixIt.BeforePreviousInsertions; + return CachedFixIt; +} + +FixItHint CachedDiagnosticSerializer::convertCachedFixIt( + const cached_diagnostics::FixItHint &CachedFixIt) { + FixItHint FixIt; + FixIt.RemoveRange = convertCachedRange(CachedFixIt.RemoveRange); + FixIt.InsertFromRange = convertCachedRange(CachedFixIt.InsertFromRange); + FixIt.CodeToInsert = CachedFixIt.CodeToInsert; + FixIt.BeforePreviousInsertions = CachedFixIt.BeforePreviousInsertions; + return FixIt; +} + +unsigned CachedDiagnosticSerializer::convertFileID(FileID FID, + const SourceManager &SM) { + auto Found = FileIDToCachedSLocIdx.find(FID); + if (Found != FileIDToCachedSLocIdx.end()) + return Found->second; + + cached_diagnostics::SLocEntry CachedEntry; + const SrcMgr::SLocEntry &Entry = SM.getSLocEntry(FID); + if (Entry.isFile()) { + const SrcMgr::FileInfo &FI = Entry.getFile(); + cached_diagnostics::SLocEntry::FileInfo CachedFI; + if (OptionalFileEntryRef FE = FI.getContentCache().ContentsEntry) { + CachedFI.Filename = FE->getName(); + } else { + CachedFI.Filename = FI.getName(); + CachedFI.Buffer = + MemoryBuffer::getMemBuffer(*FI.getContentCache().getBufferIfLoaded()); + } + CachedFI.IncludeLoc = convertLoc(FullSourceLoc(FI.getIncludeLoc(), SM)); + CachedFI.IsScratchBuffer = + SM.isWrittenInScratchSpace(SM.getLocForStartOfFile(FID)); + CachedEntry.Data = std::move(CachedFI); + } else { + const SrcMgr::ExpansionInfo &EI = Entry.getExpansion(); + cached_diagnostics::SLocEntry::ExpansionInfo CachedEI; + CachedEI.Length = SM.getFileIDSize(FID); + CachedEI.SpellingLoc = convertLoc(FullSourceLoc(EI.getSpellingLoc(), SM)); + CachedEI.ExpansionStartLoc = + convertLoc(FullSourceLoc(EI.getExpansionLocStart(), SM)); + CachedEI.ExpansionEndLoc = + convertLoc(FullSourceLoc(EI.getUnderlyingExpansionLocEnd(), SM)); + CachedEI.IsTokenRange = EI.isExpansionTokenRange(); + CachedEntry.Data = std::move(CachedEI); + } + + unsigned Idx = CachedDiags.SLocEntries.size(); + CachedDiags.SLocEntries.push_back(std::move(CachedEntry)); + FileIDToCachedSLocIdx[FID] = Idx; + return Idx; +} + +FileID CachedDiagnosticSerializer::convertCachedSLocEntry(unsigned Idx) { + if (Idx >= FileIDBySlocIdx.size()) + FileIDBySlocIdx.resize(Idx + 1); + if (FileIDBySlocIdx[Idx].isValid()) + return FileIDBySlocIdx[Idx]; + + const cached_diagnostics::SLocEntry &CachedSLocEntry = + CachedDiags.SLocEntries[Idx]; + FileID FID; + if (CachedSLocEntry.isFileInfo()) { + const auto &FI = CachedSLocEntry.getAsFileInfo(); + FullSourceLoc IncludeLoc = convertCachedLoc(FI.IncludeLoc); + if (FI.Buffer) { + FID = SourceMgr.createFileID(FI.Buffer->getMemBufferRef(), SrcMgr::C_User, + /*LoadedID*/ 0, /*LoadedOffset*/ 0, + IncludeLoc); + } else { + auto MemBufOrErr = + SourceMgr.getFileManager().getBufferForFile(FI.Filename); + if (!MemBufOrErr) + report_fatal_error( + createFileError(FI.Filename, MemBufOrErr.getError())); + SmallString<128> PathBuf; + Mapper.map(FI.Filename, PathBuf); + if (PathBuf.str() != FI.Filename) { + // The file path was remapped. Keep the original buffer and pass a new + // buffer using the remapped file path. + FileBuffers.push_back(std::move(*MemBufOrErr)); + MemoryBufferRef NewBuffer(FileBuffers.back()->getBuffer(), + Saver.save(PathBuf.str())); + // Using \p SrcMgr::C_User as default since \p + // SrcMgr::CharacteristicKind is irrelevant for diagnostic consumers; if + // it becomes relevant we need to serialize this value as well. + FID = + SourceMgr.createFileID(NewBuffer, SrcMgr::C_User, 0, 0, IncludeLoc); + } else { + FID = SourceMgr.createFileID(std::move(*MemBufOrErr), SrcMgr::C_User, 0, + 0, IncludeLoc); + } + } + } else { + const auto &EI = CachedSLocEntry.getAsExpansionInfo(); + FullSourceLoc SpellingLoc = convertCachedLoc(EI.SpellingLoc); + FullSourceLoc ExpansionStartLoc = convertCachedLoc(EI.ExpansionStartLoc); + FullSourceLoc ExpansionEndLoc = convertCachedLoc(EI.ExpansionEndLoc); + SourceLocation ExpansionLoc = SourceMgr.createExpansionLoc( + SpellingLoc, ExpansionStartLoc, ExpansionEndLoc, EI.Length, + EI.IsTokenRange); + FID = SourceMgr.getDecomposedLoc(ExpansionLoc).first; + } + + assert(FID.isValid()); + FileIDBySlocIdx[Idx] = FID; + return FID; +} + +namespace llvm::yaml { +template <> struct MappingTraits { + static void mapping(IO &io, cached_diagnostics::SLocEntry &s) { + if (io.outputting()) { + if (s.isFileInfo()) { + io.mapRequired("file", s.getAsFileInfo()); + } else { + io.mapRequired("expansion", s.getAsExpansionInfo()); + } + } else { + std::optional FI; + io.mapOptional("file", FI); + if (FI) { + s.Data = std::move(*FI); + } else { + cached_diagnostics::SLocEntry::ExpansionInfo EI; + io.mapRequired("expansion", EI); + s.Data = std::move(EI); + } + } + } +}; + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, diag::Severity &value) { + io.enumCase(value, "ignored", diag::Severity::Ignored); + io.enumCase(value, "remark", diag::Severity::Remark); + io.enumCase(value, "warning", diag::Severity::Warning); + io.enumCase(value, "error", diag::Severity::Error); + io.enumCase(value, "fatal", diag::Severity::Fatal); + } +}; +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, DiagnosticIDs::Class &value) { + io.enumCase(value, "invalid", DiagnosticIDs::CLASS_INVALID); + io.enumCase(value, "note", DiagnosticIDs::CLASS_NOTE); + io.enumCase(value, "remark", DiagnosticIDs::CLASS_REMARK); + io.enumCase(value, "warning", DiagnosticIDs::CLASS_WARNING); + io.enumCase(value, "extension", DiagnosticIDs::CLASS_EXTENSION); + io.enumCase(value, "error", DiagnosticIDs::CLASS_ERROR); + } +}; +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, diag::Group &value) { +#define DIAG_ENTRY(GroupName, FlagNameOffset, Members, SubGroups, Docs) \ + io.enumCase(value, #GroupName, diag::Group::GroupName); +#include "clang/Basic/DiagnosticGroups.inc" +#undef CATEGORY +#undef DIAG_ENTRY + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, cached_diagnostics::CustomDiagDesc &DiagDesc) { + io.mapRequired("severity", DiagDesc.DefaultSeverity); + io.mapRequired("class", DiagDesc.DiagClass); + io.mapRequired("show_in_system_header", DiagDesc.ShowInSystemHeader); + io.mapRequired("show_in_system_macro", DiagDesc.ShowInSystemMacro); + io.mapRequired("description", DiagDesc.Description); + io.mapOptional("group", DiagDesc.Group); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, cached_diagnostics::SLocEntry::FileInfo &s) { + io.mapRequired("filename", s.Filename); + io.mapOptional("include_loc", s.IncludeLoc); + if (io.outputting()) { + if (s.Buffer) { + StringRef Contents = s.Buffer->getBuffer(); + if (s.IsScratchBuffer) { + // The allocated buffer is large (~4K) but commonly a very small part + // of that is used, so we truncate to the useful part only. + // There's a '\0' between each addition in the scratch space, look for + // the end of the highest referenced section. + size_t EndIdx = + Contents.find('\0', /*From=*/s.HighestScratchBufferOffset); + Contents = Contents.substr(0, EndIdx); + } + std::string EncodedContents = encodeBase64(Contents); + io.mapRequired("buffer", EncodedContents); + } + } else { + std::optional EncodedContents; + io.mapOptional("buffer", EncodedContents); + if (EncodedContents) { + std::vector Decoded; + cantFail(decodeBase64(*EncodedContents, Decoded)); + s.Buffer = MemoryBuffer::getMemBufferCopy( + StringRef(Decoded.data(), Decoded.size()), s.Filename); + } + } + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, cached_diagnostics::SLocEntry::ExpansionInfo &s) { + io.mapRequired("length", s.Length); + io.mapOptional("spelling_loc", s.SpellingLoc); + io.mapOptional("expansion_start_loc", s.ExpansionStartLoc); + io.mapOptional("expansion_end_loc", s.ExpansionEndLoc); + io.mapRequired("is_token_range", s.IsTokenRange); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, cached_diagnostics::Location &s) { + io.mapRequired("sloc_idx", s.SLocIdx); + io.mapRequired("offset", s.Offset); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, cached_diagnostics::Range &s) { + io.mapOptional("begin", s.Begin); + io.mapOptional("end", s.End); + io.mapRequired("is_token_range", s.IsTokenRange); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, cached_diagnostics::FixItHint &s) { + io.mapOptional("remove_range", s.RemoveRange); + io.mapOptional("insert_range", s.InsertFromRange); + io.mapOptional("code", s.CodeToInsert); + io.mapRequired("before_previous", s.BeforePreviousInsertions); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, cached_diagnostics::Diagnostic &s) { + io.mapRequired("id", s.ID); + io.mapRequired("level", (unsigned &)s.Level); + io.mapRequired("message", s.Message); + io.mapOptional("loc", s.Loc); + io.mapOptional("ranges", s.Ranges); + io.mapOptional("fixits", s.FixIts); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, cached_diagnostics::Diagnostics &s) { + io.mapRequired("sloc_entries", s.SLocEntries); + io.mapRequired("diagnostics", s.Diags); + io.mapRequired("custom_diagnostics", s.CustomDiags); + } +}; +} // namespace llvm::yaml + +LLVM_YAML_IS_SEQUENCE_VECTOR(cached_diagnostics::SLocEntry) +LLVM_YAML_IS_SEQUENCE_VECTOR(cached_diagnostics::Diagnostic) +LLVM_YAML_IS_SEQUENCE_VECTOR(cached_diagnostics::Range) +LLVM_YAML_IS_SEQUENCE_VECTOR(cached_diagnostics::FixItHint) +LLVM_YAML_IS_SEQUENCE_VECTOR(cached_diagnostics::CustomDiagDesc) + +void CachedDiagnosticSerializer::captureCustomDiags( + const DiagnosticsEngine &Diags) { + auto MaxCustomDiagID = Diags.getMaxCustomDiagID(); + if (!MaxCustomDiagID) + return; + + // Capture any custom diagnostics we have not already seen. + unsigned FirstUnknownDiag = + diag::DIAG_UPPER_LIMIT + CachedDiags.CustomDiags.size(); + for (unsigned DiagID = FirstUnknownDiag; DiagID < *MaxCustomDiagID; + ++DiagID) { + auto Desc = Diags.getCustomDiagDesc(DiagID); + CachedDiags.CustomDiags.push_back(Desc); + + // Forward the custom diagnostic to the Serializer's diagnostic engine. + auto SerializerDiagID = DiagEngine.getCustomDiagID(Desc); + assert(SerializerDiagID == DiagID && "mismatched custom diags"); + (void)SerializerDiagID; + } +} + +std::optional +CachedDiagnosticSerializer::serializeEmittedDiagnostics() { + if (empty()) + return std::nullopt; + + SmallString<512> Buf; + raw_svector_ostream OS(Buf); + yaml::Output YOut(OS); + YOut << CachedDiags; + StringRef YamlContents = OS.str(); + + // Use compression to reduce the size of the yaml output. Note that we don't + // need to track whether compression was used or not, see doc-comments of \p + // serializeEmittedDiagnostics(). + SmallVector CompressedBuffer; + if (compression::zstd::isAvailable()) { + compression::zstd::compress(arrayRefFromStringRef(YamlContents), + CompressedBuffer); + + } else if (compression::zlib::isAvailable()) { + compression::zlib::compress(arrayRefFromStringRef(YamlContents), + CompressedBuffer); + } + if (!CompressedBuffer.empty()) { + raw_svector_ostream BufOS((SmallVectorImpl &)CompressedBuffer); + support::endian::Writer Writer(BufOS, endianness::little); + Writer.write(uint32_t(YamlContents.size())); + return toStringRef(CompressedBuffer).str(); + } + + return YamlContents.str(); +} + +Error CachedDiagnosticSerializer::deserializeCachedDiagnostics( + StringRef Buffer) { + + StringRef YamlContents; + SmallVector UncompressedBuffer; + if (compression::zstd::isAvailable() || compression::zlib::isAvailable()) { + uint32_t UncompressedSize = + support::endian::read( + Buffer.data() + Buffer.size() - sizeof(uint32_t)); + StringRef CompressedData = Buffer.drop_back(sizeof(uint32_t)); + if (compression::zstd::isAvailable()) { + if (Error E = compression::zstd::decompress( + arrayRefFromStringRef(CompressedData), UncompressedBuffer, + UncompressedSize)) { + return E; + } + } else { + if (Error E = compression::zlib::decompress( + arrayRefFromStringRef(CompressedData), UncompressedBuffer, + UncompressedSize)) { + return E; + } + } + YamlContents = toStringRef(UncompressedBuffer); + } else { + YamlContents = Buffer; + } + + CachedDiags.clear(); + yaml::Input YIn(YamlContents); + YIn >> CachedDiags; + if (YIn.error()) + return createStringError(YIn.error(), + "failed deserializing cached diagnostics"); + + assert(DiagEngine.getMaxCustomDiagID() == std::nullopt && + "existing custom diagnostics will conflict"); + for (const auto &CustomDiag : CachedDiags.CustomDiags) { + (void)DiagEngine.getCustomDiagID(CustomDiag.getDesc()); + } + + return Error::success(); +} + +/// Captures diagnostics emitted during compilation while also passing them +/// along to the original consumer. +struct CachingDiagnosticsProcessor::DiagnosticsConsumer + : public DiagnosticConsumer { + + CachedDiagnosticSerializer Serializer; + DiagnosticConsumer *OrigConsumer; + bool EngineOwnedOrigConsumer; + + DiagnosticsConsumer(PrefixMapper &Mapper, FileManager &FileMgr, + DiagnosticConsumer *OrigConsumer, bool EngineOwnedOrig) + : Serializer(Mapper, FileMgr), OrigConsumer(OrigConsumer), + EngineOwnedOrigConsumer(EngineOwnedOrig) { + // Using the new DiagnosticsEngine as a way to propagate converted + // StoredDiagnostics to the original consumer. The new DiagnosticsEngine + // contains the SourceManager that the SourceLocations of the converted + // StoredDiagnostics came from. + Serializer.DiagEngine.setClient(OrigConsumer, /*ShouldOwnClient*/ false); + } + + ~DiagnosticsConsumer() { + if (OrigConsumer && EngineOwnedOrigConsumer) + delete OrigConsumer; + } + + void clearConsumer() { + OrigConsumer = nullptr; + EngineOwnedOrigConsumer = false; + Serializer.DiagEngine.setClient(nullptr); + } + + void BeginSourceFile(const LangOptions &LangOpts, + const Preprocessor *PP) override { + return OrigConsumer->BeginSourceFile(LangOpts, PP); + } + + void EndSourceFile() override { return OrigConsumer->EndSourceFile(); } + + void finish() override { return OrigConsumer->finish(); } + + void HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) override { + if (shouldCacheDiagnostic(Level, Info)) { + unsigned DiagIdx = Serializer.addDiag(StoredDiagnostic(Level, Info)); + StoredDiagnostic NewDiag = Serializer.getDiag(DiagIdx); + + if (DiagnosticIDs::IsCustomDiag(NewDiag.getID())) + Serializer.captureCustomDiags(*Info.getDiags()); + + // Pass the converted diagnostic to the original consumer. We do this + // because: + // 1. It ensures that the rendered diagnostics will use the same + // diagnostics source for both a cache miss or a cache hit. + // 2. If path prefixing is enabled, we'll pass locations with + // de-canonicalized filenames during compilation (the original diagnostic + // uses canonical paths). + assert(Serializer.DiagEngine.getClient() == OrigConsumer); + // FIXME: This is not sound: giving the original consumer diagnostics with + // different SourceManager may break them. + Serializer.DiagEngine.Report(NewDiag); + } else { + OrigConsumer->HandleDiagnostic(Level, Info); + } + // Update stats. + NumWarnings = OrigConsumer->getNumWarnings(); + NumErrors = OrigConsumer->getNumErrors(); + } + + bool shouldCacheDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) { + if (Level < DiagnosticsEngine::Note) + return false; + +#ifndef NDEBUG + // These are intended for caching introspection, they should not be cached. + // If the \p CachingDiagnosticsProcessor::DiagnosticsConsumer received one + // of these it means that the diagnostic was emitted in-between a normal + // compilation starting & finishing, which should not be happening. + switch (Info.getID()) { + case diag::remark_compile_job_cache_hit: + case diag::remark_compile_job_cache_miss: + case diag::remark_compile_job_cache_miss_result_not_found: + case diag::remark_compile_job_cache_backend_output_not_found: + case diag::remark_compile_job_cache_skipped: + case diag::remark_compile_job_cache_timing_backend_key_query: + case diag::remark_compile_job_cache_timing_backend_key_update: + case diag::remark_compile_job_cache_timing_backend_load: + case diag::remark_compile_job_cache_timing_backend_store: + assert(0 && "unexpected caching remark diagnostic!"); + } +#endif + + return true; + } + + void clear() override { + Serializer.clear(); + DiagnosticConsumer::clear(); + OrigConsumer->clear(); + } + + bool IncludeInDiagnosticCounts() const override { + return OrigConsumer->IncludeInDiagnosticCounts(); + } +}; + +CachingDiagnosticsProcessor::CachingDiagnosticsProcessor(PrefixMapper Mapper, + FileManager &FileMgr) + : Mapper(std::move(Mapper)), FileMgr(FileMgr) {} + +CachingDiagnosticsProcessor::~CachingDiagnosticsProcessor() = default; + +void CachingDiagnosticsProcessor::insertDiagConsumer(DiagnosticsEngine &Diags) { + assert(!DiagConsumer && "already called insertDiagConsumer?"); + DiagConsumer.reset(new DiagnosticsConsumer(Mapper, FileMgr, Diags.getClient(), + Diags.ownsClient())); + Diags.takeClient().release(); // DiagnosticsConsumer accepted ownership. + Diags.setClient(DiagConsumer.get(), /*ShouldOwnClient*/ false); +} + +void CachingDiagnosticsProcessor::removeDiagConsumer(DiagnosticsEngine &Diags) { + assert(DiagConsumer && "didn't call insertDiagConsumer?"); + assert(DiagConsumer->OrigConsumer && "already called removeDiagConsumer?"); + assert(Diags.getClient() == DiagConsumer.get()); + Diags.setClient(DiagConsumer->OrigConsumer, + /*ShouldOwnClient*/ DiagConsumer->EngineOwnedOrigConsumer); + DiagConsumer->clearConsumer(); +} + +Expected> +CachingDiagnosticsProcessor::serializeEmittedDiagnostics() { + return DiagConsumer->Serializer.serializeEmittedDiagnostics(); +} + +Error CachingDiagnosticsProcessor::replayCachedDiagnostics( + StringRef Buffer, DiagnosticConsumer &Consumer) { + + // A compilation that only includes "#import " and + // passes "-Weverything -Wsystem-headers" generated 3780 warnings and when + // replaying them: + // * Deserialization of diagnostics: ~40ms + // * Rendering the diagnostics: ~240ms + // FIXME: Look into improving performance of diagnostic rendering. + + CachedDiagnosticSerializer Serializer(Mapper, FileMgr); + if (Error E = Serializer.deserializeCachedDiagnostics(Buffer)) + return E; + Serializer.DiagEngine.setClient(&Consumer, /*ShouldOwnClient*/ false); + for (unsigned I = 0, E = Serializer.getNumDiags(); I != E; I++) { + Serializer.DiagEngine.Report(Serializer.getDiag(I)); + } + return Error::success(); +} diff --git a/clang/lib/Frontend/CachedDiagnostics.h b/clang/lib/Frontend/CachedDiagnostics.h new file mode 100644 index 0000000000000..2de4aa5e107aa --- /dev/null +++ b/clang/lib/Frontend/CachedDiagnostics.h @@ -0,0 +1,68 @@ +//===- CachedDiagnostics.h - Serializing diagnostics for caching-*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_DRIVER_CACHEDDIAGNOSTICS_H +#define LLVM_CLANG_TOOLS_DRIVER_CACHEDDIAGNOSTICS_H + +#include "clang/Basic/LLVM.h" +#include "llvm/Support/PrefixMapper.h" +#include +#include + +namespace clang { +class DiagnosticConsumer; +class DiagnosticsEngine; +class FileManager; + +namespace cas { + +class CachingDiagnosticsProcessor { +public: + /// The \p Mapper is used for de-canonicalizing the paths of diagnostics + /// before rendering them. + CachingDiagnosticsProcessor(llvm::PrefixMapper Mapper, FileManager &FileMgr); + ~CachingDiagnosticsProcessor(); + + /// Insert a diagnostic consumer for capturing diagnostics before starting a + /// normal compilation. + void insertDiagConsumer(DiagnosticsEngine &Diags); + /// Remove the diagnostic consumer after the normal compilation finished. + void removeDiagConsumer(DiagnosticsEngine &Diags); + + /// \returns a serialized buffer of the currently recorded diagnostics, or + /// \p std::nullopt if there's no diagnostic. The buffer can be passed to + /// \p replayCachedDiagnostics for rendering the same diagnostics. + /// + /// There is no stability guarantee for the format of the buffer, the + /// expectation is that the buffer will be deserialized only by the same + /// compiler version that produced it. The format can change without + /// restrictions. + /// + /// The intended use is as implementation detail of compilation caching, where + /// the diagnostic output is associated with a compilation cache key. A + /// different compiler version will create different cache keys, which ensures + /// that the diagnostics buffer will only be read by the same compiler that + /// produced it. + Expected> serializeEmittedDiagnostics(); + + /// Used to replay the previously cached diagnostics, after a cache hit. + llvm::Error replayCachedDiagnostics(StringRef Buffer, + DiagnosticConsumer &Consumer); + +private: + llvm::PrefixMapper Mapper; + FileManager &FileMgr; + + struct DiagnosticsConsumer; + std::unique_ptr DiagConsumer; +}; + +} // namespace cas +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_DRIVER_CACHEDDIAGNOSTICS_H diff --git a/clang/lib/Frontend/CompileJobCache.cpp b/clang/lib/Frontend/CompileJobCache.cpp new file mode 100644 index 0000000000000..93c9d9b4281ab --- /dev/null +++ b/clang/lib/Frontend/CompileJobCache.cpp @@ -0,0 +1,1198 @@ +//===- CompileJobCache.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompileJobCache.h" +#include "CachedDiagnostics.h" +#include "clang/Basic/DiagnosticCAS.h" +#include "clang/Frontend/CASDependencyCollector.h" +#include "clang/Frontend/CompileJobCacheKey.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "llvm/ADT/ScopeExit.h" +#include "llvm/CAS/ActionCache.h" +#include "llvm/CAS/CASOutputBackend.h" +#include "llvm/CASUtil/Utils.h" +#include "llvm/MCCAS/MCCASObjectV1.h" +#include "llvm/RemoteCachingService/Client.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrefixMapper.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/ScopedDurationTimer.h" +#include "llvm/Support/VirtualOutputBackends.h" + +using namespace clang; +using llvm::Error; + +/// Represents a mechanism for storing and retrieving compilation artifacts. +/// It includes common functionality and extension points for specific backend +/// implementations. +class CompileJobCache::CachingOutputs { +public: + using OutputKind = clang::cas::CompileJobCacheResult::OutputKind; + + CachingOutputs(CompilerInstance &Clang, StringRef Workingdir, + llvm::PrefixMapper Mapper, bool WriteOutputAsCASID, + bool UseCASBackend); + virtual ~CachingOutputs() = default; + + /// \returns true if result was found and replayed, false otherwise. + virtual Expected + tryReplayCachedResult(const llvm::cas::CASID &ResultCacheKey, + const llvm::cas::CASID &CanonicalResultCacheKey) = 0; + + /// \returns true on failure, false on success. + virtual bool prepareOutputCollection() = 0; + + void stopDiagnosticsCapture(); + + /// This is for an output file that was written directly on the file system. + /// It's a workaround until all compilation consumers adopt + /// \c llvm::vfs::OutputBackend. + virtual Error addNonVirtualOutputFile(StringRef FilePath) = 0; + + /// Finish writing outputs from a computed result, after a cache miss. + /// If SkipCache is true, it should not insert the ResultCacheKey into + /// Cache for future uses. + virtual Error + finishComputedResult(const llvm::cas::CASID &ResultCacheKey, + const llvm::cas::CASID &CanonicalResultCacheKey, + bool SkipCache) = 0; + +protected: + StringRef getPathForOutputKind(OutputKind Kind); + + bool prepareOutputCollectionCommon( + IntrusiveRefCntPtr CacheOutputs); + Error replayCachedDiagnostics(StringRef DiagsData); + + CompilerInstance &Clang; + const llvm::PrefixMapper PrefixMapper; + const bool WriteOutputAsCASID; + const bool UseCASBackend; + clang::cas::CompileJobCacheResult::Builder CachedResultBuilder; + std::string OutputFile; + std::string DependenciesFile; + std::unique_ptr DiagProcessor; +}; + +namespace { + +/// Store and retrieve compilation artifacts using \p llvm::cas::ObjectStore and +/// \p llvm::cas::ActionCache. +class ObjectStoreCachingOutputs : public CompileJobCache::CachingOutputs { +public: + ObjectStoreCachingOutputs(CompilerInstance &Clang, StringRef WorkingDir, + llvm::PrefixMapper Mapper, bool WriteOutputAsCASID, + bool UseCASBackend, + std::optional &MCOutputID, + std::shared_ptr DB, + std::shared_ptr Cache) + : CachingOutputs(Clang, WorkingDir, std::move(Mapper), WriteOutputAsCASID, + UseCASBackend), + ComputedJobNeedsReplay(WriteOutputAsCASID || UseCASBackend), + MCOutputID(MCOutputID), CAS(std::move(DB)), Cache(std::move(Cache)) { + if (CAS) + CASOutputs = llvm::makeIntrusiveRefCnt(*CAS); + } + + Expected> + replayCachedResult(const llvm::cas::CASID &ResultCacheKey, + clang::cas::CompileJobCacheResult &Result, + bool JustComputedResult); + +private: + Expected tryReplayCachedResult( + const llvm::cas::CASID &ResultCacheKey, + const llvm::cas::CASID &CanonicalResultCacheKey) override; + + bool prepareOutputCollection() override; + + Error addNonVirtualOutputFile(StringRef FilePath) override; + + Error finishComputedResult(const llvm::cas::CASID &ResultCacheKey, + const llvm::cas::CASID &CanonicalResultCacheKey, + bool SkipCache) override; + + Expected + writeOutputs(const llvm::cas::CASID &ResultCacheKey); + + /// Replay a cache hit. + /// + /// Return status if should exit immediately, otherwise std::nullopt. + std::optional replayCachedResult(const llvm::cas::CASID &ResultCacheKey, + llvm::cas::ObjectRef ResultID, + bool JustComputedResult); + + const bool ComputedJobNeedsReplay; + std::optional &MCOutputID; + std::shared_ptr CAS; + std::shared_ptr Cache; + IntrusiveRefCntPtr CASOutputs; + std::optional DependenciesOutput; +}; + +/// An \p OutputBackend that just records the list of output paths/names. +class CollectingOutputBackend : public llvm::vfs::ProxyOutputBackend { + SmallVector OutputNames; + +public: + CollectingOutputBackend() + : llvm::vfs::ProxyOutputBackend(llvm::vfs::makeNullOutputBackend()) {} + + ArrayRef getOutputs() const { return OutputNames; } + + /// Add an association of a "kind" string with a particular output path. + /// When the output for \p Path is encountered it will be associated with + /// the \p Kind string instead of its path. + void addKindMap(StringRef Kind, StringRef Path) { + KindMaps.push_back({Saver.save(Kind), Saver.save(Path)}); + } + + void addOutput(StringRef Path) { + StringRef Name = tryRemapPath(Path); + OutputNames.push_back(Name.str()); + } + +private: + llvm::BumpPtrAllocator Alloc; + llvm::StringSaver Saver{Alloc}; + + struct KindMap { + StringRef Kind; + StringRef Path; + }; + SmallVector KindMaps; + + /// Returns the "kind" name for the path if one was added for it, otherwise + /// returns the \p Path itself. + StringRef tryRemapPath(StringRef Path) const { + for (const KindMap &Map : KindMaps) { + if (Map.Path == Path) + return Map.Kind; + } + return Path; + } + + Expected> + createFileImpl(StringRef Path, + std::optional Config) override { + addOutput(Path); + return ProxyOutputBackend::createFileImpl(Path, std::move(Config)); + } + + IntrusiveRefCntPtr cloneImpl() const override { + return IntrusiveRefCntPtr( + const_cast(this)); + } +}; + +/// Store and retrieve compilation artifacts using \p llvm::cas::CASDBClient +/// and \p llvm::cas::KeyValueDBClient. +class RemoteCachingOutputs : public CompileJobCache::CachingOutputs { +public: + RemoteCachingOutputs(CompilerInstance &Clang, StringRef WorkingDir, + llvm::PrefixMapper Mapper, + llvm::cas::remote::ClientServices Clients) + : CachingOutputs(Clang, WorkingDir, std::move(Mapper), + /*WriteOutputAsCASID*/ false, + /*UseCASBackend*/ false) { + RemoteKVClient = std::move(Clients.KVDB); + RemoteCASClient = std::move(Clients.CASDB); + CollectingOutputs = llvm::makeIntrusiveRefCnt(); + } + +private: + Expected tryReplayCachedResult( + const llvm::cas::CASID &ResultCacheKey, + const llvm::cas::CASID &CanonicalResultCacheKey) override; + + bool prepareOutputCollection() override; + + Error addNonVirtualOutputFile(StringRef FilePath) override; + + Error finishComputedResult(const llvm::cas::CASID &ResultCacheKey, + const llvm::cas::CASID &CanonicalResultCacheKey, + bool SkipCache) override; + + Expected + saveOutputs(const llvm::cas::CASID &ResultCacheKey); + + Expected replayCachedResult( + const llvm::cas::CASID &ResultCacheKey, + const llvm::cas::remote::KeyValueDBClient::ValueTy &CompResult); + + std::string getPrintableRemoteID(StringRef RemoteCASIDBytes); + + void tryReleaseLLBuildExecutionLane(); + + static StringRef getOutputKindName(OutputKind Kind); + /// \returns \p std::nullopt if \p Name doesn't match one of the output kind names. + static std::optional getOutputKindForName(StringRef Name); + + std::unique_ptr RemoteKVClient; + std::unique_ptr RemoteCASClient; + IntrusiveRefCntPtr CollectingOutputs; + bool TriedReleaseLLBuildExecutionLane = false; +}; + +} // anonymous namespace + +StringRef +CompileJobCache::CachingOutputs::getPathForOutputKind(OutputKind Kind) { + switch (Kind) { + case OutputKind::MainOutput: + return OutputFile; + case OutputKind::Dependencies: + return DependenciesFile; + default: + return ""; + } +} + +static std::string fixupRelativePath(const std::string &Path, FileManager &FM, + StringRef WorkingDir) { + if (llvm::sys::path::is_absolute(Path) || Path.empty() || Path == "-") + return Path; + + // Apply -working-dir compiler option. + // FIXME: this needs to stay in sync with createOutputFileImpl. Ideally, clang + // would create output files by their "kind" rather than by path. + SmallString<128> PathStorage(Path); + if (FM.FixupRelativePath(PathStorage)) + return std::string(PathStorage); + + // Apply "normal" working directory. + if (!WorkingDir.empty()) { + SmallString<128> Tmp(Path); + llvm::sys::fs::make_absolute(WorkingDir, Tmp); + return std::string(Tmp); + } + return Path; +} + +CompileJobCache::CompileJobCache() = default; +CompileJobCache::~CompileJobCache() = default; + +int CompileJobCache::reportCachingBackendError(DiagnosticsEngine &Diag, + Error &&E) { + Diag.Report(diag::err_caching_backend_fail) << llvm::toString(std::move(E)); + return 1; +} + +std::optional CompileJobCache::initialize(CompilerInstance &Clang) { + CompilerInvocation &Invocation = Clang.getInvocation(); + DiagnosticsEngine &Diags = Clang.getDiagnostics(); + FrontendOptions &FrontendOpts = Invocation.getFrontendOpts(); + CacheCompileJob = FrontendOpts.CacheCompileJob; + + // Nothing else to do if we're not caching. + if (!CacheCompileJob) + return std::nullopt; + + std::tie(CAS, Cache) = Invocation.getCASOpts().getOrCreateDatabases(Diags); + if (!CAS || !Cache) + return 1; // Exit with error! + + CompilerInvocation DummyInvocation = Invocation; + CompileJobCachingOptions DummyCacheOpts; + + CompileJobCachingOptions CacheOpts; + auto Keys = canonicalizeAndCreateCacheKeys(*CAS, *Cache, Diags, Invocation, + CacheOpts); + if (!Keys) + return 1; // Exit with error! + std::tie(ResultCacheKey, ResultCacheKeyWithInputCacheKeysResolved) = *Keys; + + switch (FrontendOpts.ProgramAction) { + case frontend::GenerateModule: + case frontend::GenerateModuleInterface: + case frontend::GeneratePCH: + Clang.getPreprocessorOpts().CachingDiagOption = CachingDiagKind::Error; + break; + default: + Clang.getPreprocessorOpts().CachingDiagOption = CachingDiagKind::Warning; + break; + } + + DisableCachedCompileJobReplay = CacheOpts.DisableCachedCompileJobReplay; + bool UseCASBackend = Invocation.getCodeGenOpts().UseCASBackend; + Invocation.getCodeGenOpts().MCCallBack = [&](const llvm::cas::CASID &ID) { + MCOutputID = ID; + return Error::success(); + }; + + llvm::PrefixMapper PrefixMapper; + llvm::SmallVector Split; + llvm::MappedPrefix::transformJoinedIfValid(CacheOpts.PathPrefixMappings, + Split); + for (const auto &MappedPrefix : Split) { + // We use the inverse mapping because the \p PrefixMapper will be used for + // de-canonicalization of paths. + PrefixMapper.add(MappedPrefix.getInverse()); + } + + if (!CacheOpts.CompilationCachingServicePath.empty()) { + Expected Clients = + llvm::cas::remote::createCompilationCachingRemoteClient( + CacheOpts.CompilationCachingServicePath); + if (!Clients) + return reportCachingBackendError(Clang.getDiagnostics(), + Clients.takeError()); + assert(!CacheOpts.WriteOutputAsCASID && + "combination of options not rejected earlier?"); + assert(!UseCASBackend && "combination of options not rejected earlier?"); + CacheBackend = std::make_unique( + Clang, /*WorkingDir=*/"", std::move(PrefixMapper), std::move(*Clients)); + } else { + CacheBackend = std::make_unique( + Clang, /*WorkingDir=*/"", std::move(PrefixMapper), + CacheOpts.WriteOutputAsCASID, UseCASBackend, MCOutputID, CAS, Cache); + } + + return std::nullopt; +} + +CompileJobCache::CachingOutputs::CachingOutputs(CompilerInstance &Clang, + StringRef WorkingDir, + llvm::PrefixMapper Mapper, + bool WriteOutputAsCASID, + bool UseCASBackend) + : Clang(Clang), PrefixMapper(std::move(Mapper)), + WriteOutputAsCASID(WriteOutputAsCASID), UseCASBackend(UseCASBackend) { + CompilerInvocation &Invocation = Clang.getInvocation(); + FrontendOptions &FrontendOpts = Invocation.getFrontendOpts(); + if (!Clang.hasFileManager()) + Clang.createFileManager(); + FileManager &FM = Clang.getFileManager(); + OutputFile = fixupRelativePath(FrontendOpts.OutputFile, FM, WorkingDir); + DependenciesFile = fixupRelativePath( + Invocation.getDependencyOutputOpts().OutputFile, FM, WorkingDir); + DiagProcessor = std::make_unique( + PrefixMapper, FM); +} + +Expected ObjectStoreCachingOutputs::tryReplayCachedResult( + const llvm::cas::CASID &ResultCacheKey, + const llvm::cas::CASID &CanonicalResultCacheKey) { + DiagnosticsEngine &Diags = Clang.getDiagnostics(); + + std::optional Result; + { + llvm::ScopedDurationTimer ScopedTime([&Diags](double Seconds) { + Diags.Report(diag::remark_compile_job_cache_timing_backend_key_query) + << llvm::format("%.6fs", Seconds); + }); + // Asking for the canonical cache key gives us better chances for cache hit. + if (Error E = Cache->get(CanonicalResultCacheKey, /*Globally=*/true) + .moveInto(Result)) + return std::move(E); + } + + if (!Result) { + Diags.Report(diag::remark_compile_job_cache_miss) + << ResultCacheKey.toString(); + return false; + } + + llvm::ScopedDurationTimer ScopedTime([&Diags](double Seconds) { + Diags.Report(diag::remark_compile_job_cache_timing_backend_load) + << llvm::format("%.6fs", Seconds); + }); + + std::optional ResultRef = CAS->getReference(*Result); + if (!ResultRef) { + Diags.Report(diag::remark_compile_job_cache_miss_result_not_found) + << ResultCacheKey.toString() << "result not in CAS"; + return false; + } + + // Tasks depending on this one don't know the canonical cache key. Make sure + // the requested key is associated with the result too. + if (ResultCacheKey != CanonicalResultCacheKey) + if (Error E = + Cache->put(ResultCacheKey, *Result, /*Globally=*/true)) + return std::move(E); + + // \c replayCachedResult emits remarks for a cache hit or miss. + std::optional Status = replayCachedResult(ResultCacheKey, *ResultRef, + /*JustComputedResult=*/false); + if (!Status) + return false; // cache miss. + assert(*Status == 0 && "Expected success status for a cache hit"); + return true; +} + +std::optional +CompileJobCache::tryReplayCachedResult(CompilerInstance &Clang) { + if (!CacheCompileJob) + return std::nullopt; + + DiagnosticsEngine &Diags = Clang.getDiagnostics(); + + assert(ResultCacheKey.has_value() && "ResultCacheKey not initialized?"); + + Clang.setCompileJobCacheKey(*ResultCacheKey); + + Expected ReplayedResult = + DisableCachedCompileJobReplay + ? false + : CacheBackend->tryReplayCachedResult( + *ResultCacheKey, *ResultCacheKeyWithInputCacheKeysResolved); + if (!ReplayedResult) + return reportCachingBackendError(Clang.getDiagnostics(), + ReplayedResult.takeError()); + + if (DisableCachedCompileJobReplay) + Diags.Report(diag::remark_compile_job_cache_skipped) + << ResultCacheKey->toString(); + + if (*ReplayedResult) + return 0; + + if (CacheBackend->prepareOutputCollection()) + return 1; + + return std::nullopt; +} + +bool CompileJobCache::CachingOutputs::prepareOutputCollectionCommon( + IntrusiveRefCntPtr CacheOutputs) { + // Create an on-disk backend for streaming the results live if we run the + // computation. If we're writing the output as a CASID, skip it here, since + // it'll be handled during replay. + IntrusiveRefCntPtr OnDiskOutputs = + llvm::makeIntrusiveRefCnt(); + if (WriteOutputAsCASID) { + OnDiskOutputs = llvm::vfs::makeFilteringOutputBackend( + OnDiskOutputs, + [this](StringRef ResolvedPath, std::optional) { + return ResolvedPath != this->OutputFile; + }); + } + + // Set up the output backend so we can save / cache the result after. + for (OutputKind K : clang::cas::CompileJobCacheResult::getAllOutputKinds()) { + StringRef OutPath = getPathForOutputKind(K); + if (!OutPath.empty()) + CachedResultBuilder.addKindMap(K, OutPath); + } + + // When use CAS backend, filter out the output object file. Always filter out + // the dependencies file, since we build a CAS-specific object for it. + auto FilterBackend = llvm::vfs::makeFilteringOutputBackend( + CacheOutputs, + [&](StringRef Path, std::optional Config) { + return !(UseCASBackend && (Path.str() == OutputFile)) && + Path != DependenciesFile; + }); + + Clang.setOutputBackend(llvm::vfs::makeMirroringOutputBackend( + FilterBackend, std::move(OnDiskOutputs))); + + DiagProcessor->insertDiagConsumer(Clang.getDiagnostics()); + + return false; +} + +void CompileJobCache::CachingOutputs::stopDiagnosticsCapture() { + DiagProcessor->removeDiagConsumer(Clang.getDiagnostics()); +} + +Error CompileJobCache::CachingOutputs::replayCachedDiagnostics( + StringRef DiagsData) { + DiagnosticConsumer &Consumer = *Clang.getDiagnostics().getClient(); + Consumer.BeginSourceFile(Clang.getLangOpts()); + if (Error E = DiagProcessor->replayCachedDiagnostics(DiagsData, Consumer)) + return E; + Consumer.EndSourceFile(); + Clang.printDiagnosticStats(); + return Error::success(); +} + +bool ObjectStoreCachingOutputs::prepareOutputCollection() { + if (prepareOutputCollectionCommon(CASOutputs)) + return true; + + if (!Clang.getDependencyOutputOpts().OutputFile.empty()) + Clang.addDependencyCollector(std::make_shared( + Clang.getDependencyOutputOpts(), *CAS, + [this](std::optional Deps) { + DependenciesOutput = Deps; + })); + + return false; +} + +bool CompileJobCache::finishComputedResult(CompilerInstance &Clang, + bool Success) { + // Nothing to do if not caching. + if (!CacheCompileJob) + return Success; + + CacheBackend->stopDiagnosticsCapture(); + + // Don't cache failed builds. + // + // TODO: Consider caching failed builds! Note: when output files are written + // without a temporary (non-atomically), failure may cause the removal of a + // preexisting file. That behaviour is not currently modeled by the cache. + if (!Success) + return false; + + DiagnosticsEngine &Diags = Clang.getDiagnostics(); + + bool UnsupportedOutput; + if (Error E = maybeIngestNonVirtualOutputFromFileSystem(Clang).moveInto( + UnsupportedOutput)) { + reportCachingBackendError(Diags, std::move(E)); + return false; + } + + // Check if we encounter any source that would generate non-reproducible + // outputs. + bool SkipCache = + (Clang.hasPreprocessor() && Clang.isSourceNonReproducible()) || + UnsupportedOutput; + if (SkipCache) { + switch (Clang.getPreprocessorOpts().CachingDiagOption) { + case CachingDiagKind::None: + break; + case CachingDiagKind::Warning: + Diags.Report(diag::remark_compile_job_cache_skipped) + << ResultCacheKey->toString(); + break; + case CachingDiagKind::Error: + llvm_unreachable("Should not reach here if there is an error"); + } + } + + if (Error E = CacheBackend->finishComputedResult( + *ResultCacheKey, *ResultCacheKeyWithInputCacheKeysResolved, + SkipCache)) { + reportCachingBackendError(Diags, std::move(E)); + return false; + } + return true; +} + +Expected CompileJobCache::maybeIngestNonVirtualOutputFromFileSystem( + CompilerInstance &Clang) { + // FIXME: All consumers should adopt \c llvm::vfs::OutputBackend and this + // function should go away. + + const auto &FrontendOpts = Clang.getFrontendOpts(); + if (FrontendOpts.ProgramAction == frontend::RunAnalysis) { + StringRef OutputPath = FrontendOpts.OutputFile; + if (OutputPath.empty()) + return false; + if (llvm::sys::fs::is_directory(OutputPath)) { + // FIXME: A directory is produced for the 'html' output of the analyzer, + // support it for caching purposes. + Clang.getDiagnostics().Report(diag::warn_clang_cache_disabled_caching) + << "analyzer output is not supported"; + return true; + } + if (Error E = CacheBackend->addNonVirtualOutputFile(OutputPath)) + return std::move(E); + return false; + } + + return false; +} + +Expected> CompileJobCache::replayCachedResult( + std::shared_ptr Invok, StringRef WorkingDir, + const llvm::cas::CASID &CacheKey, cas::CompileJobCacheResult &CachedResult, + SmallVectorImpl &DiagText, bool WriteOutputAsCASID, + std::optional *OutMCOutputID) { + CompilerInstance Clang; + Clang.setInvocation(std::move(Invok)); + llvm::raw_svector_ostream DiagOS(DiagText); + Clang.createDiagnostics( + *llvm::vfs::getRealFileSystem(), + new TextDiagnosticPrinter(DiagOS, &Clang.getDiagnosticOpts())); + Clang.setVerboseOutputStream(DiagOS); + + // FIXME: we should create an include-tree filesystem based on the cache key + // to guarantee that the filesystem used during diagnostic replay will match + // the cached diagnostics. Currently we rely on the invocation having a + // matching -fcas-include-tree option. + + auto FinishDiagnosticClient = + llvm::make_scope_exit([&]() { Clang.getDiagnosticClient().finish(); }); + + llvm::PrefixMapper PrefixMapper; + llvm::SmallVector Split; + llvm::MappedPrefix::transformJoinedIfValid( + Clang.getFrontendOpts().PathPrefixMappings, Split); + for (const auto &MappedPrefix : Split) { + // We use the inverse mapping because the \p PrefixMapper will be used for + // de-canonicalization of paths. + PrefixMapper.add(MappedPrefix.getInverse()); + } + + assert(!Clang.getDiagnostics().hasErrorOccurred()); + + std::optional MCOutputID; + ObjectStoreCachingOutputs CachingOutputs( + Clang, WorkingDir, std::move(PrefixMapper), WriteOutputAsCASID, + Clang.getInvocation().getCodeGenOpts().UseCASBackend, MCOutputID, + /*CAS*/ nullptr, /*Cache*/ nullptr); + if (OutMCOutputID) + *OutMCOutputID = std::move(MCOutputID); + + std::optional Ret; + if (Error E = CachingOutputs + .replayCachedResult(CacheKey, CachedResult, + /*JustComputedResult*/ false) + .moveInto(Ret)) + return std::move(E); + + if (Clang.getDiagnostics().hasErrorOccurred()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "error diagnostic during replay: " + + DiagOS.str()); + + return Ret; +} + +Expected ObjectStoreCachingOutputs::writeOutputs( + const llvm::cas::CASID &ResultCacheKey) { + DiagnosticsEngine &Diags = Clang.getDiagnostics(); + llvm::ScopedDurationTimer ScopedTime([&Diags](double Seconds) { + Diags.Report(diag::remark_compile_job_cache_timing_backend_store) + << llvm::format("%.6fs", Seconds); + }); + + // Add the MC output to the CAS Outputs. + if (MCOutputID) { + auto MCOutputRef = CAS->getReference(*MCOutputID); + if (MCOutputRef) + CachedResultBuilder.addOutput(OutputKind::MainOutput, *MCOutputRef); + } + + Expected> SerialDiags = + DiagProcessor->serializeEmittedDiagnostics(); + if (!SerialDiags) + return SerialDiags.takeError(); + if (*SerialDiags) { + Expected DiagsRef = + CAS->storeFromString(std::nullopt, **SerialDiags); + if (!DiagsRef) + return DiagsRef.takeError(); + CachedResultBuilder.addOutput(OutputKind::SerializedDiagnostics, *DiagsRef); + } + + if (DependenciesOutput) + CachedResultBuilder.addOutput(OutputKind::Dependencies, + *DependenciesOutput); + + auto BackendOutputs = CASOutputs->takeOutputs(); + for (auto &Output : BackendOutputs) + if (auto Err = CachedResultBuilder.addOutput(Output.Path, Output.Object)) + return std::move(Err); + + // Cache the result. + return CachedResultBuilder.build(*CAS); +} + +Error ObjectStoreCachingOutputs::addNonVirtualOutputFile(StringRef FilePath) { + auto F = llvm::sys::fs::openNativeFileForRead(FilePath); + if (!F) + return F.takeError(); + auto CloseOnExit = + llvm::make_scope_exit([&F]() { llvm::sys::fs::closeFile(*F); }); + + std::optional ObjRef; + if (Error E = CAS->storeFromOpenFile(*F).moveInto(ObjRef)) + return E; + CASOutputs->addObject(FilePath, *ObjRef); + return Error::success(); +} + +Error ObjectStoreCachingOutputs::finishComputedResult( + const llvm::cas::CASID &ResultCacheKey, + const llvm::cas::CASID &CanonicalResultCacheKey, bool SkipCache) { + Expected Result = writeOutputs(ResultCacheKey); + if (!Result) + return Result.takeError(); + + // Skip caching if requested. + if (!SkipCache) { + DiagnosticsEngine &Diags = Clang.getDiagnostics(); + llvm::ScopedDurationTimer ScopedTime([&Diags](double Seconds) { + Diags.Report(diag::remark_compile_job_cache_timing_backend_key_update) + << llvm::format("%.6fs", Seconds); + }); + if (llvm::Error E = Cache->put(CanonicalResultCacheKey, CAS->getID(*Result), + /*Globally=*/true)) + return E; + // Tasks depending on this one don't know the canonical cache key. Make sure + // the requested key is associated with the result too. + if (ResultCacheKey != CanonicalResultCacheKey) { + if (llvm::Error E = Cache->put(ResultCacheKey, CAS->getID(*Result), + /*Globally=*/true)) + return E; + } + } + + // Replay / decanonicalize as necessary. + std::optional Status = replayCachedResult(ResultCacheKey, *Result, + /*JustComputedResult=*/true); + (void)Status; + assert(Status == std::nullopt); + return Error::success(); +} + +/// Replay a result after a cache hit. +std::optional ObjectStoreCachingOutputs::replayCachedResult( + const llvm::cas::CASID &ResultCacheKey, llvm::cas::ObjectRef ResultID, + bool JustComputedResult) { + if (JustComputedResult && !WriteOutputAsCASID) + return std::nullopt; + + // FIXME: Stop calling report_fatal_error(). + std::optional Result; + clang::cas::CompileJobResultSchema Schema(*CAS); + if (Error E = Schema.load(ResultID).moveInto(Result)) + llvm::report_fatal_error(std::move(E)); + + std::optional Ret; + // FIXME: Stop calling report_fatal_error(). + if (Error E = replayCachedResult(ResultCacheKey, *Result, JustComputedResult) + .moveInto(Ret)) + llvm::report_fatal_error(std::move(E)); + + return Ret; +} + +Expected> ObjectStoreCachingOutputs::replayCachedResult( + const llvm::cas::CASID &ResultCacheKey, + clang::cas::CompileJobCacheResult &Result, bool JustComputedResult) { + if (JustComputedResult && !WriteOutputAsCASID) + return std::nullopt; + + llvm::cas::ObjectStore &CAS = Result.getCAS(); + DiagnosticsEngine &Diags = Clang.getDiagnostics(); + bool HasMissingOutput = false; + std::optional SerialDiags; + llvm::vfs::OnDiskOutputBackend Backend; + + auto processOutput = [&](clang::cas::CompileJobCacheResult::Output O, + std::optional Obj) -> Error { + if (!Obj.has_value()) { + Diags.Report(diag::remark_compile_job_cache_backend_output_not_found) + << clang::cas::CompileJobCacheResult::getOutputKindName(O.Kind) + << ResultCacheKey.toString() << CAS.getID(O.Object).toString(); + HasMissingOutput = true; + return Error::success(); + } + if (HasMissingOutput) + return Error::success(); + + if (O.Kind == OutputKind::SerializedDiagnostics) { + SerialDiags = Obj; + return Error::success(); + } + + std::string Path = std::string(getPathForOutputKind(O.Kind)); + if (Path.empty()) + // The output may be always generated but not needed with this invocation. + return Error::success(); // continue + + // Always create parent directory of outputs, since it is hard to precisely + // match which outputs rely on creating parents and the order outputs are + // replayed in, in case a previous output would create the parent + // (e.g. a .pcm and .diag file in the same directory). + StringRef ParentPath = llvm::sys::path::parent_path(Path); + if (!ParentPath.empty()) + llvm::sys::fs::create_directories(ParentPath); + + bool IsOutputFile = O.Kind == OutputKind::MainOutput; + + if (IsOutputFile && ComputedJobNeedsReplay) { + auto Output = Backend.createFile(Path); + if (!Output) + return Output.takeError(); + if (WriteOutputAsCASID) + llvm::cas::writeCASIDBuffer(CAS.getID(O.Object), *Output); + else if (UseCASBackend) { + // Replay by write out object file. + // When the environmental variable is set, save the backend CASID for + // analysis later. + if (llvm::sys::Process::GetEnv("CLANG_CAS_BACKEND_SAVE_CASID_FILE")) { + std::string CASIDPath = Path + ".casid"; + std::error_code EC; + auto IDOut = Backend.createFile(CASIDPath); + if (!IDOut) + return IDOut.takeError(); + writeCASIDBuffer(CAS.getID(O.Object), *IDOut); + if (auto E = IDOut->keep()) + return E; + } + auto Schema = std::make_unique(CAS); + if (auto E = Schema->serializeObjectFile(*Obj, *Output)) + return E; + } + return Output->keep(); + } + + if (JustComputedResult) + return Error::success(); // continue + + auto Output = Backend.createFile(Path); + if (!Output) + return Output.takeError(); + if (O.Kind == OutputKind::Dependencies) { + if (auto E = CASDependencyCollector::replay( + Clang.getDependencyOutputOpts(), CAS, *Obj, *Output)) + return E; + } else { + *Output << Obj->getData(); + } + + return Output->keep(); + }; + + if (auto Err = Result.forEachLoadedOutput(processOutput)) + return std::move(Err); + + if (HasMissingOutput) { + Diags.Report(diag::remark_compile_job_cache_miss) + << ResultCacheKey.toString(); + return std::nullopt; + } + + if (!JustComputedResult) { + Diags.Report(diag::remark_compile_job_cache_hit) + << ResultCacheKey.toString() << Result.getID().toString(); + + if (SerialDiags) { + if (Error E = replayCachedDiagnostics(SerialDiags->getData())) + return std::move(E); + } + } + + if (JustComputedResult) + return std::nullopt; + return 0; +} + +Expected RemoteCachingOutputs::tryReplayCachedResult( + const llvm::cas::CASID &ResultCacheKey, + const llvm::cas::CASID &CanonicalResultCacheKey) { + DiagnosticsEngine &Diags = Clang.getDiagnostics(); + + std::optional< + llvm::cas::remote::KeyValueDBClient::GetValueAsyncQueue::Response> + Response; + { + llvm::ScopedDurationTimer ScopedTime([&Diags](double Seconds) { + Diags.Report(diag::remark_compile_job_cache_timing_backend_key_query) + << llvm::format("%.6fs", Seconds); + }); + RemoteKVClient->getValueQueue().getValueAsync( + CanonicalResultCacheKey.getHash()); + if (Error E = + RemoteKVClient->getValueQueue().receiveNext().moveInto(Response)) + return std::move(E); + } + if (!Response->Value) { + Diags.Report(diag::remark_compile_job_cache_miss) + << ResultCacheKey.toString(); + return false; + } + + // Tasks depending on this one don't know the canonical cache key. Make sure + // the requested key is associated with the result too. + if (ResultCacheKey != CanonicalResultCacheKey) { + RemoteKVClient->putValueQueue().putValueAsync(ResultCacheKey.getHash(), + *Response->Value); + auto PutResponse = RemoteKVClient->putValueQueue().receiveNext(); + if (!PutResponse) + return PutResponse.takeError(); + } + + llvm::ScopedDurationTimer ScopedTime([&Diags](double Seconds) { + Diags.Report(diag::remark_compile_job_cache_timing_backend_load) + << llvm::format("%.6fs", Seconds); + }); + + Expected ReplayedResult = + replayCachedResult(ResultCacheKey, *Response->Value); + if (!ReplayedResult) + return ReplayedResult.takeError(); + + // diag::remark_compile_job_cache_hit is emitted in \p replayCachedResult. + + return ReplayedResult; +} + +static constexpr llvm::StringLiteral MainOutputKindName = ""; +static constexpr llvm::StringLiteral SerializedDiagnosticsKindName = + ""; +static constexpr llvm::StringLiteral DependenciesOutputKindName = + ""; + +StringRef RemoteCachingOutputs::getOutputKindName(OutputKind Kind) { + switch (Kind) { + case OutputKind::MainOutput: + return MainOutputKindName; + case OutputKind::SerializedDiagnostics: + return SerializedDiagnosticsKindName; + case OutputKind::Dependencies: + return DependenciesOutputKindName; + } +} + +std::optional +RemoteCachingOutputs::getOutputKindForName(StringRef Name) { + return llvm::StringSwitch>(Name) + .Case(MainOutputKindName, OutputKind::MainOutput) + .Case(SerializedDiagnosticsKindName, OutputKind::SerializedDiagnostics) + .Case(DependenciesOutputKindName, OutputKind::Dependencies) + .Default(std::nullopt); +} + +Expected RemoteCachingOutputs::replayCachedResult( + const llvm::cas::CASID &ResultCacheKey, + const llvm::cas::remote::KeyValueDBClient::ValueTy &CompResult) { + // It would be nice to release the llbuild execution lane while we wait to + // receive remote data, but if some data are missing (e.g. due to garbage + // collection), we'll fallback to normal compilation and it would be badness + // to do it outside the execution lanes. + // FIXME: Consider enhancing the llbuild interaction to allow "requesting + // back" an execution lane, then we would release the execution lane here and + // if we need to fallback to normal compilation we'd ask and wait for an + // execution lane before continuing it. + + // Replay outputs. + + DiagnosticsEngine &Diags = Clang.getDiagnostics(); + + auto &LoadQueue = RemoteCASClient->loadQueue(); + struct CallCtx : public llvm::cas::remote::AsyncCallerContext { + StringRef OutputName; + StringRef CASID; + bool IsDiags; + CallCtx(StringRef OutputName, StringRef CASID, bool IsDiags) + : OutputName(OutputName), CASID(CASID), IsDiags(IsDiags) {} + }; + auto makeCtx = + [](StringRef OutputName, StringRef CASID, + bool IsStderr = + false) -> std::shared_ptr { + return std::make_shared(OutputName, CASID, IsStderr); + }; + + for (const auto &Entry : CompResult) { + StringRef OutputName = Entry.first(); + const std::string &CASID = Entry.second; + + std::optional OutKind = getOutputKindForName(OutputName); + StringRef Path = OutKind ? getPathForOutputKind(*OutKind) : OutputName; + + if (OutKind && *OutKind == OutputKind::SerializedDiagnostics) { + LoadQueue.loadAsync(CASID, /*OutFilePath*/ std::nullopt, + makeCtx(OutputName, CASID, /*IsDiags*/ true)); + continue; + } + if (Path.empty()) { + // The output may be always generated but not needed with this invocation, + // like the serialized diagnostics file. + continue; + } + LoadQueue.loadAsync(CASID, Path.str(), makeCtx(OutputName, CASID)); + } + + bool HasMissingOutput = false; + std::optional SerialDiags; + + while (LoadQueue.hasPending()) { + auto Response = LoadQueue.receiveNext(); + if (!Response) + return Response.takeError(); + const CallCtx &Ctx = *static_cast(Response->CallCtx.get()); + if (Response->KeyNotFound) { + std::string PrintedRemoteCASID = getPrintableRemoteID(Ctx.CASID); + Diags.Report(diag::remark_compile_job_cache_backend_output_not_found) + << Ctx.OutputName << ResultCacheKey.toString() << PrintedRemoteCASID; + HasMissingOutput = true; + continue; + } + if (HasMissingOutput) + continue; + + if (Ctx.IsDiags) + SerialDiags = std::move(Response->BlobData); + } + + if (HasMissingOutput) + return false; + + StringRef MainOutputName = getOutputKindName(OutputKind::MainOutput); + auto MainOutputI = CompResult.find(MainOutputName); + assert(MainOutputI != CompResult.end()); + std::string PrintedRemoteMainOutputCASID = + getPrintableRemoteID(MainOutputI->second); + Diags.Report(diag::remark_compile_job_cache_hit) + << ResultCacheKey.toString() + << (Twine(MainOutputName) + ": " + PrintedRemoteMainOutputCASID).str(); + + if (SerialDiags) { + if (Error E = replayCachedDiagnostics(*SerialDiags)) + return std::move(E); + } + + return true; +} + +bool RemoteCachingOutputs::prepareOutputCollection() { + // Set up the output backend so we can save / cache the result after. + for (OutputKind K : clang::cas::CompileJobCacheResult::getAllOutputKinds()) { + StringRef OutPath = getPathForOutputKind(K); + if (!OutPath.empty()) + CollectingOutputs->addKindMap(getOutputKindName(K), OutPath); + } + + // FIXME: Handle collecting the dependencies as well. + return prepareOutputCollectionCommon(CollectingOutputs); +} + +Expected +RemoteCachingOutputs::saveOutputs(const llvm::cas::CASID &ResultCacheKey) { + DiagnosticsEngine &Diags = Clang.getDiagnostics(); + llvm::ScopedDurationTimer ScopedTime([&Diags](double Seconds) { + Diags.Report(diag::remark_compile_job_cache_timing_backend_store) + << llvm::format("%.6fs", Seconds); + }); + + auto &SaveQueue = RemoteCASClient->saveQueue(); + struct CallCtx : public llvm::cas::remote::AsyncCallerContext { + StringRef OutputName; + CallCtx(StringRef OutputName) : OutputName(OutputName) {} + }; + auto makeCtx = [](StringRef OutputName) + -> std::shared_ptr { + return std::make_shared(OutputName); + }; + + Expected> SerialDiags = + DiagProcessor->serializeEmittedDiagnostics(); + if (!SerialDiags) + return SerialDiags.takeError(); + if (*SerialDiags) { + SaveQueue.saveDataAsync( + std::move(**SerialDiags), + makeCtx(getOutputKindName(OutputKind::SerializedDiagnostics))); + } + + // FIXME: Save dependencies output. + + for (StringRef OutputName : CollectingOutputs->getOutputs()) { + std::optional OutKind = getOutputKindForName(OutputName); + StringRef Path = OutKind ? getPathForOutputKind(*OutKind) : OutputName; + assert(!Path.empty()); + SmallString<256> AbsPath{Path}; + llvm::sys::fs::make_absolute(AbsPath); + SaveQueue.saveFileAsync(AbsPath.str().str(), makeCtx(OutputName)); + } + + // Cache the result. + + llvm::cas::remote::KeyValueDBClient::ValueTy CompResult; + while (SaveQueue.hasPending()) { + auto Response = SaveQueue.receiveNext(); + if (!Response) + return Response.takeError(); + StringRef OutputName = + static_cast(Response->CallCtx.get())->OutputName; + CompResult[OutputName] = Response->CASID; + } + + return std::move(CompResult); +} + +Error RemoteCachingOutputs::addNonVirtualOutputFile(StringRef FilePath) { + CollectingOutputs->addOutput(FilePath); + return Error::success(); +} + +Error RemoteCachingOutputs::finishComputedResult( + const llvm::cas::CASID &ResultCacheKey, + const llvm::cas::CASID &CanonicalResultCacheKey, bool SkipCache) { + if (SkipCache) + return Error::success(); + + // Release the llbuild execution lane while we wait to upload data to remote + // cache. + tryReleaseLLBuildExecutionLane(); + + Expected CompResult = + saveOutputs(ResultCacheKey); + if (!CompResult) + return CompResult.takeError(); + + DiagnosticsEngine &Diags = Clang.getDiagnostics(); + llvm::ScopedDurationTimer ScopedTime([&Diags](double Seconds) { + Diags.Report(diag::remark_compile_job_cache_timing_backend_key_update) + << llvm::format("%.6fs", Seconds); + }); + + RemoteKVClient->putValueQueue().putValueAsync( + CanonicalResultCacheKey.getHash(), *CompResult); + // Tasks depending on this one don't know the canonical cache key. Make sure + // the requested key is associated with the result too. + if (ResultCacheKey != CanonicalResultCacheKey) + RemoteKVClient->putValueQueue().putValueAsync(ResultCacheKey.getHash(), + *CompResult); + + auto Response = RemoteKVClient->putValueQueue().receiveNext(); + if (!Response) + return Response.takeError(); + // Tasks depending on this one don't know the canonical cache key. Make sure + // the requested key is associated with the result too. + if (ResultCacheKey != CanonicalResultCacheKey) { + auto Response2 = RemoteKVClient->putValueQueue().receiveNext(); + if (!Response2) + return Response2.takeError(); + } + + return Error::success(); +} + +std::string +RemoteCachingOutputs::getPrintableRemoteID(StringRef RemoteCASIDBytes) { + // FIXME: Enhance the remote protocol for the service to be able to provide + // a string suitable for logging a remote CASID. + return ""; +} + +void RemoteCachingOutputs::tryReleaseLLBuildExecutionLane() { + if (TriedReleaseLLBuildExecutionLane) + return; + TriedReleaseLLBuildExecutionLane = true; + if (auto LLTaskID = llvm::sys::Process::GetEnv("LLBUILD_TASK_ID")) { + // Use the llbuild protocol to request to release the execution lane for + // this task. + auto LLControlFD = llvm::sys::Process::GetEnv("LLBUILD_CONTROL_FD"); + if (!LLControlFD) + return; // LLBUILD_CONTROL_FD may not be set if a shell script is invoked. + int LLCtrlFD; + bool HasErr = StringRef(*LLControlFD).getAsInteger(10, LLCtrlFD); + if (HasErr) + llvm::report_fatal_error(Twine("failed converting 'LLBUILD_CONTROL_FD' " + "to an integer, it was: ") + + *LLControlFD); + llvm::raw_fd_ostream FDOS(LLCtrlFD, /*shouldClose*/ false); + FDOS << "llbuild.1\n" << LLTaskID << '\n'; + FDOS.flush(); + } +} diff --git a/clang/lib/Frontend/CompileJobCacheKey.cpp b/clang/lib/Frontend/CompileJobCacheKey.cpp new file mode 100644 index 0000000000000..354700d688249 --- /dev/null +++ b/clang/lib/Frontend/CompileJobCacheKey.cpp @@ -0,0 +1,375 @@ +//===- CompileJobCacheKey.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompileJobCacheKey.h" +#include "clang/Basic/DiagnosticCAS.h" +#include "clang/Basic/Version.h" +#include "clang/CAS/IncludeTree.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "llvm/CAS/ActionCache.h" +#include "llvm/CAS/HierarchicalTreeBuilder.h" +#include "llvm/CAS/ObjectStore.h" +#include "llvm/CAS/TreeSchema.h" +#include "llvm/CAS/Utils.h" +#include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::cas; +using namespace llvm; +using namespace llvm::cas; + +static llvm::cas::CASID createCompileJobCacheKeyForArgs( + ObjectStore &CAS, ArrayRef CC1Args, ObjectRef InputRef, + CachingInputKind InputKind) { + assert(!CC1Args.empty() && StringRef(CC1Args[0]) == "-cc1"); + SmallString<256> CommandLine; + for (StringRef Arg : CC1Args) { + CommandLine.append(Arg); + CommandLine.push_back(0); + } + + llvm::cas::HierarchicalTreeBuilder Builder; + switch (InputKind) { + case CachingInputKind::IncludeTree: + Builder.push(InputRef, llvm::cas::TreeEntry::Regular, "include-tree"); + break; + case CachingInputKind::FileSystemRoot: + Builder.push(InputRef, llvm::cas::TreeEntry::Tree, "filesystem"); + break; + case CachingInputKind::CachedCompilation: + // TODO: Pass a "ref" tree entry kind. + Builder.push(InputRef, llvm::cas::TreeEntry::Tree, "cache-key"); + break; + case CachingInputKind::Object: + // TODO: Pass a "ref" tree entry kind. + Builder.push(InputRef, llvm::cas::TreeEntry::Tree, "object"); + break; + } + Builder.push( + llvm::cantFail(CAS.storeFromString(std::nullopt, CommandLine)), + llvm::cas::TreeEntry::Regular, "command-line"); + Builder.push( + llvm::cantFail(CAS.storeFromString(std::nullopt, "-cc1")), + llvm::cas::TreeEntry::Regular, "computation"); + + // FIXME: The version is maybe insufficient... + Builder.push( + llvm::cantFail(CAS.storeFromString(std::nullopt, getClangFullVersion())), + llvm::cas::TreeEntry::Regular, "version"); + + return llvm::cantFail(Builder.create(CAS)).getID(); +} + +static std::optional +createCompileJobCacheKeyImpl(ObjectStore &CAS, DiagnosticsEngine &Diags, + CompilerInvocation CI) { + FrontendOptions &FrontendOpts = CI.getFrontendOpts(); + DependencyOutputOptions &DepOpts = CI.getDependencyOutputOpts(); + + // Keep the key independent of the paths of these outputs. + if (!FrontendOpts.OutputFile.empty()) + FrontendOpts.OutputFile = "-"; + if (!DepOpts.OutputFile.empty()) + DepOpts.OutputFile = "-"; + // Dependency options that do not affect the list of files are canonicalized. + if (!DepOpts.Targets.empty()) + DepOpts.Targets = {"-"}; + DepOpts.UsePhonyTargets = false; + // These are added in when the dependency file is generated, but they don't + // affect the actual compilation. + DepOpts.ExtraDeps.clear(); + + // Canonicalize indexing options. + + // Indexing data are allowed to "escape" the CAS sandbox without indexing + // options affecting the CAS key. Essentially indexing data are produced when + // the compilation is executed but they are not replayed if the compilation is + // cached. + + FrontendOpts.IndexStorePath.clear(); + FrontendOpts.IndexUnitOutputPath.clear(); + FrontendOpts.IndexIgnoreSystemSymbols = false; + FrontendOpts.IndexRecordCodegenName = false; + FrontendOpts.IndexIgnoreMacros = false; + FrontendOpts.IndexIgnorePcms = false; + + // Canonicalize diagnostic options. + + DiagnosticOptions &DiagOpts = CI.getDiagnosticOpts(); + // These options affect diagnostic rendering but not the cached diagnostics. + DiagOpts.ShowLine = false; + DiagOpts.ShowColumn = false; + DiagOpts.ShowLocation = false; + DiagOpts.ShowLevel = false; + DiagOpts.AbsolutePath = false; + DiagOpts.ShowCarets = false; + DiagOpts.ShowFixits = false; + DiagOpts.ShowSourceRanges = false; + DiagOpts.ShowParseableFixits = false; + DiagOpts.ShowPresumedLoc = false; + DiagOpts.ShowOptionNames = false; + DiagOpts.ShowNoteIncludeStack = false; + DiagOpts.ShowCategories = false; + DiagOpts.ShowColors = false; + DiagOpts.UseANSIEscapeCodes = false; + DiagOpts.VerifyDiagnostics = false; + DiagOpts.setVerifyIgnoreUnexpected(DiagnosticLevelMask::None); + // Note: ErrorLimit affects the set of diagnostics emitted, but since we do + // not cache compilation failures, it's safe to clear here. + DiagOpts.ErrorLimit = 0; + DiagOpts.MacroBacktraceLimit = 0; + DiagOpts.SnippetLineLimit = 0; + DiagOpts.TabStop = 0; + DiagOpts.MessageLength = 0; + DiagOpts.DiagnosticLogFile.clear(); + DiagOpts.DiagnosticSerializationFile.clear(); + DiagOpts.VerifyPrefixes.clear(); + + llvm::erase_if(DiagOpts.Remarks, [](StringRef Remark) -> bool { + // These are intended for caching introspection, they are not cached. + return Remark.starts_with("compile-job-cache"); + }); + + // Generate a new command-line in case Invocation has been canonicalized. + llvm::BumpPtrAllocator Alloc; + llvm::StringSaver Saver(Alloc); + llvm::SmallVector Argv; + Argv.push_back("-cc1"); + CI.generateCC1CommandLine( + Argv, [&Saver](const llvm::Twine &T) { return Saver.save(T).data(); }); + + // FIXME: currently correct since the main executable is always in the root + // from scanning, but we should probably make it explicit here... + StringRef InputIDString; + CachingInputKind InputKind; + if (!CI.getFrontendOpts().CASIncludeTreeID.empty()) { + InputIDString = CI.getFrontendOpts().CASIncludeTreeID; + InputKind = CachingInputKind::IncludeTree; + } else if (!CI.getFileSystemOpts().CASFileSystemRootID.empty()) { + InputIDString = CI.getFileSystemOpts().CASFileSystemRootID; + InputKind = CachingInputKind::FileSystemRoot; + } else if (!CI.getFrontendOpts().CASInputFileCacheKey.empty()) { + InputIDString = CI.getFrontendOpts().CASInputFileCacheKey; + InputKind = CachingInputKind::CachedCompilation; + } else if (!CI.getFrontendOpts().CASInputFileCASID.empty()) { + InputIDString = CI.getFrontendOpts().CASInputFileCASID; + InputKind = CachingInputKind::Object; + } else { + Diags.Report(diag::err_cas_cannot_parse_root_id) << ""; + return std::nullopt; + } + + Expected InputID = CAS.parseID(InputIDString); + if (!InputID) { + llvm::consumeError(InputID.takeError()); + Diags.Report(diag::err_cas_cannot_parse_root_id) << InputIDString; + return std::nullopt; + } + std::optional InputRef = CAS.getReference(*InputID); + if (!InputRef) { + Diags.Report(diag::err_cas_missing_root_id) << InputIDString; + return std::nullopt; + } + + return createCompileJobCacheKeyForArgs(CAS, Argv, *InputRef, InputKind); +} + +static CompileJobCachingOptions +canonicalizeForCaching(llvm::cas::ObjectStore &CAS, DiagnosticsEngine &Diags, + CompilerInvocation &Invocation) { + CompileJobCachingOptions Opts; + FrontendOptions &FrontendOpts = Invocation.getFrontendOpts(); + + // Canonicalize settings for caching, extracting settings that affect the + // compilation even if will clear them during the main compilation. + FrontendOpts.CacheCompileJob = false; + Opts.CompilationCachingServicePath = + std::move(FrontendOpts.CompilationCachingServicePath); + FrontendOpts.CompilationCachingServicePath.clear(); + Opts.WriteOutputAsCASID = FrontendOpts.WriteOutputAsCASID; + FrontendOpts.WriteOutputAsCASID = false; + Opts.DisableCachedCompileJobReplay = + FrontendOpts.DisableCachedCompileJobReplay; + FrontendOpts.DisableCachedCompileJobReplay = false; + FrontendOpts.IncludeTimestamps = false; + std::swap(Opts.PathPrefixMappings, FrontendOpts.PathPrefixMappings); + + // Hide the CAS configuration, canonicalizing it to keep the path to the + // CAS from leaking to the compile job, where it might affecting its + // output (e.g., in a diagnostic). + // + // TODO: Extract CASOptions.Path first if we need it later since it'll + // disappear here. + Invocation.getCASOpts() = {}; + // Set the CASPath to the hash schema to match CASOptions::freezeConfig. + Invocation.getCASOpts().CASPath = + CAS.getContext().getHashSchemaIdentifier().str(); + + // TODO: Canonicalize DiagnosticOptions here to be "serialized" only. Pass in + // a hook to mirror diagnostics to stderr (when writing there), and handle + // other outputs during replay. + + // TODO: migrate the output path changes from createCompileJobCacheKey here. + return Opts; +} + +std::optional> +clang::canonicalizeAndCreateCacheKeys(ObjectStore &CAS, + ActionCache &Cache, + DiagnosticsEngine &Diags, + CompilerInvocation &CI, + CompileJobCachingOptions &Opts) { + // Preserve and freeze CASOptions so that we do not modify behaviour of + // Invocation.getCASOpts().getOrCreateDatabases(). + CASOptions CASOpts(CI.getCASOpts()); + CASOpts.freezeConfig(Diags); + + Opts = canonicalizeForCaching(CAS, Diags, CI); + auto CacheKey = createCompileJobCacheKeyImpl(CAS, Diags, CI); + if (!CacheKey) + return std::nullopt; + + assert(CI.getCASOpts().CASPath == CASOpts.CASPath && + "cas instance has incompatible hash with cas options"); + CI.getCASOpts() = std::move(CASOpts); + + if (CI.getFrontendOpts().CASInputFileCacheKey.empty()) + return std::make_pair(*CacheKey, *CacheKey); + + auto ID = CAS.parseID(CI.getFrontendOpts().CASInputFileCacheKey); + if (!ID) { + Diags.Report(diag::err_cas_cannot_parse_input_cache_key) + << CI.getFrontendOpts().CASInputFileCacheKey; + return std::nullopt; + } + auto Value = Cache.get(*ID); + if (!Value) { + Diags.Report(diag::err_cas_cannot_lookup_input_cache_key) + << Value.takeError(); + return std::nullopt; + } + if (!*Value) { + Diags.Report(diag::err_cas_missing_input_cache_entry) + << llvm::cas::ObjectStore::createUnknownObjectError(*ID); + return std::nullopt; + } + + CI.getFrontendOpts().CASInputFileCASID = Value.get()->toString(); + CI.getFrontendOpts().CASInputFileCacheKey.clear(); + + CompilerInvocation CICopy = CI; + (void)canonicalizeForCaching(CAS, Diags, CICopy); + auto CanonicalCacheKey = createCompileJobCacheKeyImpl(CAS, Diags, CICopy); + if (!CanonicalCacheKey) + return std::nullopt; + + return std::make_pair(*CacheKey, *CanonicalCacheKey); +} + +std::optional +clang::createCompileJobCacheKey(ObjectStore &CAS, DiagnosticsEngine &Diags, + const CompilerInvocation &OriginalCI) { + CompilerInvocation CI(OriginalCI); + (void)canonicalizeForCaching(CAS, Diags, CI); + return createCompileJobCacheKeyImpl(CAS, Diags, std::move(CI)); +} + +std::optional +clang::createCompileJobCacheKey(ObjectStore &CAS, DiagnosticsEngine &Diags, + const CowCompilerInvocation &OriginalCI) { + CompilerInvocation CI(OriginalCI); + (void)canonicalizeForCaching(CAS, Diags, CI); + return createCompileJobCacheKeyImpl(CAS, Diags, std::move(CI)); +} + +static Error printFileSystem(ObjectStore &CAS, ObjectRef Root, + raw_ostream &OS) { + TreeSchema Schema(CAS); + return Schema.walkFileTreeRecursively( + CAS, Root, [&](const NamedTreeEntry &Entry, std::optional Tree) { + if (Entry.getKind() != TreeEntry::Tree || Tree->empty()) { + OS << "\n "; + Entry.print(OS, CAS); + } + return Error::success(); + }); +} + +static Error printCompileJobCacheKey(ObjectStore &CAS, ObjectProxy Node, + raw_ostream &OS) { + auto strError = [](const Twine &Err) { + return createStringError(inconvertibleErrorCode(), Err); + }; + + TreeSchema Schema(CAS); + Expected Tree = Schema.load(Node); + if (!Tree) + return Tree.takeError(); + + // Not exhaustive, but quick check that this looks like a cache key. + if (!Tree->lookup("computation")) + return strError("cas object is not a valid cache key"); + + return Tree->forEachEntry([&](const NamedTreeEntry &E) -> Error { + OS << E.getName() << ": " << CAS.getID(E.getRef()); + if (E.getKind() == TreeEntry::Tree) + return printFileSystem(CAS, E.getRef(), OS); + + if (E.getKind() != TreeEntry::Regular) + return strError("expected blob for entry " + E.getName()); + + if (E.getName() == "include-tree") { + auto IncludeTree = IncludeTreeRoot::get(CAS, E.getRef()); + if (!IncludeTree) + return IncludeTree.takeError(); + OS << '\n'; + return IncludeTree->print(OS, 2); + } + + auto Blob = CAS.getProxy(E.getRef()); + if (!Blob) + return Blob.takeError(); + + auto Data = Blob->getData(); + if (E.getName() == "command-line") { + StringRef Arg; + StringRef Trailing; + do { + std::tie(Arg, Data) = Data.split('\0'); + if (Arg.starts_with("-")) { + OS << Trailing << "\n " << Arg; + } else { + OS << " " << Arg; + } + Trailing = " \\"; + } while (!Data.empty()); + } else { + OS << "\n " << Data; + } + OS << "\n"; + return Error::success(); + }); +} + +Error clang::printCompileJobCacheKey(ObjectStore &CAS, const CASID &Key, + raw_ostream &OS) { + auto H = CAS.getProxy(Key); + if (!H) + return H.takeError(); + TreeSchema Schema(CAS); + if (!Schema.isNode(*H)) { + std::string ErrStr; + llvm::raw_string_ostream Err(ErrStr); + Err << "expected cache key to be a CAS tree; got "; + H->getID().print(Err); + return createStringError(inconvertibleErrorCode(), Err.str()); + } + return ::printCompileJobCacheKey(CAS, *H, OS); +} diff --git a/clang/lib/Frontend/CompileJobCacheResult.cpp b/clang/lib/Frontend/CompileJobCacheResult.cpp new file mode 100644 index 0000000000000..1d0f624a2753a --- /dev/null +++ b/clang/lib/Frontend/CompileJobCacheResult.cpp @@ -0,0 +1,195 @@ +//===- CompileJobCacheResult.cpp ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompileJobCacheResult.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::cas; +using namespace llvm::cas; +using llvm::Error; + +ArrayRef +CompileJobCacheResult::getAllOutputKinds() { + static const OutputKind OutputKinds[] = {OutputKind::MainOutput, + OutputKind::SerializedDiagnostics, + OutputKind::Dependencies}; + return ArrayRef(OutputKinds); +} + +Error CompileJobCacheResult::forEachOutput( + llvm::function_ref Callback) const { + size_t Count = getNumOutputs(); + for (size_t I = 0; I < Count; ++I) { + OutputKind Kind = getOutputKind(I); + ObjectRef Object = getOutputObject(I); + if (auto Err = Callback({Object, Kind})) + return Err; + } + return Error::success(); +} + +Error CompileJobCacheResult::forEachLoadedOutput( + llvm::function_ref)> Callback) { + // Kick-off materialization for all outputs concurrently. + SmallVector, 4> FutureOutputs; + size_t Count = getNumOutputs(); + for (size_t I = 0; I < Count; ++I) { + ObjectRef Ref = getOutputObject(I); + FutureOutputs.push_back(getCAS().getProxyFuture(Ref)); + } + + // Make sure all the outputs have materialized. + std::optional OccurredError; + SmallVector, 4> Outputs; + for (auto &FutureOutput : FutureOutputs) { + auto Obj = FutureOutput.get().take(); + if (!Obj) { + if (!OccurredError) + OccurredError = Obj.takeError(); + else + OccurredError = + llvm::joinErrors(std::move(*OccurredError), Obj.takeError()); + continue; + } + Outputs.push_back(*Obj); + } + if (OccurredError) + return std::move(*OccurredError); + + // Pass the loaded outputs. + for (size_t I = 0; I < Count; ++I) { + if (auto Err = Callback({getOutputObject(I), getOutputKind(I)}, Outputs[I])) + return Err; + } + return Error::success(); +} + +CompileJobCacheResult::Output CompileJobCacheResult::getOutput(size_t I) const { + return Output{getOutputObject(I), getOutputKind(I)}; +} + +std::optional +CompileJobCacheResult::getOutput(OutputKind Kind) const { + size_t Count = getNumOutputs(); + for (size_t I = 0; I < Count; ++I) { + OutputKind K = getOutputKind(I); + if (Kind == K) + return Output{getOutputObject(I), Kind}; + } + return std::nullopt; +} + +StringRef CompileJobCacheResult::getOutputKindName(OutputKind Kind) { + switch (Kind) { + case OutputKind::MainOutput: + return "main"; + case OutputKind::SerializedDiagnostics: + return "diags"; + case OutputKind::Dependencies: + return "deps"; + } +} + +Error CompileJobCacheResult::print(llvm::raw_ostream &OS) { + return forEachOutput([&](Output O) -> Error { + OS << getOutputKindName(O.Kind) << " " << getCAS().getID(O.Object) + << '\n'; + return Error::success(); + }); +} + +size_t CompileJobCacheResult::getNumOutputs() const { return getData().size(); } +ObjectRef CompileJobCacheResult::getOutputObject(size_t I) const { + return getReference(I); +} +CompileJobCacheResult::OutputKind +CompileJobCacheResult::getOutputKind(size_t I) const { + return static_cast(getData()[I]); +} + +CompileJobCacheResult::CompileJobCacheResult(const ObjectProxy &Obj) + : ObjectProxy(Obj) {} + +struct CompileJobCacheResult::Builder::PrivateImpl { + SmallVector Objects; + SmallVector Kinds; + + struct KindMap { + OutputKind Kind; + std::string Path; + }; + SmallVector KindMaps; +}; + +CompileJobCacheResult::Builder::Builder() : Impl(*new PrivateImpl) {} +CompileJobCacheResult::Builder::~Builder() { delete &Impl; } + +void CompileJobCacheResult::Builder::addKindMap(OutputKind Kind, + StringRef Path) { + Impl.KindMaps.push_back({Kind, std::string(Path)}); +} +void CompileJobCacheResult::Builder::addOutput(OutputKind Kind, + ObjectRef Object) { + Impl.Kinds.push_back(Kind); + Impl.Objects.push_back(Object); +} +Error CompileJobCacheResult::Builder::addOutput(StringRef Path, + ObjectRef Object) { + Impl.Objects.push_back(Object); + for (auto &KM : Impl.KindMaps) { + if (KM.Path == Path) { + Impl.Kinds.push_back(KM.Kind); + return Error::success(); + } + } + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "cached output file has unknown path '" + + Path + "'"); +} + +Expected CompileJobCacheResult::Builder::build(ObjectStore &CAS) { + CompileJobResultSchema Schema(CAS); + // The resulting Refs contents are: + // Object 0...N, SchemaKind + SmallVector Refs; + std::swap(Impl.Objects, Refs); + Refs.push_back(Schema.getKindRef()); + return CAS.store(Refs, {(char *)Impl.Kinds.begin(), Impl.Kinds.size()}); +} + +static constexpr llvm::StringLiteral CompileJobResultSchemaName = + "llvm::cas::schema::compile_job_result::v1"; + +char CompileJobResultSchema::ID = 0; + +CompileJobResultSchema::CompileJobResultSchema(ObjectStore &CAS) + : CompileJobResultSchema::RTTIExtends(CAS), + KindRef( + llvm::cantFail(CAS.storeFromString({}, CompileJobResultSchemaName))) { +} + +Expected +CompileJobResultSchema::load(ObjectRef Ref) const { + auto Proxy = CAS.getProxy(Ref); + if (!Proxy) + return Proxy.takeError(); + if (!isNode(*Proxy)) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "not a compile job result"); + return CompileJobCacheResult(*Proxy); +} + +bool CompileJobResultSchema::isRootNode(const ObjectProxy &Node) const { + return isNode(Node); +} + +bool CompileJobResultSchema::isNode(const ObjectProxy &Node) const { + size_t N = Node.getNumReferences(); + return N && Node.getReference(N - 1) == getKindRef(); +} diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index cbc9c9dcee05f..dd8d409fe40a6 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -7,11 +7,13 @@ //===----------------------------------------------------------------------===// #include "clang/Frontend/CompilerInstance.h" +#include "clang/APINotes/APINotesReader.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticCAS.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LangStandard.h" @@ -19,8 +21,12 @@ #include "clang/Basic/Stack.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/Version.h" +#include "clang/CAS/IncludeTree.h" #include "clang/Config/config.h" +#include "clang/Frontend/CASDependencyCollector.h" #include "clang/Frontend/ChainedDiagnosticConsumer.h" +#include "clang/Frontend/CompileJobCacheKey.h" +#include "clang/Frontend/CompileJobCacheResult.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/FrontendDiagnostic.h" @@ -31,6 +37,7 @@ #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/Utils.h" #include "clang/Frontend/VerifyDiagnosticConsumer.h" +#include "clang/Index/IndexingAction.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/PreprocessorOptions.h" @@ -45,6 +52,8 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/Statistic.h" +#include "llvm/CAS/ActionCache.h" +#include "llvm/CAS/ObjectStore.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/AdvisoryLock.h" #include "llvm/Support/BuryPointer.h" @@ -58,6 +67,8 @@ #include "llvm/Support/TimeProfiler.h" #include "llvm/Support/Timer.h" #include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/VirtualOutputBackends.h" +#include "llvm/Support/VirtualOutputError.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Host.h" #include @@ -94,6 +105,11 @@ void CompilerInstance::setDiagnostics(DiagnosticsEngine *Value) { Diagnostics = Value; } +bool CompilerInstance::isSourceNonReproducible() const { + assert(PP && "Need to have preprocessor"); + return PP->isSourceNonReproducible(); +} + void CompilerInstance::setVerboseOutputStream(raw_ostream &Value) { OwnedVerboseOutputStream.reset(); VerboseOutputStream = &Value; @@ -381,7 +397,7 @@ FileManager *CompilerInstance::createFileManager( if (!VFS) VFS = FileMgr ? &FileMgr->getVirtualFileSystem() : createVFSFromCompilerInvocation(getInvocation(), - getDiagnostics()); + getDiagnostics(), CAS); assert(VFS && "FileManager has no VFS?"); if (getFrontendOpts().ShowStats) VFS = @@ -486,7 +502,7 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) { PP->setPreprocessedOutput(getPreprocessorOutputOpts().ShowCPP); if (PP->getLangOpts().Modules && PP->getLangOpts().ImplicitModules) { - std::string ModuleHash = getInvocation().getModuleHash(); + std::string ModuleHash = getInvocation().getModuleHash(getDiagnostics()); PP->getHeaderSearchInfo().setModuleHash(ModuleHash); PP->getHeaderSearchInfo().setModuleCachePath( getSpecificModuleCachePath(ModuleHash)); @@ -495,7 +511,8 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) { // Handle generating dependencies, if requested. const DependencyOutputOptions &DepOpts = getDependencyOutputOpts(); if (!DepOpts.OutputFile.empty()) - addDependencyCollector(std::make_shared(DepOpts)); + addDependencyCollector( + std::make_shared(DepOpts, TheOutputBackend)); if (!DepOpts.DOTOutputFile.empty()) AttachDependencyGraphGen(*PP, DepOpts.DOTOutputFile, getHeaderSearchOpts().Sysroot); @@ -516,6 +533,10 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) { collectVFSEntries(*this, ModuleDepCollector); } + // Modules need an output manager. + if (!hasOutputBackend()) + createOutputBackend(); + for (auto &Listener : DependencyCollectors) Listener->attachToPreprocessor(*PP); @@ -608,12 +629,34 @@ struct ReadModuleNames : ASTReaderListener { LoadedModules.clear(); } }; + +class CompileCacheASTReaderHelper : public ASTReaderListener { +public: + CompileCacheASTReaderHelper(cas::ObjectStore &CAS, cas::ActionCache &Cache, + ModuleCache &ModCache, DiagnosticsEngine &Diags) + : CAS(CAS), Cache(Cache), ModCache(ModCache), Diags(Diags) {} + + bool readCASFileSystemRootID(StringRef RootID, bool Complain) override; + bool readIncludeTreeID(StringRef ID, bool Complain) override; + bool readModuleCacheKey(StringRef ModuleName, StringRef Filename, + StringRef CacheKey) override; + +private: + bool checkCASID(bool Complain, StringRef RootID, unsigned ParseDiagID, + unsigned MissingDiagID); + + cas::ObjectStore &CAS; + cas::ActionCache &Cache; + ModuleCache &ModCache; + DiagnosticsEngine &Diags; +}; } // namespace void CompilerInstance::createPCHExternalASTSource( StringRef Path, DisableValidationForModuleKind DisableValidation, bool AllowPCHWithCompilerErrors, void *DeserializationListener, - bool OwnDeserializationListener) { + bool OwnDeserializationListener, + std::unique_ptr PCHBuffer) { bool Preamble = getPreprocessorOpts().PrecompiledPreambleBytes.first != 0; TheASTReader = createPCHExternalASTSource( Path, getHeaderSearchOpts().Sysroot, DisableValidation, @@ -621,7 +664,9 @@ void CompilerInstance::createPCHExternalASTSource( getASTContext(), getPCHContainerReader(), getFrontendOpts().ModuleFileExtensions, DependencyCollectors, DeserializationListener, OwnDeserializationListener, Preamble, - getFrontendOpts().UseGlobalModuleIndex); + getFrontendOpts().UseGlobalModuleIndex, getOrCreateObjectStore(), + getOrCreateActionCache(), getFrontendOpts().ModuleLoadIgnoreCAS, + std::move(PCHBuffer)); } IntrusiveRefCntPtr CompilerInstance::createPCHExternalASTSource( @@ -632,7 +677,9 @@ IntrusiveRefCntPtr CompilerInstance::createPCHExternalASTSource( ArrayRef> Extensions, ArrayRef> DependencyCollectors, void *DeserializationListener, bool OwnDeserializationListener, - bool Preamble, bool UseGlobalModuleIndex) { + bool Preamble, bool UseGlobalModuleIndex, cas::ObjectStore &CAS, + cas::ActionCache &Cache, bool ignoreCAS, + std::unique_ptr PCHBuffer) { const HeaderSearchOptions &HSOpts = PP.getHeaderSearchInfo().getHeaderSearchOpts(); @@ -654,11 +701,19 @@ IntrusiveRefCntPtr CompilerInstance::createPCHExternalASTSource( for (auto &Listener : DependencyCollectors) Listener->attachToASTReader(*Reader); + if (!ignoreCAS) + Reader->addListener(std::make_unique( + CAS, Cache, ModCache, PP.getDiagnostics())); + auto Listener = std::make_unique(PP); auto &ListenerRef = *Listener; ASTReader::ListenerScope ReadModuleNamesListener(*Reader, std::move(Listener)); + if (PCHBuffer) { + Reader->addInMemoryBuffer(Path, std::move(PCHBuffer)); + } + switch (Reader->ReadAST(Path, Preamble ? serialization::MK_Preamble : serialization::MK_PCH, @@ -742,6 +797,52 @@ CompilerInstance::createCodeCompletionConsumer(Preprocessor &PP, return new PrintingCodeCompleteConsumer(Opts, OS); } +static void loadAPINotesFromIncludeTree(cas::ObjectStore &DB, + api_notes::APINotesManager &APINotes, + DiagnosticsEngine &Diags, + StringRef IncludeTreeRootID) { + Expected RootID = DB.parseID(IncludeTreeRootID); + if (!RootID) { + llvm::consumeError(RootID.takeError()); + Diags.Report(diag::err_cas_cannot_parse_include_tree_id) + << IncludeTreeRootID; + return; + } + std::optional Ref = DB.getReference(*RootID); + if (!Ref) { + Diags.Report(diag::err_cas_missing_include_tree_id) << IncludeTreeRootID; + return; + } + auto Root = cas::IncludeTreeRoot::get(DB, *Ref); + if (!Root) { + consumeError(Root.takeError()); + Diags.Report(diag::err_cas_missing_include_tree_id) << IncludeTreeRootID; + return; + } + auto Notes = Root->getAPINotes(); + if (!Notes) { + consumeError(Notes.takeError()); + Diags.Report(diag::err_cas_cannot_load_api_notes_include_tree) + << IncludeTreeRootID; + return; + } + if (!*Notes) + return; + std::vector Buffers; + + if (auto E = (*Notes)->forEachAPINotes([&](StringRef Buffer) { + Buffers.push_back(Buffer); + return llvm::Error::success(); + })) { + consumeError(std::move(E)); + Diags.Report(diag::err_cas_cannot_load_api_notes_include_tree) + << IncludeTreeRootID; + return; + } + + APINotes.loadCurrentModuleAPINotesFromBuffer(Buffers); +} + void CompilerInstance::createSema(TranslationUnitKind TUKind, CodeCompleteConsumer *CompletionConsumer) { TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(), @@ -750,19 +851,36 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, // Set up API notes. TheSema->APINotes.setSwiftVersion(getAPINotesOpts().SwiftVersion); + // If we're building a module and are supposed to load API notes, + // notify the API notes manager. + if (auto currentModule = getPreprocessor().getCurrentModule()) { + // If using include tree, APINotes for current module is loaded from include + // tree. + if (getFrontendOpts().CASIncludeTreeID.empty()) + (void)TheSema->APINotes.loadCurrentModuleAPINotes( + currentModule, getLangOpts().APINotesModules, + getAPINotesOpts().ModuleSearchPaths); + else + loadAPINotesFromIncludeTree( + *getCASOpts().getOrCreateDatabases(getDiagnostics()).first, + TheSema->APINotes, getDiagnostics(), + getFrontendOpts().CASIncludeTreeID); + + // Check for any attributes we should add to the module + for (auto reader : TheSema->APINotes.getCurrentModuleReaders()) { + // swift_infer_import_as_member + if (reader->getModuleOptions().SwiftInferImportAsMember) { + currentModule->IsSwiftInferImportAsMember = true; + break; + } + } + } + // Attach the external sema source if there is any. if (ExternalSemaSrc) { TheSema->addExternalSource(ExternalSemaSrc.get()); ExternalSemaSrc->InitializeSema(*TheSema); } - - // If we're building a module and are supposed to load API notes, - // notify the API notes manager. - if (auto *currentModule = getPreprocessor().getCurrentModule()) { - (void)TheSema->APINotes.loadCurrentModuleAPINotes( - currentModule, getLangOpts().APINotesModules, - getAPINotesOpts().ModuleSearchPaths); - } } // Output Files @@ -770,32 +888,19 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, void CompilerInstance::clearOutputFiles(bool EraseFiles) { // The ASTConsumer can own streams that write to the output files. assert(!hasASTConsumer() && "ASTConsumer should be reset"); - // Ignore errors that occur when trying to discard the temp file. - for (OutputFile &OF : OutputFiles) { - if (EraseFiles) { - if (OF.File) - consumeError(OF.File->discard()); - if (!OF.Filename.empty()) - llvm::sys::fs::remove(OF.Filename); - continue; - } - - if (!OF.File) - continue; - - if (OF.File->TmpName.empty()) { - consumeError(OF.File->discard()); - continue; - } - - llvm::Error E = OF.File->keep(OF.Filename); - if (!E) - continue; - - getDiagnostics().Report(diag::err_unable_to_rename_temp) - << OF.File->TmpName << OF.Filename << std::move(E); - - llvm::sys::fs::remove(OF.File->TmpName); + if (!EraseFiles) { + for (auto &O : OutputFiles) + llvm::handleAllErrors( + O.keep(), + [&](const llvm::vfs::TempFileOutputError &E) { + getDiagnostics().Report(diag::err_unable_to_rename_temp) + << E.getTempPath() << E.getOutputPath() + << E.convertToErrorCode().message(); + }, + [&](const llvm::vfs::OutputError &E) { + getDiagnostics().Report(diag::err_fe_unable_to_open_output) + << E.getOutputPath() << E.convertToErrorCode().message(); + }); } OutputFiles.clear(); if (DeleteBuiltModules) { @@ -829,6 +934,49 @@ std::unique_ptr CompilerInstance::createNullOutputFile() { return std::make_unique(); } +void CompilerInstance::setOutputBackend( + IntrusiveRefCntPtr NewOutputs) { + assert(!TheOutputBackend && "Already has an output manager"); + TheOutputBackend = std::move(NewOutputs); +} + +void CompilerInstance::createOutputBackend() { + assert(!TheOutputBackend && "Already has an output manager"); + TheOutputBackend = llvm::makeIntrusiveRefCnt(); +} + +llvm::vfs::OutputBackend &CompilerInstance::getOutputBackend() { + assert(TheOutputBackend); + return *TheOutputBackend; +} + +llvm::vfs::OutputBackend &CompilerInstance::getOrCreateOutputBackend() { + if (!hasOutputBackend()) + createOutputBackend(); + return getOutputBackend(); +} + +void CompilerInstance::createCASDatabases() { + // Create a new CAS databases from the CompilerInvocation. Future calls to + // createFileManager() will use the same CAS. + std::tie(CAS, ActionCache) = + getInvocation().getCASOpts().getOrCreateDatabases( + getDiagnostics(), + /*CreateEmptyCASOnFailure=*/true); +} + +llvm::cas::ObjectStore &CompilerInstance::getOrCreateObjectStore() { + if (!CAS) + createCASDatabases(); + return *CAS; +} + +llvm::cas::ActionCache &CompilerInstance::getOrCreateActionCache() { + if (!ActionCache) + createCASDatabases(); + return *ActionCache; +} + std::unique_ptr CompilerInstance::createOutputFile(StringRef OutputPath, bool Binary, bool RemoveFileOnSignal, bool UseTemporary, @@ -843,6 +991,89 @@ CompilerInstance::createOutputFile(StringRef OutputPath, bool Binary, return nullptr; } +void CompilerInstance::initializeDelayedInputFileFromCAS() { + auto &Opts = Invocation->getFrontendOpts(); + // Return if no need to initialize or already initialized. + if ((Opts.CASIncludeTreeID.empty() && Opts.CASInputFileCASID.empty()) || + !Opts.Inputs.empty()) + return; + + assert(hasDiagnostics() && "need diagnostics engine for CAS loading"); + + // If there is include tree, initialize the inputs from CAS. + auto reportError = [&](llvm::Error &&E) { + Diagnostics->Report(diag::err_fe_unable_to_load_include_tree) + << Opts.CASIncludeTreeID << llvm::toString(std::move(E)); + }; + auto CAS = Invocation->getCASOpts().getOrCreateDatabases(*Diagnostics).first; + if (!CAS) + return; + if (!Opts.CASIncludeTreeID.empty()) { + auto ID = CAS->parseID(Opts.CASIncludeTreeID); + if (!ID) + return reportError(ID.takeError()); + auto Object = CAS->getReference(*ID); + if (!Object) + return reportError(llvm::cas::ObjectStore::createUnknownObjectError(*ID)); + auto Root = cas::IncludeTreeRoot::get(*CAS, *Object); + if (!Root) + return reportError(Root.takeError()); + auto MainTree = Root->getMainFileTree(); + if (!MainTree) + return reportError(MainTree.takeError()); + auto BaseFile = MainTree->getBaseFile(); + if (!BaseFile) + return reportError(BaseFile.takeError()); + auto FilenameBlob = BaseFile->getFilename(); + if (!FilenameBlob) + return reportError(FilenameBlob.takeError()); + + auto InputFilename = FilenameBlob->getData(); + + if (InputFilename != Module::getModuleInputBufferName()) { + Opts.Inputs.emplace_back(Root->getRef(), InputFilename, Opts.DashX, + /*isSystem=*/false); + } else { + assert(Opts.ProgramAction == frontend::GenerateModule); + + auto Kind = Opts.DashX.withFormat(InputKind::Source); + auto Contents = BaseFile->getContents(); + if (!Contents) + return reportError(Contents.takeError()); + auto Buffer = llvm::MemoryBufferRef(Contents->getData(), InputFilename); + Opts.Inputs.emplace_back(Root->getRef(), Buffer, Kind, + (bool)Opts.IsSystemModule); + } + return; + } + if (!Opts.CASInputFileCASID.empty()) { + auto ID = CAS->parseID(Opts.CASInputFileCASID); + if (!ID) + return reportError(ID.takeError()); + auto ValueRef = CAS->getReference(*ID); + if (!ValueRef) + return reportError(llvm::cas::ObjectStore::createUnknownObjectError(*ID)); + + cas::CompileJobResultSchema Schema(*CAS); + auto Result = Schema.load(*ValueRef); + if (!Result) + return reportError(Result.takeError()); + auto Output = + Result->getOutput(cas::CompileJobCacheResult::OutputKind::MainOutput); + if (!Output) + return reportError( + llvm::createStringError("unable to get the main compilation output")); + + auto OutProxy = CAS->getProxy(Output->Object); + if (!OutProxy) + return reportError(OutProxy.takeError()); + + auto Buff = llvm::MemoryBufferRef(OutProxy->getData(), ""); + Opts.Inputs.emplace_back(Buff, Opts.DashX, /*IsSystem=*/false); + return; + } +} + Expected> CompilerInstance::createOutputFileImpl(StringRef OutputPath, bool Binary, bool RemoveFileOnSignal, @@ -863,98 +1094,20 @@ CompilerInstance::createOutputFileImpl(StringRef OutputPath, bool Binary, OutputPath = *AbsPath; } - std::unique_ptr OS; - std::optional OSFile; + using namespace llvm::vfs; + Expected O = getOrCreateOutputBackend().createFile( + OutputPath, + OutputConfig() + .setTextWithCRLF(!Binary) + .setDiscardOnSignal(RemoveFileOnSignal) + .setAtomicWrite(UseTemporary) + .setImplyCreateDirectories(UseTemporary && CreateMissingDirectories)); + if (!O) + return O.takeError(); - if (UseTemporary) { - if (OutputPath == "-") - UseTemporary = false; - else { - llvm::sys::fs::file_status Status; - llvm::sys::fs::status(OutputPath, Status); - if (llvm::sys::fs::exists(Status)) { - // Fail early if we can't write to the final destination. - if (!llvm::sys::fs::can_write(OutputPath)) - return llvm::errorCodeToError( - make_error_code(llvm::errc::operation_not_permitted)); - - // Don't use a temporary if the output is a special file. This handles - // things like '-o /dev/null' - if (!llvm::sys::fs::is_regular_file(Status)) - UseTemporary = false; - } - } - } - - std::optional Temp; - if (UseTemporary) { - // Create a temporary file. - // Insert -%%%%%%%% before the extension (if any), and because some tools - // (noticeable, clang's own GlobalModuleIndex.cpp) glob for build - // artifacts, also append .tmp. - StringRef OutputExtension = llvm::sys::path::extension(OutputPath); - SmallString<128> TempPath = - StringRef(OutputPath).drop_back(OutputExtension.size()); - TempPath += "-%%%%%%%%"; - TempPath += OutputExtension; - TempPath += ".tmp"; - llvm::sys::fs::OpenFlags BinaryFlags = - Binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_Text; - Expected ExpectedFile = - llvm::sys::fs::TempFile::create( - TempPath, llvm::sys::fs::all_read | llvm::sys::fs::all_write, - BinaryFlags); - - llvm::Error E = handleErrors( - ExpectedFile.takeError(), [&](const llvm::ECError &E) -> llvm::Error { - std::error_code EC = E.convertToErrorCode(); - if (CreateMissingDirectories && - EC == llvm::errc::no_such_file_or_directory) { - StringRef Parent = llvm::sys::path::parent_path(OutputPath); - EC = llvm::sys::fs::create_directories(Parent); - if (!EC) { - ExpectedFile = llvm::sys::fs::TempFile::create( - TempPath, llvm::sys::fs::all_read | llvm::sys::fs::all_write, - BinaryFlags); - if (!ExpectedFile) - return llvm::errorCodeToError( - llvm::errc::no_such_file_or_directory); - } - } - return llvm::errorCodeToError(EC); - }); - - if (E) { - consumeError(std::move(E)); - } else { - Temp = std::move(ExpectedFile.get()); - OS.reset(new llvm::raw_fd_ostream(Temp->FD, /*shouldClose=*/false)); - OSFile = Temp->TmpName; - } - // If we failed to create the temporary, fallback to writing to the file - // directly. This handles the corner case where we cannot write to the - // directory, but can write to the file. - } - - if (!OS) { - OSFile = OutputPath; - std::error_code EC; - OS.reset(new llvm::raw_fd_ostream( - *OSFile, EC, - (Binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF))); - if (EC) - return llvm::errorCodeToError(EC); - } - - // Add the output file -- but don't try to remove "-", since this means we are - // using stdin. - OutputFiles.emplace_back(((OutputPath != "-") ? OutputPath : "").str(), - std::move(Temp)); - - if (!Binary || OS->supportsSeeking()) - return std::move(OS); - - return std::make_unique(std::move(OS)); + O->discardOnDestroy([](llvm::Error E) { consumeError(std::move(E)); }); + OutputFiles.push_back(std::move(*O)); + return OutputFiles.back().createProxy(); } // Initialization Utilities @@ -1018,8 +1171,10 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) { noteBottomOfStack(); auto FinishDiagnosticClient = llvm::make_scope_exit([&]() { - // Notify the diagnostic client that all files were processed. - getDiagnosticClient().finish(); + if (!getFrontendOpts().MayEmitDiagnosticsAfterProcessingSourceFiles) { + // Notify the diagnostic client that all files were processed. + getDiagnosticClient().finish(); + } }); raw_ostream &OS = getVerboseOutputStream(); @@ -1048,6 +1203,8 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) { llvm::sort(getCodeGenOpts().TocDataVarsUserSpecified); llvm::sort(getCodeGenOpts().NoTocDataVars); + initializeDelayedInputFileFromCAS(); + for (const FrontendInputFile &FIF : getFrontendOpts().Inputs) { // Reset the ID tables if we are reusing the SourceManager and parsing // regular files. @@ -1195,6 +1352,7 @@ std::unique_ptr CompilerInstance::cloneForModuleCompileImpl( // Force implicitly-built modules to hash the content of the module file. HSOpts.ModulesHashContent = true; FrontendOpts.Inputs = {Input}; + FrontendOpts.MayEmitDiagnosticsAfterProcessingSourceFiles = false; // Don't free the remapped file buffers; they are owned by our caller. PPOpts.RetainRemappedFileBuffers = true; @@ -1202,7 +1360,8 @@ std::unique_ptr CompilerInstance::cloneForModuleCompileImpl( DiagnosticOptions &DiagOpts = Invocation->getDiagnosticOpts(); DiagOpts.VerifyDiagnostics = 0; - assert(getInvocation().getModuleHash() == Invocation->getModuleHash() && + assert(getInvocation().getModuleHash(getDiagnostics()) == + Invocation->getModuleHash(getDiagnostics()) && "Module hash mismatch!"); // Construct a compiler instance that will be used to actually create the @@ -1257,6 +1416,15 @@ std::unique_ptr CompilerInstance::cloneForModuleCompileImpl( Instance.GetDependencyDirectives = GetDependencyDirectives->cloneFor(Instance.getFileManager()); + // Pass along the GenModuleActionWrapper callback + auto WrapGenModuleAction = getGenModuleActionWrapper(); + Instance.setGenModuleActionWrapper(WrapGenModuleAction); + + // Share an output manager. + assert(hasOutputBackend() && + "Expected an output manager to already be set up"); + Instance.setOutputBackend(&getOutputBackend()); + if (ThreadSafeConfig) { Instance.setModuleDepCollector(ThreadSafeConfig->getModuleDepCollector()); } else { @@ -1291,8 +1459,12 @@ bool CompilerInstance::compileModule(SourceLocation ImportLoc, // thread so that we get a stack large enough. bool Crashed = !llvm::CrashRecoveryContext().RunSafelyOnThread( [&]() { - GenerateModuleFromModuleMapAction Action; - Instance.ExecuteAction(Action); + std::unique_ptr Action( + new GenerateModuleFromModuleMapAction); + if (auto WrapGenModuleAction = Instance.getGenModuleActionWrapper()) + Action = WrapGenModuleAction(Instance.getFrontendOpts(), + std::move(Action)); + Instance.ExecuteAction(*Action); }, DesiredStackSize); @@ -1771,6 +1943,11 @@ void CompilerInstance::createASTReader() { for (auto &Listener : DependencyCollectors) Listener->attachToASTReader(*TheASTReader); + + if (!FEOpts.ModuleLoadIgnoreCAS) + TheASTReader->addListener(std::make_unique( + getOrCreateObjectStore(), getOrCreateActionCache(), getModuleCache(), + getDiagnostics())); } bool CompilerInstance::loadModuleFile( @@ -1943,9 +2120,16 @@ ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST( // Check whether M refers to the file in the prebuilt module path. if (M && M->getASTFile()) - if (auto ModuleFile = FileMgr->getOptionalFileRef(ModuleFilename)) + if (auto ModuleFile = FileMgr->getOptionalFileRef(ModuleFilename)) { if (*ModuleFile == M->getASTFile()) return M; +#if !defined(__APPLE__) + // Workaround for ext4 file system. Also check bypass file if exists. + if (auto Bypass = FileMgr->getBypassFile(*ModuleFile)) + if (*Bypass == M->getASTFile()) + return M; +#endif + } getDiagnostics().Report(ModuleNameLoc, diag::err_module_prebuilt) << ModuleName; @@ -2193,6 +2377,8 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, << Module->getFullModuleName() << SourceRange(Path.front().getLoc(), Path.back().getLoc()); + Module->IsInferredMissingFromUmbrellaHeader = true; + return ModuleLoadResult(Module, ModuleLoadResult::MissingExpected); } @@ -2382,3 +2568,118 @@ void CompilerInstance::setExternalSemaSource( IntrusiveRefCntPtr ESS) { ExternalSemaSrc = std::move(ESS); } + +static bool addCachedModuleFileToInMemoryCache( + StringRef Path, StringRef CacheKey, StringRef Provider, + cas::ObjectStore &CAS, cas::ActionCache &Cache, + ModuleCache &ModCache, DiagnosticsEngine &Diags) { + + if (ModCache.getInMemoryModuleCache().lookupPCM(Path)) + return false; + + auto ID = CAS.parseID(CacheKey); + if (!ID) { + Diags.Report(diag::err_cas_cannot_get_module_cache_key) + << CacheKey << Provider << ID.takeError(); + return true; + } + + auto Value = Cache.get(*ID); + if (!Value || !*Value) { + auto Diag = Diags.Report(diag::err_cas_cannot_get_module_cache_key) + << CacheKey << Provider; + if (!Value) { + Diag << Value.takeError(); + } else { + std::string ErrStr("no such entry in action cache; expected compile:\n"); + llvm::raw_string_ostream Err(ErrStr); + if (auto E = printCompileJobCacheKey(CAS, *ID, Err)) + Diag << std::move(E); + else + Diag << Err.str(); + } + return true; + } + auto ValueRef = CAS.getReference(**Value); + if (!ValueRef) { + Diags.Report(diag::err_cas_cannot_get_module_cache_key) + << CacheKey << Provider << "result module doesn't exist in CAS"; + + return true; + } + + std::optional Result; + cas::CompileJobResultSchema Schema(CAS); + if (llvm::Error E = Schema.load(*ValueRef).moveInto(Result)) { + Diags.Report(diag::err_cas_cannot_get_module_cache_key) + << CacheKey << Provider << std::move(E); + return true; + } + auto Output = + Result->getOutput(cas::CompileJobCacheResult::OutputKind::MainOutput); + if (!Output) + llvm::report_fatal_error("missing main output"); + // FIXME: We wait to materialize each module file before proceeding, which + // introduces latency for a network CAS. Instead we should collect all the + // module keys and materialize them concurrently using \c getProxyFuture, for + // better network utilization. + auto OutputProxy = CAS.getProxy(Output->Object); + if (!OutputProxy) { + Diags.Report(diag::err_cas_cannot_get_module_cache_key) + << CacheKey << Provider << OutputProxy.takeError(); + return true; + } + + ModCache.getInMemoryModuleCache().addPCM(Path, + OutputProxy->getMemoryBuffer()); + return false; +} + +bool CompilerInstance::addCachedModuleFile(StringRef Path, StringRef CacheKey, + StringRef Provider) { + return addCachedModuleFileToInMemoryCache( + Path, CacheKey, Provider, getOrCreateObjectStore(), + getOrCreateActionCache(), getModuleCache(), getDiagnostics()); +} + +bool CompileCacheASTReaderHelper::readModuleCacheKey(StringRef ModuleName, + StringRef Filename, + StringRef CacheKey) { + // FIXME: add name/path of the importing module? + return addCachedModuleFileToInMemoryCache( + Filename, CacheKey, "imported module", CAS, Cache, ModCache, Diags); +} + +/// Verify that ID is in the CAS. Otherwise the module cache probably was +/// created with a different CAS. +bool CompileCacheASTReaderHelper::checkCASID(bool Complain, StringRef RootID, + unsigned ParseDiagID, + unsigned MissingDiagID) { + std::optional ID; + if (errorToBool(CAS.parseID(RootID).moveInto(ID))) { + if (Complain) + Diags.Report(ParseDiagID) << RootID; + return true; + } + if (errorToBool(CAS.getProxy(*ID).takeError())) { + if (Complain) { + Diags.Report(MissingDiagID) << RootID; + } + return true; + } + return false; +} + +bool CompileCacheASTReaderHelper::readCASFileSystemRootID(StringRef RootID, + bool Complain) { + return checkCASID(Complain, RootID, diag::err_cas_cannot_parse_root_id, + diag::err_cas_missing_root_id); +} + +bool CompileCacheASTReaderHelper::readIncludeTreeID(StringRef ID, + bool Complain) { + // Verify that ID is in the CAS. Otherwise the module cache probably was + // created with a different CAS. + return checkCASID(Complain, ID, diag::err_cas_cannot_parse_include_tree_id, + diag::err_cas_missing_include_tree_id); +} diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 8ff62ae2552c3..3252a29b7a44f 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -13,6 +13,7 @@ #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/CommentOptions.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticCAS.h" #include "clang/Basic/DiagnosticDriver.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileSystemOptions.h" @@ -26,11 +27,14 @@ #include "clang/Basic/Version.h" #include "clang/Basic/Visibility.h" #include "clang/Basic/XRayInstr.h" +#include "clang/CAS/IncludeTree.h" #include "clang/Config/config.h" +#include "clang/Driver/BoundsSafetyArgs.h" #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/Options.h" #include "clang/Frontend/CommandLineSourceLoc.h" +#include "clang/Frontend/CompileJobCacheResult.h" #include "clang/Frontend/DependencyOutputOptions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/FrontendOptions.h" @@ -56,6 +60,10 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" +#include "llvm/CAS/ActionCache.h" +#include "llvm/CAS/CASFileSystem.h" +#include "llvm/CAS/ObjectStore.h" +#include "llvm/CAS/TreeSchema.h" #include "llvm/Config/llvm-config.h" #include "llvm/Frontend/Debug/Options.h" #include "llvm/IR/DebugInfoMetadata.h" @@ -145,6 +153,7 @@ CompilerInvocationBase::CompilerInvocationBase() AnalyzerOpts(std::make_shared()), MigratorOpts(std::make_shared()), APINotesOpts(std::make_shared()), + CASOpts(std::make_shared()), CodeGenOpts(std::make_shared()), FSOpts(std::make_shared()), FrontendOpts(std::make_shared()), @@ -162,6 +171,7 @@ CompilerInvocationBase::deep_copy_assign(const CompilerInvocationBase &X) { AnalyzerOpts = make_shared_copy(X.getAnalyzerOpts()); MigratorOpts = make_shared_copy(X.getMigratorOpts()); APINotesOpts = make_shared_copy(X.getAPINotesOpts()); + CASOpts = make_shared_copy(X.getCASOpts()); CodeGenOpts = make_shared_copy(X.getCodeGenOpts()); FSOpts = make_shared_copy(X.getFileSystemOpts()); FrontendOpts = make_shared_copy(X.getFrontendOpts()); @@ -182,6 +192,7 @@ CompilerInvocationBase::shallow_copy_assign(const CompilerInvocationBase &X) { AnalyzerOpts = X.AnalyzerOpts; MigratorOpts = X.MigratorOpts; APINotesOpts = X.APINotesOpts; + CASOpts = X.CASOpts; CodeGenOpts = X.CodeGenOpts; FSOpts = X.FSOpts; FrontendOpts = X.FrontendOpts; @@ -250,6 +261,10 @@ APINotesOptions &CowCompilerInvocation::getMutAPINotesOpts() { return ensureOwned(APINotesOpts); } +CASOptions &CowCompilerInvocation::getMutCASOpts() { + return ensureOwned(CASOpts); +} + CodeGenOptions &CowCompilerInvocation::getMutCodeGenOpts() { return ensureOwned(CodeGenOpts); } @@ -525,6 +540,30 @@ static void denormalizeStringVector(ArgumentConsumer Consumer, } } +using StringPair = std::pair; + +static std::optional>> +normalizeStringPairVector(OptSpecifier Opt, int, const ArgList &Args, + DiagnosticsEngine &) { + std::vector> Pairs; + for (StringRef Arg : Args.getAllArgValues(Opt)) { + auto [L, R] = Arg.split('='); + Pairs.emplace_back(std::string(L), std::string(R)); + } + return Pairs; +} + +static void denormalizeStringPairVector( + ArgumentConsumer Consumer, unsigned SpellingOffset, + Option::OptionClass OptClass, unsigned TableIndex, + const std::vector> &Values) { + std::vector Joined; + for (const auto &Pair : Values) { + Joined.push_back(Pair.first + "=" + Pair.second); + } + denormalizeStringVector(Consumer, SpellingOffset, OptClass, TableIndex, Joined); +} + static std::optional normalizeTriple(OptSpecifier Opt, int TableIndex, const ArgList &Args, @@ -707,6 +746,13 @@ static bool FixupInvocation(CompilerInvocation &Invocation, << A->getSpelling() << T.getTriple(); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (!LangOpts.BoundsSafety && + Invocation.getDiagnosticOpts().BoundsSafetyAdoptionMode) { + Diags.Report(diag::warn_bounds_safety_adoption_mode_ignored); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + return Diags.getNumErrors() == NumErrorsBefore; } @@ -774,6 +820,17 @@ static void GenerateArg(ArgumentConsumer Consumer, denormalizeString(Consumer, Opt.getPrefixedName(), Opt.getKind(), 0, Value); } +static void GenerateMultiArg(ArgumentConsumer Consumer, + llvm::opt::OptSpecifier OptSpecifier, + ArrayRef Values) { + Option Opt = getDriverOptTable().getOption(OptSpecifier); + assert(Opt.getKind() == Option::MultiArgClass); + assert(Opt.getNumArgs() == Values.size()); + Consumer(Opt.getPrefix() + Opt.getName()); + for (StringRef Value : Values) + Consumer(Value); +} + // Parse command line arguments into CompilerInvocation. using ParseFn = llvm::function_ref, @@ -1493,6 +1550,98 @@ static std::string serializeXRayInstrumentationBundle(const XRayInstrSet &S) { return Buffer; } +static IntrusiveRefCntPtr +createBaseFS(const FileSystemOptions &FSOpts, const FrontendOptions &FEOpts, + const CASOptions &CASOpts, DiagnosticsEngine &Diags, + std::shared_ptr OverrideCAS) { + if (FSOpts.CASFileSystemRootID.empty() && FEOpts.CASIncludeTreeID.empty()) + return llvm::vfs::getRealFileSystem(); + + // If no CAS was provided, create one with CASOptions. + std::shared_ptr CAS = std::move(OverrideCAS); + if (!CAS) + CAS = CASOpts.getOrCreateDatabases(Diags).first; + + // Helper for creating a valid (but empty) CASFS if an error is encountered. + auto makeEmptyCASFS = [&CAS]() { + // Try to use the configured CAS, if any. + std::optional EmptyRootID; + if (CAS) { + llvm::cas::TreeSchema Schema(*CAS); + // If we cannot create an empty tree, fall back to creating an empty + // in-memory CAS. + if (llvm::Error E = Schema.create(std::nullopt).moveInto(EmptyRootID)) { + consumeError(std::move(E)); + CAS = nullptr; + } + } + // Create an empty in-memory CAS with an empty tree. + if (!CAS) { + CAS = llvm::cas::createInMemoryCAS(); + llvm::cas::TreeSchema Schema(*CAS); + EmptyRootID = llvm::cantFail(Schema.create(std::nullopt)); + } + return llvm::cantFail( + llvm::cas::createCASFileSystem(std::move(CAS), *EmptyRootID)); + }; + + // CAS couldn't be created. The error was already reported to Diags. + if (!CAS) + return makeEmptyCASFS(); + + auto IsIncludeTreeFS = !FEOpts.CASIncludeTreeID.empty(); + + StringRef RootIDString = + IsIncludeTreeFS ? FEOpts.CASIncludeTreeID : FSOpts.CASFileSystemRootID; + + Expected RootID = CAS->parseID(RootIDString); + if (!RootID) { + llvm::consumeError(RootID.takeError()); + Diags.Report(diag::err_cas_cannot_parse_root_id) << RootIDString; + return makeEmptyCASFS(); + } + + auto makeIncludeTreeFS = [&](std::shared_ptr CAS, + llvm::cas::CASID &ID) + -> Expected> { + std::optional Ref = CAS->getReference(ID); + if (!Ref) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "RootID does not exist"); + auto Root = cas::IncludeTreeRoot::get(*CAS, *Ref); + if (!Root) + return Root.takeError(); + return cas::createIncludeTreeFileSystem(*Root); + }; + auto makeCASFS = [&](std::shared_ptr CAS, + llvm::cas::CASID &ID) + -> Expected> { + Expected> ExpectedFS = + llvm::cas::createCASFileSystem(std::move(CAS), ID); + if (!ExpectedFS) + return ExpectedFS.takeError(); + return std::move(*ExpectedFS); + }; + + auto ExpectedFS = IsIncludeTreeFS ? makeIncludeTreeFS(std::move(CAS), *RootID) + : makeCASFS(std::move(CAS), *RootID); + if (!ExpectedFS) { + Diags.Report(diag::err_cas_filesystem_cannot_be_initialized) + << RootIDString << llvm::toString(ExpectedFS.takeError()); + return makeEmptyCASFS(); + } + IntrusiveRefCntPtr FS = std::move(*ExpectedFS); + + // Try to change directories. + StringRef CWD = FSOpts.CASFileSystemWorkingDirectory; + if (!CWD.empty()) + if (std::error_code EC = FS->setCurrentWorkingDirectory(CWD)) + Diags.Report(diag::err_cas_filesystem_cannot_set_working_directory) + << CWD; + + return FS; +} + // Set the profile kind using fprofile-instrument-use-path. static void setPGOUseInstrumentor(CodeGenOptions &Opts, const Twine &ProfileName, @@ -1524,6 +1673,43 @@ static void setPGOUseInstrumentor(CodeGenOptions &Opts, void CompilerInvocation::setDefaultPointerAuthOptions( PointerAuthOptions &Opts, const LangOptions &LangOpts, const llvm::Triple &Triple) { + + if (LangOpts.SoftPointerAuth) { + if (LangOpts.PointerAuthCalls) { + using Key = PointerAuthSchema::SoftKey; + using Discrimination = PointerAuthSchema::Discrimination; + Opts.FunctionPointers = PointerAuthSchema( + Key::FunctionPointers, false, Discrimination::None); + + Opts.CXXVTablePointers = PointerAuthSchema( + Key::CXXVTablePointers, + LangOpts.PointerAuthVTPtrAddressDiscrimination, + LangOpts.PointerAuthVTPtrTypeDiscrimination ? Discrimination::Type + : Discrimination::None); + Opts.CXXVTTVTablePointers = PointerAuthSchema( + Key::CXXVTablePointers, false, Discrimination::None); + Opts.CXXVirtualFunctionPointers = + Opts.CXXVirtualVariadicFunctionPointers = PointerAuthSchema( + Key::CXXVirtualFunctionPointers, true, Discrimination::Decl); + Opts.CXXMemberFunctionPointers = PointerAuthSchema( + Key::CXXMemberFunctionPointers, false, Discrimination::Type); + + Opts.BlockInvocationFunctionPointers = PointerAuthSchema( + Key::BlockInvocationFunctionPointers, true, Discrimination::None); + Opts.BlockHelperFunctionPointers = PointerAuthSchema( + Key::BlockHelperFunctionPointers, true, Discrimination::None); + Opts.BlockByrefHelperFunctionPointers = PointerAuthSchema( + Key::BlockHelperFunctionPointers, true, Discrimination::None); + Opts.ObjCMethodListFunctionPointers = PointerAuthSchema( + Key::ObjCMethodListFunctionPointers, true, Discrimination::None); + + } + Opts.ReturnAddresses = LangOpts.PointerAuthReturns; + Opts.IndirectGotos = LangOpts.PointerAuthIndirectGotos; + Opts.AuthTraps = LangOpts.PointerAuthAuthTraps; + return; + } + assert(Triple.getArch() == llvm::Triple::aarch64); if (LangOpts.PointerAuthCalls) { using Key = PointerAuthSchema::ARM8_3Key; @@ -1559,6 +1745,16 @@ void CompilerInvocation::setDefaultPointerAuthOptions( Key::ASIA, LangOpts.PointerAuthInitFiniAddressDiscrimination, Discrimination::Constant, InitFiniPointerConstantDiscriminator); } + + Opts.BlockInvocationFunctionPointers = + PointerAuthSchema(Key::ASIA, true, Discrimination::None); + Opts.BlockHelperFunctionPointers = + PointerAuthSchema(Key::ASIA, true, Discrimination::None); + Opts.BlockByrefHelperFunctionPointers = + PointerAuthSchema(Key::ASIA, true, Discrimination::None); + + Opts.ObjCMethodListFunctionPointers = + PointerAuthSchema(Key::ASIA, true, Discrimination::None); } Opts.ReturnAddresses = LangOpts.PointerAuthReturns; Opts.AuthTraps = LangOpts.PointerAuthAuthTraps; @@ -1838,6 +2034,10 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts, if (!Opts.EmitVersionIdentMetadata) GenerateArg(Consumer, OPT_Qn); + if (!Opts.SplitColdCode && (Opts.OptimizationLevel > 0) && + (Opts.OptimizeSize != 2)) + GenerateArg(Consumer, OPT_fno_split_cold_code); + switch (Opts.FiniteLoops) { case CodeGenOptions::FiniteLoopsKind::Language: break; @@ -1858,7 +2058,10 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, DiagnosticsEngine &Diags, const llvm::Triple &T, const std::string &OutputFile, - const LangOptions &LangOptsRef) { + const LangOptions &LangOptsRef, + const FileSystemOptions &FSOpts, + const FrontendOptions &FEOpts, + const CASOptions &CASOpts) { unsigned NumErrorsBefore = Diags.getNumErrors(); unsigned OptimizationLevel = getOptimizationLevel(Args, IK, Diags); @@ -2000,6 +2203,11 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, : llvm::codegenoptions::DebugTemplateNamesKind::Mangled); } + if (!Opts.ProfileInstrumentUsePath.empty()) { + auto FS = createBaseFS(FSOpts, FEOpts, CASOpts, Diags, nullptr); + setPGOUseInstrumentor(Opts, Opts.ProfileInstrumentUsePath, *FS, Diags); + } + if (const Arg *A = Args.getLastArg(OPT_ftime_report, OPT_ftime_report_EQ)) { Opts.TimePasses = true; @@ -2330,6 +2538,15 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, if (!LangOpts->CUDAIsDevice) parsePointerAuthOptions(Opts.PointerAuth, *LangOpts, T, Diags); + // -f[no-]split-cold-code + // This may only be enabled when optimizing, and when small code size + // increases are tolerable. + // + // swift-clang: Enable hot/cold splitting by default. + Opts.SplitColdCode = + (Opts.OptimizationLevel > 0) && (Opts.OptimizeSize != 2) && + Args.hasFlag(OPT_fsplit_cold_code, OPT_fno_split_cold_code, true); + if (Args.hasArg(options::OPT_ffinite_loops)) Opts.FiniteLoops = CodeGenOptions::FiniteLoopsKind::Always; else if (Args.hasArg(options::OPT_fno_finite_loops)) @@ -2507,6 +2724,29 @@ static bool checkVerifyPrefixes(const std::vector &VerifyPrefixes, return Success; } +void CompilerInvocationBase::GenerateCASArgs(const CASOptions &Opts, + ArgumentConsumer Consumer) { + const CASOptions &CASOpts = Opts; + +#define CAS_OPTION_WITH_MARSHALLING(...) \ + GENERATE_OPTION_WITH_MARSHALLING(Consumer, __VA_ARGS__) +#include "clang/Driver/Options.inc" +#undef CAS_OPTION_WITH_MARSHALLING +} + +bool CompilerInvocation::ParseCASArgs(CASOptions &Opts, const ArgList &Args, + DiagnosticsEngine &Diags) { + CASOptions &CASOpts = Opts; + bool Success = true; + +#define CAS_OPTION_WITH_MARSHALLING(...) \ + PARSE_OPTION_WITH_MARSHALLING(Args, Diags, __VA_ARGS__) +#include "clang/Driver/Options.inc" +#undef CAS_OPTION_WITH_MARSHALLING + + return Success; +} + static void GenerateFileSystemArgs(const FileSystemOptions &Opts, ArgumentConsumer Consumer) { const FileSystemOptions &FileSystemOpts = Opts; @@ -2893,6 +3133,8 @@ static void GenerateFrontendArgs(const FrontendOptions &Opts, for (const auto &ModuleFile : Opts.ModuleFiles) GenerateArg(Consumer, OPT_fmodule_file, ModuleFile); + for (const auto &A : Opts.ModuleCacheKeys) + GenerateMultiArg(Consumer, OPT_fmodule_file_cache_key, {A.first, A.second}); if (Opts.AuxTargetCPU) GenerateArg(Consumer, OPT_aux_target_cpu, *Opts.AuxTargetCPU); @@ -2972,11 +3214,13 @@ static void GenerateFrontendArgs(const FrontendOptions &Opts, // OPT_INPUT has a unique class, generate it directly. for (const auto &Input : Opts.Inputs) - Consumer(Input.getFile()); + if (!Input.isIncludeTree() && !Input.isBuffer()) + Consumer(Input.getFile()); } static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, - DiagnosticsEngine &Diags, bool &IsHeaderFile) { + CASOptions &CASOpts, DiagnosticsEngine &Diags, + bool &IsHeaderFile) { unsigned NumErrorsBefore = Diags.getNumErrors(); FrontendOptions &FrontendOpts = Opts; @@ -3110,6 +3354,11 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, if (!Val.contains('=')) Opts.ModuleFiles.push_back(std::string(Val)); } + for (const Arg *A : Args.filtered(OPT_fmodule_file_cache_key)) { + ArrayRef Values = A->getValues(); + assert(Values.size() == 2); + Opts.ModuleCacheKeys.emplace_back(Values[0], Values[1]); + } if (Opts.ProgramAction != frontend::GenerateModule && Opts.IsSystemModule) Diags.Report(diag::err_drv_argument_only_allowed_with) << "-fsystem-module" @@ -3214,6 +3463,21 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, // '-' is the default input if none is given. std::vector Inputs = Args.getAllArgValues(OPT_INPUT); Opts.Inputs.clear(); + + if (!Opts.CASIncludeTreeID.empty() || !Opts.CASInputFileCacheKey.empty()) { + if (!Inputs.empty()) + Diags.Report(diag::err_drv_inputs_and_cas_input); + + if (DashX.isUnknown()) { + Diags.Report(diag::err_drv_include_tree_miss_input_kind); + DashX = Language::C; // set to default C to avoid crashing. + } + + // Quit early as include tree will delay input initialization. + Opts.DashX = DashX; + return Diags.getNumErrors() == NumErrorsBefore; + } + if (Inputs.empty()) Inputs.push_back("-"); @@ -3521,6 +3785,11 @@ static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args, } for (const Arg *A : Args.filtered(OPT_iapinotes_modules)) Opts.ModuleSearchPaths.push_back(A->getValue()); + + if (Args.hasFlag(OPT_fapinotes, OPT_fno_apinotes, false) && + Args.hasArg(OPT_fcas_include_tree)) + diags.Report(diag::err_drv_incompatible_option_include_tree) + << "-fapinotes"; } static void GeneratePointerAuthArgs(const LangOptions &Opts, @@ -3551,6 +3820,16 @@ static void GeneratePointerAuthArgs(const LangOptions &Opts, GenerateArg(Consumer, OPT_fptrauth_elf_got); if (Opts.AArch64JumpTableHardening) GenerateArg(Consumer, OPT_faarch64_jump_table_hardening); + + if (Opts.SoftPointerAuth) + GenerateArg(Consumer, OPT_fptrauth_soft); + + if (Opts.PointerAuthABIVersionEncoded) { + GenerateArg(Consumer, OPT_fptrauth_abi_version_EQ, + Twine(Opts.PointerAuthABIVersion)); + if (Opts.PointerAuthKernelABIVersion) + GenerateArg(Consumer, OPT_fptrauth_kernel_abi_version); + } } static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, @@ -3574,6 +3853,15 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args, Opts.PointerAuthELFGOT = Args.hasArg(OPT_fptrauth_elf_got); Opts.AArch64JumpTableHardening = Args.hasArg(OPT_faarch64_jump_table_hardening); + + Opts.SoftPointerAuth = Args.hasArg(OPT_fptrauth_soft); + + Opts.PointerAuthABIVersionEncoded = + Args.hasArg(OPT_fptrauth_abi_version_EQ) || + Args.hasArg(OPT_fptrauth_kernel_abi_version); + Opts.PointerAuthABIVersion = + getLastArgIntValue(Args, OPT_fptrauth_abi_version_EQ, 0, Diags); + Opts.PointerAuthKernelABIVersion = Args.hasArg(OPT_fptrauth_kernel_abi_version); } /// Check if input file kind and language standard are compatible. @@ -3707,6 +3995,9 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts, #include "clang/Driver/Options.inc" #undef LANG_OPTION_WITH_MARSHALLING + if (Opts.NeededByPCHOrCompilationUsesPCH) + GenerateArg(Consumer, OPT_fmodule_related_to_pch); + // The '-fcf-protection=' option is generated by CodeGenOpts generator. if (Opts.ObjC) { @@ -3766,6 +4057,39 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts, GenerateArg(Consumer, OPT_ftrigraphs); } + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (Opts.BoundsSafety && + Opts.BoundsSafetyBringUpMissingChecks != + LangOptions::getDefaultBoundsSafetyNewChecksMask()) { + std::string Checks; + if (Opts.BoundsSafetyBringUpMissingChecks == + LangOptions::getBoundsSafetyNewChecksMaskForGroup("all")) { + Checks = "all"; + } else if (Opts.BoundsSafetyBringUpMissingChecks == + LangOptions::getBoundsSafetyNewChecksMaskForGroup("batch_0")) { + Checks = "batch_0"; + } else { + if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_AccessSize)) + Checks += "access_size,"; + if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_IndirectCountUpdate)) + Checks += "indirect_count_update,"; + if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_ReturnSize)) + Checks += "return_size,"; + if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_EndedByLowerBound)) + Checks += "ended_by_lower_bound,"; + if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_CompoundLiteralInit)) + Checks += "compound_literal_init,"; + if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_LibCAttributes)) + Checks += "libc_attributes,"; + if (Opts.hasNewBoundsSafetyCheck(LangOptions::BS_CHK_ArraySubscriptAgg)) + Checks += "array_subscript_agg"; + if (StringRef(Checks).ends_with(",")) + Checks.pop_back(); + } + GenerateArg(Consumer, OPT_fbounds_safety_bringup_missing_checks_EQ, Checks); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (T.isOSzOS() && !Opts.ZOSExt) GenerateArg(Consumer, OPT_fno_zos_extensions); else if (Opts.ZOSExt) @@ -3959,6 +4283,34 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts, GenerateArg(Consumer, OPT_frandomize_layout_seed_EQ, Opts.RandstructSeed); } +static bool parseFeatureAvailability(const Arg *A, LangOptions &Opts, + DiagnosticsEngine &Diags) { + llvm::StringRef ArgRef(A->getValue()); + auto Split = ArgRef.split(':'); + llvm::StringRef Feature = Split.first; + std::string StateStr = Split.second.lower(); + + if (Feature.empty() || StateStr.empty()) { + Diags.Report(clang::diag::err_feature_availability_flag_invalid_value) + << ArgRef; + return false; + } + + FeatureAvailKind State; + if (StateStr == "on" || StateStr == "1") { + State = FeatureAvailKind::Available; + } else if (StateStr == "off" || StateStr == "0") { + State = FeatureAvailKind::Unavailable; + } else { + Diags.Report(clang::diag::err_feature_availability_flag_invalid_value) + << ArgRef; + return false; + } + + Opts.setFeatureAvailability(Feature, State); + return true; +} + bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, const llvm::Triple &T, std::vector &Includes, @@ -3983,6 +4335,9 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, return Diags.getNumErrors() == NumErrorsBefore; } + for (const Arg *A : Args.filtered(OPT_ffeature_availability_EQ)) + parseFeatureAvailability(A, Opts, Diags); + // Other LangOpts are only initialized when the input is not AST or LLVM IR. // FIXME: Should we really be parsing this for an Language::Asm input? @@ -4066,6 +4421,9 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, #include "clang/Driver/Options.inc" #undef LANG_OPTION_WITH_MARSHALLING + if (Args.hasArg(OPT_fmodule_related_to_pch)) + Opts.NeededByPCHOrCompilationUsesPCH = true; + if (const Arg *A = Args.getLastArg(OPT_fcf_protection_EQ)) { StringRef Name = A->getValue(); if (Name == "full") { @@ -4147,6 +4505,9 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, (Opts.ObjCRuntime.getKind() == ObjCRuntime::FragileMacOSX); } + if (Opts.CPlusPlusModules && !Opts.ModulesLocalVisibility) + Diags.Report(diag::err_modules_no_lsv); + if (Arg *A = Args.getLastArg(options::OPT_fgnuc_version_EQ)) { // Check that the version has 1 to 3 components and the minor and patch // versions fit in two decimal digits. @@ -4197,6 +4558,71 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.Trigraphs = Args.hasFlag(OPT_ftrigraphs, OPT_fno_trigraphs, Opts.Trigraphs); + /*TO_UPSTREAM(BoundsSafety) ON*/ + // Parse the enabled checks and emit any necessary diagnostics + Opts.BoundsSafetyBringUpMissingChecks = + ParseBoundsSafetyNewChecksMaskFromArgs(Args, &Diags); + + // -fbounds-safety should be automatically marshalled into `Opts` in + // GenerateFrontendArgs() via `LangOpts<"BoundsSafety">` on + // `defm bounds_safety` in `clang/Driver/Options.td`. + + if (Opts.BoundsSafety) { + // If BoundsSafety is enabled, check that it is for a supported language. + // Currently, this is only C in languages that Clang has to parse. However, + // it's also possible to pass assembly files and LLVM IR through Clang, and + // those should be trivially supported. This is especially important because + // some build systems, like xcbuild and somewhat clumsy Makefiles, will pass + // C_FLAGS to Clang while building assembly files. + bool SupportsBoundsSafety = false; + switch (IK.getLanguage()) { + case Language::Unknown: + case Language::Asm: + case Language::LLVM_IR: + case Language::C: + SupportsBoundsSafety = true; + break; + case Language::ObjC: + SupportsBoundsSafety = Opts.BoundsAttributesObjCExperimental; + break; + case Language::CXX: + SupportsBoundsSafety = Opts.BoundsAttributesCXXExperimental; + break; + default: + break; + } + if (!SupportsBoundsSafety) + Diags.Report(diag::error_bounds_safety_lang_not_supported); + } + + bool IsBoundsSafetyAttributesExplicitlyDisabled = + !Args.hasFlag(OPT_fexperimental_bounds_safety_attributes, + OPT_fno_experimental_bounds_safety_attributes, true); + if (Opts.BoundsSafety && IsBoundsSafetyAttributesExplicitlyDisabled) + Diags.Report(diag::err_bounds_safety_attributes_cannot_be_disabled); + + if (Opts.BoundsSafety) { + // BoundsSafety implies BoundsSafetyAttributes. + Opts.BoundsSafetyAttributes = 1; + } + + if (Opts.BoundsSafetyAttributes) { + // Have -fbounds-safety silently imply + // `-fexperimental-late-parse-attributes`. This is kind of a hack because it + // won't be visible in the command line invocation. + Opts.ExperimentalLateParseAttributes = 1; + } + + if (Opts.BoundsAttributesCXXExperimental && !Opts.BoundsSafety) + Diags.Report(diag::warn_bounds_attributes_cxx_experimental_ignored); + + if (Opts.BoundsAttributesObjCExperimental && !Opts.BoundsSafety) + Diags.Report(diag::warn_bounds_attributes_objc_experimental_ignored); + + if (!Opts.BoundsSafetyRelaxedSystemHeaders && !Opts.BoundsSafety) + Diags.Report(diag::warn_bounds_safety_relaxed_system_headers_ignored); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + Opts.ZOSExt = Args.hasFlag(OPT_fzos_extensions, OPT_fno_zos_extensions, T.isOSzOS()); @@ -5015,12 +5441,14 @@ bool CompilerInvocation::CreateFromArgsImpl( << ArgString << Nearest; } + ParseCASArgs(Res.getCASOpts(), Args, Diags); ParseFileSystemArgs(Res.getFileSystemOpts(), Args, Diags); ParseMigratorArgs(Res.getMigratorOpts(), Args, Diags); ParseAnalyzerArgs(Res.getAnalyzerOpts(), Args, Diags); ParseDiagnosticArgs(Res.getDiagnosticOpts(), Args, &Diags, /*DefaultDiagColor=*/false); - ParseFrontendArgs(Res.getFrontendOpts(), Args, Diags, LangOpts.IsHeaderFile); + ParseFrontendArgs(Res.getFrontendOpts(), Args, Res.getCASOpts(), Diags, + LangOpts.IsHeaderFile); // FIXME: We shouldn't have to pass the DashX option around here InputKind DashX = Res.getFrontendOpts().DashX; ParseTargetArgs(Res.getTargetOpts(), Args, Diags); @@ -5036,6 +5464,7 @@ bool CompilerInvocation::CreateFromArgsImpl( Res.getHeaderSearchOpts().ModulesSkipHeaderSearchPaths = true; } ParseAPINotesArgs(Res.getAPINotesOpts(), Args, Diags); + ParsePointerAuthArgs(LangOpts, Args, Diags); ParsePointerAuthArgs(LangOpts, Args, Diags); @@ -5067,7 +5496,20 @@ bool CompilerInvocation::CreateFromArgsImpl( Res.getTargetOpts().HostTriple = Res.getFrontendOpts().AuxTriple; ParseCodeGenArgs(Res.getCodeGenOpts(), Args, DashX, Diags, T, - Res.getFrontendOpts().OutputFile, LangOpts); + Res.getFrontendOpts().OutputFile, LangOpts, + Res.getFileSystemOpts(), Res.getFrontendOpts(), + Res.getCASOpts()); + + // BEGIN MCCAS + if (!Res.getFrontendOpts().CompilationCachingServicePath.empty()) { + if (Res.getCodeGenOpts().UseCASBackend) + Diags.Report(diag::err_fe_incompatible_option_with_remote_cache) + << "-fcas-backend"; + if (Res.getFrontendOpts().WriteOutputAsCASID) + Diags.Report(diag::err_fe_incompatible_option_with_remote_cache) + << "-fcasid-output"; + } + // END MCCAS // FIXME: Override value name discarding when asan or msan is used because the // backend passes depend on the name of the alloca in order to print out @@ -5091,6 +5533,10 @@ bool CompilerInvocation::CreateFromArgsImpl( Res.getDependencyOutputOpts().Targets.empty()) Diags.Report(diag::err_fe_dependency_file_requires_MT); + if (!Res.getPreprocessorOpts().ImplicitPCHInclude.empty() || + Res.getFrontendOpts().ProgramAction == frontend::GeneratePCH) + LangOpts.NeededByPCHOrCompilationUsesPCH = true; + // If sanitizer is enabled, disable OPT_ffine_grained_bitfield_accesses. if (Res.getCodeGenOpts().FineGrainedBitfieldAccesses && !Res.getLangOpts().Sanitize.empty()) { @@ -5104,17 +5550,6 @@ bool CompilerInvocation::CreateFromArgsImpl( append_range(Res.getCodeGenOpts().CommandLineArgs, CommandLineArgs); } - // Set PGOOptions. Need to create a temporary VFS to read the profile - // to determine the PGO type. - if (!Res.getCodeGenOpts().ProfileInstrumentUsePath.empty()) { - auto FS = - createVFSFromOverlayFiles(Res.getHeaderSearchOpts().VFSOverlayFiles, - Diags, llvm::vfs::getRealFileSystem()); - setPGOUseInstrumentor(Res.getCodeGenOpts(), - Res.getCodeGenOpts().ProfileInstrumentUsePath, *FS, - Diags); - } - FixupInvocation(Res, Diags, Args, DashX); return Diags.getNumErrors() == NumErrorsBefore; @@ -5139,7 +5574,16 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Invocation, Invocation, DummyInvocation, CommandLineArgs, Diags, Argv0); } -std::string CompilerInvocation::getModuleHash() const { +// Some extension diagnostics aren't explicitly mapped and require custom +// logic in the dianognostic engine to be used, track -pedantic-errors +static bool isExtHandlingFromDiagsError(DiagnosticsEngine &Diags) { + diag::Severity Ext = Diags.getExtensionHandlingBehavior(); + if (Ext == diag::Severity::Warning && Diags.getWarningsAsErrors()) + return true; + return Ext >= diag::Severity::Error; +} + +std::string CompilerInvocation::getModuleHash(DiagnosticsEngine &Diags) const { // FIXME: Consider using SHA1 instead of MD5. llvm::HashBuilder HBuilder; @@ -5254,6 +5698,28 @@ std::string CompilerInvocation::getModuleHash() const { if (!SanHash.empty()) HBuilder.add(SanHash.Mask); + // Check for a couple things (see checkDiagnosticMappings in ASTReader.cpp): + // -Werror: consider all warnings into the hash + // -Werror=something: consider only the specified into the hash + // -pedantic-error + if (getLangOpts().ModulesHashErrorDiags) { + bool ConsiderAllWarningsAsErrors = Diags.getWarningsAsErrors(); + HBuilder.add(isExtHandlingFromDiagsError(Diags)); + for (auto DiagIDMappingPair : Diags.getDiagnosticMappings()) { + diag::kind DiagID = DiagIDMappingPair.first; + auto CurLevel = Diags.getDiagnosticLevel(DiagID, SourceLocation()); + if (CurLevel < DiagnosticsEngine::Error && !ConsiderAllWarningsAsErrors) + continue; // not significant + HBuilder.add( + Diags.getDiagnosticIDs()->getWarningOptionForDiag(DiagID).str()); + } + } + + // Caching + implicit modules, which is only set in clang-scan-deps, puts + // additional CASIDs in the pcm for either cas-fs or include-tree. + HBuilder.add(getFrontendOpts().CacheCompileJob, + getFrontendOpts().ForIncludeTreeScan); + llvm::MD5::MD5Result Result; HBuilder.getHasher().final(Result); uint64_t Hash = Result.high() ^ Result.low(); @@ -5264,6 +5730,7 @@ void CompilerInvocationBase::generateCC1CommandLine( ArgumentConsumer Consumer) const { llvm::Triple T(getTargetOpts().Triple); + GenerateCASArgs(getCASOpts(), Consumer); GenerateFileSystemArgs(getFileSystemOpts(), Consumer); GenerateMigratorArgs(getMigratorOpts(), Consumer); GenerateAnalyzerArgs(getAnalyzerOpts(), Consumer); @@ -5310,10 +5777,13 @@ void CompilerInvocation::clearImplicitModuleBuildOptions() { } IntrusiveRefCntPtr -clang::createVFSFromCompilerInvocation(const CompilerInvocation &CI, - DiagnosticsEngine &Diags) { - return createVFSFromCompilerInvocation(CI, Diags, - llvm::vfs::getRealFileSystem()); +clang::createVFSFromCompilerInvocation( + const CompilerInvocation &CI, DiagnosticsEngine &Diags, + std::shared_ptr OverrideCAS) { + return createVFSFromCompilerInvocation( + CI, Diags, + createBaseFS(CI.getFileSystemOpts(), CI.getFrontendOpts(), + CI.getCASOpts(), Diags, std::move(OverrideCAS))); } IntrusiveRefCntPtr diff --git a/clang/lib/Frontend/DependencyFile.cpp b/clang/lib/Frontend/DependencyFile.cpp index 15fa7de35df97..0c4812e764006 100644 --- a/clang/lib/Frontend/DependencyFile.cpp +++ b/clang/lib/Frontend/DependencyFile.cpp @@ -23,6 +23,7 @@ #include "llvm/ADT/StringSet.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/Support/VirtualOutputBackends.h" #include "llvm/Support/raw_ostream.h" #include @@ -134,6 +135,36 @@ struct DepCollectorMMCallbacks : public ModuleMapCallbacks { } }; +// FIXME: This should not be separate from upstream, but we haven't +// upstreamed support for SkipUnusedModuleMaps. +struct DFGMMCallback : public ModuleMapCallbacks { + DependencyCollector &DepCollector; + bool SkipUnusedModuleMaps; + DFGMMCallback(DependencyCollector &DC, bool SkipUnusedModuleMaps) + : DepCollector(DC), SkipUnusedModuleMaps(SkipUnusedModuleMaps) {} + + void moduleMapFileRead(SourceLocation Loc, FileEntryRef Entry, + bool IsSystem) override { + if (SkipUnusedModuleMaps) + return; + StringRef Filename = Entry.getName(); + DepCollector.maybeAddDependency(Filename, /*FromModule*/ false, + /*IsSystem*/ IsSystem, + /*IsModuleFile*/ false, + /*IsMissing*/ false); + } + + void moduleMapFoundForModule(FileEntryRef Entry, const Module *M, + bool IsSystem) override { + if (!SkipUnusedModuleMaps) + return; + DepCollector.maybeAddDependency(Entry.getName(), /*FromModule*/ false, + /*IsSystem*/ IsSystem, + /*IsModuleFile*/ false, + /*IsMissing*/ false); + } +}; + struct DepCollectorASTListener : public ASTReaderListener { DependencyCollector &DepCollector; FileManager &FileMgr; @@ -217,13 +248,17 @@ void DependencyCollector::attachToASTReader(ASTReader &R) { } DependencyFileGenerator::DependencyFileGenerator( - const DependencyOutputOptions &Opts) - : OutputFile(Opts.OutputFile), Targets(Opts.Targets), - IncludeSystemHeaders(Opts.IncludeSystemHeaders), + const DependencyOutputOptions &Opts, + IntrusiveRefCntPtr OB) + : OutputBackend(std::move(OB)), OutputFile(Opts.OutputFile), + Targets(Opts.Targets), IncludeSystemHeaders(Opts.IncludeSystemHeaders), PhonyTarget(Opts.UsePhonyTargets), AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false), IncludeModuleFiles(Opts.IncludeModuleFiles), + SkipUnusedModuleMaps(Opts.SkipUnusedModuleMaps), OutputFormat(Opts.OutputFormat), InputFileIndex(0) { + if (!OutputBackend) + OutputBackend = llvm::makeIntrusiveRefCnt(); for (const auto &ExtraDep : Opts.ExtraDeps) { if (addDependency(ExtraDep.first)) ++InputFileIndex; @@ -235,7 +270,11 @@ void DependencyFileGenerator::attachToPreprocessor(Preprocessor &PP) { if (AddMissingHeaderDeps) PP.SetSuppressIncludeNotFoundError(true); - DependencyCollector::attachToPreprocessor(PP); + // FIXME: Restore the call to DependencyCollector::attachToPreprocessor(PP); + // once the SkipUnusedModuleMaps is upstreamed. + PP.addPPCallbacks(std::make_unique(*this, PP)); + PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( + std::make_unique(*this, SkipUnusedModuleMaps)); } bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule, @@ -344,19 +383,33 @@ static void PrintFilename(raw_ostream &OS, StringRef Filename, } void DependencyFileGenerator::outputDependencyFile(DiagnosticsEngine &Diags) { - if (SeenMissingHeader) { - llvm::sys::fs::remove(OutputFile); + // The use of NoAtomicWrite and calling discard on SeenMissingHeader + // preserves the previous behaviour: no temporary files are used, and when + // SeenMissingHeader is true it deletes a previously-existing file. + // FIXME: switch to atomic-write based on FrontendOptions::UseTemporary and + // and not deleting the previous file, if possible. + Expected O = + OutputBackend->createFile(OutputFile, llvm::vfs::OutputConfig() + .setTextWithCRLF() + .setNoAtomicWrite() + .setNoDiscardOnSignal()); + + if (!O) { + Diags.Report(diag::err_fe_error_opening) + << OutputFile << toString(O.takeError()); return; } - std::error_code EC; - llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF); - if (EC) { - Diags.Report(diag::err_fe_error_opening) << OutputFile << EC.message(); + if (SeenMissingHeader) { + consumeError(O->discard()); return; } - outputDependencyFile(OS); + outputDependencyFile(O->getOS()); + + if (auto Err = O->keep()) + Diags.Report(diag::err_fe_error_writing) + << OutputFile << toString(std::move(Err)); } void DependencyFileGenerator::outputDependencyFile(llvm::raw_ostream &OS) { diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp index 9b2aa253c90ee..b01cd8fc4c497 100644 --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -15,6 +15,7 @@ #include "clang/Basic/FileEntry.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/LangStandard.h" +#include "clang/CAS/IncludeTree.h" #include "clang/Basic/Sarif.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" @@ -24,6 +25,7 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/IncludeTreePPActions.h" #include "clang/Frontend/LayoutOverrideSource.h" #include "clang/Frontend/MultiplexConsumer.h" #include "clang/Frontend/SARIFDiagnosticPrinter.h" @@ -467,7 +469,7 @@ operator+=(SmallVectorImpl &Includes, StringRef RHS) { static void addHeaderInclude(StringRef HeaderName, SmallVectorImpl &Includes, const LangOptions &LangOpts, - bool IsExternC) { + llvm::PrefixMapper &PrefixMapper, bool IsExternC) { if (IsExternC && LangOpts.CPlusPlus) Includes += "extern \"C\" {\n"; if (LangOpts.ObjC) @@ -475,7 +477,13 @@ static void addHeaderInclude(StringRef HeaderName, else Includes += "#include \""; - Includes += HeaderName; + if (PrefixMapper.empty() || llvm::sys::path::is_relative(HeaderName)) { + Includes += HeaderName; + } else { + SmallString<128> MappedPath; + PrefixMapper.map(HeaderName, MappedPath); + Includes += MappedPath; + } Includes += "\"\n"; if (IsExternC && LangOpts.CPlusPlus) @@ -490,8 +498,9 @@ static void addHeaderInclude(StringRef HeaderName, /// \param Includes Will be augmented with the set of \#includes or \#imports /// needed to load all of the named headers. static std::error_code collectModuleHeaderIncludes( - const LangOptions &LangOpts, FileManager &FileMgr, DiagnosticsEngine &Diag, - ModuleMap &ModMap, clang::Module *Module, SmallVectorImpl &Includes) { + const LangOptions &LangOpts, llvm::PrefixMapper &PrefixMapper, + FileManager &FileMgr, DiagnosticsEngine &Diag, ModuleMap &ModMap, + clang::Module *Module, SmallVectorImpl &Includes) { // Don't collect any headers for unavailable modules. if (!Module->isAvailable()) return std::error_code(); @@ -520,7 +529,7 @@ static std::error_code collectModuleHeaderIncludes( // the module map file) so this will find the same file that we found // while parsing the module map. addHeaderInclude(H.PathRelativeToRootModuleDirectory, Includes, LangOpts, - Module->IsExternC); + PrefixMapper, Module->IsExternC); } } // Note that Module->PrivateHeaders will not be a TopHeader. @@ -531,7 +540,7 @@ static std::error_code collectModuleHeaderIncludes( if (Module->Parent) // Include the umbrella header for submodules. addHeaderInclude(UmbrellaHeader->PathRelativeToRootModuleDirectory, - Includes, LangOpts, Module->IsExternC); + Includes, LangOpts, PrefixMapper, Module->IsExternC); } else if (std::optional UmbrellaDir = Module->getUmbrellaDirAsWritten()) { // Add all of the headers we find in this subdirectory. @@ -585,14 +594,15 @@ static std::error_code collectModuleHeaderIncludes( for (auto &H : Headers) { // Include this header as part of the umbrella directory. Module->addTopHeader(H.second); - addHeaderInclude(H.first, Includes, LangOpts, Module->IsExternC); + addHeaderInclude(H.first, Includes, LangOpts, PrefixMapper, + Module->IsExternC); } } // Recurse into submodules. for (auto *Submodule : Module->submodules()) if (std::error_code Err = collectModuleHeaderIncludes( - LangOpts, FileMgr, Diag, ModMap, Submodule, Includes)) + LangOpts, PrefixMapper, FileMgr, Diag, ModMap, Submodule, Includes)) return Err; return std::error_code(); @@ -673,6 +683,9 @@ static Module *prepareToBuildModule(CompilerInstance &CI, // be resolved relative to the build directory of the module map file. CI.getPreprocessor().setMainFileDir(*M->Directory); + if (auto CacheKey = CI.getCompileJobCacheKey()) + M->setModuleCacheKey(CacheKey->toString()); + // If the module was inferred from a different module map (via an expanded // umbrella module definition), track that fact. // FIXME: It would be preferable to fill this in as part of processing @@ -710,6 +723,121 @@ static Module *prepareToBuildModule(CompilerInstance &CI, return M; } +static Expected makeIncludeTreeModule(CompilerInstance &CI, + cas::IncludeTree::Module Mod, + Module *Parent) { + ModuleMap &MMap = CI.getPreprocessor().getHeaderSearchInfo().getModuleMap(); + auto Flags = Mod.getFlags(); + Module *M = nullptr; + bool NewModule = false; + std::tie(M, NewModule) = MMap.findOrCreateModule( + Mod.getName(), Parent, Flags.IsFramework, Flags.IsExplicit); + assert(NewModule); + M->Kind = Module::IncludeTreeModuleMap; + M->IsExternC = Flags.IsExternC; + M->IsSystem = Flags.IsSystem; + M->InferSubmodules = Flags.InferSubmodules; + M->InferExplicitSubmodules = Flags.InferExplicitSubmodules; + M->InferExportWildcard = Flags.InferExportWildcard; + M->UseExportAsModuleLinkName = Flags.UseExportAsModuleLinkName; + M->ExportAsModule = Mod.getExportAsModule(); + + auto ExportList = Mod.getExports(); + if (!ExportList) + return ExportList.takeError(); + if (*ExportList) { + if ((*ExportList)->hasGlobalWildcard()) + M->Exports.push_back(Module::ExportDecl(nullptr, true)); + + llvm::Error Err = (*ExportList)->forEachExplicitExport([&](auto Export) { + Module::UnresolvedExportDecl UED; + SmallVector ModuleComponents; + Export.ModuleName.split(ModuleComponents, '.'); + for (StringRef Name : ModuleComponents) + UED.Id.push_back({std::string(Name), SourceLocation()}); + UED.Wildcard = Export.Wildcard; + M->UnresolvedExports.push_back(std::move(UED)); + return llvm::Error::success(); + }); + if (Err) + return std::move(Err); + } + + auto LinkLibs = Mod.getLinkLibraries(); + if (!LinkLibs) + return LinkLibs.takeError(); + if (*LinkLibs) { + llvm::Error Err = (*LinkLibs)->forEachLinkLibrary([&](auto LL) { + M->LinkLibraries.emplace_back(std::string(LL.Library), LL.IsFramework); + return llvm::Error::success(); + }); + if (Err) + return std::move(Err); + } + + llvm::Error Err = Mod.forEachSubmodule([&](cas::IncludeTree::Module Sub) { + return makeIncludeTreeModule(CI, Sub, M).takeError(); + }); + if (Err) + return std::move(Err); + return M; +} + +/// Loads the include tree modulemap \p MM. +/// \returns true if there was an error. +static bool loadIncludeTreeModuleMap(CompilerInstance &CI, + cas::IncludeTree::ModuleMap MM) { + llvm::Error Err = MM.forEachModule([&](auto M) -> llvm::Error { + return makeIncludeTreeModule(CI, M, /*Parent=*/nullptr).takeError(); + }); + if (Err) { + CI.getDiagnostics().Report(diag::err_fe_unable_to_load_include_tree) + << CI.getFrontendOpts().CASIncludeTreeID << std::move(Err); + return true; + } + return false; +} + +static Module *prepareToBuildModule(CompilerInstance &CI, + cas::IncludeTree::ModuleMap MM) { + if (CI.getLangOpts().CurrentModule.empty()) { + CI.getDiagnostics().Report(diag::err_missing_module_name); + + // FIXME: Eventually, we could consider asking whether there was just + // a single module described in the module map, and use that as a + // default. Then it would be fairly trivial to just "compile" a module + // map with a single module (the common case). + return nullptr; + } + + if (loadIncludeTreeModuleMap(CI, MM)) + return nullptr; + + // Dig out the module definition. + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + Module *M = HS.lookupModule(CI.getLangOpts().CurrentModule, SourceLocation(), + /*AllowSearch=*/false); + if (!M) { + CI.getDiagnostics().Report(diag::err_missing_module_include_tree) + << CI.getLangOpts().CurrentModule + << CI.getFrontendOpts().CASIncludeTreeID; + + return nullptr; + } + + if (auto CacheKey = CI.getCompileJobCacheKey()) + M->setModuleCacheKey(CacheKey->toString()); + + // If we're being run from the command-line, the module build stack will not + // have been filled in yet, so complete it now in order to allow us to detect + // module cycles. + SourceManager &SourceMgr = CI.getSourceManager(); + if (SourceMgr.getModuleBuildStack().empty()) + SourceMgr.pushModuleBuildStack(CI.getLangOpts().CurrentModule, + FullSourceLoc(SourceLocation(), SourceMgr)); + return M; +} + /// Compute the input buffer that should be used to build the specified module. static std::unique_ptr getInputBufferForModule(CompilerInstance &CI, Module *M) { @@ -721,9 +849,10 @@ getInputBufferForModule(CompilerInstance &CI, Module *M) { if (std::optional UmbrellaHeader = M->getUmbrellaHeaderAsWritten()) addHeaderInclude(UmbrellaHeader->PathRelativeToRootModuleDirectory, - HeaderContents, CI.getLangOpts(), M->IsExternC); + HeaderContents, CI.getLangOpts(), CI.getPrefixMapper(), + M->IsExternC); Err = collectModuleHeaderIncludes( - CI.getLangOpts(), FileMgr, CI.getDiagnostics(), + CI.getLangOpts(), CI.getPrefixMapper(), FileMgr, CI.getDiagnostics(), CI.getPreprocessor().getHeaderSearchInfo().getModuleMap(), M, HeaderContents); @@ -937,6 +1066,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, FileManager &FileMgr = CI.getFileManager(); PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); StringRef PCHInclude = PPOpts.ImplicitPCHInclude; + CI.getLangOpts().NeededByPCHOrCompilationUsesPCH = true; std::string SpecificModuleCachePath = CI.getSpecificModuleCachePath(); if (auto PCHDir = FileMgr.getOptionalDirectoryRef(PCHInclude)) { std::error_code EC; @@ -966,6 +1096,9 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, } } + if (CI.getFrontendOpts().ProgramAction == frontend::GeneratePCH) + CI.getLangOpts().NeededByPCHOrCompilationUsesPCH = true; + // Set up the preprocessor if needed. When parsing model files the // preprocessor of the original source is reused. if (!isModelParsingAction()) @@ -1016,6 +1149,48 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, CI.getLangOpts().CurrentModule = CI.getLangOpts().ModuleName; } + auto reportError = [&](llvm::Error &&E) -> bool { + std::string IncludeTreeID = + CI.getOrCreateObjectStore().getID(Input.getIncludeTree()).toString(); + CI.getDiagnostics().Report(diag::err_fe_unable_to_load_include_tree) + << IncludeTreeID << llvm::toString(std::move(E)); + return false; + }; + + std::optional IncludeTreeRoot; + std::optional IncludeTreePCH; + if (Input.isIncludeTree()) { + if (llvm::Error E = cas::IncludeTreeRoot::get(CI.getOrCreateObjectStore(), + Input.getIncludeTree()) + .moveInto(IncludeTreeRoot)) + return reportError(std::move(E)); + + Expected> PPCachedAct = + createPPActionsFromIncludeTree(*IncludeTreeRoot); + if (!PPCachedAct) + return reportError(PPCachedAct.takeError()); + CI.getPreprocessor().setPPCachedActions(std::move(*PPCachedAct)); + CI.getFrontendOpts().IncludeTimestamps = false; + + if (llvm::Error E = IncludeTreeRoot->getPCH().moveInto(IncludeTreePCH)) + return reportError(std::move(E)); + + auto ModMap = IncludeTreeRoot->getModuleMap(); + if (!ModMap) + return reportError(ModMap.takeError()); + if (*ModMap) { + if (CI.getFrontendOpts().ProgramAction == frontend::GenerateModule) { + auto *CurrentModule = prepareToBuildModule(CI, **ModMap); + if (!CurrentModule) + return false; + CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleMap); + } else { + if (loadIncludeTreeModuleMap(CI, **ModMap)) + return false; + } + } + } + if (!CI.InitializeSourceManager(Input)) return false; @@ -1083,6 +1258,13 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, CI.getDiagnostics().Report(diag::err_module_map_not_found) << Filename; } + // Provide any modules from the action cache. + for (const auto &KeyPair : CI.getFrontendOpts().ModuleCacheKeys) + if (CI.addCachedModuleFile(KeyPair.first, KeyPair.second, + "-fmodule-file-cache-key")) + return false; + + // If compiling implementation of a module, load its module map file now. (void)CI.getPreprocessor().getCurrentModuleImplementation(); @@ -1124,7 +1306,8 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, CI.setASTReader(static_cast(FinalReader.get())); CI.getASTContext().setExternalSource(source); } else if (CI.getLangOpts().Modules || - !CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { + !CI.getPreprocessorOpts().ImplicitPCHInclude.empty() || + IncludeTreePCH) { // Use PCM or PCH. assert(hasPCHSupport() && "This action does not have PCH support!"); ASTDeserializationListener *DeserialListener = @@ -1142,12 +1325,27 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, DeserialListener, DeleteDeserialListener); DeleteDeserialListener = true; } - if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { + if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty() || + IncludeTreePCH) { + StringRef PCHPath; + DisableValidationForModuleKind DisableValidation; + std::unique_ptr PCHBuffer; + if (IncludeTreePCH) { + // No need to do any validation. + DisableValidation = DisableValidationForModuleKind::All; + if (llvm::Error E = + IncludeTreePCH->getMemoryBuffer().moveInto(PCHBuffer)) + return reportError(std::move(E)); + PCHPath = PCHBuffer->getBufferIdentifier(); + } else { + PCHPath = CI.getPreprocessorOpts().ImplicitPCHInclude; + DisableValidation = + CI.getPreprocessorOpts().DisablePCHOrModuleValidation; + } CI.createPCHExternalASTSource( - CI.getPreprocessorOpts().ImplicitPCHInclude, - CI.getPreprocessorOpts().DisablePCHOrModuleValidation, + PCHPath, DisableValidation, CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, - DeserialListener, DeleteDeserialListener); + DeserialListener, DeleteDeserialListener, std::move(PCHBuffer)); if (!CI.getASTContext().getExternalSource()) return false; } @@ -1162,6 +1360,10 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, } } + if (!CI.getFrontendOpts().CASIncludeTreeID.empty()) + CI.getPreprocessor().setCASIncludeTreeID( + CI.getFrontendOpts().CASIncludeTreeID); + CI.setASTConsumer(std::move(Consumer)); if (!CI.hasASTConsumer()) return false; diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 51e9f2c6b39a7..d99475d8459f2 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -845,6 +845,8 @@ static StringRef ModuleKindName(Module::ModuleKind MK) { switch (MK) { case Module::ModuleMapModule: return "Module Map Module"; + case Module::IncludeTreeModuleMap: + return "Include Tree Module"; case Module::ModuleInterfaceUnit: return "Interface Unit"; case Module::ModuleImplementationUnit: @@ -1229,6 +1231,7 @@ void GetDependenciesByModuleNameAction::ExecuteAction() { Preprocessor &PP = CI.getPreprocessor(); SourceManager &SM = PP.getSourceManager(); FileID MainFileID = SM.getMainFileID(); + PP.EnterSourceFile(MainFileID, nullptr, SourceLocation()); SourceLocation FileStart = SM.getLocForStartOfFile(MainFileID); SmallVector Path; IdentifierInfo *ModuleID = PP.getIdentifierInfo(ModuleName); diff --git a/clang/lib/Frontend/IncludeTreePPActions.cpp b/clang/lib/Frontend/IncludeTreePPActions.cpp new file mode 100644 index 0000000000000..ab163ce6e571b --- /dev/null +++ b/clang/lib/Frontend/IncludeTreePPActions.cpp @@ -0,0 +1,224 @@ +//===- IncludeTreePPActions.cpp - PP actions using include-tree -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/IncludeTreePPActions.h" +#include "clang/CAS/IncludeTree.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/PPCachedActions.h" +#include "clang/Lex/Preprocessor.h" + +using namespace clang; + +namespace { + +struct IncludeStackInfo { + cas::IncludeTree Tree; + SourceLocation FileStartLoc; + unsigned CurIncludeIndex = 0; + unsigned CurHasIncludeCheckIndex = 0; +}; + +/// Uses the info from an \p IncludeTreeRoot to resolve include directives +/// and evaluate \p __has_include checks. +class IncludeTreePPActions final : public PPCachedActions { + cas::IncludeTree MainTree; + SmallVector IncludeStack; + bool HasCASErrorOccurred = false; + + void reportError(Preprocessor &PP, llvm::Error &&E) { + PP.getDiagnostics().Report(diag::err_unable_to_load_include_tree_node) + << llvm::toString(std::move(E)); + HasCASErrorOccurred = true; + } + +public: + IncludeTreePPActions(cas::IncludeTree MainTree) + : MainTree(std::move(MainTree)) {} + + FileID handlePredefines(Preprocessor &PP) override { + auto createEmptyFID = [&]() -> FileID { + llvm::MemoryBufferRef Buffer({}, ""); + return PP.getSourceManager().createFileID(Buffer); + }; + + if (HasCASErrorOccurred) { + return createEmptyFID(); + } + + auto reportError = [&](llvm::Error &&E) -> FileID { + this->reportError(PP, std::move(E)); + return createEmptyFID(); + }; + + SourceManager &SM = PP.getSourceManager(); + SourceLocation MainFileLoc = SM.getLocForStartOfFile(SM.getMainFileID()); + IncludeStack.push_back({std::move(MainTree), MainFileLoc}); + + IncludeStackInfo &IncludeInfo = IncludeStack.back(); + Expected EnteredTree = + IncludeInfo.Tree.getIncludeTree(IncludeInfo.CurIncludeIndex++); + if (!EnteredTree) + return reportError(EnteredTree.takeError()); + auto FileInfo = EnteredTree->getBaseFileInfo(); + if (!FileInfo) + return reportError(FileInfo.takeError()); + llvm::MemoryBufferRef Buffer(FileInfo->Contents, FileInfo->Filename); + FileID FID = SM.createFileID(Buffer); + IncludeStack.push_back( + {std::move(*EnteredTree), SM.getLocForStartOfFile(FID)}); + return FID; + } + + bool evaluateHasInclude(Preprocessor &PP, SourceLocation Loc, + bool IsIncludeNext) override { + if (HasCASErrorOccurred) + return false; + + IncludeStackInfo &IncludeInfo = IncludeStack.back(); + unsigned Index = IncludeInfo.CurHasIncludeCheckIndex++; + return IncludeInfo.Tree.getCheckResult(Index); + } + + std::variant + handleIncludeDirective(Preprocessor &PP, SourceLocation IncludeLoc, + SourceLocation AfterDirectiveLoc) override { + if (HasCASErrorOccurred) + return {}; + + IncludeStackInfo &IncludeInfo = IncludeStack.back(); + if (IncludeInfo.CurIncludeIndex >= IncludeInfo.Tree.getNumIncludes()) + return {}; + + unsigned ExpectedOffset = + IncludeInfo.Tree.getIncludeOffset(IncludeInfo.CurIncludeIndex); + SourceLocation ExpectedLoc = + IncludeInfo.FileStartLoc.getLocWithOffset(ExpectedOffset); + if (ExpectedLoc != AfterDirectiveLoc) + return {}; + + auto reportError = [&](llvm::Error &&E) -> std::monostate { + this->reportError(PP, std::move(E)); + return {}; + }; + + auto reportErrorTwine = [&](const llvm::Twine &T) -> std::monostate { + return reportError( + llvm::createStringError(llvm::inconvertibleErrorCode(), T)); + }; + + Expected Node = + IncludeInfo.Tree.getIncludeNode(IncludeInfo.CurIncludeIndex++); + if (!Node) + return reportError(Node.takeError()); + + auto MakeModuleImport = [&](cas::IncludeTree::ModuleImport Import) { + SmallVector Path; + SmallVector ModuleComponents; + Import.getModuleName().split(ModuleComponents, '.'); + for (StringRef Component : ModuleComponents) + Path.emplace_back(IncludeLoc, PP.getIdentifierInfo(Component)); + return IncludeModule{std::move(Path), Import.visibilityOnly()}; + }; + + auto MakeIncludeTree = [&](cas::IncludeTree EnteredTree) + -> std::variant { + auto File = EnteredTree.getBaseFile(); + if (!File) + return reportError(File.takeError()); + auto FilenameBlob = File->getFilename(); + if (!FilenameBlob) + return reportError(FilenameBlob.takeError()); + + SourceManager &SM = PP.getSourceManager(); + ModuleMap &MMap = PP.getHeaderSearchInfo().getModuleMap(); + Expected FE = + SM.getFileManager().getFileRef(FilenameBlob->getData(), + /*OpenFile=*/true); + if (!FE) + return reportError(FE.takeError()); + FileID FID = + SM.createFileID(*FE, IncludeLoc, EnteredTree.getFileCharacteristic()); + PP.markIncluded(*FE); + IncludeStack.push_back( + {std::move(EnteredTree), SM.getLocForStartOfFile(FID)}); + + Module *M = nullptr; + auto SubmoduleName = EnteredTree.getSubmoduleName(); + if (!SubmoduleName) + return reportError(SubmoduleName.takeError()); + if (*SubmoduleName) { + SmallVector ModuleComponents; + (*SubmoduleName)->split(ModuleComponents, '.'); + M = PP.getHeaderSearchInfo().lookupModule( + ModuleComponents[0], IncludeLoc, + /*AllowSearch=*/false, /*AllowExtraModuleMapSearch=*/false); + if (!M) + return reportErrorTwine(llvm::Twine("failed to find module '") + + ModuleComponents[0] + "'"); + for (StringRef Sub : ArrayRef(ModuleComponents).drop_front()) { + M = MMap.findOrInferSubmodule(M, Sub); + if (!M) + return reportErrorTwine( + llvm::Twine("failed to find or infer submodule '") + Sub + "'"); + } + + // Add to known headers for the module. + Module::Header H{"", "", *FE}; + MMap.addHeader(M, std::move(H), ModuleMap::NormalHeader); + } + + return IncludeFile{FID, M}; + }; + + switch (Node->getKind()) { + case cas::IncludeTree::NodeKind::ModuleImport: + return MakeModuleImport(Node->getModuleImport()); + case cas::IncludeTree::NodeKind::Tree: { + auto IncludeTree = MakeIncludeTree(Node->getIncludeTree()); + if (std::holds_alternative(IncludeTree)) + return std::monostate{}; + return std::get(IncludeTree); + } + case cas::IncludeTree::NodeKind::SpuriousImport: { + auto SpuriousImportNode = Node->getSpuriousImport(); + auto ModuleImportNode = SpuriousImportNode.getModuleImport(); + if (!ModuleImportNode) + return reportError(ModuleImportNode.takeError()); + auto IncludeTreeNode = SpuriousImportNode.getIncludeTree(); + if (!IncludeTreeNode) + return reportError(IncludeTreeNode.takeError()); + auto ModuleImport = MakeModuleImport(*ModuleImportNode); + auto IncludeTree = MakeIncludeTree(*IncludeTreeNode); + if (std::holds_alternative(IncludeTree)) + return std::monostate{}; + return SpuriousImport{ModuleImport, std::get(IncludeTree)}; + } + } + } + + void exitedFile(Preprocessor &PP, FileID FID) override { + if (HasCASErrorOccurred) + return; + + assert(!IncludeStack.empty()); + assert(IncludeStack.back().FileStartLoc == + PP.getSourceManager().getLocForStartOfFile(FID)); + assert(IncludeStack.back().CurIncludeIndex == + IncludeStack.back().Tree.getNumIncludes()); + IncludeStack.pop_back(); + } +}; +} // namespace + +Expected> +clang::createPPActionsFromIncludeTree(cas::IncludeTreeRoot &Root) { + auto MainTree = Root.getMainFileTree(); + if (!MainTree) + return MainTree.takeError(); + return std::make_unique(std::move(*MainTree)); +} diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp index 3fd3c9bd69037..d7e92178faf95 100644 --- a/clang/lib/Frontend/MultiplexConsumer.cpp +++ b/clang/lib/Frontend/MultiplexConsumer.cpp @@ -123,6 +123,10 @@ class MultiplexASTMutationListener : public ASTMutationListener { void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) override; + /* TO_UPSTREAM(BoundsSafety) ON*/ + void LazyAttributeToDecl(const Attr *Attr, + const Decl *D) override; + /* TO_UPSTREAM(BoundsSafety) OFF*/ void EnteringModulePurview() override; void AddedManglingNumber(const Decl *D, unsigned) override; void AddedStaticLocalNumbers(const Decl *D, unsigned) override; @@ -251,6 +255,14 @@ void MultiplexASTMutationListener::AddedAttributeToRecord( L->AddedAttributeToRecord(Attr, Record); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +void MultiplexASTMutationListener::LazyAttributeToDecl(const Attr *Attr, + const Decl *D) { + for (auto *L : Listeners) + L->LazyAttributeToDecl(Attr, D); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + void MultiplexASTMutationListener::EnteringModulePurview() { for (auto *L : Listeners) L->EnteringModulePurview(); diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp index 2ae355fb33885..cf0ce1ad5099a 100644 --- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -184,6 +184,11 @@ class PrintPPOutputPPCallbacks : public PPCallbacks { void PragmaAssumeNonNullBegin(SourceLocation Loc) override; void PragmaAssumeNonNullEnd(SourceLocation Loc) override; + /* TO_UPSTREAM(BoundsSafety) ON */ + void PragmaAbiPointerAttributesSet( + SourceLocation Loc, ArrayRef Spec) override; + /* TO_UPSTREAM(BoundsSafety) OFF */ + /// Insert whitespace before emitting the next token. /// /// @param Tok Next token to be emitted. @@ -752,6 +757,25 @@ PragmaAssumeNonNullEnd(SourceLocation Loc) { setEmittedDirectiveOnThisLine(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +void PrintPPOutputPPCallbacks:: +PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef Spec) { + startNewLineIfNeeded(); + MoveToLine(Loc, /*RequireStartOfLine=*/true); + *OS << "#pragma clang abi_ptr_attr set("; + bool IsFirst = true; + for (const IdentifierInfo *Str : Spec) { + if (!IsFirst) + *OS << ' '; + *OS << Str->getName(); + IsFirst = false; + } + *OS << ')'; + setEmittedDirectiveOnThisLine(); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void PrintPPOutputPPCallbacks::HandleWhitespaceBeforeTok(const Token &Tok, bool RequireSpace, bool RequireSameLine) { diff --git a/clang/lib/Frontend/Rewrite/FrontendActions.cpp b/clang/lib/Frontend/Rewrite/FrontendActions.cpp index 5d2e1d7877095..ed68c603b7960 100644 --- a/clang/lib/Frontend/Rewrite/FrontendActions.cpp +++ b/clang/lib/Frontend/Rewrite/FrontendActions.cpp @@ -216,6 +216,12 @@ class RewriteIncludesAction::RewriteImportsListener : public ASTReaderListener { auto File = CI.getFileManager().getOptionalFileRef(Filename); assert(File && "missing file for loaded module?"); +#if !defined(__APPLE__) + // Workaround for ext4 file system. + if (auto Bypass = CI.getFileManager().getBypassFile(*File)) + File = *Bypass; +#endif + // Only rewrite each module file once. if (!Rewritten.insert(*File).second) return; diff --git a/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp b/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp index 02aa3e8e4d984..81ccaef0ea559 100644 --- a/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp +++ b/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -91,6 +91,7 @@ class SDiagsMerger : SerializedDiagnosticReader { AbbrevLookup FileLookup; AbbrevLookup CategoryLookup; AbbrevLookup DiagFlagLookup; + llvm::DenseSet ContentsWritten; public: SDiagsMerger(SDiagsWriter &Writer) : Writer(Writer) {} @@ -110,6 +111,12 @@ class SDiagsMerger : SerializedDiagnosticReader { std::error_code visitFilenameRecord(unsigned ID, unsigned Size, unsigned Timestamp, StringRef Name) override; + std::error_code visitSourceFileContentsRecord( + unsigned ID, + const Location &OriginalStartLoc, + const Location &OriginalEndLoc, + StringRef Contents) override; + std::error_code visitFixitRecord(const serialized_diags::Location &Start, const serialized_diags::Location &End, StringRef CodeToInsert) override; @@ -140,10 +147,11 @@ class SDiagsWriter : public DiagnosticConsumer { State(std::move(State)) {} public: - SDiagsWriter(StringRef File, DiagnosticOptions *Diags, bool MergeChildRecords) + SDiagsWriter(StringRef File, std::unique_ptr OS, + DiagnosticOptions *Diags, bool MergeChildRecords) : LangOpts(nullptr), OriginalInstance(true), MergeChildRecords(MergeChildRecords), - State(std::make_shared(File, Diags)) { + State(std::make_shared(File, std::move(OS), Diags)) { if (MergeChildRecords) RemoveOldDiagnostics(); EmitPreamble(); @@ -242,9 +250,10 @@ class SDiagsWriter : public DiagnosticConsumer { /// State that is shared among the various clones of this diagnostic /// consumer. struct SharedState { - SharedState(StringRef File, DiagnosticOptions *Diags) + SharedState(StringRef File, std::unique_ptr OS, + DiagnosticOptions *Diags) : DiagOpts(Diags), Stream(Buffer), OutputFile(File.str()), - EmittedAnyDiagBlocks(false) {} + OutputStream(std::move(OS)), EmittedAnyDiagBlocks(false) {} /// Diagnostic options. IntrusiveRefCntPtr DiagOpts; @@ -258,6 +267,9 @@ class SDiagsWriter : public DiagnosticConsumer { /// The name of the diagnostics file. std::string OutputFile; + /// Optional output stream instead of writing directly to a file. + std::unique_ptr OutputStream; + /// The set of constructed record abbreviations. AbbreviationMap Abbrevs; @@ -295,9 +307,12 @@ class SDiagsWriter : public DiagnosticConsumer { namespace clang { namespace serialized_diags { -std::unique_ptr -create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords) { - return std::make_unique(OutputFile, Diags, MergeChildRecords); +std::unique_ptr create(StringRef OutputFile, + DiagnosticOptions *Diags, + bool MergeChildRecords, + std::unique_ptr OS) { + return std::make_unique(OutputFile, std::move(OS), Diags, + MergeChildRecords); } } // end namespace serialized_diags @@ -452,6 +467,8 @@ void SDiagsWriter::EmitBlockInfoBlock() { EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record); EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record); EmitRecordID(RECORD_FIXIT, "FixIt", Stream, Record); + EmitRecordID( + RECORD_SOURCE_FILE_CONTENTS, "SourceFileContents", Stream, Record); // Emit abbreviation for RECORD_DIAG. Abbrev = std::make_shared(); @@ -508,6 +525,16 @@ void SDiagsWriter::EmitBlockInfoBlock() { Abbrevs.set(RECORD_FIXIT, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); + // Emit the abbreviation for RECORD_SOURCE_FILE_CONTENTS. + Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_FILE_CONTENTS)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // File ID. + AddRangeLocationAbbrev(*Abbrev); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // File size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File contents. + Abbrevs.set(RECORD_SOURCE_FILE_CONTENTS, + Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); + Stream.ExitBlock(); } @@ -581,6 +608,9 @@ void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, return; } + // Call base class to update diagnostic counts. + DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); + // Enter the block for a non-note diagnostic immediately, rather than waiting // for beginDiagnostic, in case associated notes are emitted before we get // there. @@ -796,6 +826,15 @@ void SDiagsWriter::finish() { getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure); } + if (State->OutputStream) { + // Write the generated bitstream to "Out". + State->OutputStream->write((char *)&State->Buffer.front(), + State->Buffer.size()); + State->OutputStream->flush(); + State->OutputStream.reset(); + return; + } + std::error_code EC; auto OS = std::make_unique(State->OutputFile.c_str(), EC, llvm::sys::fs::OF_None); @@ -873,6 +912,28 @@ std::error_code SDiagsMerger::visitFilenameRecord(unsigned ID, unsigned Size, return std::error_code(); } +std::error_code SDiagsMerger::visitSourceFileContentsRecord( + unsigned ID, + const Location &OriginalStartLoc, + const Location &OriginalEndLoc, + StringRef Contents) { + unsigned MappedID = FileLookup[ID]; + if (!ContentsWritten.insert(MappedID).second) + return std::error_code(); + + RecordData::value_type Record[] = { + RECORD_SOURCE_FILE_CONTENTS, MappedID, + FileLookup[OriginalStartLoc.FileID], + OriginalStartLoc.Line, OriginalStartLoc.Col, OriginalStartLoc.Offset, + FileLookup[OriginalEndLoc.FileID], OriginalEndLoc.Line, + OriginalEndLoc.Col, OriginalEndLoc.Offset, + Contents.size()}; + + Writer.State->Stream.EmitRecordWithBlob( + Writer.State->Abbrevs.get(RECORD_SOURCE_FILE_CONTENTS), Record, Contents); + return std::error_code(); +} + std::error_code SDiagsMerger::visitCategoryRecord(unsigned ID, StringRef Name) { CategoryLookup[ID] = Writer.getEmitCategory(ID); return std::error_code(); diff --git a/clang/lib/Frontend/SerializedDiagnosticReader.cpp b/clang/lib/Frontend/SerializedDiagnosticReader.cpp index 5f5ed41b04629..def3e2a432908 100644 --- a/clang/lib/Frontend/SerializedDiagnosticReader.cpp +++ b/clang/lib/Frontend/SerializedDiagnosticReader.cpp @@ -34,7 +34,12 @@ std::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) { if (!Buffer) return SDError::CouldNotLoad; - llvm::BitstreamCursor Stream(**Buffer); + return readDiagnostics(**Buffer); +} + +std::error_code +SerializedDiagnosticReader::readDiagnostics(llvm::MemoryBufferRef Buffer) { + llvm::BitstreamCursor Stream(Buffer); std::optional BlockInfo; if (Stream.AtEndOfStream()) @@ -261,13 +266,28 @@ SerializedDiagnosticReader::readDiagnosticBlock(llvm::BitstreamCursor &Stream) { continue; switch ((RecordIDs)RecID) { - case RECORD_CATEGORY: + case RECORD_CATEGORY: { // A category has ID and name size. if (Record.size() != 2) return SDError::MalformedDiagnosticRecord; - if ((EC = visitCategoryRecord(Record[0], Blob))) + + // Make sure the name size makes sense. + unsigned NameLen = Record[1]; + if (NameLen > Blob.size()) + return SDError::MalformedDiagnosticRecord; + + StringRef Name = Blob.substr(0, NameLen); + StringRef URL; + + // If the name didn't take up the full blob, and the character that + // follows it is an '@', the rest is the category URL. + if (NameLen < Blob.size() && Blob[NameLen] == '@') + URL = Blob.substr(NameLen + 1); + + if ((EC = visitCategoryRecord(Record[0], Name, URL))) return EC; continue; + } case RECORD_DIAG: // A diagnostic has severity, location (4), category, flag, and message // size. @@ -302,6 +322,16 @@ SerializedDiagnosticReader::readDiagnosticBlock(llvm::BitstreamCursor &Stream) { Location(Record[4], Record[5], Record[6], Record[7]), Blob))) return EC; continue; + case RECORD_SOURCE_FILE_CONTENTS: + if (Record.size() != 10 || Record[9] != Blob.size()) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitSourceFileContentsRecord( + Record[0], + Location(Record[1], Record[2], Record[3], Record[4]), + Location(Record[5], Record[6], Record[7], Record[8]), + Blob))) + return EC; + continue; case RECORD_SOURCE_RANGE: // A source range is two locations (4 each). if (Record.size() != 8) diff --git a/clang/lib/Frontend/TextDiagnostic.cpp b/clang/lib/Frontend/TextDiagnostic.cpp index 4119ce6048d45..21c966793abf2 100644 --- a/clang/lib/Frontend/TextDiagnostic.cpp +++ b/clang/lib/Frontend/TextDiagnostic.cpp @@ -1138,7 +1138,7 @@ highlightLines(StringRef FileData, unsigned StartLineNumber, std::make_unique[]>( EndLineNumber - StartLineNumber + 1); - if (!PP || !ShowColors) + if (!PP || &PP->getSourceManager() != &SM || !ShowColors) return SnippetRanges; // Might cause emission of another diagnostic. diff --git a/clang/lib/FrontendTool/CMakeLists.txt b/clang/lib/FrontendTool/CMakeLists.txt index 061e54c3e62d0..8fbf931b6e9a9 100644 --- a/clang/lib/FrontendTool/CMakeLists.txt +++ b/clang/lib/FrontendTool/CMakeLists.txt @@ -9,6 +9,7 @@ set(link_libs clangDriver clangExtractAPI clangFrontend + clangIndex clangRewriteFrontend ) diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 6fc587f4de9de..7c2133647b19d 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -22,6 +22,7 @@ #include "clang/Frontend/FrontendPluginRegistry.h" #include "clang/Frontend/Utils.h" #include "clang/FrontendTool/Utils.h" +#include "clang/Index/IndexingAction.h" #include "clang/Rewrite/Frontend/FrontendActions.h" #include "clang/StaticAnalyzer/Frontend/AnalyzerHelpFlags.h" #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" @@ -186,6 +187,12 @@ CreateFrontendAction(CompilerInstance &CI) { Act = std::make_unique(std::move(Act)); } + if (!FEOpts.IndexStorePath.empty()) { + CI.getCodeGenOpts().ClearASTBeforeBackend = false; + Act = index::createIndexDataRecordingAction(FEOpts, std::move(Act)); + CI.setGenModuleActionWrapper(&index::createIndexDataRecordingAction); + } + // Wrap the base FE action in an extract api action to generate // symbol graph as a biproduct of compilation (enabled with // --emit-symbol-graph option) @@ -228,6 +235,19 @@ bool ExecuteCompilerInvocation(CompilerInstance *Clang) { Clang->LoadRequestedPlugins(); + /*TO_UPSTREAM(BoundsSafety) ON*/ + // XXX: Is it already enabled by default? + if (Clang->getLangOpts().BoundsSafety) { + auto &LLVMArgs = Clang->getFrontendOpts().LLVMArgs; + bool HasConstraintElim = std::any_of(LLVMArgs.begin(), LLVMArgs.end(), + [](std::string &arg) { + return StringRef(arg).starts_with("-enable-constraint-elimination"); + }); + if (!HasConstraintElim) + LLVMArgs.push_back("-enable-constraint-elimination"); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + // Honor -mllvm. // // FIXME: Remove this, one day. diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt index acf49e40c447e..f8420a45a1664 100644 --- a/clang/lib/Headers/CMakeLists.txt +++ b/clang/lib/Headers/CMakeLists.txt @@ -36,6 +36,7 @@ set(core_files tgmath.h unwind.h varargs.h + feature-availability.h ) set(arm_common_files @@ -325,6 +326,9 @@ set(files ${utility_files} ) +# TO_UPSTREAM(BoundsSafety) +list(APPEND files ptrcheck.h) + set(cuda_wrapper_files cuda_wrappers/algorithm cuda_wrappers/cmath diff --git a/clang/lib/Headers/feature-availability.h b/clang/lib/Headers/feature-availability.h new file mode 100644 index 0000000000000..31abd2e4e081b --- /dev/null +++ b/clang/lib/Headers/feature-availability.h @@ -0,0 +1,33 @@ +/*===---- feature_availability.h - Feature Availability --------------------=== + * + * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + * See https://llvm.org/LICENSE.txt for license information. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + *===-----------------------------------------------------------------------=== + */ + +#ifndef __FEATURE_AVAILABILITY_H +#define __FEATURE_AVAILABILITY_H + +#include + +/// The possible availability domain states. These values are hardcoded in the +/// compiler and reproduced here for convenience when defining domains. + +#define __AVAILABILITY_DOMAIN_ENABLED 0 +#define __AVAILABILITY_DOMAIN_DISABLED 1 +#define __AVAILABILITY_DOMAIN_DYNAMIC 2 + +/// A struct describing availability domain definitions. This struct definition +/// is just a convenience to ensure that a header defining an availability +/// domain can define it with the arguments that Clang expects at parse time. +struct __AvailabilityDomain { + /// The state of the domain (AVAILABLE, UNAVAILABLE, DYNAMIC, etc.). + intptr_t state; + /// An optional function pointer to call to query the availability of a domain + /// at runtime. This should only be non-null for domains in the DYNAMIC state. + int (*const runtimeQuery)(void); +}; + +#endif /* __FEATURE_AVAILABILITY_H */ diff --git a/clang/lib/Headers/float.h b/clang/lib/Headers/float.h index 84551af473b28..407b7d3ae8682 100644 --- a/clang/lib/Headers/float.h +++ b/clang/lib/Headers/float.h @@ -14,14 +14,11 @@ #include_next #else -/* If we're on MinGW, fall back to the system's float.h, which might have - * additional definitions provided for Windows. - * For more details see http://msdn.microsoft.com/en-us/library/y0ybw9fy.aspx - * - * Also fall back on AIX to allow additional definitions and - * implementation-defined values. +/* On various platforms, fall back to the system's float.h, which might have + * additional definitions and/or implementation-defined values. */ -#if (defined(__MINGW32__) || defined(_MSC_VER) || defined(_AIX)) && \ +#if (defined(__MINGW32__) || defined(_MSC_VER) || defined(_AIX) || \ + defined(__musl__)) && \ __STDC_HOSTED__ && __has_include_next() # include_next diff --git a/clang/lib/Headers/module.modulemap b/clang/lib/Headers/module.modulemap index dcaf09e8f2c55..4ef2bf66bbacc 100644 --- a/clang/lib/Headers/module.modulemap +++ b/clang/lib/Headers/module.modulemap @@ -326,3 +326,10 @@ module ptrauth { header "ptrauth.h" export * } + +/* TO_UPSTREAM(BoundsSafety) ON */ +module ptrcheck { + header "ptrcheck.h" + export * +} +/* TO_UPSTREAM(BoundsSafety) OFF */ \ No newline at end of file diff --git a/clang/lib/Headers/ptrauth.h b/clang/lib/Headers/ptrauth.h index d489a67c533d4..69dfb3d6beb3d 100644 --- a/clang/lib/Headers/ptrauth.h +++ b/clang/lib/Headers/ptrauth.h @@ -34,10 +34,29 @@ typedef enum { (or, in other words, the value of the stack pointer on function entry) */ ptrauth_key_return_address = ptrauth_key_process_dependent_code, + /* The key used to sign frame pointers on the stack. + The extra data is based on the storage address of the frame pointer. + On AArch64, that is always the storage address of the frame pointer + 16 + (or, in other words, the value of the stack pointer on function entry) */ + ptrauth_key_frame_pointer = ptrauth_key_process_dependent_data, + /* The key used to sign C function pointers. The extra data is always 0. */ ptrauth_key_function_pointer = ptrauth_key_process_independent_code, + /* The key used to sign block function pointers, including: + invocation functions, + block object copy functions, + block object destroy functions, + __block variable copy functions, and + __block variable destroy functions. + The extra data is always the address at which the function pointer + is stored. + + Note that block object pointers themselves (i.e. the direct + representations of values of block-pointer type) are not signed. */ + ptrauth_key_block_function = ptrauth_key_asia, + /* The key used to sign C++ v-table pointers. The extra data is always 0. */ ptrauth_key_cxx_vtable_pointer = ptrauth_key_process_independent_data, @@ -108,8 +127,8 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; The first argument must be an expression of pointer type. The second argument must be an expression of integer type. - The result will have type uintptr_t. */ -#define ptrauth_blend_discriminator(__pointer, __integer) \ + The result will have type ptrauth_extra_data_t. */ +#define ptrauth_blend_discriminator(__pointer, __integer) \ __builtin_ptrauth_blend_discriminator(__pointer, __integer) /* Return a signed pointer for a constant address in a manner which guarantees @@ -138,7 +157,7 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; The extra data must be an expression of pointer or integer type; if an integer, it will be coerced to ptrauth_extra_data_t. The result will have the same type as the original value. */ -#define ptrauth_sign_unauthenticated(__value, __key, __data) \ +#define ptrauth_sign_unauthenticated(__value, __key, __data) \ __builtin_ptrauth_sign_unauthenticated(__value, __key, __data) /* Authenticate a pointer using one scheme and resign it using another. @@ -195,7 +214,7 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; The result will have the same type as the original value. This operation traps if the authentication fails. */ -#define ptrauth_auth_data(__value, __old_key, __old_data) \ +#define ptrauth_auth_data(__value, __old_key, __old_data) \ __builtin_ptrauth_auth(__value, __old_key, __old_data) /* Compute a constant discriminator from the given string. @@ -246,10 +265,47 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; if the arguments were a pointer and a discriminator. The arguments must be either pointers or integers; if integers, they - will be coerce to uintptr_t. */ -#define ptrauth_sign_generic_data(__value, __data) \ + will be coerce to ptrauth_extra_data_t. */ +#define ptrauth_sign_generic_data(__value, __data) \ __builtin_ptrauth_sign_generic_data(__value, __data) +/* Define some standard __ptrauth qualifiers used in the ABI. */ +#define __ptrauth_function_pointer \ + __ptrauth(ptrauth_key_function_pointer,0,0) +#define __ptrauth_return_address \ + __ptrauth(ptrauth_key_return_address,1,0) +#define __ptrauth_block_invocation_pointer \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_block_copy_helper \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_block_destroy_helper \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_block_byref_copy_helper \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_block_byref_destroy_helper \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_objc_method_list_imp \ + __ptrauth(ptrauth_key_function_pointer,1,0) +#define __ptrauth_cxx_vtable_pointer \ + __ptrauth(ptrauth_key_cxx_vtable_pointer,0,0) +#define __ptrauth_cxx_vtt_vtable_pointer \ + __ptrauth(ptrauth_key_cxx_vtable_pointer,0,0) +#define __ptrauth_swift_heap_object_destructor \ + __ptrauth(ptrauth_key_function_pointer,1,0xbbbf) + +/* Some situations in the C++ and Swift ABIs use declaration-specific + or type-specific extra discriminators. */ +#define __ptrauth_cxx_virtual_function_pointer(__declkey) \ + __ptrauth(ptrauth_key_function_pointer,1,__declkey) +#define __ptrauth_swift_function_pointer(__typekey) \ + __ptrauth(ptrauth_key_function_pointer,0,__typekey) +#define __ptrauth_swift_class_method_pointer(__declkey) \ + __ptrauth(ptrauth_key_function_pointer,1,__declkey) +#define __ptrauth_swift_protocol_witness_function_pointer(__declkey) \ + __ptrauth(ptrauth_key_function_pointer,1,__declkey) +#define __ptrauth_swift_value_witness_function_pointer(__key) \ + __ptrauth(ptrauth_key_function_pointer,1,__key) + /* C++ vtable pointer signing class attribute */ #define ptrauth_cxx_vtable_pointer(key, address_discrimination, \ extra_discrimination...) \ @@ -327,6 +383,24 @@ typedef __UINTPTR_TYPE__ ptrauth_generic_signature_t; ((ptrauth_generic_signature_t)0); \ }) +#define ptrauth_string_discriminator(__string) ((ptrauth_extra_data_t)0) + +#define __ptrauth_function_pointer +#define __ptrauth_return_address +#define __ptrauth_block_invocation_pointer +#define __ptrauth_block_copy_helper +#define __ptrauth_block_destroy_helper +#define __ptrauth_block_byref_copy_helper +#define __ptrauth_block_byref_destroy_helper +#define __ptrauth_objc_method_list_imp +#define __ptrauth_cxx_vtable_pointer +#define __ptrauth_cxx_vtt_vtable_pointer +#define __ptrauth_swift_heap_object_destructor +#define __ptrauth_cxx_virtual_function_pointer(__declkey) +#define __ptrauth_swift_function_pointer(__typekey) +#define __ptrauth_swift_class_method_pointer(__declkey) +#define __ptrauth_swift_protocol_witness_function_pointer(__declkey) +#define __ptrauth_swift_value_witness_function_pointer(__key) #define ptrauth_cxx_vtable_pointer(key, address_discrimination, \ extra_discrimination...) diff --git a/clang/lib/Headers/ptrcheck.h b/clang/lib/Headers/ptrcheck.h new file mode 100644 index 0000000000000..6a6bb71df7b91 --- /dev/null +++ b/clang/lib/Headers/ptrcheck.h @@ -0,0 +1,339 @@ +/*===---- ptrcheck.h - Pointer bounds hints & specifications ----------------=== + * + * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + * See https://llvm.org/LICENSE.txt for license information. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + *===-----------------------------------------------------------------------=== + */ + +#ifndef __PTRCHECK_H +#define __PTRCHECK_H + +/* __has_ptrcheck can be used in preprocessor macros (and other parts of the + language expecting constant expressions) to test if bounds attributes + exist. */ +#if defined(__has_feature) && __has_feature(bounds_attributes) + #define __has_ptrcheck 1 +#else + #define __has_ptrcheck 0 +#endif + +#if defined(__has_feature) && __has_feature(bounds_safety_attributes) + #define __has_bounds_safety_attributes 1 +#else + #define __has_bounds_safety_attributes 0 +#endif + +#if __has_ptrcheck || __has_bounds_safety_attributes + +/* An attribute that modifies a pointer type such than it has the ABI of a + regular C pointer, without allowing pointer arithmetic. Pointer arithmetic is + a compile-time error. A __single pointer is expected to be either NULL or + point to exactly one valid value. */ +#define __single __attribute__((__single__)) + +/* An attribute that modifies a pointer type such than it can be used exactly + like a regular C pointer, with unchecked arithmetic and dereferencing. An + __unsafe_indexable pointer cannot convert implicitly to another type of + pointer since that would require information that is not available to the + program. You must use __unsafe_forge_bidi_indexable or __unsafe_forge_single + to convert __unsafe_indexable pointers to so-called safe pointers. */ +#define __unsafe_indexable __attribute__((__unsafe_indexable__)) + +/* An attribute that modifies a pointer type such that it has the ABI of a + regular C pointer, but it implicitly converts to a __bidi_indexable pointer + with bounds that assume there are N valid elements starting at its address. + The conversion happens at the same point the object converts to an rvalue, or + immediately for values which cannot be lvalues (such as function calls). */ + +/* Assignments to the pointer object must be accompanied with an assignment to + N if it is assignable. */ + +/* N must either be an expression that evaluates to a constant, or an integer + declaration from the same scope, or (for structure fields) a declaration + contained in basic arithmetic. */ +#define __counted_by(N) __attribute__((__counted_by__(N))) + +/* Identical to __counted_by_or_null(N), aside that the pointer may be null for + * non-zero values of N. */ +#define __counted_by_or_null(N) __attribute__((__counted_by_or_null__(N))) + +/* Identical to __counted_by(N), aside that N is a byte count instead of an + object count. */ +#define __sized_by(N) __attribute__((__sized_by__(N))) + +/* Identical to __sized_by(N), aside that the pointer may be null for non-zero + * values of N. */ +#define __sized_by_or_null(N) __attribute__((__sized_by_or_null__(N))) + +/* An attribute that modifies a pointer type such that it has the ABI of a + regular C pointer, but it implicitly converts to a __bidi_indexable pointer + with bounds that assume that E is one-past-the-end of the original object. + Implicitly, referencing E in the same scope will create a pointer that + converts to a __bidi_indexable pointer one-past-the-end of the original + object, but with a lower bound set to the value of the pointer that is + attributed. */ + +/* Assignments to the pointer object must be accompanied with an assignment to + E if it is assignable. */ +#define __ended_by(E) __attribute__((__ended_by__(E))) + +/* The __terminated_by(T) attribute can be applied to arrays and pointers. The + argument T specifies the terminator and must be an integer constant + expression. Even though T has to be an integer constant, __terminated_by(T) + can be applied to pointer arrays as well. For convenience, the + __null_terminated macro is provided, which is equivalent to + __terminated_by(0). + + The __terminated_by(T) attribute can be applied only to __single pointers. If + the pointer attribute is not specified, it is automatically set to __single. + A __terminated_by(T) pointer points to the first element of an array that is + terminated with T. + + Arithmetic on __terminated_by(T) pointers is restricted to only incrementing + the pointer by one, and must be able to be evaluated at compile-time. + Pointer arithmetic generates a runtime check to ensure that the pointer + doesn't point pass the terminator. + + A __terminated_by(T) pointer has the ABI of a regular C pointer. + + When __terminated_by(T) is applied to an array, the compiler checks if the + array is terminated with the given terminator T during the initialization. + Moreover, a __terminated_by(T) array decays to a __terminated_by(T) __single + pointer, instead of decaying to a __bidi_indexable pointer. */ +#define __terminated_by(T) __attribute__((__terminated_by__(T))) +#define __null_terminated __terminated_by(0) + +/* Directives that tells the compiler to assume that subsequent pointer types + have the ABI specified by the ABI parameter, which may be one of single, + indexable, bidi_indexable or unsafe_indexable. */ + +/* In project files, the ABI is assumed to be single by default. In headers + included from libraries or the SDK, the ABI is assumed to be unsafe_indexable + by default. */ +#define __ptrcheck_abi_assume_single() \ + _Pragma("clang abi_ptr_attr set(single)") + +#define __ptrcheck_abi_assume_unsafe_indexable() \ + _Pragma("clang abi_ptr_attr set(unsafe_indexable)") + +/* Create a __single pointer of a given type (T), starting at address P. T must + be a pointer type. */ +#define __unsafe_forge_single(T, P) \ + ((T __single)__builtin_unsafe_forge_single((P))) + +/* Create a __terminated_by pointer of a given pointer type (T), starting at + address P, terminated by E. T must be a pointer type. */ +#define __unsafe_forge_terminated_by(T, P, E) \ + ((T __terminated_by(E))__builtin_unsafe_forge_terminated_by((P), (E))) + +/* Create a __terminated_by pointer of a given pointer type (T), starting at + address P, terminated by 0. T must be a pointer type. */ +#define __unsafe_forge_null_terminated(T, P) __unsafe_forge_terminated_by(T, P, 0) + +/* Instruct the compiler to disregard the bounds of an array used in a function + prototype and allow the decayed pointer to use __counted_by. This is a niche + capability that is only useful in limited patterns (the way that `mig` uses + arrays being one of them). */ +#define __array_decay_discards_count_in_parameters \ + __attribute__((__decay_discards_count_in_parameters__)) + +/* An attribute to indicate a variable to be effectively constant (or data const) + that it is allocated in a const section so cannot be modified after an early + stage of bootup, for example. Adding this attribute allows a global variable + to be used in __counted_by attribute of struct fields, function parameter, or + local variable just like actual constants. + Note that ensuring the value never changes once it is used is the user's + responsibility. One way to achieve this is the xnu model, in which certain + variables are placed in a segment that is remapped as read-only after + initialization. */ +#define __unsafe_late_const __attribute__((__unsafe_late_const__)) + +#else + +#define __single +#define __unsafe_indexable +#define __counted_by(N) +#define __counted_by_or_null(N) +#define __sized_by(N) +#define __sized_by_or_null(N) +#define __ended_by(E) + +/* We intentionally define the terminated_by attributes to nothing. */ +#define __terminated_by(T) +#define __null_terminated + +/* Similarly, we intentionally define to nothing the + __ptrcheck_abi_assume_single and __ptrcheck_abi_assume_unsafe_indexable + macros because they do not lead to an ABI incompatibility. However, we do not + define the indexable and unsafe_indexable ones because the diagnostic is + better than the silent ABI break. */ +#define __ptrcheck_abi_assume_single() +#define __ptrcheck_abi_assume_unsafe_indexable() + +/* __unsafe_forge intrinsics are defined as regular C casts. */ +#define __unsafe_forge_single(T, P) ((T)(P)) +#define __unsafe_forge_terminated_by(T, P, E) ((T)(P)) +#define __unsafe_forge_null_terminated(T, P) ((T)(P)) + +/* decay operates normally; attribute is meaningless without pointer checks. */ +#define __array_decay_discards_count_in_parameters + +#endif /* __has_ptrcheck || __has_bounds_safety_attributes */ + +#if __has_ptrcheck + +/* An attribute that modifies a pointer type such that its ABI is three pointer + components: the pointer value itself (the pointer value); one-past-the-end of + the object it is derived from (the upper bound); and the base address of the + object it is derived from (the lower bound). The pointer value is allowed to + lie outside the [lower bound, upper bound) interval, and it supports the + entire range of arithmetic operations that are usually applicable to + pointers. Bounds are implicitly checked only when the pointer is dereferenced + or converted to a different representation. */ +#define __bidi_indexable __attribute__((__bidi_indexable__)) + +/* An attribute that modifies a pointer type such that its ABI is two pointer + components: the pointer value itself (the lower bound); and one-past-the-end + of the object it is derived from (the upper bound). Indexable pointers do not + support negative arithmetic operations: it is a compile-time error to use a + subtraction or add a negative quantity to them, and it is a runtime error if + the same happens at runtime while it can't be detected at compile-time. Same + as __bidi_indexable pointers, __indexable pointers are bounds-checked when + dereferenced or converted to another representation. */ +#define __indexable __attribute__((__indexable__)) + +/* Directives that tells the compiler to assume that subsequent pointer types + have the ABI specified by the ABI parameter, which may be one of single, + indexable, bidi_indexable or unsafe_indexable. */ + +#define __ptrcheck_abi_assume_indexable() \ + _Pragma("clang abi_ptr_attr set(indexable)") + +#define __ptrcheck_abi_assume_bidi_indexable() \ + _Pragma("clang abi_ptr_attr set(bidi_indexable)") + +/* Create a __bidi_indexable pointer of a given pointer type (T), starting at + address P, pointing to S bytes of valid memory. T must be a pointer type. */ +#define __unsafe_forge_bidi_indexable(T, P, S) \ + ((T __bidi_indexable)__builtin_unsafe_forge_bidi_indexable((P), (S))) + +/* Create a wide pointer with the same lower bound and upper bounds as X, but + with a pointer component also equal to the lower bound. */ +#define __ptr_lower_bound(X) __builtin_get_pointer_lower_bound(X) + +/* Create a wide pointer with the same lower bound and upper bounds as X, but + with a pointer component also equal to the upper bound. */ +#define __ptr_upper_bound(X) __builtin_get_pointer_upper_bound(X) + +/* Convert a __terminated_by(T) pointer to an __indexable pointer. These + operations will calculate the upper bound by iterating over the memory + pointed to by P in order to find the terminator. + + The __terminated_by_to_indexable(P) does NOT include the terminator within + bounds of the __indexable pointer. Consequently, the terminator cannot be + erased (or even accessed) through the __indexable pointer. The address one + past the end of the array (pointing to the terminator) can be found with + __ptr_upper_bound(). + + The __unsafe_terminated_by_to_indexable(P) does include the terminator within + the bounds of the __indexable pointer. This makes the operation unsafe, since + the terminator can be erased, and thus using P might result in out-of-bounds + access. */ +#define __terminated_by_to_indexable(P) \ + __builtin_terminated_by_to_indexable(P) +#define __unsafe_terminated_by_to_indexable(P) \ + __builtin_unsafe_terminated_by_to_indexable(P) +#define __null_terminated_to_indexable(P) \ + __builtin_terminated_by_to_indexable((P), 0) +#define __unsafe_null_terminated_to_indexable(P) \ + __builtin_unsafe_terminated_by_to_indexable((P), 0) + +/* __unsafe_terminated_by_from_indexable(T, PTR [, PTR_TO_TERM]) converts an + __indexable pointer to a __terminated_by(T) pointer. The operation will + check if the given terminator T occurs in the memory pointed to by PTR. + If so, the operation evaluates to __terminated_by(T) pointer. Otherwise, it + traps. + + The operation has an optional parameter PTR_TO_TERM, which changes the way + how the check for the terminator existence is generated. PTR_TO_TERM must + point to the terminator element and be within the bounds of PTR. + If PTR_TO_TERM is provided, the runtime will check if it is in fact within + the bounds and points to an element that equals to T. If PTR_TO_TERM is not + provided, the runtime will iterate over the memory pointed to by PTR to find + the terminator. + + The operation is unsafe, since the terminator can be erased through PTR after + the conversion. This can result in out-of-bounds access through the newly + created __terminated_by(T) pointer. + + For convenience, the + __unsafe_null_terminated_from_indexable(PTR [, PTR_TO_TERM]) macro is + provided, which assumes that the terminator is 0. */ +#define __unsafe_terminated_by_from_indexable(T, ...) \ + __builtin_unsafe_terminated_by_from_indexable((T), __VA_ARGS__) +#define __unsafe_null_terminated_from_indexable(...) \ + __builtin_unsafe_terminated_by_from_indexable(0, __VA_ARGS__) + +/* An attribute to indicate that a function is unavailable when -fbounds-safety + is enabled because it is unsafe. Calls to functions annotated with this + attribute are errors when -fbounds-safety is enabled, but are allowed when + -fbounds-safety is disabled. + + Example: + + void* __ptrcheck_unavailable some_unsafe_api(void*); + */ +#define __ptrcheck_unavailable \ + __attribute__((__unavailable__("unavailable with -fbounds-safety."))) + +/* __ptrcheck_unavailable_r is the same as __ptrcheck_unavailable but it takes + as an argument the name of replacement function that is safe for use with + -fbounds-safety enabled. + + Example: + + void* __counted_by(size) safe_api(void* __counted_by(size), size_t size); + + void* __ptrcheck_unavailable_r(safe_api) some_unsafe_api(void*); + */ +#define __ptrcheck_unavailable_r(REPLACEMENT) \ + __attribute__((__unavailable__( \ + "unavailable with -fbounds-safety. Use " #REPLACEMENT " instead."))) + +#else + +/* We intentionally define to nothing pointer attributes which do not have an + impact on the ABI. __indexable and __bidi_indexable are not defined because + of the ABI incompatibility that makes the diagnostic preferable. */ + +/* __unsafe_forge intrinsics are defined as regular C casts. */ +#define __unsafe_forge_bidi_indexable(T, P, S) ((T)(P)) + +/* The conversion between terminated_by pointers just evaluates to the pointer + argument. */ +#define __terminated_by_to_indexable(P) (P) +#define __unsafe_terminated_by_to_indexable(P) (P) +#define __null_terminated_to_indexable(P) (P) +#define __unsafe_null_terminated_to_indexable(P) (P) + +#define __IGNORE_REST(P, ...) (P) +/* Adding DUMMY is a workaround for the case where the macro is called + with only a single argument: calling the __IGNORE_REST macro with only a + single argument is a C23 extension and emits a warning for earlier + versions. + */ +#define __unsafe_terminated_by_from_indexable(T, ...) \ + __IGNORE_REST(__VA_ARGS__, DUMMY) +#define __unsafe_null_terminated_from_indexable(...) \ + __unsafe_terminated_by_from_indexable(DUMMY_TYPE, __VA_ARGS__) + +/* The APIs marked with these attributes are available outside the context of + pointer checks, so do nothing. */ +#define __ptrcheck_unavailable +#define __ptrcheck_unavailable_r(REPLACEMENT) + +#endif /* __has_ptrcheck */ + +#endif /* __PTRCHECK_H */ diff --git a/clang/lib/Headers/stddef.h b/clang/lib/Headers/stddef.h index 99b275aebf5aa..2bcf35eb5f355 100644 --- a/clang/lib/Headers/stddef.h +++ b/clang/lib/Headers/stddef.h @@ -33,6 +33,11 @@ #include <__stddef_header_macro.h> #include_next +#elif defined(__musl__) + +// On musl systems, use the system header +#include_next + #else #if !defined(__need_ptrdiff_t) && !defined(__need_size_t) && \ @@ -136,4 +141,4 @@ __WINT_TYPE__ directly; accommodate both by requiring __need_wint_t */ #undef __need_wint_t #endif /* __need_wint_t */ -#endif /* __MVS__ */ +#endif /* !defined(__MVS__) && !defined(__musl__) */ diff --git a/clang/lib/Index/BitstreamVisitor.h b/clang/lib/Index/BitstreamVisitor.h new file mode 100644 index 0000000000000..6d990e120a62b --- /dev/null +++ b/clang/lib/Index/BitstreamVisitor.h @@ -0,0 +1,190 @@ +//===--- BitstreamVisitor.h - Helper for reading a bitstream --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_BITSTREAMVISITOR_H +#define LLVM_CLANG_LIB_INDEX_BITSTREAMVISITOR_H + +#include "llvm/Bitstream/BitstreamReader.h" +#include "clang/Basic/LLVM.h" +#include "clang/Serialization/ASTReader.h" +#include + +namespace clang { +namespace index { +namespace store { + +/// Helper class that saves the current stream position and +/// then restores it when destroyed. +struct SavedStreamPosition { + explicit SavedStreamPosition(llvm::BitstreamCursor &Cursor) + : Cursor(Cursor), Offset(Cursor.GetCurrentBitNo()) { } + + ~SavedStreamPosition() { + if (llvm::Error Err = Cursor.JumpToBit(Offset)) + llvm::report_fatal_error(Twine("SavedStreamPosition failed jumping: ") + + toString(std::move(Err))); + } + +private: + llvm::BitstreamCursor &Cursor; + uint64_t Offset; +}; + +enum class StreamVisit { + Continue, + Skip, + Abort +}; + +template +class BitstreamVisitor { + SmallVector BlockStack; + +protected: + llvm::BitstreamCursor &Stream; + std::optional BlockInfo; + std::string *Error; + +public: + BitstreamVisitor(llvm::BitstreamCursor &Stream) + : Stream(Stream) {} + + StreamVisit visitBlock(unsigned ID) { + return StreamVisit::Continue; + } + + bool visit(std::string &Error) { + this->Error = &Error; + + ASTReader::RecordData Record; + while (1) { + Expected MaybeEntry = Stream.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd); + if (!MaybeEntry) { + Error = toString(MaybeEntry.takeError()); + return false; + } + llvm::BitstreamEntry Entry = MaybeEntry.get(); + + switch (Entry.Kind) { + case llvm::BitstreamEntry::Error: + Error = "malformed serialization"; + return false; + + case llvm::BitstreamEntry::EndBlock: + if (BlockStack.empty()) + return true; + BlockStack.pop_back(); + if (Stream.ReadBlockEnd()) { + Error = "malformed serialization"; + return false; + } + if (Stream.AtEndOfStream()) + return true; + break; + + case llvm::BitstreamEntry::SubBlock: { + if (Entry.ID == llvm::bitc::BLOCKINFO_BLOCK_ID) { + Expected> MaybeBlockInfo = Stream.ReadBlockInfoBlock(); + if (!MaybeBlockInfo) { + Error = toString(MaybeBlockInfo.takeError()); + return false; + } + BlockInfo = MaybeBlockInfo.get(); + if (!BlockInfo) { + Error = "malformed BlockInfoBlock"; + return false; + } + Stream.setBlockInfo(&*BlockInfo); + break; + } + + StreamVisit Ret = static_cast(this)->visitBlock(Entry.ID); + switch (Ret) { + case StreamVisit::Continue: + if (Stream.EnterSubBlock(Entry.ID)) { + Error = "malformed block record"; + return false; + } + if (llvm::Error Err = readBlockAbbrevs(Stream)) { + Error = toString(std::move(Err)); + return false; + } + BlockStack.push_back(Entry.ID); + break; + + case StreamVisit::Skip: + if (Stream.SkipBlock()) { + Error = "malformed serialization"; + return false; + } + if (Stream.AtEndOfStream()) + return true; + break; + + case StreamVisit::Abort: + return false; + } + break; + } + + case llvm::BitstreamEntry::Record: { + Record.clear(); + StringRef Blob; + Expected MaybeRecID = Stream.readRecord(Entry.ID, Record, &Blob); + if (!MaybeRecID) { + Error = toString(MaybeRecID.takeError()); + return false; + } + unsigned RecID = MaybeRecID.get(); + unsigned BlockID = BlockStack.empty() ? 0 : BlockStack.back(); + StreamVisit Ret = static_cast(this)->visitRecord(BlockID, RecID, Record, Blob); + switch (Ret) { + case StreamVisit::Continue: + break; + + case StreamVisit::Skip: + if (Expected Skipped = Stream.skipRecord(Entry.ID)) { + Error = toString(Skipped.takeError()); + return false; + } + break; + + case StreamVisit::Abort: + return false; + } + break; + } + } + } + } + + static llvm::Error readBlockAbbrevs(llvm::BitstreamCursor &Cursor) { + while (true) { + uint64_t Offset = Cursor.GetCurrentBitNo(); + Expected MaybeCode = Cursor.ReadCode(); + if (!MaybeCode) + return MaybeCode.takeError(); + unsigned Code = MaybeCode.get(); + + // We expect all abbrevs to be at the start of the block. + if (Code != llvm::bitc::DEFINE_ABBREV) { + if (llvm::Error Err = Cursor.JumpToBit(Offset)) + return Err; + return llvm::Error::success(); + } + if (llvm::Error Err = Cursor.ReadAbbrevRecord()) + return Err; + } + } +}; + +} // end namespace store +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/lib/Index/CMakeLists.txt b/clang/lib/Index/CMakeLists.txt index f0d2b579c8df6..57ae6546369e3 100644 --- a/clang/lib/Index/CMakeLists.txt +++ b/clang/lib/Index/CMakeLists.txt @@ -1,17 +1,26 @@ set(LLVM_LINK_COMPONENTS + BitReader + BitstreamReader Core Support ) add_clang_library(clangIndex + ClangIndexRecordWriter.cpp CommentToXML.cpp FileIndexRecord.cpp IndexBody.cpp + IndexDataStoreUtils.cpp IndexDecl.cpp IndexingAction.cpp IndexingContext.cpp + IndexRecordHasher.cpp + IndexRecordReader.cpp + IndexRecordWriter.cpp IndexSymbol.cpp IndexTypeSourceInfo.cpp + IndexUnitReader.cpp + IndexUnitWriter.cpp USRGeneration.cpp ADDITIONAL_HEADERS diff --git a/clang/lib/Index/ClangIndexRecordWriter.cpp b/clang/lib/Index/ClangIndexRecordWriter.cpp new file mode 100644 index 0000000000000..0c3793bfb2284 --- /dev/null +++ b/clang/lib/Index/ClangIndexRecordWriter.cpp @@ -0,0 +1,167 @@ +//===--- ClangIndexRecordWriter.cpp - Index record serialization ----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ClangIndexRecordWriter.h" +#include "FileIndexRecord.h" +#include "clang/Index/IndexSymbol.h" +#include "clang/Index/IndexRecordReader.h" +#include "clang/Index/USRGeneration.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" + +using namespace clang; +using namespace clang::index; + +StringRef ClangIndexRecordWriter::getUSR(const Decl *D) { + assert(D->isCanonicalDecl()); + auto Insert = USRByDecl.insert(std::make_pair(D, StringRef())); + if (Insert.second) { + Insert.first->second = getUSRNonCached(D); + } + return Insert.first->second; +} + +StringRef ClangIndexRecordWriter::getUSR(const IdentifierInfo *Name, + const MacroInfo *MI) { + assert(Name && MI); + auto Insert = USRByDecl.insert(std::make_pair(MI, StringRef())); + if (Insert.second) { + Insert.first->second = getUSRNonCached(Name, MI); + } + return Insert.first->second; +} + +StringRef ClangIndexRecordWriter::getUSRNonCached(const Decl *D) { + SmallString<256> Buf; + bool Ignore = generateUSRForDecl(D, Buf); + if (Ignore) + return StringRef(); + StringRef USR = Buf.str(); + char *Ptr = Allocator.Allocate(USR.size()); + std::copy(USR.begin(), USR.end(), Ptr); + return StringRef(Ptr, USR.size()); +} + +StringRef ClangIndexRecordWriter::getUSRNonCached(const IdentifierInfo *Name, + const MacroInfo *MI) { + SmallString<256> Buf; + bool Ignore = generateUSRForMacro(Name->getName(), MI->getDefinitionLoc(), + Ctx.getSourceManager(), Buf); + if (Ignore) + return StringRef(); + StringRef USR = Buf.str(); + char *Ptr = Allocator.Allocate(USR.size()); + std::copy(USR.begin(), USR.end(), Ptr); + return StringRef(Ptr, USR.size()); +} + +ClangIndexRecordWriter::ClangIndexRecordWriter(ASTContext &Ctx, + RecordingOptions Opts) + : Impl(Opts.DataDirPath), Ctx(Ctx), RecordOpts(std::move(Opts)) { + if (Opts.RecordSymbolCodeGenName) + ASTNameGen.reset(new ASTNameGenerator(Ctx)); +} + +ClangIndexRecordWriter::~ClangIndexRecordWriter() {} + +bool ClangIndexRecordWriter::writeRecord(StringRef Filename, + const FileIndexRecord &IdxRecord, + std::string &Error, + std::string *OutRecordFile) { + + std::array RecordHashArr = index::hashRecord(IdxRecord, Ctx); + uint64_t RecordHash = 0; + std::memcpy(&RecordHash, RecordHashArr.data(), RecordHashArr.size()); + + switch (Impl.beginRecord(Filename, RecordHash, Error, OutRecordFile)) { + case IndexRecordWriter::Result::Success: + break; // Continue writing. + case IndexRecordWriter::Result::Failure: + return true; + case IndexRecordWriter::Result::AlreadyExists: + return false; + } + + ASTContext &Ctx = getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + FileID FID = IdxRecord.getFileID(); + auto getLineCol = [&](unsigned Offset) -> std::pair { + unsigned LineNo = SM.getLineNumber(FID, Offset); + unsigned ColNo = SM.getColumnNumber(FID, Offset); + return std::make_pair(LineNo, ColNo); + }; + + llvm::DenseMap MacroNames; + + for (auto &Occur : IdxRecord.getDeclOccurrencesSortedByOffset()) { + unsigned Line, Col; + std::tie(Line, Col) = getLineCol(Occur.Offset); + SmallVector Related; + Related.reserve(Occur.Relations.size()); + for (auto &Rel : Occur.Relations) + Related.push_back(writer::SymbolRelation{Rel.RelatedSymbol, Rel.Roles}); + if (Occur.MacroName) + MacroNames[cast(Occur.DeclOrMacro)] = Occur.MacroName; + + Impl.addOccurrence(Occur.DeclOrMacro.getOpaqueValue(), Occur.Roles, Line, + Col, Related); + } + + PrintingPolicy Policy(Ctx.getLangOpts()); + Policy.SuppressTemplateArgsInCXXConstructors = true; + + auto Result = Impl.endRecord(Error, + [&](writer::OpaqueDecl OD, SmallVectorImpl &Scratch) { + writer::Symbol Sym; + auto DeclOrMacro = + llvm::PointerUnion::getFromOpaqueValue( + const_cast(OD)); + if (auto *MI = DeclOrMacro.dyn_cast()) { + auto *II = MacroNames[MI]; + assert(II); + Sym.SymInfo = getSymbolInfoForMacro(*MI); + Sym.Name = II->getName(); + Sym.USR = getUSR(II, MI); + assert(!Sym.USR.empty() && "Recorded macro without USR!"); + } else { + const Decl *D = cast(DeclOrMacro); + Sym.SymInfo = getSymbolInfo(D); + + auto *ND = dyn_cast(D); + if (ND) { + llvm::raw_svector_ostream OS(Scratch); + DeclarationName DeclName = ND->getDeclName(); + if (!DeclName.isEmpty()) + DeclName.print(OS, Policy); + } + unsigned NameLen = Scratch.size(); + Sym.Name = StringRef(Scratch.data(), NameLen); + + Sym.USR = getUSR(D); + assert(!Sym.USR.empty() && "Recorded decl without USR!"); + + if (ASTNameGen && ND) { + llvm::raw_svector_ostream OS(Scratch); + ASTNameGen->writeName(ND, OS); + } + unsigned CGNameLen = Scratch.size() - NameLen; + Sym.CodeGenName = StringRef(Scratch.data() + NameLen, CGNameLen); + } + + return Sym; + }); + + switch (Result) { + case IndexRecordWriter::Result::Success: + case IndexRecordWriter::Result::AlreadyExists: + return false; + case IndexRecordWriter::Result::Failure: + return true; + } +} diff --git a/clang/lib/Index/ClangIndexRecordWriter.h b/clang/lib/Index/ClangIndexRecordWriter.h new file mode 100644 index 0000000000000..eb8c66e1f2936 --- /dev/null +++ b/clang/lib/Index/ClangIndexRecordWriter.h @@ -0,0 +1,57 @@ +//===--- ClangIndexRecordWriter.h - Index record serialization ------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_CLANGINDEXRECORDWRITER_H +#define LLVM_CLANG_LIB_INDEX_CLANGINDEXRECORDWRITER_H + +#include "IndexRecordHasher.h" +#include "clang/AST/Mangle.h" +#include "clang/Index/IndexRecordWriter.h" +#include "clang/Index/IndexingAction.h" +#include "llvm/ADT/SmallString.h" + +namespace clang { + class ASTContext; + class Decl; + class IdentifierInfo; + class MacroInfo; + +namespace index { + class FileIndexRecord; + +class ClangIndexRecordWriter { + IndexRecordWriter Impl; + + ASTContext &Ctx; + RecordingOptions RecordOpts; + + std::unique_ptr ASTNameGen; + llvm::BumpPtrAllocator Allocator; + llvm::DenseMap USRByDecl; + +public: + ClangIndexRecordWriter(ASTContext &Ctx, RecordingOptions Opts); + ~ClangIndexRecordWriter(); + + ASTContext &getASTContext() { return Ctx; } + ASTNameGenerator *getASTNameGen() { return ASTNameGen.get(); } + + bool writeRecord(StringRef Filename, const FileIndexRecord &Record, + std::string &Error, std::string *RecordFile = nullptr); + StringRef getUSR(const Decl *D); + StringRef getUSR(const IdentifierInfo *Name, const MacroInfo *MI); + +private: + StringRef getUSRNonCached(const Decl *D); + StringRef getUSRNonCached(const IdentifierInfo *Name, const MacroInfo *MI); +}; + +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/lib/Index/IndexDataStoreUtils.cpp b/clang/lib/Index/IndexDataStoreUtils.cpp new file mode 100644 index 0000000000000..e3f9119b0e4d0 --- /dev/null +++ b/clang/lib/Index/IndexDataStoreUtils.cpp @@ -0,0 +1,554 @@ +//===--- IndexDataStoreUtils.cpp - Functions/constants for the data store -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexDataStoreSymbolUtils.h" +#include "IndexDataStoreUtils.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Bitstream/BitstreamWriter.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +static void appendSubDir(StringRef subdir, SmallVectorImpl &StorePathBuf) { + SmallString<10> VersionPath; + raw_svector_ostream(VersionPath) << 'v' << STORE_FORMAT_VERSION; + + sys::path::append(StorePathBuf, VersionPath); + sys::path::append(StorePathBuf, subdir); +} + +void store::appendInteriorUnitPath(StringRef UnitName, + SmallVectorImpl &PathBuf) { + sys::path::append(PathBuf, UnitName); +} + +void store::appendUnitSubDir(SmallVectorImpl &StorePathBuf) { + return appendSubDir("units", StorePathBuf); +} + +void store::appendRecordSubDir(SmallVectorImpl &StorePathBuf) { + return appendSubDir("records", StorePathBuf); +} + +void store::appendInteriorRecordPath(StringRef RecordName, + SmallVectorImpl &PathBuf) { + // To avoid putting a huge number of files into the records directory, create + // subdirectories based on the last 2 characters from the hash. + StringRef hash2chars = RecordName.substr(RecordName.size()-2); + sys::path::append(PathBuf, hash2chars); + sys::path::append(PathBuf, RecordName); +} + +void store::emitBlockID(unsigned ID, const char *Name, + BitstreamWriter &Stream, RecordDataImpl &Record) { + Record.clear(); + Record.push_back(ID); + Stream.EmitRecord(bitc::BLOCKINFO_CODE_SETBID, Record); + + // Emit the block name if present. + if (!Name || Name[0] == 0) + return; + Record.clear(); + while (*Name) + Record.push_back(*Name++); + Stream.EmitRecord(bitc::BLOCKINFO_CODE_BLOCKNAME, Record); +} + +void store::emitRecordID(unsigned ID, const char *Name, + BitstreamWriter &Stream, + RecordDataImpl &Record) { + Record.clear(); + Record.push_back(ID); + while (*Name) + Record.push_back(*Name++); + Stream.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, Record); +} + +/// Map an indexstore_symbol_kind_t to a SymbolKind, handling unknown values. +SymbolKind index::getSymbolKind(indexstore_symbol_kind_t K) { + switch ((uint64_t)K) { + default: + case INDEXSTORE_SYMBOL_KIND_UNKNOWN: + return SymbolKind::Unknown; + case INDEXSTORE_SYMBOL_KIND_MODULE: + return SymbolKind::Module; + case INDEXSTORE_SYMBOL_KIND_NAMESPACE: + return SymbolKind::Namespace; + case INDEXSTORE_SYMBOL_KIND_NAMESPACEALIAS: + return SymbolKind::NamespaceAlias; + case INDEXSTORE_SYMBOL_KIND_MACRO: + return SymbolKind::Macro; + case INDEXSTORE_SYMBOL_KIND_ENUM: + return SymbolKind::Enum; + case INDEXSTORE_SYMBOL_KIND_STRUCT: + return SymbolKind::Struct; + case INDEXSTORE_SYMBOL_KIND_CLASS: + return SymbolKind::Class; + case INDEXSTORE_SYMBOL_KIND_PROTOCOL: + return SymbolKind::Protocol; + case INDEXSTORE_SYMBOL_KIND_EXTENSION: + return SymbolKind::Extension; + case INDEXSTORE_SYMBOL_KIND_UNION: + return SymbolKind::Union; + case INDEXSTORE_SYMBOL_KIND_TYPEALIAS: + return SymbolKind::TypeAlias; + case INDEXSTORE_SYMBOL_KIND_FUNCTION: + return SymbolKind::Function; + case INDEXSTORE_SYMBOL_KIND_VARIABLE: + return SymbolKind::Variable; + case INDEXSTORE_SYMBOL_KIND_FIELD: + return SymbolKind::Field; + case INDEXSTORE_SYMBOL_KIND_ENUMCONSTANT: + return SymbolKind::EnumConstant; + case INDEXSTORE_SYMBOL_KIND_INSTANCEMETHOD: + return SymbolKind::InstanceMethod; + case INDEXSTORE_SYMBOL_KIND_CLASSMETHOD: + return SymbolKind::ClassMethod; + case INDEXSTORE_SYMBOL_KIND_STATICMETHOD: + return SymbolKind::StaticMethod; + case INDEXSTORE_SYMBOL_KIND_INSTANCEPROPERTY: + return SymbolKind::InstanceProperty; + case INDEXSTORE_SYMBOL_KIND_CLASSPROPERTY: + return SymbolKind::ClassProperty; + case INDEXSTORE_SYMBOL_KIND_STATICPROPERTY: + return SymbolKind::StaticProperty; + case INDEXSTORE_SYMBOL_KIND_CONSTRUCTOR: + return SymbolKind::Constructor; + case INDEXSTORE_SYMBOL_KIND_DESTRUCTOR: + return SymbolKind::Destructor; + case INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION: + return SymbolKind::ConversionFunction; + case INDEXSTORE_SYMBOL_KIND_PARAMETER: + return SymbolKind::Parameter; + case INDEXSTORE_SYMBOL_KIND_USING: + return SymbolKind::Using; + case INDEXSTORE_SYMBOL_KIND_COMMENTTAG: + return SymbolKind::CommentTag; + case INDEXSTORE_SYMBOL_KIND_CONCEPT: + return SymbolKind::Concept; + } +} + +SymbolSubKind index::getSymbolSubKind(indexstore_symbol_subkind_t K) { + switch ((uint64_t)K) { + default: + case INDEXSTORE_SYMBOL_SUBKIND_NONE: + return SymbolSubKind::None; + case INDEXSTORE_SYMBOL_SUBKIND_CXXCOPYCONSTRUCTOR: + return SymbolSubKind::CXXCopyConstructor; + case INDEXSTORE_SYMBOL_SUBKIND_CXXMOVECONSTRUCTOR: + return SymbolSubKind::CXXMoveConstructor; + case INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER: + return SymbolSubKind::AccessorGetter; + case INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER: + return SymbolSubKind::AccessorSetter; + case INDEXSTORE_SYMBOL_SUBKIND_USINGTYPENAME: + return SymbolSubKind::UsingTypename; + case INDEXSTORE_SYMBOL_SUBKIND_USINGVALUE: + return SymbolSubKind::UsingValue; + case INDEXSTORE_SYMBOL_SUBKIND_USINGENUM: + return SymbolSubKind::UsingEnum; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORWILLSET: + return SymbolSubKind::SwiftAccessorWillSet; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORDIDSET: + return SymbolSubKind::SwiftAccessorDidSet; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORADDRESSOR: + return SymbolSubKind::SwiftAccessorAddressor; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMUTABLEADDRESSOR: + return SymbolSubKind::SwiftAccessorMutableAddressor; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORREAD: + return SymbolSubKind::SwiftAccessorRead; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMODIFY: + return SymbolSubKind::SwiftAccessorModify; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORINIT: + return SymbolSubKind::SwiftAccessorInit; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFSTRUCT: + return SymbolSubKind::SwiftExtensionOfStruct; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFCLASS: + return SymbolSubKind::SwiftExtensionOfClass; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFENUM: + return SymbolSubKind::SwiftExtensionOfEnum; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFPROTOCOL: + return SymbolSubKind::SwiftExtensionOfProtocol; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTPREFIXOPERATOR: + return SymbolSubKind::SwiftPrefixOperator; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTPOSTFIXOPERATOR: + return SymbolSubKind::SwiftPostfixOperator; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTINFIXOPERATOR: + return SymbolSubKind::SwiftInfixOperator; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTSUBSCRIPT: + return SymbolSubKind::SwiftSubscript; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTASSOCIATEDTYPE: + return SymbolSubKind::SwiftAssociatedType; + case INDEXSTORE_SYMBOL_SUBKIND_SWIFTGENERICTYPEPARAM: + return SymbolSubKind::SwiftGenericTypeParam; + } +} + +/// Map an indexstore_symbol_language_t to a SymbolLanguage, handling unknown +/// values. +SymbolLanguage index::getSymbolLanguage(indexstore_symbol_language_t L) { + switch ((uint64_t)L) { + default: // FIXME: add an unknown language? + case INDEXSTORE_SYMBOL_LANG_C: + return SymbolLanguage::C; + case INDEXSTORE_SYMBOL_LANG_OBJC: + return SymbolLanguage::ObjC; + case INDEXSTORE_SYMBOL_LANG_CXX: + return SymbolLanguage::CXX; + case INDEXSTORE_SYMBOL_LANG_SWIFT: + return SymbolLanguage::Swift; + } +} + +/// Map an indexstore representation to a SymbolPropertySet, handling +/// unknown values. +SymbolPropertySet index::getSymbolProperties(uint64_t Props) { + SymbolPropertySet SymbolProperties = 0; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_GENERIC) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::Generic; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::TemplatePartialSpecialization; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::TemplateSpecialization; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_UNITTEST) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::UnitTest; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_IBANNOTATED) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::IBAnnotated; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_IBOUTLETCOLLECTION) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::IBOutletCollection; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_GKINSPECTABLE) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::GKInspectable; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_LOCAL) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::Local; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_PROTOCOL_INTERFACE) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::ProtocolInterface; + if (Props & INDEXSTORE_SYMBOL_PROPERTY_SWIFT_ASYNC) + SymbolProperties |= (SymbolPropertySet)SymbolProperty::SwiftAsync; + + return SymbolProperties; +} + +/// Map an indexstore representation to a SymbolRoleSet, handling unknown +/// values. +SymbolRoleSet index::getSymbolRoles(uint64_t Roles) { + SymbolRoleSet SymbolRoles = 0; + if (Roles & INDEXSTORE_SYMBOL_ROLE_DECLARATION) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Declaration; + if (Roles & INDEXSTORE_SYMBOL_ROLE_DEFINITION) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Definition; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REFERENCE) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Reference; + if (Roles & INDEXSTORE_SYMBOL_ROLE_READ) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Read; + if (Roles & INDEXSTORE_SYMBOL_ROLE_WRITE) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Write; + if (Roles & INDEXSTORE_SYMBOL_ROLE_CALL) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Call; + if (Roles & INDEXSTORE_SYMBOL_ROLE_DYNAMIC) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Dynamic; + if (Roles & INDEXSTORE_SYMBOL_ROLE_ADDRESSOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::AddressOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_IMPLICIT) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Implicit; + if (Roles & INDEXSTORE_SYMBOL_ROLE_UNDEFINITION) + SymbolRoles |= (SymbolRoleSet)SymbolRole::Undefinition; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationChildOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_BASEOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationBaseOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationOverrideOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationReceivedBy; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationCalledBy; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationExtendedBy; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationAccessorOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationContainedBy; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationIBTypeOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF) + SymbolRoles |= (SymbolRoleSet)SymbolRole::RelationSpecializationOf; + if (Roles & INDEXSTORE_SYMBOL_ROLE_NAMEREFERENCE) + SymbolRoles |= (SymbolRoleSet)SymbolRole::NameReference; + + return SymbolRoles; +} + +/// Map a SymbolLanguage to a indexstore_symbol_language_t. +indexstore_symbol_kind_t index::getIndexStoreKind(SymbolKind K) { + switch (K) { + case SymbolKind::Unknown: + // FIXME: Tweak SymbolKind/SymbolSubKind to separate + // generic-ish/language-specific-ish concepts. + // + // We use SymbolKind/SymbolSubKind + // downstream for Swift and this would help us avoid intrusions here or + // upstream. + // + // Separation of concepts for C++ and Objective-C could also be + // improved. Once that is done on SymbolKind/SymbolSubKind level we should + // update indexstore_symbol_kind_t too. + // + // Initial discussion here: + // https://github.com/apple/llvm-project/pull/1099 + case SymbolKind::TemplateTypeParm: + case SymbolKind::TemplateTemplateParm: + case SymbolKind::NonTypeTemplateParm: + return INDEXSTORE_SYMBOL_KIND_UNKNOWN; + case SymbolKind::Module: + return INDEXSTORE_SYMBOL_KIND_MODULE; + case SymbolKind::Namespace: + return INDEXSTORE_SYMBOL_KIND_NAMESPACE; + case SymbolKind::NamespaceAlias: + return INDEXSTORE_SYMBOL_KIND_NAMESPACEALIAS; + case SymbolKind::Macro: + return INDEXSTORE_SYMBOL_KIND_MACRO; + case SymbolKind::Enum: + return INDEXSTORE_SYMBOL_KIND_ENUM; + case SymbolKind::Struct: + return INDEXSTORE_SYMBOL_KIND_STRUCT; + case SymbolKind::Class: + return INDEXSTORE_SYMBOL_KIND_CLASS; + case SymbolKind::Protocol: + return INDEXSTORE_SYMBOL_KIND_PROTOCOL; + case SymbolKind::Extension: + return INDEXSTORE_SYMBOL_KIND_EXTENSION; + case SymbolKind::Union: + return INDEXSTORE_SYMBOL_KIND_UNION; + case SymbolKind::TypeAlias: + return INDEXSTORE_SYMBOL_KIND_TYPEALIAS; + case SymbolKind::Function: + return INDEXSTORE_SYMBOL_KIND_FUNCTION; + case SymbolKind::Variable: + return INDEXSTORE_SYMBOL_KIND_VARIABLE; + case SymbolKind::Field: + return INDEXSTORE_SYMBOL_KIND_FIELD; + case SymbolKind::EnumConstant: + return INDEXSTORE_SYMBOL_KIND_ENUMCONSTANT; + case SymbolKind::InstanceMethod: + return INDEXSTORE_SYMBOL_KIND_INSTANCEMETHOD; + case SymbolKind::ClassMethod: + return INDEXSTORE_SYMBOL_KIND_CLASSMETHOD; + case SymbolKind::StaticMethod: + return INDEXSTORE_SYMBOL_KIND_STATICMETHOD; + case SymbolKind::InstanceProperty: + return INDEXSTORE_SYMBOL_KIND_INSTANCEPROPERTY; + case SymbolKind::ClassProperty: + return INDEXSTORE_SYMBOL_KIND_CLASSPROPERTY; + case SymbolKind::StaticProperty: + return INDEXSTORE_SYMBOL_KIND_STATICPROPERTY; + case SymbolKind::Constructor: + return INDEXSTORE_SYMBOL_KIND_CONSTRUCTOR; + case SymbolKind::Destructor: + return INDEXSTORE_SYMBOL_KIND_DESTRUCTOR; + case SymbolKind::ConversionFunction: + return INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION; + case SymbolKind::Parameter: + return INDEXSTORE_SYMBOL_KIND_PARAMETER; + case SymbolKind::Using: + return INDEXSTORE_SYMBOL_KIND_USING; + case SymbolKind::CommentTag: + return INDEXSTORE_SYMBOL_KIND_COMMENTTAG; + case SymbolKind::Concept: + return INDEXSTORE_SYMBOL_KIND_CONCEPT; + } + llvm_unreachable("unexpected symbol kind"); +} + +indexstore_symbol_subkind_t index::getIndexStoreSubKind(SymbolSubKind K) { + switch (K) { + case SymbolSubKind::None: + return INDEXSTORE_SYMBOL_SUBKIND_NONE; + case SymbolSubKind::CXXCopyConstructor: + return INDEXSTORE_SYMBOL_SUBKIND_CXXCOPYCONSTRUCTOR; + case SymbolSubKind::CXXMoveConstructor: + return INDEXSTORE_SYMBOL_SUBKIND_CXXMOVECONSTRUCTOR; + case SymbolSubKind::AccessorGetter: + return INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER; + case SymbolSubKind::AccessorSetter: + return INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER; + case SymbolSubKind::UsingTypename: + return INDEXSTORE_SYMBOL_SUBKIND_USINGTYPENAME; + case SymbolSubKind::UsingValue: + return INDEXSTORE_SYMBOL_SUBKIND_USINGVALUE; + case SymbolSubKind::UsingEnum: + return INDEXSTORE_SYMBOL_SUBKIND_USINGENUM; + case SymbolSubKind::SwiftAccessorWillSet: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORWILLSET; + case SymbolSubKind::SwiftAccessorDidSet: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORDIDSET; + case SymbolSubKind::SwiftAccessorAddressor: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORADDRESSOR; + case SymbolSubKind::SwiftAccessorMutableAddressor: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMUTABLEADDRESSOR; + case SymbolSubKind::SwiftAccessorRead: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORREAD; + case SymbolSubKind::SwiftAccessorModify: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMODIFY; + case SymbolSubKind::SwiftAccessorInit: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORINIT; + case SymbolSubKind::SwiftExtensionOfStruct: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFSTRUCT; + case SymbolSubKind::SwiftExtensionOfClass: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFCLASS; + case SymbolSubKind::SwiftExtensionOfEnum: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFENUM; + case SymbolSubKind::SwiftExtensionOfProtocol: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFPROTOCOL; + case SymbolSubKind::SwiftPrefixOperator: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTPREFIXOPERATOR; + case SymbolSubKind::SwiftPostfixOperator: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTPOSTFIXOPERATOR; + case SymbolSubKind::SwiftInfixOperator: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTINFIXOPERATOR; + case SymbolSubKind::SwiftSubscript: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTSUBSCRIPT; + case SymbolSubKind::SwiftAssociatedType: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTASSOCIATEDTYPE; + case SymbolSubKind::SwiftGenericTypeParam: + return INDEXSTORE_SYMBOL_SUBKIND_SWIFTGENERICTYPEPARAM; + } + llvm_unreachable("unexpected symbol subkind"); +} + +/// Map a SymbolLanguage to a indexstore_symbol_language_t. +indexstore_symbol_language_t index::getIndexStoreLang(SymbolLanguage L) { + switch (L) { + case SymbolLanguage::C: + return INDEXSTORE_SYMBOL_LANG_C; + case SymbolLanguage::ObjC: + return INDEXSTORE_SYMBOL_LANG_OBJC; + case SymbolLanguage::CXX: + return INDEXSTORE_SYMBOL_LANG_CXX; + case SymbolLanguage::Swift: + return INDEXSTORE_SYMBOL_LANG_SWIFT; + } + llvm_unreachable("unexpected symbol language"); +} + +/// Map a SymbolPropertySet to its indexstore representation. +indexstore_symbol_property_t index::getIndexStoreProperties(SymbolPropertySet Props) { + uint64_t storeProp = 0; + applyForEachSymbolProperty(Props, [&](SymbolProperty prop) { + switch (prop) { + case SymbolProperty::Generic: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_GENERIC; + break; + case SymbolProperty::TemplatePartialSpecialization: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION; + break; + case SymbolProperty::TemplateSpecialization: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION; + break; + case SymbolProperty::UnitTest: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_UNITTEST; + break; + case SymbolProperty::IBAnnotated: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_IBANNOTATED; + break; + case SymbolProperty::IBOutletCollection: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_IBOUTLETCOLLECTION; + break; + case SymbolProperty::GKInspectable: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_GKINSPECTABLE; + break; + case SymbolProperty::Local: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_LOCAL; + break; + case SymbolProperty::ProtocolInterface: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_PROTOCOL_INTERFACE; + break; + case SymbolProperty::SwiftAsync: + storeProp |= INDEXSTORE_SYMBOL_PROPERTY_SWIFT_ASYNC; + break; + } + }); + return static_cast(storeProp); +} + +/// Map a SymbolRoleSet to its indexstore representation. +indexstore_symbol_role_t index::getIndexStoreRoles(SymbolRoleSet Roles) { + uint64_t storeRoles = 0; + applyForEachSymbolRole(Roles, [&](SymbolRole role) { + switch (role) { + case SymbolRole::Declaration: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_DECLARATION; + break; + case SymbolRole::Definition: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_DEFINITION; + break; + case SymbolRole::Reference: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REFERENCE; + break; + case SymbolRole::Read: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_READ; + break; + case SymbolRole::Write: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_WRITE; + break; + case SymbolRole::Call: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_CALL; + break; + case SymbolRole::Dynamic: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_DYNAMIC; + break; + case SymbolRole::AddressOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_ADDRESSOF; + break; + case SymbolRole::Implicit: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_IMPLICIT; + break; + case SymbolRole::Undefinition: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_UNDEFINITION; + break; + case SymbolRole::RelationChildOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF; + break; + case SymbolRole::RelationBaseOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_BASEOF; + break; + case SymbolRole::RelationOverrideOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF; + break; + case SymbolRole::RelationReceivedBy: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY; + break; + case SymbolRole::RelationCalledBy: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY; + break; + case SymbolRole::RelationExtendedBy: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY; + break; + case SymbolRole::RelationAccessorOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF; + break; + case SymbolRole::RelationContainedBy: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY; + break; + case SymbolRole::RelationIBTypeOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF; + break; + case SymbolRole::RelationSpecializationOf: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF; + break; + case SymbolRole::NameReference: + storeRoles |= INDEXSTORE_SYMBOL_ROLE_NAMEREFERENCE; + break; + } + }); + return static_cast(storeRoles); +} diff --git a/clang/lib/Index/IndexDataStoreUtils.h b/clang/lib/Index/IndexDataStoreUtils.h new file mode 100644 index 0000000000000..6fbaf0cb4c7a8 --- /dev/null +++ b/clang/lib/Index/IndexDataStoreUtils.h @@ -0,0 +1,115 @@ +//===--- IndexDataStoreUtils.h - Functions/constants for the data store ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_INDEXDATASTOREUTILS_H +#define LLVM_CLANG_LIB_INDEX_INDEXDATASTOREUTILS_H + +#include "llvm/Bitstream/BitCodes.h" +#include "clang/Basic/LLVM.h" + +namespace llvm { + class BitstreamWriter; +} + +namespace clang { +namespace index { +namespace store { + +static const unsigned STORE_FORMAT_VERSION = 5; + +void appendUnitSubDir(SmallVectorImpl &StorePathBuf); +void appendInteriorUnitPath(StringRef UnitName, + SmallVectorImpl &PathBuf); +void appendRecordSubDir(SmallVectorImpl &StorePathBuf); +void appendInteriorRecordPath(StringRef RecordName, + SmallVectorImpl &PathBuf); + +enum RecordBitRecord { + REC_VERSION = 0, + REC_DECLINFO = 1, + REC_DECLOFFSETS = 2, + REC_DECLOCCURRENCE = 3, +}; + +enum RecordBitBlock { + REC_VERSION_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID, + REC_DECLS_BLOCK_ID, + REC_DECLOFFSETS_BLOCK_ID, + REC_DECLOCCURRENCES_BLOCK_ID, +}; + +enum UnitBitRecord { + UNIT_VERSION = 0, + UNIT_INFO = 1, + UNIT_DEPENDENCY = 2, + UNIT_INCLUDE = 3, + UNIT_PATH = 4, + UNIT_PATH_BUFFER = 5, + UNIT_MODULE = 6, + UNIT_MODULE_BUFFER = 7, +}; + +enum UnitBitBlock { + UNIT_VERSION_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID, + UNIT_INFO_BLOCK_ID, + UNIT_DEPENDENCIES_BLOCK_ID, + UNIT_INCLUDES_BLOCK_ID, + UNIT_PATHS_BLOCK_ID, + UNIT_MODULES_BLOCK_ID, +}; + +enum UnitDependencyKind { + UNIT_DEPEND_KIND_FILE = 0, + UNIT_DEPEND_KIND_RECORD = 1, + UNIT_DEPEND_KIND_UNIT = 2, +}; +static const unsigned UnitDependencyKindBitNum = 2; + +enum UnitFilePathPrefixKind { + UNIT_PATH_PREFIX_NONE = 0, + UNIT_PATH_PREFIX_WORKDIR = 1, + UNIT_PATH_PREFIX_SYSROOT = 2, +}; +static const unsigned UnitFilePathPrefixKindBitNum = 2; + +typedef SmallVector RecordData; +typedef SmallVectorImpl RecordDataImpl; + +struct BitPathComponent { + size_t Offset = 0; + size_t Size = 0; + BitPathComponent(size_t Offset, size_t Size) : Offset(Offset), Size(Size) {} + BitPathComponent() = default; +}; + +struct DirBitPath { + UnitFilePathPrefixKind PrefixKind = UNIT_PATH_PREFIX_NONE; + BitPathComponent Dir; + DirBitPath(UnitFilePathPrefixKind Kind, + BitPathComponent Dir) : PrefixKind(Kind), Dir(Dir) {} + DirBitPath() = default; +}; + +struct FileBitPath : DirBitPath { + BitPathComponent Filename; + FileBitPath(UnitFilePathPrefixKind Kind, BitPathComponent Dir, + BitPathComponent Filename) : DirBitPath(Kind, Dir), Filename(Filename) {} + FileBitPath() = default; +}; + +void emitBlockID(unsigned ID, const char *Name, + llvm::BitstreamWriter &Stream, RecordDataImpl &Record); + +void emitRecordID(unsigned ID, const char *Name, + llvm::BitstreamWriter &Stream, RecordDataImpl &Record); + +} // end namespace store +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/lib/Index/IndexRecordHasher.cpp b/clang/lib/Index/IndexRecordHasher.cpp new file mode 100644 index 0000000000000..c2f627bed4603 --- /dev/null +++ b/clang/lib/Index/IndexRecordHasher.cpp @@ -0,0 +1,491 @@ +//===--- IndexRecordHasher.cpp - Index record hashing ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "IndexRecordHasher.h" +#include "FileIndexRecord.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/BLAKE3.h" +#include "llvm/Support/HashBuilder.h" +#include "llvm/Support/Path.h" + +using namespace clang; +using namespace clang::index; + +namespace { + +struct IndexRecordHasher { + ASTContext &Ctx; + llvm::HashBuilder, llvm::endianness::little> + HashBuilder; + + explicit IndexRecordHasher(ASTContext &context) : Ctx(context) {} + + void hashDecl(const Decl *D); + void hashMacro(const IdentifierInfo *name, const MacroInfo *macroInfo); + + void hashType(QualType ty); + void hashCanonicalType(CanQualType canTy); + void hashDeclName(DeclarationName name); + void hashNameSpec(const NestedNameSpecifier *nestedName); + void hashSelector(Selector selector); + void hashTemplateName(TemplateName name); + void hashTemplateArg(const TemplateArgument &arg); + void hashLoc(SourceLocation loc, bool includeOffset); + void hashAPInt(const llvm::APInt &val); +}; + +class DeclHashVisitor : public ConstDeclVisitor { + IndexRecordHasher &Hasher; + +public: + DeclHashVisitor(IndexRecordHasher &Hasher) : Hasher(Hasher) {} + + void VisitDecl(const Decl *decl) { + return VisitDeclContext(decl->getDeclContext()); + } + + void VisitNamedDecl(const NamedDecl *named) { + VisitDecl(named); + if (auto *attr = named->getExternalSourceSymbolAttr()) { + Hasher.HashBuilder.add(attr->getDefinedIn()); + } + Hasher.hashDeclName(named->getDeclName()); + } + + void VisitTagDecl(const TagDecl *tag) { + if (!tag->getDeclName().isEmpty()) { + Hasher.HashBuilder.add('T'); + VisitTypeDecl(tag); + return; + } + + if (const TypedefNameDecl *TD = tag->getTypedefNameForAnonDecl()) { + Visit(TD); + return; + } + + VisitDeclContext(tag->getDeclContext()); + if (tag->isEmbeddedInDeclarator() && !tag->isFreeStanding()) { + Hasher.hashLoc(tag->getLocation(), /*IncludeOffset=*/true); + return; + } + + Hasher.HashBuilder.add('a'); + } + + void VisitClassTemplateSpecializationDecl( + const ClassTemplateSpecializationDecl *D) { + Hasher.HashBuilder.add('>'); + VisitCXXRecordDecl(D); + for (const TemplateArgument &arg : D->getTemplateArgs().asArray()) { + Hasher.hashTemplateArg(arg); + } + } + + void VisitObjCContainerDecl(const ObjCContainerDecl *container) { + Hasher.HashBuilder.add('I'); + VisitNamedDecl(container); + } + + void VisitObjCImplDecl(const ObjCImplDecl *impl) { + if (auto *interface = impl->getClassInterface()) { + return VisitObjCInterfaceDecl(interface); + } + } + + void VisitObjCCategoryDecl(const ObjCCategoryDecl *category) { + // FIXME: Differentiate between category and the interface ? + if (auto *interface = category->getClassInterface()) + return VisitObjCInterfaceDecl(interface); + } + + void VisitFunctionDecl(const FunctionDecl *func) { + VisitNamedDecl(func); + if ((!Hasher.Ctx.getLangOpts().CPlusPlus && + !func->hasAttr()) || + func->isExternC()) + return; + + for (const auto *param : func->parameters()) { + Hasher.hashType(param->getType()); + } + } + + void VisitUnresolvedUsingTypenameDecl( + const UnresolvedUsingTypenameDecl *unresolved) { + VisitNamedDecl(unresolved); + Hasher.hashNameSpec(unresolved->getQualifier()); + } + + void + VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *unresolved) { + VisitNamedDecl(unresolved); + Hasher.hashNameSpec(unresolved->getQualifier()); + } + + void VisitDeclContext(const DeclContext *context) { + // FIXME: Add location if this is anonymous namespace ? + context = context->getRedeclContext(); + const Decl *decl = cast(context)->getCanonicalDecl(); + if (const auto *named = dyn_cast(decl)) { + Hasher.hashDecl(named); + } + } +}; +} + +std::array index::hashRecord(const FileIndexRecord &record, + ASTContext &context) { + IndexRecordHasher hasher(context); + + for (auto &Info : record.getDeclOccurrencesSortedByOffset()) { + hasher.HashBuilder.add(Info.Roles); + hasher.HashBuilder.add(Info.Offset); + + if (auto *D = Info.DeclOrMacro.dyn_cast()) { + hasher.hashDecl(D); + } else { + hasher.hashMacro(Info.MacroName, + cast(Info.DeclOrMacro)); + } + + for (auto &Rel : Info.Relations) { + hasher.hashDecl(Rel.RelatedSymbol); + } + } + + return hasher.HashBuilder.final(); +} + +void IndexRecordHasher::hashMacro(const IdentifierInfo *name, + const MacroInfo *macroInfo) { + HashBuilder.add("@macro@"); + HashBuilder.add(name->getName()); + + auto &sm = Ctx.getSourceManager(); + auto loc = macroInfo->getDefinitionLoc(); + // Only hash the location if it's not in a system header, to match how + // USR generation behaves. + if (loc.isValid() && !sm.isInSystemHeader(loc)) { + hashLoc(loc, /*IncludeOffset=*/true); + } +} + +void IndexRecordHasher::hashDecl(const Decl *D) { + assert(D->isCanonicalDecl()); + + if (auto *NS = dyn_cast(D)) { + if (NS->isAnonymousNamespace()) { + HashBuilder.add("@aN"); + return; + } + } + + DeclHashVisitor(*this).Visit(D); +} + +void IndexRecordHasher::hashType(QualType NonCanTy) { + CanQualType CanTy = Ctx.getCanonicalType(NonCanTy); + hashCanonicalType(CanTy); +} + +void IndexRecordHasher::hashCanonicalType(CanQualType CT) { + auto asCanon = [](QualType Ty) -> CanQualType { + return CanQualType::CreateUnsafe(Ty); + }; + + while (true) { + Qualifiers Q = CT.getQualifiers(); + CT = CT.getUnqualifiedType(); + const Type *T = CT.getTypePtr(); + unsigned qVal = 0; + if (Q.hasConst()) + qVal |= 0x1; + if (Q.hasVolatile()) + qVal |= 0x2; + if (Q.hasRestrict()) + qVal |= 0x4; + if(qVal) + HashBuilder.add(qVal); + + // Hash in ObjC GC qualifiers? + + if (const BuiltinType *builtin = dyn_cast(T)) { + HashBuilder.add(builtin->getKind()); + return; + } + if (const PointerType *pointer = dyn_cast(T)) { + HashBuilder.add('*'); + CT = asCanon(pointer->getPointeeType()); + continue; + } + if (const ReferenceType *ref = dyn_cast(T)) { + HashBuilder.add('&'); + CT = asCanon(ref->getPointeeType()); + continue; + } + if (const BlockPointerType *block = dyn_cast(T)) { + HashBuilder.add('B'); + CT = asCanon(block->getPointeeType()); + continue; + } + if (const ObjCObjectPointerType *pointer = + dyn_cast(T)) { + HashBuilder.add('*'); + CT = asCanon(pointer->getPointeeType()); + continue; + } + if (const TagType *tag = dyn_cast(T)) { + HashBuilder.add('$'); + hashDecl(tag->getDecl()->getCanonicalDecl()); + return; + } + if (const ObjCInterfaceType *interface = dyn_cast(T)) { + HashBuilder.add('$'); + hashDecl(interface->getDecl()->getCanonicalDecl()); + return; + } + if (const ObjCObjectType *obj = dyn_cast(T)) { + for (auto *proto : obj->getProtocols()) { + hashDecl(proto); + } + CT = asCanon(obj->getBaseType()); + continue; + } + if (const TemplateTypeParmType *param = dyn_cast(T)) { + HashBuilder.add('t'); + HashBuilder.add(param->getDepth()); + HashBuilder.add(param->getIndex()); + return; + } + if (const InjectedClassNameType *injected = + dyn_cast(T)) { + CT = + asCanon(injected->getInjectedSpecializationType().getCanonicalType()); + continue; + } + if (const PackExpansionType *expansion = dyn_cast(T)) { + HashBuilder.add('P'); + CT = asCanon(expansion->getPattern()); + continue; + } + if (const RValueReferenceType *ref = dyn_cast(T)) { + HashBuilder.add('%'); + CT = asCanon(ref->getPointeeType()); + continue; + } + if (const FunctionProtoType *func = dyn_cast(T)) { + HashBuilder.add('F'); + hashCanonicalType(asCanon(func->getReturnType())); + for (const auto ¶mType : func->param_types()) { + hashCanonicalType(asCanon(paramType)); + } + HashBuilder.add(func->isVariadic()); + return; + } + if (const ComplexType *complex = dyn_cast(T)) { + HashBuilder.add('<'); + CT = asCanon(complex->getElementType()); + } + if (const TemplateSpecializationType *spec = + dyn_cast(T)) { + HashBuilder.add('>'); + hashTemplateName(spec->getTemplateName()); + for (const TemplateArgument &arg : spec->template_arguments()) { + hashTemplateArg(arg); + } + return; + } + if (const DependentNameType *depName = dyn_cast(T)) { + HashBuilder.add('^'); + if (const NestedNameSpecifier *nameSpec = depName->getQualifier()) { + hashNameSpec(nameSpec); + } + HashBuilder.add(depName->getIdentifier()->getName()); + return; + } + + break; + } +} + +void IndexRecordHasher::hashDeclName(DeclarationName name) { + if (name.isEmpty()) + return; + + HashBuilder.add(name.getNameKind()); + + switch (name.getNameKind()) { + case DeclarationName::Identifier: + HashBuilder.add(name.getAsIdentifierInfo()->getName()); + break; + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + hashSelector(name.getObjCSelector()); + break; + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + break; + case DeclarationName::CXXOperatorName: + HashBuilder.add(name.getCXXOverloadedOperator()); + break; + case DeclarationName::CXXLiteralOperatorName: + HashBuilder.add(name.getCXXLiteralIdentifier()->getName()); + break; + case DeclarationName::CXXUsingDirective: + break; + case DeclarationName::CXXDeductionGuideName: + HashBuilder.add(name.getCXXDeductionGuideTemplate() + ->getDeclName() + .getAsIdentifierInfo() + ->getName()); + break; + } +} + +void IndexRecordHasher::hashNameSpec(const NestedNameSpecifier *nameSpec) { + assert(nameSpec); + + if (auto *prefix = nameSpec->getPrefix()) { + hashNameSpec(prefix); + } + + HashBuilder.add(nameSpec->getKind()); + + switch (nameSpec->getKind()) { + case NestedNameSpecifier::Identifier: + HashBuilder.add(nameSpec->getAsIdentifier()->getName()); + break; + + case NestedNameSpecifier::Namespace: + hashDecl(nameSpec->getAsNamespace()->getCanonicalDecl()); + break; + + case NestedNameSpecifier::NamespaceAlias: + hashDecl(nameSpec->getAsNamespaceAlias()->getCanonicalDecl()); + break; + + case NestedNameSpecifier::Global: + break; + + case NestedNameSpecifier::Super: + break; + + case NestedNameSpecifier::TypeSpec: + hashType(QualType(nameSpec->getAsType(), 0)); + break; + } +} + +void IndexRecordHasher::hashSelector(Selector selector) { + unsigned numArgs = selector.getNumArgs(); + if (numArgs == 0) { + ++numArgs; + } + + for (unsigned i = 0; i < numArgs; ++i) { + HashBuilder.add(selector.getNameForSlot(i)); + } +} + +void IndexRecordHasher::hashTemplateName(TemplateName name) { + if (TemplateDecl *decl = name.getAsTemplateDecl()) { + if (TemplateTemplateParmDecl *param = + dyn_cast(decl)) { + HashBuilder.add('t'); + HashBuilder.add(param->getDepth()); + HashBuilder.add(param->getIndex()); + return; + } + + hashDecl(decl->getCanonicalDecl()); + } + + // FIXME: Hash dependent template names. +} + +void IndexRecordHasher::hashTemplateArg(const TemplateArgument &arg) { + switch (arg.getKind()) { + case TemplateArgument::Null: + break; + + case TemplateArgument::Declaration: + hashDecl(arg.getAsDecl()); + break; + + case TemplateArgument::NullPtr: + break; + + case TemplateArgument::TemplateExpansion: + HashBuilder.add('P'); // pack expansion of... + LLVM_FALLTHROUGH; + case TemplateArgument::Template: + hashTemplateName(arg.getAsTemplateOrTemplatePattern()); + break; + + case TemplateArgument::Expression: + // FIXME: Hash expressions. + break; + + case TemplateArgument::Pack: + HashBuilder.add('p'); + for (const auto &element : arg.pack_elements()) { + hashTemplateArg(element); + } + break; + + case TemplateArgument::Type: + hashType(arg.getAsType()); + break; + + case TemplateArgument::Integral: + HashBuilder.add('V'); + hashType(arg.getIntegralType()); + hashAPInt(arg.getAsIntegral()); + break; + + case TemplateArgument::StructuralValue: + // FIXME: Hash structural values + break; + } +} + +void IndexRecordHasher::hashLoc(SourceLocation loc, bool includeOffset) { + if (loc.isInvalid()) + return; + + auto &SM = Ctx.getSourceManager(); + loc = SM.getFileLoc(loc); + + const std::pair &decomposed = SM.getDecomposedLoc(loc); + OptionalFileEntryRef entry = SM.getFileEntryRefForID(decomposed.first); + if (!entry) + return; + + HashBuilder.add(llvm::sys::path::filename(entry->getName())); + + if (includeOffset) { + // Use the offest into the FileID to represent the location. Using + // a line/column can cause us to look back at the original source file, + // which is expensive. + HashBuilder.add(decomposed.second); + } +} + +void IndexRecordHasher::hashAPInt(const llvm::APInt &val) { + HashBuilder.add(val.getBitWidth()); + HashBuilder.addRangeElements( + llvm::ArrayRef(val.getRawData(), val.getNumWords())); +} diff --git a/clang/lib/Index/IndexRecordHasher.h b/clang/lib/Index/IndexRecordHasher.h new file mode 100644 index 0000000000000..4ca663c30957f --- /dev/null +++ b/clang/lib/Index/IndexRecordHasher.h @@ -0,0 +1,26 @@ +//===--- IndexRecordHasher.h - Index record hashing -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_INDEXRECORDHASHER_H +#define LLVM_CLANG_LIB_INDEX_INDEXRECORDHASHER_H + +#include +#include + +namespace clang { +class ASTContext; + +namespace index { + class FileIndexRecord; + + std::array hashRecord(const FileIndexRecord &record, + ASTContext &context); +} // end namespace index +} // end namespace clang + +#endif diff --git a/clang/lib/Index/IndexRecordReader.cpp b/clang/lib/Index/IndexRecordReader.cpp new file mode 100644 index 0000000000000..f8078becdded5 --- /dev/null +++ b/clang/lib/Index/IndexRecordReader.cpp @@ -0,0 +1,436 @@ +//===--- IndexRecordReader.cpp - Index record deserialization -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexRecordReader.h" +#include "IndexDataStoreUtils.h" +#include "BitstreamVisitor.h" +#include "clang/Index/IndexDataStoreSymbolUtils.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +struct IndexRecordReader::Implementation { + BumpPtrAllocator Allocator; + std::unique_ptr Buffer; + llvm::BitstreamCursor DeclCursor; + llvm::BitstreamCursor OccurCursor; + ArrayRef DeclOffsets; + const IndexRecordDecl **Decls; + + void setDeclOffsets(ArrayRef Offs) { + DeclOffsets = Offs; + Decls = Allocator.Allocate(Offs.size()); + memset(Decls, 0, sizeof(IndexRecordDecl*)*Offs.size()); + } + + unsigned getNumDecls() const { return DeclOffsets.size(); } + + const IndexRecordDecl *getDeclByID(unsigned DeclID) { + if (DeclID == 0) + return nullptr; + return getDecl(DeclID-1); + } + + const IndexRecordDecl *getDecl(unsigned Index) { + assert(Index < getNumDecls()); + if (const IndexRecordDecl *D = Decls[Index]) + return D; + + IndexRecordDecl *D = Allocator.Allocate(); + readDecl(Index, *D); + Decls[Index] = D; + return D; + } + + /// Goes through the decls and populates a vector of record decls, based on + /// what the given function returns. + /// + /// The advantage of this function is to allocate memory only for the record + /// decls that the caller is interested in. + bool searchDecls(llvm::function_ref Checker, + llvm::function_ref Receiver) { + for (unsigned I = 0, E = getNumDecls(); I != E; ++I) { + if (const IndexRecordDecl *D = Decls[I]) { + DeclSearchReturn Ret = Checker(*D); + if (Ret.AcceptDecl) + Receiver(D); + if (!Ret.ContinueSearch) + return false; + continue; + } + + IndexRecordDecl LocalD; + readDecl(I, LocalD); + DeclSearchReturn Ret = Checker(LocalD); + if (Ret.AcceptDecl) { + IndexRecordDecl *D = Allocator.Allocate(); + *D = LocalD; + Decls[I] = D; + Receiver(D); + } + if (!Ret.ContinueSearch) + return false; + } + return true; + } + + void readDecl(unsigned Index, IndexRecordDecl &RecD) { + RecordData Record; + StringRef Blob; + if (llvm::Error Err = DeclCursor.JumpToBit(DeclOffsets[Index])) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + } + Expected MaybeCode = DeclCursor.ReadCode(); + if (!MaybeCode) { + // FIXME this drops the error on the floor. + consumeError(MaybeCode.takeError()); + } + unsigned Code = MaybeCode.get(); + Expected MaybeRecID = DeclCursor.readRecord(Code, Record, &Blob); + if (!MaybeRecID) { + // FIXME this drops the error on the floor. + consumeError(MaybeRecID.takeError()); + } + unsigned RecID = MaybeRecID.get(); + assert(RecID == REC_DECLINFO); + (void)RecID; + + unsigned I = 0; + RecD.DeclID = Index+1; + RecD.SymInfo.Kind = getSymbolKind((indexstore_symbol_kind_t)read(Record, I)); + RecD.SymInfo.SubKind = getSymbolSubKind((indexstore_symbol_subkind_t)read(Record, I)); + RecD.SymInfo.Lang = + getSymbolLanguage((indexstore_symbol_language_t)read(Record, I)); + RecD.SymInfo.Properties = getSymbolProperties(read(Record, I)); + RecD.Roles = getSymbolRoles(read(Record, I)); + RecD.RelatedRoles = getSymbolRoles(read(Record, I)); + size_t NameLen = read(Record, I); + size_t USRLen = read(Record, I); + RecD.Name = Blob.substr(0, NameLen); + RecD.USR = Blob.substr(NameLen, USRLen); + RecD.CodeGenName = Blob.substr(NameLen+USRLen); + } + + /// Reads occurrence data. + /// \param DeclsFilter if non-empty indicates the list of decls that we want + /// to get occurrences for. If empty then indicates that we want occurrences + /// for all decls. + /// \param RelatedDeclsFilter Same as \c DeclsFilter but for related decls. + /// \returns true if the occurrence info was filled out, false if occurrence + /// was ignored. + bool readOccurrence(RecordDataImpl &Record, StringRef Blob, + ArrayRef DeclsFilter, + ArrayRef RelatedDeclsFilter, + IndexRecordOccurrence &RecOccur) { + + auto isDeclIDContained = [](unsigned DeclID, + ArrayRef Ds) -> bool { + if (Ds.empty()) + return true; // empty means accept all. + auto pred = [DeclID](const IndexRecordDecl *D) { return D->DeclID == DeclID; }; + return std::find_if(Ds.begin(), Ds.end(), pred) != Ds.end(); + }; + + unsigned I = 0; + unsigned DeclID = read(Record, I); + if (!isDeclIDContained(DeclID, DeclsFilter)) + return false; + + if (!RelatedDeclsFilter.empty()) { + unsigned RelI = I+3; + unsigned NumRelated = read(Record, RelI); + bool FoundRelated = false; + while (NumRelated--) { + ++RelI; // roles; + unsigned RelDID = read(Record, RelI); + if (isDeclIDContained(RelDID, RelatedDeclsFilter)) { + FoundRelated = true; + break; + } + } + if (!FoundRelated) + return false; + } + + RecOccur.Dcl = getDeclByID(DeclID); + RecOccur.Roles = getSymbolRoles(read(Record, I)); + RecOccur.Line = read(Record, I); + RecOccur.Column = read(Record, I); + + unsigned NumRelated = read(Record, I); + while (NumRelated--) { + SymbolRoleSet RelRoles = getSymbolRoles(read(Record, I)); + const IndexRecordDecl *RelD = getDeclByID(read(Record, I)); + RecOccur.Relations.emplace_back(RelRoles, RelD); + } + + return true; + } + + bool foreachDecl(bool NoCache, + function_ref Receiver) { + for (unsigned I = 0, E = getNumDecls(); I != E; ++I) { + if (const IndexRecordDecl *D = Decls[I]) { + if (!Receiver(D)) + return false; + continue; + } + + if (NoCache) { + IndexRecordDecl LocalD; + readDecl(I, LocalD); + if (!Receiver(&LocalD)) + return false; + } else { + if (!Receiver(getDecl(I))) + return false; + } + } + return true; + } + + bool foreachOccurrence(ArrayRef DeclsFilter, + ArrayRef RelatedDeclsFilter, + function_ref Receiver) { + class OccurBitVisitor : public BitstreamVisitor { + IndexRecordReader::Implementation &Reader; + ArrayRef DeclsFilter; + ArrayRef RelatedDeclsFilter; + function_ref Receiver; + + public: + OccurBitVisitor(llvm::BitstreamCursor &Stream, + IndexRecordReader::Implementation &Reader, + ArrayRef DeclsFilter, + ArrayRef RelatedDeclsFilter, + function_ref Receiver) + : BitstreamVisitor(Stream), + Reader(Reader), + DeclsFilter(DeclsFilter), + RelatedDeclsFilter(RelatedDeclsFilter), + Receiver(std::move(Receiver)) {} + + StreamVisit visitRecord(unsigned BlockID, unsigned RecID, + RecordDataImpl &Record, StringRef Blob) { + assert(RecID == REC_DECLOCCURRENCE); + IndexRecordOccurrence RecOccur; + if (Reader.readOccurrence(Record, Blob, DeclsFilter, RelatedDeclsFilter, + RecOccur)) + if (!Receiver(RecOccur)) + return StreamVisit::Abort; + return StreamVisit::Continue; + } + }; + + SavedStreamPosition SavedPosition(OccurCursor); + OccurBitVisitor Visitor(OccurCursor, *this, DeclsFilter, RelatedDeclsFilter, + Receiver); + std::string Error; + return Visitor.visit(Error); + } + + bool foreachOccurrenceInLineRange(unsigned lineStart, unsigned lineCount, + llvm::function_ref receiver) { + // FIXME: Use binary search and make this more efficient. + unsigned lineEnd = lineStart+lineCount; + return foreachOccurrence(std::nullopt, std::nullopt, + [&](const IndexRecordOccurrence &occur) -> bool { + if (occur.Line > lineEnd) + return false; // we're done. + if (occur.Line >= lineStart) { + if (!receiver(occur)) + return false; + } + return true; + }); + } + + static uint64_t read(RecordDataImpl &Record, unsigned &I) { + return Record[I++]; + } +}; + +namespace { + +class IndexBitstreamVisitor : public BitstreamVisitor { + IndexRecordReader::Implementation &Reader; + +public: + IndexBitstreamVisitor(llvm::BitstreamCursor &Stream, + IndexRecordReader::Implementation &Reader) + : BitstreamVisitor(Stream), Reader(Reader) {} + + StreamVisit visitBlock(unsigned ID) { + switch ((RecordBitBlock)ID) { + case REC_VERSION_BLOCK_ID: + case REC_DECLOFFSETS_BLOCK_ID: + return StreamVisit::Continue; + + case REC_DECLS_BLOCK_ID: + Reader.DeclCursor = Stream; + if (Reader.DeclCursor.EnterSubBlock(ID)) { + *Error = "malformed block record"; + return StreamVisit::Abort; + } + if (llvm::Error Err = readBlockAbbrevs(Reader.DeclCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } + return StreamVisit::Skip; + + case REC_DECLOCCURRENCES_BLOCK_ID: + Reader.OccurCursor = Stream; + if (Reader.OccurCursor.EnterSubBlock(ID)) { + *Error = "malformed block record"; + return StreamVisit::Abort; + } + if (llvm::Error Err = readBlockAbbrevs(Reader.OccurCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } + return StreamVisit::Skip; + } + + // Some newly introduced block in a minor version update that we cannot + // handle. + return StreamVisit::Skip; + } + + StreamVisit visitRecord(unsigned BlockID, unsigned RecID, + RecordDataImpl &Record, StringRef Blob) { + switch (BlockID) { + case REC_VERSION_BLOCK_ID: { + unsigned StoreFormatVersion = Record[0]; + if (StoreFormatVersion != STORE_FORMAT_VERSION) { + llvm::raw_string_ostream OS(*Error); + OS << "Store format version mismatch: " << StoreFormatVersion; + OS << " , expected: " << STORE_FORMAT_VERSION; + return StreamVisit::Abort; + } + break; + } + case REC_DECLOFFSETS_BLOCK_ID: + assert(RecID == REC_DECLOFFSETS); + Reader.setDeclOffsets(ArrayRef((const uint32_t*)Blob.data(), + Record[0])); + break; + + case REC_DECLS_BLOCK_ID: + case REC_DECLOCCURRENCES_BLOCK_ID: + llvm_unreachable("shouldn't visit this block'"); + } + return StreamVisit::Continue; + } +}; + +} // anonymous namespace + +std::unique_ptr +IndexRecordReader::createWithRecordFilename(StringRef RecordFilename, + StringRef StorePath, + std::string &Error) { + SmallString<128> PathBuf = StorePath; + appendRecordSubDir(PathBuf); + appendInteriorRecordPath(RecordFilename, PathBuf); + return createWithFilePath(PathBuf.str(), Error); +} + +std::unique_ptr +IndexRecordReader::createWithFilePath(StringRef FilePath, std::string &Error) { + auto ErrOrBuf = MemoryBuffer::getFile(FilePath, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrOrBuf) { + raw_string_ostream(Error) << "failed opening index record '" + << FilePath << "': " << ErrOrBuf.getError().message(); + return nullptr; + } + return createWithBuffer(std::move(*ErrOrBuf), Error); +} + +std::unique_ptr +IndexRecordReader::createWithBuffer(std::unique_ptr Buffer, + std::string &Error) { + + std::unique_ptr Reader; + Reader.reset(new IndexRecordReader()); + auto &Impl = Reader->Impl; + Impl.Buffer = std::move(Buffer); + llvm::BitstreamCursor Stream(*Impl.Buffer); + + if (Stream.AtEndOfStream()) { + Error = "empty file"; + return nullptr; + } + + // Sniff for the signature. + for (unsigned char C : {'I', 'D', 'X', 'R'}) { + if (Expected Res = Stream.Read(8)) { + if (Res.get() == C) + continue; + } else { + Error = toString(Res.takeError()); + return nullptr; + } + Error = "not a serialized index record file"; + return nullptr; + } + + IndexBitstreamVisitor BitVisitor(Stream, Impl); + if (!BitVisitor.visit(Error)) + return nullptr; + + return Reader; +} + +IndexRecordReader::IndexRecordReader() + : Impl(*new Implementation()) { + +} + +IndexRecordReader::~IndexRecordReader() { + delete &Impl; +} + +bool IndexRecordReader::searchDecls( + llvm::function_ref Checker, + llvm::function_ref Receiver) { + return Impl.searchDecls(std::move(Checker), std::move(Receiver)); +} + +bool IndexRecordReader::foreachDecl(bool NoCache, + function_ref Receiver) { + return Impl.foreachDecl(NoCache, std::move(Receiver)); +} + +bool IndexRecordReader::foreachOccurrence( + ArrayRef DeclsFilter, + ArrayRef RelatedDeclsFilter, + function_ref Receiver) { + return Impl.foreachOccurrence(DeclsFilter, RelatedDeclsFilter, + std::move(Receiver)); +} + +bool IndexRecordReader::foreachOccurrence( + llvm::function_ref Receiver) { + return foreachOccurrence(std::nullopt, std::nullopt, std::move(Receiver)); +} + +bool IndexRecordReader::foreachOccurrenceInLineRange(unsigned lineStart, + unsigned lineCount, + llvm::function_ref Receiver) { + return Impl.foreachOccurrenceInLineRange(lineStart, lineCount, Receiver); +} diff --git a/clang/lib/Index/IndexRecordWriter.cpp b/clang/lib/Index/IndexRecordWriter.cpp new file mode 100644 index 0000000000000..ec1ed09b95afd --- /dev/null +++ b/clang/lib/Index/IndexRecordWriter.cpp @@ -0,0 +1,375 @@ +//===--- IndexRecordWriter.cpp - Index record serialization ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexRecordWriter.h" +#include "IndexDataStoreUtils.h" +#include "indexstore/indexstore.h" +#include "clang/Index/IndexDataStoreSymbolUtils.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Bitstream/BitstreamWriter.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +using writer::OpaqueDecl; + +namespace { +struct DeclInfo { + OpaqueDecl D; + SymbolRoleSet Roles; + SymbolRoleSet RelatedRoles; +}; + +struct OccurrenceInfo { + unsigned DeclID; + OpaqueDecl D; + SymbolRoleSet Roles; + unsigned Line; + unsigned Column; + SmallVector, 4> Related; +}; + +struct RecordState { + std::string RecordPath; + SmallString<512> Buffer; + BitstreamWriter Stream; + + DenseMap IndexForDecl; + std::vector Decls; + std::vector Occurrences; + + RecordState(std::string &&RecordPath) + : RecordPath(std::move(RecordPath)), Stream(Buffer) {} +}; +} // end anonymous namespace + +static void writeBlockInfo(BitstreamWriter &Stream) { + RecordData Record; + + Stream.EnterBlockInfoBlock(); +#define BLOCK(X) emitBlockID(X ## _ID, #X, Stream, Record) +#define RECORD(X) emitRecordID(X, #X, Stream, Record) + + BLOCK(REC_VERSION_BLOCK); + RECORD(REC_VERSION); + + BLOCK(REC_DECLS_BLOCK); + RECORD(REC_DECLINFO); + + BLOCK(REC_DECLOFFSETS_BLOCK); + RECORD(REC_DECLOFFSETS); + + BLOCK(REC_DECLOCCURRENCES_BLOCK); + RECORD(REC_DECLOCCURRENCE); + +#undef RECORD +#undef BLOCK + Stream.ExitBlock(); +} + +static void writeVersionInfo(BitstreamWriter &Stream) { + using namespace llvm::sys; + + Stream.EnterSubblock(REC_VERSION_BLOCK_ID, 3); + + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(REC_VERSION)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Store format version + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + RecordData Record; + Record.push_back(REC_VERSION); + Record.push_back(STORE_FORMAT_VERSION); + Stream.EmitRecordWithAbbrev(AbbrevCode, Record); + + Stream.ExitBlock(); +} + +template +static StringRef data(const std::vector &v) { + if (v.empty()) + return StringRef(); + return StringRef(reinterpret_cast(&v[0]), sizeof(T) * v.size()); +} + +template static StringRef data(const SmallVectorImpl &v) { + return StringRef(reinterpret_cast(v.data()), + sizeof(T) * v.size()); +} + +static void writeDecls(BitstreamWriter &Stream, ArrayRef Decls, + ArrayRef Occurrences, + writer::SymbolWriterCallback GetSymbolForDecl) { + SmallVector DeclOffsets; + DeclOffsets.reserve(Decls.size()); + + //===--------------------------------------------------------------------===// + // DECLS_BLOCK_ID + //===--------------------------------------------------------------------===// + + Stream.EnterSubblock(REC_DECLS_BLOCK_ID, 3); + + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(REC_DECLINFO)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // Kind + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // SubKind + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // Language + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 9)); // Properties + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, SymbolRoleBitNum)); // Roles + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, SymbolRoleBitNum)); // Related Roles + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Length of name in block + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Length of USR in block + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name + USR + CodeGen symbol name + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + +#ifndef NDEBUG + StringSet<> USRSet; + bool enableValidation = getenv("CLANG_INDEX_VALIDATION_CHECKS") != nullptr; +#endif + + RecordData Record; + llvm::SmallString<256> Blob; + llvm::SmallString<256> Scratch; + for (auto &Info : Decls) { + DeclOffsets.push_back(Stream.GetCurrentBitNo()); + Blob.clear(); + Scratch.clear(); + + writer::Symbol SymInfo = GetSymbolForDecl(Info.D, Scratch); + assert(SymInfo.SymInfo.Kind != SymbolKind::Unknown); + assert(!SymInfo.USR.empty() && "Recorded decl without USR!"); + + Blob += SymInfo.Name; + Blob += SymInfo.USR; + Blob += SymInfo.CodeGenName; + +#ifndef NDEBUG + if (enableValidation) { + bool IsNew = USRSet.insert(SymInfo.USR).second; + if (!IsNew) { + llvm::errs() << "Index: Duplicate USR! " << SymInfo.USR << "\n"; + // FIXME: print more information so it's easier to find the declaration. + } + } +#endif + + Record.clear(); + Record.push_back(REC_DECLINFO); + Record.push_back(getIndexStoreKind(SymInfo.SymInfo.Kind)); + Record.push_back(getIndexStoreSubKind(SymInfo.SymInfo.SubKind)); + Record.push_back(getIndexStoreLang(SymInfo.SymInfo.Lang)); + Record.push_back(getIndexStoreProperties(SymInfo.SymInfo.Properties)); + Record.push_back(getIndexStoreRoles(Info.Roles)); + Record.push_back(getIndexStoreRoles(Info.RelatedRoles)); + Record.push_back(SymInfo.Name.size()); + Record.push_back(SymInfo.USR.size()); + Stream.EmitRecordWithBlob(AbbrevCode, Record, Blob); + } + + Stream.ExitBlock(); + + //===--------------------------------------------------------------------===// + // DECLOFFSETS_BLOCK_ID + //===--------------------------------------------------------------------===// + + Stream.EnterSubblock(REC_DECLOFFSETS_BLOCK_ID, 3); + + Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(REC_DECLOFFSETS)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Number of Decls + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Offsets array + AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + Record.clear(); + Record.push_back(REC_DECLOFFSETS); + Record.push_back(DeclOffsets.size()); + Stream.EmitRecordWithBlob(AbbrevCode, Record, data(DeclOffsets)); + + Stream.ExitBlock(); + + //===--------------------------------------------------------------------===// + // DECLOCCURRENCES_BLOCK_ID + //===--------------------------------------------------------------------===// + + Stream.EnterSubblock(REC_DECLOCCURRENCES_BLOCK_ID, 3); + + Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(REC_DECLOCCURRENCE)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Decl ID + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, SymbolRoleBitNum)); // Roles + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 12)); // Line + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Column + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // Num related + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); // Related Roles/IDs + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 16)); // Roles or ID + AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + for (auto &Occur : Occurrences) { + Record.clear(); + Record.push_back(REC_DECLOCCURRENCE); + Record.push_back(Occur.DeclID); + Record.push_back(getIndexStoreRoles(Occur.Roles)); + Record.push_back(Occur.Line); + Record.push_back(Occur.Column); + Record.push_back(Occur.Related.size()); + for (auto &Rel : Occur.Related) { + Record.push_back(getIndexStoreRoles(Rel.first.Roles)); + Record.push_back(Rel.second); + } + Stream.EmitRecordWithAbbrev(AbbrevCode, Record); + } + Stream.ExitBlock(); +} + +IndexRecordWriter::IndexRecordWriter(StringRef IndexPath) + : RecordsPath(IndexPath) { + store::appendRecordSubDir(RecordsPath); +} + +IndexRecordWriter::Result +IndexRecordWriter::beginRecord(StringRef Filename, uint64_t RecordHash, + std::string &Error, std::string *OutRecordFile) { + using namespace llvm::sys; + assert(!Record && "called beginRecord before calling endRecord on previous"); + + std::string RecordName; + { + llvm::raw_string_ostream RN(RecordName); + RN << path::filename(Filename); + RN << "-" << toString(APInt(64, RecordHash), 36, /*Signed=*/false); + } + SmallString<256> RecordPath = RecordsPath.str(); + appendInteriorRecordPath(RecordName, RecordPath); + + if (OutRecordFile) + *OutRecordFile = RecordName; + + if (std::error_code EC = + fs::access(RecordPath.c_str(), fs::AccessMode::Exist)) { + if (EC != errc::no_such_file_or_directory) { + llvm::raw_string_ostream Err(Error); + Err << "could not access record '" << RecordPath + << "': " << EC.message(); + return Result::Failure; + } + } else { + return Result::AlreadyExists; + } + + // Write the record header. + auto *State = new RecordState(std::string(RecordPath.str())); + Record = State; + llvm::BitstreamWriter &Stream = State->Stream; + Stream.Emit('I', 8); + Stream.Emit('D', 8); + Stream.Emit('X', 8); + Stream.Emit('R', 8); + + writeBlockInfo(Stream); + writeVersionInfo(Stream); + + return Result::Success; +} + +IndexRecordWriter::Result +IndexRecordWriter::endRecord(std::string &Error, + writer::SymbolWriterCallback GetSymbolForDecl) { + assert(Record && "called endRecord without calling beginRecord"); + auto &State = *static_cast(Record); + Record = nullptr; + struct ScopedDelete { + RecordState *S; + ScopedDelete(RecordState *S) : S(S) {} + ~ScopedDelete() { delete S; } + } Deleter(&State); + + if (!State.Decls.empty()) { + writeDecls(State.Stream, State.Decls, State.Occurrences, GetSymbolForDecl); + } + + if (std::error_code EC = sys::fs::create_directory(sys::path::parent_path(State.RecordPath))) { + llvm::raw_string_ostream Err(Error); + Err << "failed to create directory '" << sys::path::parent_path(State.RecordPath) << "': " << EC.message(); + return Result::Failure; + } + + // Create a unique file to write to so that we can move the result into place + // atomically. If this process crashes we don't want to interfere with any + // other concurrent processes. + SmallString<128> TempPath(State.RecordPath); + TempPath += "-temp-%%%%%%%%"; + int TempFD; + if (sys::fs::createUniqueFile(TempPath.str(), TempFD, TempPath)) { + llvm::raw_string_ostream Err(Error); + Err << "failed to create temporary file: " << TempPath; + return Result::Failure; + } + + raw_fd_ostream OS(TempFD, /*shouldClose=*/true); + OS.write(State.Buffer.data(), State.Buffer.size()); + OS.close(); + + if (OS.has_error()) { + llvm::raw_string_ostream Err(Error); + Err << "failed to write '" << TempPath << "': " << OS.error().message(); + OS.clear_error(); + return Result::Failure; + } + + // Atomically move the unique file into place. + if (std::error_code EC = + sys::fs::rename(TempPath.c_str(), State.RecordPath.c_str())) { + llvm::raw_string_ostream Err(Error); + Err << "failed to rename '" << TempPath << "' to '" << State.RecordPath << "': " << EC.message(); + return Result::Failure; + } + + return Result::Success; +} + +void IndexRecordWriter::addOccurrence( + OpaqueDecl D, SymbolRoleSet Roles, unsigned Line, unsigned Column, + ArrayRef Related) { + assert(Record && "called addOccurrence without calling beginRecord"); + auto &State = *static_cast(Record); + + auto insertDecl = [&](OpaqueDecl D, SymbolRoleSet Roles, + SymbolRoleSet RelatedRoles) -> unsigned { + auto Insert = + State.IndexForDecl.insert(std::make_pair(D, State.Decls.size())); + unsigned Index = Insert.first->second; + + if (Insert.second) { + State.Decls.push_back(DeclInfo{D, Roles, RelatedRoles}); + } else { + State.Decls[Index].Roles |= Roles; + State.Decls[Index].RelatedRoles |= RelatedRoles; + } + return Index + 1; + }; + + unsigned DeclID = insertDecl(D, Roles, SymbolRoleSet()); + + decltype(OccurrenceInfo::Related) RelatedDecls; + RelatedDecls.reserve(Related.size()); + for (auto &Rel : Related) { + unsigned ID = insertDecl(Rel.RelatedSymbol, SymbolRoleSet(), Rel.Roles); + RelatedDecls.emplace_back(Rel, ID); + } + + State.Occurrences.push_back( + OccurrenceInfo{DeclID, D, Roles, Line, Column, std::move(RelatedDecls)}); +} diff --git a/clang/lib/Index/IndexSymbol.cpp b/clang/lib/Index/IndexSymbol.cpp index 419ff79a5cbab..fd30480a3cf3d 100644 --- a/clang/lib/Index/IndexSymbol.cpp +++ b/clang/lib/Index/IndexSymbol.cpp @@ -534,6 +534,7 @@ StringRef index::getSymbolKindString(SymbolKind K) { case SymbolKind::ConversionFunction: return "conversion-func"; case SymbolKind::Parameter: return "param"; case SymbolKind::Using: return "using"; + case SymbolKind::CommentTag: return "comment-tag"; case SymbolKind::TemplateTypeParm: return "template-type-param"; case SymbolKind::TemplateTemplateParm: return "template-template-param"; case SymbolKind::NonTypeTemplateParm: return "non-type-template-param"; @@ -553,6 +554,23 @@ StringRef index::getSymbolSubKindString(SymbolSubKind K) { case SymbolSubKind::UsingTypename: return "using-typename"; case SymbolSubKind::UsingValue: return "using-value"; case SymbolSubKind::UsingEnum: return "using-enum"; + case SymbolSubKind::SwiftAccessorWillSet: return "acc-willset"; + case SymbolSubKind::SwiftAccessorDidSet: return "acc-didset"; + case SymbolSubKind::SwiftAccessorAddressor: return "acc-addr"; + case SymbolSubKind::SwiftAccessorMutableAddressor: return "acc-mutaddr"; + case SymbolSubKind::SwiftAccessorRead: return "acc-read"; + case SymbolSubKind::SwiftAccessorModify: return "acc-modify"; + case SymbolSubKind::SwiftAccessorInit: return "acc-init"; + case SymbolSubKind::SwiftExtensionOfStruct: return "ext-struct"; + case SymbolSubKind::SwiftExtensionOfClass: return "ext-class"; + case SymbolSubKind::SwiftExtensionOfEnum: return "ext-enum"; + case SymbolSubKind::SwiftExtensionOfProtocol: return "ext-protocol"; + case SymbolSubKind::SwiftPrefixOperator: return "prefix-operator"; + case SymbolSubKind::SwiftPostfixOperator: return "postfix-operator"; + case SymbolSubKind::SwiftInfixOperator: return "infix-operator"; + case SymbolSubKind::SwiftSubscript: return "subscript"; + case SymbolSubKind::SwiftAssociatedType: return "associated-type"; + case SymbolSubKind::SwiftGenericTypeParam: return "generic-type-param"; } llvm_unreachable("invalid symbol subkind"); } @@ -582,6 +600,7 @@ void index::applyForEachSymbolProperty(SymbolPropertySet Props, APPLY_FOR_PROPERTY(GKInspectable); APPLY_FOR_PROPERTY(Local); APPLY_FOR_PROPERTY(ProtocolInterface); + APPLY_FOR_PROPERTY(SwiftAsync); #undef APPLY_FOR_PROPERTY } @@ -603,6 +622,7 @@ void index::printSymbolProperties(SymbolPropertySet Props, raw_ostream &OS) { case SymbolProperty::GKInspectable: OS << "GKI"; break; case SymbolProperty::Local: OS << "local"; break; case SymbolProperty::ProtocolInterface: OS << "protocol"; break; + case SymbolProperty::SwiftAsync: OS << "swift_async"; break; } }); } diff --git a/clang/lib/Index/IndexUnitReader.cpp b/clang/lib/Index/IndexUnitReader.cpp new file mode 100644 index 0000000000000..a455f1e4977e9 --- /dev/null +++ b/clang/lib/Index/IndexUnitReader.cpp @@ -0,0 +1,545 @@ +//===--- IndexUnitReader.cpp - Index unit deserialization -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexUnitReader.h" +#include "IndexDataStoreUtils.h" +#include "BitstreamVisitor.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +namespace { + +typedef function_ref DependencyReceiver; +typedef function_ref IncludeReceiver; + +class IndexUnitReaderImpl { + sys::TimePoint<> ModTime; + std::unique_ptr MemBuf; + +public: + StringRef ProviderIdentifier; + StringRef ProviderVersion; + llvm::BitstreamCursor DependCursor; + llvm::BitstreamCursor IncludeCursor; + bool IsSystemUnit; + bool IsModuleUnit; + bool IsDebugCompilation; + std::string WorkingDir; + std::string OutputFile; + std::string SysrootPath; + StringRef ModuleName; + SmallString<128> MainFilePath; + StringRef Target; + std::vector Paths; + StringRef PathsBuffer; + const PathRemapper &Remapper; + + struct ModuleInfo { + unsigned NameOffset; + unsigned NameSize; + }; + std::vector Modules; + StringRef ModuleNamesBuffer; + + IndexUnitReaderImpl(const PathRemapper &remapper) : Remapper(remapper) {} + bool init(std::unique_ptr Buf, sys::TimePoint<> ModTime, + std::string &Error); + + StringRef getProviderIdentifier() const { return ProviderIdentifier; } + StringRef getProviderVersion() const { return ProviderVersion; } + + sys::TimePoint<> getModificationTime() const { return ModTime; } + StringRef getWorkingDirectory() const { return WorkingDir; } + StringRef getOutputFile() const { return OutputFile; } + StringRef getSysrootPath() const { return SysrootPath; } + StringRef getTarget() const { return Target; } + + StringRef getModuleName() const { return ModuleName; } + StringRef getMainFilePath() const { return MainFilePath.str(); } + bool hasMainFile() const { return !MainFilePath.empty(); } + bool isSystemUnit() const { return IsSystemUnit; } + bool isModuleUnit() const { return IsModuleUnit; } + bool isDebugCompilation() const { return IsDebugCompilation; } + + /// Unit dependencies are provided ahead of record ones, record ones + /// ahead of the file ones. + bool foreachDependency(DependencyReceiver Receiver); + + bool foreachInclude(IncludeReceiver Receiver); + + StringRef getPathFromBuffer(size_t Offset, size_t Size) { + return PathsBuffer.substr(Offset, Size); + } + + std::string getAndRemapPathFromBuffer(size_t Offset, size_t Size) { + return Remapper.remapPath(getPathFromBuffer(Offset, Size)); + } + + void constructFilePath(SmallVectorImpl &Path, int PathIndex); + + StringRef getModuleName(int ModuleIndex); +}; + +class IndexUnitBitstreamVisitor : public BitstreamVisitor { + IndexUnitReaderImpl &Reader; + size_t WorkDirOffset; + size_t WorkDirSize; + size_t OutputFileOffset; + size_t OutputFileSize; + size_t SysrootOffset; + size_t SysrootSize; + int MainPathIndex; + +public: + IndexUnitBitstreamVisitor(llvm::BitstreamCursor &Stream, + IndexUnitReaderImpl &Reader) + : BitstreamVisitor(Stream), Reader(Reader) {} + + StreamVisit visitBlock(unsigned ID) { + switch ((UnitBitBlock)ID) { + case UNIT_VERSION_BLOCK_ID: + case UNIT_INFO_BLOCK_ID: + case UNIT_PATHS_BLOCK_ID: + case UNIT_MODULES_BLOCK_ID: + return StreamVisit::Continue; + + case UNIT_DEPENDENCIES_BLOCK_ID: + Reader.DependCursor = Stream; + if (Reader.DependCursor.EnterSubBlock(ID)) { + *Error = "malformed unit dependencies block record"; + return StreamVisit::Abort; + } + if (llvm::Error Err = readBlockAbbrevs(Reader.DependCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } + return StreamVisit::Skip; + case UNIT_INCLUDES_BLOCK_ID: + Reader.IncludeCursor = Stream; + if (Reader.IncludeCursor.EnterSubBlock(ID)) { + *Error = "malformed unit includes block record"; + return StreamVisit::Abort; + } + if (llvm::Error Err = readBlockAbbrevs(Reader.IncludeCursor)) { + *Error = toString(std::move(Err)); + return StreamVisit::Abort; + } + return StreamVisit::Skip; + } + + // Some newly introduced block in a minor version update that we cannot + // handle. + return StreamVisit::Skip; + } + + StreamVisit visitRecord(unsigned BlockID, unsigned RecID, + RecordDataImpl &Record, StringRef Blob) { + switch (BlockID) { + case UNIT_VERSION_BLOCK_ID: { + unsigned StoreFormatVersion = Record[0]; + if (StoreFormatVersion != STORE_FORMAT_VERSION) { + llvm::raw_string_ostream OS(*Error); + OS << "Store format version mismatch: " << StoreFormatVersion; + OS << " , expected: " << STORE_FORMAT_VERSION; + return StreamVisit::Abort; + } + break; + } + + case UNIT_INFO_BLOCK_ID: { + assert(RecID == UNIT_INFO); + unsigned I = 0; + Reader.IsSystemUnit = Record[I++]; + + // Save these to lookup them up after we get the paths buffer. + WorkDirOffset = Record[I++]; + WorkDirSize = Record[I++]; + OutputFileOffset = Record[I++]; + OutputFileSize = Record[I++]; + SysrootOffset = Record[I++]; + SysrootSize = Record[I++]; + MainPathIndex = (int)Record[I++] - 1; + Reader.IsDebugCompilation = Record[I++]; + Reader.IsModuleUnit = Record[I++]; + + size_t moduleNameSize = Record[I++]; + size_t providerIdentifierSize = Record[I++]; + size_t providerVersionSize = Record[I++]; + I++; // Reserved for ProviderDataVersion. + Reader.ModuleName = Blob.substr(0, moduleNameSize); + Blob = Blob.drop_front(moduleNameSize); + Reader.ProviderIdentifier = Blob.substr(0, providerIdentifierSize); + Blob = Blob.drop_front(providerIdentifierSize); + Reader.ProviderVersion = Blob.substr(0, providerVersionSize); + Reader.Target = Blob.drop_front(providerVersionSize); + break; + } + + case UNIT_PATHS_BLOCK_ID: + switch (RecID) { + case UNIT_PATH: + { + unsigned I = 0; + UnitFilePathPrefixKind Kind = (UnitFilePathPrefixKind)Record[I++]; + size_t DirOffset = Record[I++]; + size_t DirSize = Record[I++]; + size_t FilenameOffset = Record[I++]; + size_t FilenameSize = Record[I++]; + + Reader.Paths.emplace_back(Kind, BitPathComponent(DirOffset, DirSize), + BitPathComponent(FilenameOffset, FilenameSize)); + } + break; + case UNIT_PATH_BUFFER: + Reader.PathsBuffer = Blob; + Reader.WorkingDir = Reader.getAndRemapPathFromBuffer(WorkDirOffset, WorkDirSize); + // We intentionally do -not- remap the output file and instead leave it + // in the canonical form. This is because the output file need not be an + // actual real file path and this allows clients to provide the same + // canonical output path e.g. in the explicit output files list. + Reader.OutputFile = Reader.getPathFromBuffer(OutputFileOffset, OutputFileSize).str(); + Reader.SysrootPath = Reader.getAndRemapPathFromBuffer(SysrootOffset, SysrootSize); + + // now we can populate the main file's path + Reader.constructFilePath(Reader.MainFilePath, MainPathIndex); + break; + default: + llvm_unreachable("shouldn't visit this record"); + } + break; + + case UNIT_MODULES_BLOCK_ID: + switch (RecID) { + case UNIT_MODULE: + { + unsigned I = 0; + unsigned NameOffset = Record[I++]; + unsigned NameSize = Record[I++]; + Reader.Modules.push_back({NameOffset, NameSize}); + } + break; + case UNIT_MODULE_BUFFER: + Reader.ModuleNamesBuffer = Blob; + break; + default: + llvm_unreachable("shouldn't visit this record"); + } + break; + + case UNIT_DEPENDENCIES_BLOCK_ID: + case UNIT_INCLUDES_BLOCK_ID: + llvm_unreachable("shouldn't visit this block'"); + } + return StreamVisit::Continue; + } +}; + +typedef std::function + BlockVisitorCallback; + +class IndexUnitBlockBitstreamVisitor : public BitstreamVisitor { + unsigned RecID; + BlockVisitorCallback Visit; + +public: + IndexUnitBlockBitstreamVisitor(unsigned RecID, + llvm::BitstreamCursor &BlockStream, + BlockVisitorCallback Visit) + : BitstreamVisitor(BlockStream), RecID(RecID), Visit(std::move(Visit)) {} + + StreamVisit visitRecord(unsigned BlockID, unsigned RecID, + RecordDataImpl &Record, StringRef Blob) { + if (RecID != this->RecID) + llvm_unreachable("shouldn't be called with this RecID"); + + if (Visit(Record, Blob)) + return StreamVisit::Continue; + return StreamVisit::Abort; + } +}; + +} // anonymous namespace + +bool IndexUnitReaderImpl::init(std::unique_ptr Buf, + sys::TimePoint<> ModTime, + std::string &Error) { + this->ModTime = ModTime; + this->MemBuf = std::move(Buf); + llvm::BitstreamCursor Stream(*MemBuf); + + if (Stream.AtEndOfStream()) { + Error = "empty file"; + return true; + } + + // Sniff for the signature. + for (unsigned char C : {'I', 'D', 'X', 'U'}) { + if (Expected Res = Stream.Read(8)) { + if (Res.get() == C) + continue; + } else { + Error = toString(Res.takeError()); + return true; + } + Error = "not a serialized index unit file"; + return true; + } + + IndexUnitBitstreamVisitor BitVisitor(Stream, *this); + return !BitVisitor.visit(Error); +} + +/// Unit dependencies are provided ahead of record ones, record ones +/// ahead of the file ones. +bool IndexUnitReaderImpl::foreachDependency(DependencyReceiver Receiver) { + store::SavedStreamPosition SavedDepPosition(DependCursor); + IndexUnitBlockBitstreamVisitor Visitor(UNIT_DEPENDENCY, DependCursor, + [&](RecordDataImpl& Record, StringRef Blob) { + unsigned I = 0; + UnitDependencyKind UnitDepKind = (UnitDependencyKind)Record[I++]; + bool IsSystem = Record[I++]; + int PathIndex = (int)Record[I++] - 1; + int ModuleIndex = (int)Record[I++] - 1; + I++; // Reserved field. + I++; // Reserved field. + StringRef Name = Blob; + + IndexUnitReader::DependencyKind DepKind; + switch (UnitDepKind) { + case UNIT_DEPEND_KIND_UNIT: + DepKind = IndexUnitReader::DependencyKind::Unit; break; + case UNIT_DEPEND_KIND_RECORD: + DepKind = IndexUnitReader::DependencyKind::Record; break; + case UNIT_DEPEND_KIND_FILE: + DepKind = IndexUnitReader::DependencyKind::File; break; + } + + SmallString<512> PathBuf; + this->constructFilePath(PathBuf, PathIndex); + StringRef ModuleName = this->getModuleName(ModuleIndex); + + return Receiver(IndexUnitReader::DependencyInfo{DepKind, IsSystem, Name, + PathBuf.str(), ModuleName}); + }); + + std::string Error; + return Visitor.visit(Error); +} + +bool IndexUnitReaderImpl::foreachInclude(IncludeReceiver Receiver) { + store::SavedStreamPosition SavedIncPosition(IncludeCursor); + IndexUnitBlockBitstreamVisitor Visitor(UNIT_INCLUDE, IncludeCursor, + [&](RecordDataImpl& Record, StringRef Blob) { + unsigned I = 0; + int SourcePathIndex = (int)Record[I++] - 1; + unsigned Line = Record[I++]; + int TargetPathIndex = (int)Record[I++] - 1; + + SmallString<512> SourceBuf, TargetBuf; + this->constructFilePath(SourceBuf, SourcePathIndex); + this->constructFilePath(TargetBuf, TargetPathIndex); + return Receiver(IndexUnitReader::IncludeInfo{SourceBuf.str(), Line, TargetBuf.str()}); + }); + + std::string Error; + return Visitor.visit(Error); +} + + +void IndexUnitReaderImpl::constructFilePath(SmallVectorImpl &PathBuf, + int PathIndex) { + + if (PathIndex < 0) return; + FileBitPath &Path = Paths[PathIndex]; + StringRef Prefix; + switch (Path.PrefixKind) { + case UNIT_PATH_PREFIX_NONE: + break; + case UNIT_PATH_PREFIX_WORKDIR: + Prefix = getWorkingDirectory(); + break; + case UNIT_PATH_PREFIX_SYSROOT: + Prefix = getSysrootPath(); + break; + } + PathBuf.append(Prefix.begin(), Prefix.end()); + sys::path::append(PathBuf, + getPathFromBuffer(Path.Dir.Offset, Path.Dir.Size), + getPathFromBuffer(Path.Filename.Offset, Path.Filename.Size)); + if (Path.PrefixKind == UNIT_PATH_PREFIX_NONE && !Remapper.empty()) + Remapper.remapPath(PathBuf); +} + +StringRef IndexUnitReaderImpl::getModuleName(int ModuleIndex) { + if (ModuleIndex < 0 || ModuleNamesBuffer.empty()) + return StringRef(); + auto &ModInfo = Modules[ModuleIndex]; + return ModuleNamesBuffer.substr(ModInfo.NameOffset, ModInfo.NameSize); +} + + +//===----------------------------------------------------------------------===// +// IndexUnitReader +//===----------------------------------------------------------------------===// + +std::unique_ptr +IndexUnitReader::createWithUnitFilename(StringRef UnitFilename, + StringRef StorePath, + const PathRemapper &Remapper, + std::string &Error) { + SmallString<128> PathBuf = StorePath; + appendUnitSubDir(PathBuf); + sys::path::append(PathBuf, UnitFilename); + return createWithFilePath(PathBuf.str(), Remapper, Error); +} + +std::unique_ptr +IndexUnitReader::createWithFilePath(StringRef FilePath, + const PathRemapper &Remapper, + std::string &Error) { + int FD; + std::error_code EC = sys::fs::openFileForRead(FilePath, FD); + if (EC) { + raw_string_ostream(Error) << "Failed opening '" << FilePath << "': " + << EC.message(); + return nullptr; + } + + assert(FD != -1); + struct AutoFDClose { + int FD; + AutoFDClose(int FD) : FD(FD) {} + ~AutoFDClose() { + llvm::sys::Process::SafelyCloseFileDescriptor(FD); + } + } AutoFDClose(FD); + + sys::fs::file_status FileStat; + EC = sys::fs::status(FD, FileStat); + if (EC) { + Error = EC.message(); + return nullptr; + } + + auto ErrOrBuf = MemoryBuffer::getOpenFile(sys::fs::convertFDToNativeFile(FD), + FilePath, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrOrBuf) { + raw_string_ostream(Error) << "Failed opening '" << FilePath << "': " + << ErrOrBuf.getError().message(); + return nullptr; + } + + auto Impl = std::make_unique(Remapper); + bool Err = Impl->init(std::move(*ErrOrBuf), FileStat.getLastModificationTime(), + Error); + if (Err) + return nullptr; + + std::unique_ptr Reader; + Reader.reset(new IndexUnitReader(Impl.release())); + return Reader; +} + +std::optional> +IndexUnitReader::getModificationTimeForUnit(StringRef UnitFilename, + StringRef StorePath, + std::string &Error) { + SmallString<128> PathBuf = StorePath; + appendUnitSubDir(PathBuf); + sys::path::append(PathBuf, UnitFilename); + + sys::fs::file_status FileStat; + std::error_code EC = sys::fs::status(PathBuf.str(), FileStat); + if (EC) { + Error = EC.message(); + return std::nullopt; + } + return FileStat.getLastModificationTime(); +} + +#define IMPL static_cast(Impl) + +IndexUnitReader::~IndexUnitReader() { + delete IMPL; +} + +StringRef IndexUnitReader::getProviderIdentifier() const { + return IMPL->getProviderIdentifier(); +} + +StringRef IndexUnitReader::getProviderVersion() const { + return IMPL->getProviderVersion(); +} + +llvm::sys::TimePoint<> IndexUnitReader::getModificationTime() const { + return IMPL->getModificationTime(); +} + +StringRef IndexUnitReader::getWorkingDirectory() const { + return IMPL->getWorkingDirectory(); +} + +StringRef IndexUnitReader::getOutputFile() const { + return IMPL->getOutputFile(); +} + +StringRef IndexUnitReader::getSysrootPath() const { + return IMPL->getSysrootPath(); +} + +StringRef IndexUnitReader::getMainFilePath() const { + return IMPL->getMainFilePath(); +} + +StringRef IndexUnitReader::getModuleName() const { + return IMPL->getModuleName(); +} + +StringRef IndexUnitReader::getTarget() const { + return IMPL->getTarget(); +} + +bool IndexUnitReader::hasMainFile() const { + return IMPL->hasMainFile(); +} + +bool IndexUnitReader::isSystemUnit() const { + return IMPL->isSystemUnit(); +} + +bool IndexUnitReader::isModuleUnit() const { + return IMPL->isModuleUnit(); +} + +bool IndexUnitReader::isDebugCompilation() const { + return IMPL->isDebugCompilation(); +} + +/// \c Index is the index in the \c getDependencies array. +/// Unit dependencies are provided ahead of record ones. +bool IndexUnitReader::foreachDependency(DependencyReceiver Receiver) { + return IMPL->foreachDependency(std::move(Receiver)); +} + +bool IndexUnitReader::foreachInclude(IncludeReceiver Receiver) { + return IMPL->foreachInclude(std::move(Receiver)); +} diff --git a/clang/lib/Index/IndexUnitWriter.cpp b/clang/lib/Index/IndexUnitWriter.cpp new file mode 100644 index 0000000000000..f5a1855b26b76 --- /dev/null +++ b/clang/lib/Index/IndexUnitWriter.cpp @@ -0,0 +1,667 @@ +//===--- IndexUnitWriter.cpp - Index unit serialization -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexUnitWriter.h" +#include "IndexDataStoreUtils.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/PathRemapper.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Bitstream/BitstreamWriter.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/xxhash.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +class IndexUnitWriter::PathStorage { + FileManager &FileMgr; + std::string WorkDir; + std::string SysrootPath; + SmallString<512> PathsBuf; + StringMap Dirs; + std::vector FileBitPaths; + DenseMap FileToIndex; + const PathRemapper &Remapper; + +public: + PathStorage(FileManager &fileMgr, StringRef workDir, StringRef sysrootPath, + const PathRemapper &remapper) : FileMgr(fileMgr), Remapper(remapper) { + WorkDir = std::string(workDir); + if (sysrootPath == "/") + sysrootPath = StringRef(); + SysrootPath = std::string(sysrootPath); + } + + StringRef getPathsBuffer() const { return PathsBuf.str(); } + + ArrayRef getBitPaths() const { return FileBitPaths; } + + int getPathIndex(OptionalFileEntryRef FE) { + if (!FE) + return -1; + auto Pair = FileToIndex.insert(std::make_pair(*FE, FileBitPaths.size())); + bool IsNew = Pair.second; + size_t Index = Pair.first->getSecond(); + + if (IsNew) { + StringRef Filename = sys::path::filename(FE->getName()); + SmallString<256> AbsDirPath(sys::path::parent_path(FE->getName())); + FileMgr.makeAbsolutePath(AbsDirPath); + DirBitPath Dir = getDirBitPath(AbsDirPath.str()); + FileBitPaths.emplace_back(Dir.PrefixKind, Dir.Dir, + BitPathComponent(getPathOffset(Filename), + Filename.size())); + } + return Index; + } + + size_t getPathOffset(StringRef Path) { + if (Path.empty()) + return 0; + size_t offset = PathsBuf.size(); + PathsBuf += Path; + return offset; + } + +private: + DirBitPath getDirBitPath(StringRef dirStr) { + auto pair = Dirs.insert(std::make_pair(dirStr, DirBitPath())); + bool isNew = pair.second; + auto &dirPath = pair.first->second; + + if (isNew) { + if (isPathInDir(SysrootPath, dirStr)) { + dirPath.PrefixKind = UNIT_PATH_PREFIX_SYSROOT; + dirStr = dirStr.drop_front(SysrootPath.size()); + while (!dirStr.empty() && dirStr[0] == '/') + dirStr = dirStr.drop_front(); + } else if (isPathInDir(WorkDir, dirStr)) { + dirPath.PrefixKind = UNIT_PATH_PREFIX_WORKDIR; + dirStr = dirStr.drop_front(WorkDir.size()); + while (!dirStr.empty() && dirStr[0] == '/') + dirStr = dirStr.drop_front(); + } + + if (dirPath.PrefixKind != UNIT_PATH_PREFIX_NONE) { + // No need to remap since it's already relative to another directory. + dirPath.Dir.Offset = getPathOffset(dirStr); + dirPath.Dir.Size = dirStr.size(); + } else { // Remap the path before storing. + std::string Remapped = Remapper.remapPath(dirStr); + dirPath.Dir.Offset = getPathOffset(Remapped); + dirPath.Dir.Size = Remapped.size(); + } + } + return dirPath; + } + + static bool isPathInDir(StringRef dir, StringRef path) { + if (dir.empty() || !path.starts_with(dir)) + return false; + StringRef rest = path.drop_front(dir.size()); + return !rest.empty() && sys::path::is_separator(rest.front()); + } +}; + +IndexUnitWriter::IndexUnitWriter(FileManager &FileMgr, + StringRef StorePath, + StringRef ProviderIdentifier, + StringRef ProviderVersion, + StringRef OutputFile, + StringRef ModuleName, + OptionalFileEntryRef MainFile, + bool IsSystem, + bool IsModuleUnit, + bool IsDebugCompilation, + StringRef TargetTriple, + StringRef SysrootPath, + const PathRemapper &Remapper, + writer::ModuleInfoWriterCallback GetInfoForModule) +: FileMgr(FileMgr), Remapper(Remapper) { + this->UnitsPath = StorePath; + store::appendUnitSubDir(this->UnitsPath); + this->ProviderIdentifier = std::string(ProviderIdentifier); + this->ProviderVersion = std::string(ProviderVersion); + SmallString<256> AbsOutputFile(OutputFile); + if (OutputFile != "-") { + // Can't make stdout absolute, should stay as "-". + FileMgr.makeAbsolutePath(AbsOutputFile); + llvm::sys::path::native(AbsOutputFile); + } + + this->OutputFile = std::string(AbsOutputFile.str()); + this->ModuleName = std::string(ModuleName); + this->MainFile = MainFile; + this->IsSystemUnit = IsSystem; + this->IsModuleUnit = IsModuleUnit; + this->IsDebugCompilation = IsDebugCompilation; + this->TargetTriple = std::string(TargetTriple); + SmallString<256> AbsSysroot(SysrootPath); + FileMgr.makeAbsolutePath(AbsSysroot); + this->SysrootPath = std::string(AbsSysroot.str()); + this->GetInfoForModuleFn = GetInfoForModule; +} + +IndexUnitWriter::~IndexUnitWriter() {} + +int IndexUnitWriter::addModule(writer::OpaqueModule Mod) { + if (!Mod) + return -1; + + auto Pair = IndexByModule.insert(std::make_pair(Mod, Modules.size())); + bool WasInserted = Pair.second; + if (WasInserted) { + Modules.push_back(Mod); + } + return Pair.first->second; +} + +int IndexUnitWriter::addFileDependency(OptionalFileEntryRef File, bool IsSystem, + writer::OpaqueModule Mod) { + assert(File); + auto Pair = IndexByFile.insert(std::make_pair(*File, Files.size())); + bool WasInserted = Pair.second; + if (WasInserted) { + Files.push_back(FileEntryData{*File, IsSystem, addModule(Mod), {}}); + } + return Pair.first->second; +} + +void IndexUnitWriter::addRecordFile(StringRef RecordFile, + OptionalFileEntryRef File, bool IsSystem, + writer::OpaqueModule Mod) { + int Dep = File ? addFileDependency(File, IsSystem, /*module=*/nullptr) : -1; + Records.push_back(RecordOrUnitData{std::string(RecordFile), Dep, addModule(Mod), IsSystem}); +} + +void IndexUnitWriter::addASTFileDependency(OptionalFileEntryRef File, + bool IsSystem, + writer::OpaqueModule Mod, + bool withoutUnitName) { + assert(File); + if (!SeenASTFiles.insert(*File).second) + return; + + SmallString<64> UnitName; + if (!withoutUnitName) + getUnitNameForOutputFile(File->getName(), UnitName); + addUnitDependency(UnitName.str(), File, IsSystem, Mod); +} + +void IndexUnitWriter::addUnitDependency(StringRef UnitFile, + OptionalFileEntryRef File, + bool IsSystem, + writer::OpaqueModule Mod) { + int Dep = File ? addFileDependency(File, IsSystem, /*module=*/nullptr) : -1; + ASTFileUnits.emplace_back(RecordOrUnitData{std::string(UnitFile), Dep, addModule(Mod), IsSystem}); +} + +bool IndexUnitWriter::addInclude(const FileEntry *Source, unsigned Line, + const FileEntry *Target) { + // FIXME: This will ignore includes of headers that resolve to module imports + // because the 'target' header has not been added as a file dependency earlier + // so it is missing from \c IndexByFile. + + auto It = IndexByFile.find(Source); + if (It == IndexByFile.end()) + return false; + int SourceIndex = It->getSecond(); + It = IndexByFile.find(Target); + if (It == IndexByFile.end()) + return false; + int TargetIndex = It->getSecond(); + Files[SourceIndex].Includes.emplace_back(FileInclude{TargetIndex, Line}); + return true; +} + +void IndexUnitWriter::getUnitNameForOutputFile(StringRef FilePath, + SmallVectorImpl &Str) { + SmallString<256> AbsPath(FilePath); + FileMgr.makeAbsolutePath(AbsPath); + llvm::sys::path::native(AbsPath); + return getUnitNameForAbsoluteOutputFile(AbsPath, Str, Remapper); +} + +void IndexUnitWriter::getUnitPathForOutputFile(StringRef FilePath, + SmallVectorImpl &Str) { + Str.append(UnitsPath.begin(), UnitsPath.end()); + Str.push_back(llvm::sys::path::get_separator().front()); + return getUnitNameForOutputFile(FilePath, Str); +} + +std::optional IndexUnitWriter::isUnitUpToDateForOutputFile( + StringRef FilePath, std::optional TimeCompareFilePath, + std::string &Error) { + SmallString<256> UnitPath; + getUnitPathForOutputFile(FilePath, UnitPath); + + llvm::sys::fs::file_status UnitStat; + if (std::error_code EC = llvm::sys::fs::status(UnitPath.c_str(), UnitStat)) { + if (EC != llvm::errc::no_such_file_or_directory && EC != llvm::errc::delete_pending) { + llvm::raw_string_ostream Err(Error); + Err << "could not access path '" << UnitPath + << "': " << EC.message(); + return std::nullopt; + } + return false; + } + + if (!TimeCompareFilePath) + return true; + + llvm::sys::fs::file_status CompareStat; + if (std::error_code EC = llvm::sys::fs::status(*TimeCompareFilePath, CompareStat)) { + if (EC != llvm::errc::no_such_file_or_directory && EC != llvm::errc::delete_pending) { + llvm::raw_string_ostream Err(Error); + Err << "could not access path '" << *TimeCompareFilePath + << "': " << EC.message(); + return std::nullopt; + } + return true; + } + + // Return true (unit is up-to-date) if the file to compare is older than the + // unit file. + return CompareStat.getLastModificationTime() <= UnitStat.getLastModificationTime(); +} + +void IndexUnitWriter::getUnitNameForAbsoluteOutputFile(StringRef FilePath, + SmallVectorImpl &Str, + const PathRemapper &Remapper) { + StringRef Fname = sys::path::filename(FilePath); + Str.append(Fname.begin(), Fname.end()); + Str.push_back('-'); + // Need to be sure we use the remapped path to keep things hermetic. + std::string RemappedPath = Remapper.remapPath(FilePath); + auto PathHashVal = llvm::xxh3_64bits(RemappedPath); + llvm::APInt(64, PathHashVal).toStringUnsigned(Str, /*Radix*/ 36); +} + +static void writeBlockInfo(BitstreamWriter &Stream) { + RecordData Record; + + Stream.EnterBlockInfoBlock(); +#define BLOCK(X) emitBlockID(X ## _ID, #X, Stream, Record) +#define RECORD(X) emitRecordID(X, #X, Stream, Record) + + BLOCK(UNIT_VERSION_BLOCK); + RECORD(UNIT_VERSION); + + BLOCK(UNIT_INFO_BLOCK); + RECORD(UNIT_INFO); + + BLOCK(UNIT_DEPENDENCIES_BLOCK); + RECORD(UNIT_DEPENDENCY); + + BLOCK(UNIT_INCLUDES_BLOCK); + RECORD(UNIT_INCLUDE); + + BLOCK(UNIT_PATHS_BLOCK); + RECORD(UNIT_PATH); + RECORD(UNIT_PATH_BUFFER); + + BLOCK(UNIT_MODULES_BLOCK); + RECORD(UNIT_MODULE); + RECORD(UNIT_MODULE_BUFFER); + +#undef RECORD +#undef BLOCK + Stream.ExitBlock(); +} + +static void writeVersionInfo(BitstreamWriter &Stream) { + using namespace llvm::sys; + + Stream.EnterSubblock(UNIT_VERSION_BLOCK_ID, 3); + + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(UNIT_VERSION)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Store format version + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + RecordData Record; + Record.push_back(UNIT_VERSION); + Record.push_back(STORE_FORMAT_VERSION); + Stream.EmitRecordWithAbbrev(AbbrevCode, Record); + + Stream.ExitBlock(); +} + +bool IndexUnitWriter::write(std::string &Error) { + using namespace llvm::sys; + + // Determine the working directory. + SmallString<128> CWDPath; + if (!FileMgr.getFileSystemOpts().WorkingDir.empty()) { + CWDPath = FileMgr.getFileSystemOpts().WorkingDir; + if (!path::is_absolute(CWDPath)) { + fs::make_absolute(CWDPath); + } + } else { + std::error_code EC = sys::fs::current_path(CWDPath); + if (EC) { + llvm::raw_string_ostream Err(Error); + Err << "failed to determine current working directory: " << EC.message(); + return true; + } + } + WorkDir = std::string(CWDPath.str()); + + SmallString<512> Buffer; + BitstreamWriter Stream(Buffer); + Stream.Emit('I', 8); + Stream.Emit('D', 8); + Stream.Emit('X', 8); + Stream.Emit('U', 8); + + PathStorage PathStore(FileMgr, WorkDir, SysrootPath, Remapper); + + writeBlockInfo(Stream); + writeVersionInfo(Stream); + writeUnitInfo(Stream, PathStore); + writeDependencies(Stream, PathStore); + writeIncludes(Stream, PathStore); + writePaths(Stream, PathStore); + writeModules(Stream); + + SmallString<256> UnitPath; + getUnitPathForOutputFile(OutputFile, UnitPath); + + SmallString<128> TempPath; + TempPath = path::parent_path(UnitsPath); + TempPath += '/'; + TempPath += path::filename(UnitPath); + TempPath += "-%%%%%%%%"; + int TempFD; + if (llvm::sys::fs::createUniqueFile(TempPath.str(), TempFD, TempPath)) { + llvm::raw_string_ostream Err(Error); + Err << "failed to create temporary file: " << TempPath; + return true; + } + + raw_fd_ostream OS(TempFD, /*shouldClose=*/true); + OS.write(Buffer.data(), Buffer.size()); + OS.close(); + + if (OS.has_error()) { + llvm::raw_string_ostream Err(Error); + Err << "failed to write '" << TempPath << "': " << OS.error().message(); + OS.clear_error(); + return true; + } + + std::error_code EC = fs::rename(/*from=*/TempPath.c_str(), /*to=*/UnitPath.c_str()); + if (EC) { + llvm::raw_string_ostream Err(Error); + Err << "failed to rename '" << TempPath << "' to '" << UnitPath << "': " << EC.message(); + return true; + } + + return false; +} + +void IndexUnitWriter::writeUnitInfo(llvm::BitstreamWriter &Stream, + PathStorage &PathStore) { + Stream.EnterSubblock(UNIT_INFO_BLOCK_ID, 3); + + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(UNIT_INFO)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystemUnit + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // WorkDir offset + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // WorkDir size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // OutputFile offset + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // OutputFile size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // Sysroot offset + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Sysroot size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // Main path id + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsDebugCompilation + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsModuleUnit + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // Module name size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // ProviderIdentifier size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // ProviderVersion size + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // ProviderDataVersion + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Module name + ProviderIdentifier + ProviderVersion + target triple + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + RecordData Record; + Record.push_back(UNIT_INFO); + Record.push_back(IsSystemUnit); + std::string RemappedWorkDir = Remapper.remapPath(WorkDir); + Record.push_back(PathStore.getPathOffset(RemappedWorkDir)); + Record.push_back(RemappedWorkDir.size()); + std::string RemappedOutputFile = Remapper.remapPath(OutputFile); + Record.push_back(PathStore.getPathOffset(RemappedOutputFile)); + Record.push_back(RemappedOutputFile.size()); + std::string RemappedSysrootPath = Remapper.remapPath(SysrootPath); + Record.push_back(PathStore.getPathOffset(RemappedSysrootPath)); + Record.push_back(RemappedSysrootPath.size()); + Record.push_back(PathStore.getPathIndex(MainFile) + 1); // Make 1-based with 0=invalid + Record.push_back(IsDebugCompilation); + Record.push_back(IsModuleUnit); + Record.push_back(ModuleName.size()); + Record.push_back(ProviderIdentifier.size()); + Record.push_back(ProviderVersion.size()); + // ProviderDataVersion is reserved. Not sure it is a good to idea to have + // clients consider the specifics of a 'provider data version', but reserving + // to avoid store format version change in case there is a use case in the + // future. + Record.push_back(0); // ProviderDataVersion + SmallString<128> InfoStrings; + InfoStrings += ModuleName; + InfoStrings += ProviderIdentifier; + InfoStrings += ProviderVersion; + InfoStrings += TargetTriple; + Stream.EmitRecordWithBlob(AbbrevCode, Record, InfoStrings); + + Stream.ExitBlock(); +} + +void IndexUnitWriter::writeDependencies(llvm::BitstreamWriter &Stream, + PathStorage &PathStore) { + std::vector FileUsedForRecordOrUnit; + FileUsedForRecordOrUnit.resize(Files.size()); + + Stream.EnterSubblock(UNIT_DEPENDENCIES_BLOCK_ID, 3); + + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(UNIT_DEPENDENCY)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, UnitDependencyKindBitNum)); // Dependency kind + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystem + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // PathIndex (1-based, 0 = none) + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // ModuleIndex (1-based, 0 = none) + // Reserved. These used to be time_t & file size but we decided against + // writing these in order to get reproducible build products (index data + // output being the same with the same inputs). Keep these reserved for the + // future, for coming up with a better scheme to track state of dependencies + // without using modification time. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 0)); // Reserved + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 0)); // Reserved + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + RecordData Record; + + auto addRecordOrUnitData = [&](UnitDependencyKind K, const RecordOrUnitData &Data) { + Record.push_back(UNIT_DEPENDENCY); + Record.push_back(K); + Record.push_back(Data.IsSystem); + if (Data.FileIndex != -1) { + Record.push_back(PathStore.getPathIndex(Files[Data.FileIndex].File) + 1); + FileUsedForRecordOrUnit[Data.FileIndex] = true; + } else { + Record.push_back(0); + } + if (Data.ModuleIndex != -1) { + Record.push_back(Data.ModuleIndex + 1); + } else { + Record.push_back(0); + } + Record.push_back(0); // Reserved. + Record.push_back(0); // Reserved. + Stream.EmitRecordWithBlob(AbbrevCode, Record, Data.Name); + }; + + for (auto &ASTData : ASTFileUnits) { + Record.clear(); + addRecordOrUnitData(UNIT_DEPEND_KIND_UNIT, ASTData); + } + for (auto &recordData : Records) { + Record.clear(); + addRecordOrUnitData(UNIT_DEPEND_KIND_RECORD, recordData); + } + size_t FileIndex = 0; + for (auto &File : Files) { + if (FileUsedForRecordOrUnit[FileIndex++]) + continue; + Record.clear(); + Record.push_back(UNIT_DEPENDENCY); + Record.push_back(UNIT_DEPEND_KIND_FILE); + Record.push_back(File.IsSystem); + Record.push_back(PathStore.getPathIndex(File.File) + 1); + if (File.ModuleIndex != -1) { + Record.push_back(File.ModuleIndex + 1); + } else { + Record.push_back(0); + } + Record.push_back(0); // Reserved. + Record.push_back(0); // Reserved. + Stream.EmitRecordWithBlob(AbbrevCode, Record, StringRef()); + } + + Stream.ExitBlock(); +} + +void IndexUnitWriter::writeIncludes(llvm::BitstreamWriter &Stream, + PathStorage &PathStore) { + Stream.EnterSubblock(UNIT_INCLUDES_BLOCK_ID, 3); + + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(UNIT_INCLUDE)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // source path index (1-based, 0 = no path) + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 12)); // source include line + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // target path index (1-based, 0 = no path) + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + RecordData Record; + + for (auto &Including : Files) { + for(auto &Included: Including.Includes) { + Record.clear(); + Record.push_back(UNIT_INCLUDE); + Record.push_back(PathStore.getPathIndex(Including.File) + 1); + Record.push_back(Included.Line); + Record.push_back(PathStore.getPathIndex(Files[Included.Index].File) + 1); + Stream.EmitRecordWithAbbrev(AbbrevCode, Record); + } + } + Stream.ExitBlock(); +} + +void IndexUnitWriter::writePaths(llvm::BitstreamWriter &Stream, + PathStorage &PathStore) { + Stream.EnterSubblock(UNIT_PATHS_BLOCK_ID, 3); + + auto PathAbbrev = std::make_shared(); + PathAbbrev->Add(BitCodeAbbrevOp(UNIT_PATH)); + PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, UnitFilePathPrefixKindBitNum)); // Path prefix kind + PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // DirPath offset + PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // DirPath size + PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // Filename offset + PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Filename size + unsigned PathAbbrevCode = Stream.EmitAbbrev(std::move(PathAbbrev)); + + auto PathBufferAbbrev = std::make_shared(); + PathBufferAbbrev->Add(BitCodeAbbrevOp(UNIT_PATH_BUFFER)); + PathBufferAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Paths buffer + unsigned PathBufferAbbrevCode = Stream.EmitAbbrev(PathBufferAbbrev); + + RecordData Record; + for(auto &BitPath: PathStore.getBitPaths()) { + Record.push_back(UNIT_PATH); + Record.push_back(BitPath.PrefixKind); + Record.push_back(BitPath.Dir.Offset); + Record.push_back(BitPath.Dir.Size); + Record.push_back(BitPath.Filename.Offset); + Record.push_back(BitPath.Filename.Size); + Stream.EmitRecordWithAbbrev(PathAbbrevCode, Record); + Record.clear(); + } + + Record.push_back(UNIT_PATH_BUFFER); + Stream.EmitRecordWithBlob(PathBufferAbbrevCode, Record, PathStore.getPathsBuffer()); + + Stream.ExitBlock(); +} + +void IndexUnitWriter::writeModules(llvm::BitstreamWriter &Stream) { + Stream.EnterSubblock(UNIT_MODULES_BLOCK_ID, 3); + + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(UNIT_MODULE)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 9)); // Module name offset + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Module name size + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + + auto BufferAbbrev = std::make_shared(); + BufferAbbrev->Add(BitCodeAbbrevOp(UNIT_MODULE_BUFFER)); + BufferAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Module names buffer + unsigned BufferAbbrevCode = Stream.EmitAbbrev(BufferAbbrev); + + SmallString<512> ModuleNamesBuf; + + RecordData Record; + for (auto &Mod : Modules) { + SmallString<64> ModuleName; + StringRef name = GetInfoForModuleFn(Mod, ModuleName).Name; + size_t offset = ModuleNamesBuf.size(); + ModuleNamesBuf += name; + + Record.push_back(UNIT_MODULE); + Record.push_back(offset); + Record.push_back(name.size()); + Stream.EmitRecordWithAbbrev(AbbrevCode, Record); + Record.clear(); + } + + Record.push_back(UNIT_MODULE_BUFFER); + Stream.EmitRecordWithBlob(BufferAbbrevCode, Record, ModuleNamesBuf.str()); + + Stream.ExitBlock(); +} + +bool IndexUnitWriter::initIndexDirectory(StringRef StorePath, + std::string &Error) { + using namespace llvm::sys; + SmallString<128> SubPath = StorePath; + store::appendRecordSubDir(SubPath); + std::error_code EC = fs::create_directories(SubPath); + if (EC) { + llvm::raw_string_ostream Err(Error); + Err << "failed to create directory '" << SubPath << "': " << EC.message(); + return true; + } + + SubPath = StorePath; + store::appendUnitSubDir(SubPath); + EC = fs::create_directory(SubPath); + if (EC) { + llvm::raw_string_ostream Err(Error); + Err << "failed to create directory '" << SubPath << "': " << EC.message(); + return true; + } + + return false; +} diff --git a/clang/lib/Index/IndexingAction.cpp b/clang/lib/Index/IndexingAction.cpp index 81c46a0d08de6..36650ad7d7f78 100644 --- a/clang/lib/Index/IndexingAction.cpp +++ b/clang/lib/Index/IndexingAction.cpp @@ -7,11 +7,18 @@ //===----------------------------------------------------------------------===// #include "clang/Index/IndexingAction.h" +#include "ClangIndexRecordWriter.h" +#include "FileIndexRecord.h" +#include "IndexDataStoreUtils.h" #include "IndexingContext.h" +#include "clang/Basic/PathRemapper.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Frontend/Utils.h" #include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexUnitWriter.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" #include "clang/Serialization/ASTReader.h" @@ -310,3 +317,747 @@ void index::indexModuleFile(serialization::ModuleFile &Mod, ASTReader &Reader, } DataConsumer.finish(); } + +//===----------------------------------------------------------------------===// +// Index Data Recording +//===----------------------------------------------------------------------===// + +namespace { + +class IndexDataRecorder : public IndexDataConsumer { + IndexingContext *IndexCtx = nullptr; + const Preprocessor *PP = nullptr; + typedef llvm::DenseMap> + RecordByFileTy; + RecordByFileTy RecordByFile; + +public: + void init(IndexingContext *idxCtx, const CompilerInstance &CI) { + IndexCtx = idxCtx; + PP = &CI.getPreprocessor(); + initialize(CI.getASTContext()); + } + + RecordByFileTy::const_iterator record_begin() const { + return RecordByFile.begin(); + } + RecordByFileTy::const_iterator record_end() const { + return RecordByFile.end(); + } + bool record_empty() const { return RecordByFile.empty(); } + +private: + bool handleDeclOccurrence(const Decl *D, SymbolRoleSet Roles, + ArrayRef Relations, + SourceLocation Loc, ASTNodeInfo ASTNode) override { + FileID FID; + unsigned Offset; + if (!getFileIDAndOffset(Loc, FID, Offset)) + return true; + + FileIndexRecord &Rec = getFileIndexRecord(FID); + Rec.addDeclOccurence(Roles, Offset, D, Relations); + return true; + } + + bool handleMacroOccurrence(const IdentifierInfo *Name, const MacroInfo *MI, + SymbolRoleSet Roles, SourceLocation Loc) override { + FileID FID; + unsigned Offset; + if (!getFileIDAndOffset(Loc, FID, Offset)) + return true; + + FileIndexRecord &Rec = getFileIndexRecord(FID); + Rec.addMacroOccurence(Roles, Offset, Name, MI); + return true; + } + + FileIndexRecord &getFileIndexRecord(FileID FID) { + auto &Entry = RecordByFile[FID]; + if (!Entry) { + Entry.reset(new FileIndexRecord(FID, IndexCtx->isSystemFile(FID))); + } + return *Entry; + } + + bool getFileIDAndOffset(SourceLocation Loc, FileID &FID, unsigned &Offset) { + SourceManager &SM = PP->getSourceManager(); + Loc = SM.getFileLoc(Loc); + if (Loc.isInvalid()) + return false; + + std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); + + if (FID.isInvalid()) + return false; + + // Ignore the predefines buffer. + const FileEntry *FE = PP->getSourceManager().getFileEntryForID(FID); + return FE != nullptr; + } + +public: + void finish() override { + if (IndexCtx->getIndexOpts().IndexMacros) { + SmallVector ToRemove; + for (auto &pair : RecordByFile) { + pair.second->removeHeaderGuardMacros(); + // Remove now-empty records. + if (pair.second->getDeclOccurrencesSortedByOffset().empty()) + ToRemove.push_back(pair.first); + } + for (auto FID : ToRemove) { + RecordByFile.erase(FID); + } + } + } +}; + +struct IncludeLocation { + const FileEntry *Source; + const FileEntry *Target; + unsigned Line; +}; + +class IncludePPCallbacks : public PPCallbacks { + IndexingContext &IndexCtx; + RecordingOptions RecordOpts; + std::vector &Includes; + SourceManager &SourceMgr; + +public: + IncludePPCallbacks(IndexingContext &indexCtx, RecordingOptions recordOpts, + std::vector &IncludesForFile, + SourceManager &SourceMgr) + : IndexCtx(indexCtx), RecordOpts(recordOpts), Includes(IncludesForFile), + SourceMgr(SourceMgr) {} + +private: + void addInclude(SourceLocation From, const FileEntry *To) { + assert(To); + if (RecordOpts.RecordIncludes == + RecordingOptions::IncludesRecordingKind::None) + return; + + std::pair LocInfo = + SourceMgr.getDecomposedExpansionLoc(From); + switch (RecordOpts.RecordIncludes) { + case RecordingOptions::IncludesRecordingKind::None: + llvm_unreachable("should have already checked in the beginning"); + case RecordingOptions::IncludesRecordingKind::UserOnly: + if (IndexCtx.isSystemFile(LocInfo.first)) + return; // Ignore includes of system headers. + break; + case RecordingOptions::IncludesRecordingKind::All: + break; + } + auto *FE = SourceMgr.getFileEntryForID(LocInfo.first); + if (!FE) + return; + auto lineNo = SourceMgr.getLineNumber(LocInfo.first, LocInfo.second); + Includes.push_back({FE, To, lineNo}); + } + + virtual void + InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, OptionalFileEntryRef File, + StringRef SearchPath, StringRef RelativePath, + const Module *SuggestedModule, bool ModuleImported, + SrcMgr::CharacteristicKind FileType) override { + if (HashLoc.isFileID() && File) + addInclude(HashLoc, *File); + } +}; + +class IndexDependencyProvider { +public: + virtual ~IndexDependencyProvider() {} + + virtual void visitFileDependencies( + const CompilerInstance &CI, + llvm::function_ref visitor) = 0; + virtual void + visitIncludes(llvm::function_ref + visitor) = 0; + virtual void visitModuleImports( + const CompilerInstance &CI, + llvm::function_ref + visitor) = 0; +}; + +class SourceFilesIndexDependencyCollector : public DependencyCollector, + public IndexDependencyProvider { + IndexingContext &IndexCtx; + RecordingOptions RecordOpts; + llvm::SetVector Entries; + llvm::BitVector IsSystemByUID; + std::vector Includes; + SourceManager *SourceMgr = nullptr; + std::string SysrootPath; + +public: + SourceFilesIndexDependencyCollector(IndexingContext &indexCtx, + RecordingOptions recordOpts) + : IndexCtx(indexCtx), RecordOpts(recordOpts) {} + + virtual void attachToPreprocessor(Preprocessor &PP) override { + DependencyCollector::attachToPreprocessor(PP); + PP.addPPCallbacks(std::make_unique( + IndexCtx, RecordOpts, Includes, PP.getSourceManager())); + } + + void setSourceManager(SourceManager *SourceMgr) { + this->SourceMgr = SourceMgr; + } + void setSysrootPath(StringRef sysroot) { SysrootPath = std::string(sysroot); } + + void visitFileDependencies( + const CompilerInstance &CI, + llvm::function_ref visitor) + override { + for (FileEntryRef FE : getEntries()) { + visitor(FE, isSystemFile(FE)); + } + } + + void + visitIncludes(llvm::function_ref + visitor) override { + for (auto &Include : Includes) { + visitor(Include.Source, Include.Line, Include.Target); + } + } + + void visitModuleImports( + const CompilerInstance &CI, + llvm::function_ref + visitor) override { + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + + if (auto Reader = CI.getASTReader()) { + Reader->getModuleManager().visit( + [&](serialization::ModuleFile &Mod) -> bool { + bool isSystemMod = false; + if (Mod.isModule()) { + if (auto *M = HS.lookupModule(Mod.ModuleName, SourceLocation(), + /*AllowSearch=*/false)) + isSystemMod = M->IsSystem; + } + if (!isSystemMod || needSystemDependencies()) + visitor(Mod, isSystemMod); + return true; // skip module dependencies. + }); + } + } + +private: + bool isSystemFile(const FileEntry *FE) { + auto UID = FE->getUID(); + return IsSystemByUID.size() > UID && IsSystemByUID[UID]; + } + + ArrayRef getEntries() const { + return Entries.getArrayRef(); + } + + bool needSystemDependencies() override { + return RecordOpts.RecordSystemDependencies; + } + + bool sawDependency(StringRef Filename, bool FromModule, bool IsSystem, + bool IsModuleFile, bool IsMissing) override { + bool sawIt = DependencyCollector::sawDependency( + Filename, FromModule, IsSystem, IsModuleFile, IsMissing); + if (auto FE = SourceMgr->getFileManager().getOptionalFileRef(Filename)) { + if (sawIt) + Entries.insert(*FE); + // Record system-ness for all files that we pass through. + if (IsSystemByUID.size() < FE->getUID() + 1) + IsSystemByUID.resize(FE->getUID() + 1); + IsSystemByUID[FE->getUID()] = IsSystem || isInSysroot(Filename); + } + return sawIt; + } + + bool isInSysroot(StringRef Filename) { + return !SysrootPath.empty() && Filename.starts_with(SysrootPath); + } +}; + + +class IndexRecordASTConsumer : public ASTConsumer { + std::shared_ptr PP; + std::shared_ptr IndexCtx; + +public: + IndexRecordASTConsumer(std::shared_ptr PP, + std::shared_ptr IndexCtx) + : PP(std::move(PP)), IndexCtx(std::move(IndexCtx)) {} + +protected: + void Initialize(ASTContext &Context) override { + IndexCtx->setASTContext(Context); + IndexCtx->getDataConsumer().initialize(Context); + IndexCtx->getDataConsumer().setPreprocessor(PP); + } + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + return IndexCtx->indexDeclGroupRef(DG); + } + + void HandleInterestingDecl(DeclGroupRef DG) override { + // Ignore deserialized decls. + } + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { + IndexCtx->indexDeclGroupRef(DG); + } + + void HandleTranslationUnit(ASTContext &Ctx) override {} +}; + +class IndexRecordActionBase { +protected: + RecordingOptions RecordOpts; + IndexDataRecorder Recorder; + std::shared_ptr IndexCtx; + SourceFilesIndexDependencyCollector DepCollector; + + IndexRecordActionBase(IndexingOptions IndexOpts, RecordingOptions recordOpts) + : RecordOpts(std::move(recordOpts)), + IndexCtx(new IndexingContext(IndexOpts, Recorder)), + DepCollector(*IndexCtx, RecordOpts) {} + + std::unique_ptr + createIndexASTConsumer(CompilerInstance &CI) { + IndexCtx->setSysrootPath(CI.getHeaderSearchOpts().Sysroot); + Recorder.init(IndexCtx.get(), CI); + + Preprocessor &PP = CI.getPreprocessor(); + DepCollector.setSourceManager(&CI.getSourceManager()); + DepCollector.setSysrootPath(IndexCtx->getSysrootPath()); + DepCollector.attachToPreprocessor(PP); + + if (IndexCtx->getIndexOpts().IndexMacros) + PP.addPPCallbacks(std::make_unique(IndexCtx)); + + return std::make_unique(CI.getPreprocessorPtr(), + IndexCtx); + } + + void finish(CompilerInstance &CI); +}; + +class IndexRecordAction : public ASTFrontendAction, IndexRecordActionBase { +public: + IndexRecordAction(IndexingOptions IndexOpts, RecordingOptions RecordOpts) + : IndexRecordActionBase(std::move(IndexOpts), std::move(RecordOpts)) {} + +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + return createIndexASTConsumer(CI); + } + + void EndSourceFileAction() override { + FrontendAction::EndSourceFileAction(); + finish(getCompilerInstance()); + } +}; + +class WrappingIndexRecordAction : public WrapperFrontendAction, + IndexRecordActionBase { + bool CreatedASTConsumer = false; + +public: + WrappingIndexRecordAction(std::unique_ptr WrappedAction, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts) + : WrapperFrontendAction(std::move(WrappedAction)), + IndexRecordActionBase(std::move(IndexOpts), std::move(RecordOpts)) {} + +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); + if (!OtherConsumer) + return nullptr; + + CreatedASTConsumer = true; + std::vector> Consumers; + Consumers.push_back(std::move(OtherConsumer)); + Consumers.push_back(createIndexASTConsumer(CI)); + return std::make_unique(std::move(Consumers)); + } + + void EndSourceFile() override { + FrontendAction::EndSourceFile(); + } + + void EndSourceFileAction() override { + // Invoke wrapped action's method. + WrapperFrontendAction::EndSourceFileAction(); + if (CreatedASTConsumer) + finish(getCompilerInstance()); + } +}; + +} // anonymous namespace + +static std::string getClangVersion() { + // Try picking the version from an Apple Clang tag. + std::string RepositoryPath = getClangRepositoryPath(); + StringRef BuildNumber = StringRef(RepositoryPath); + size_t DashOffset = BuildNumber.find('-'); + if (BuildNumber.starts_with("clang") && DashOffset != StringRef::npos) { + BuildNumber = BuildNumber.substr(DashOffset + 1); + return std::string(BuildNumber); + } + // Fallback to the generic version. + return CLANG_VERSION_STRING; +} + +static void writeUnitData(const CompilerInstance &CI, + IndexDataRecorder &Recorder, + IndexDependencyProvider &DepProvider, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts, StringRef OutputFile, + OptionalFileEntryRef RootFile, Module *UnitModule, + StringRef SysrootPath); + +void IndexRecordActionBase::finish(CompilerInstance &CI) { + // We may emit more diagnostics so do the begin/end source file invocations + // on the diagnostic client. + // FIXME: FrontendAction::EndSourceFile() should probably not call + // CI.getDiagnosticClient().EndSourceFile()' until after it has called + // 'EndSourceFileAction()', so that code executing during + // EndSourceFileAction() can emit diagnostics. If this is fixed, + // DiagClientBeginEndRAII can go away. + struct DiagClientBeginEndRAII { + CompilerInstance &CI; + DiagClientBeginEndRAII(CompilerInstance &CI) : CI(CI) { + CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts()); + } + ~DiagClientBeginEndRAII() { CI.getDiagnosticClient().EndSourceFile(); } + } diagClientBeginEndRAII(CI); + + Recorder.finish(); + + SourceManager &SM = CI.getSourceManager(); + DiagnosticsEngine &Diag = CI.getDiagnostics(); + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + StringRef DataPath = RecordOpts.DataDirPath; + + std::string Error; + if (IndexUnitWriter::initIndexDirectory(DataPath, Error)) { + unsigned DiagID = Diag.getCustomDiagID( + DiagnosticsEngine::Error, "failed creating index directory %0"); + Diag.Report(DiagID) << Error; + return; + } + + std::string OutputFile = CI.getFrontendOpts().IndexUnitOutputPath; + if (OutputFile.empty()) + OutputFile = CI.getFrontendOpts().OutputFile; + if (OutputFile.empty()) { + OutputFile = std::string(CI.getFrontendOpts().Inputs[0].getFile()); + OutputFile += ".o"; + } + + OptionalFileEntryRef RootFile; + Module *UnitMod = nullptr; + bool isModuleGeneration = CI.getLangOpts().isCompilingModule(); + if (!isModuleGeneration && + CI.getFrontendOpts().ProgramAction != frontend::GeneratePCH) { + RootFile = SM.getFileEntryRefForID(SM.getMainFileID()); + } + if (isModuleGeneration) { + UnitMod = HS.lookupModule(CI.getLangOpts().CurrentModule, SourceLocation(), + /*AllowSearch=*/false); + } + + writeUnitData(CI, Recorder, DepCollector, IndexCtx->getIndexOpts(), RecordOpts, + OutputFile, RootFile, UnitMod, IndexCtx->getSysrootPath()); +} + +/// Checks if the unit file exists for module file, if it doesn't it generates +/// index data for it. +static bool produceIndexDataForModuleFile(serialization::ModuleFile &Mod, + const CompilerInstance &CI, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts, + IndexUnitWriter &ParentUnitWriter); + +static void writeUnitData(const CompilerInstance &CI, + IndexDataRecorder &Recorder, + IndexDependencyProvider &DepProvider, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts, StringRef OutputFile, + OptionalFileEntryRef RootFile, Module *UnitModule, + StringRef SysrootPath) { + + SourceManager &SM = CI.getSourceManager(); + DiagnosticsEngine &Diag = CI.getDiagnostics(); + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + StringRef DataPath = RecordOpts.DataDirPath; + bool IsSystemUnit = UnitModule ? UnitModule->IsSystem : false; + bool IsModuleUnit = UnitModule != nullptr; + bool IsDebugCompilation = CI.getCodeGenOpts().OptimizationLevel == 0; + std::string ModuleName = + UnitModule ? UnitModule->getFullModuleName() : std::string(); + + auto getModuleInfo = + [](writer::OpaqueModule mod, + SmallVectorImpl &Scratch) -> writer::ModuleInfo { + assert(mod); + writer::ModuleInfo info; + std::string fullName = + static_cast(mod)->getFullModuleName(); + unsigned offset = Scratch.size(); + Scratch.append(fullName.begin(), fullName.end()); + info.Name = StringRef(Scratch.data() + offset, fullName.size()); + return info; + }; + + auto findModuleForHeader = [&](FileEntryRef FE) -> Module * { + if (!UnitModule) + return nullptr; + if (Module *Mod = HS.findModuleForHeader(FE).getModule()) + if (Mod->isSubModuleOf(UnitModule)) + return Mod; + return nullptr; + }; + PathRemapper Remapper; + auto &PrefixMap = CI.getCodeGenOpts().DebugPrefixMap; + // We need to add in reverse order since the `DebugPrefixMap` currently sorts + // ascending instead of descending, but we want `foo/subpath/` to come before + // `foo/`. + for (auto It = PrefixMap.rbegin(); It != PrefixMap.rend(); ++It) + Remapper.addMapping(It->first, It->second); + + IndexUnitWriter UnitWriter( + CI.getFileManager(), DataPath, "clang", getClangVersion(), OutputFile, + ModuleName, RootFile, IsSystemUnit, IsModuleUnit, IsDebugCompilation, + CI.getTargetOpts().Triple, SysrootPath, Remapper, getModuleInfo); + + DepProvider.visitFileDependencies( + CI, [&](FileEntryRef FE, bool isSystemFile) { + UnitWriter.addFileDependency(FE, isSystemFile, findModuleForHeader(FE)); + }); + DepProvider.visitIncludes( + [&](const FileEntry *Source, unsigned Line, const FileEntry *Target) { + UnitWriter.addInclude(Source, Line, Target); + }); + bool IndexPcms = IndexOpts.IndexPcms; + bool WithoutUnitName = !IndexPcms; + DepProvider.visitModuleImports(CI, [&](serialization::ModuleFile &Mod, + bool isSystemMod) { + Module *UnitMod = HS.lookupModule(Mod.ModuleName, Mod.ImportLoc, + /*AllowSearch=*/false); + UnitWriter.addASTFileDependency(Mod.File, isSystemMod, UnitMod, + WithoutUnitName); + if (Mod.isModule() && IndexPcms) { + produceIndexDataForModuleFile(Mod, CI, IndexOpts, RecordOpts, UnitWriter); + } + }); + + ClangIndexRecordWriter RecordWriter(CI.getASTContext(), RecordOpts); + for (auto I = Recorder.record_begin(), E = Recorder.record_end(); I != E; + ++I) { + FileID FID = I->first; + const FileIndexRecord &Rec = *I->second; + OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID); + std::string RecordFile; + std::string Error; + + if (RecordWriter.writeRecord(FE->getName(), Rec, Error, &RecordFile)) { + unsigned DiagID = Diag.getCustomDiagID(DiagnosticsEngine::Error, + "failed writing record '%0': %1"); + Diag.Report(DiagID) << RecordFile << Error; + return; + } + UnitWriter.addRecordFile(RecordFile, *FE, Rec.isSystem(), + findModuleForHeader(*FE)); + } + + std::string Error; + if (UnitWriter.write(Error)) { + unsigned DiagID = Diag.getCustomDiagID(DiagnosticsEngine::Error, + "failed writing unit data: %0"); + Diag.Report(DiagID) << Error; + return; + } +} + +namespace { +class ModuleFileIndexDependencyCollector : public IndexDependencyProvider { + serialization::ModuleFile &ModFile; + RecordingOptions RecordOpts; + +public: + ModuleFileIndexDependencyCollector(serialization::ModuleFile &Mod, + RecordingOptions recordOpts) + : ModFile(Mod), RecordOpts(recordOpts) {} + + void visitFileDependencies( + const CompilerInstance &CI, + llvm::function_ref visitor) + override { + auto Reader = CI.getASTReader(); + Reader->visitInputFiles( + ModFile, RecordOpts.RecordSystemDependencies, + /*Complain=*/false, + [&](const serialization::InputFile &IF, bool isSystem) { + auto FE = IF.getFile(); + if (!FE) + return; + // Ignore module map files, they are not as important to track as + // source files and they may be auto-generated which would create an + // undesirable dependency on an intermediate build byproduct. + if (FE->getName().ends_with("module.modulemap")) + return; + + visitor(*FE, isSystem); + }); + } + + void + visitIncludes(llvm::function_ref + visitor) override { + // FIXME: Module files without a preprocessing record do not have info about + // include locations. Serialize enough data to be able to retrieve such + // info. + } + + void visitModuleImports( + const CompilerInstance &CI, + llvm::function_ref + visitor) override { + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + for (auto *Mod : ModFile.Imports) { + bool isSystemMod = false; + if (auto *M = HS.lookupModule(Mod->ModuleName, Mod->ImportLoc, + /*AllowSearch=*/false)) + isSystemMod = M->IsSystem; + if (!isSystemMod || RecordOpts.RecordSystemDependencies) + visitor(*Mod, isSystemMod); + } + } +}; +} // anonymous namespace. + +static void indexModule(serialization::ModuleFile &Mod, + const CompilerInstance &CI, IndexingOptions IndexOpts, + RecordingOptions RecordOpts) { + DiagnosticsEngine &Diag = CI.getDiagnostics(); + Diag.Report(Mod.ImportLoc, diag::remark_index_producing_module_file_data) + << Mod.FileName; + + StringRef SysrootPath = CI.getHeaderSearchOpts().Sysroot; + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + Module *UnitMod = + HS.lookupModule(Mod.ModuleName, Mod.ImportLoc, /*AllowSearch=*/false); + + IndexDataRecorder Recorder; + IndexingContext IndexCtx(IndexOpts, Recorder); + + IndexCtx.setASTContext(CI.getASTContext()); + IndexCtx.setSysrootPath(SysrootPath); + Recorder.init(&IndexCtx, CI); + + for (const Decl *D : CI.getASTReader()->getModuleFileLevelDecls(Mod)) { + IndexCtx.indexTopLevelDecl(D); + } + if (IndexOpts.IndexMacrosInPreprocessor) { + indexPreprocessorModuleMacros(CI.getPreprocessor(), Mod, Recorder); + } + Recorder.finish(); + + ModuleFileIndexDependencyCollector DepCollector(Mod, RecordOpts); + writeUnitData(CI, Recorder, DepCollector, IndexOpts, RecordOpts, Mod.FileName, + /*RootFile=*/std::nullopt, UnitMod, SysrootPath); +} + +static bool produceIndexDataForModuleFile(serialization::ModuleFile &Mod, + const CompilerInstance &CI, + IndexingOptions IndexOpts, + RecordingOptions RecordOpts, + IndexUnitWriter &ParentUnitWriter) { + DiagnosticsEngine &Diag = CI.getDiagnostics(); + std::string Error; + // We don't do timestamp check with the PCM file, on purpose. The PCM may get + // touched for various reasons which would cause unnecessary work to emit + // index data. User modules normally will get rebuilt and their index data + // re-emitted, and system modules are generally stable (and they can also can + // get rebuilt along with their index data). + auto IsUptodateOpt = ParentUnitWriter.isUnitUpToDateForOutputFile( + Mod.FileName, std::nullopt, Error); + if (!IsUptodateOpt) { + unsigned DiagID = Diag.getCustomDiagID(DiagnosticsEngine::Error, + "failed file status check: %0"); + Diag.Report(DiagID) << Error; + return false; + } + if (*IsUptodateOpt) + return false; + + indexModule(Mod, CI, IndexOpts, RecordOpts); + return true; +} + +static std::unique_ptr +createIndexDataRecordingAction(IndexingOptions IndexOpts, + RecordingOptions RecordOpts, + std::unique_ptr WrappedAction) { + if (WrappedAction) + return std::make_unique( + std::move(WrappedAction), std::move(IndexOpts), std::move(RecordOpts)); + return std::make_unique(std::move(IndexOpts), + std::move(RecordOpts)); +} + +static std::pair +getIndexOptionsFromFrontendOptions(const FrontendOptions &FEOpts) { + index::IndexingOptions IndexOpts; + index::RecordingOptions RecordOpts; + RecordOpts.DataDirPath = FEOpts.IndexStorePath; + if (FEOpts.IndexIgnoreSystemSymbols) { + IndexOpts.SystemSymbolFilter = + index::IndexingOptions::SystemSymbolFilterKind::None; + } + IndexOpts.IndexMacros = !FEOpts.IndexIgnoreMacros; + IndexOpts.IndexMacrosInPreprocessor = !FEOpts.IndexIgnoreMacros; + IndexOpts.IndexPcms = !FEOpts.IndexIgnorePcms; + RecordOpts.RecordSymbolCodeGenName = FEOpts.IndexRecordCodegenName; + return {IndexOpts, RecordOpts}; +} + +std::unique_ptr index::createIndexDataRecordingAction( + const FrontendOptions &FEOpts, + std::unique_ptr WrappedAction) { + index::IndexingOptions IndexOpts; + index::RecordingOptions RecordOpts; + std::tie(IndexOpts, RecordOpts) = getIndexOptionsFromFrontendOptions(FEOpts); + return ::createIndexDataRecordingAction(IndexOpts, RecordOpts, + std::move(WrappedAction)); +} + +bool index::emitIndexDataForModuleFile(const Module *Mod, + const CompilerInstance &CI, + IndexUnitWriter &ParentUnitWriter) { + index::IndexingOptions IndexOpts; + index::RecordingOptions RecordOpts; + std::tie(IndexOpts, RecordOpts) = + getIndexOptionsFromFrontendOptions(CI.getFrontendOpts()); + + auto astReader = CI.getASTReader(); + serialization::ModuleFile *ModFile = + astReader->getModuleManager().lookup(*Mod->getASTFile()); + assert(ModFile && "no module file loaded for module ?"); + return produceIndexDataForModuleFile(*ModFile, CI, IndexOpts, RecordOpts, + ParentUnitWriter); +} diff --git a/clang/lib/Index/IndexingContext.cpp b/clang/lib/Index/IndexingContext.cpp index bdd6c5acf1d34..37b526099f4de 100644 --- a/clang/lib/Index/IndexingContext.cpp +++ b/clang/lib/Index/IndexingContext.cpp @@ -11,6 +11,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" +#include "clang/Basic/FileManager.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Index/IndexDataConsumer.h" @@ -129,12 +130,7 @@ bool IndexingContext::importedModule(const ImportDecl *ImportD) { if (FID.isInvalid()) return true; - bool Invalid = false; - const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); - if (Invalid || !SEntry.isFile()) - return true; - - if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { + if (isSystemFile(FID)) { switch (IndexOpts.SystemSymbolFilter) { case IndexingOptions::SystemSymbolFilterKind::None: return true; @@ -207,6 +203,53 @@ bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { return true; } +void IndexingContext::setSysrootPath(StringRef path) { + // Ignore sysroot path if it points to root, otherwise every header will be + // treated as system one. + if (path == "/") + path = StringRef(); + SysrootPath = std::string(path); +} + +bool IndexingContext::isSystemFile(FileID FID) { + if (LastFileCheck.first == FID) + return LastFileCheck.second; + + auto result = [&](bool res) -> bool { + LastFileCheck = { FID, res }; + return res; + }; + + bool Invalid = false; + const SrcMgr::SLocEntry &SEntry = + Ctx->getSourceManager().getSLocEntry(FID, &Invalid); + if (Invalid || !SEntry.isFile()) + return result(false); + + const SrcMgr::FileInfo &FI = SEntry.getFile(); + if (FI.getFileCharacteristic() != SrcMgr::C_User) + return result(true); + + auto FE = FI.getContentCache().OrigEntry; + if (!FE) + return result(false); + + if (SysrootPath.empty()) + return result(false); + + // Check if directory is in sysroot so that we can consider system headers + // even the headers found via a user framework search path, pointing inside + // sysroot. + auto dirEntry = FE->getDir(); + auto pair = DirEntries.insert(std::make_pair(dirEntry, false)); + bool &isSystemDir = pair.first->second; + bool wasInserted = pair.second; + if (wasInserted) { + isSystemDir = StringRef(dirEntry.getName()).starts_with(SysrootPath); + } + return result(isSystemDir); +} + static const CXXRecordDecl * getDeclContextForTemplateInstationPattern(const Decl *D) { if (const auto *CTSD = @@ -371,7 +414,7 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, const Expr *OrigE, const Decl *OrigD, const DeclContext *ContainerDC) { - if (D->isImplicit() && !isa(D)) + if (D->isImplicit() && !(isa(D) || isa(D))) return true; if (!isa(D) || shouldSkipNamelessDecl(cast(D))) return true; @@ -381,12 +424,7 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, if (FID.isInvalid()) return true; - bool Invalid = false; - const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); - if (Invalid || !SEntry.isFile()) - return true; - - if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { + if (isSystemFile(FID)) { switch (IndexOpts.SystemSymbolFilter) { case IndexingOptions::SystemSymbolFilterKind::None: return true; @@ -506,13 +544,5 @@ bool IndexingContext::shouldIndexMacroOccurrence(bool IsRef, SourceManager &SM = Ctx->getSourceManager(); FileID FID = SM.getFileID(SM.getFileLoc(Loc)); - if (FID.isInvalid()) - return false; - - bool Invalid = false; - const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); - if (Invalid || !SEntry.isFile()) - return false; - - return SEntry.getFile().getFileCharacteristic() == SrcMgr::C_User; + return !isSystemFile(FID); } diff --git a/clang/lib/Index/IndexingContext.h b/clang/lib/Index/IndexingContext.h index 01bfcb9d578bc..8a1ea4c9a0d30 100644 --- a/clang/lib/Index/IndexingContext.h +++ b/clang/lib/Index/IndexingContext.h @@ -11,10 +11,12 @@ #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Index/IndexSymbol.h" #include "clang/Index/IndexingAction.h" #include "clang/Lex/MacroInfo.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" namespace clang { class ASTContext; @@ -31,7 +33,7 @@ namespace clang { class Stmt; class Expr; class TypeLoc; - class SourceLocation; + class DirectoryEntry; namespace index { class IndexDataConsumer; @@ -40,6 +42,13 @@ class IndexingContext { IndexingOptions IndexOpts; IndexDataConsumer &DataConsumer; ASTContext *Ctx = nullptr; + std::string SysrootPath; + + // Records whether a directory entry is system or not. + llvm::DenseMap DirEntries; + // Keeps track of the last check for whether a FileID is system or + // not. This is used to speed up isSystemFile() call. + std::pair LastFileCheck; std::unique_ptr Resolver; public: @@ -52,6 +61,10 @@ class IndexingContext { void setASTContext(ASTContext &ctx); HeuristicResolver *getResolver() const { return Resolver.get(); } + void setSysrootPath(StringRef path); + StringRef getSysrootPath() const { return SysrootPath; } + + bool isSystemFile(FileID FID); bool shouldIndex(const Decl *D); diff --git a/clang/lib/IndexDataStore/CMakeLists.txt b/clang/lib/IndexDataStore/CMakeLists.txt new file mode 100644 index 0000000000000..35c8dc05631fd --- /dev/null +++ b/clang/lib/IndexDataStore/CMakeLists.txt @@ -0,0 +1,11 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_library(clangIndexDataStore + IndexDataStore.cpp + + LINK_LIBS + clangDirectoryWatcher + clangIndex + ) diff --git a/clang/lib/IndexDataStore/IndexDataStore.cpp b/clang/lib/IndexDataStore/IndexDataStore.cpp new file mode 100644 index 0000000000000..72108834ce9c4 --- /dev/null +++ b/clang/lib/IndexDataStore/IndexDataStore.cpp @@ -0,0 +1,245 @@ +//===--- IndexDataStore.cpp - Index data store info -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexDataStore.h" +#include "clang/Basic/PathRemapper.h" +#include "clang/DirectoryWatcher/DirectoryWatcher.h" +#include "../lib/Index/IndexDataStoreUtils.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; +using namespace clang::index::store; +using namespace llvm; + +namespace { + +class UnitEventHandlerData { + mutable sys::Mutex Mtx; + IndexDataStore::UnitEventHandler Handler; + +public: + void setHandler(IndexDataStore::UnitEventHandler handler) { + sys::ScopedLock L(Mtx); + Handler = std::move(handler); + } + IndexDataStore::UnitEventHandler getHandler() const { + sys::ScopedLock L(Mtx); + return Handler; + } +}; + +class IndexDataStoreImpl { + std::string FilePath; + PathRemapper Remapper; + std::shared_ptr TheUnitEventHandlerData; + std::unique_ptr DirWatcher; + +public: + explicit IndexDataStoreImpl(StringRef indexStorePath, PathRemapper remapper) + : FilePath(indexStorePath), Remapper(remapper) { + TheUnitEventHandlerData = std::make_shared(); + } + + StringRef getFilePath() const { return FilePath; } + const PathRemapper &getPathRemapper() const { + return Remapper; + } + bool foreachUnitName(bool sorted, + llvm::function_ref receiver); + void setUnitEventHandler(IndexDataStore::UnitEventHandler Handler); + bool startEventListening(bool waitInitialSync, std::string &Error); + void stopEventListening(); + void discardUnit(StringRef UnitName); + void discardRecord(StringRef RecordName); + void purgeStaleData(); +}; + +} // anonymous namespace + +bool IndexDataStoreImpl::foreachUnitName(bool sorted, + llvm::function_ref receiver) { + SmallString<128> UnitPath; + UnitPath = FilePath; + appendUnitSubDir(UnitPath); + + std::vector filenames; + + std::error_code EC; + for (auto It = sys::fs::directory_iterator(UnitPath, EC), + End = sys::fs::directory_iterator(); + !EC && It != End; It.increment(EC)) { + StringRef unitName = sys::path::filename(It->path()); + if (!sorted) { + if (!receiver(unitName)) + return false; + } else { + filenames.push_back(std::string(unitName)); + } + } + + if (sorted) { + std::sort(filenames.begin(), filenames.end()); + for (auto &fname : filenames) + if (!receiver(fname)) + return false; + } + return true; +} + +void IndexDataStoreImpl::setUnitEventHandler(IndexDataStore::UnitEventHandler handler) { + TheUnitEventHandlerData->setHandler(std::move(handler)); +} + +bool IndexDataStoreImpl::startEventListening(bool waitInitialSync, std::string &Error) { + if (DirWatcher) { + Error = "event listener already active"; + return true; + } + + SmallString<128> UnitPath; + UnitPath = FilePath; + appendUnitSubDir(UnitPath); + + auto localUnitEventHandlerData = TheUnitEventHandlerData; + auto OnUnitsChange = [localUnitEventHandlerData](ArrayRef Events, bool isInitial) { + SmallVector UnitEvents; + UnitEvents.reserve(Events.size()); + for (const DirectoryWatcher::Event &evt : Events) { + IndexDataStore::UnitEventKind K; + StringRef UnitName = sys::path::filename(evt.Filename); + switch (evt.Kind) { + case DirectoryWatcher::Event::EventKind::Removed: + K = IndexDataStore::UnitEventKind::Removed; break; + case DirectoryWatcher::Event::EventKind::Modified: + K = IndexDataStore::UnitEventKind::Modified; break; + case DirectoryWatcher::Event::EventKind::WatchedDirRemoved: + K = IndexDataStore::UnitEventKind::DirectoryDeleted; + UnitName = StringRef(); + break; + case DirectoryWatcher::Event::EventKind::WatcherGotInvalidated: + K = IndexDataStore::UnitEventKind::Failure; + UnitName = StringRef(); + break; + } + UnitEvents.push_back(IndexDataStore::UnitEvent{K, UnitName}); + } + + if (auto handler = localUnitEventHandlerData->getHandler()) { + IndexDataStore::UnitEventNotification EventNote{isInitial, UnitEvents}; + handler(EventNote); + } + }; + + // Create the unit path if necessary so that the directory watcher can start + // even if the data has not been populated yet. + if (std::error_code EC = llvm::sys::fs::create_directories(UnitPath)) { + Error = EC.message(); + return true; + } + + llvm::Expected> ExpectedDirWatcher = + DirectoryWatcher::create(UnitPath.str(), OnUnitsChange, waitInitialSync); + if (!ExpectedDirWatcher) { + Error = llvm::toString(ExpectedDirWatcher.takeError()); + return true; + } + + DirWatcher = std::move(ExpectedDirWatcher.get()); + return false; +} + +void IndexDataStoreImpl::stopEventListening() { + DirWatcher.reset(); +} + +void IndexDataStoreImpl::discardUnit(StringRef UnitName) { + SmallString<128> UnitPath; + UnitPath = FilePath; + appendUnitSubDir(UnitPath); + appendInteriorUnitPath(UnitName, UnitPath); + sys::fs::remove(UnitPath); +} + +void IndexDataStoreImpl::discardRecord(StringRef RecordName) { + SmallString<128> RecordPath; + RecordPath = FilePath; + appendRecordSubDir(RecordPath); + appendInteriorRecordPath(RecordName, RecordPath); + sys::fs::remove(RecordPath); +} + +void IndexDataStoreImpl::purgeStaleData() { + // FIXME: Implement. +} + + +std::unique_ptr +IndexDataStore::create(StringRef IndexStorePath, const PathRemapper &Remapper, + std::string &Error) { + if (!sys::fs::exists(IndexStorePath)) { + raw_string_ostream OS(Error); + OS << "index store path does not exist: " << IndexStorePath; + return nullptr; + } + + return std::unique_ptr( + new IndexDataStore(new IndexDataStoreImpl(IndexStorePath, Remapper))); +} + +#define IMPL static_cast(Impl) + +IndexDataStore::~IndexDataStore() { + delete IMPL; +} + +StringRef IndexDataStore::getFilePath() const { + return IMPL->getFilePath(); +} + +const PathRemapper & IndexDataStore::getPathRemapper() const { + return IMPL->getPathRemapper(); +} + +bool IndexDataStore::foreachUnitName(bool sorted, + llvm::function_ref receiver) { + return IMPL->foreachUnitName(sorted, std::move(receiver)); +} + +unsigned IndexDataStore::getFormatVersion() { + return STORE_FORMAT_VERSION; +} + +void IndexDataStore::setUnitEventHandler(UnitEventHandler Handler) { + return IMPL->setUnitEventHandler(std::move(Handler)); +} + +bool IndexDataStore::startEventListening(bool waitInitialSync, std::string &Error) { + return IMPL->startEventListening(waitInitialSync, Error); +} + +void IndexDataStore::stopEventListening() { + return IMPL->stopEventListening(); +} + +void IndexDataStore::discardUnit(StringRef UnitName) { + IMPL->discardUnit(UnitName); +} + +void IndexDataStore::discardRecord(StringRef RecordName) { + IMPL->discardRecord(RecordName); +} + +void IndexDataStore::purgeStaleData() { + IMPL->purgeStaleData(); +} diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index 93200458f04b4..6402ffdf6bbd9 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -1408,6 +1408,35 @@ SourceLocation Lexer::findLocationAfterToken( return TokenLoc.getLocWithOffset(Tok->getLength() + NumWhitespaceChars); } +SourceLocation Lexer::findNextTokenLocationAfterTokenAt( + SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts) { + // TODO: Share the code with the function above when upstreaming. + if (Loc.isMacroID()) { + if (!Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc)) + return SourceLocation(); + } + Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts); + + // Break down the source location. + std::pair LocInfo = SM.getDecomposedLoc(Loc); + + // Try to load the file buffer. + bool InvalidTemp = false; + StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp); + if (InvalidTemp) + return SourceLocation(); + + const char *TokenBegin = File.data() + LocInfo.second; + + // Lex from the start of the given location. + Lexer lexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(), + TokenBegin, File.end()); + // Find the token. + Token Tok; + lexer.LexFromRawLexer(Tok); + return Tok.getLocation(); +} + /// getCharAndSizeSlow - Peek a single 'character' from the specified buffer, /// get its size, and return it. This is tricky in several cases: /// 1. If currently at the start of a trigraph, we warn about the trigraph, diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp index a1394fd3900b0..1e9754e7dae72 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -812,8 +812,17 @@ bool ModuleMap::isHeaderUnavailableInModule( Module *ModuleMap::findModule(StringRef Name) const { llvm::StringMap::const_iterator Known = Modules.find(Name); - if (Known != Modules.end()) - return Known->getValue(); + if (Known != Modules.end()) { + Module *M = Known->getValue(); + // Notify callbacks that we found a module map for the module. + if (M->DefinitionLoc.isValid() && !M->IsFromModuleFile) + for (const auto &Cb : Callbacks) + Cb->moduleMapFoundForModule( + *getContainingModuleMapFile(M), M, + SourceMgr.getFileCharacteristic(M->DefinitionLoc) == + SrcMgr::C_System_ModuleMap); + return M; + } return nullptr; } diff --git a/clang/lib/Lex/ModuleMapFile.cpp b/clang/lib/Lex/ModuleMapFile.cpp index 5cf4a4c3d96c1..b9ff712429904 100644 --- a/clang/lib/Lex/ModuleMapFile.cpp +++ b/clang/lib/Lex/ModuleMapFile.cpp @@ -1092,6 +1092,9 @@ bool ModuleMapFileParser::parseOptionalAttributes(ModuleAttributes &Attrs) { /// The 'exhaustive' attribute. AT_exhaustive, + // \brief The 'swift_infer_import_as_member' attribute. + AT_swift_infer_import_as_member, + /// The 'no_undeclared_includes' attribute. AT_no_undeclared_includes }; @@ -1103,6 +1106,7 @@ bool ModuleMapFileParser::parseOptionalAttributes(ModuleAttributes &Attrs) { .Case("extern_c", AT_extern_c) .Case("no_undeclared_includes", AT_no_undeclared_includes) .Case("system", AT_system) + .Case("swift_infer_import_as_member", AT_swift_infer_import_as_member) .Default(AT_unknown); switch (Attribute) { case AT_unknown: @@ -1118,6 +1122,10 @@ bool ModuleMapFileParser::parseOptionalAttributes(ModuleAttributes &Attrs) { Attrs.IsExternC = true; break; + case AT_swift_infer_import_as_member: + Attrs.IsSwiftInferImportAsMember = true; + break; + case AT_exhaustive: Attrs.IsExhaustive = true; break; diff --git a/clang/lib/Lex/PPCallbacks.cpp b/clang/lib/Lex/PPCallbacks.cpp index cf473aa4df656..d9a86ba35721f 100644 --- a/clang/lib/Lex/PPCallbacks.cpp +++ b/clang/lib/Lex/PPCallbacks.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/PPCachedActions.h" using namespace clang; @@ -26,3 +27,5 @@ void PPChainedCallbacks::HasInclude(SourceLocation Loc, StringRef FileName, First->HasInclude(Loc, FileName, IsAngled, File, FileType); Second->HasInclude(Loc, FileName, IsAngled, File, FileType); } + +void PPCachedActions::anchor() {} diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 6c337801a8435..23dbf37daba07 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -23,6 +23,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TokenKinds.h" +#include "clang/Basic/DiagnosticFrontend.h" #include "clang/Lex/CodeCompletionHandler.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/LexDiagnostic.h" @@ -869,9 +870,7 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc, // the #if block. CurPPLexer->LexingRawMode = false; - // The last skipped range isn't actually skipped yet if it's truncated - // by the end of the preamble; we'll resume parsing after the preamble. - if (Callbacks && (Tok.isNot(tok::eof) || !isRecordingPreamble())) + if (Callbacks) Callbacks->SourceRangeSkipped( SourceRange(HashTokenLoc, endLoc.isValid() ? endLoc @@ -2025,6 +2024,11 @@ bool Preprocessor::checkModuleIsAvailable(const LangOptions &LangOpts, std::pair Preprocessor::getIncludeNextStart(const Token &IncludeNextTok) const { + if (getPPCachedActions()) { + // We have pre-recorded include lookups. + return std::make_pair(nullptr, nullptr); + } + // #include_next is like #include, except that we start searching after // the current found directory. If we can't do this, issue a // diagnostic. @@ -2095,6 +2099,149 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, SourceLocation EndLoc = CheckEndOfDirective(IncludeTok.getIdentifierInfo()->getNameStart(), true); + if (auto *CActions = getPPCachedActions()) { + SourceLocation IncludePos = FilenameTok.getLocation(); + // If the filename string was the result of macro expansions, set the + // include position on the file where it will be included and after the + // expansions. + if (IncludePos.isMacroID()) + IncludePos = SourceMgr.getExpansionRange(IncludePos).getEnd(); + auto Include = CActions->handleIncludeDirective( + *this, IncludePos, CurLexer->getSourceLocation()); + + auto InclusionCallback = [&](OptionalFileEntryRef FileRef, + const Module *SuggestedModule) { + if (!Callbacks || HashLoc.isInvalid()) + return; + + SmallString<128> FilenameBuffer; + StringRef Filename = getSpelling(FilenameTok, FilenameBuffer); + SourceLocation CharEnd = FilenameTok.getEndLoc(); + CharSourceRange FilenameRange = + CharSourceRange::getCharRange(FilenameTok.getLocation(), CharEnd); + bool isAngled = + GetIncludeFilenameSpelling(FilenameTok.getLocation(), Filename); + SrcMgr::CharacteristicKind FileCharacter = + SourceMgr.getFileCharacteristic(FilenameTok.getLocation()); + if (SuggestedModule) + Callbacks->InclusionDirective( + HashLoc, IncludeTok, Filename, isAngled, FilenameRange, FileRef, + /*SearchPath=*/"", /*RelativePath=*/"", SuggestedModule, + /*ModuleImported=*/true, FileCharacter); + else + Callbacks->InclusionDirective( + HashLoc, IncludeTok, Filename, isAngled, FilenameRange, FileRef, + /*SearchPath=*/"", /*RelativePath=*/"", /*SuggestedModule=*/nullptr, + /*ModuleImported=*/false, FileCharacter); + }; + + auto HandleIncludeFile = [&](const PPCachedActions::IncludeFile *File) { + OptionalFileEntryRef FE = SourceMgr.getFileEntryRefForID(File->FID); + bool IsImport = + IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import; + if (FE && IsImport) { + HeaderInfo.getFileInfo(*FE).isImport = true; + } + InclusionCallback(SourceMgr.getFileEntryRefForID(File->FID), nullptr); + EnterSourceFile(File->FID, nullptr, FilenameTok.getLocation(), + /*IsFirstIncludeOfFile*/ true); + + if (Module *SM = File->Submodule) { + assert(!CurLexerSubmodule && + "should not have marked this as a module yet"); + CurLexerSubmodule = SM; + EnterSubmodule(SM, EndLoc, /*ForPragma=*/false); + EnterAnnotationToken(SourceRange(HashLoc, EndLoc), + tok::annot_module_begin, SM); + } + }; + + auto CheckLoadResult = [&](ModuleLoadResult Result) { + if (Result) + return true; + assert(hadModuleLoaderFatalFailure() && "unexpected failure kind"); + if (hadModuleLoaderFatalFailure()) { + IncludeTok.setKind(tok::eof); + CurLexer->cutOffLexing(); + } + return false; + }; + + auto LoadModule = [&](const PPCachedActions::IncludeModule *Import) { + auto Imported = TheModuleLoader.loadModule( + IncludeTok.getLocation(), ArrayRef(Import->ImportPath).take_front(), + Module::Hidden, /*IsInclusionDirective=*/true); + if (!CheckLoadResult(Imported)) + return; + + auto Path = Import->ImportPath; + std::string PathStr = Path.front().getIdentifierInfo()->getName().str(); + for (unsigned I = 1; I != Path.size(); ++I) + PathStr += ("." + Path[I].getIdentifierInfo()->getName()).str(); + + getDiagnostics().Report(IncludeTok.getLocation(), + diag::warn_missing_submodule) + << PathStr + << SourceRange(Path.front().getLoc(), Path.back().getLoc()); + }; + + auto HandleIncludeMod = [&](const PPCachedActions::IncludeModule *Import) { + ModuleLoadResult Imported; + if (Import->VisibilityOnly) { + ModuleMap &MMap = getHeaderSearchInfo().getModuleMap(); + Module *M = nullptr; + for (auto &NameLoc : Import->ImportPath) { + M = MMap.lookupModuleQualified(NameLoc.getIdentifierInfo()->getName(), + M); + if (!M) + break; + } + if (!M) { + Diags->Report(diag::err_pp_missing_module_include_tree) + << getLangOpts().CurrentModule; + + return; + } + Imported = M; + } else { + Imported = TheModuleLoader.loadModule( + IncludeTok.getLocation(), Import->ImportPath, Module::Hidden, + /*IsIncludeDirective=*/true); + if (!CheckLoadResult(Imported)) + return; + // PPCallback for IncludeDirective. Using the AST file as the FileEntry + // in the callback to indicate this is not a missing header. Note this + // is not the same behavior as non-include-tree build where the + // FileEntry is for the header file. + // FIXME: Need to clarify what `File` means in the callback, and if that + // can be the module file entry instead of header file entry. + Module *M = Imported; + InclusionCallback(M->getASTFile(), Imported); + } + + makeModuleVisible(Imported, EndLoc); + if (IncludeTok.getIdentifierInfo()->getPPKeywordID() != + tok::pp___include_macros) + EnterAnnotationToken(SourceRange(HashLoc, EndLoc), + tok::annot_module_include, Imported); + }; + + if (auto *SpuriousImport = + std::get_if(&Include)) { + LoadModule(&SpuriousImport->IM); + HandleIncludeFile(&SpuriousImport->IF); + return; + } + if (auto *File = std::get_if(&Include)) + return HandleIncludeFile(File); + if (auto *Import = std::get_if(&Include)) + return HandleIncludeMod(Import); + assert(std::holds_alternative(Include)); + // FIXME: Report \p Callbacks->FileSkipped? Note that it currently + // requires the resolved FileEntry for this particular #include. + return; + } + auto Action = HandleHeaderIncludeOrImport(HashLoc, IncludeTok, FilenameTok, EndLoc, LookupFrom, LookupFromFile); switch (Action.Kind) { diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp index 44b5fa8be9f1b..9f84dd4ba9c83 100644 --- a/clang/lib/Lex/PPLexerChange.cpp +++ b/clang/lib/Lex/PPLexerChange.cpp @@ -506,6 +506,10 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) { PPCallbacks::LexedFileChangeReason::ExitFile, FileType, ExitedFID, Loc); } + if (auto *CActions = getPPCachedActions()) { + if (ExitedFID.isValid()) + CActions->exitedFile(*this, ExitedFID); + } // Restore conditional stack as well as the recorded // \#pragma clang assume_nonnull from the preamble right after exiting diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 37ac1bf07e9c0..5c1402b4a6f45 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1171,11 +1171,14 @@ static bool HasExtension(const Preprocessor &PP, StringRef Extension) { /// EvaluateHasIncludeCommon - Process a '__has_include("path")' /// or '__has_include_next("path")' expression. +/// If \p Precomputed is set it progresses the lexer but doesn't do a file +/// lookup, it returns the value of \p Precomputed instead. /// Returns true if successful. static bool EvaluateHasIncludeCommon(Token &Tok, IdentifierInfo *II, Preprocessor &PP, ConstSearchDirIterator LookupFrom, - const FileEntry *LookupFromFile) { + const FileEntry *LookupFromFile, + std::optional Precomputed) { // Save the location of the current token. If a '(' is later found, use // that location. If not, use the end of this location instead. SourceLocation LParenLoc = Tok.getLocation(); @@ -1236,6 +1239,9 @@ static bool EvaluateHasIncludeCommon(Token &Tok, IdentifierInfo *II, return false; } + if (Precomputed) + return *Precomputed; + bool isAngled = PP.GetIncludeFilenameSpelling(Tok.getLocation(), Filename); // If GetIncludeFilenameSpelling set the start ptr to null, there was an // error. @@ -1363,15 +1369,27 @@ EmbedResult Preprocessor::EvaluateHasEmbed(Token &Tok, IdentifierInfo *II) { } bool Preprocessor::EvaluateHasInclude(Token &Tok, IdentifierInfo *II) { - return EvaluateHasIncludeCommon(Tok, II, *this, nullptr, nullptr); + std::optional Precomputed; + if (auto *CActions = getPPCachedActions()) { + Precomputed = CActions->evaluateHasInclude(*this, Tok.getLocation(), + /*IsIncludeNext*/ false); + } + return EvaluateHasIncludeCommon(Tok, II, *this, nullptr, nullptr, + Precomputed); } bool Preprocessor::EvaluateHasIncludeNext(Token &Tok, IdentifierInfo *II) { + std::optional Precomputed; + if (auto *CActions = getPPCachedActions()) { + Precomputed = CActions->evaluateHasInclude(*this, Tok.getLocation(), + /*IsIncludeNext*/ true); + } ConstSearchDirIterator Lookup = nullptr; const FileEntry *LookupFromFile; std::tie(Lookup, LookupFromFile) = getIncludeNextStart(Tok); - return EvaluateHasIncludeCommon(Tok, II, *this, Lookup, LookupFromFile); + return EvaluateHasIncludeCommon(Tok, II, *this, Lookup, LookupFromFile, + Precomputed); } /// Process single-argument builtin feature-like macros that return @@ -1649,6 +1667,19 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { Tok.clearFlag(Token::NeedsCleaning); bool IsAtStartOfLine = Tok.isAtStartOfLine(); bool HasLeadingSpace = Tok.hasLeadingSpace(); + auto handleDateTimeWarnings = [&](SourceLocation Loc) { + IsSourceNonReproducible = true; + switch (getPreprocessorOpts().CachingDiagOption) { + case CachingDiagKind::None: + return; + case CachingDiagKind::Warning: + Diag(Loc, diag::warn_pp_encounter_nonreproducible); + return; + case CachingDiagKind::Error: + Diag(Loc, diag::err_pp_encounter_nonreproducible); + return; + } + }; if (II == Ident__LINE__) { // C99 6.10.8: "__LINE__: The presumed line number (within the current @@ -1707,6 +1738,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { Tok.setKind(tok::string_literal); } else if (II == Ident__DATE__) { Diag(Tok.getLocation(), diag::warn_pp_date_time); + handleDateTimeWarnings(Tok.getLocation()); if (!DATELoc.isValid()) ComputeDATE_TIME(DATELoc, TIMELoc, *this); Tok.setKind(tok::string_literal); @@ -1717,6 +1749,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { return; } else if (II == Ident__TIME__) { Diag(Tok.getLocation(), diag::warn_pp_date_time); + handleDateTimeWarnings(Tok.getLocation()); if (!TIMELoc.isValid()) ComputeDATE_TIME(DATELoc, TIMELoc, *this); Tok.setKind(tok::string_literal); @@ -1742,6 +1775,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { Tok.setKind(tok::numeric_constant); } else if (II == Ident__TIMESTAMP__) { Diag(Tok.getLocation(), diag::warn_pp_date_time); + handleDateTimeWarnings(Tok.getLocation()); // MSVC, ICC, GCC, VisualAge C++ extension. The generated string should be // of the form "Ddd Mmm dd hh::mm::ss yyyy", which is returned by asctime. std::string Result; diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index 5b6a29bdad910..15277bed7a51f 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -1983,6 +1983,50 @@ struct PragmaAssumeNonNullHandler : public PragmaHandler { } }; +/* TO_UPSTREAM(BoundsSafety) ON*/ +struct PragmaAbiPointerAttributesHandler : public PragmaHandler { + PragmaAbiPointerAttributesHandler() : PragmaHandler("abi_ptr_attr") {} + + void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, + Token &NameTok) override { + SourceLocation Loc = NameTok.getLocation(); + PPCallbacks *Callbacks = PP.getPPCallbacks(); + Token Tok; + + // Lex the mode. + PP.LexUnexpandedToken(Tok); + const IdentifierInfo *Mode = Tok.getIdentifierInfo(); + if (!Mode || !Mode->isStr("set")) { + PP.Diag(Tok.getLocation(), diag::err_pp_abi_ptr_attr_set_syntax); + return; + } + + // Parse '(', arbitrary list of identifiers, ')' + PP.LexUnexpandedToken(Tok); + if (Tok.isNot(tok::l_paren)) { + PP.Diag(Tok.getLocation(), diag::err_pp_abi_ptr_attr_set_syntax); + return; + } + + SmallVector Attributes; + for (PP.LexUnexpandedToken(Tok); + const IdentifierInfo *II = Tok.getIdentifierInfo(); + PP.LexUnexpandedToken(Tok)) + Attributes.push_back(II); + + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::err_pp_abi_ptr_attr_set_syntax); + return; + } + + PP.LexUnexpandedToken(Tok); + if (Tok.isNot(tok::eod)) + PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma"; + Callbacks->PragmaAbiPointerAttributesSet(Loc, Attributes); + } +}; +/* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Handle "\#pragma region [...]" /// /// The syntax is @@ -2163,6 +2207,9 @@ void Preprocessor::RegisterBuiltinPragmas() { AddPragmaHandler("clang", new PragmaDiagnosticHandler("clang")); AddPragmaHandler("clang", new PragmaARCCFCodeAuditedHandler()); AddPragmaHandler("clang", new PragmaAssumeNonNullHandler()); + /* TO_UPSTREAM(BoundsSafety) ON*/ + AddPragmaHandler("clang", new PragmaAbiPointerAttributesHandler()); + /* TO_UPSTREAM(BoundsSafety) OFF*/ AddPragmaHandler("clang", new PragmaDeprecatedHandler()); AddPragmaHandler("clang", new PragmaRestrictExpansionHandler()); AddPragmaHandler("clang", new PragmaFinalHandler()); diff --git a/clang/lib/Lex/PreprocessingRecord.cpp b/clang/lib/Lex/PreprocessingRecord.cpp index f76155b1c45d6..e0ffc8a94aecc 100644 --- a/clang/lib/Lex/PreprocessingRecord.cpp +++ b/clang/lib/Lex/PreprocessingRecord.cpp @@ -321,23 +321,6 @@ unsigned PreprocessingRecord::allocateLoadedEntities(unsigned NumEntities) { return Result; } -unsigned PreprocessingRecord::allocateSkippedRanges(unsigned NumRanges) { - unsigned Result = SkippedRanges.size(); - SkippedRanges.resize(SkippedRanges.size() + NumRanges); - SkippedRangesAllLoaded = false; - return Result; -} - -void PreprocessingRecord::ensureSkippedRangesLoaded() { - if (SkippedRangesAllLoaded || !ExternalSource) - return; - for (unsigned Index = 0; Index != SkippedRanges.size(); ++Index) { - if (SkippedRanges[Index].isInvalid()) - SkippedRanges[Index] = ExternalSource->ReadSkippedRange(Index); - } - SkippedRangesAllLoaded = true; -} - void PreprocessingRecord::RegisterMacroDefinition(MacroInfo *Macro, MacroDefinitionRecord *Def) { MacroDefinitions[Macro] = Def; @@ -439,7 +422,6 @@ void PreprocessingRecord::Defined(const Token &MacroNameTok, void PreprocessingRecord::SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) { - assert(Range.isValid()); SkippedRanges.emplace_back(Range.getBegin(), EndifLoc); } @@ -513,6 +495,5 @@ size_t PreprocessingRecord::getTotalMemory() const { return BumpAlloc.getTotalMemory() + llvm::capacity_in_bytes(MacroDefinitions) + llvm::capacity_in_bytes(PreprocessedEntities) - + llvm::capacity_in_bytes(LoadedPreprocessedEntities) - + llvm::capacity_in_bytes(SkippedRanges); + + llvm::capacity_in_bytes(LoadedPreprocessedEntities); } diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index 4c050bf1f5bb2..dabefbaf99bbc 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -110,6 +110,7 @@ Preprocessor::Preprocessor(const PreprocessorOptions &PPOpts, PragmasEnabled = true; ParsingIfOrElifDirective = false; PreprocessedOutput = false; + IsSourceNonReproducible = false; // We haven't read anything from the external source. ReadMacrosFromExternalSource = false; @@ -566,12 +567,17 @@ void Preprocessor::EnterMainSourceFile() { markIncluded(*FE); } - // Preprocess Predefines to populate the initial preprocessor state. - std::unique_ptr SB = - llvm::MemoryBuffer::getMemBufferCopy(Predefines, ""); - assert(SB && "Cannot create predefined source buffer"); - FileID FID = SourceMgr.createFileID(std::move(SB)); - assert(FID.isValid() && "Could not create FileID for predefines?"); + FileID FID; + if (auto *CActions = getPPCachedActions()) { + FID = CActions->handlePredefines(*this); + } else { + // Preprocess Predefines to populate the initial preprocessor state. + std::unique_ptr SB = + llvm::MemoryBuffer::getMemBufferCopy(Predefines, ""); + assert(SB && "Cannot create predefined source buffer"); + FID = SourceMgr.createFileID(std::move(SB)); + assert(FID.isValid() && "Could not create FileID for predefines?"); + } setPredefinesFileID(FID); // Start parsing the predefines. diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index e76435d0e9de7..03fa2d7971e88 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -90,6 +90,7 @@ NamedDecl *Parser::ParseCXXInlineMethodDef( VS, ICIS_NoInit); if (FnD) { Actions.ProcessDeclAttributeList(getCurScope(), FnD, AccessAttrs); + Actions.ProcessAPINotes(FnD); if (PureSpecLoc.isValid()) Actions.ActOnPureSpecifier(FnD, PureSpecLoc); } @@ -825,6 +826,16 @@ void Parser::ParseLexedAttribute(LateParsedAttribute &LA, Diag(Tok, diag::warn_attribute_on_function_definition) << &LA.AttrName; + /* TO_UPSTREAM(BoundsSafety) ON */ + if (LA.MacroII) { + const auto &SM = PP.getSourceManager(); + CharSourceRange ExpansionRange = SM.getExpansionRange(LA.AttrNameLoc); + for (unsigned i = 0; i < Attrs.size(); ++i) + Attrs[i].setMacroIdentifier(LA.MacroII, ExpansionRange.getBegin(), + SM.isInSystemMacro(LA.AttrNameLoc)); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + for (unsigned i = 0, ni = LA.Decls.size(); i < ni; ++i) Actions.ActOnFinishDelayedAttribute(getCurScope(), LA.Decls[i], Attrs); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 0747790b96d2f..25ecdc99faeb5 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -51,7 +51,9 @@ using namespace clang; /// Called type-id in C++. TypeResult Parser::ParseTypeName(SourceRange *Range, DeclaratorContext Context, AccessSpecifier AS, Decl **OwnedType, - ParsedAttributes *Attrs) { + ParsedAttributes *Attrs, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs) { DeclSpecContext DSC = getDeclSpecContextFromDeclaratorContext(Context); if (DSC == DeclSpecContext::DSC_normal) DSC = DeclSpecContext::DSC_type_specifier; @@ -82,6 +84,11 @@ TypeResult Parser::ParseTypeName(SourceRange *Range, DeclaratorContext Context, if (Range) *Range = DeclaratorInfo.getSourceRange(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (LateAttrs) + DistributeCLateParsedAttrs(DeclaratorInfo, nullptr, LateAttrs); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (DeclaratorInfo.isInvalidType()) return true; @@ -324,11 +331,17 @@ void Parser::ParseGNUAttributes(ParsedAttributes &Attrs, FindLocsWithCommonFileID(PP, AttrTokLoc, Loc)) { CharSourceRange ExpansionRange = SM.getExpansionRange(AttrTokLoc); StringRef FoundName = - Lexer::getSourceText(ExpansionRange, SM, PP.getLangOpts()); + Lexer::getSourceText(ExpansionRange, SM, PP.getLangOpts()) + /* TO_UPSTREAM(BoundsSafety) ON */ + .split('(') + .first; + /* TO_UPSTREAM(BoundsSafety) OFF */ IdentifierInfo *MacroII = PP.getIdentifierInfo(FoundName); for (unsigned i = OldNumAttrs; i < Attrs.size(); ++i) - Attrs[i].setMacroIdentifier(MacroII, ExpansionRange.getBegin()); + Attrs[i].setMacroIdentifier(MacroII, ExpansionRange.getBegin(), + // TO_UPSTREAM(BoundsSafety) + SM.isInSystemMacro(AttrTokLoc)); if (LateAttrs) { for (unsigned i = OldNumLateAttrs; i < LateAttrs->size(); ++i) @@ -699,10 +712,14 @@ unsigned Parser::ParseAttributeArgsCommon( /// Parse the arguments to a parameterized GNU attribute or /// a C++11 attribute in "gnu" namespace. +/* TO_UPSTREAM(BoundsSafety) ON */ +// NestedTypeLevel parameter isn't present in upstream void Parser::ParseGNUAttributeArgs( IdentifierInfo *AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, - SourceLocation ScopeLoc, ParsedAttr::Form Form, Declarator *D) { + SourceLocation ScopeLoc, ParsedAttr::Form Form, Declarator *D, + size_t NestedTypeLevel) { + /* TO_UPSTREAM(BoundsSafety) OFF */ assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('"); @@ -733,12 +750,19 @@ void Parser::ParseGNUAttributeArgs( ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc, Form); return; + /* TO_UPSTREAM(BoundsSafety) ON */ + } else if (AttrKind == ParsedAttr::AT_PtrEndedBy) { + ParseBoundsAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc, + Form, NestedTypeLevel); + return; + /* TO_UPSTREAM(BoundsSafety) OFF */ } else if (AttrKind == ParsedAttr::AT_CountedBy || AttrKind == ParsedAttr::AT_CountedByOrNull || AttrKind == ParsedAttr::AT_SizedBy || AttrKind == ParsedAttr::AT_SizedByOrNull) { + // TO_UPSTREAM(BoundsSafety): `NestedTypeLevel` is not passed in upstream ParseBoundsAttribute(*AttrName, AttrNameLoc, Attrs, ScopeName, ScopeLoc, - Form); + Form, NestedTypeLevel); return; } else if (AttrKind == ParsedAttr::AT_CXXAssume) { ParseCXXAssumeAttributeArg(Attrs, AttrName, AttrNameLoc, EndLoc, Form); @@ -1305,6 +1329,55 @@ VersionTuple Parser::ParseVersionTuple(SourceRange &Range) { return VersionTuple(Major, Minor, Subminor); } +void Parser::ParseFeatureAvailabilityAttribute( + IdentifierInfo &Availability, SourceLocation AvailabilityLoc, + ParsedAttributes &attrs, SourceLocation *endLoc, IdentifierInfo *ScopeName, + SourceLocation ScopeLoc, ParsedAttr::Form Form, + BalancedDelimiterTracker &T) { + + if (Tok.isNot(tok::colon)) { + Diag(Tok, diag::err_expected) << tok::colon; + SkipUntil(tok::r_paren, StopAtSemi); + return; + } + + ConsumeToken(); + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_features_domain_name); + SkipUntil(tok::r_paren, StopAtSemi); + return; + } + + ArgsVector ArgExprs; + IdentifierLoc *Feature = ParseIdentifierLoc(); + ArgExprs.push_back(Feature); + + if (ExpectAndConsume(tok::comma)) { + SkipUntil(tok::r_paren, StopAtSemi); + return; + } + + if (Tok.isNot(tok::numeric_constant)) { + Diag(Tok, diag::err_expected) << tok::numeric_constant; + SkipUntil(tok::r_paren, StopAtSemi); + return; + } + + ExprResult ExprRes(Actions.ActOnNumericConstant(Tok, getCurScope())); + ArgExprs.push_back(ExprRes.get()); + + ConsumeToken(); + + // Closing ')'. + if (T.consumeClose()) + return; + + attrs.addNew(&Availability, + SourceRange(AvailabilityLoc, T.getCloseLocation()), ScopeName, + ScopeLoc, ArgExprs.data(), ArgExprs.size(), Form); +} + /// Parse the contents of the "availability" attribute. /// /// availability-attribute: @@ -1354,6 +1427,11 @@ void Parser::ParseAvailabilityAttribute( } IdentifierLoc *Platform = ParseIdentifierLoc(); if (const IdentifierInfo *const Ident = Platform->getIdentifierInfo()) { + if (!Ident->getName().compare("domain")) { + ParseFeatureAvailabilityAttribute(Availability, AvailabilityLoc, attrs, + endLoc, ScopeName, ScopeLoc, Form, T); + return; + } // Disallow xrOS for availability attributes. if (Ident->getName().contains("xrOS") || Ident->getName().contains("xros")) Diag(Platform->getLoc(), diag::warn_availability_unknown_platform) @@ -2133,7 +2211,13 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration( ParsedTemplateInfo TemplateInfo; DeclSpecContext DSContext = getDeclSpecContextFromDeclaratorContext(Context); - ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, DSContext); + /* TO_UPSTREAM(BoundsSafety) ON */ + // FIXME: Why is PSoon true? + LateParsedAttrList BoundsSafetyLateAttrs( + /*PSoon=*/true, /*LateAttrParseExperimentalExtOnly=*/true); + ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, DSContext, + &BoundsSafetyLateAttrs); + /* TO_UPSTREAM(BoundsSafety) OFF */ // If we had a free-standing type definition with a missing semicolon, we // may get this far before the problem becomes obvious. @@ -2165,7 +2249,9 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration( if (DeclSpecStart) DS.SetRangeStart(*DeclSpecStart); - return ParseDeclGroup(DS, Context, DeclAttrs, TemplateInfo, &DeclEnd, FRI); + return ParseDeclGroup(DS, Context, DeclAttrs, TemplateInfo, &DeclEnd, FRI, + // TO_UPSTREAM(BoundsSafety) + &BoundsSafetyLateAttrs); } /// Returns true if this might be the start of a declarator, or a common typo @@ -2324,7 +2410,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, ParsedAttributes &Attrs, ParsedTemplateInfo &TemplateInfo, SourceLocation *DeclEnd, - ForRangeInit *FRI) { + ForRangeInit *FRI, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *BoundsSafetyLateAttrs) { // Parse the first declarator. // Consume all of the attributes from `Attrs` by moving them to our own local // list. This ensures that we will not attempt to interpret them as statement @@ -2434,7 +2522,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // and recover by ignoring the 'template' keyword. Diag(Tok, diag::err_template_defn_explicit_instantiation) << 0; TheDecl = ParseFunctionDefinition(D, ParsedTemplateInfo(), - &LateParsedAttrs); + &LateParsedAttrs, + // TO_UPSTREAM(BoundsSafety) + BoundsSafetyLateAttrs); } else { SourceLocation LAngleLoc = PP.getLocForEndOfToken(TemplateInfo.TemplateLoc); @@ -2454,11 +2544,15 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, ParsedTemplateInfo(&FakedParamLists, /*isSpecialization=*/true, /*lastParameterListWasEmpty=*/true), - &LateParsedAttrs); + &LateParsedAttrs, + // TO_UPSTREAM(BoundsSafety) + BoundsSafetyLateAttrs); } } else { TheDecl = - ParseFunctionDefinition(D, TemplateInfo, &LateParsedAttrs); + ParseFunctionDefinition(D, TemplateInfo, &LateParsedAttrs, + // TO_UPSTREAM(BoundsSafety) + BoundsSafetyLateAttrs); } return Actions.ConvertDeclToDeclGroup(TheDecl); @@ -2549,6 +2643,19 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, SmallVector DeclsInGroup; Decl *FirstDecl = ParseDeclarationAfterDeclaratorAndAttributes(D, TemplateInfo, FRI); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Make sure unrelated attributes are not silently picked up by + // -fbounds-safety late parsing logic and ignored. + assert(getLangOpts().BoundsSafetyAttributes || (!BoundsSafetyLateAttrs || BoundsSafetyLateAttrs->size() == 0)); + + if (getLangOpts().BoundsSafetyAttributes && BoundsSafetyLateAttrs) { + DistributeCLateParsedAttrs(D, FirstDecl, BoundsSafetyLateAttrs); + if (BoundsSafetyLateAttrs->size() > 0) + ParseLexedCAttributeList(*BoundsSafetyLateAttrs, true); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (LateParsedAttrs.size() > 0) ParseLexedAttributeList(LateParsedAttrs, FirstDecl, true, false); D.complete(FirstDecl); @@ -2611,6 +2718,14 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, if (Tok.is(tok::kw_requires)) ParseTrailingRequiresClause(D); Decl *ThisDecl = ParseDeclarationAfterDeclarator(D, TemplateInfo); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafetyAttributes && BoundsSafetyLateAttrs) { + DistributeCLateParsedAttrs(D, ThisDecl, BoundsSafetyLateAttrs); + if (BoundsSafetyLateAttrs->size() > 0) + ParseLexedCAttributeList(*BoundsSafetyLateAttrs, true); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + D.complete(ThisDecl); if (ThisDecl) DeclsInGroup.push_back(ThisDecl); @@ -2949,12 +3064,23 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( /// void Parser::ParseSpecifierQualifierList( DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename, - AccessSpecifier AS, DeclSpecContext DSC) { - ParsedTemplateInfo TemplateInfo; + AccessSpecifier AS, DeclSpecContext DSC, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs) { /// specifier-qualifier-list is a subset of declaration-specifiers. Just /// parse declaration-specifiers and complain about extra stuff. /// TODO: diagnose attribute-specifiers and alignment-specifiers. - ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC, nullptr, + + /*TO_UPSTREAM(BoundsSafety) ON*/ + assert(((LateAttrs && getLangOpts().BoundsSafetyAttributes) + ? LateAttrs->lateAttrParseExperimentalExtOnly() + : true) && + "Experimental late parsing must be enabled for BoundsSafety"); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + + ParsedTemplateInfo TemplateInfo; + // TO_UPSTREAM(BoundsSafety): Pass LateAttrs + ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC, LateAttrs, AllowImplicitTypename); // Validate declspec for type-name. @@ -3388,7 +3514,8 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs, } } -void Parser::DistributeCLateParsedAttrs(Decl *Dcl, +// TO_UPSTREAM(BoundsSafety): Declator &D isn't passed in upstream +void Parser::DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl, LateParsedAttrList *LateAttrs) { if (!LateAttrs) return; @@ -3399,6 +3526,59 @@ void Parser::DistributeCLateParsedAttrs(Decl *Dcl, LateAttr->addDecl(Dcl); } } + + /* TO_UPSTREAM(BoundsSafety) ON */ + unsigned NestedLevel = 0; + struct FunctionChunkInfo { + bool Valid = false; + unsigned Index = 0; + } FuncInfo; + std::unique_ptr ProtoScope; + LateParsedAttrList ProtoLateAttrs(true); + + for (unsigned i = 0; i < D.getNumTypeObjects(); ++i) { + DeclaratorChunk &DC = D.getTypeObject(i); + + ArrayRef LPAI; + switch (DC.Kind) { + case DeclaratorChunk::Pointer: + LPAI = DC.Ptr.getLateParsedAttrInfos(); + break; + case DeclaratorChunk::Array: + LPAI = DC.Arr.getLateParsedAttrInfos(); + break; + case DeclaratorChunk::Function: + FuncInfo.Index = i; + FuncInfo.Valid = true; + continue; + default: + continue; + } + + if (!LPAI.empty() && FuncInfo.Valid && !ProtoScope) { + const DeclaratorChunk &FuncChunk = D.getTypeObject(FuncInfo.Index); + ProtoScope.reset(new ParseScope(this, Scope::FunctionPrototypeScope | + Scope::DeclScope)); + const DeclaratorChunk::FunctionTypeInfo &FTI = FuncChunk.Fun; + for (unsigned i = 0; i != FTI.NumParams; ++i) { + ParmVarDecl *Param = cast(FTI.Params[i].Param); + Actions.ActOnReenterCXXMethodParameter(getCurScope(), Param); + } + LateAttrs = &ProtoLateAttrs; + } + for (const auto *LI : LPAI) { + LateParsedAttribute *LA = new LateParsedAttribute( + this, LI->AttrName, LI->AttrNameLoc, NestedLevel); + LA->MacroII = std::move(LI->MacroII); + LA->Toks = std::move(LI->Toks); + if (Dcl) + LA->addDecl(Dcl); + LateAttrs->push_back(LA); + } + NestedLevel++; + } + ParseLexedCAttributeList(ProtoLateAttrs, false); + /* TO_UPSTREAM(BoundsSafety) OFF */ } /// type-qualifier: @@ -3447,7 +3627,8 @@ void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName, ParsedAttributes &Attrs, IdentifierInfo *ScopeName, SourceLocation ScopeLoc, - ParsedAttr::Form Form) { + ParsedAttr::Form Form, + unsigned NestedTypeLevel) { // TO_UPSTREAM(BoundsSafety) assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('"); BalancedDelimiterTracker Parens(*this, tok::l_paren); @@ -3460,6 +3641,10 @@ void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName, } ArgsVector ArgExprs; + /* TO_UPSTREAM(BoundsSafety) ON */ + // FIXME: Don't diagnose the argument when the attribute is ignored. + // Currently, the argument expressions are always diagnosed even if + /* TO_UPSTREAM(BoundsSafety) OFF */ // Don't evaluate argument when the attribute is ignored. using ExpressionKind = Sema::ExpressionEvaluationContextRecord::ExpressionKind; @@ -3480,9 +3665,12 @@ void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName, ASTContext &Ctx = Actions.getASTContext(); + /* TO_UPSTREAM(BoundsSafety) ON */ + // Pass NestedTypeLevel ArgExprs.push_back(IntegerLiteral::Create( - Ctx, llvm::APInt(Ctx.getTypeSize(Ctx.getSizeType()), 0), + Ctx, llvm::APInt(Ctx.getTypeSize(Ctx.getSizeType()), NestedTypeLevel), Ctx.getSizeType(), SourceLocation())); + /* TO_UPSTREAM(BoundsSafety) OFF */ Attrs.addNew(&AttrName, SourceRange(AttrNameLoc, Parens.getCloseLocation()), ScopeName, ScopeLoc, ArgExprs.data(), ArgExprs.size(), Form); @@ -4837,7 +5025,7 @@ void Parser::ParseDeclarationSpecifiers( // type qualifier. diagnoseUseOfC11Keyword(Tok); if (NextToken().is(tok::l_paren)) { - ParseAtomicSpecifier(DS); + ParseAtomicSpecifier(DS, /*TO_UPSTREAM(BoundsSafety)*/LateAttrs); continue; } isInvalid = DS.SetTypeQual(DeclSpec::TQ_atomic, Loc, PrevSpec, DiagID, @@ -4945,6 +5133,10 @@ void Parser::ParseDeclarationSpecifiers( static void DiagnoseCountAttributedTypeInUnnamedAnon(ParsingDeclSpec &DS, Parser &P) { + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (P.getLangOpts().BoundsSafetyAttributes) + return; + /*TO_UPSTREAM(BoundsSafety) OFF*/ if (DS.getTypeSpecType() != DeclSpec::TST_struct) return; @@ -5013,7 +5205,9 @@ void Parser::ParseStructDeclaration( MaybeParseCXX11Attributes(Attrs); // Parse the common specifier-qualifiers-list piece. - ParseSpecifierQualifierList(DS); + ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_normal, + // TO_UPSTREAM(BoundsSafety) + LateFieldAttrs); // If there are no declarators, this is a free-standing declaration // specifier. Let the actions module cope with it. @@ -5074,8 +5268,11 @@ void Parser::ParseStructDeclaration( // We're done with this declarator; invoke the callback. Decl *Field = FieldsCallback(DeclaratorInfo); + /* TO_UPSTREAM(BoundsSafety) ON */ + // Upstream doesn't pass `DeclaratorInfo.D`. if (Field) - DistributeCLateParsedAttrs(Field, LateFieldAttrs); + DistributeCLateParsedAttrs(DeclaratorInfo.D, Field, LateFieldAttrs); + /* TO_UPSTREAM(BoundsSafety) OFF */ // If we don't have a comma, it is either the end of the list (a ';') // or an error, bail out. @@ -5132,10 +5329,42 @@ void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope, assert(LA.Decls.size() <= 1 && "late field attribute expects to have at most one declaration."); - // Dispatch based on the attribute and parse it - ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, nullptr, nullptr, - SourceLocation(), ParsedAttr::Form::GNU(), nullptr); + /* TO_UPSTREAM(BoundsSafety) ON */ + Decl *D = LA.Decls.empty() ? nullptr : LA.Decls[0]; + // If the Decl is on a function, add function parameters to the scope. + { + // Support for adding function scope is not yet upstream. + std::unique_ptr Scope; + EnterScope &= D && D->isFunctionOrFunctionTemplate(); + if (EnterScope) { + Scope.reset(new ParseScope(this, Scope::FnScope | Scope::DeclScope)); + Actions.ActOnReenterFunctionContext(Actions.CurScope, D); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + + // Dispatch based on the attribute and parse it + /* TO_UPSTREAM(BoundsSafety) ON */ + // NestedTypeLevel is not passed in upstream + ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, nullptr, nullptr, + SourceLocation(), ParsedAttr::Form::GNU(), nullptr, + LA.NestedTypeLevel); + /* TO_UPSTREAM(BoundsSafety) OFF */ + + /* TO_UPSTREAM(BoundsSafety) ON */ + if (EnterScope) { + Actions.ActOnExitFunctionContext(); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + } + + if (LA.MacroII) { + const auto &SM = PP.getSourceManager(); + CharSourceRange ExpansionRange = SM.getExpansionRange(LA.AttrNameLoc); + for (unsigned i = 0; i < Attrs.size(); ++i) + Attrs[i].setMacroIdentifier(LA.MacroII, ExpansionRange.getBegin(), + SM.isInSystemMacro(LA.AttrNameLoc)); + } for (auto *D : LA.Decls) Actions.ActOnFinishDelayedAttribute(getCurScope(), D, Attrs); @@ -5288,13 +5517,21 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, // If attributes exist after struct contents, parse them. MaybeParseGNUAttributes(attrs, &LateFieldAttrs); - // Late parse field attributes if necessary. - ParseLexedCAttributeList(LateFieldAttrs, /*EnterScope=*/false); - + /* TO_UPSTREAM(BoundsSafety) ON */ SmallVector FieldDecls(TagDecl->fields()); - Actions.ActOnFields(getCurScope(), RecordLoc, TagDecl, FieldDecls, T.getOpenLocation(), T.getCloseLocation(), attrs); + /* TO_UPSTREAM(BoundsSafety) OFF */ + + // Late parse field attributes if necessary. + /* TO_UPSTREAM(BoundsSafety) ON */ + if (getLangOpts().CPlusPlus) { + // FIXME : Properly handle CXX structs + for (auto *LateAttr : LateFieldAttrs) + LateAttr->ParseLexedAttributes(); + } /* TO_UPSTREAM(BoundsSafety) OFF */ else + ParseLexedCAttributeList(LateFieldAttrs, /*EnterScope=*/false); + StructScope.Exit(); Actions.ActOnTagFinishDefinition(getCurScope(), TagDecl, T.getRange()); } @@ -6512,7 +6749,9 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide, void Parser::ParseTypeQualifierListOpt( DeclSpec &DS, unsigned AttrReqs, bool AtomicAllowed, bool IdentifierRequired, - std::optional> CodeCompletionHandler) { + std::optional> CodeCompletionHandler, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *LateAttrs) { if ((AttrReqs & AR_CXX11AttributesParsed) && isAllowedCXX11AttributeSpecifier()) { ParsedAttributes Attrs(AttrFactory); @@ -6573,6 +6812,12 @@ void Parser::ParseTypeQualifierListOpt( ParseOpenCLQualifiers(DS.getAttributes()); break; + // __ptrauth qualifier. + case tok::kw___ptrauth: + ParsePtrauthQualifier(DS.getAttributes()); + EndLoc = PrevTokLocation; + continue; + case tok::kw_groupshared: case tok::kw_in: case tok::kw_inout: @@ -6581,12 +6826,6 @@ void Parser::ParseTypeQualifierListOpt( ParseHLSLQualifiers(DS.getAttributes()); continue; - // __ptrauth qualifier. - case tok::kw___ptrauth: - ParsePtrauthQualifier(DS.getAttributes()); - EndLoc = PrevTokLocation; - continue; - case tok::kw___unaligned: isInvalid = DS.SetTypeQual(DeclSpec::TQ_unaligned, Loc, PrevSpec, DiagID, getLangOpts()); @@ -6652,7 +6891,14 @@ void Parser::ParseTypeQualifierListOpt( // recovery is graceful. if (AttrReqs & AR_GNUAttributesParsed || AttrReqs & AR_GNUAttributesParsedAndRejected) { - ParseGNUAttributes(DS.getAttributes()); + /*TO_UPSTREAM(BoundsSafety) ON*/ + assert(((LateAttrs && getLangOpts().BoundsSafetyAttributes) + ? LateAttrs->lateAttrParseExperimentalExtOnly() + : true) && + "Experimental late parsing must be enabled for BoundsSafety"); + // Upstream doesn't pass LateAttrs + ParseGNUAttributes(DS.getAttributes(), LateAttrs, nullptr); + /*TO_UPSTREAM(BoundsSafety) OFF*/ continue; // do *not* consume the next token! } // otherwise, FALL THROUGH! @@ -6724,6 +6970,16 @@ static bool isPipeDeclarator(const Declarator &D) { return false; } +/* TO_UPSTREAM(BoundsSafety) ON */ +// Late parsing for attributes on the type position is currently only supported +// with -fbounds-safety or -fbounds-safety-attributes. +// Checking `BoundsSafety` is redundant because -fbounds-safety-attributes +// is always implied with -fbounds-safety, but this is added for readability. +static bool enableTypeAttrLateParsing(const LangOptions &LangOpts) { + return LangOpts.BoundsSafety || LangOpts.hasBoundsSafetyAttributes(); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + /// ParseDeclaratorInternal - Parse a C or C++ declarator. The direct-declarator /// is parsed by the function passed to it. Pass null, and the direct-declarator /// isn't parsed at all, making this function effectively parse the C++ @@ -6859,20 +7115,80 @@ void Parser::ParseDeclaratorInternal(Declarator &D, ((D.getContext() != DeclaratorContext::CXXNew) ? AR_GNUAttributesParsed : AR_GNUAttributesParsedAndRejected); - ParseTypeQualifierListOpt(DS, Reqs, true, !D.mayOmitIdentifier()); + /* TO_UPSTREAM(BoundsSafety) ON */ + LateParsedAttrList LateAttrs(/*PSoon=*/true, + /*LateAttrParseExperimentalExtOnly=*/true); + LateParsedAttrList *LateAttrsPtr = + enableTypeAttrLateParsing(getLangOpts()) ? &LateAttrs : nullptr; + // Upstream doesn't pass LateAttrsPtr + ParseTypeQualifierListOpt(DS, Reqs, true, !D.mayOmitIdentifier(), + std::nullopt, LateAttrsPtr); + /* TO_UPSTREAM(BoundsSafety) OFF */ D.ExtendWithDeclSpec(DS); // Recursively parse the declarator. Actions.runWithSufficientStackSpace( D.getBeginLoc(), [&] { ParseDeclaratorInternal(D, DirectDeclParser); }); - if (Kind == tok::star) + if (Kind == tok::star) { + /* TO_UPSTREAM(BoundsSafety) ON */ + SmallVector LateAttrInfos; + // We parse '__counted_by' or '__ended_by' attributes immediately + // if this is a function declarator. We need these attributes to be + // parsed early so we can construct the full function type before Sema + // is checking and merging the function declaration with the previous + // declaration. + if (getLangOpts().hasBoundsSafetyAttributes() && + getLangOpts().ExperimentalLateParseAttributes && !LateAttrs.empty()) { + unsigned FuncIndex = 0; + if (D.isFunctionDeclarator(FuncIndex)) { + // Upstream doesn't support bounds safety attributes on functions yet + // so avoid taking this path when bounds safety is off. + DeclaratorChunk::FunctionTypeInfo FTI = D.getFunctionTypeInfo(); + ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope | + Scope::FunctionDeclarationScope | + Scope::DeclScope); + for (unsigned i = 0; i != FTI.NumParams; ++i) { + if (Decl *D = FTI.Params[i].Param) { + // Param can be missing if the declaration is malformed. The + // diagnostic is emitted later. + Actions.ActOnReenterCXXMethodParameter( + getCurScope(), cast(D)); + } + } + // Handling nested return type with counted_by, e.g.: + // T *__counted_by(x) *foo(size_t x); + unsigned NestedLevel = 0; + for (unsigned i = FuncIndex + 1; i != D.getNumTypeObjects(); ++i) { + if (D.getTypeObject(i).Kind == DeclaratorChunk::Pointer) + NestedLevel++; + } + + if (NestedLevel != 0) { + for (auto *LateAttr : LateAttrs) { + LateAttr->NestedTypeLevel = NestedLevel; + } + } + ParseLexedCAttributeList(LateAttrs, false, &D.getAttributes()); + } else { + for (auto LA : LateAttrs) { + auto LI = new DeclaratorChunk::LateParsedAttrInfo( + LA->Toks, LA->AttrName, LA->MacroII, LA->AttrNameLoc); + LateAttrInfos.push_back(LI); + delete LA; + } + } + LateAttrs.clear(); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ // Remember that we parsed a pointer type, and remember the type-quals. D.AddTypeInfo(DeclaratorChunk::getPointer( DS.getTypeQualifiers(), Loc, DS.getConstSpecLoc(), DS.getVolatileSpecLoc(), DS.getRestrictSpecLoc(), - DS.getAtomicSpecLoc(), DS.getUnalignedSpecLoc()), + DS.getAtomicSpecLoc(), DS.getUnalignedSpecLoc(), + // TO_UPSTREAM(BoundsSafety) + LateAttrInfos), std::move(DS.getAttributes()), SourceLocation()); - else + } else // Remember that we parsed a Block type, and remember the type-quals. D.AddTypeInfo( DeclaratorChunk::getBlockPointer(DS.getTypeQualifiers(), Loc), @@ -7683,6 +7999,11 @@ void Parser::ParseFunctionDeclarator(Declarator &D, LParenLoc = Tracker.getOpenLocation(); StartLoc = LParenLoc; + /* TO_UPSTREAM(BoundsSafety) ON */ + LateParsedAttrList LateParamAttrs(/*PSoon=*/false, + /*LateAttrParseExperimentalExtOnly=*/true); + /* TO_UPSTREAM(BoundsSafety) OFF */ + if (isFunctionDeclaratorIdentifierList()) { if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -7700,7 +8021,8 @@ void Parser::ParseFunctionDeclarator(Declarator &D, ProhibitAttributes(FnAttrs); } else { if (Tok.isNot(tok::r_paren)) - ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, EllipsisLoc); + ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, EllipsisLoc, + &LateParamAttrs); else if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -7802,6 +8124,12 @@ void Parser::ParseFunctionDeclarator(Declarator &D, } } + /* TO_UPSTREAM(BoundsSafety) ON*/ + for (auto LateParamAttr : LateParamAttrs) { + ParseLexedCAttribute(*LateParamAttr, true); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Collect non-parameter declarations from the prototype if this is a function // declaration. They will be moved into the scope of the function. Only do // this in C and not C++, where the decls will continue to live in the @@ -7977,7 +8305,8 @@ void Parser::ParseFunctionDeclaratorIdentifierList( void Parser::ParseParameterDeclarationClause( DeclaratorContext DeclaratorCtx, ParsedAttributes &FirstArgAttrs, SmallVectorImpl &ParamInfo, - SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration) { + SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration, + LateParsedAttrList *LateParamAttrs) { // Avoid exceeding the maximum function scope depth. // See https://bugs.llvm.org/show_bug.cgi?id=19607 @@ -8043,10 +8372,18 @@ void Parser::ParseParameterDeclarationClause( if (getLangOpts().CPlusPlus && Tok.is(tok::kw_this)) ThisLoc = ConsumeToken(); + /*TO_UPSTREAM(BoundsSafety) ON*/ + assert(((LateParamAttrs && getLangOpts().BoundsSafetyAttributes) + ? LateParamAttrs->lateAttrParseExperimentalExtOnly() + : true) && + "Experimental late parsing must be enabled for BoundsSafety"); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + ParsedTemplateInfo TemplateInfo; ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, DeclSpecContext::DSC_normal, - /*LateAttrs=*/nullptr, AllowImplicitTypename); + // TO_UPSTREAM(BoundsSafety): LateAttrs + LateParamAttrs, AllowImplicitTypename); DS.takeAttributesFrom(ArgDeclSpecAttrs); @@ -8065,7 +8402,13 @@ void Parser::ParseParameterDeclarationClause( ParmDeclarator.SetRangeBegin(ThisLoc); // Parse GNU attributes, if present. - MaybeParseGNUAttributes(ParmDeclarator); + /* TO_UPSTREAM(BoundsSafety) ON*/ + assert(((LateParamAttrs && getLangOpts().BoundsSafetyAttributes) + ? LateParamAttrs->lateAttrParseExperimentalExtOnly() + : true) && + "Experimental late parsing must be enabled for BoundsSafety"); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + MaybeParseGNUAttributes(ParmDeclarator, LateParamAttrs); if (getLangOpts().HLSL) MaybeParseHLSLAnnotations(DS.getAttributes()); @@ -8146,6 +8489,9 @@ void Parser::ParseParameterDeclarationClause( // added to the current scope. Decl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDeclarator, ThisLoc); + // TO_UPSTREAM(BoundsSafety) + DistributeCLateParsedAttrs(ParmDeclarator, Param, LateParamAttrs); + // Parse the default argument, if any. We parse the default // arguments in all dialects; the semantic analysis in // ActOnParamDefaultArgument will reject the default argument in @@ -8308,7 +8654,15 @@ void Parser::ParseBracketDeclarator(Declarator &D) { // If there is a type-qualifier-list, read it now. // Type qualifiers in an array subscript are a C99 feature. DeclSpec DS(AttrFactory); - ParseTypeQualifierListOpt(DS, AR_CXX11AttributesParsed); + /* TO_UPSTREAM(BoundsSafety) ON */ + LateParsedAttrList LateAttrs(/*PSoon=*/true, + /*LateAttrParseExperimentalExtOnly=*/true); + LateParsedAttrList *LateAttrsPtr = + enableTypeAttrLateParsing(getLangOpts()) ? &LateAttrs : nullptr; + ParseTypeQualifierListOpt(DS, + AR_CXX11AttributesParsed | AR_GNUAttributesParsed, + true, false, std::nullopt, LateAttrsPtr); + /* TO_UPSTREAM(BoundsSafety) OFF */ // If we haven't already read 'static', check to see if there is one after the // type-qualifier-list. @@ -8366,11 +8720,23 @@ void Parser::ParseBracketDeclarator(Declarator &D) { MaybeParseCXX11Attributes(DS.getAttributes()); + /* TO_UPSTREAM(BoundsSafety) ON */ + SmallVector LateAttrInfos; + for (auto LA : LateAttrs) { + auto LI = new DeclaratorChunk::LateParsedAttrInfo( + LA->Toks, LA->AttrName, LA->MacroII, LA->AttrNameLoc); + LateAttrInfos.push_back(LI); + delete LA; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Remember that we parsed a array type, and remember its features. D.AddTypeInfo( DeclaratorChunk::getArray(DS.getTypeQualifiers(), StaticLoc.isValid(), isStar, NumElements.get(), T.getOpenLocation(), - T.getCloseLocation()), + T.getCloseLocation(), + // TO_UPSTREAM(BoundsSafety) + LateAttrInfos), std::move(DS.getAttributes()), T.getCloseLocation()); } @@ -8430,10 +8796,15 @@ void Parser::ParseMisplacedBracketDeclarator(Declarator &D) { } // Adding back the bracket info to the end of the Declarator. - for (unsigned i = 0, e = TempDeclarator.getNumTypeObjects(); i < e; ++i) { - const DeclaratorChunk &Chunk = TempDeclarator.getTypeObject(i); - D.AddTypeInfo(Chunk, TempDeclarator.getAttributePool(), SourceLocation()); - } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafetyAttributes) + D.TakeTypeObjects(TempDeclarator); + else + /* TO_UPSTREAM(BoundsSafety) OFF*/ + for (unsigned i = 0, e = TempDeclarator.getNumTypeObjects(); i < e; ++i) { + const DeclaratorChunk &Chunk = TempDeclarator.getTypeObject(i); + D.AddTypeInfo(Chunk, TempDeclarator.getAttributePool(), SourceLocation()); + } // The missing identifier would have been diagnosed in ParseDirectDeclarator. // If parentheses are required, always suggest them. @@ -8551,7 +8922,8 @@ void Parser::ParseTypeofSpecifier(DeclSpec &DS) { /// [C11] atomic-specifier: /// _Atomic ( type-name ) /// -void Parser::ParseAtomicSpecifier(DeclSpec &DS) { +// TO_UPSTREAM(BoundsSafety): LateAttrs +void Parser::ParseAtomicSpecifier(DeclSpec &DS, LateParsedAttrList *LateAttrs) { assert(Tok.is(tok::kw__Atomic) && NextToken().is(tok::l_paren) && "Not an atomic specifier"); @@ -8560,7 +8932,11 @@ void Parser::ParseAtomicSpecifier(DeclSpec &DS) { if (T.consumeOpen()) return; - TypeResult Result = ParseTypeName(); + TypeResult Result = + ParseTypeName(/*Range=*/nullptr, DeclaratorContext::TypeName, AS_none, + /*OwnedType=*/nullptr, /*Attrs=*/nullptr, + // TO_UPSTREAM(BoundsSafety) + LateAttrs); if (Result.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return; @@ -8681,6 +9057,7 @@ TypeResult Parser::ParseTypeFromString(StringRef TypeStr, StringRef Context, // Form a new lexer that references the buffer. Lexer L(FID, SourceMgr.getBufferOrFake(FID), PP); L.setParsingPreprocessorDirective(true); + L.setIsPragmaLexer(true); // Lex the tokens from that buffer. Token Tok; diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 8dfc0fa53dd88..45b112a48f743 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -3369,8 +3369,10 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration( // initialize it. ThisDecl = VT->getTemplatedDecl(); - if (ThisDecl) + if (ThisDecl) { Actions.ProcessDeclAttributeList(getCurScope(), ThisDecl, AccessAttrs); + Actions.ProcessAPINotes(ThisDecl); + } } // Error recovery might have converted a non-static member into a static @@ -3435,6 +3437,14 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration( for (unsigned i = 0, ni = LateParsedAttrs.size(); i < ni; ++i) LateParsedAttrs[i]->addDecl(ThisDecl); + + /* TO_UPSTREAM(BoundsSafety) ON */ + LateParsedAttrList LateCAttrs; + + DistributeCLateParsedAttrs(DeclaratorInfo, ThisDecl, &LateCAttrs); + for (auto *LCA : LateCAttrs) + getCurrentClass().LateParsedDeclarations.push_back(LCA); + /* TO_UPSTREAM(BoundsSafety) OFF */ } Actions.FinalizeDeclaration(ThisDecl); DeclsInGroup.push_back(ThisDecl); @@ -3743,7 +3753,8 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclarationWithPragmas( return nullptr; } ParsedTemplateInfo TemplateInfo; - return ParseCXXClassMemberDeclaration(AS, AccessAttrs, TemplateInfo); + return ParseCXXClassMemberDeclaration(AS, AccessAttrs, TemplateInfo, + /*TemplateDiags=*/nullptr); } } @@ -3964,7 +3975,6 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, Actions.ActOnFinishCXXMemberSpecification(getCurScope(), RecordLoc, TagDecl, T.getOpenLocation(), T.getCloseLocation(), attrs); - // C++11 [class.mem]p2: // Within the class member-specification, the class is regarded as complete // within function bodies, default arguments, exception-specifications, and diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index a5dd08529cf37..0e5070e9a5b6d 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1591,6 +1591,44 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, *NotPrimaryExpression = true; Res = ParseBuiltinBitCast(); break; + /* TO_UPSTREAM(BoundsSafety) ON*/ + case tok::kw___builtin_unsafe_forge_bidi_indexable: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + Res = ParseUnsafeForgeBidiIndexable(); + break; + case tok::kw___builtin_unsafe_forge_single: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + Res = ParseUnsafeForgeSingle(); + break; + case tok::kw___builtin_unsafe_forge_terminated_by: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + Res = ParseUnsafeForgeTerminatedBy(); + break; + case tok::kw___builtin_get_pointer_lower_bound: + case tok::kw___builtin_get_pointer_upper_bound: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + Res = ParseGetPointerBound( + SavedKind == tok::kw___builtin_get_pointer_lower_bound + ? PBK_Lower : PBK_Upper); + break; + case tok::kw___builtin_terminated_by_to_indexable: + case tok::kw___builtin_unsafe_terminated_by_to_indexable: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + Res = ParseTerminatedByToIndexable( + /*Unsafe=*/SavedKind == + tok::kw___builtin_unsafe_terminated_by_to_indexable); + break; + case tok::kw___builtin_unsafe_terminated_by_from_indexable: + if (NotPrimaryExpression) + *NotPrimaryExpression = true; + Res = ParseUnsafeTerminatedByFromIndexable(); + break; + /* TO_UPSTREAM(BoundsSafety) OFF*/ case tok::kw_typeid: if (NotPrimaryExpression) *NotPrimaryExpression = true; @@ -3946,6 +3984,15 @@ static bool CheckAvailabilitySpecList(Parser &P, bool HasOtherPlatformSpec = false; bool Valid = true; for (const auto &Spec : AvailSpecs) { + if (Spec.isDomainName()) { + if (AvailSpecs.size() != 1) { + P.Diag(Spec.getBeginLoc(), + diag::err_feature_invalid_availability_check); + return true; + } + return false; + } + if (Spec.isOtherPlatformSpec()) { if (HasOtherPlatformSpec) { P.Diag(Spec.getBeginLoc(), diag::err_availability_query_repeated_star); @@ -3993,6 +4040,23 @@ std::optional Parser::ParseAvailabilitySpec() { Actions.CodeCompletion().CodeCompleteAvailabilityPlatformName(); return std::nullopt; } + + if (Tok.is(tok::identifier) && GetLookAheadToken(1).is(tok::colon)) { + if (ParseIdentifierLoc()->getIdentifierInfo()->getName() != "domain") { + return std::nullopt; + } + + ConsumeToken(); // colon + + if (Tok.isNot(tok::identifier)) { + return std::nullopt; + } + + IdentifierLoc *DomainIdentifier = ParseIdentifierLoc(); + return AvailabilitySpec(DomainIdentifier->getIdentifierInfo()->getName(), + DomainIdentifier->getLoc()); + } + if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_avail_query_expected_platform_name); return std::nullopt; @@ -4060,3 +4124,188 @@ ExprResult Parser::ParseAvailabilityCheckExpr(SourceLocation BeginLoc) { return Actions.ObjC().ActOnObjCAvailabilityCheckExpr( AvailSpecs, BeginLoc, Parens.getCloseLocation()); } + +/* TO_UPSTREAM(BoundsSafety) ON*/ +/// BoundsSafety: Parse __builtin_unsafe_forge_bidi_indexable(ptr, size). +ExprResult Parser::ParseUnsafeForgeBidiIndexable() { + SourceLocation KWLoc = ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, "__builtin_unsafe_forge_bidi_indexable")) + return ExprError(); + + ExprResult Addr = ParseAssignmentExpression(); + if (Addr.isInvalid()) + return ExprError(); + + if (ExpectAndConsume(tok::comma)) { + Diag(Tok.getLocation(), diag::err_expected) << tok::comma; + SkipUntil(tok::r_paren, StopAtSemi); + return ExprError(); + } + + ExprResult Size = ParseAssignmentExpression(); + if (Size.isInvalid()) + return ExprError(); + + if (T.consumeClose()) + return ExprError(); + + return Actions.ActOnForgeBidiIndexable(KWLoc, Addr.get(), Size.get(), + T.getCloseLocation()); +} + +/// BoundsSafety: Parse __builtin_unsafe_single(ptr). +ExprResult Parser::ParseUnsafeForgeSingle() { + SourceLocation KWLoc = ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, "__builtin_unsafe_forge_single")) + return ExprError(); + + ExprResult Addr = ParseAssignmentExpression(); + if (Addr.isInvalid()) + return ExprError(); + + if (T.consumeClose()) + return ExprError(); + + return Actions.ActOnForgeSingle(KWLoc, Addr.get(), T.getCloseLocation()); +} + +/// BoundsSafety: Parse __builtin_unsafe_forge_terminated_by(ptr, terminator). +ExprResult Parser::ParseUnsafeForgeTerminatedBy() { + SourceLocation KWLoc = ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, + "__builtin_unsafe_forge_terminated_by")) + return ExprError(); + + ExprResult Addr = ParseAssignmentExpression(); + if (Addr.isInvalid()) + return ExprError(); + + if (ExpectAndConsume(tok::comma)) { + Diag(Tok.getLocation(), diag::err_expected) << tok::comma; + SkipUntil(tok::r_paren, StopAtSemi); + return ExprError(); + } + + ExprResult Term = ParseConstantExpression(); + if (Term.isInvalid()) + return ExprError(); + + if (T.consumeClose()) + return ExprError(); + + return Actions.ActOnForgeTerminatedBy(KWLoc, Addr.get(), Term.get(), + T.getCloseLocation()); +} + +ExprResult Parser::ParseGetPointerBound(PointerBoundKind K) { + SourceLocation KWLoc = ConsumeToken(); + ExprResult (Sema::*ActOnBoundExpr)(Expr *, SourceLocation, SourceLocation); + const char *ExpectName; + + if (K == PBK_Lower) { + ExpectName = "__builtin_get_pointer_lower_bound"; + ActOnBoundExpr = &Sema::ActOnGetLowerBound; + } else { + ExpectName = "__builtin_get_pointer_upper_bound"; + ActOnBoundExpr = &Sema::ActOnGetUpperBound; + } + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, ExpectName)) + return ExprError(); + + ExprResult Pointer = ParseAssignmentExpression(); + if (Pointer.isInvalid()) + return ExprError(); + + if (T.consumeClose()) + return ExprError(); + + return (Actions.*ActOnBoundExpr)(Pointer.get(), KWLoc, T.getCloseLocation()); +} + +/// BoundsSafety: +/// __builtin_terminated_by_to_indexable(pointer [, terminator]) +/// __builtin_unsafe_terminated_by_to_indexable(pointer [, terminator]) +ExprResult Parser::ParseTerminatedByToIndexable(bool Unsafe) { + SourceLocation KWLoc = ConsumeToken(); + + const char *ExpectName; + ExprResult (Sema::*Act)(Expr * PointerExpr, Expr * TerminatorExpr, + SourceLocation BuiltinLoc, SourceLocation RParenLoc); + if (Unsafe) { + ExpectName = "__builtin_unsafe_terminated_by_to_indexable"; + Act = &Sema::ActOnUnsafeTerminatedByToIndexable; + } else { + ExpectName = "__builtin_terminated_by_to_indexable"; + Act = &Sema::ActOnTerminatedByToIndexable; + } + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, ExpectName)) + return ExprError(); + + ExprResult Pointer = ParseAssignmentExpression(); + if (Pointer.isInvalid()) + return ExprError(); + + Expr *Terminator = nullptr; + if (TryConsumeToken(tok::comma)) { + ExprResult Res = ParseAssignmentExpression(); + if (Res.isInvalid()) + return ExprError(); + Terminator = Res.get(); + } + + if (T.consumeClose()) + return ExprError(); + + return (Actions.*Act)(Pointer.get(), Terminator, KWLoc, T.getCloseLocation()); +} + +/// BoundsSafety: Parse __builtin_unsafe_terminated_by_from_indexable(terminator, +/// pointer [, pointer-to-terminator]). +ExprResult Parser::ParseUnsafeTerminatedByFromIndexable() { + SourceLocation KWLoc = ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume(diag::err_expected_lparen_after, + "__builtin_unsafe_terminated_by_from_indexable")) + return ExprError(); + + ExprResult Terminator = ParseAssignmentExpression(); + if (Terminator.isInvalid()) + return ExprError(); + + if (ExpectAndConsume(tok::comma)) { + Diag(Tok.getLocation(), diag::err_expected) << tok::comma; + SkipUntil(tok::r_paren, StopAtSemi); + return ExprError(); + } + + ExprResult Pointer = ParseAssignmentExpression(); + if (Pointer.isInvalid()) + return ExprError(); + + Expr *PointerToTerminator = nullptr; + if (TryConsumeToken(tok::comma)) { + ExprResult Res = ParseAssignmentExpression(); + if (Res.isInvalid()) + return ExprError(); + PointerToTerminator = Res.get(); + } + + if (T.consumeClose()) + return ExprError(); + + return Actions.ActOnUnsafeTerminatedByFromIndexable( + Terminator.get(), Pointer.get(), PointerToTerminator, KWLoc, + T.getCloseLocation()); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 6496b4fba54f2..3633f546be5f3 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -234,6 +234,8 @@ void Parser::CheckNestedObjCContexts(SourceLocation AtLoc) /// __attribute__((unavailable)) /// __attribute__((objc_exception)) - used by NSException on 64-bit /// __attribute__((objc_root_class)) +/// __attribute__((objc_subclassing_restricted)) +/// __attribute__((objc_complete_definition)) /// Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc, ParsedAttributes &attrs) { @@ -2941,27 +2943,26 @@ ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) { return ParsePostfixExpressionSuffix(ParseObjCSelectorExpression(AtLoc)); case tok::objc_available: return ParseAvailabilityCheckExpr(AtLoc); - default: { - const char *str = nullptr; - // Only provide the @try/@finally/@autoreleasepool fixit when we're sure - // that this is a proper statement where such directives could actually - // occur. - if (GetLookAheadToken(1).is(tok::l_brace) && - ExprStatementTokLoc == AtLoc) { - char ch = Tok.getIdentifierInfo()->getNameStart()[0]; - str = - ch == 't' ? "try" - : (ch == 'f' ? "finally" - : (ch == 'a' ? "autoreleasepool" : nullptr)); - } - if (str) { - SourceLocation kwLoc = Tok.getLocation(); - return ExprError(Diag(AtLoc, diag::err_unexpected_at) << - FixItHint::CreateReplacement(kwLoc, str)); - } - else - return ExprError(Diag(AtLoc, diag::err_unexpected_at)); + default: { + const char *str = nullptr; + // Only provide the @try/@finally/@autoreleasepool fixit when we're sure + // that this is a proper statement where such directives could actually + // occur. + if (GetLookAheadToken(1).is(tok::l_brace) && + ExprStatementTokLoc == AtLoc) { + char ch = Tok.getIdentifierInfo()->getNameStart()[0]; + str = ch == 't' + ? "try" + : (ch == 'f' ? "finally" + : (ch == 'a' ? "autoreleasepool" : nullptr)); } + if (str) { + SourceLocation kwLoc = Tok.getLocation(); + return ExprError(Diag(AtLoc, diag::err_unexpected_at) + << FixItHint::CreateReplacement(kwLoc, str)); + } else + return ExprError(Diag(AtLoc, diag::err_unexpected_at)); + } } } } diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index a610e56760328..02af23ba636a5 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -484,6 +484,9 @@ Parser::ParseScopeFlags::~ParseScopeFlags() { //===----------------------------------------------------------------------===// Parser::~Parser() { + // Clear out the parse-type-from-string callback. + Actions.ParseTypeFromStringCallback = nullptr; + // If we still have scopes active, delete the scope tree. delete getCurScope(); Actions.CurScope = nullptr; @@ -1174,8 +1177,13 @@ Parser::DeclGroupPtrTy Parser::ParseDeclOrFunctionDefInternal( ParsedTemplateInfo TemplateInfo; MaybeParseMicrosoftAttributes(DS.getAttributes()); // Parse the common declaration-specifiers piece. + /* TO_UPSTREAM(BoundsSafety) ON */ + LateParsedAttrList BoundsSafetyLateAttrs( + /*PSoon=*/true, /*LateAttrParseExperimentalExtOnly=*/true); ParseDeclarationSpecifiers(DS, TemplateInfo, AS, - DeclSpecContext::DSC_top_level); + DeclSpecContext::DSC_top_level, + &BoundsSafetyLateAttrs); + /* TO_UPSTREAM(BoundsSafety) OFF */ // If we had a free-standing type definition with a missing semicolon, we // may get this far before the problem becomes obvious. @@ -1270,7 +1278,11 @@ Parser::DeclGroupPtrTy Parser::ParseDeclOrFunctionDefInternal( return Actions.ConvertDeclToDeclGroup(TheDecl); } - return ParseDeclGroup(DS, DeclaratorContext::File, Attrs, TemplateInfo); + return ParseDeclGroup(DS, DeclaratorContext::File, Attrs, TemplateInfo, + /*DeclEnd=*/nullptr, + /*FRI=*/nullptr, + // TO_UPSTREAM(BoundsSafety) + &BoundsSafetyLateAttrs); } Parser::DeclGroupPtrTy Parser::ParseDeclarationOrFunctionDefinition( @@ -1312,7 +1324,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclarationOrFunctionDefinition( /// Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, const ParsedTemplateInfo &TemplateInfo, - LateParsedAttrList *LateParsedAttrs) { + LateParsedAttrList *LateParsedAttrs, + // TO_UPSTREAM(BoundsSafety) + LateParsedAttrList *BoundsSafetyLateAttrs) { llvm::TimeTraceScope TimeScope("ParseFunctionDefinition", [&]() { return Actions.GetNameForDeclarator(D).getName().getAsString(); }); @@ -1542,7 +1556,16 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, } else Actions.ActOnDefaultCtorInitializers(Res); + /*TO_UPSTREAM(BoundsSafety) ON*/ // Late attributes are parsed in the same scope as the function body. + LateParsedAttrList BoundsSafetyAttrList(true); + if (!BoundsSafetyLateAttrs) + BoundsSafetyLateAttrs = &BoundsSafetyAttrList; + DistributeCLateParsedAttrs(D, Res, BoundsSafetyLateAttrs); + if (BoundsSafetyLateAttrs->size() > 0) + ParseLexedCAttributeList(*BoundsSafetyLateAttrs, false); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (LateParsedAttrs) ParseLexedAttributeList(*LateParsedAttrs, Res, false, true); diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 2418aaf8de8e6..2b9acd8504a38 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -13,6 +13,8 @@ //===----------------------------------------------------------------------===// #include "clang/Sema/AnalysisBasedWarnings.h" +// TO_UPSTREAM(BoundsSafety) +#include "clang/Sema/BoundsSafetySuggestions.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -2373,6 +2375,49 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { } } + /* TO_UPSTREAM(BoundsSafety) ON */ + void handleUnsafeCountAttributedPointerArgument(const CallExpr *Call, + const Expr *Arg, + bool IsRelatedToDecl, + ASTContext &Ctx) override { + const FunctionDecl *FD = Call->getDirectCallee(); + const auto *FPT = FD->getType()->getAs(); + + const auto ArgIt = std::find(Call->arg_begin(), Call->arg_end(), Arg); + assert(ArgIt != Call->arg_end()); + const unsigned ArgIndex = ArgIt - Call->arg_begin(); + + const auto *CATy = + FPT->getParamType(ArgIndex)->getAs(); + + // For __counted_by(C) where C is just a DRE, we recommend passing '.data()' + // to the pointer and '.size()' to the count parameter. For other C, don't + // recommend it. + bool IsSimpleCount = + CATy->getNumCoupledDecls() == 1 && + isa(CATy->getCountExpr()->IgnoreParenImpCasts()); + + const ParmVarDecl *PVD = FD->getParamDecl(ArgIndex); + + StringRef PtrParamName = PVD->getName(); + StringRef CountParamName = + IsSimpleCount ? CATy->dependent_decls().begin()->getDecl()->getName() + : ""; + + S.Diag(Arg->getBeginLoc(), + diag::warn_unsafe_count_attributed_pointer_argument); + S.Diag(PVD->getBeginLoc(), + diag::note_unsafe_count_attributed_pointer_argument) + << IsSimpleCount << QualType(CATy, 0) << !PtrParamName.empty() + << PtrParamName << CountParamName; + } + + void handleUnsafeSinglePointerArgument(const Expr *Arg, bool IsRelatedToDecl, + ASTContext &Ctx) override { + S.Diag(Arg->getBeginLoc(), diag::warn_unsafe_single_pointer_argument); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void handleUnsafeVariableGroup(const VarDecl *Variable, const VariableGroupsManager &VarGrpMgr, FixItList &&Fixes, const Decl *D, @@ -2462,6 +2507,577 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { }; } // namespace +/* TO_UPSTREAM(BoundsSafety) ON*/ +//===----------------------------------------------------------------------===// +// -fbounds-safety suggestions. +//===----------------------------------------------------------------------===// + +class BoundsSafetySuggestionReporter : public BoundsSafetySuggestionHandler { +public: + BoundsSafetySuggestionReporter(Sema &S) : S(S) {} + +private: + Sema &S; + + int UnsafeOpToSelectIndex(UnsafeOpKind Kind) { + // Don't trust the enum to stay in sync with our %select (layering). + switch (Kind) { + case UnsafeOpKind::Index: + return 0; + case UnsafeOpKind::Arithmetic: + return 1; + case UnsafeOpKind::Deref: + return 2; + case UnsafeOpKind::MemberAccess: + return 3; + case UnsafeOpKind::Assignment: + return 4; + case UnsafeOpKind::Return: + return 5; + case UnsafeOpKind::CallArg: + return 6; + case UnsafeOpKind::Cast: + return 7; + } + llvm_unreachable("Unhandled UnsafeOpKind"); + } + + int WillTrapKindSelectIndex(WillTrapKind Kind) { + switch (Kind) { + case WillTrapKind::NoTrap: + return 0; + case WillTrapKind::Unknown: + return 1; + case WillTrapKind::Trap: + return 2; + case WillTrapKind::TrapIffPtrNotNull: + return 3; + case WillTrapKind::TrapIffPtrNull: + return 4; + } + llvm_unreachable("Unhandled WillTrapKind"); + } + + int PtrArithOOBKindSelectIndex(PtrArithOOBKind Kind) { + switch (Kind) { + case PtrArithOOBKind::ALWAYS_OOB_BASE_OOB: + return 0; + case PtrArithOOBKind::ALWAYS_OOB_CONSTANT_OFFSET: + return 1; + case PtrArithOOBKind:: + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET: + return 2; + case PtrArithOOBKind:: + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET_OR_LT_ZERO: + return 3; + case PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_LT_ZERO: + return 4; + case PtrArithOOBKind::UNKNOWN: + case PtrArithOOBKind::NEVER_OOB: + llvm_unreachable("Should not emit diagnostic with this kind"); + } + llvm_unreachable("Unhandled PtrArithOOBKind"); + } + + int AssignmentExprToSelectIndex(const Expr *AssignmentExpr) { + if (AssignmentExpr) { + return 0; // "assigned" + } + return 1; // "initialized" + } + + void emitSingleEntityDeclaredHereNote(const SingleEntity Entity) { + switch (Entity.Kind) { + case AssignmentSourceKind::Parameter: + case AssignmentSourceKind::GlobalVar: + case AssignmentSourceKind::LocalVar: + S.Diag(Entity.Entity->getBeginLoc(), diag::note_pointer_declared_here) + << Entity.Entity; + break; + case AssignmentSourceKind::FunctionCallReturnValue: + case AssignmentSourceKind::ArrayElement: + S.Diag(Entity.Entity->getBeginLoc(), diag::note_entity_declared_at) + << Entity.Entity; + break; + case AssignmentSourceKind::StructMember: + case AssignmentSourceKind::UnionMember: + // Use qualified name so diagnostic consumer knowns which struct the + // member came from. + S.Diag(Entity.Entity->getBeginLoc(), diag::note_entity_declared_at) + << Entity.Entity->getQualifiedNameAsString(); + break; + } + } + + void EmitSingleEntityAssignedToPointerHereNote( + const SingleEntity &Entity, const VarDecl *IndexableLocalVar, + const Stmt *UnsafeOp, UnsafeOpKind Kind, const Expr *AssignmentExpr, + const QualType UnsafeOpPointerTy, bool IdentifyEntityKind) { + + bool IsInit = AssignmentExpr == IndexableLocalVar->getInit(); + + // Check if the pointee type sizes match. If they don't we'll want to emit + // a different diagnostic that notes the type sizes. + const auto UnsafeOpPointeeTy = UnsafeOpPointerTy->getPointeeType(); + const auto IndexableLocalVarPointeeTy = + IndexableLocalVar->getType()->getAs()->getPointeeType(); + + bool PointeeTypesSizesMatch = + S.getASTContext().getTypeSizeOrNull(IndexableLocalVarPointeeTy) == + S.getASTContext().getTypeSizeOrNull(UnsafeOpPointeeTy); + + if (PointeeTypesSizesMatch) { + if (IdentifyEntityKind) { + // TODO(dliew): We should make a distinction between initialization + // and assignment here (rdar://122940682). + S.Diag(AssignmentExpr->getBeginLoc(), + diag::note_single_entity_assigned_here) + << Entity.Kind << Entity.Entity << IndexableLocalVar + << AssignmentExpr->getSourceRange(); + return; + } + S.Diag(AssignmentExpr->getBeginLoc(), + IsInit ? diag::note_pointer_initialized_here + : diag::note_pointer_assigned_here) + << IndexableLocalVar; + return; + } + + // The Pointee sizes don't match. Use a diagnostic that explicitly states + // the size of the different pointee types in bytes. + // The diagnostic text will mention the type of the cast. We make the + // assumption that if the pointee sizes are different that there must be a + // cast somewhere. + int IsInitSelect = IsInit ? 1 : 0; + + S.Diag(AssignmentExpr->getBeginLoc(), + diag::note_single_entity_assigned_here_with_pointee_sizes) + << /*0*/ Entity.Kind << /*1*/ Entity.Entity << /*2*/ IndexableLocalVar + << /*3*/ Entity.SinglePointeeTy + << /*4*/ S.getASTContext() + .getTypeSizeInChars(Entity.SinglePointeeTy) + .getQuantity() + << /*5*/ UnsafeOpPointeeTy << /*6*/ S.getASTContext() + .getTypeSizeInChars(UnsafeOpPointeeTy) + .getQuantity() + << /*7*/ 1 /*Cast of IndexableLocalVar has UnsafeOpPointerTy type*/ + << /*8*/ UnsafeOpPointerTy << /*9*/ IsInitSelect + << AssignmentExpr->getSourceRange(); + } + + void handleSingleEntityFlowingToIndexableVariable( + const SingleEntity Entity, const VarDecl *IndexableLocalVar, + const Stmt *UnsafeOp, UnsafeOpKind Kind, const QualType UnsafeOpPtrTy, + PtrArithOOBKind PtrArithIsOOB = PtrArithOOBKind::UNKNOWN, + size_t MinimumPtrArithOOBOffset = 0) { + const Expr *AssignmentExpr = Entity.AssignmentExpr; + int OpSelect = UnsafeOpToSelectIndex(Kind); + int IsInitialization = AssignmentExprToSelectIndex(AssignmentExpr); + int PtrArithOOBSelect = PtrArithOOBKindSelectIndex(PtrArithIsOOB); + + if (Kind == UnsafeOpKind::Index && + PtrArithIsOOB == PtrArithOOBKind::ALWAYS_OOB_BASE_OOB) { + // This case is handled by + // `handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB` + llvm_unreachable("This method should not handle this case"); + } + + S.Diag(UnsafeOp->getBeginLoc(), + diag::warn_bounds_safety_conv_single_to_implicit_indexable) + << Entity.Kind << OpSelect << IndexableLocalVar << IsInitialization + << Entity.Entity << PtrArithOOBSelect << MinimumPtrArithOOBOffset + << UnsafeOp->getSourceRange(); + + if (AssignmentExpr) { + // Pointer is assigned to. Report the assignment location and where + // the pointer is declared. + EmitSingleEntityAssignedToPointerHereNote( + Entity, IndexableLocalVar, UnsafeOp, Kind, AssignmentExpr, + UnsafeOpPtrTy, /*IdentifyEntityKind=*/false); + + S.Diag(IndexableLocalVar->getBeginLoc(), diag::note_pointer_declared_here) + << IndexableLocalVar; + } else { + // The pointer was initialized. + EmitSingleEntityAssignedToPointerHereNote( + Entity, IndexableLocalVar, UnsafeOp, Kind, + IndexableLocalVar->getInit(), UnsafeOpPtrTy, + /*IdentifyEntityKind=*/false); + + // Don't report diag::note_pointer_declared_here because the + // initialization and declaration location will be very similar + } + + emitSingleEntityDeclaredHereNote(Entity); + } + + void handleSingleEntitiesFlowingToIndexableVariableImpl( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + const QualType UnsafeOpPtrTy, + PtrArithOOBKind PtrArithIsOOB = PtrArithOOBKind::UNKNOWN, + size_t MinimumPtrArithOOBOffset = 0) { + int OpSelect = UnsafeOpToSelectIndex(Kind); + int PtrArithOOBSelect = PtrArithOOBKindSelectIndex(PtrArithIsOOB); + + if (Kind == UnsafeOpKind::Index && + PtrArithIsOOB == PtrArithOOBKind::ALWAYS_OOB_BASE_OOB) { + // This case is handled by + // `handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB` + llvm_unreachable("This method should not handle this case"); + } + + S.Diag( + UnsafeOp->getBeginLoc(), + diag:: + warn_bounds_safety_conv_single_to_implicit_indexable_multiple_assignments) + << OpSelect << IndexableLocalVar << PtrArithOOBSelect + << MinimumPtrArithOOBOffset << UnsafeOp->getSourceRange(); + + // Note where the __bidi_indexable local var is declared. + S.Diag(IndexableLocalVar->getBeginLoc(), diag::note_pointer_declared_here) + << IndexableLocalVar; + + // Emit a note for each assignment to the __bidi_indexable variable. + // Currently `UnsafeOperationVisitor` doesn't use happens-before so + // some of the assignments noted here might be false positives. The warning + // diagnostic itself is much less likely to be a false positive because + // all the assignments are __single. + for (const auto &Entity : Entities) { + // Get the expression representing the assignment/initialization. + const Expr *AssignmentExpr = Entity.AssignmentExpr; + if (!AssignmentExpr) { + // The assignment is from the variable initializer + AssignmentExpr = IndexableLocalVar->getInit(); + } + assert(AssignmentExpr); + + EmitSingleEntityAssignedToPointerHereNote( + Entity, IndexableLocalVar, UnsafeOp, Kind, AssignmentExpr, + UnsafeOpPtrTy, /*IdentifyEntityKind=*/true); + + emitSingleEntityDeclaredHereNote(Entity); + } + } + bool IsReallySinglePtr(const QualType Ty) const { + if (!Ty->isSinglePointerType()) + return false; + + // Unfortunately __counted_by and friends, and __terminated_by use sugar + // types wrapping a __single so we need to check for those explicitly and + // bail in those cases. + if (Ty->isBoundsAttributedType() || Ty->isValueTerminatedType()) + return false; + + return true; + }; + +public: + void handleSingleEntitiesFlowingToIndexableVariableIndexOrPtrArith( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + PtrArithOOBKind IsOOB, size_t MinimumPtrArithOOBOffset) override { + assert(Entities.size() > 0); + + QualType UnsafeOpPtrTy; + // This method handles both indexing and pointer arithmetic + switch (Kind) { + case UnsafeOpKind::Index: + UnsafeOpPtrTy = cast(UnsafeOp)->getBase()->getType(); + break; + case UnsafeOpKind::Arithmetic: + UnsafeOpPtrTy = cast(UnsafeOp)->getType(); + break; + default: + llvm_unreachable("Unhandled UnsafeOpKind"); + } + + if (Entities.size() == 1) { + // When there is only one __single entity reaching the __bidi_indexable + // variable produce a specialized diagnostic + handleSingleEntityFlowingToIndexableVariable( + Entities[0], IndexableLocalVar, UnsafeOp, Kind, UnsafeOpPtrTy, IsOOB, + MinimumPtrArithOOBOffset); + } else + handleSingleEntitiesFlowingToIndexableVariableImpl( + Entities, IndexableLocalVar, UnsafeOp, Kind, UnsafeOpPtrTy, IsOOB, + MinimumPtrArithOOBOffset); + } + + void handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, + const Expr *Operand, UnsafeOpKind Kind) override { + int OpSelect = UnsafeOpToSelectIndex(Kind); + + auto RealPointeeTy = + Operand->getType()->getAs()->getPointeeType(); + assert(!RealPointeeTy->isIncompleteType()); + // This is rather subtle but we **also** have to determine the pointee type + // with implicit casts stripped. This is because when `Operand` is printed + // it may contain implicit casts which do not show up when printing the + // expression. This could cause very confusing diagnostics that claim the + // the type of an expression is different to what the user would deduce by + // reading the printed expression. To avoid this we determine the + // `PerceivedPointeeTy` which is the type the user would expect from the + // printed form of `Operand`. + const auto *OperandNoParenImpCasts = Operand->IgnoreParenImpCasts(); + assert(OperandNoParenImpCasts->getType()->isPointerType()); + auto PerceivedPointeeTy = OperandNoParenImpCasts->getType() + ->getAs() + ->getPointeeType(); + assert(!PerceivedPointeeTy->isIncompleteType()); + + std::string FieldName(""); + if (Kind == UnsafeOpKind::MemberAccess) { + auto *ME = cast(UnsafeOp); + const auto *FD = cast(ME->getMemberDecl()); + if (FD->getType()->isIncompleteArrayType() && + !FD->getType()->isCountAttributedType()) { + // Suppress the warning for incomplete array types because the + // warn_bounds_safety_promoting_incomplete_array_without_count warning + // already handles this. E.g.: + // + // ``` + // struct Foo { + // int member; + // int* buffer[]; // Size of array is "incomplete" + // }; + // ``` + return; + } + FieldName = FD->getQualifiedNameAsString(); + } + + auto UnsafeOpLoc = UnsafeOp->getBeginLoc(); + if (const auto* ILE = dyn_cast(UnsafeOp)) { + // Initializer lists don't have a location for assignment in + // the list so use `Operand` which is the expression used for + // the assignment instead. + assert(std::any_of( + ILE->begin(), ILE->end(), + [&Operand](const Stmt *InitStmt) { return InitStmt == Operand; })); + UnsafeOpLoc = Operand->getBeginLoc(); + } + + const auto RealPointeeTySizeInBytes = + S.getASTContext().getTypeSizeInChars(RealPointeeTy).getQuantity(); + const auto PerceivedPointeeTySizeInBytes = + S.getASTContext().getTypeSizeInChars(PerceivedPointeeTy).getQuantity(); + + S.Diag( + UnsafeOpLoc, + diag:: + warn_bounds_safety_conv_single_to_implicit_indexable_unsafe_zeroth_element) + << /*0*/ OpSelect << /*1*/ const_cast(Operand) + << /*2*/ RealPointeeTy << /*3*/ RealPointeeTySizeInBytes + << /*4*/ IndexableLocalVar << /*5*/ FieldName + << UnsafeOp->getSourceRange(); + + // Note where the __bidi_indexable local var is declared. + S.Diag(IndexableLocalVar->getBeginLoc(), diag::note_pointer_declared_here) + << IndexableLocalVar; + + // Emit a note for each assignment to the __bidi_indexable variable. + // Currently `UnsafeOperationVisitor` doesn't use happens-before so + // some of the assignments noted here might be false positives. The warning + // diagnostic itself is much less likely to be a false positive because + // all the assignments are __single. + for (const auto &Entity : Entities) { + // Get the expression representing the assignment/initialization. + const Expr *AssignmentExpr = Entity.AssignmentExpr; + int IsInitSelect = 0; + if (!AssignmentExpr) { + // The assignment is from the variable initializer + AssignmentExpr = IndexableLocalVar->getInit(); + IsInitSelect = 1; + } + assert(AssignmentExpr); + assert(!Entity.SinglePointeeTy->isIncompleteType()); + + S.Diag(AssignmentExpr->getBeginLoc(), + diag::note_single_entity_assigned_here_with_pointee_sizes) + << /*0*/ Entity.Kind << /*1*/ Entity.Entity << /*2*/ IndexableLocalVar + << /*3*/ Entity.SinglePointeeTy + << /*4*/ S.getASTContext() + .getTypeSizeInChars(Entity.SinglePointeeTy) + .getQuantity() + << /*5*/ PerceivedPointeeTy << /*6*/ PerceivedPointeeTySizeInBytes + << /*7*/ 0 /* Operand has element type*/ + << /*8*/ const_cast(Operand) << /*9*/ IsInitSelect + << AssignmentExpr->getSourceRange(); + + emitSingleEntityDeclaredHereNote(Entity); + } + } + + void handleSingleEntitiesFlowingToIndexableVariableUnsafelyCasted( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + const Expr *Operand) override { + + const auto OperandTy = Operand->getType(); + assert(OperandTy->isPointerTypeWithBounds() || + IsReallySinglePtr(OperandTy)); + + assert(Kind == UnsafeOpKind::Cast); + const auto *CE = cast(UnsafeOp); + + assert(CE->getSubExpr()->getType()->isPointerTypeWithBounds()); + auto CastPointeeTy = CE->getType()->getAs()->getPointeeType(); + + if (CE->getType()->isPointerTypeWithBounds() && + CE->getCastKind() == clang::CK_BitCast) { + // Unsafe bitcast on __bidi_indexable/__indexable + S.Diag( + CE->getBeginLoc(), + diag::warn_bounds_safety_conv_single_to_implicit_indexable_unsafely_cast) + << ((isa(CE)) ? 1 : 0) << IndexableLocalVar + << CE->getSourceRange(); + } else if (IsReallySinglePtr(CE->getType()) && + CE->getCastKind() == clang::CK_BoundsSafetyPointerCast) { + // __bidi_indexable/__indexable -> __single that traps + S.Diag( + CE->getBeginLoc(), + diag:: + warn_bounds_safety_conv_single_to_implicit_indexable_unsafely_cast_to_single_trap) + << ((isa(CE)) ? 1 : 0) << IndexableLocalVar + << CastPointeeTy << CE->getSourceRange(); + } else { + llvm_unreachable("Unhandled cast type"); + } + + // Note where the __bidi_indexable local var is declared. + S.Diag(IndexableLocalVar->getBeginLoc(), diag::note_pointer_declared_here) + << IndexableLocalVar; + + // Emit a note for each assignment to the __bidi_indexable variable. + // Currently `UnsafeOperationVisitor` doesn't use happens-before so + // some of the assignments noted here might be false positives. The warning + // diagnostic itself is much less likely to be a false positive because + // all the assignments are __single. + for (const auto &Entity : Entities) { + // Get the expression representing the assignment/initialization. + const Expr *AssignmentExpr = Entity.AssignmentExpr; + int IsInitSelect = 0; + if (!AssignmentExpr) { + // The assignment is from the variable initializer + AssignmentExpr = IndexableLocalVar->getInit(); + IsInitSelect = 1; + } + assert(AssignmentExpr); + assert(!Entity.SinglePointeeTy->isIncompleteType()); + + S.Diag(AssignmentExpr->getBeginLoc(), + diag::note_single_entity_assigned_here_with_pointee_sizes) + << /*0*/ Entity.Kind << /*1*/ Entity.Entity << /*2*/ IndexableLocalVar + << /*3*/ Entity.SinglePointeeTy + << /*4*/ S.getASTContext() + .getTypeSizeInChars(Entity.SinglePointeeTy) + .getQuantity() + << /*5*/ CastPointeeTy << /*6*/ S.getASTContext() + .getTypeSizeInChars(CastPointeeTy) + .getQuantity() + << /*7*/ 1 /*Cast of IndexableLocalVar has UnsafeBitCast type*/ + << /*8*/ CE->getType() << /*9*/ IsInitSelect + << AssignmentExpr->getSourceRange(); + + emitSingleEntityDeclaredHereNote(Entity); + } + } + + void handleSingleEntitiesFlowingToIndexableDynamicCountConversion( + const llvm::ArrayRef Entities, + const VarDecl *IndexableLocalVar, const Stmt *UnsafeOp, UnsafeOpKind Kind, + const Expr *Operand, const QualType DCPT, WillTrapKind WillTrap, + std::optional ConstantCount, + size_t MaxSafeSizeOrCount) override { + const auto OperandTy = Operand->getType(); + assert(OperandTy->isPointerTypeWithBounds() || + IsReallySinglePtr(OperandTy)); + + assert(Kind == UnsafeOpKind::Cast || Kind == UnsafeOpKind::CallArg || + Kind == UnsafeOpKind::Return || Kind == UnsafeOpKind::Assignment); + const auto *CE = cast(UnsafeOp); + + assert(CE->getType()->isCountAttributedType()); + + int OpSelect = UnsafeOpToSelectIndex(Kind); + int WillTrapSelect = WillTrapKindSelectIndex(WillTrap); + assert(DCPT->isCountAttributedType()); + int OrNullSelect = + DCPT->getAs()->isOrNull() ? 1 : 0; + int SizedInBytesSelect = + DCPT->getAs()->isCountInBytes() ? 1 : 0; + + auto DCPTPointeeTy = DCPT->getAs()->getPointeeType(); + + int ConstantCountSelect = ConstantCount ? 1 : 0; + size_t ConstantCountInBytes = 0; + if (ConstantCount) { + if (DCPT->getAs()->isCountInBytes()) { + // __sized_by/__sized_by_or_null + ConstantCountInBytes = ConstantCount->getZExtValue(); + } else { + // __counted_by/__counted_by_or_null + auto PointeeTySizeInBytes = + S.getASTContext().getTypeSizeInChars(DCPTPointeeTy).getQuantity(); + auto PointeeTySizeInBytesAP = + llvm::APInt(ConstantCount->getBitWidth(), PointeeTySizeInBytes); + auto ConstantCountInBytesAP = + ((*ConstantCount) * PointeeTySizeInBytesAP); + ConstantCountInBytes = ConstantCountInBytesAP.getZExtValue(); + } + } + + // Due to a bug (rdar://83900556) checks are not emitted when returning + // a __counted_by(or_null)/__sized_by(or_null) pointer. + int TrapInFutureCompilerVersion = (Kind == UnsafeOpKind::Return) ? 1 : 0; + + S.Diag( + UnsafeOp->getBeginLoc(), + diag:: + warn_bounds_safety_conv_single_to_implicit_indexable_dynamic_count_conversion) + << /*0*/ OpSelect << /*1*/ IndexableLocalVar << /*2*/ WillTrapSelect + << /*3*/ DCPT << /*4*/ ConstantCountSelect << /*5*/ OrNullSelect + << /*6*/ TrapInFutureCompilerVersion << /*7*/ SizedInBytesSelect + << /*8*/ MaxSafeSizeOrCount << UnsafeOp->getSourceRange(); + + // Note where the __bidi_indexable local var is declared. + S.Diag(IndexableLocalVar->getBeginLoc(), diag::note_pointer_declared_here) + << IndexableLocalVar; + + // Emit a note for each assignment to the __bidi_indexable variable. + // Currently `UnsafeOperationVisitor` doesn't use happens-before so + // some of the assignments noted here might be false positives. The warning + // diagnostic itself is much less likely to be a false positive because + // all the assignments are __single. + for (const auto &Entity : Entities) { + // Get the expression representing the assignment/initialization. + const Expr *AssignmentExpr = Entity.AssignmentExpr; + if (!AssignmentExpr) { + // The assignment is from the variable initializer + AssignmentExpr = IndexableLocalVar->getInit(); + } + assert(AssignmentExpr); + assert(!Entity.SinglePointeeTy->isIncompleteType()); + + S.Diag(AssignmentExpr->getBeginLoc(), + diag::note_single_entity_assigned_here_with_dyn_count_conversion) + << /*0*/ Entity.Kind << /*1*/ Entity.Entity << /*2*/ IndexableLocalVar + << /*3*/ Entity.SinglePointeeTy + << /*4*/ S.getASTContext() + .getTypeSizeInChars(Entity.SinglePointeeTy) + .getQuantity() + << /*5*/ ConstantCountSelect << /*6*/ DCPT + << /*7*/ ConstantCountInBytes << AssignmentExpr->getSourceRange(); + + emitSingleEntityDeclaredHereNote(Entity); + } + } +}; +/* TO_UPSTREAM(BoundsSafety) OFF*/ + //===----------------------------------------------------------------------===// // AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based // warnings on a function, method, or block. @@ -2880,6 +3496,15 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings( if (S.getLangOpts().CPlusPlus && !fscope->isCoroutine() && isNoexcept(FD)) checkThrowInNonThrowingFunc(S, FD, AC); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (S.getLangOpts().BoundsSafety && + !Diags.isIgnored(diag::warn_bounds_safety_conv_single_to_implicit_indexable, + D->getBeginLoc())) { + BoundsSafetySuggestionReporter Reporter(S); + checkBoundsSafetySuggestions(D, Reporter, S); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + // If none of the previous checks caused a CFG build, trigger one here // for the logical error handler. if (LogicalErrorHandler::hasActiveDiagnostics(Diags, D->getBeginLoc())) { diff --git a/clang/lib/Sema/BoundsSafetySuggestions.cpp b/clang/lib/Sema/BoundsSafetySuggestions.cpp new file mode 100644 index 0000000000000..f7fb0d7adadc1 --- /dev/null +++ b/clang/lib/Sema/BoundsSafetySuggestions.cpp @@ -0,0 +1,1800 @@ +//===- BoundsSafetySuggestions.cpp - Improve your -fbounds-safety code ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Attr.h" +#include "clang/AST/Expr.h" +#include "clang/AST/StmtVisitor.h" +// TO_UPSTREAM(BoundsSafety) +#include "clang/Sema/BoundsSafetySuggestions.h" +#include "clang/Sema/DynamicCountPointerAssignmentAnalysisExported.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/SmallPtrSet.h" + +using namespace llvm; +using namespace clang; + +namespace { + +// A map from variables to values stored in them (their potential +// "definitions", as in "reaching definitions" or "use-def chains"). +using DefinitionList = SmallVector; +using DefinitionMap = DenseMap; + +// A visitor that recursively scans an AST subtree to identify values of +// encountered local variables. It's purely syntactic; it doesn't account +// for "happens-before" relationships between definitions, and the answer +// doesn't depend on the point in the program in which possible definitions +// are queried. +// +// Still, it is very useful for identifying values of variables +// in situations when the value is actually unconditional, +// but otherwise unobvious from the AST. Or confirming that +// all possible values fall into a certain category. +// +// The visitor performs exactly one pass over the AST, which is fast enough +// for the compiler. +// +// TODO: Teach the DefinitionVisitor to understand happens-before +// (rdar://117166345). +class DefinitionVisitor : public ConstStmtVisitor { // CRTP! + void VisitChildren(const Stmt *S) { + for (const Stmt *ChildS : S->children()) + if (ChildS) + Visit(ChildS); + } + + bool isSupportedVariable(const Decl *D) { + // We currently support local variables. + if (const auto *VD = dyn_cast_or_null(D)) + if (VD->isLocalVarDecl()) + return true; + + return false; + } + +public: + DefinitionMap DefMap; + + void VisitStmt(const Stmt *S) { + // This is a manual implementation of RecursiveASTVIsitor behavior. + // It only applies to statements and gives us fine control + // over what exactly do we recurse into. + VisitChildren(S); + } + + // These statements are sources of variable values. + void VisitDeclStmt(const DeclStmt *DS); + void VisitBinaryOperator(const BinaryOperator *BO); + + // Unevaluated context visitors + void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *) { + // Unevaluated context such as sizeof()/alignof()/__alignof + return; + } + + void VisitGenericSelectionExpr(const GenericSelectionExpr *GSE) { + // Only the result expr is evaluated in `_Generic`. + // E.g. + // _Generic(0, // Not evaluated + // int: foo(), // Evaluated + // char: bar() // Not evaluated + // ); + Visit(GSE->getResultExpr()); + } +}; + +} // namespace + +void DefinitionVisitor::VisitDeclStmt(const DeclStmt *DS) { + for (const Decl *D : DS->decls()) + if (const auto *VD = dyn_cast(D)) + if (const Expr *E = VD->getInit()) { + // An initialization is a definition. An uninitialized variable + // declaration isn't a definition. + if (isSupportedVariable(VD)) + DefMap[VD].push_back(E); + + // The initializer may have more interesting sub-expressions. + // FIXME: For non-variable declarations, should we visit children + // in a different way? VisitChildren() is probably unhelpful + // because children aren't statements(?). + Visit(E); + } +} + +void DefinitionVisitor::VisitBinaryOperator(const BinaryOperator *BO) { + // Compound assignment operations (+= etc.) don't count as definitions. + // They just reuse whatever's already there. + if (BO->getOpcode() == BO_Assign) { + if (const auto *LHSDRE = + dyn_cast(BO->getLHS()->IgnoreParens())) { + const auto *VD = dyn_cast(LHSDRE->getDecl()); + + // An assignment to a variable is a definition of that variable. + if (isSupportedVariable(VD)) + DefMap[VD].push_back(BO->getRHS()); + } + } + + // Continue visitation normally. + VisitChildren(BO); +} + +namespace { + +// The visitor that enumerates unsafe buffer operations and informs the Handler +// about problems associated with them. +// +// TODO: Teach UnsafeOperationVisitor to understand happens-before +// (rdar://117166345). +class UnsafeOperationVisitor : public ConstStmtVisitor { + using UnsafeOpKind = BoundsSafetySuggestionHandler::UnsafeOpKind; + using WillTrapKind = BoundsSafetySuggestionHandler::WillTrapKind; + using PtrArithOOBKind = BoundsSafetySuggestionHandler::PtrArithOOBKind; + + BoundsSafetySuggestionHandler &Handler; + const DefinitionMap &DefMap; + Sema &S; + ASTContext &Ctx; + llvm::SmallPtrSet VisitedOVESourceExprs; + llvm::SmallPtrSet UnsafeBitCastsToSkip; + llvm::SmallPtrSet FBPtrCastsToSkip; + + void VisitChildren(const Stmt *S) { + for (const Stmt *ChildS : S->children()) + if (ChildS) + Visit(ChildS); + } + + // Individual checks performed on each unsafe operation. + void checkSingleEntityFlowingToIndexableLocalVariable(const Stmt *UnsafeOp, + const Expr *Operand, + UnsafeOpKind Kind); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleOp( + const Stmt *UnsafeOp, UnsafeOpKind Kind, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleIndexing( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleArithmetic( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleDeref( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleMemberAccess( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingLocal( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingOOBLocal( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind); + + void checkSingleEntityFlowingToIndexableLocalVariableHandleCast( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind); + + void + checkSingleEntityFlowingToIndexableLocalVariableHandleCastConvertedToSingle( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind); + + bool + checkSingleEntityFlowingToIndexableLocalVariableHandleUnsafeCastToLargerPointee( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind, bool TestOnly); + + using ExtraDynCountLogicFn = std::function &)>; + + void checkSingleEntityFlowingToIndexableLocalVariableHandleCastToDynamicCount( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind, ExtraDynCountLogicFn Predicate); + + void + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingCastConvertedToDynamicCount( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind); + + // Run all individual checks. + void handleUnsafeOperation(const Stmt *UnsafeOp, const Expr *Operand, + UnsafeOpKind Kind) { + checkSingleEntityFlowingToIndexableLocalVariable(UnsafeOp, Operand, Kind); + } + + bool ExprIsConstantZero(const Expr *E) { + auto Result = EvaluateAsInt(E); + if (!Result) + return false; + if (Result->isZero()) + return true; + return false; + } + + std::optional EvaluateAsInt(const Expr *E) { + Expr::EvalResult Result; + if (E->isValueDependent()) { + // Expr::EvaluateAsInt will assert if we try to call it in this + // case so just give up. + return std::optional(); + } + + bool success = E->EvaluateAsInt(Result, Ctx); + if (!success) + return std::optional(); + if (!Result.Val.isInt()) + return std::optional(); + return std::optional(Result.Val.getInt()); + } + + /// Determines if the provided expression is a __bidi_indexable pointer + /// that only allows access to it's 0th element. + /// + /// \param E - The expression to visit + /// + /// \return Tuple (X, Y). + /// X will be true iff `E` has type `T* __bidi_indexable` and has the bounds + /// of a `U* __single` where `sizeof(U) == sizeof(T)` and false otherwise. + /// + /// If X is true then Y will be the __single pointer type used for the bounds + /// of the __bidi_indexable pointer. + std::tuple + IsWidePointerWithBoundsOfSingle(const Expr *) const; + + bool IsReallySinglePtr(const QualType Ty) const { + if (!Ty->isSinglePointerType()) + return false; + + // Unfortunately __counted_by and friends, and __terminated_by use sugar + // types wrapping a __single so we need to check for those explicitly and + // bail in those cases. + if (Ty->isBoundsAttributedType() || Ty->isValueTerminatedType()) + return false; + + return true; + }; + + bool FindSingleEntity( + const Expr *Def, const Expr *AssignmentExpr, + QualType SingleTyUsedForBidiBounds, + llvm::SmallVectorImpl + &Entities); + +public: + UnsafeOperationVisitor(BoundsSafetySuggestionHandler &Handler, + const DefinitionMap &DefMap, Sema &S) + : Handler(Handler), DefMap(DefMap), S(S), Ctx(S.getASTContext()) {} + + void VisitStmt(const Stmt *S) { + // Recurse normally. + VisitChildren(S); + } + + void reset() { + VisitedOVESourceExprs.clear(); + UnsafeBitCastsToSkip.clear(); + } + + // Unevaluated context visitors + void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *) { + // Unevaluated context such as sizeof()/alignof()/__alignof + return; + } + + void VisitGenericSelectionExpr(const GenericSelectionExpr *GSE) { + // Only the result expr is evaluated in `_Generic`. + // E.g. + // _Generic(0, // Not evaluated + // int: foo(), // Evaluated + // char: bar() // Not evaluated + // ); + Visit(GSE->getResultExpr()); + } + + // These are the individual unsafe operations we'll react upon. + void VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE); + void VisitUnaryOperator(const UnaryOperator *UO); + void VisitBinaryOperator(const BinaryOperator *BO); + void VisitMemberExpr(const MemberExpr *ME); + void VisitOpaqueValueExpr(const OpaqueValueExpr *OVE); + void VisitDeclStmt(const DeclStmt *DS); + void VisitReturnStmt(const ReturnStmt *RS); + void VisitCallExpr(const CallExpr *CE); + void VisitCastExpr(const CastExpr *CE); +}; + +} // namespace + +std::tuple +UnsafeOperationVisitor::IsWidePointerWithBoundsOfSingle(const Expr *E) const { + const auto Failure = std::make_tuple(false, QualType()); + if (!E->getType()->isPointerTypeWithBounds()) + return Failure; + + // Find the top-most BoundsSafetyPointerCast + const Expr *Curr = E; + const Expr *Prev = nullptr; + const CastExpr *FBPtrCast = nullptr; + while (Curr && Curr != Prev) { + Prev = Curr; + // Walk through ParenExprs + if (const auto *PE = dyn_cast(Curr)) { + Curr = PE->getSubExpr(); + continue; + } + // BoundsSafetyPointerCast can be on either ImplicitCastExpr or + // ExplicitCastExpr so use `CastExpr` which covers both. + if (const auto *CE = dyn_cast(Curr)) { + if (CE->getCastKind() != clang::CK_BoundsSafetyPointerCast) { + // Walk through all other casts. + Curr = CE->getSubExpr(); + continue; + } + FBPtrCast = CE; + break; + } + } + + if (!FBPtrCast) + return Failure; // Failed to find BoundsSafetyPointerCast + + // Check that the BoundsSafetyPointerCast acts on a __single pointer. + const auto SinglePtrQualTy = FBPtrCast->getSubExpr()->getType(); + if (!IsReallySinglePtr(SinglePtrQualTy)) + return Failure; + + // The resulting `T* __bidi_indexable` of E will get the bounds + // of a `U* __single`. + // + // Now check if `sizeof(T) >= sizeof(U)`. If that's the case then + // `E[]` will be out-of-bounds (where + // `E` is the expression `E`). + // + // When `sizeof(T) <= sizeof(U)` then `E[0]` will be in-bounds. + // + // When `sizeof(T) > sizeof(U)` then `E[0]` will also be out-of-bounds. + // However, due to rdar://119744147 generated code won't trap, so we currently + // don't try to emit a warning in that situation either (rdar://119775862). + + // Use getAs Walk through AttributedType sugar. E.g.: + // + // AttributedType 0x120024c90 'uint8_t *__bidi_indexable' sugar + // `-PointerType 0x120024c60 'uint8_t *__bidi_indexable' + const auto *IndexablePtrType = E->getType()->getAs(); + assert(IndexablePtrType); + + const auto TypeUsedForIndexing = IndexablePtrType->getPointeeType(); + const auto TypeUsedForIndexingSize = + Ctx.getTypeSizeOrNull(TypeUsedForIndexing); + if (TypeUsedForIndexingSize == 0) { + // This situation already raises `warn_bounds_safety_single_bitcast_lose_bounds` + // (if there's an explicit cast to a pointer with a sized type) or + // `err_bounds_safety_incomplete_single_to_indexable` elsewhere. + return Failure; + } + + // Walk through AttributedType sugar + const auto *SinglePointerTy = SinglePtrQualTy->getAs(); + assert(SinglePointerTy); + + const auto SinglePointeeType = SinglePointerTy->getPointeeType(); + const auto SinglePointeeTypeSize = Ctx.getTypeSizeOrNull(SinglePointeeType); + if (SinglePointeeTypeSize == 0) { + // This situation isn't legal and raises + // `err_bounds_safety_incomplete_single_to_indexable` + // elsewhere. + return Failure; + } + + if (TypeUsedForIndexingSize >= SinglePointeeTypeSize) + return std::make_tuple(true, SinglePointeeType); + + return Failure; +} + +bool UnsafeOperationVisitor::FindSingleEntity( + const Expr *Def, const Expr *AssignmentExpr, QualType SinglePointeeTy, + llvm::SmallVectorImpl + &SingleEntities) { + assert(IsReallySinglePtr(Def->getType())); + + // Consider the different ways a __single can end up in the variable + // definition + + // Definition comes directly from a variable + if (const auto *DRE = dyn_cast(Def)) { + const auto *SingleDecl = DRE->getDecl(); + if (!SingleDecl) + return false; + const auto *SingleVarDecl = dyn_cast(SingleDecl); + if (!SingleVarDecl) + return false; + + if (const auto *Param = dyn_cast(SingleVarDecl)) { + // Variable definition is from a __single parameter + BoundsSafetySuggestionHandler::SingleEntity Entity = { + /*Kind*/ BoundsSafetySuggestionHandler::AssignmentSourceKind:: + Parameter, + /*Entity*/ Param, + /*AssignmentExpr*/ AssignmentExpr, + /*SinglePointeeTy*/ SinglePointeeTy}; + SingleEntities.emplace_back(Entity); + return true; + } + + if (SingleVarDecl->hasGlobalStorage() && !SingleVarDecl->isStaticLocal()) { + // Variable definition is from a __single global + BoundsSafetySuggestionHandler::SingleEntity Entity = { + /*Kind*/ BoundsSafetySuggestionHandler::AssignmentSourceKind:: + GlobalVar, + /*Entity*/ SingleVarDecl, + /*AssignmentExpr*/ AssignmentExpr, + /*SinglePointeeTy*/ SinglePointeeTy}; + SingleEntities.emplace_back(Entity); + return true; + } + + if (SingleVarDecl->isLocalVarDecl() || SingleVarDecl->isStaticLocal()) { + // Variable definition is from a local __single or a + // "locally scoped" static __single. + BoundsSafetySuggestionHandler::SingleEntity Entity = { + /*Kind*/ BoundsSafetySuggestionHandler::AssignmentSourceKind:: + LocalVar, + /*Entity*/ SingleVarDecl, + /*AssignmentExpr*/ AssignmentExpr, + /*SinglePointeeTy*/ SinglePointeeTy}; + SingleEntities.emplace_back(Entity); + return true; + } + + return false; + } + + // __single comes from value returned by function call + if (const auto *CE = dyn_cast(Def)) { + const auto *DirectCallDecl = CE->getDirectCallee(); + if (!DirectCallDecl) { + // Don't support indirect calls for now. + return false; + } + if (DirectCallDecl->hasAttr() && + IsReallySinglePtr(DirectCallDecl->getReturnType())) { + // Functions declared like + // void * custom_malloc(size_t s) __attribute__((alloc_size(1))) + // + // are currently are annotated as returning `void *__single` rather + // than `void *__sized_by(s)`. To make the right thing happen at call + // sites `BoundsSafetyPointerPromotionExpr` is used to generate a pointer + // with the appropriate bounds from the `void *__single`. For functions + // like this the warning needs to be suppressed because from the user's + // perspective the returned value is not actually __single. + // + // This code path can be deleted once allocating functions are properly + // annotated with __sized_by_or_null. rdar://117114186 + return false; + } + + assert(IsReallySinglePtr(DirectCallDecl->getReturnType())); + + BoundsSafetySuggestionHandler::SingleEntity Entity = { + /*Kind*/ BoundsSafetySuggestionHandler::AssignmentSourceKind:: + FunctionCallReturnValue, + /*Entity*/ DirectCallDecl, + /*AssignmentExpr*/ AssignmentExpr, + /*SinglePointeeTy*/ SinglePointeeTy}; + SingleEntities.emplace_back(Entity); + return true; + } + + auto HasSingleFromStructOrUnion = [&](const Expr *E) -> bool { + if (const auto *ME = dyn_cast(E)) { + if (const auto *FD = dyn_cast(ME->getMemberDecl())) { + BoundsSafetySuggestionHandler::AssignmentSourceKind AssignmentKind; + if (FD->getParent()->isUnion()) { + AssignmentKind = + BoundsSafetySuggestionHandler::AssignmentSourceKind::UnionMember; + } else { + assert(FD->getParent()->isStruct()); + AssignmentKind = + BoundsSafetySuggestionHandler::AssignmentSourceKind::StructMember; + } + + BoundsSafetySuggestionHandler::SingleEntity Entity = { + /*Kind*/ AssignmentKind, + /*Entity*/ FD, + /*AssignmentExpr*/ AssignmentExpr, + /*SinglePointeeTy*/ SinglePointeeTy}; + SingleEntities.emplace_back(Entity); + return true; + } + } + return false; + }; + + // __single comes from array access + if (const auto *ASE = dyn_cast(Def)) { + // The `IgnoreImpCasts()` on `ASE->getBase()` is necessary here to walk + // through MaterializeSequenceExpr, BoundsSafetyPointerPromotionExpr, + // OpaqueValueExpr, and ImplicitCastExpr. + const Expr *BaseExpr = ASE->getBase()->IgnoreImpCasts(); + + // Array access is on a variable. + if (const auto *DRE = dyn_cast(BaseExpr)) { + if (const auto *ArrayDecl = dyn_cast(DRE->getDecl())) { + BoundsSafetySuggestionHandler::SingleEntity Entity = { + /*Kind*/ BoundsSafetySuggestionHandler::AssignmentSourceKind:: + ArrayElement, + /*Entity*/ ArrayDecl, + /*AssignmentExpr*/ AssignmentExpr, + /*SinglePointeeTy*/ SinglePointeeTy}; + SingleEntities.emplace_back(Entity); + return true; + } + } + + // Array access is on struct/union member + if (HasSingleFromStructOrUnion(BaseExpr)) + return true; + } + + // __single comes from direct access to a struct/union member + if (HasSingleFromStructOrUnion(Def)) + return true; + + return false; +} + +void UnsafeOperationVisitor::checkSingleEntityFlowingToIndexableLocalVariable( + const Stmt *UnsafeOp, const Expr *Operand, UnsafeOpKind Kind) { + + const Expr *PreviousOperand = nullptr; + const Expr *CurrentOperand = Operand; + + while (CurrentOperand != PreviousOperand) { + PreviousOperand = CurrentOperand; + if (const auto *OVE = dyn_cast(CurrentOperand)) { + CurrentOperand = OVE->getSourceExpr(); + continue; + } + // This is quite broad, but such transformation typically results in + // an expression with roughly the same value, i.e. it refers to the same + // object. The only exception from this rule is, implicit casts of + // CK_LValueToRValue kind, which constitute memory load operations. + // But we don't expect any of that coming in. + CurrentOperand = CurrentOperand->IgnoreParenCasts(); + } + + const auto *VarDRE = dyn_cast(CurrentOperand); + if (!VarDRE) + return; + + const auto *Var = dyn_cast(VarDRE->getDecl()); + if (!Var || !Var->isLocalVarDecl()) + return; + + QualType VarTy = Var->getType(); + + // It might be that we've discarded an explicit cast from an integer + // to a pointer, so let's double-check. + if (!VarTy->isPointerTypeWithBounds()) + return; + + // Check that the variable is *implicitly* __bidi_indexable. + // Explicitly __bidi_indexable variables are covered by + // -Wbounds-attributes-implicit-conversion-single-to-explicit-indexable. + // + // We use `hasAttr` to walk through additional AttributedTypes that may be + // present. + if (!VarTy->hasAttr(attr::PtrAutoAttr)) + return; + + auto DefI = DefMap.find(Var); // The map is const, can't use []. + if (DefI == DefMap.end()) + return; + + const DefinitionList &Defs = DefI->second; + + llvm::SmallVector + SingleEntities; + + if (Defs.size() == 0) + return; + + // Walk over all the potential definitions that the __bidi_indexable variable + // might take. If they are all __single and can all be identified as + // an "entity" then a warning is emitted. + for (const auto *Def : Defs) { + // Skip emitting warnings if any of the definitions aren't T* + // __bidi_indexable with the bounds of a T* __single. + bool IsWPWBOS = false; + QualType SingleTyUsedForBidiBounds; + std::tie(IsWPWBOS, SingleTyUsedForBidiBounds) = + IsWidePointerWithBoundsOfSingle(Def); + if (!IsWPWBOS) + return; + + // It's ok to walk through all casts (including explicit) at this point + // because `IsWidePointerWithBoundsOfSingle` has already checked that this + // is a scenario where we should warn. + const Expr *DefWithoutParensAndCasts = Def->IgnoreParenCasts(); + + // Skip emitting warnings if any of the definitions are not "effectively" + // __single. We can only be sure a `__single` flowed into an indexable + // variable in this case. + if (!IsReallySinglePtr(DefWithoutParensAndCasts->getType())) + return; + + const Expr *AssignmentExpr = nullptr; + if (Def != Var->getInit()) { + // The definition comes from assignment (i.e. not an initializer). + AssignmentExpr = DefWithoutParensAndCasts; + } + // Skip emitting the warning if the entity for any of the definitions can't + // be found. + if (!FindSingleEntity(DefWithoutParensAndCasts, AssignmentExpr, + SingleTyUsedForBidiBounds, SingleEntities)) + return; + } + + // Dispatch based on the unsafe operation. + switch (Kind) { + case BoundsSafetySuggestionHandler::UnsafeOpKind::Arithmetic: + checkSingleEntityFlowingToIndexableLocalVariableHandleArithmetic( + UnsafeOp, Var, SingleEntities); + break; + case BoundsSafetySuggestionHandler::UnsafeOpKind::Index: + checkSingleEntityFlowingToIndexableLocalVariableHandleIndexing( + UnsafeOp, Var, SingleEntities); + break; + case clang::BoundsSafetySuggestionHandler::UnsafeOpKind::Deref: + checkSingleEntityFlowingToIndexableLocalVariableHandleDeref(UnsafeOp, Var, + SingleEntities); + break; + case clang::BoundsSafetySuggestionHandler::UnsafeOpKind::MemberAccess: + checkSingleEntityFlowingToIndexableLocalVariableHandleMemberAccess( + UnsafeOp, Var, SingleEntities); + break; + case clang::BoundsSafetySuggestionHandler::UnsafeOpKind::Assignment: + case clang::BoundsSafetySuggestionHandler::UnsafeOpKind::Return: + case clang::BoundsSafetySuggestionHandler::UnsafeOpKind::CallArg: + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingLocal( + UnsafeOp, Operand, Var, SingleEntities, Kind); + break; + case clang::BoundsSafetySuggestionHandler::UnsafeOpKind::Cast: + checkSingleEntityFlowingToIndexableLocalVariableHandleCast( + UnsafeOp, Operand, Var, SingleEntities, Kind); + break; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcovered-switch-default" + default: + llvm_unreachable("Unhandled UnsafeOpKind"); +#pragma clang diagnostic pop + } +} + +static bool AccessingElementZeroIsOOB( + ASTContext &Ctx, const QualType EltTy, + llvm::SmallVectorImpl + &SingleEntities) { + auto EltSize = Ctx.getTypeSizeOrNull(EltTy); + if (EltSize == 0) + return false; + // If the EltTy used for dereferencing is larger than all the potential + // __single bounds i then this out-of-bounds. This currently won't trap in + // some cases due to rdar://104845295 but it will be in the future so we + // should still warn. + bool DefinitelyOOB = std::all_of( + SingleEntities.begin(), SingleEntities.end(), + [&](const BoundsSafetySuggestionHandler::SingleEntity &S) -> bool { + auto SingleSize = Ctx.getTypeSizeOrNull(S.SinglePointeeTy); + if (SingleSize == 0) + return false; + return EltSize > SingleSize; + }); + return DefinitelyOOB; +} + +static QualType GetLargestSinglePointee( + llvm::SmallVectorImpl + &SingleEntities, + ASTContext &Ctx) { + QualType LargestSinglePointee = SingleEntities.front().SinglePointeeTy; + auto LargestPointeeSizeInBits = Ctx.getTypeSizeOrNull(LargestSinglePointee); + for (const auto &SingleEntity : SingleEntities) { + const auto CurrentPointeeTy = SingleEntity.SinglePointeeTy; + auto CurrentPointeeSizeInBits = Ctx.getTypeSizeOrNull(CurrentPointeeTy); + if (CurrentPointeeSizeInBits > LargestPointeeSizeInBits) { + LargestSinglePointee = CurrentPointeeTy; + LargestPointeeSizeInBits = CurrentPointeeSizeInBits; + } + } + return LargestSinglePointee; +} + +struct PointerArithInBoundsInfo { + // Any effective offset >= to this is out-of-bounds. + // Note the offset is assumed to be in units of the pointee + // type used for the pointer arithmetic. + // + // The "effective offset" is the offset assuming the operation + // was an addition operation. + size_t MinimumOOBPositiveOffset; + + BoundsSafetySuggestionHandler::PtrArithOOBKind IsOOB; + + PointerArithInBoundsInfo() + : MinimumOOBPositiveOffset(0), + IsOOB(BoundsSafetySuggestionHandler::PtrArithOOBKind::UNKNOWN) {} +}; + +/// Compute if pointer arithmetic would be in bounds and other related info. +/// +/// \param PointeeTySizeInBits The pointee size used for pointer arithmetic. +/// \param AvailableBits The number bits available in the memory pointed to by +/// the pointer. \param Offset If set it is the offset that would be used. +/// +/// Example: +/// +/// PointeeTySizeInBits = 1*8 = 8 +/// AvailableBits = 4*8 = 32 +/// Offset = Unset +/// OpIsIncrement = true +/// OffsetIsSignedTy = false (size_t is unsigned) +/// +/// Assume sizeof(int) = 4 and sizeof(char) = 1. This corresponds to +/// +/// ``` +/// void foo(int* p, size_t offset) { +/// int* local = p; +/// ((char*)local + offset); +/// } +PointerArithInBoundsInfo ComputeIfPointerArithIsInBounds( + size_t PointeeTySizeInBits, size_t AvailableBits, + std::optional Offset, bool OpIsIncrement, bool OffsetIsSignedTy) { + PointerArithInBoundsInfo Result; + using PtrArithOOBKind = BoundsSafetySuggestionHandler::PtrArithOOBKind; + + // Compute the smallest offset that would be out-of-bounds. + Result.MinimumOOBPositiveOffset = AvailableBits / PointeeTySizeInBits; + + if (Result.MinimumOOBPositiveOffset == 0) { + // Special case: This means any offset >= 0 would cause an out-of-bounds + // access. This means **any** offset would be out-of-bounds. + // + // This will happen if PointeeTySizeInBits > Availablebits. + // + // E.g. + // ``` + // uint32_t* local; + // *((uint64_t*)local + offset) = 0; + // ``` + Result.IsOOB = PtrArithOOBKind::ALWAYS_OOB_BASE_OOB; + return Result; + } + + if (!Offset.has_value()) { + // The offset isn't known + + if (OffsetIsSignedTy) { + // Effective offset can be negative or positive or zero + Result.IsOOB = PtrArithOOBKind:: + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET_OR_LT_ZERO; + } else { + // Effective offset can be positive or zero + if (OpIsIncrement) { + // Addition operation + // Effective offset can be zero or positive (cannot be negative) + Result.IsOOB = PtrArithOOBKind:: + OOB_IF_EFFECTIVE_OFFSET_GTE_MINIMUM_OOB_POSITIVE_OFFSET; + } else { + // Subtract operation + // Effective offset can be negative or zero (cannot be positive) + Result.IsOOB = PtrArithOOBKind::OOB_IF_EFFECTIVE_OFFSET_LT_ZERO; + } + } + return Result; + } + + // Offset is a constant + + // Compute the "EffectiveOffset" which is the offset that would be used + // assuming the operation was always a pointer increment operation. I.e.: + // + // ptr + EffectiveOffset + // + APInt EffectiveOffset = Offset.value(); + if (!OpIsIncrement) { + // The operation does pointer subtraction. + // Note if `EffectiveOffset.isMinSignedValue()` + // then the negation will get the same value back. + + EffectiveOffset.negate(); + } + + if (EffectiveOffset.isNegative()) { + // Any negative offset is out-of-bounds + Result.IsOOB = PtrArithOOBKind::ALWAYS_OOB_CONSTANT_OFFSET; + return Result; + } + + // If this condition is true + // + // EffectiveOffset >= (AvailableBits / PointeeTySizeInBits) + // + // then then a memory access at the offset would access out-of-bounds + // memory. + assert(!EffectiveOffset.isNegative()); + auto MinimumOOBOffset = + APInt(EffectiveOffset.getBitWidth(), Result.MinimumOOBPositiveOffset); + + if (EffectiveOffset.uge(MinimumOOBOffset)) + Result.IsOOB = PtrArithOOBKind::ALWAYS_OOB_CONSTANT_OFFSET; + else + Result.IsOOB = PtrArithOOBKind::NEVER_OOB; + + return Result; +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleIndexing( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities) { + auto *ASE = cast(UnsafeOp); + // Determine the pointee sized used for indexing + const auto BasePtrTy = ASE->getBase()->getType(); + const auto IndexingPointeeTy = BasePtrTy->getPointeeType(); + + if (!BasePtrTy->isPointerTypeWithBounds()) { + // This path can be hit if the __bidi_indexable is casted to a different + // attribute type before being indexed. E.g.: + // + // ``` + // int* __single p; + // int* local = p; + // ((int* __single) local)[0] = 0; + // ``` + // + // The analysis here currently assumes the base pointer is __bidi_indexable + // so don't try to proceed further. + return; + } + + const auto IndexingPointeeTyInBits = Ctx.getTypeSizeOrNull(IndexingPointeeTy); + assert(IndexingPointeeTyInBits > 0); + + // Determine the max possible bounds that the __bidi_indexable could + // be storing. We use the maximum to reduce chances of false positives. + const auto LargestSinglePointeeTy = + GetLargestSinglePointee(SingleEntities, Ctx); + const auto LargestSinglePointeeTyInBits = + Ctx.getTypeSizeOrNull(LargestSinglePointeeTy); + + // Try to evaluate the index as a constant + const auto KnownOffset = EvaluateAsInt(ASE->getIdx()); + bool IndexTyIsSigned = ASE->getIdx()->getType()->isSignedIntegerType(); + const auto PABInfo = ComputeIfPointerArithIsInBounds( + IndexingPointeeTyInBits, LargestSinglePointeeTyInBits, KnownOffset, + /*OpIsIncrement=*/true, /*OffsetIsSignedTy=*/IndexTyIsSigned); + + if (PABInfo.IsOOB == PtrArithOOBKind::NEVER_OOB) + return; + + if (PABInfo.IsOOB == PtrArithOOBKind::ALWAYS_OOB_BASE_OOB) { + // Accessing any index is out-of-bounds + const auto *IndexableVarExpr = ASE->getBase(); + // Accessing element is guaranteed to be out-of-bounds. This currently + // won't trap due to rdar://104845295 but it will be in the future so we + // should still warn. + Handler.handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB( + SingleEntities, IndexableVar, UnsafeOp, IndexableVarExpr, + UnsafeOpKind::Index); + return; + } + + Handler.handleSingleEntitiesFlowingToIndexableVariableIndexOrPtrArith( + SingleEntities, IndexableVar, UnsafeOp, UnsafeOpKind::Index, + PABInfo.IsOOB, PABInfo.MinimumOOBPositiveOffset); +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleArithmetic( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities) { + const Expr *E = cast(UnsafeOp); + const auto PointeeTy = E->getType()->getAs()->getPointeeType(); + if (PointeeTy->isIncompleteType()) + return; + const auto PointeeTySizeInBits = Ctx.getTypeSizeOrNull(PointeeTy); + + const auto LargestSinglePointee = + GetLargestSinglePointee(SingleEntities, Ctx); + const auto LargestSinglePointeeInBits = + Ctx.getTypeSizeOrNull(LargestSinglePointee); + + size_t MinimumOOBOffset = 0; + PtrArithOOBKind IsOOB = PtrArithOOBKind::UNKNOWN; + + auto ShouldWarn = [&](std::optional KnownOffset, bool OpIsIncrement, + bool OffsetIsSignedTy) -> bool { + auto Info = ComputeIfPointerArithIsInBounds( + PointeeTySizeInBits, LargestSinglePointeeInBits, KnownOffset, + OpIsIncrement, OffsetIsSignedTy); + + MinimumOOBOffset = Info.MinimumOOBPositiveOffset; + IsOOB = Info.IsOOB; + assert(IsOOB != PtrArithOOBKind::UNKNOWN); + + if (IsOOB == PtrArithOOBKind::NEVER_OOB) + return false; + + return true; + }; + + if (auto *UO = dyn_cast(UnsafeOp)) { + std::optional IsIncrement; + switch (UO->getOpcode()) { + case UO_PostInc: + case UO_PreInc: + IsIncrement = true; + break; + case UO_PreDec: + case UO_PostDec: + IsIncrement = false; + break; + default: + llvm_unreachable("Unhandled UnaryOperator"); + } + assert(IsIncrement.has_value()); + if (!ShouldWarn(/*KnownOffset=*/APInt(/*numBits=*/64, 1), + /*OpIsIncrement=*/IsIncrement.value(), + /*OffsetIsSignedTy=*/false)) + return; + + Handler.handleSingleEntitiesFlowingToIndexableVariableIndexOrPtrArith( + SingleEntities, IndexableVar, UnsafeOp, UnsafeOpKind::Arithmetic, IsOOB, + MinimumOOBOffset); + + return; + } + if (auto *BO = dyn_cast(UnsafeOp)) { + const Expr *Offset; + + bool IsIncrement = false; + switch (BO->getOpcode()) { + case BO_Add: + // n + ptr or ptr + n + case BO_AddAssign: + // ptr += n (i.e. ptr = ptr + n) + IsIncrement = true; + break; + case BO_Sub: + // ptr - n + case BO_SubAssign: + // ptr -= n (i.e. ptr = ptr - n) + IsIncrement = false; + break; + default: + llvm_unreachable("Unhandled BinaryOperator"); + } + + if (BO->getLHS()->getType()->isPointerType()) { + Offset = BO->getRHS(); + } else { + Offset = BO->getLHS(); + } + assert(Offset->getType()->isIntegralOrEnumerationType()); + bool OffsetIsSignedTy = + Offset->getType()->isSignedIntegerOrEnumerationType(); + + auto EvaluatedOffset = EvaluateAsInt(Offset); + if (!ShouldWarn(/*KnownOffset=*/EvaluatedOffset, + /*OpIsIncrement=*/IsIncrement, + /*OffsetIsSignedTy=*/OffsetIsSignedTy)) + return; + + Handler.handleSingleEntitiesFlowingToIndexableVariableIndexOrPtrArith( + SingleEntities, IndexableVar, UnsafeOp, UnsafeOpKind::Arithmetic, IsOOB, + MinimumOOBOffset); + return; + } + llvm_unreachable("Unhandled UnsafeOp"); +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleDeref( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities) { + + if (auto *UO = dyn_cast(UnsafeOp)) { + assert(UO->getOpcode() == UO_Deref); + const auto *BasePtrTy = UO->getSubExpr()->getType()->getAs(); + assert(BasePtrTy); + const auto BasePointeeTy = BasePtrTy->getPointeeType(); + + if (!BasePtrTy->isPointerTypeWithBounds()) { + // It's possible that the there are casts that change the attribute type. + // E.g. + // + // void consume(int* p) { + // int* ptr = p; + // *((int* __single) ptr) = 0; + // } + return; + } + + if (!AccessingElementZeroIsOOB(Ctx, BasePointeeTy, SingleEntities)) + return; + + const auto *IndexableVarExpr = UO->getSubExpr(); + Handler.handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB( + SingleEntities, IndexableVar, UnsafeOp, IndexableVarExpr, + UnsafeOpKind::Deref); + return; + } + + llvm_unreachable("Unhandled UnsafeOp"); +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleMemberAccess( + const Stmt *UnsafeOp, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities) { + const auto *ME = cast(UnsafeOp); + const auto *BasePtrTy = ME->getBase()->getType()->getAs(); + assert(BasePtrTy); + const auto BasePointeeTy = BasePtrTy->getPointeeType(); + + if (!BasePtrTy->isPointerTypeWithBounds()) { + // It's possible that the there are casts that change the attribute type. + // E.g. + // + // void consume(struct Foo* p) { + // struct Foo* ptr = p; + // *((struct Bar* __single) ptr) = 0; + // } + return; + } + + // This is rather subtle but access to **any** field in the struct (not just + // the fields above the upper bound of the wide pointer) will trap because + // `CodeGenFunction::EmitMemberExpr` checks that "one-past-the-end" (assuming + // the pointee type of the wide pointer) is <= the upper bound. This check + // will always fail if the bounds stored in the wide pointer are for a smaller + // type. + // + // So it is not sufficient to check if the field being accessed is above the + // upper bound. Instead check if **any** part of the struct will be + // out-of-bounds which corresponds directly to the check that + // `CodeGenFunction::EmitMemberExpr` does. + if (!AccessingElementZeroIsOOB(Ctx, BasePointeeTy, SingleEntities)) + return; + + const auto *IndexableVarExpr = ME->getBase(); + Handler.handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB( + SingleEntities, IndexableVar, UnsafeOp, IndexableVarExpr, + UnsafeOpKind::MemberAccess); +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingLocal( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind) { + // Note. This is "escape" in the sense that the indexable variable is being + // assigned to something else which means it "escapes" the analysis (the + // bounds are no longer tracked due to the fact transitive assignments are not + // tracked) and not necessarily "escapes" in the sense that the pointer + // escapes the current function (although that is what happens in some cases). + + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingOOBLocal( + UnsafeOp, Operand, IndexableVar, SingleEntities, Kind); + + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingCastConvertedToDynamicCount( + UnsafeOp, Operand, IndexableVar, SingleEntities, Kind); +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingOOBLocal( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind) { + const auto IndexableEltTy = + IndexableVar->getType()->getAs()->getPointeeType(); + + // Note: We deliberately don't check the pointer attributes on `Operand` + // as `IndexableVar` could have been casted to have a different pointer + // attribute and we still want to warn about it escaping the analysis. + + // Using `IndexableEltTy` as the element type is "technically" wrong. + // The correct element type is actually: + // + // Operand->getType()->getPointeeType() + // + // However, doing this causes many more warnings to be emitted because + // we will warn about unsafe casts at the use site that we already warned + // about. E.g.: + // + // ``` + // int* consume(char*p) { + // char* local = p; + // // The cast already causes a warning to be emitted. + // return (int*) local; + // } + // ``` + // + // To avoid emitting these extra warnings we use `IndexableEltTy` instead + // which indirectly avoids emitting warnings for casts we already warned about + // but still allows us to warn about when the unsafe cast at assignment to + // the local `__bidi_indexable.` E.g.: + // + // ``` + // int* consume(char* p) { + // int* local = (int* __bidi_indexable)(char* __bidi_indexable) p; + // return local; // Warn here + // } + // ``` + // + // Revisiting this design decision is tracked by rdar://123654605. + // + if (!AccessingElementZeroIsOOB(Ctx, IndexableEltTy, SingleEntities)) + return; + // The local __bidi_indexable does not have bounds sufficient to access a + // single element so warn when this pointer escapes the analysis. + + Handler.handleSingleEntitiesFlowingToIndexableVariableWithEltZeroOOB( + SingleEntities, IndexableVar, UnsafeOp, Operand, Kind); +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleCastConvertedToSingle( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind) { + // Look for unsafe `__bidi_indexable -> __single conversion (causes a bounds + // check). + auto *CE = cast(UnsafeOp); + + if (!IsReallySinglePtr(CE->getType())) { + return; + } + + assert(CE->getSubExpr() == Operand); + + // Make sure this is a `__bidi_indexable/__indexable -> __single` conversion + if (CE->getCastKind() != clang::CK_BoundsSafetyPointerCast) + return; + const auto OperandTy = Operand->getType(); + if (!OperandTy->isPointerTypeWithBounds()) + return; + const auto EltTy = OperandTy->getAs()->getPointeeType(); + + if (!AccessingElementZeroIsOOB(Ctx, EltTy, SingleEntities)) + return; + + // At this cast a bounds check will be emitted. The pointee type on the + // cast is larger than the bounds that will be stored in `IndexableVar` at + // runtime. This should be a trap but currently isn't due to rdar://104845295 + // but will be in the future so warn about this. + Handler.handleSingleEntitiesFlowingToIndexableVariableUnsafelyCasted( + SingleEntities, IndexableVar, UnsafeOp, Kind, Operand); + + // Try to suppress warnings about unsafe bit casts in the expr tree of the + // operand given that we've already warned about this trapping cast. + const auto IndexableVarPointeeTy = + IndexableVar->getType()->getAs()->getPointeeType(); + if (EltTy != IndexableVarPointeeTy) { + const Expr *Current = Operand; + const Expr *Previous = nullptr; + while (Current != Previous) { + Previous = Current; + if (const auto *PE = dyn_cast(Current)) { + Current = PE->getSubExpr(); + continue; + } + if (const auto *CE = dyn_cast(Current)) { + if (CE->getCastKind() == clang::CK_BitCast) { + if (checkSingleEntityFlowingToIndexableLocalVariableHandleUnsafeCastToLargerPointee( + CE, CE->getSubExpr(), IndexableVar, SingleEntities, Kind, + /*TestOnly=*/true)) { + // If this bit cast was visited it would be warned about so add it + // to the set of bitcasts to skip. + UnsafeBitCastsToSkip.insert(CE); + } + } + Current = CE->getSubExpr(); + } + } + } +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleCast( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind) { + checkSingleEntityFlowingToIndexableLocalVariableHandleUnsafeCastToLargerPointee( + UnsafeOp, Operand, IndexableVar, SingleEntities, Kind, + /*TestOnly=*/false); + checkSingleEntityFlowingToIndexableLocalVariableHandleCastConvertedToSingle( + UnsafeOp, Operand, IndexableVar, SingleEntities, Kind); + checkSingleEntityFlowingToIndexableLocalVariableHandleCastToDynamicCount( + UnsafeOp, Operand, IndexableVar, SingleEntities, Kind, + /*Predicate=*/nullptr); +} + +bool UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleUnsafeCastToLargerPointee( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind, bool TestOnly) { + + // Report casts that bitcast __bidi_indexable/__indexable pointer + // to a larger pointee type where we know the bounds at runtime will + // be too small. + auto *CE = cast(UnsafeOp); + + if (UnsafeBitCastsToSkip.contains(CE)) { + // This cast was already reported elsewhere so skip it. + return false; + } + + if (CE->getCastKind() != clang::CK_BitCast) + return false; + + // The result needs to be a __bidi_indexable/__indexable pointer + if (!CE->getType()->isPointerTypeWithBounds()) + return false; + + // This is a BitCast so the pointer attribute should not have changed. + assert(CE->getSubExpr()->getType()->isPointerTypeWithBounds()); + + // Go through all __single entities and find the largest pointee type. + QualType LargestSinglePointee = GetLargestSinglePointee(SingleEntities, Ctx); + const auto LargestSinglePointeeSizeInBits = + Ctx.getTypeSizeOrNull(LargestSinglePointee); + if (LargestSinglePointeeSizeInBits == 0) + return false; + + // Get size of pointee of CastExpr + auto CEPointeeTy = CE->getType()->getAs()->getPointeeType(); + const auto CEPointeeTySizeInBits = Ctx.getTypeSize(CEPointeeTy); + if (CEPointeeTySizeInBits == 0) + return false; + + if (CEPointeeTySizeInBits <= LargestSinglePointeeSizeInBits) + return false; + + if (TestOnly) + return true; + + // Found unsafe bitcast + UnsafeBitCastsToSkip.insert(CE); + Handler.handleSingleEntitiesFlowingToIndexableVariableUnsafelyCasted( + SingleEntities, IndexableVar, UnsafeOp, Kind, Operand); + return true; +} + +using WillTrapKind = clang::BoundsSafetySuggestionHandler::WillTrapKind; +static WillTrapKind +TryDetermineIfBidiIndexableToDynamicCountWithConstantCountTraps( + const CountAttributedType *DCPT, const APInt &CountExprAsConstant, + size_t LargestSinglePointeeTySizeInBits, size_t CastPointeeTySizeInBits) { + const auto CountExprBitWidth = CountExprAsConstant.getBitWidth(); + if (CountExprAsConstant.isZero()) { + // The bounds in the `__bidi_indexable` are guaranteed to be >= 0 + // bytes/objects so this can't trap. + return WillTrapKind::NoTrap; + } + if (DCPT->isCountInBytes()) { + // __sized_by(_or_null) + auto LargestSinglePointeeTySizeInBytes = + llvm::APInt(CountExprBitWidth, LargestSinglePointeeTySizeInBits / 8); + if (CountExprAsConstant.ule(LargestSinglePointeeTySizeInBytes)) { + // The bounds of the __bidi_indexable are larger or equal to the + // constant byte count. In this case the only possible trap is caused + // by the pointer being null. + if (DCPT->isOrNull()) { + // Won't trap even if nullptr passed + return WillTrapKind::NoTrap; + } + // Won't trap unless ptr is null + return WillTrapKind::TrapIffPtrNull; + } + + // The bounds of the __bidi_indexable are smaller than the constant + // byte count expected. So the bounds are insufficient. Whether or not + // this traps depends on the attribute type and the pointer value. + if (DCPT->isOrNull()) { + // If the pointer is not null then the bounds are insufficient, so + // this traps. + return WillTrapKind::TrapIffPtrNotNull; + } + // Always traps + return WillTrapKind::Trap; + } + + // __counted_by(_or_null) + const auto CastPointeeTySizeInBitsAP = + APInt(CountExprBitWidth, CastPointeeTySizeInBits); + const auto MinimumBufferSizeInBitsAP = + CastPointeeTySizeInBitsAP * CountExprAsConstant; + const auto LargestSinglePointeeTySizeInBitsAP = + APInt(CountExprBitWidth, LargestSinglePointeeTySizeInBits); + + if (LargestSinglePointeeTySizeInBitsAP.uge(MinimumBufferSizeInBitsAP)) { + // The number of bits the __counted_by_(or_null) expects is <= to + // the number of bits of the `__single` stored in the `__bidi_indexable` + // pointer. So at the point of conversion the bounds in the + // `__bidi_indexable` are big enough. + // + // If the pointee sizes are the same then this is just __counted_by(1) or + // __counted_by_or_null(1) +#ifndef NDEBUG + if (LargestSinglePointeeTySizeInBitsAP == CastPointeeTySizeInBitsAP) + assert(CountExprAsConstant.isOne()); +#endif + + if (DCPT->isOrNull()) { + // __counted_by_or_null: Won't trap. Even if nullptr passed + return WillTrapKind::NoTrap; + } + // __counted_by: Traps if and only if ptr is null + return WillTrapKind::TrapIffPtrNull; + } + + // LargestSinglePointeeTySizeInBitsAP < MinimumBufferSizeInBitsAP + // The number of bits the __counted_by_(or_null) expects is > + // the number of bits of the `__single` stored in the `__bidi_indexable`. + // So at the point of conversion the bounds in the `__bidi_indexable` + // are **not** big enough. + // + // If the pointee sizes are the same then this is + // `__counted_by()` or `__counted_by_or_null()` + // where is > 1 +#ifndef NDEBUG + if (LargestSinglePointeeTySizeInBitsAP == CastPointeeTySizeInBitsAP) + assert(CountExprAsConstant.ugt(APInt(CountExprBitWidth, 1))); +#endif + + if (DCPT->isOrNull()) { + // Traps if and only if the ptr is non-null + return WillTrapKind::TrapIffPtrNotNull; + } + + // Always traps + return WillTrapKind::Trap; +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleCastToDynamicCount( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind, ExtraDynCountLogicFn Predicate) { + + // Report casts that __bidi_indexable/__indexable pointers + // to __counted_by/__sized_by + auto *CE = cast(UnsafeOp); + + if (CE->getCastKind() != clang::CK_BoundsSafetyPointerCast) + return; + + if (FBPtrCastsToSkip.contains(CE)) + return; + + // Check for __bidi_indexable/__indexable being converted + assert(Operand == CE->getSubExpr()); + if (!Operand->getType()->isPointerTypeWithBounds()) + return; + + if (!CE->getType()->isBoundsAttributedType()) + return; + + const auto *DCPT = CE->getType()->getAs(); + if (!DCPT) + return; + const auto DCPTQualTy = CE->getType(); + + // Use the largest pointee type out of all the potential __single to avoid + // potential false positives. This choice can cause false negatives. + const auto LargestSinglePointeeTy = + GetLargestSinglePointee(SingleEntities, Ctx); + const auto LargestSinglePointeeTySizeInBits = + Ctx.getTypeSizeOrNull(LargestSinglePointeeTy); + const auto CastPointeeTy = DCPT->desugar()->getPointeeType(); + const auto CastPointeeTySizeInBits = Ctx.getTypeSizeOrNull(CastPointeeTy); + + // Try to see if the count expr is a constant + WillTrapKind WillTrap = WillTrapKind::Unknown; + auto CountExprAsConstant = + EvaluateAsInt(DCPT->getCountExpr()->IgnoreParens()); + if (CountExprAsConstant) { + WillTrap = TryDetermineIfBidiIndexableToDynamicCountWithConstantCountTraps( + DCPT, *CountExprAsConstant, LargestSinglePointeeTySizeInBits, + CastPointeeTySizeInBits); + } + + // Compute the maximum count/size expression that can be used without leading + // to a trap. Any count/size > will trap. + // E.g. + // int foo(int* __counted_by(size) b, size_t size); + // + // void use_foo(int* b, size_t s) { + // // The maximum value `s` can be is 1. + // foo(b, s); + // } + size_t MaxSafeSizeOrCount = 0; + if (DCPT->isCountInBytes()) { + // __sized_by(or_null) + MaxSafeSizeOrCount = LargestSinglePointeeTySizeInBits / 8; + } else { + // __counted_by(_or_null) + // + // If the pointee types are the same size this is 1. + // If the __single pointee size is < DCPT pointee size then this is 0 + // If the __single pointee size is > DCPT pointee size then this >= 1. + MaxSafeSizeOrCount = + LargestSinglePointeeTySizeInBits / CastPointeeTySizeInBits; + } + + // Run custom logic to suppress this warning or determine that the count + // expression is a constant. + if (Predicate) { + WillTrap = Predicate(WillTrap, LargestSinglePointeeTySizeInBits, + CastPointeeTySizeInBits, CountExprAsConstant); + } + + // Make sure we don't visit this cast again. + FBPtrCastsToSkip.insert(CE); + + // Technically if `Kind == UnsafeOpKind::Return` then we will never trap due + // a bug where the bounds checks are missing (rdar://83900556). We ignore that + // problem here and let the handler decide what to do. + + switch (WillTrap) { + case WillTrapKind::NoTrap: + // Nothing to warn about. + return; + case WillTrapKind::TrapIffPtrNull: + // TODO: We should probably warn about this but we probably want to do this + // elsewhere (i.e. not in the analysis). + // For now just drop this. + return; + case WillTrapKind::Unknown: // Might trap + case WillTrapKind::Trap: // Definitely traps + case WillTrapKind::TrapIffPtrNotNull: // Traps in almost all cases + Handler.handleSingleEntitiesFlowingToIndexableDynamicCountConversion( + SingleEntities, IndexableVar, UnsafeOp, Kind, Operand, DCPTQualTy, + WillTrap, CountExprAsConstant, MaxSafeSizeOrCount); + break; + } +} + +void UnsafeOperationVisitor:: + checkSingleEntityFlowingToIndexableLocalVariableHandleEscapingCastConvertedToDynamicCount( + const Stmt *UnsafeOp, const Expr *Operand, const VarDecl *IndexableVar, + llvm::SmallVectorImpl + &SingleEntities, + UnsafeOpKind Kind) { + assert(Kind == UnsafeOpKind::CallArg || Kind == UnsafeOpKind::Return || + Kind == UnsafeOpKind::Assignment); + + const auto *DCPT = Operand->getType()->getAs(); + if (!DCPT) + return; + + // Set up code to lazily compute the count expression with call parameters + // replaced with arguments used at call site. + const Expr *CountExprUsingParams = nullptr; + const Expr *CountExprUsingArguments = nullptr; + const auto *Call = dyn_cast(UnsafeOp); + auto LazilyInitCountExprUsingArguments = [&]() { + if (CountExprUsingArguments) + return; + ExprResult Replaced = + ReplaceCountExprParamsWithArgsFromCall(CountExprUsingParams, Call, S); + if (auto *NewCountExpr = Replaced.get()) { + CountExprUsingArguments = NewCountExpr; + } + }; + + auto CallExprPredicate = + [&](WillTrapKind WillTrap, size_t LargestSinglePointeeTySizeInBits, + size_t CastPointeeTySizeInBits, + std::optional &CountExprAsConstant) -> WillTrapKind { + // This predicate tries to see if it can statically determine + // if the cast will fail. It does this by trying to see if the + // __counted_by/__sized_by size expression is a constant if the + // call arguments are substituted into the expression. + if (WillTrap != WillTrapKind::Unknown) { + // Trust the existing analysis + return WillTrap; + } + + LazilyInitCountExprUsingArguments(); + const auto CountExprUsingArgumentsAsConstant = + EvaluateAsInt(CountExprUsingArguments); + + if (CountExprUsingArgumentsAsConstant) { + CountExprAsConstant = + *CountExprUsingArgumentsAsConstant; // Pass back to caller + WillTrap = + TryDetermineIfBidiIndexableToDynamicCountWithConstantCountTraps( + DCPT, *CountExprUsingArgumentsAsConstant, + LargestSinglePointeeTySizeInBits, CastPointeeTySizeInBits); + } + + return WillTrap; + }; + + ExtraDynCountLogicFn Predicate = nullptr; + // Currently only `CallArg` needs a special predicate. + if (Kind == UnsafeOpKind::CallArg) { + assert(Call); + CountExprUsingParams = DCPT->getCountExpr(); + Predicate = CallExprPredicate; + } + + // FIXME: Ideally we'd support constant evaluation for returns and + // variable assignment too. Unfortunately we don't anyway right now + // of knowing what values the variables referred to in the count expression + // could take. We'd need a reaching-definition analysis to do this. + + // Walk through the Operand to find potential CK_BoundsSafetyPointerCast's to + // warn about. Although the visitor will normally find this cast later on its + // done this way here so that when the cast is found the extra context (e.g. + // the cast happens at a call site) needed to emit the diagnostic. + const Expr *Current = Operand; + const Expr *Previous = nullptr; + while (Current != Previous) { + Previous = Current; + if (const auto *PE = dyn_cast(Current)) { + Current = PE->getSubExpr(); + continue; + } + if (const auto *CE = dyn_cast(Current)) { + if (CE->getCastKind() == clang::CK_BoundsSafetyPointerCast) { + // Found a cast we might want to warn about. + checkSingleEntityFlowingToIndexableLocalVariableHandleCastToDynamicCount( + CE, CE->getSubExpr(), IndexableVar, SingleEntities, Kind, + Predicate); + } + Current = CE->getSubExpr(); + } + } +} + +void UnsafeOperationVisitor::VisitArraySubscriptExpr( + const ArraySubscriptExpr *ASE) { + handleUnsafeOperation(ASE, ASE->getBase(), UnsafeOpKind::Index); + + // Continue visitation normally. + VisitChildren(ASE); +} + +void UnsafeOperationVisitor::VisitUnaryOperator(const UnaryOperator *UO) { + + switch (UO->getOpcode()) { + case UO_PostInc: + case UO_PostDec: + case UO_PreInc: + case UO_PreDec: + handleUnsafeOperation(UO, UO->getSubExpr(), UnsafeOpKind::Arithmetic); + break; + case UO_Deref: + handleUnsafeOperation(UO, UO->getSubExpr(), UnsafeOpKind::Deref); + break; + default: + break; + } + + // Continue visitation normally. + VisitChildren(UO); +} + +void UnsafeOperationVisitor::VisitBinaryOperator(const BinaryOperator *BO) { + const Expr *Lhs = BO->getLHS(), *Rhs = BO->getRHS(); + QualType LhsTy = Lhs->getType(), RhsTy = Rhs->getType(); + + switch (BO->getOpcode()) { + case BO_Add: + // n + ptr, a special case that only works with BO_Add. + if (RhsTy->isAnyPointerType() && LhsTy->isIntegralOrEnumerationType()) { + handleUnsafeOperation(BO, Rhs, UnsafeOpKind::Arithmetic); + } + LLVM_FALLTHROUGH; // Fall through to handle ptr + n + case BO_Sub: + case BO_AddAssign: + case BO_SubAssign: + // ptr + n, ptr - n, ptr += n, ptr -= n, i.e. the typical case. + if (LhsTy->isAnyPointerType() && RhsTy->isIntegralOrEnumerationType()) { + handleUnsafeOperation(BO, Lhs, UnsafeOpKind::Arithmetic); + } + break; + case BO_Assign: { + if (RhsTy->isPointerType()) { + handleUnsafeOperation(BO, Rhs, UnsafeOpKind::Assignment); + } + break; + } + default: + break; + } + + // Continue visitation normally. + VisitChildren(BO); +} + +void UnsafeOperationVisitor::VisitMemberExpr(const MemberExpr *ME) { + if (ME->isArrow()) { + assert(ME->getBase()->getType()->isPointerType()); + handleUnsafeOperation(ME, ME->getBase(), UnsafeOpKind::MemberAccess); + } + + // Continue visitation normally. + VisitChildren(ME); +} + +void UnsafeOperationVisitor::VisitOpaqueValueExpr(const OpaqueValueExpr *OVE) { + // OpaqueValueExpr doesn't have any children so Visit() won't traverse the + // SourceExpr so we need to handle it manually here. + const auto *SrcExpr = OVE->getSourceExpr(); + if (!SrcExpr) + return; + + // OVEs wrap an expression that should only be evaluated once. Thus this + // visitor needs to make sure it only visits the SourceExpr once, otherwise it + // will warn about the same unsafe operation multiple times. + if (VisitedOVESourceExprs.contains(SrcExpr)) + return; // Don't visit again + + VisitedOVESourceExprs.insert(SrcExpr); + Visit(SrcExpr); +} + +void UnsafeOperationVisitor::VisitDeclStmt(const DeclStmt *DS) { + for (const Decl *D : DS->decls()) { + if (const auto *VD = dyn_cast(D)) { + if (const Expr *E = VD->getInit()) { + if (E->getType()->isPointerType()) { + handleUnsafeOperation(DS, E, UnsafeOpKind::Assignment); + } else if (const auto *ILE = dyn_cast(E)) { + // Struct/Union initializer. + // Note `InitListExpr` may be nested so iterative DFS + // is used to walk through nested `InitListExpr` and visit + // all the initializers. + llvm::SmallVector> WorkList; + + // Collect initial set of initializers + WorkList.reserve(ILE->getNumInits()); + auto CollectInitializers = [&](const InitListExpr *ILE) -> void { + if (ILE->getNumInits() == 0) + return; + // Add in reverse order so that when the worklist is processed + // the initializers are visited in order + for (size_t Index = ILE->getNumInits(); Index > 0; --Index) { + WorkList.push_back(std::make_tuple(ILE, ILE->getInit(Index - 1))); + } + }; + CollectInitializers(ILE); + + while (!WorkList.empty()) { + const Expr* Initializer; + const InitListExpr* ParentILE; + + std::tie(ParentILE, Initializer) = WorkList.back(); + WorkList.pop_back(); + if (const auto *SubILE = dyn_cast(Initializer)) { + CollectInitializers(SubILE); + continue; + } + // Field initializer of something that's not a union or struct + // (i.e. a leaf of "InitListExpr" graph). + if (Initializer->getType()->isPointerType()) { + handleUnsafeOperation(ParentILE, Initializer, + UnsafeOpKind::Assignment); + } + } + } + } + } + } + + // This handles visiting the children. We don't do that above because the + // visited Decls aren't necessarily `VarDecl`s (e.g. `TypedefDecl`) + VisitChildren(DS); +} + +void UnsafeOperationVisitor::VisitReturnStmt(const ReturnStmt *RS) { + const auto *const ReturnExpr = RS->getRetValue(); + if (ReturnExpr && ReturnExpr->getType()->isPointerType()) { + handleUnsafeOperation(RS, ReturnExpr, UnsafeOpKind::Return); + } + + // Visit children to look for other unsafe operations + VisitChildren(RS); +} + +void UnsafeOperationVisitor::VisitCallExpr(const CallExpr *CE) { + for (const auto &Arg : CE->arguments()) { + if (Arg->getType()->isPointerType()) { + handleUnsafeOperation(CE, Arg, UnsafeOpKind::CallArg); + } + } + + // Visit children to look for other unsafe operations + VisitChildren(CE); +} + +void UnsafeOperationVisitor::VisitCastExpr(const CastExpr *CE) { + if (CE->getSubExpr()->getType()->isPointerType()) { + handleUnsafeOperation(CE, CE->getSubExpr(), UnsafeOpKind::Cast); + } + + // Visit children to look for other unsafe operations + VisitChildren(CE); +} + +void clang::checkBoundsSafetySuggestions(const Decl *D, + BoundsSafetySuggestionHandler &Handler, + Sema &S) { + const Stmt *Body = D->getBody(); + assert(Body); + + // First pass: Populate DefMap by connecting variables + // to their potential values. + DefinitionVisitor DefV; + DefV.Visit(Body); + + // Second pass: Scan the function for unsafe operations. + // Use DefMap to assess where the operands are coming from. + // + // Fundamentally all of this could have been done in one pass, + // with some postprocessing, but it's nice to have some + // separation of concerns. + UnsafeOperationVisitor OpV(Handler, DefV.DefMap, S); + OpV.Visit(Body); + OpV.reset(); +} \ No newline at end of file diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 4b87004e4b8ea..10eee5c609454 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -15,10 +15,17 @@ clang_tablegen(OpenCLBuiltins.inc -gen-clang-opencl-builtins add_clang_library(clangSema AnalysisBasedWarnings.cpp + # TO_UPSTREAM(BoundsSafety) ON + BoundsSafetySuggestions.cpp + # TO_UPSTREAM(BoundsSafety) OFF CheckExprLifetime.cpp CodeCompleteConsumer.cpp DeclSpec.cpp DelayedDiagnostic.cpp + # TO_UPSTREAM(BoundsSafety) ON + DynamicCountPointerAssignmentAnalysis.cpp + DynamicCountPointerAssignmentAnalysisExported.cpp + # TO_UPSTREAM(BoundsSafety) OFF HeuristicResolver.cpp HLSLBuiltinTypeDeclBuilder.cpp HLSLExternalSemaSource.cpp @@ -57,6 +64,7 @@ add_clang_library(clangSema SemaExprCXX.cpp SemaExprMember.cpp SemaExprObjC.cpp + SemaFeatureAvailability.cpp SemaFixItUtils.cpp SemaFunctionEffects.cpp SemaHLSL.cpp diff --git a/clang/lib/Sema/DelayedDiagnostic.cpp b/clang/lib/Sema/DelayedDiagnostic.cpp index cb2721b92090e..ff814a0e9b140 100644 --- a/clang/lib/Sema/DelayedDiagnostic.cpp +++ b/clang/lib/Sema/DelayedDiagnostic.cpp @@ -56,6 +56,18 @@ DelayedDiagnostic::makeAvailability(AvailabilityResult AR, return DD; } +DelayedDiagnostic +DelayedDiagnostic::makeFeatureAvailability(NamedDecl *D, + ArrayRef Locs) { + assert(!Locs.empty()); + DelayedDiagnostic DD; + DD.Kind = FeatureAvailability; + DD.Triggered = false; + DD.Loc = Locs.front(); + DD.FeatureAvailabilityData.Decl = D; + return DD; +} + void DelayedDiagnostic::Destroy() { switch (Kind) { case Access: @@ -67,6 +79,7 @@ void DelayedDiagnostic::Destroy() { delete[] AvailabilityData.SelectorLocs; break; + case FeatureAvailability: case ForbiddenType: break; } diff --git a/clang/lib/Sema/DynamicCountPointerAssignmentAnalysis.cpp b/clang/lib/Sema/DynamicCountPointerAssignmentAnalysis.cpp new file mode 100644 index 0000000000000..d24dbcb300109 --- /dev/null +++ b/clang/lib/Sema/DynamicCountPointerAssignmentAnalysis.cpp @@ -0,0 +1,3667 @@ +//===--- DynamicCountPointerAssignmentAnalysis.cpp ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements semantic analysis for dynamic count pointer assignments +// for -fbounds-safety based on CFGBlock analysis. +// +//===----------------------------------------------------------------------===// + +#include "DynamicCountPointerAssignmentAnalysis.h" +#include "clang/AST/Attr.h" +#include "clang/AST/EvaluatedExprVisitor.h" +#include "clang/AST/Expr.h" +#include "clang/AST/IgnoreExpr.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Sema/SemaInternal.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/SaveAndRestore.h" + +#include + +using namespace clang; + +namespace { +/// This is to generate a unique key for members in nested structs. This is +/// necessary to distinguish member accesses of nested structs in different +/// parent structs as well as members of same structs from different instances. +/// @code +/// struct nested { int i; }; +/// struct parent { struct nested n1; struct nested n2; }; +/// struct parent p1; +/// struct parent p2; +/// @endcode +/// For the above example, this function distinguishes member accesses "p1.n1.i" +/// and "p1.n2.i", and "p1.n1.i" and "p2.n1.i". +void computeMemberExprKey(const Expr *E, llvm::raw_string_ostream &OS, + bool Top = true) { + E = E->IgnoreParenCasts(); + if (auto ME = dyn_cast(E)) { + computeMemberExprKey(ME->getBase(), OS, false); + OS << (ME->isArrow() ? "->" : "."); + if (!Top) { + OS << ME->getMemberDecl()->getName(); + } + return; + } else if (auto DE = dyn_cast(E)) { + OS << DE->getDecl()->getDeclName(); + } else if (auto OVE = dyn_cast(E)) { + return computeMemberExprKey(OVE->getSourceExpr(), OS, Top); + } +} + +/// AssignedDeclRefResult - This represents result of analysis for a declaration +/// reference in LHS or RHS of variable assignment operation in terms of dynamic +/// counts or dynamic count pointers including params, locals, and fields. +/// @code +/// struct S { int *__counted_by(n) ptr; int n; }; +/// S s; +/// s.ptr = ptrVal; +/// s.n = intVal; +/// @endcode +/// For instance, this holds information whether "s.ptr" in the assignment +/// operation is dynamic count or dynamic count pointer and ditto for "s.n". +struct AssignedDeclRefResult { + /// The decl reference should be tracked. It is dynamic count or dynamic count + /// pointer. + unsigned IsTrackedVar : 1; + /// The decl reference is dynamic range pointer. + unsigned IsRangePtrVar : 1; + /// The decl reference is dynamic count. + unsigned IsCountVar : 1; + /// The decl reference is dynamic count pointer. + unsigned IsCountPtrVar : 1; + /// The dynamic count is out parameter. + unsigned IsOutCount : 1; + /// The dynamic count pointer is out parameter. + unsigned IsOutBuf : 1; + /// The decl is a single pointer to struct with flexible array member. + unsigned IsFlexBase : 1; + /// The decl is a nested struct containing a dynamic count decl + unsigned IsInnerStruct : 1; + unsigned IsCountForFam : 1; + /// The decl is a dependent param that is referred to by the return type that + /// is a bounds-attributed type. + unsigned IsDependentParamOfReturnType : 1; + /// Dereference level of decl reference. Dereference level of decl reference. + /// E.g., "**p = " has Level 2 while "p = " has zero. + unsigned Level : 32 - 10; + + /// The decl actually referenced in the analyzed expression. + ValueDecl *ThisVD = nullptr; + /// Partial key calculation of DeclGroup in which ThisVD belongs. + std::string Key; + /// Pointer to struct with fam as a key. + std::string FlexBaseKey; + /// Type of assignment expression. + QualType Ty; + /// Decl of struct containing flexible array member. + RecordDecl *FlexBaseDecl = nullptr; + /// Loc of decl reference expression. + SourceLocation Loc; + + AssignedDeclRefResult() { + IsTrackedVar = 0; + IsRangePtrVar = 0; + IsCountPtrVar = 0; + IsCountVar = 0; + IsOutCount = 0; + IsOutBuf = 0; + IsFlexBase = 0; + IsInnerStruct = 0; + IsCountForFam = 0; + IsDependentParamOfReturnType = 0; + Level = 0; + } +}; + +static ExprResult CastToCharPointer(Sema &S, Expr *PointerToCast) { + SourceLocation Loc = PointerToCast->getBeginLoc(); + auto PtrTy = PointerToCast->getType()->getAs(); + auto SQT = PtrTy->getPointeeType().split(); + SQT.Ty = S.Context.CharTy.getTypePtr(); + QualType QualifiedChar = S.Context.getQualifiedType(SQT); + QualType CharPtrTy = S.Context.getPointerType( + QualifiedChar, PtrTy->getPointerAttributes()); + return S.BuildCStyleCastExpr( + Loc, S.Context.getTrivialTypeSourceInfo(CharPtrTy), Loc, PointerToCast); +} + +/// getInnerType - This returns "Level" nested pointee type. +QualType getInnerType(QualType Ty, unsigned Level) { + for (unsigned i = 0; i < Level; ++i) { + if (!Ty->isPointerType()) + return QualType(); + Ty = Ty->getPointeeType(); + } + return Ty; +} + +bool isValueDeclOutBuf(const ValueDecl *VD) { + QualType Ty = VD->getType(); + return isa(VD) && Ty->isPointerType() && + Ty->getPointeeType()->isBoundsAttributedType(); +} + +bool isValueDeclOutCount(const ValueDecl *VD) { + const auto *Att = VD->getAttr(); + return isa(VD) && Att && Att->getIsDeref(); +} + +bool isSinglePtrToStructWithFam(Sema &S, QualType Ty) { + if (!Ty->isSinglePointerType() || Ty->isBoundsAttributedType()) + return false; + + FlexibleArrayMemberUtils FlexUtils(S); + return FlexUtils.GetFlexibleRecord(Ty->getPointeeType()); +} + +/// analyzeAssignedDeclCommon - This shares analysis logic for declaration +/// reference from assignment expression and variable declaration statement. +void analyzeAssignedDeclCommon(Sema &S, ValueDecl *VD, QualType Ty, + AssignedDeclRefResult &Result) { + if (Ty->isCountAttributedType()) { + Result.ThisVD = VD; + Result.IsTrackedVar = 1; + Result.IsCountPtrVar = 1; + return; + } + + if (Result.Level == 0 && isa(VD) && Ty->isPointerType() && + Ty->getPointeeType()->isCountAttributedType()) { + Result.IsOutBuf = 1; + return; + } + + const auto *Attr = VD->getAttr(); + + const BoundsAttributedType *RetType = nullptr; + const TypeCoupledDeclRefInfo *Info = nullptr; + bool IsDepParamOfRetType = VD->isDependentParamOfReturnType(&RetType, &Info); + if (IsDepParamOfRetType) { + Result.IsDependentParamOfReturnType = 1; + Result.ThisVD = VD; + } + + bool IsDependentCount = + Attr || (IsDepParamOfRetType && isa(RetType)); + if (IsDependentCount) { + bool IsDeref = Attr ? Attr->getIsDeref() : Info->isDeref(); + if (Result.Level == 0 && isa(VD) && IsDeref) { + Result.IsOutCount = 1; + } else if (Result.Level < 2) { + // If the VD doesn't have a DependerDeclsAttr, this VD is used only in the + // return type. For example: + // int *__counted_by(count) foo(int count); + // In that case, the VD can be freely updated and we won't generate a + // bounds-check for the updates, so set IsTrackedVar to false. + Result.IsTrackedVar = !!Attr; + Result.ThisVD = VD; + if (Ty->isIntegralOrEnumerationType()) + Result.IsCountVar = 1; + else + Result.IsInnerStruct = 1; + } else + llvm_unreachable( + "Dependent count reference can only be a single-level indirection"); + return; + } + + if (Ty->isDynamicRangePointerType()) { + Result.ThisVD = VD; + Result.IsTrackedVar = 1; + Result.IsRangePtrVar = 1; + return; + } + + if (isSinglePtrToStructWithFam(S, Ty)) { + Result.ThisVD = VD; + Result.IsTrackedVar = 1; + Result.IsFlexBase = 1; + return; + } +} + +/// analyzeVarDecl - This function analyzes variable declaration like +/// "int *__counted_by(len) ptr;" or "int len;". +void analyzeVarDecl(Sema &S, VarDecl *Var, AssignedDeclRefResult &Result) { + Result.Ty = Var->getType(); + Result.Loc = Var->getLocation(); + analyzeAssignedDeclCommon(S, Var, Result.Ty, Result); +} + +/// Check whether the dep group contains a flexible array member, +/// and update FlexBaseKey and FlexBaseDecl if so, for matching +/// with the relevant DepGroup. +/// Because counted_by pointers can share count fields with the +/// FAM, this relation can be indirect. +/// In the code below, assigning to \c SharedCount::p requires assigning +/// to \c SharedCount::len, which in turns requires assigning to the +/// flexbase pointer from a wide pointer: +/// \code +/// struct SharedCount { +/// int * __counted_by(len) p; +/// int len; +/// int fam[__counted_by(len)]; +/// }; +/// \endcode +/// +/// Fields in structs that are nested in other structs can be referred +/// to from fields in the outer structs. Care must be taken to only check +/// the dependent fields when the inner struct type is actually used in the +/// context of the outer struct. This is checked with \c isParentStructOf(). +/// Example of irrelevant dep decl ( \c SimpleOuter1::fam): +/// \code +/// struct SimpleInner { +/// int dummy; +/// int len; +/// }; +/// struct SimpleOuter1 { +/// struct SimpleInner hdr; +/// char fam[__counted_by(hdr.len)]; +/// }; +/// void set_len(struct SimpleInner * p) { +/// // struct SimpleInner doesn't contain any FAMs, so not checked +/// // -fbounds-safety doesn't allow taking address of +/// // \c SimpleOuter1::hdr though, so this is never referred to by +/// // a FAM and thus safe +/// p->len = 2; +/// } +/// struct SimpleOuter2 { +/// struct SimpleInner hdr; +/// char fam[__counted_by(hdr.len)]; +/// }; +/// struct SimpleOuter2 *foo(int len) { +/// // This is a flex base pointer, but \c SimpleOuter1::fam should not be +/// // included in the analysis in this context since it's the +/// struct SimpleOuter2 *p = malloc(sizeof(struct SimpleOuter2) + len); +/// p->hdr.len = len; +/// return p; +/// } +/// \endcode +/// +/// Even if a pointer doesn't directly share a count variable with a FAM, +/// it may belong to the same group indirectly as seen in \c p1 below: +/// \code +/// struct S { +/// int len1; +/// int len2; +/// int * __counted_by(len1) p1; +/// int * __counted_by(len1 + len2) p2; +/// int fam[__counted_by(len2)]; +/// }; +/// \endcode +void analyzeFlexBase(Sema &S, ValueDecl *VD, Expr *E, + AssignedDeclRefResult &Result) { + std::string FlexBase; + llvm::raw_string_ostream FlexBaseOS(FlexBase); + RecordDecl *FlexBaseDecl = + DynamicCountPointerAssignmentAnalysis::computeFlexBaseKey(E, &FlexBaseOS); + if (!FlexBaseDecl) + return; + + // Recursion is needed to check indirect flexbase members, so we need to track + // which decls we've already visited. + llvm::SmallPtrSet Visited; + std::function CheckDependentDecls; + CheckDependentDecls = [&Visited, &CheckDependentDecls, &FlexBaseDecl, + &FlexBase, &Result](const ValueDecl *VD) { + if (!FlexBaseDecl->isParentStructOf(VD)) + return false; // if this is a nested field referred to by outer structs, + // only consider fields in the scope of the relevant struct. + if (!Visited.insert(VD).second) + return false; + + if (VD->getType()->isIncompleteArrayType() && + VD->getType()->isCountAttributedType()) { + Result.FlexBaseKey = FlexBase; + Result.FlexBaseDecl = FlexBaseDecl; + return true; + } + + if (auto *Att = VD->getAttr()) + for (const auto *D : Att->dependerDecls()) + if (CheckDependentDecls(cast(D))) + return true; + + const auto *DCPTy = VD->getType()->getAs(); + if (!DCPTy && VD->getType()->isPointerType()) + DCPTy = VD->getType()->getPointeeType()->getAs(); + + if (!DCPTy) + return false; + + for (const auto &DI : DCPTy->dependent_decls()) + if (CheckDependentDecls(cast(DI.getDecl()))) + return true; + return false; + }; + + if (auto *Att = VD->getAttr()) { + // CheckDependentDecls searches depth first, so check all the direct + // neighbors first to correctly set the IsCountForFam. If a __counted_by + // pointer shares count with a FAM it should belong to the same group and + // thus requires a FlexBaseKey. But it is not the count, and any count + // variables not shared should not be treated as count for FAM, despite + // having FlexBaseKey. + for (const auto *D : Att->dependerDecls()) { + const auto *DepVD = cast(D); + if (FlexBaseDecl->isParentStructOf(DepVD) && + DepVD->getType()->isIncompleteArrayType() && + DepVD->getType()->isCountAttributedType()) { + Result.FlexBaseKey = FlexBase; + Result.FlexBaseDecl = FlexBaseDecl; + Result.IsCountForFam = true; + return; + } + } + } + + // Check if this is a pointer that shares a count with a FAM. + CheckDependentDecls(VD); +} + +/// analyzeAssignedDeclRef - This function analyzes declaration reference in +/// assignment expression, including DeclRefExpr and MemberExpr. This also +/// supports analysis of dereferenced decl like "*p". +void analyzeAssignedDeclRef(Sema &S, Expr *E, AssignedDeclRefResult &Result) { + E = E->IgnoreParenCasts(); + if (auto UO = dyn_cast(E)) { + /// This is to support cases like '*&*ptr = ...'. + switch (UO->getOpcode()) { + case UO_Deref: + Result.Level++; + break; + case UO_AddrOf: + /// '&x' would be non-assignable'. + if (Result.Level == 0) + return; + Result.Level--; + break; + default: + return; + } + return analyzeAssignedDeclRef(S, UO->getSubExpr(), Result); + } + + ValueDecl *VD = nullptr; + std::string Prefix; + if (auto ME = dyn_cast(E)) { + llvm::raw_string_ostream PrefixOS(Prefix); + computeMemberExprKey(ME, PrefixOS); + VD = ME->getMemberDecl(); + analyzeFlexBase(S, VD, ME, Result); + } else if (auto DRE = dyn_cast(E)) { + VD = DRE->getDecl(); + } + if (!VD) + return; + + QualType Ty = getInnerType(E->getType(), Result.Level); + if (Ty.isNull()) + return; + + Result.Ty = E->getType(); + Result.Loc = E->getExprLoc(); + Result.Key = Prefix; + + analyzeAssignedDeclCommon(S, VD, Ty, Result); +} + +bool ReplaceSubExpr(ParentMap &PM, Expr *SubExpr, Expr *NewSubExpr) { + Stmt *Parent = PM.getParent(SubExpr); + if (!Parent) + return false; + + for (auto &C : Parent->children()) { + if (C == SubExpr) { + *&C = NewSubExpr; + PM.setParent(NewSubExpr, Parent); + PM.setParent(SubExpr, NewSubExpr); + return true; + } + } + return false; +} + +/// Build the bounds check expression for delayed count/range checks. +class PreAssignCheck { + Sema &SemaRef; + ParentMap &PM; + Expr *GuardedValue; + +public: + using DeclToNewValueTy = llvm::DenseMap>; + +private: + ReplaceDeclRefWithRHS DeclReplacer; + +public: + PreAssignCheck(Sema &SemaRef, ParentMap &PM, Expr *GuardedValue, + DeclToNewValueTy &DeclToNewValue) + : SemaRef(SemaRef), + PM(PM), + GuardedValue(GuardedValue), + DeclReplacer(SemaRef, DeclToNewValue) {} + +private: + ExprResult createZeroValue(Sema &S, QualType Ty) { + auto &Ctx = SemaRef.Context; + auto BitWidth = Ctx.getTypeSize(Ctx.IntTy); + llvm::APInt ZeroVal(BitWidth, 0); + ExprResult Zero = IntegerLiteral::Create(SemaRef.Context, ZeroVal, Ctx.IntTy, + SourceLocation()); + CastKind CK = CastKind::CK_NoOp; + auto ConversionType = S.CheckAssignmentConstraints(Ty, Zero, CK, true); + if (ConversionType == Sema::AssignConvertType::Incompatible) { + // `CK` is not set for this return value so bail out. + return ExprError(); + } + + switch (CK) { + case CK_NoOp: + return Zero.get(); + case CK_IntegralToPointer: + CK = CK_NullToPointer; + LLVM_FALLTHROUGH; + default: + return S.ImpCastExprToType(Zero.get(), Ty, CK); + } + } + +public: + + using TypeExprPairTy = std::pair; + + ExprResult getMaterializedValueIfNot( + Expr *E, SmallVectorImpl *MateredExprs = nullptr, + bool UpdateParent = true) { + if (isa(E)) + return E; + + if (isa(E)) + return createZeroValue(SemaRef, E->getType()); + + OpaqueValueExpr *OVE = OpaqueValueExpr::Wrap(SemaRef.Context, E); + if (MateredExprs) { + MateredExprs->push_back(OVE); + } + if (UpdateParent) + ReplaceSubExpr(PM, E, OVE); + return OVE; + } + + void insertDeclToNewValue(const ValueDecl *VD, Expr *New, unsigned Level = 0) { + DeclReplacer.DeclToNewValue[VD] = std::make_pair(New, Level); + } + + void setMemberBase(Expr *Base) { + DeclReplacer.MemberBase = Base; + } + + // UpFront -> Materialize something + // MateredExprsInFlight is not... + // We need InitListExpr to be in UpFront! + ExprResult build(SmallVectorImpl &Pairs, InitListExpr *IL, + SmallVectorImpl &MateredExprs) { + Expr *WrappedExpr = IL; + // Reverse so that the first check is inserted at the outermost. + for (auto I = Pairs.rbegin(), E = Pairs.rend(); I != E; ++I) { + auto Pair = *I; + ExprResult R; + SmallVectorImpl *MateredExprsPtr = + (Pair == Pairs.front()) ? &MateredExprs : nullptr; + if (const auto *DCPTy = Pair.first->getAs()) { + R = build(DCPTy, Pair.second, WrappedExpr, MateredExprsPtr); + } else { + const auto *DRPTy = Pair.first->getAs(); + assert(DRPTy); + R = build(DRPTy, Pair.second, WrappedExpr, MateredExprsPtr); + } + if (R.isInvalid()) + return ExprError(); + WrappedExpr = R.get(); + } + return WrappedExpr; + } + + ExprResult build(QualType Ty, Expr *WidePtr, Expr *WrappedValue = nullptr, + SmallVectorImpl *MateredExprs = nullptr) { + if (const auto *DCPTy = Ty->getAs()) { + return build(DCPTy, WidePtr, WrappedValue, MateredExprs); + } else { + const auto *DRPTy = Ty->getAs(); + assert(DRPTy); + return build(DRPTy, WidePtr, WrappedValue, MateredExprs); + } + } + + /// This is to build the trap condition necessary for a sized_by or counted_by pointer and its related + /// count assignments like: Count = NewCount; Ptr = WidePtr; + /// + /// The condition to check will look like below: + /// \code + /// 0 <= NewCount && (lower(WidePtr) <= WidePtr <= upper(WidePtr)) && + /// NewCount <= upper(WidePtr) - WidePtr + /// \endcode + /// The reason why we do `NewCount <= upper(WidePtr) - WidePtr` instead of + /// `WidePtr + NewCount <= upper(WidePtr)` is that the backend optimizes the former better because + /// for the latter the compiler needs to consider integer overflow on the pointer arithmetic. + /// For `upper(WidePtr) - WidePtr`, on the other hand, we can assume no overflow is happening because + /// we already checked `WidePtr <= upper(WidePtr)`. + ExprResult build(const CountAttributedType *Ty, Expr *WidePtr, + Expr *WrappedValue = nullptr, + SmallVectorImpl *MateredExprs = nullptr) { + if (!WrappedValue) + WrappedValue = GuardedValue; + auto &Ctx = SemaRef.Context; + + SmallVector CommonExprs; + if (!MateredExprs) { + MateredExprs = &CommonExprs; + } + + assert(!Ty->getCountExpr()->HasSideEffects(Ctx)); + ExprResult CountR = DeclReplacer.TransformExpr(Ty->getCountExpr()); + if (CountR.isInvalid()) + return ExprError(); + Expr *Count = CountR.get(); + assert(!Count->HasSideEffects(Ctx)); + + SourceLocation Loc = WrappedValue->getBeginLoc(); + // Later, we might be able to merge diagnostics to check if count is + // non-constant when a pointer is null here instead of doing it separately. + if (isa(WidePtr->IgnoreImpCasts()) || + WidePtr->isNullPointerConstantIgnoreCastsAndOVEs( + SemaRef.getASTContext(), Expr::NPC_NeverValueDependent) != + Expr::NPCK_NotNull) { + if (Ty->isOrNull()) { + if (MateredExprs->empty()) + return WrappedValue; + // Materialize the OVEs, but perform the assignment unconditionally. + // This materialization happens as part of BoundsCheckExpr otherwise. + auto Result = MaterializeSequenceExpr::Create( + SemaRef.Context, WrappedValue, *MateredExprs); + Result = MaterializeSequenceExpr::Create(SemaRef.Context, Result, + *MateredExprs, true); + return Result; + } + // if wideptr == 0 then count expr should also be 0. + ExprResult Zero = createZeroValue(SemaRef, Count->getType()); + if (!Zero.get()) + return ExprError(); + ExprResult Cond = SemaRef.CreateBuiltinBinOp(Loc, BO_EQ, Count, + Zero.get()); + if (Cond.isInvalid()) + return ExprError(); + return SemaRef.BuildBoundsCheckExpr(WrappedValue, Cond.get(), *MateredExprs); + } + + ExprResult MatWidePtr = getMaterializedValueIfNot(WidePtr, MateredExprs); + if (!(WidePtr = MatWidePtr.get())) + return ExprError(); + + SmallVector WidePtrSeq; + ExprResult LowerR = SemaRef.BuildLowerBoundExpr(WidePtr, Loc, Loc, /*RawPointer*/true); + if (LowerR.isInvalid()) + return ExprError(); + + WidePtrSeq.push_back(LowerR.get()); + WidePtrSeq.push_back(WidePtr); + + ExprResult UpperR = SemaRef.BuildUpperBoundExpr(WidePtr, Loc, Loc, /*RawPointer*/true); + if (UpperR.isInvalid()) + return ExprError(); + + WidePtrSeq.push_back(UpperR.get()); + + ExprResult WidePtrSeqR = BoundsCheckBuilder::BuildLEChecks(SemaRef, + WrappedValue->getExprLoc(), + WidePtrSeq, *MateredExprs); + if (WidePtrSeqR.isInvalid()) + return ExprError(); + + // 0 <= Count + SmallVector CountCheckSeq; + if (Count->getType()->isSignedIntegerOrEnumerationType()) { + auto Zero = createZeroValue(SemaRef, Count->getType()); + if (!Zero.get()) + return ExprError(); + CountCheckSeq.push_back(Zero.get()); + } + + CountCheckSeq.push_back(Count); + + UpperR = SemaRef.BuildUpperBoundExpr(WidePtr, Loc, Loc, /*ToSingle*/true); + if (UpperR.isInvalid()) + return ExprError(); + + ExprResult WidePtrCmpOperand = WidePtr; + if (Ty->isCountInBytes()) { + UpperR = CastToCharPointer(SemaRef, UpperR.get()); + WidePtrCmpOperand = CastToCharPointer(SemaRef, WidePtrCmpOperand.get()); + if (WidePtrCmpOperand.isInvalid()) + return ExprError(); + } else { + // We can't do pointer arithmetic on incomplete pointee types. The + // `UpperMinusPtr` computation will fail this case and emit a confusing + // `err_typecheck_arithmetic_incomplete_or_sizeless_type` diagnostic. To + // avoid this bail out early. + if (UpperR.get()->getType()->getPointeeType()->isIncompleteType() || + WidePtrCmpOperand.get() + ->getType() + ->getPointeeType() + ->isIncompleteType()) + return ExprError(); + } + // Upper - Ptr + ExprResult UpperMinusPtr = SemaRef.CreateBuiltinBinOp(Loc, BO_Sub, UpperR.get(), + WidePtrCmpOperand.get()); + + if (UpperMinusPtr.isInvalid()) + return ExprError(); + + CountCheckSeq.push_back(UpperMinusPtr.get()); + // 0 <= Count <= (Upper - Ptr) + ExprResult CountCheckSeqR = BoundsCheckBuilder::BuildLEChecks(SemaRef, + Loc, + CountCheckSeq, *MateredExprs); + if (CountCheckSeqR.isInvalid()) + return ExprError(); + + if (Ty->isOrNull()) { + ExprResult NullCheck = + SemaRef.CreateBuiltinUnaryOp(Loc, UO_LNot, WidePtr); + if (NullCheck.isInvalid()) + return ExprError(); + // !Ptr || 0 <= Count <= (Upper - Ptr) + CountCheckSeqR = SemaRef.CreateBuiltinBinOp(Loc, BO_LOr, NullCheck.get(), + CountCheckSeqR.get()); + if (CountCheckSeqR.isInvalid()) + return ExprError(); + } + + // __counted_by/__sized_by: + // (Lower <= Ptr <= Upper) && (0 <= Count <= Upper - Ptr) + // __counted_by_or_null/__sized_by_or_null: + // (Lower <= Ptr <= Upper) && (!Ptr || (0 <= Count <= Upper - Ptr)) + // This allows null pointers to be assigned to *_or_null variables + // even if the count is negative or the count is larger than the range of + // the RHS (null pointers generally have 0 range). Assigning from one + // *_or_null variable to another works because the RHS is first converted to + // a 0 range __bidi_indexable if the value is null, so it still passes the + // range check. We preserve the semantics of __bidi_indexables to + // trap if OOB when converted to a type that cannot represent the OOB-ness. + // So the wide pointer (0x0, 0xBAD, 0x00B) will trap when assigned to a + // *_or_null pointer even though the base pointer is null, because we cannot + // represent the lower bound in the LHS. + ExprResult CondSeqR = SemaRef.CreateBuiltinBinOp( + Loc, BO_LAnd, WidePtrSeqR.get(), CountCheckSeqR.get()); + if (CondSeqR.isInvalid()) + return ExprError(); + + return SemaRef.BuildBoundsCheckExpr(WrappedValue, CondSeqR.get(), + *MateredExprs); + } + + /// This is to build sequential checks, `lower(WidePtr) <= start <= WidePtr <= end <= upper(WidePtr)`. + ExprResult build(const DynamicRangePointerType *Ty, Expr *WidePtr, + Expr *WrappedExpr = nullptr, + SmallVectorImpl *MateredExprs = nullptr) { + assert(!WidePtr->HasSideEffects(SemaRef.Context) || + isa(WidePtr) || WidePtr->containsErrors()); + if (!WrappedExpr) + WrappedExpr = GuardedValue; + SmallVector Bounds; + SmallVector CommonExprs; + SourceLocation Loc = WrappedExpr->getBeginLoc(); + + if (!MateredExprs) + MateredExprs = &CommonExprs; + auto &Ctx = SemaRef.Context; + if (isa(WidePtr->IgnoreImpCasts())) { + llvm::APInt Zero(Ctx.getTypeSize(WidePtr->getType()), 0); + WidePtr = IntegerLiteral::Create(SemaRef.Context, Zero, Ctx.getIntPtrType(), Loc); + + ExprResult ImpResult = SemaRef.BuildCStyleCastExpr( + Loc, Ctx.getTrivialTypeSourceInfo(QualType(Ty, 0)), Loc, WidePtr); + if (ImpResult.isInvalid()) + return ExprError(); + WidePtr = ImpResult.get(); + } + + ExprResult MatWidePtr = getMaterializedValueIfNot(WidePtr, MateredExprs); + if (!(WidePtr = MatWidePtr.get())) + return ExprError(); + + auto PushValidOrErr = [&](ExprResult Result) -> bool { + if (Expr *RE = Result.get()) { + if (auto *Lit = dyn_cast(RE)) { + // This is only possible if TransformExpr transformed an implicit + // null-to-pointer cast. + assert(Lit->getValue().isZero()); + Result = SemaRef.ImpCastExprToType(Lit, WidePtr->getType(), + CK_NullToPointer); + if (!(RE = Result.get())) + return false; + } + Bounds.push_back(RE); + return true; + } + return false; + }; + + if (WidePtr->getType()->isBidiIndexablePointerType()) { + ExprResult LowerR = SemaRef.BuildLowerBoundExpr(WidePtr, Loc, Loc, /*RawPointer*/true); + if (LowerR.isInvalid()) + return ExprError(); + Bounds.push_back(LowerR.get()); + } + + if (!PushValidOrErr(WidePtr)) + return ExprError(); + + // The pointer that has an associated end pointer is responsible to do the + // bounds checks: + // lb(new_ptr) <= start <= new_ptr <= end <= ub(new_ptr) + // Thus, we check if the pointer has an end pointer. This is consistent with + // how we handle assignments (and this is a routine we handle init lists). + // XXX: This is based on the assumption that only '__ended_by' is exposed to + // the users and '__started_by' is an implicit attribute the compiler adds + // based on the corresponding + // '__ended_by'. We may revisit this if we decide to expose '__started_by' + // to users. + if (auto *End = Ty->getEndPointer()) { + if (!PushValidOrErr(DeclReplacer.TransformExpr(End))) + return ExprError(); + } + + ExprResult UpperR = SemaRef.BuildUpperBoundExpr(WidePtr, Loc, Loc, /*RawPointer*/true); + if (UpperR.isInvalid()) + return ExprError(); + Bounds.push_back(UpperR.get()); + + ExprResult Cond = + BoundsCheckBuilder::BuildLEChecks(SemaRef, + Loc, + Bounds, *MateredExprs); + + if (Cond.isInvalid()) + return ExprError(); + return SemaRef.BuildBoundsCheckExpr(WrappedExpr, Cond.get(), *MateredExprs); + } + + void buildAndChain(Expr *AssignExpr, Expr *WidePtr) { + Expr *LHS = nullptr; + if (auto *BO = dyn_cast(AssignExpr)) { + LHS = BO->getLHS(); + } else { + auto *UO = dyn_cast(AssignExpr); + assert(UO && UO->isIncrementDecrementOp()); + LHS = UO->getSubExpr(); + } + + LHS = LHS->IgnoreParenCasts(); + if (auto *OVE = dyn_cast(LHS)) { + LHS = OVE->getSourceExpr()->IgnoreParenCasts(); + } + + if (auto *ME = dyn_cast(LHS)) { + setMemberBase(ME->getBase()); + } + + buildAndChain(AssignExpr->getType(), WidePtr); + } + + void buildAndChain(QualType Ty, Expr *WidePtr) { + if (!Ty->isBoundsAttributedType()) + return; + + auto *CE = dyn_cast(WidePtr); + if (CE && CE->getCastKind() == CK_BoundsSafetyPointerCast) + WidePtr = CE->getSubExpr(); + ExprResult WidePtrR = ForceRebuild(SemaRef).TransformExpr(WidePtr); + assert(!WidePtrR.isInvalid()); + WidePtr = WidePtrR.get(); + + // This is a good place to modify AssignOp if we need to. Check if any work + // needs to be done due to dynamic-bound pointer types. + ExprResult Result; + if (const auto *DBPTy = Ty->getAs()) { + if (const auto *DCPTy = dyn_cast(DBPTy)) { + Result = build(DCPTy, WidePtr); + } else { + const auto *DRPTy = dyn_cast(DBPTy); + if (DRPTy->getEndPointer()) + Result = build(DRPTy, WidePtr); + } + } + + // The error should have been diagnosed somewhere else. + if (Result.get()) + GuardedValue = Result.get(); + } + + /// The function generates an assignment check for flexible base similar to + /// pre-assignment checks on count and pointer pairs. + /// + /// @code + /// f = new_f; // check if `0 <= new_count <= bounds(new_f)` + /// f->count = new_count; + /// @endcode + void buildAndChainFlex(Expr *WidePtr) { + llvm::SmallVector OVEs; + auto *CE = dyn_cast(WidePtr); + if (CE && CE->getCastKind() == CK_BoundsSafetyPointerCast && + !WidePtr->getType()->isPointerTypeWithBounds()) + WidePtr = CE->getSubExpr(); + Expr *OldWidePtr = WidePtr; + ExprResult WidePtrR = ForceRebuild(SemaRef).TransformExpr(WidePtr); + assert(!WidePtrR.isInvalid()); + WidePtr = OpaqueValueExpr::EnsureWrapped( + SemaRef.Context, WidePtrR.get(), OVEs); + + SourceLocation Loc = GuardedValue->getBeginLoc(); + + // This is a good place to modify AssignOp if we need to. Check if any work + // needs to be done due to dynamic-bound pointer types. + CopyExpr Copy(SemaRef); + for (auto It : DeclReplacer.DeclToNewValue) + Copy.UnsafelyAddDeclSubstitution(const_cast(It.first), + It.second.first->IgnoreImpCasts()); + ExprResult Result = + BoundsCheckBuilder::CheckFlexibleArrayMemberSizeWithOVEs( + SemaRef, Loc, BoundsCheckKind::FlexibleArrayCountAssign, WidePtr, + OVEs, &Copy); + if (Result.isInvalid()) + return; + + if (!OVEs.empty()) { + Result = MaterializeSequenceExpr::Create(SemaRef.Context, Result.get(), OVEs); + Result = MaterializeSequenceExpr::Create(SemaRef.Context, Result.get(), OVEs, true); + } + + bool Succ = ReplaceSubExpr(PM, OldWidePtr, Result.get()); + (void)Succ; + assert(Succ); + } + + std::tuple GetFlexBase(Expr *InE) { + auto *ME = cast(InE); + QualType RecordTy = ME->getBase()->getType(); + if (ME->isArrow()) + RecordTy = RecordTy->getPointeeType(); + auto *RD = RecordTy->getAsRecordDecl(); + assert(RD); + if (!RD->hasFlexibleArrayMember()) + return GetFlexBase(ME->getBase()); + return {ME->getBase(), ME->isArrow(), RD}; + } + + /// This function generates a code to assert that the new count is + /// smaller or equal to the original count value. + /// + /// @code + /// typedef struct { int count; char fam[__counted_by(count)]; } flex_t; + /// flex_t g_flex = ... + /// + /// void test() { + /// g_flex.count = new_count; // check if `0 <= new_count <= g_flex.count` + /// } + /// @endcode + void buildAndChainOldCountCheck(Expr *AssignExpr) { + Expr *OldSelf = nullptr; + if (auto *BO = dyn_cast(AssignExpr)) { + OldSelf = BO->getLHS(); + } else { + auto *UO = dyn_cast(AssignExpr); + assert(UO && UO->isIncrementDecrementOp()); + OldSelf = UO->getSubExpr(); + } + OldSelf = OldSelf->IgnoreParenCasts(); + if (auto *OVE = dyn_cast(OldSelf)) + OldSelf = OVE->getSourceExpr()->IgnoreParenCasts(); + // Currently, this can only be reached with a member expression, + // which is the count of flexible array member. + + Expr *Base; + bool IsArrow; + RecordDecl *RD; + // The member expression may be nested, recurse to the flexible array + // struct. + std::tie(Base, IsArrow, RD) = GetFlexBase(OldSelf); + + FieldDecl *LastField; + for (auto *FD : RD->fields()) + LastField = FD; + assert(LastField->getType()->isIncompleteArrayType()); + const auto *DCPTy = LastField->getType()->getAs(); + assert(DCPTy); + Expr *Count = DCPTy->getCountExpr(); + + SmallVector CommonExprs; + ExprResult OpaqueBase; + if (auto *OVE = dyn_cast(Base)) { + OpaqueBase = OVE; + } else { + OpaqueBase = getMaterializedValueIfNot(Base, &CommonExprs); + if (OpaqueBase.isInvalid()) + return; + } + + ExprResult OldCount = + SemaRef.InstantiateDeclRefField(OpaqueBase.get(), IsArrow, Count); + + SmallVector Counts; + if (Count->getType()->isSignedIntegerOrEnumerationType()) { + ExprResult Zero = createZeroValue(SemaRef, Count->getType()); + if (Zero.isInvalid()) + return; + Counts.push_back(Zero.get()); + } + + ExprResult NewCount = DeclReplacer.TransformExpr(Count); + NewCount = SemaRef.DefaultLvalueConversion(NewCount.get()); + if (NewCount.isInvalid()) + return; + Counts.push_back(NewCount.get()); + + OldCount = SemaRef.DefaultLvalueConversion(OldCount.get()); + if (OldCount.isInvalid()) + return; + Counts.push_back(OldCount.get()); + + ExprResult LEChecks = BoundsCheckBuilder::BuildLEChecks( + SemaRef, GuardedValue->getExprLoc(), Counts, CommonExprs); + // Error should have been handled inside 'BuildLEChecks'. + if (LEChecks.isInvalid()) + return; + ExprResult Result = + SemaRef.BuildBoundsCheckExpr(GuardedValue, LEChecks.get(), CommonExprs); + if (Result.isInvalid()) + return; + GuardedValue = Result.get(); + } + + /// Perform if the new assignment is within the old range when the rest of the dependent pointers will not be changed. + bool buildAndChainOldRangeCheck(Expr *AssignExpr, Expr *NewSelf) { + const auto *DRPTy = AssignExpr->getType()->getAs(); + assert(DRPTy && (DRPTy->getStartPointer() || DRPTy->getEndPointer())); + SmallVector Ptrs; + Expr *OldSelf = nullptr; + if (auto *BO = dyn_cast(AssignExpr)) { + OldSelf = BO->getLHS(); + } else { + auto *UO = dyn_cast(AssignExpr); + assert(UO && UO->isIncrementDecrementOp()); + OldSelf = UO->getSubExpr(); + } + + // The new pointer might be still within the old range, but the buffer has possibly + // been resized its bounds, e.g., by calling realloc(). For such cases, we fallback + // to generic range checks. + if (NewSelf->HasSideEffects(SemaRef.Context)) + return false; + + // old_start <= new_ptr_val <= old_end + auto rebuildAndPushPtr = [&](Expr *RangePtr, Expr *OldSelf = nullptr) { + if (!RangePtr) { + assert(OldSelf && !OldSelf->HasSideEffects(SemaRef.Context)); + ExprResult OldSelfR = ForceRebuild(SemaRef).TransformExpr(OldSelf); + OldSelfR = SemaRef.DefaultFunctionArrayLvalueConversion(OldSelfR.get()); + assert(!OldSelfR.isInvalid()); + Ptrs.push_back(OldSelfR.get()); + return; + } + + assert(!RangePtr->HasSideEffects(SemaRef.Context)); + // Since we materialize self assignments, we can reuse the materialized value. + ExprResult NewRangePtr = DeclReplacer.TransformExpr(RangePtr); + assert(!NewRangePtr.isInvalid()); + // This is assuming the range has not been changed. + NewRangePtr = SemaRef.DefaultFunctionArrayLvalueConversion(NewRangePtr.get()); + assert(!NewRangePtr.isInvalid()); + Ptrs.push_back(NewRangePtr.get()); + }; + + rebuildAndPushPtr(DRPTy->getStartPointer(), OldSelf); + rebuildAndPushPtr(nullptr, NewSelf); + rebuildAndPushPtr(DRPTy->getEndPointer(), OldSelf); + + SmallVector CommonExprs; + ExprResult LEChecks = BoundsCheckBuilder::BuildLEChecks(SemaRef, + GuardedValue->getExprLoc(), + Ptrs, CommonExprs); + assert(!LEChecks.isInvalid()); + ExprResult Result = SemaRef.BuildBoundsCheckExpr(GuardedValue, + LEChecks.get(), + CommonExprs); + assert(!Result.isInvalid()); + GuardedValue = Result.get(); + return true; + } + + Expr *getChainedBoundsCheckExpr() const { + return GuardedValue; + } + +}; + +class DeclRefFinder : public ConstEvaluatedExprVisitor { +public: + typedef ConstEvaluatedExprVisitor Inherited; + const Decl *DeclToFind; + const Expr *Result; + + DeclRefFinder(ASTContext &Ctx, const Expr *Haystack, const Decl *Needle) + : Inherited(Ctx), DeclToFind(Needle) { + Visit(Haystack); + } + + const Expr *getExpr() { return Result; } + + void VisitMemberExpr(const MemberExpr *E) { + if (E->getMemberDecl() == DeclToFind) { + Result = E; + } + } + + void VisitDeclRefExpr(const DeclRefExpr *E) { + if (E->getDecl() == DeclToFind) { + Result = E; + } + } +}; + +class CountDepGroup; +class RangeDepGroup; + +/// Group decls with the decl referenced in count expression as a key decl. +/// Pointers that share the same count decl are in the same group. We currently +/// have "a" key decl because we don't currently allow having multiple count +/// variables referenced in a count expression. +/// FIXME: This should be extended to have multiple count declarations. +/// rdar://70692513 +class DepGroup { +public: + enum DepGroupKind { DGK_Count, DGK_Range }; +private: + const DepGroupKind Kind; + +protected: + class AssignExprInfo { + llvm::PointerIntPair Data; + llvm::PointerIntPair ExtData; + // The map 'ReferencedDecls' tracks dependent Decls referenced inside the assignment + // expression and the nested level of the decl reference, for the sake of diagnostics. + llvm::DenseMap ReferencedDecls; + enum { + SELF_ASSIGN = 0, + COUNT_VAR = 1, + COUNT_FOR_FAM = 0, + FLEX_BASE = 1, + }; + + public: + explicit AssignExprInfo(Expr *E, ValueDecl *VD, bool SelfAssign, + bool CountVar, bool CountForFam, bool FlexBase) + : Data(E, ((SelfAssign ? 1 << SELF_ASSIGN : 0) | + (CountVar ? 1 << COUNT_VAR : 0))), + ExtData(VD, ((CountForFam ? 1 << COUNT_FOR_FAM : 0) | + (FlexBase ? 1 << FLEX_BASE : 0))) {} + + bool IsSelfAssign() const { return (Data.getInt() >> SELF_ASSIGN) & 1; } + bool IsCountVar() const { return (Data.getInt() >> COUNT_VAR) & 1; } + bool IsCountForFam() const { + return (ExtData.getInt() >> COUNT_FOR_FAM) & 1; + } + bool IsFlexBase() const { return (ExtData.getInt() >> FLEX_BASE) & 1; } + void AddReferencedDecl(const ValueDecl *VD, unsigned Level) { + ReferencedDecls.insert(std::make_pair(VD, Level)); + } + const llvm::DenseMap& getReferencedDecls() const { + return ReferencedDecls; + } + + bool IsDeclInit() const { return !getExpr() && getDeclInit(); } + ValueDecl *getValueDecl() const { return ExtData.getPointer(); } + Expr *getExpr() const { return Data.getPointer(); } + Expr *getDeclInit() const { + auto Var = dyn_cast(getValueDecl()); + if (!Var) + return nullptr; + return Var->getInit(); + } + Expr *getAssignOrInitExpr() const { + return IsDeclInit() ? getDeclInit() : getExpr(); + } + }; + +protected: + bool SideEffectAfter = false; + bool SkipFlexCheck = false; + bool FlexBaseNull = false; + std::string Key; + std::string FlexBaseKey; + /// FIXME: Support multiple dependent lengths in a length expression. + llvm::SmallPtrSet KeyDecls; + // The map 'AssignDecls' keeps a link to the expression that assigns to + // the decl for diagnostics, i.e., the 'unsigned' data is an index to + // 'AssignExprList'. + llvm::DenseMap AssignedDecls; + llvm::DenseMap> DeclToNewValue; + llvm::SmallPtrSet SelfAssignedDecls; + llvm::SmallVector AssignExprList; + llvm::SmallVector MateredExprsUpFront; + + std::string getPrefixedName(const ValueDecl *VD, unsigned NestedLevel = 0) const { + std::string starPrefix = ""; + while (NestedLevel--) + starPrefix += "*"; + return (starPrefix + Key + VD->getName()).str(); + } + + std::string getPrefixedNameWithFlexBase(const Expr *E, + unsigned NestedLevel = 0) const { + std::string starPrefix = ""; + while (NestedLevel--) + starPrefix += "*"; + std::string MemberExprString; + llvm::raw_string_ostream MemberExprStringOS(MemberExprString); + computeMemberExprKey(E, MemberExprStringOS, /*Top*/ false); + return starPrefix + FlexBaseKey + MemberExprString; + } + + bool isFlexBasePointer() const { + size_t KeyLen = FlexBaseKey.length(); + return KeyLen > 2 && FlexBaseKey.substr(KeyLen - 2, 2) == "->"; + } + + bool hasAssignToFlexBase() const { + return AssignExprList.back().IsFlexBase(); + } + + bool hasFlexBase() const { return !FlexBaseKey.empty(); } + + std::string getFlexBaseName() const { + if (FlexBaseKey.empty()) + return ""; + size_t KeyLen = FlexBaseKey.length(); + if (isFlexBasePointer()) + return FlexBaseKey.substr(0, KeyLen - 2); + assert(KeyLen > 1 && FlexBaseKey.substr(KeyLen - 1, 1) == "."); + return FlexBaseKey.substr(0, KeyLen - 1); + } + + void FinalizePreAssignCheck(Sema &SemaRef, ParentMap &PM, Expr *LastBoundsCheckExpr); + +public: + explicit DepGroup(DepGroupKind Kind, StringRef Key, StringRef FlexBaseKey) + : Kind(Kind), Key(Key), FlexBaseKey(FlexBaseKey) { + // Produce KeyDecls + } + + virtual ~DepGroup() {} + + void Finalize(Sema &S, ParentMap &PM); + + virtual void EmitChecksToAST(Sema &SemaRef, ParentMap &PM) = 0; + + DepGroupKind getKind() const { return Kind; } + + Expr *getAssignExpr(size_t Index) const { + if (Index >= AssignExprList.size()) + return nullptr; + return AssignExprList[Index].getExpr(); + } + + void setSideEffectAfter() { SideEffectAfter = true; } + void clearSideEffectAfter() { SideEffectAfter = false; } + bool hasSideEffectAfter() const { return SideEffectAfter; } + void setSkipFlexCheck() { SkipFlexCheck = true; } + bool skipFlexCheck() const { return SkipFlexCheck; } + void setFlexBaseNull() { FlexBaseNull = true; } + bool isFlexBaseNull() const { return FlexBaseNull; } + + bool insertAssignedDecl(const ValueDecl *D, bool IsSelfAssign = false) { + auto It = AssignedDecls.find(D); + if (It != AssignedDecls.end()) + return false; + AssignedDecls[D] = AssignExprList.size(); + if (IsSelfAssign) + SelfAssignedDecls.insert(D); + return true; + } + bool insertDeclToNewValue(const ValueDecl *D, Expr *NewValue, unsigned Level) { + auto It = DeclToNewValue.find(D); + if (It != DeclToNewValue.end()) + return false; + DeclToNewValue[D] = std::make_pair(NewValue, Level); + return true; + } + + // This is in reverse order. + void insertMateredExpr(OpaqueValueExpr *OVE) { + MateredExprsUpFront.push_back(OVE); + } + + void insertMateredExprsReverse(llvm::SmallVectorImpl &OVEs) { + std::reverse(OVEs.begin(), OVEs.end()); + MateredExprsUpFront.append(OVEs); + } + + bool hasSelfAssign() const { return !SelfAssignedDecls.empty(); } + void addReferencedDecl(const ValueDecl *VD, unsigned Level) { + assert(!AssignExprList.empty()); + assert(!AssignExprList.back().IsSelfAssign()); + AssignExprList.back().AddReferencedDecl(VD, Level); + } + bool isDeclInitBasedGroup() const { return AssignExprList.empty(); } + void updateLastAssign(Expr *E, ValueDecl *VD, bool SelfAssign, bool CountVar, + bool CountForFam, bool FlexBase) { + AssignExprList.emplace_back(E, VD, SelfAssign, CountVar, CountForFam, + FlexBase); + } + Expr *getLastAssign() { + return AssignExprList.empty() ? nullptr + : AssignExprList[0].getAssignOrInitExpr(); + } + Expr *getFirstAssign() { + return AssignExprList.empty() + ? nullptr + : AssignExprList.rbegin()->getAssignOrInitExpr(); + } + + const AssignExprInfo &getFirstAssignExprInfoUnsafe() const { + return *AssignExprList.rbegin(); + } + + virtual bool DiagnoseMissingDependentAssign(Sema &SemaRef, const ValueDecl *VD) const = 0; + bool matchKey(StringRef K, const ValueDecl *VD) const { + return Key == K && KeyDecls.find(VD) != KeyDecls.end(); + } + bool matchFlexBaseKey(StringRef K) const { + return !FlexBaseKey.empty() && FlexBaseKey == K; + } + const Expr *getAssignExprForDecl(const ValueDecl *D) const { + auto It = AssignedDecls.find(D); + if (It == AssignedDecls.end() || It->second >= AssignExprList.size()) + return nullptr; + return AssignExprList[It->second].getExpr(); + } + + bool CheckDynamicCountAssignments(Sema &SemaRef); +}; + +void DepGroup::FinalizePreAssignCheck(Sema &SemaRef, + ParentMap &PM, + Expr *LastBoundsCheckExpr) { + if (!MateredExprsUpFront.empty()) { + std::reverse(MateredExprsUpFront.begin(), MateredExprsUpFront.end()); + bool FirstIsLast = (getFirstAssign() == getLastAssign()); + assert(getLastAssign()); + // FIXME: Binding comes first in the other places + Expr *UnbindExpr = FirstIsLast ? LastBoundsCheckExpr : getLastAssign(); + UnbindExpr = MaterializeSequenceExpr::Create(SemaRef.Context, + UnbindExpr, + MateredExprsUpFront, + /*Unbind*/true); + Expr *BindExpr = FirstIsLast ? UnbindExpr : LastBoundsCheckExpr; + BindExpr = MaterializeSequenceExpr::Create(SemaRef.Context, + BindExpr, + MateredExprsUpFront); + LastBoundsCheckExpr = BindExpr; + if (!FirstIsLast) { + bool Succ = ReplaceSubExpr(PM, getLastAssign(), UnbindExpr); + (void)Succ; + assert(Succ); + } + } + + if (getFirstAssign() != LastBoundsCheckExpr) { + bool Succ = ReplaceSubExpr(PM, getFirstAssign(), LastBoundsCheckExpr); + (void)Succ; + assert(Succ); + } +} + +static bool IsModifiableValue(Sema &S, const ValueDecl *VD) { + if (clang::IsConstOrLateConst(VD)) + return false; + Expr *TempLValueForVD = S.BuildDeclRefExpr( + const_cast(VD), VD->getType(), VK_LValue, SourceLocation()); + return TempLValueForVD->isModifiableLvalue(S.Context) == Expr::MLV_Valid; +} + +/// Finalize closes the current decl group analysis window. Before closing, +/// the function diagnoses the analyzed group to check if there was any missing +/// assignment to declaration within the same group. +void DepGroup::Finalize(Sema &SemaRef, ParentMap &PM) { + bool HadError = false; + for (const auto *Var : KeyDecls) { + /// We don't worry about instantiation here because we've already checked + /// the base as part of the group key. + if (AssignedDecls.find(Var) == AssignedDecls.end() && + IsModifiableValue(SemaRef, Var)) { + /// FIXME: Improve diagnostics using the base of field + SourceLocation Loc; + SourceRange Range; + /// We can have empty AssignExprList since we also track DeclStmt. + if (!AssignExprList.empty()) { + /// Continue if there was no directly dependent assignment to report. + /// In the following example, + /// we don't report on missing assignment to 'ptr2' and only report on + /// 'len' on which 'ptr' immediately depends. + /// \code + /// struct Foo { + /// int len; + /// int *__counted_by(len) ptr; + /// int *__counted_by(len) ptr2; + /// }; + /// struct Foo f; + /// f.ptr = nullptr; + /// \endcode + if (!DiagnoseMissingDependentAssign(SemaRef, Var)) + continue; + } else { + Loc = Var->getLocation(); + Range = Var->getSourceRange(); + SemaRef.Diag(Loc, diag::err_bounds_safety_non_adjacent_dependent_var_decl) + << getPrefixedName(Var) << Range; + } + HadError = true; + } + } + + /// This diagnoses if the consecutive assignments are ordered in a way to introduce + /// an unexpected runtime trap, as in the following example. + /// \code + /// struct S{ + /// unsigned len; + /// int *__counted_by(len) ptr1; + /// int *__counted_by(len) ptr2; + /// }; + /// void foo(struct S *s) { + /// s->ptr1 = s->ptr1 + 1; // ok: 's->ptr1' is referenced before updated. + /// s->ptr2 = s->ptr1; // error: cannot reference 's->ptr1' after it is changed during ... + /// s->len = 10; + /// } + /// \endcode + /// The following routine checks if a decl referenced in the assignment has an implicitly + /// dependent decl (e.g., 'ptr' is dependent to 'len' in the above example). If so, it + /// diagnoses if an assignment to the dependent decl (e.g., 'len') preceeds the current + /// assignment in the analyzed group. The same applies to '__ended_by'. + bool HasHadCountForFamError = false; + for (unsigned i = AssignExprList.size(); i--;) { + auto &ExprInfo = AssignExprList[i]; + if (ExprInfo.IsSelfAssign()) + continue; + + /// We skip bounds checks for single to single with flexible array member as + /// any other single to single cast. However, we still prevent the count + /// being silently unchecked by requiring the base to be initialized with a + /// wide pointer. + /// \code + /// flex = new_single_flex; + /// flex->count = new_count; + /// \endcode + if (ExprInfo.IsCountForFam() && + (!hasAssignToFlexBase() || (skipFlexCheck() && !isFlexBaseNull())) && + isFlexBasePointer() && !HasHadCountForFamError) { + const Expr *AssignedExpr = nullptr; + if (auto AssignOp = dyn_cast(ExprInfo.getExpr())) { + assert(AssignOp->getOpcode() == BO_Assign); + AssignedExpr = AssignOp->getLHS(); + } else if (auto UOp = dyn_cast(ExprInfo.getExpr())) { + AssignedExpr = UOp->getSubExpr(); + } + assert(AssignedExpr); + std::string AssignedExprString; + llvm::raw_string_ostream AssignedExprStringOS(AssignedExprString); + computeMemberExprKey(AssignedExpr, AssignedExprStringOS, /*Top*/ false); + const bool IsFirstNonFlexBase = + i == AssignExprList.size() - 1 || + (hasAssignToFlexBase() && i == AssignExprList.size() - 2); + SemaRef.Diag(ExprInfo.getExpr()->getExprLoc(), + diag::err_bounds_safety_no_preceding_fam_base_assignment) + << AssignedExprString << getFlexBaseName() << IsFirstNonFlexBase; + if (skipFlexCheck() && hasAssignToFlexBase()) { + SemaRef.Diag(getFirstAssign()->getExprLoc(), + diag::note_bounds_safety_flex_base_assign) + << getFlexBaseName(); + } else if (i < AssignExprList.size() - 1) { + SemaRef.Diag(getFirstAssign()->getExprLoc(), + diag::note_bounds_safety_first_dep_assign) + << getFlexBaseName(); + } + HadError = HasHadCountForFamError = true; + } + + for (auto It : ExprInfo.getReferencedDecls()) { + const auto *RefDecl = cast(It.first); + + auto RefUpdateIt = AssignedDecls.find(RefDecl); + if (RefUpdateIt != AssignedDecls.end() && RefUpdateIt->second > i) { + auto *RefUpdateExpr = getAssignExprForDecl(RefDecl); + // Referenced after updated. + SemaRef.Diag(ExprInfo.getExpr()->getExprLoc(), + diag::err_bounds_safety_dependent_assignments_order) + << getPrefixedName(RefDecl); + SemaRef.Diag(RefUpdateExpr->getExprLoc(), diag::note_bounds_safety_decl_assignment) + << getPrefixedName(RefDecl); + HadError = true; + } + } + } + + HadError |= CheckDynamicCountAssignments(SemaRef); + + if (HadError) + return; + + EmitChecksToAST(SemaRef, PM); +} + +// Check assignment to dynamic count pointer. Use the computed dependent values +// within the assignemnt group and pass them to +// CheckDynamicCountSizeForAssignment() to check all constraints. +bool DepGroup::CheckDynamicCountAssignments(Sema &SemaRef) { + bool HadError = false; + + for (const auto &ExprInfo : AssignExprList) { + const auto *BinOp = + dyn_cast(ExprInfo.getAssignOrInitExpr()); + if (!BinOp || BinOp->getOpcode() != BO_Assign) + continue; + + const Expr *LHS = BinOp->getLHS(); + QualType LHSTy = LHS->getType(); + const auto *DCPT = LHSTy->getAs(); + if (!DCPT) + continue; + + Expr *RHS = BinOp->getRHS(); + + // Ignore Parens, ImpCasts and OVEs in RHS. + auto IgnoreOVE = [](Expr *E) -> Expr * { + if (auto *OVE = dyn_cast(E)) + return OVE->getSourceExpr(); + return E; + }; + RHS = IgnoreExprNodes(RHS, IgnoreParensSingleStep, + IgnoreImplicitCastsSingleStep, IgnoreOVE); + + const ValueDecl *VD = nullptr; + if (const auto *DRE = dyn_cast(LHS)) + VD = DRE->getDecl(); + + Expr *LHSMemberBase = nullptr; + if (auto *ME = dyn_cast(LHS)) + LHSMemberBase = ME->getBase(); + + if (!SemaRef.CheckDynamicCountSizeForAssignment( + LHSTy, RHS, AssignmentAction::Assigning, BinOp->getExprLoc(), + VD ? VD->getName() : StringRef(), DeclToNewValue, LHSMemberBase)) { + HadError = true; + } + } + + return HadError; +} + +class CountDepGroup : public DepGroup { +protected: + static ExprResult MakeWidePtrFromDecl(Sema &SemaRef, ValueDecl *VD, + SourceLocation Loc) { + auto *DRE = SemaRef.BuildDeclRefExpr(VD, VD->getType(), VK_LValue, Loc); + return SemaRef.DefaultLvalueConversion(DRE); + } + + void EmitChecksToAST(Sema &SemaRef, ParentMap &PM) override { + auto *const FA = getFirstAssign(); + if (!FA) + return; + if (const auto *AE = dyn_cast(FA)) { + assert(BinaryOperator::isAssignmentOp(AE->getOpcode())); + if (SemaRef.allowBoundsUnsafePointerAssignment( + AE->getLHS()->getType(), AE->getRHS(), AE->getExprLoc())) + return; + } + PreAssignCheck Builder(SemaRef, PM, FA, DeclToNewValue); + /// We insert information on delayed or non-delayed checks for dynamic count + /// assignments. + /// BoundsSafety mandates the assignments to decl within a group should be all + /// or nothing which means the user needs to add a self assignment for + /// unchanged variables. If there was a self assignment to a dynamic count + /// pointer, the result of count expression with the new count can't be + /// bigger than the result with the old count since the bound of the pointer + /// hasn't changed. + if (hasSelfAssign() || hasFlexBase()) { + for (const auto &ExprInfo : AssignExprList) { + if (!ExprInfo.IsCountVar() || ExprInfo.IsSelfAssign()) + continue; + const Expr *E = ExprInfo.getExpr()->IgnoreParenImpCasts(); + const auto *UO = dyn_cast(E); + assert(!UO || UO->isIncrementDecrementOp()); + if (!UO || !UO->isIncrementOp()) + continue; + + const ValueDecl *VD = ExprInfo.getValueDecl(); + auto *Attr = VD->getAttr(); + for (unsigned i = 0; i < Attr->dependerDecls_size(); ++i) { + unsigned DepLevel = Attr->dependerLevels_begin()[i]; + auto *DepVD = cast(Attr->dependerDecls_begin()[i]); + auto It = AssignedDecls.find(DepVD); + bool NoDepAssign = It == AssignedDecls.end(); + assert(!NoDepAssign || ExprInfo.IsCountForFam()); + if (NoDepAssign) { + SemaRef.Diag(E->getExprLoc(), + diag::err_bounds_safety_increment_dynamic_count_nodep) + << getPrefixedName(VD, Attr->getIsDeref()); + } else if (AssignExprList[It->second].IsSelfAssign()) { + SemaRef.Diag(E->getExprLoc(), + diag::err_bounds_safety_increment_dynamic_count) + << getPrefixedName(VD, Attr->getIsDeref()) + << getPrefixedName(DepVD, DepLevel); + return; + } + } + } + } + + // For flexible base as, having at least two expressions in the group + // means it has a member assignment. + auto *MemberAccess = getAssignExpr(AssignExprList.size() - 2); + if (MemberAccess && isFlexBaseNull()) { + assert(skipFlexCheck()); + // Report error as there is a subsequent member access on this NULL + // base. + SemaRef.Diag(MemberAccess->getExprLoc(), + diag::err_bounds_safety_member_reference_null_base); + return; + } + + if (SemaRef.getDiagnostics().hasUnrecoverableErrorOccurred()) + return; + + const auto &FirstExprInfo = getFirstAssignExprInfoUnsafe(); + if (FirstExprInfo.IsFlexBase()) { + if (!skipFlexCheck()) { + Builder.buildAndChainFlex( + DeclToNewValue[FirstExprInfo.getValueDecl()].first); + } + } else if (FirstExprInfo.IsCountForFam()) { + Builder.buildAndChainOldCountCheck(FirstExprInfo.getExpr()); + } + for (const auto &ExprInfo : AssignExprList) { + if (ExprInfo.IsFlexBase() || ExprInfo.IsCountForFam()) + continue; + const auto *Expr = ExprInfo.getExpr(); + const auto *VD = ExprInfo.getValueDecl(); + Builder.buildAndChain(ExprInfo.getExpr(), + DeclToNewValue[ExprInfo.getValueDecl()].first); + if (isValueDeclOutCount(VD)) { + const auto *Att = VD->getAttr(); + for (Decl *DepD : Att->dependerDecls()) { + auto *DepVD = dyn_cast(DepD); + if (!DepVD || isValueDeclOutBuf(DepVD)) + continue; + if (DeclToNewValue.find(DepVD) != DeclToNewValue.end()) + continue; + auto WidePtrR = + MakeWidePtrFromDecl(SemaRef, DepVD, Expr->getBeginLoc()); + if (!WidePtrR.get()) + return; + auto *OVE = OpaqueValueExpr::Wrap(SemaRef.Context, WidePtrR.get()); + insertMateredExpr(OVE); + Builder.buildAndChain(DepVD->getType(), OVE); + } + } + } + FinalizePreAssignCheck(SemaRef, PM, Builder.getChainedBoundsCheckExpr()); + } + + bool DiagnoseMissingDependentAssign(Sema &SemaRef, const ValueDecl *VD) const override { + if (!VD->getType()->isIntegralOrEnumerationType() && + !VD->getType()->isPointerType()) + return false; + if (auto *Att = VD->getAttr()) { + unsigned FirstAssignIndex = 0; + bool HasDepAssign = false; + const ValueDecl *AssignedDecl = nullptr; + unsigned PtrLevel = 0; + for (unsigned i = 0; i < Att->dependerDecls_size(); ++i) { + const Decl *D = Att->dependerDecls_begin()[i]; + assert(isa(D)); + const ValueDecl *KeyVD = cast(D); + + if (KeyVD->getType()->isIncompleteArrayType() && + hasAssignToFlexBase()) { + // This must be flexible array member and `VD` must be referred + // to by the count expression for fam. + const auto *DCPTy = KeyVD->getType()->getAs(); + assert(DCPTy); + bool HasFlexDepAssign = false; + unsigned FirstFlexAssignIndex = 0; + const ValueDecl *FlexAssignedDecl = nullptr; + unsigned FlexPtrLevel = 0; + for (const auto &DI : DCPTy->getCoupledDecls()) { + const auto *D = DI.getDecl(); + if (D == VD) + continue; + assert(isa(D)); + const auto *DepVD = cast(D); + auto It = AssignedDecls.find(DepVD); + // Report the missing assignment error only if there is an + // assignment to any other decl referred to by the same count + // expression of fam. + // Find the first assignment (the bigger the index, the earlier the statement is) + // that creates a direct dependency with 'VD'. + if (It != AssignedDecls.end() && + // Don't report missing assignments for structs containing a + // referenced field. + DepVD->getType()->isIntegralOrEnumerationType() && + (!HasFlexDepAssign || FirstFlexAssignIndex < It->second)) { + HasFlexDepAssign = true; + FirstFlexAssignIndex = It->second; + FlexAssignedDecl = DepVD; + FlexPtrLevel = DI.isDeref(); + } + } + if (HasFlexDepAssign) { + const auto &ExprInfo = AssignExprList[FirstFlexAssignIndex]; + const Expr *DependentAssign = ExprInfo.getExpr(); + QualType DependerType = getInnerType(FlexAssignedDecl->getType(), FlexPtrLevel); + DeclRefFinder VDFinder(SemaRef.getASTContext(), + DCPTy->getCountExpr(), VD); + const Expr *VDExpr = VDFinder.getExpr(); + assert(VDExpr); + SemaRef.Diag(DependentAssign->getExprLoc(), + diag::err_bounds_safety_missing_dependent_var_assignment) + << getPrefixedName(FlexAssignedDecl, FlexPtrLevel) + << getPrefixedNameWithFlexBase(VDExpr, Att->getIsDeref()) + << DependerType << 0; + return true; + } + } else { + auto It = AssignedDecls.find(KeyVD); + // Find the first assignment (the bigger the index, the earlier the statement is) + // that creates a direct dependency with 'VD'. + if (It != AssignedDecls.end() && + (!HasDepAssign || FirstAssignIndex < It->second)) { + HasDepAssign = true; + FirstAssignIndex = It->second; + AssignedDecl = KeyVD; + PtrLevel = Att->dependerLevels_begin()[i]; + } + } + } + if (HasDepAssign) { + const auto &ExprInfo = AssignExprList[FirstAssignIndex]; + const Expr *DependentAssign = ExprInfo.getExpr(); + QualType DependerType = getInnerType(AssignedDecl->getType(), PtrLevel); + + if (!SemaRef.allowBoundsUnsafeAssignment(DependentAssign->getExprLoc())) + SemaRef.Diag(DependentAssign->getExprLoc(), + diag::err_bounds_safety_missing_dependent_var_assignment) + << getPrefixedName(AssignedDecl, PtrLevel) + << getPrefixedName(VD, Att->getIsDeref()) << DependerType << 0; + } + return HasDepAssign; + } + const auto *DCPTy = VD->getType()->getAs(); + unsigned PtrLevel = 0; + if (!DCPTy && VD->getType()->isPointerType()) { + DCPTy = VD->getType()->getPointeeType()->getAs(); + PtrLevel = 1; + } + if (!DCPTy) + return false; + assert(DCPTy); + unsigned FirstAssignIndex = 0; + bool HasDepAssign = false; + const ValueDecl *AssignedDecl = nullptr; + unsigned AssigneeLevel = 0; + // We don't force the user to assign VD if VD is non-inout buf parameter and + // all dependent declarations are inout counts. + bool CanSkipAssign = isa(VD) && !isValueDeclOutBuf(VD); + for (const auto &DI : DCPTy->dependent_decls()) { + const auto *D = DI.getDecl(); + assert(isa(D)); + const ValueDecl *KeyVD = cast(D); + auto It = AssignedDecls.find(KeyVD); + if (It != AssignedDecls.end() && + (!HasDepAssign || FirstAssignIndex < It->second)) { + HasDepAssign = true; + FirstAssignIndex = It->second; + AssignedDecl = KeyVD; + AssigneeLevel = DI.isDeref(); + } + CanSkipAssign &= isValueDeclOutCount(D); + } + bool Diagnose = HasDepAssign && !CanSkipAssign; + if (Diagnose) { + const auto &ExprInfo = AssignExprList[FirstAssignIndex]; + const Expr *DependentAssign = ExprInfo.getExpr(); + if (!SemaRef.allowBoundsUnsafeAssignment(DependentAssign->getExprLoc())) + SemaRef.Diag(DependentAssign->getExprLoc(), + diag::err_bounds_safety_missing_dependent_var_assignment) + << getPrefixedName(AssignedDecl, AssigneeLevel) + << getPrefixedName(VD, PtrLevel) << QualType(DCPTy, 0) << 1; + } + return Diagnose; + } + +public: + explicit CountDepGroup(const ValueDecl *VD, StringRef Key, + StringRef FlexBaseKey, const RecordDecl *FlexBaseDecl) + : DepGroup(DGK_Count, Key, FlexBaseKey) { + std::function AddDependentDecls; + AddDependentDecls = [this, &AddDependentDecls, + &FlexBaseDecl](const ValueDecl *VD) { + if (FlexBaseDecl && !FlexBaseDecl->isParentStructOf(VD)) + return; // if this is a nested field referred to by outer structs, + // only consider fields in the scope of the relevant struct. + if (!KeyDecls.insert(VD).second) + return; + + // Skip adding decls that use a const/late const. E.g.: + // + // ``` + // extern const unsigned extern_const_global_count; + // + // void fun_extern_const_global_count( + // int *__counted_by(extern_const_global_count) arg); + // + // void test_local_extern_const_global_count() { + // int *__counted_by(extern_const_global_count) local; + // } + // ``` + // + // For the case above the loop below is going over the dependent decls + // of `extern_const_global_count` when we are running DCPAA on + // `test_local_extern_const_global_count`. We don't want to add + // all other uses of the count outside of function (i.e. `arg` in this + // example) otherwise we will emit an incorrect diagnostic (`local + // variable arg must be declared right next to its dependent decl`) which + // doesn't make sense because `arg` isn't in the function being analyzed. + if (!clang::IsConstOrLateConst(VD)) { + if (auto *Att = VD->getAttr()) { + for (const auto *D : Att->dependerDecls()) { + // XXX: We could've just make it ValueDecl. + assert(isa(D)); + const auto *DepVD = cast(D); + AddDependentDecls(DepVD); + } + } + } + + const auto *DCPTy = VD->getType()->getAs(); + if (!DCPTy && VD->getType()->isPointerType()) { + DCPTy = VD->getType()->getPointeeType()->getAs(); + } + + if (!DCPTy) + return; + + for (const auto &DI : DCPTy->dependent_decls()) { + const auto *D = DI.getDecl(); + assert(isa(D)); + const auto *DepVD = cast(D); + AddDependentDecls(DepVD); + } + }; + + AddDependentDecls(VD); + } + + static CountDepGroup *Create(const ValueDecl *VD, StringRef Key, + StringRef FlexBaseKey, + const RecordDecl *FlexBaseDecl) { + return new CountDepGroup(VD, Key, FlexBaseKey, FlexBaseDecl); + } + + /// LLVM RTTI Interface + static bool classof(const DepGroup *DG) { + return DG->getKind() == DGK_Count; + } +}; + +class RangeDepGroup : public DepGroup { +protected: + void EmitChecksToAST(Sema &SemaRef, ParentMap &PM) override { + if (!getFirstAssign() || SemaRef.hasUncompilableErrorOccurred()) + return; + else if (const auto *AE = dyn_cast(getFirstAssign())) { + assert(BinaryOperator::isAssignmentOp(AE->getOpcode())); + if (SemaRef.allowBoundsUnsafePointerAssignment( + AE->getLHS()->getType(), AE->getRHS(), AE->getExprLoc())) + return; + } + PreAssignCheck Builder(SemaRef, PM, getFirstAssign(), DeclToNewValue); + bool HadOldRangeCheck = false; + /// If there is only one actual update, we just check the range of that actual assignment. + if (hasSelfAssign() && AssignedDecls.size() == SelfAssignedDecls.size() + 1) { + for (const auto &ExprInfo : AssignExprList) { + // XXX: We should just prevent having multiple assignments to the same decl in the + // same analysis region (or basic block). + if (!ExprInfo.IsSelfAssign()) { + if (SemaRef.allowBoundsUnsafePointerAssignment( + ExprInfo.getValueDecl()->getType(), + DeclToNewValue[ExprInfo.getValueDecl()].first, + ExprInfo.getExpr()->getExprLoc())) + return; + HadOldRangeCheck = Builder.buildAndChainOldRangeCheck(ExprInfo.getExpr(), + DeclToNewValue[ExprInfo.getValueDecl()].first); + break; + } + } + } + if (!HadOldRangeCheck) { + for (auto &ExprInfo : llvm::reverse(AssignExprList)) { + if (SemaRef.allowBoundsUnsafePointerAssignment( + ExprInfo.getValueDecl()->getType(), + DeclToNewValue[ExprInfo.getValueDecl()].first, + ExprInfo.getExpr()->getExprLoc())) + return; + Builder.buildAndChain(ExprInfo.getExpr(), + DeclToNewValue[ExprInfo.getValueDecl()].first); + } + } + FinalizePreAssignCheck(SemaRef, PM, Builder.getChainedBoundsCheckExpr()); + } + + bool DiagnoseMissingDependentAssign(Sema &SemaRef, + const ValueDecl *VD) const override { + unsigned PtrLevel = 0; + const auto *DRPTy = VD->getType()->getAs(); + if (!DRPTy && VD->getType()->isPointerType()) { + DRPTy = VD->getType()->getPointeeType()->getAs(); + PtrLevel = 1; + } + if (!DRPTy) + return false; + assert(DRPTy); + unsigned FirstAssignIndex = 0; + bool HasDepAssign = false; + QualType DependerType; + const ValueDecl *AssignedDecl = nullptr; + unsigned AssigneeLevel = 0; + unsigned VDLevel = 0; + auto FindPrecedingAssignIndex = [&](const TypeCoupledDeclRefInfo &DI, + const DynamicRangePointerType *DRPTy) { + if (PtrLevel != (unsigned)DI.isDeref()) + return; + const auto *D = DI.getDecl(); + assert(isa(D)); + const ValueDecl *KeyVD = cast(D); + auto It = AssignedDecls.find(KeyVD); + if (It != AssignedDecls.end() && + (!HasDepAssign || FirstAssignIndex < It->second)) { + HasDepAssign = true; + FirstAssignIndex = It->second; + AssignedDecl = KeyVD; + AssigneeLevel = DI.isDeref(); + DependerType = DRPTy ? QualType(DRPTy, 0) : cast(D)->getType(); + DependerType = getInnerType(DependerType, PtrLevel); + } + }; + for (const auto &DI : DRPTy->getEndPtrDecls()) { + FindPrecedingAssignIndex(DI, DRPTy); + } + for (const auto &DI : DRPTy->getStartPtrDecls()) { + FindPrecedingAssignIndex(DI, nullptr); + } + if (HasDepAssign) { + const auto &ExprInfo = AssignExprList[FirstAssignIndex]; + const Expr *DependentAssign = ExprInfo.getExpr(); + if (!SemaRef.allowBoundsUnsafeAssignment(DependentAssign->getExprLoc())) + SemaRef.Diag(DependentAssign->getExprLoc(), + diag::err_bounds_safety_missing_dependent_var_assignment) + << getPrefixedName(AssignedDecl, AssigneeLevel) + << getPrefixedName(VD, VDLevel) << DependerType << 0; + } + return HasDepAssign; + } + +public: + explicit RangeDepGroup(const ValueDecl *VD, StringRef Key) + : DepGroup(DGK_Range, Key, StringRef()) { + std::function AddDependentDecls; + AddDependentDecls = [this, &AddDependentDecls](const ValueDecl *VD) { + if (!KeyDecls.insert(VD).second) + return; + + const auto *DRPTy = VD->getType()->getAs(); + if (!DRPTy && VD->getType()->isPointerType()) { + DRPTy = VD->getType()->getPointeeType()->getAs(); + } + + if (!DRPTy) + return; + + for (const auto &DI : DRPTy->endptr_decls()) { + const auto *D = DI.getDecl(); + assert(isa(D)); + const auto *DepVD = cast(D); + AddDependentDecls(DepVD); + } + + for (const auto &DI : DRPTy->startptr_decls()) { + const auto *D = DI.getDecl(); + assert(isa(D)); + const auto *DepVD = cast(D); + AddDependentDecls(DepVD); + } + }; + + AddDependentDecls(VD); + } + + static RangeDepGroup *Create(const ValueDecl *VD, StringRef Key) { + return new RangeDepGroup(VD, Key); + } + + /// LLVM RTTI Interface + static bool classof(const DepGroup *DG) { + return DG->getKind() == DGK_Range; + } +}; + +/// CheckCountAttributedDeclAssignments - This tracks a group of assignments to +/// decl with dynamic count dependency, assuming each Stmt is analyzed backward +/// through "BeginStmt(Stmt)". This reports an error if there is a side effect +/// between assignments within the same group. For example, +/// @code +/// int len; +/// int *__counted_by(len + 1) ptr; +/// ... +/// len = ...; +/// function_with_side_effect(); +/// ptr = ...; +/// @endcode +/// "len" and "ptr" are in the same DepGroup and the analyzer detects the side +/// effect in between assignments to these variables in the same group and +/// reports an error. +class CheckCountAttributedDeclAssignments + : public RecursiveASTVisitor { + + using BaseVisitor = RecursiveASTVisitor; + + Sema &SemaRef; + AnalysisDeclContext &AC; + std::unique_ptr CurrentDepGroup; + bool InAssignToDepRHS = false; + unsigned AssigneeLevel = 0; + QualType AssigneeType; + bool EarlyLookup = false; + llvm::SmallPtrSet DelayedStmts; + llvm::SmallPtrSet VisitedILEInCompoundLiteralExprs; + llvm::SmallPtrSet DiagnosedPtrIncDec; + + void Initialize() { + InAssignToDepRHS = false; + AssigneeLevel = 0; + AssigneeType = QualType(); + } + + void PushDelayedStmt(Stmt *S) { DelayedStmts.insert(S); } + + bool CheckAndPopDelayedStmt(Stmt *S) { return DelayedStmts.erase(S); } + + bool MatchDepGroup(const AssignedDeclRefResult &Result) { + if (!CurrentDepGroup) + return false; + + if (Result.IsFlexBase) { + // XXX: I could just calculate the exact base from the beginning? + std::string FlexBaseKey = + Result.Key + Result.ThisVD->getName().str() + "->"; + return CurrentDepGroup->matchFlexBaseKey(FlexBaseKey); + } + if (!Result.FlexBaseKey.empty()) + return CurrentDepGroup->matchFlexBaseKey(Result.FlexBaseKey); + + return CurrentDepGroup->matchKey(Result.Key, Result.ThisVD); + } + + /// Assignee is either DeclRef or MemberExpr that is LHS of assignment. + bool InsertAssignedDecl(const AssignedDeclRefResult &Result, + bool IsSelfAssign, + bool IsVarDecl = false); + void RegisterDeclAssignment(Expr *AssignExpr, AssignedDeclRefResult &Result, + bool IsSelfAssign); + + void UpdateLastAssign(Expr *E, const AssignedDeclRefResult &Result, + bool IsSelfAssign) { + if (EarlyLookup) + return; + assert(CurrentDepGroup && "Empty dependent length"); + CurrentDepGroup->updateLastAssign(E, Result.ThisVD, IsSelfAssign, + Result.IsCountVar, Result.IsCountForFam, + Result.IsFlexBase); + } + + void SetSideEffectAfter() { + if (EarlyLookup) + return; + if (CurrentDepGroup) + CurrentDepGroup->setSideEffectAfter(); + } + + void FinalizeDepGroup() { + if (CurrentDepGroup) { + CurrentDepGroup->Finalize(SemaRef, getParentMap()); + CurrentDepGroup.reset(); + } + } + + bool HasDepGroup() const { return CurrentDepGroup != nullptr; } + + bool InCountDepGroup() const { return CurrentDepGroup && isa(CurrentDepGroup); } + bool InRangeDepGroup() const { return CurrentDepGroup && isa(CurrentDepGroup); } + void HandleRHSOfAssignment(BinaryOperator *E); + void HandleVarInit(VarDecl *VD); + void ProcessDeclRefInRHSRecursive(Expr *E); + + ParentMap &getParentMap() { return AC.getParentMap(); } + +public: + explicit CheckCountAttributedDeclAssignments(Sema &SemaRef, + AnalysisDeclContext &AC) + : SemaRef(SemaRef), AC(AC) {} + + ~CheckCountAttributedDeclAssignments() { + /// Finalize last assign expressions. + FinalizeDepGroup(); + } + + bool VisitCallExpr(CallExpr *E); + bool TraverseBinaryOperator(BinaryOperator *E); + bool TraverseCompoundAssignOperator(CompoundAssignOperator *E) { + return TraverseBinaryOperator(E); + } + bool TraverseUnaryOperator(UnaryOperator *E); + bool TraverseStmtExpr(StmtExpr *E) { return true; } + bool TraverseOpaqueValueExpr(OpaqueValueExpr *E) { + return TraverseStmt(E->getSourceExpr()); + } + bool TraverseBoundsCheckExpr(BoundsCheckExpr *E) { + return TraverseStmt(E->getGuardedExpr()); + } + bool TraverseMaterializeSequenceExpr(MaterializeSequenceExpr *E) { + return TraverseStmt(E->getWrappedExpr()); + } + bool TraverseBoundsSafetyPointerPromotionExpr(BoundsSafetyPointerPromotionExpr *E) { + return TraverseStmt(E->getSubExpr()); + } + bool VisitVarDecl(VarDecl *VD); + bool TraverseDeclStmt(DeclStmt *S); + bool VisitCompoundLiteralExpr(CompoundLiteralExpr *CLE); + bool TraverseReturnStmt(ReturnStmt *S); + + Expr *HandleInitListExpr(InitListExpr *IL, PreAssignCheck &Builder); + void BeginStmt(Stmt *S); +}; +} // namespace + +/// This function generates the flexible struct base. +/// This function finds the flexible struct pointer base that can be updated +/// with a bounded pointer. For `a.c.f->s.b.count = xxx`, as an example, +/// `a.c.f->` is identified as the flexible pointer base. If there is no pointer +/// base, this returns the entire struct base. +/// The access path is emitted to @p OS if provided. +/// The RecordDecl of the struct containing the flexible array member is +/// returned if found. +RecordDecl *DynamicCountPointerAssignmentAnalysis::computeFlexBaseKey( + Expr *InE, llvm::raw_string_ostream *OS) { + Expr *E = InE->IgnoreParenCasts(); + RecordDecl *BaseRD = nullptr; + while (auto ME = dyn_cast(E)) { + // Stripping implicit casts of the base allows us to distinguish between + // __bidi_indexable vs. __single implicitly promoted to __bidi_indexable. + QualType BaseTy = ME->getBase()->IgnoreImpCasts()->getType(); + // The closest pointer base becomes the flexible base. + if (ME->isArrow()) { + // The base is a pointer, but since we ignore implicit casts we + // skip over the decay from array to pointer type. + assert(BaseTy->isPointerType() || BaseTy->isArrayType()); + if (!BaseTy->isSinglePointerType() || BaseTy->isBoundsAttributedType()) + return nullptr; + if (auto *RD = BaseTy->getPointeeType()->getAsRecordDecl()) { + if (!RD->hasFlexibleArrayMember()) + return nullptr; + if (OS) + computeMemberExprKey(ME, *OS); + return RD; + } + return nullptr; + } + BaseRD = BaseTy->getAsRecordDecl(); + E = ME->getBase()->IgnoreParenCasts(); + } + + // There is no pointer base. Emit the whole lvalue base. + auto *EndME = dyn_cast(InE->IgnoreParenCasts()); + if (!EndME || !BaseRD->hasFlexibleArrayMember()) + return nullptr; + if (OS) + computeMemberExprKey(EndME, *OS); + return BaseRD; +} + +static DepGroup *CreateDepGroup(const AssignedDeclRefResult &Result) { + if (Result.IsRangePtrVar) + return RangeDepGroup::Create(Result.ThisVD, Result.Key); + else + return CountDepGroup::Create(Result.ThisVD, Result.Key, Result.FlexBaseKey, + Result.FlexBaseDecl); +} + +bool CheckCountAttributedDeclAssignments::InsertAssignedDecl( + const AssignedDeclRefResult &Result, bool IsSelfAssign, bool IsVarDecl) { + if (EarlyLookup) + return false; + + /// If the current group tracks assignments, finalize it. + if (!MatchDepGroup(Result) || + (IsVarDecl && CurrentDepGroup && CurrentDepGroup->getLastAssign())) { + FinalizeDepGroup(); + CurrentDepGroup.reset(CreateDepGroup(Result)); + } + + AssigneeLevel = Result.Level; + AssigneeType = Result.Ty; + if (CurrentDepGroup->hasSideEffectAfter()) { + SourceLocation Loc = CurrentDepGroup->getLastAssign() + ? CurrentDepGroup->getLastAssign()->getExprLoc() + : Result.Loc; + SourceRange Range = + CurrentDepGroup->getLastAssign() + ? SourceRange(Result.Loc, + CurrentDepGroup->getLastAssign()->getEndLoc()) + : SourceRange(); + + SemaRef.Diag(Loc, diag::err_bounds_safety_dependent_vars_assign_side_effect) + << Range; + /// Keep tracking the dep group as the error has been reported. + CurrentDepGroup->clearSideEffectAfter(); + } + const ValueDecl *VD = Result.ThisVD; + assert(VD); + return CurrentDepGroup->insertAssignedDecl(VD, IsSelfAssign); +} + +void CheckCountAttributedDeclAssignments::BeginStmt(Stmt *S) { + Initialize(); + /// Currently, we only detect a group of adjacent simple assignments. + switch (S->getStmtClass()) { + case Stmt::DeclRefExprClass: + case Stmt::BinaryOperatorClass: + case Stmt::CompoundAssignOperatorClass: + case Stmt::CallExprClass: + case Stmt::UnaryExprOrTypeTraitExprClass: + case Stmt::UnaryOperatorClass: + case Stmt::DeclStmtClass: + break; + default: + FinalizeDepGroup(); + } + TraverseStmt(S); +} + +namespace { + +Expr *findSourceExpr(Expr *E) { + if (!E) + return nullptr; + E = E->IgnoreParens(); + if (auto CE = dyn_cast(E)) { + if (CE->getCastKind() == CK_BoundsSafetyPointerCast) + return CE->getSubExpr(); + } + return E; +} + +// Get the mapping from dependent fields and non-param variables to their +// initializers. If a field/variable doesn't have an initializer, create an +// implicit one (zero-init). +Sema::DependentValuesMap getDependentInits(Sema &SemaRef, + const CountAttributedType *DCPT, + InitListExpr *ILE) { + Sema::DependentValuesMap DependentValues; + for (auto &DI : DCPT->dependent_decls()) { + // Fields and non-param variables cannot have dependent inout counts. + assert(!DI.isDeref()); + + ValueDecl *D = DI.getDecl(); + auto *VD = dyn_cast(D); + auto *FD = dyn_cast(D); + assert(FD || (VD && !isa(VD))); + + // We allow the dependent decl to be an __unsafe_late_const, but we don't + // know its value, thus we don't replace it. + if (VD && VD->hasAttr()) + continue; + + Expr *Init; + if (VD && VD->hasInit()) { + Init = VD->getInit(); + } else if (FD && ILE && FD->getFieldIndex() < ILE->getNumInits()) { + Init = ILE->getInit(FD->getFieldIndex()); + } else { + // Assume zero-init when there is no initializer. + Init = new (SemaRef.Context) ImplicitValueInitExpr(D->getType()); + } + + DependentValues[D] = {Init, /*Level=*/0}; + } + return DependentValues; +} + +bool diagnoseDynamicCountVarInit(Sema &SemaRef, SourceLocation Loc, + const Twine &Designator, QualType Ty, + Expr *Init, InitListExpr *ParentInit) { + const auto *DCPT = Ty->getAs(); + if (DCPT && Ty->isPointerType()) { + auto DependentValues = getDependentInits(SemaRef, DCPT, ParentInit); + + Expr *RHS; + if (Init) + RHS = Init->IgnoreParenImpCasts(); + else + RHS = new (SemaRef.Context) ImplicitValueInitExpr(Ty); + + return !SemaRef.CheckDynamicCountSizeForAssignment( + Ty, RHS, AssignmentAction::Initializing, Loc, Designator, + DependentValues); + } + + auto GetChildLoc = [&](Expr *ChildInit) { + // If we have an explicit init, get the location from it. + if (ChildInit && !isa(ChildInit)) + return ChildInit->getBeginLoc(); + + // For implicit inits, we point to the '}' in the parent initializer list, + // for example: + // struct foo { int len; int *__counted_by(len) p; }; + // struct foo f = { 1 }; + // ^ diagnostic for 'p' here + if (const auto *ILE = dyn_cast_or_null(Init)) + return ILE->getEndLoc(); + + // Otherwise, point at the parent location (e.g., the VarDecl's location). + return Loc; + }; + + auto GetInitListExpr = [&SemaRef](Expr *Init) -> InitListExpr * { + if (!Init) + return nullptr; + auto *ILE = dyn_cast(Init); + if (ILE) + return ILE; + + // Try CompoundLiteralExpr assignment. + // E.g. + // struct Foo a = (struct Foo) { .field = 0x0 }; + + // Checking CompoundLiteralExpr assignment is currently guarded by the + // new bounds check to avoid potential build failures. + // TODO: We should **always** check CompoundLiteralExprs (rdar://138982703). + if (!SemaRef.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_CompoundLiteralInit)) + return nullptr; + auto *CLE = + dyn_cast_if_present(Init->IgnoreImpCasts()); + if (!CLE) + return nullptr; + ILE = dyn_cast_if_present(CLE->getInitializer()); + return ILE; + }; + + if (const auto *RD = Ty->getAsRecordDecl()) { + assert(RD->isStruct() || RD->isUnion()); + + auto *ILE = GetInitListExpr(Init); + + // If RD is an union and we know which field is being initialized, check + // only that field and ignore the rest. + if (RD->isUnion() && ILE) { + const FieldDecl *FD = ILE->getInitializedFieldInUnion(); + Expr *ChildInit = ILE->getNumInits() > 0 ? ILE->getInit(0) : nullptr; + return diagnoseDynamicCountVarInit( + SemaRef, GetChildLoc(ChildInit), + Designator.concat(llvm::Twine('.').concat(FD->getName())), + FD->getType(), ChildInit, ILE); + } + + bool HadError = false; + for (const auto *FD : RD->fields()) { + Expr *ChildInit = nullptr; + if (ILE && FD->getFieldIndex() < ILE->getNumInits()) + ChildInit = ILE->getInit(FD->getFieldIndex()); + HadError |= diagnoseDynamicCountVarInit( + SemaRef, GetChildLoc(ChildInit), + Designator.concat(llvm::Twine('.').concat(FD->getName())), + FD->getType(), ChildInit, ILE); + } + return HadError; + } + + if (const auto *CAT = SemaRef.Context.getAsConstantArrayType(Ty)) { + assert(CAT->getSize().isNonNegative()); + uint64_t Size = CAT->getSize().getZExtValue(); + auto *ILE = GetInitListExpr(Init); + + // Don't interate over the array elements if the element type is + // uninteresting for the diagnosis. + QualType ET = CAT->getElementType(); + if (!ET->isCountAttributedType() && !ET->getAsRecordDecl() && + !SemaRef.Context.getAsConstantArrayType(ET)) { + return false; + } + + // Avoid complaining multiple times for implicit init, since the errors are + // the same. + uint64_t MaxSize = !ILE ? 1 : (ILE->getNumInits() + 1); + if (MaxSize < Size) + Size = MaxSize; + + bool HadError = false; + for (uint64_t I = 0; I < Size; ++I) { + Expr *ChildInit = nullptr; + if (ILE && I < ILE->getNumInits()) + ChildInit = ILE->getInit(I); + HadError |= diagnoseDynamicCountVarInit( + SemaRef, GetChildLoc(ChildInit), + Designator.concat(llvm::Twine('[') + .concat(llvm::utostr(I)) + .concat(llvm::Twine(']'))), + ET, ChildInit, nullptr); + } + return HadError; + } + + return false; +} + +} // namespace + +bool Sema::DiagnoseDynamicCountVarZeroInit(VarDecl *VD) { + if (VD->hasExternalStorage()) + return false; + + return diagnoseDynamicCountVarInit(*this, VD->getLocation(), VD->getName(), + VD->getType(), VD->getInit(), + /*ParentInit=*/nullptr); +} + +namespace { + +bool diagnoseRecordInitsImpl( + Sema &SemaRef, InitListExpr *IL, bool &NeedPreCheck, + bool DiagnoseAssignments, + SmallVectorImpl &InitializersWithSideEffects) { + bool HadError = false; + + if (SemaRef.Context.getAsArrayType(IL->getType())) { + for (unsigned i = 0; i < IL->getNumInits(); ++i) { + if (auto SubIL = dyn_cast(IL->getInit(i))) { + HadError |= diagnoseRecordInitsImpl(SemaRef, SubIL, NeedPreCheck, + DiagnoseAssignments, + InitializersWithSideEffects); + } + } + return HadError; + } + + auto RD = IL->getType()->getAsRecordDecl(); + if (!RD) + return true; + + if (RD->isUnion()) { + auto FD = IL->getInitializedFieldInUnion(); + if (FD && IL->getNumInits()) { + if (auto SubIL = dyn_cast(IL->getInit(0))) { + HadError |= diagnoseRecordInitsImpl(SemaRef, SubIL, NeedPreCheck, + DiagnoseAssignments, + InitializersWithSideEffects); + } + } + return HadError; + } + + // We memoize null-pointer evaluation results for each init so we don't + // repeat the same evaluation. + enum NullState { NS_Unknown, NS_Null, NS_Nonnull }; + llvm::SmallVector InitIsNull(IL->getNumInits(), NS_Unknown); + unsigned InitIndex = 0; + for (auto FD : RD->fields()) { + assert(InitIndex < IL->getNumInits()); + unsigned CurrInitIndex = InitIndex; + Expr *Init = IL->getInit(InitIndex++); + if (auto SubIL = dyn_cast(Init)) { + HadError |= diagnoseRecordInitsImpl(SemaRef, SubIL, NeedPreCheck, + DiagnoseAssignments, + InitializersWithSideEffects); + continue; + } + if (SemaRef.allowBoundsUnsafePointerAssignment(FD->getType(), Init, + Init->getExprLoc())) + continue; + + auto isNullPtrInit = [&](unsigned Idx, ASTContext &Context) -> bool { + assert(Idx < IL->getNumInits()); + if (InitIsNull[Idx] != NS_Unknown) + return InitIsNull[Idx] == NS_Null; + Expr *ThisInit = IL->getInit(Idx); + bool IsNull = + isa(ThisInit) || + ThisInit->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNotNull) != Expr::NPCK_NotNull; + InitIsNull[Idx] = IsNull ? NS_Null : NS_Nonnull; + return IsNull; + }; + + // XXX: rdar://76568300 + if (Init->HasSideEffects(SemaRef.Context)) { + InitializersWithSideEffects.emplace_back(Init); + SourceLocation Loc = Init->getExprLoc(); + if (auto *Att = FD->getAttr()) { + NeedPreCheck = true; + if (!Att->getIsDeref()) { + assert(Att->dependerDecls_size() > 0); + const auto *Depender = *Att->dependerDecls_begin(); + assert(isa(Depender)); + const auto *DepVD = cast(Depender); + const auto *DepDCPTy = DepVD->getType()->getAs(); + assert(DepDCPTy); + SemaRef.Diag(Loc, diag::err_bounds_safety_dynamic_bound_init_side_effect) + << (DepDCPTy->isCountInBytes() ? 1 : 0); + HadError = true; + continue; + } + } + if (const auto *DCPTy = FD->getType()->getAs()) { + NeedPreCheck = true; + SemaRef.Diag(Loc, diag::err_bounds_safety_dynamic_bound_init_side_effect) + << DCPTy->getKind() + 2; + HadError = true; + continue; + } + if (const auto *DRPTy = FD->getType()->getAs()) { + NeedPreCheck = true; + SemaRef.Diag(Loc, diag::err_bounds_safety_dynamic_bound_init_side_effect) + << (DRPTy->getEndPointer() ? 6 : 7); + HadError = true; + continue; + } + } + + if (auto *OrigDCPTy = FD->getType()->getAs()) { + NeedPreCheck = true; + if (OrigDCPTy->isPointerType() && DiagnoseAssignments) { + // If requested try to diagnose assignments. This is currently + // only for CompoundLiteralExpr initializer lists. Variable initializers + // are handled elsewhere (DepGroup::CheckDynamicCountAssignments). + auto DependentValues = getDependentInits(SemaRef, OrigDCPTy, IL); + HadError |= !SemaRef.CheckDynamicCountSizeForAssignment( + FD->getType(), Init, AssignmentAction::Initializing, + Init->getExprLoc(), StringRef(), DependentValues); + } + } else if (auto *OrigDRPTy = + FD->getType()->getAs()) { + NeedPreCheck = true; + bool IsImplicitInitExpr = isa(Init); + if (isNullPtrInit(CurrInitIndex, SemaRef.Context)) { + auto diagnosePartialNullRange = [&](const TypeCoupledDeclRefInfo &DepDeclInfo, + Expr *DepExp) { + if (DepDeclInfo.isDeref()) + return; + assert(isa(DepDeclInfo.getDecl())); + const auto *DepField = cast(DepDeclInfo.getDecl()); + assert(DepField->getFieldIndex() < IL->getNumInits()); + if (!isNullPtrInit(DepField->getFieldIndex(), SemaRef.Context)) { + SourceLocation Loc = IsImplicitInitExpr ? IL->getEndLoc() : Init->getExprLoc(); + SemaRef.Diag(Loc, diag::warn_bounds_safety_initlist_range_partial_null) + << IsImplicitInitExpr << FD << FD->getType() << DepExp; + } + }; + if (auto *EndPtr = OrigDRPTy->getEndPointer()) { + assert(isa(EndPtr->IgnoreParenCasts()) + || isa(EndPtr->IgnoreParenCasts())); + for (const auto &EndPtrDecl : OrigDRPTy->getEndPtrDecls()) { + diagnosePartialNullRange(EndPtrDecl, EndPtr); + } + } + if (auto *StartPtr = OrigDRPTy->getStartPointer()) { + assert(isa(StartPtr->IgnoreParenCasts()) + || isa(StartPtr->IgnoreParenCasts())); + for (const auto &StartPtrDecl : OrigDRPTy->getStartPtrDecls()) { + diagnosePartialNullRange(StartPtrDecl, StartPtr); + } + } + } + } + } + return HadError; +} + +bool diagnoseRecordInits(Sema &SemaRef, InitListExpr *IL, bool &NeedPreCheck, + bool DiagnoseAssignments = false) { + SmallVector FieldInitializersWithSideEffects; + bool Result = + diagnoseRecordInitsImpl(SemaRef, IL, NeedPreCheck, DiagnoseAssignments, + FieldInitializersWithSideEffects); + + // Avoid + // * Warning about structs that don't contain externally counted attributes. + // * Warning when we emitted errors. That just adds extra noise. + if (!NeedPreCheck || Result) + return Result; + + // This record needs bounds checks and there were no errors. Warn about any + // initializers that cause side effects. + for (auto *FE : FieldInitializersWithSideEffects) { + SemaRef.Diag(FE->getExprLoc(), diag::warn_bounds_safety_struct_init_side_effect) + << FE; + } + + return Result; +} + +} // namespace + +/// Recursively check Inits of InitListExpr and directly insert new AST nodes for pre-assignment checks and +/// necessary materializations. +/// +/// This currently doesn't support inits used in the checks to have side effects because this doesn't guarantees +/// that inits are evaluated in order when dependent fields and the other fields are mixed in an InitListExpr. +/// To support it, we need to make sure all inits including nested ones are materialized in the program order +/// before performing the assignment checks. +/// rdar://76568300 +/// +/// Similarly to assignment expressions, initialization list of structs with +/// dynamic count and/or dynamic count pointer fields needs run-time integrity +/// checks, e.g., the init list like following. +/// @code +/// struct S { int *__counted_by(len) ptr; int len; }; +/// struct S s = {wide_p, l}; +/// @endcode +/// This function tracks and records wide pointer expression assigned as an +/// element of init list so that CodeGen can insert necessary runtime integrity +/// checks for assigned dynamic count value, e.g., "len <= bound_of(wide_p)". +Expr *CheckCountAttributedDeclAssignments::HandleInitListExpr( + InitListExpr *IL, PreAssignCheck &Builder) { + if (IL->getType()->isRecordType()) { + auto RD = IL->getType()->getAsRecordDecl(); + if (RD->isUnion()) { + auto FD = IL->getInitializedFieldInUnion(); + if (FD && IL->getNumInits()) { + if (auto SubIL = dyn_cast(IL->getInit(0))) { + if (auto *NewSubIL = HandleInitListExpr(SubIL, Builder)) + IL->setInit(0, NewSubIL); + } + } + return nullptr; + } + llvm::SmallVector, 2> + CountPointerCheckPairs; + unsigned InitIndex = 0; + llvm::SmallVector MateredExprs; + for (auto FD : RD->fields()) { + assert(InitIndex < IL->getNumInits()); + unsigned CurrInitIndex = InitIndex; + Expr *Init = IL->getInit(InitIndex++); + + // Note: We don't handle CompoundLiteralExpr here which would contain + // a sub InitListExpr. Instead we rely on VisitCompoundLiteralExpr being + // called appropriately. + if (auto SubIL = dyn_cast(Init)) { + if (auto *NewSubIL = HandleInitListExpr(SubIL, Builder)) + IL->setInit(CurrInitIndex, NewSubIL); + continue; + } + + if (FD->hasAttr()) { + auto InitRes = Builder.getMaterializedValueIfNot(Init, &MateredExprs); + if (!(Init = InitRes.get())) + return nullptr; + Builder.insertDeclToNewValue(FD, Init); + } else if (FD->getType()->isCountAttributedType()) { + Expr *PtrExpr = findSourceExpr(Init); + auto PtrExprRes = + Builder.getMaterializedValueIfNot(PtrExpr, &MateredExprs); + if (!(PtrExpr = PtrExprRes.get())) + return nullptr; + + CountPointerCheckPairs.push_back( + std::make_pair(FD->getType(), PtrExpr)); + } else if (auto *OrigDRPTy = + FD->getType()->getAs()) { + Expr *PtrExpr = findSourceExpr(Init); + auto PtrExprRes = + Builder.getMaterializedValueIfNot(PtrExpr, &MateredExprs); + if (!(PtrExpr = PtrExprRes.get())) + return nullptr; + Builder.insertDeclToNewValue(FD, Init); + // The pointer that has an associated end pointer is responsible to do the bounds checks: + // lb(new_ptr) <= start <= new_ptr <= end <= ub(new_ptr) + // Thus, we check if the pointer has an end pointer. This is consistent with how we + // handle assignments (and this is a routine we handle init lists). + // XXX: This is based on the assumption that only '__ended_by' is exposed to the users + // and '__started_by' is an implicit attribute the compiler adds based on the corresponding + // '__ended_by'. We may revisit this if we decide to expose '__started_by' to users. + if (OrigDRPTy->getEndPointer()) { + CountPointerCheckPairs.push_back(std::make_pair(FD->getType(), PtrExpr)); + } + } + } + if (!CountPointerCheckPairs.empty()) { + ExprResult Result = Builder.build(CountPointerCheckPairs, IL, MateredExprs); + if (!Result.isInvalid()) + return Result.get(); + } + return nullptr; + } + + if (SemaRef.Context.getAsArrayType(IL->getType())) { + for (unsigned i = 0; i < IL->getNumInits(); ++i) { + if (auto SubIL = dyn_cast(IL->getInit(i))) { + if (auto *NewSubIL = HandleInitListExpr(SubIL, Builder)) + IL->setInit(i, NewSubIL); + } + } + } + return nullptr; +} + +bool CheckCountAttributedDeclAssignments::TraverseDeclStmt(DeclStmt *S) { + for (auto *I : S->decls()) { + auto Var = dyn_cast(I); + if (!Var) + return TraverseDecl(I); + + AssignedDeclRefResult Result; + analyzeVarDecl(SemaRef, Var, Result); + if (Result.IsFlexBase && Var->getInit()) { + RegisterDeclAssignment(nullptr, Result, /* IsSelfAssign */ false); + if (CurrentDepGroup->skipFlexCheck()) + return true; + + Expr *Init = Var->getInit(); + auto *CE = dyn_cast(Init); + if (CE && CE->getCastKind() == CK_BoundsSafetyPointerCast && + !Init->getType()->isPointerTypeWithBounds()) { + Init = CE->getSubExpr(); + } + if (Init->IgnoreParenCasts()->isNullPointerConstant( + SemaRef.Context, Expr::NPC_NeverValueDependent) != + Expr::NPCK_NotNull) { + CurrentDepGroup->setFlexBaseNull(); + CurrentDepGroup->setSkipFlexCheck(); + return true; + } + // -fbounds-safety doesn't check single to single casts. Likewise, + // we skip checks for single to single with flexible array member. + // The exception is when both the source and the destination are + // pointers to struct with flexible array member, because a single + // pointer to struct with flexible array member promotes to + // '__bidi_indexable' with the specified count value. + if (Init->getType()->isSinglePointerType()) { + assert(!Init->getType()->getAs() && + !Init->getType()->getAs()); + CurrentDepGroup->setSkipFlexCheck(); + return true; + } + auto *OVE = OpaqueValueExpr::Wrap(SemaRef.Context, Init); + CurrentDepGroup->insertMateredExpr(OVE); + ReplaceSubExpr(getParentMap(), OVE->getSourceExpr(), OVE); + CurrentDepGroup->insertDeclToNewValue(Var, Var->getInit(), Result.Level); + } else if (Result.IsTrackedVar) { + InsertAssignedDecl(Result, /*IsSelfAssign*/false, /*VarDecl*/true); + if (Var->getType()->isBoundsAttributedType()) + HandleVarInit(Var); + else + TraverseStmt(Var->getInit()); + } else { + if (CurrentDepGroup && !CurrentDepGroup->isDeclInitBasedGroup()) + FinalizeDepGroup(); + TraverseDecl(I); + } + } + return true; +} + +bool CheckCountAttributedDeclAssignments::VisitVarDecl(VarDecl *VD) { + auto InitExpr = VD->getInit(); + if (auto IL = dyn_cast_or_null(InitExpr)) { + // If VD is not local, the check should have already been done at earlier + // semantic analysis. + bool NeedPreCheck = false; + if (!diagnoseRecordInits(SemaRef, IL, NeedPreCheck) + && VD->hasLocalStorage() && NeedPreCheck) { + PreAssignCheck::DeclToNewValueTy DeclToNewValue; + PreAssignCheck Builder(SemaRef, getParentMap(), IL, DeclToNewValue); + if (auto *NewIL = HandleInitListExpr(IL, Builder)) { + if (VD->getInit() == InitExpr) { + VD->setInit(NewIL); + } else { + bool Mod = ReplaceSubExpr(getParentMap(), InitExpr, NewIL); + (void)Mod; + assert(Mod); + } + } + } + SetSideEffectAfter(); + } + return true; +} + +bool CheckCountAttributedDeclAssignments::VisitCompoundLiteralExpr( + CompoundLiteralExpr *CLE) { + // Note this check prevents us from trying to transform already transformed + // CompoundLiteralExpr because the initializer list will be a + // `BoundsCheckExpr` after its transformed. + auto *ILE = llvm::dyn_cast_if_present(CLE->getInitializer()); + if (!ILE) + return true; + + // Avoid emitting duplicate diagnostics. There are at least two reasons why + // this can happen: + // + // 1. `diagnoseRecordInits` found an error so adding bounds checks was skipped + // and then this CompoundLiteralExpr is visited again. + // + // 2. New bounds checks are disabled. When this happens the ILE is not + // replaced with a BoundsCheckExpr which means this method won't return + // early if this CompoundLiteralExpr is visited again. + // + // + // The CFG form of the function being analyzed may contain the same ILE + // multiple times in different CFG statements which means the same + // CompoundLiteralExpr can be visited multiple times. + // + if (VisitedILEInCompoundLiteralExprs.contains(ILE)) { + return true; + } + VisitedILEInCompoundLiteralExprs.insert(ILE); + + // Skip emitting the bounds check if its not enabled + // rdar://110871666 + if (!SemaRef.getLangOpts().hasNewBoundsSafetyCheck( + clang::LangOptionsBase::BS_CHK_CompoundLiteralInit)) + return true; + + // TODO: This diagnostic should always be enabled (i.e. should actually be + // called before we check for BS_CHK_CompoundLiteralInit being enabled) + // (rdar://138982703). However the diagnostics are currently guarded to avoid + // a build failure (rdar://137774167). + bool NeedPreCheck = false; + if (diagnoseRecordInits(SemaRef, ILE, NeedPreCheck, + /*DiagnoseAssignments=*/true) || + !NeedPreCheck) + return true; + + PreAssignCheck::DeclToNewValueTy EmptyMap; + PreAssignCheck Builder(SemaRef, getParentMap(), ILE, EmptyMap); + auto *NewILE = HandleInitListExpr(ILE, Builder); + if (!NewILE) + // Note in this case the ILE might still have changed but rather than the + // whole ILE being replaced one of the assignments might have changed. In + // this case the ILE will have been modified in-place so there is nothing + // left to do. + return true; + + // The ILE needs replacing + assert(CLE->getInitializer() == ILE && + "CLE initializer unexpectedly changed"); + CLE->setInitializer(NewILE); + getParentMap().setParent(NewILE, CLE); + return true; +} + +bool CheckCountAttributedDeclAssignments::VisitCallExpr(CallExpr *E) { + if (CheckAndPopDelayedStmt(E)) + return true; + + if (InAssignToDepRHS) + PushDelayedStmt(E); + else + FinalizeDepGroup(); + + BaseVisitor::VisitCallExpr(E); + if (E->HasSideEffects(SemaRef.getASTContext(), + /*IncludePossibleEffects*/ true)) { + SetSideEffectAfter(); + } + return true; +} + +void CheckCountAttributedDeclAssignments::RegisterDeclAssignment( + Expr *AssignExpr, AssignedDeclRefResult &Result, bool IsSelfAssign) { + assert(Result.ThisVD); + if (!InsertAssignedDecl(Result, IsSelfAssign)) { + const Expr *PrevAssignExpr = CurrentDepGroup->getAssignExprForDecl(Result.ThisVD); + assert(PrevAssignExpr); + const unsigned DiagIndex = Result.IsRangePtrVar ? 2 + : (Result.IsOutCount || Result.IsCountVar) ? 0 + : 1; + SemaRef.Diag(PrevAssignExpr->getExprLoc(), + diag::err_bounds_safety_multiple_assignments_to_dynamic_bound_decl) + << DiagIndex << Result.ThisVD; + SemaRef.Diag(AssignExpr->getExprLoc(), diag::note_bounds_safety_previous_assignment); + } + UpdateLastAssign(AssignExpr, Result, IsSelfAssign); +} + +namespace { + +Expr *materializeLValue(ASTContext &Ctx, Expr *LV, + SmallVectorImpl &OVEs) { + Expr *Unwrapped = LV->IgnoreParenImpCasts(); + if (isa(Unwrapped)) + return LV; + + if (auto *ME = dyn_cast(Unwrapped)) { + if (!isa(ME->getBase())) { + OVEs.push_back(OpaqueValueExpr::Wrap(Ctx, ME->getBase())); + ME->setBase(OVEs.back()); + } + } + OVEs.push_back(OpaqueValueExpr::Wrap(Ctx, LV)); + return OVEs.back(); +} + +// A non-inout buf parameter having at least one dependent inout count parameter +// must be immutable. +void checkImplicitlyReadOnlyBuf(Sema &SemaRef, const Expr *E, + const AssignedDeclRefResult &Result, + bool IsSelfAssign = false) { + const ValueDecl *VD = Result.ThisVD; + if (IsSelfAssign || (!Result.IsCountPtrVar && !Result.IsRangePtrVar) || + !isa(VD) || isValueDeclOutBuf(VD)) { + return; + } + if (Result.IsCountPtrVar) { + const auto *DCPT = Result.Ty->getAs(); + assert(DCPT); + bool HasOutCount = std::any_of( + DCPT->dependent_decl_begin(), DCPT->dependent_decl_end(), + [](const auto &DI) { return isValueDeclOutCount(DI.getDecl()); }); + if (HasOutCount) { + SemaRef.Diag(E->getBeginLoc(), + diag::err_bounds_safety_read_only_dynamic_count_pointer) + << VD->getName() << DCPT->getKind(); + } + return; + } + if (Result.IsRangePtrVar) { + const auto *DRPT = Result.Ty->getAs(); + assert(DRPT); + auto IsDIDeref = [](const TypeCoupledDeclRefInfo &DI) { + return DI.isDeref(); + }; + int DiagNo = -1; + if (std::any_of(DRPT->endptr_decl_begin(), DRPT->endptr_decl_end(), + IsDIDeref)) { + DiagNo = 0; + } else if (std::any_of(DRPT->startptr_decl_begin(), + DRPT->startptr_decl_end(), IsDIDeref)) { + DiagNo = 1; + } + + if (DiagNo >= 0) { + SemaRef.Diag(E->getBeginLoc(), + diag::err_bounds_safety_read_only_dynamic_range_pointer) + << VD->getName() << DiagNo; + } + return; + } +} + +void checkImplicitlyReadOnlyDependentParamOfReturnType( + Sema &S, const Expr *E, const AssignedDeclRefResult &Result) { + bool IsDeref = Result.Level >= 1; + if (!Result.IsDependentParamOfReturnType || IsDeref) + return; + + // This should trigger an error, don't emit another one. + if (Result.IsOutCount) + return; + + // TODO: This diagnostic check should always be performed (rdar://138982703). + // The check is currently guarded to avoid potentially breaking the build. + if (!S.getLangOpts().hasNewBoundsSafetyCheck(LangOptions::BS_CHK_ReturnSize)) + return; + + const ValueDecl *VD = Result.ThisVD; + const FunctionDecl *FD = cast(VD->getDeclContext()); + const auto *RetCATy = FD->getReturnType()->getAs(); + unsigned Kind = RetCATy ? RetCATy->getKind() : BoundsAttributedType::EndedBy; + S.Diag(E->getBeginLoc(), + diag::err_bounds_safety_read_only_dependent_param_in_return) + << VD->getName() << Kind << FD->getName() << FD->getReturnType(); +} + +} // namespace + +bool CheckCountAttributedDeclAssignments::TraverseUnaryOperator( + UnaryOperator *E) { + if (!E->isIncrementDecrementOp()) + return BaseVisitor::TraverseUnaryOperator(E); + + AssignedDeclRefResult LHSResult; + Expr *SubExpr = E->getSubExpr(); + analyzeAssignedDeclRef(SemaRef, SubExpr, LHSResult); + + checkImplicitlyReadOnlyBuf(SemaRef, E, LHSResult); + checkImplicitlyReadOnlyDependentParamOfReturnType(SemaRef, E, LHSResult); + + // This check should be done in `checkArithmeticUnaryOpBoundsSafetyPointer`, + // however if the check is performed there then there will be false positives + // for pointer inc/dec in the lhs of assignments which aren't actually bounds + // checked due to rdar://98749526. So we do the check here so that we emit + // the diagnostic for the cases where bounds checks **are** inserted. + // TODO: Remove this (rdar://135833598). + if (!SemaRef.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_IndirectCountUpdate)) { + if (const auto *CATTy = SubExpr->getType()->getAs()) { + if (!DiagnosedPtrIncDec.contains(E) && + !SemaRef + .BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp( + CATTy, SubExpr, E->isIncrementOp())) { + DiagnosedPtrIncDec.insert(E); + } + } + } + + if (LHSResult.IsTrackedVar) { + assert(!SubExpr->HasSideEffects(SemaRef.Context)); + RegisterDeclAssignment(E, LHSResult, /*SelfAssign*/false); + if (CurrentDepGroup->skipFlexCheck()) + return true; + + SmallVector OVEs; + auto *LHS = SubExpr; + if (!isa(SubExpr)) { + LHS = materializeLValue(SemaRef.Context, LHS, OVEs); + E->setSubExpr(LHS); + } + + SourceLocation Loc = E->getBeginLoc(); + QualType Ty = SubExpr->getType(); + llvm::APInt OneVal(/*bitwidth*/SemaRef.Context.getTypeSize(Ty), /*val*/1); + QualType IncDecTy = Ty->isIntegralOrEnumerationType() ? Ty : + SemaRef.Context.getIntTypeForBitwidth(OneVal.getBitWidth(), /*IsSigned*/false); + Expr* IncDecValue = IntegerLiteral::Create(SemaRef.Context, OneVal, IncDecTy, + Loc); + ExprResult NewBinOp = SemaRef.CreateBuiltinBinOp(Loc, + (E->isIncrementOp() ? BO_Add : BO_Sub), + LHS, + IncDecValue); + if (NewBinOp.isInvalid()) + return true; + OVEs.push_back(OpaqueValueExpr::Wrap(SemaRef.Context, NewBinOp.get())); + CurrentDepGroup->insertDeclToNewValue(LHSResult.ThisVD, OVEs.back(), + LHSResult.Level); + CurrentDepGroup->insertMateredExprsReverse(OVEs); + auto Res = TraverseStmt(SubExpr); + if (LHSResult.IsFlexBase) + SetSideEffectAfter(); + return Res; + } + return BaseVisitor::TraverseUnaryOperator(E); +} + +bool CheckCountAttributedDeclAssignments::TraverseBinaryOperator(BinaryOperator *E) { + if (E->isAssignmentOp()) { + AssignedDeclRefResult LHSResult; + analyzeAssignedDeclRef(SemaRef, E->getLHS(), LHSResult); + + // This check should be done in `checkArithmeticBinOpBoundsSafetyPointer`, + // however if the check is performed there then there will be false + // positives for pointer inc/dec in the lhs of assignments which aren't + // actually bounds checked due to rdar://98749526. So we do the check here + // so that we emit the diagnostic for the cases where bounds checks **are** + // inserted. + // TODO: Remove this (rdar://135833598). + if (!SemaRef.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_IndirectCountUpdate)) { + if (const auto *CATTy = + E->getLHS()->getType()->getAs()) { + switch (E->getOpcode()) { + case BO_AddAssign: // += + case BO_SubAssign: // -= + if (!DiagnosedPtrIncDec.contains(E) && + !SemaRef + .BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp( + CATTy, E->getRHS(), E->getOpcode())) { + DiagnosedPtrIncDec.insert(E); + } + + break; + default: + // Don't try to emit the diagnostic for other operators + break; + } + } + } + + if (LHSResult.IsInnerStruct) { + auto *InnerStructDecl = cast(LHSResult.ThisVD); + auto *Att = InnerStructDecl->getAttr(); + assert(Att); + auto *RDecl = + InnerStructDecl->getType()->getAs()->getAsRecordDecl(); + assert(RDecl); + // Multiple structs with FAMs can refer to fields in the same struct, + // so the number can be >1 + assert(Att->dependerDecls_size() > 0); + for (auto DepDecl : Att->dependerDecls()) { + if (!LHSResult.FlexBaseDecl->isParentStructOf(DepDecl)) + continue; + ValueDecl *FAMDecl = cast(DepDecl); + auto *DCTy = FAMDecl->getType()->getAs(); + assert(DCTy); + for (auto Dep : DCTy->dependent_decls()) { + auto FieldD = dyn_cast(Dep.getDecl()); + if (!FieldD || !RDecl->isParentStructOf(FieldD)) + continue; + SemaRef.Diag(E->getOperatorLoc(), + diag::err_bounds_safety_dependent_struct_assignment) + << E->getLHS() << FieldD << FAMDecl; + SemaRef.Diag(DCTy->getCountExpr()->getBeginLoc(), + diag::note_bounds_safety_count_param_loc) + << DCTy->getCountExpr()->getSourceRange(); + return true; + } + llvm_unreachable( + "FAM refers to struct without referring to one of it's fields"); + } + } + + if (LHSResult.IsOutBuf) + SemaRef.Diag(E->getBeginLoc(), + diag::err_bounds_safety_dependent_out_count_buf_assign); + + if (LHSResult.IsOutCount) + SemaRef.Diag(E->getBeginLoc(), + diag::err_bounds_safety_dependent_out_count_assign); + + AssignedDeclRefResult RHSResult; + analyzeAssignedDeclRef(SemaRef, E->getRHS(), RHSResult); + // Allow self assignment + bool IsSelfAssign = LHSResult.IsTrackedVar && + (LHSResult.ThisVD == RHSResult.ThisVD) && + (LHSResult.Key == RHSResult.Key) && + (LHSResult.Level == RHSResult.Level); + + checkImplicitlyReadOnlyBuf(SemaRef, E, LHSResult, IsSelfAssign); + checkImplicitlyReadOnlyDependentParamOfReturnType(SemaRef, E, LHSResult); + + if (LHSResult.IsTrackedVar) + RegisterDeclAssignment(E, LHSResult, IsSelfAssign); + + SaveAndRestore InAssignToDepRHSLocal(InAssignToDepRHS, + LHSResult.IsTrackedVar); + + // We are only interested in the immediate wide pointer to dynamic + // bound pointer casts, rather than any casts happening during + // recursive traversal of subexpressions. + Expr *OrigRHS = E->getRHS(); + if (InAssignToDepRHS && !CurrentDepGroup->skipFlexCheck()) { + + // SelfAssign doesn't need additional recursive decl ref check, but + // it still needs materialization in HandleRHSOfAssignment. + if (!IsSelfAssign) + ProcessDeclRefInRHSRecursive(OrigRHS); + + Expr *NewValue = nullptr; + if (E->isCompoundAssignmentOp()) { + auto *LHS = E->getLHS(); + auto *RHS = E->getRHS(); + SmallVector OVEs; + // Evaluate RHS first. + if (!isa(RHS)) { + RHS = materializeLValue(SemaRef.Context, RHS, OVEs); + E->setRHS(RHS); + } + if (!isa(LHS)) { + LHS = materializeLValue(SemaRef.Context, LHS, OVEs); + E->setLHS(LHS); + } + BinaryOperator::Opcode AdjustedBinOp; + switch (E->getOpcode()) { + case BO_MulAssign: AdjustedBinOp = BO_Mul; break; + case BO_DivAssign: AdjustedBinOp = BO_Div; break; + case BO_RemAssign: AdjustedBinOp = BO_Rem; break; + case BO_AddAssign: AdjustedBinOp = BO_Add; break; + case BO_SubAssign: AdjustedBinOp = BO_Sub; break; + case BO_ShlAssign: AdjustedBinOp = BO_Shl; break; + case BO_ShrAssign: AdjustedBinOp = BO_Shr; break; + case BO_AndAssign: AdjustedBinOp = BO_And; break; + case BO_XorAssign: AdjustedBinOp = BO_Xor; break; + case BO_OrAssign: AdjustedBinOp = BO_Or; break; + default: llvm_unreachable("unknown compound operator!"); + } + ExprResult NewBinOp = SemaRef.CreateBuiltinBinOp( + E->getExprLoc(), AdjustedBinOp, LHS, RHS); + if (NewBinOp.isInvalid()) + return true; + NewValue = NewBinOp.get(); + assert(!isa(NewValue)); + OVEs.push_back(OpaqueValueExpr::Wrap(SemaRef.Context, NewValue)); + NewValue = OVEs.back(); + // Inserting OVE in a reversed order to match that the entire analysis + // is reversed. + CurrentDepGroup->insertMateredExprsReverse(OVEs); + } else if (LHSResult.IsFlexBase) { + Expr *RHS = OrigRHS; + CastExpr *E = dyn_cast(RHS); + if (E && E->getCastKind() == CK_BoundsSafetyPointerCast && + !OrigRHS->getType()->isPointerTypeWithBounds()) { + // This is a best effort to get an underlying wide pointer, but it may + // not be a wide pointer if there was no wide pointer in the first + // place and/or it is a null pointer. + RHS = E->getSubExpr(); + } + if (RHS->IgnoreParenCasts()->isNullPointerConstant( + SemaRef.Context, Expr::NPC_NeverValueDependent) != + Expr::NPCK_NotNull) { + CurrentDepGroup->setFlexBaseNull(); + CurrentDepGroup->setSkipFlexCheck(); + } else if (RHS->getType()->isSinglePointerType()) { + // -fbounds-safety doesn't check single to single casts. Likewise, + // we skip checks for single to single with flexible array member. + // The exception is when both the source and the destination are + // pointers to struct with flexible array member, because a single + // pointer to struct with flexible array member promotes to + // '__bidi_indexable' with the specified count value. + assert(!RHS->getType()->getAs() && + !RHS->getType()->getAs()); + CurrentDepGroup->setSkipFlexCheck(); + } + } + + if (!CurrentDepGroup->skipFlexCheck()) { + HandleRHSOfAssignment(E); + if (!NewValue) { + assert(E->isAssignmentOp()); + NewValue = E->getRHS(); + } + CurrentDepGroup->insertDeclToNewValue(LHSResult.ThisVD, NewValue, + LHSResult.Level); + } + } + // We still traverse RHS to detect possible side effects. + if (!IsSelfAssign) + TraverseStmt(OrigRHS); + if (!LHSResult.IsTrackedVar && + SemaRef.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_IndirectCountUpdate)) + TraverseStmt(E->getLHS()); + + if (LHSResult.IsFlexBase) + SetSideEffectAfter(); + + return true; + } else if (E->isCommaOp()) { + // Skip traversing the operands of comma operators here because the operands + // will be visited as seperate statements. Without skipping, the same + // expressions were traversed twice and this produced unstructured OVEs and + // led to an assertion failure in Clang CodeGen. For example, the block for + // `p++, len--` should look like this. The code here skips (3) and the + // traversals are done for (1) and (2). [B1] + // 1: p++ + // 2: len-- + // 3: [B1.1] , [B1.2] <- comma operator + return false; + } + return BaseVisitor::TraverseBinaryOperator(E); +} + +void CheckCountAttributedDeclAssignments::ProcessDeclRefInRHSRecursive(Expr *E) { + assert(CurrentDepGroup != nullptr); + struct DeclRefVisitor : public RecursiveASTVisitor { + Sema &SemaRef; + DepGroup &DepGroupRef; + + DeclRefVisitor(Sema &SemaRef, DepGroup &DepGroupRef) + : SemaRef(SemaRef), DepGroupRef(DepGroupRef) {} + + bool VisitDeclRefExpr(DeclRefExpr *E) { + addReferencedDecl(E); + return true; + } + bool VisitMemberExpr(MemberExpr *E) { + addReferencedDecl(E); + return true; + } + + bool TraverseOpaqueValueExpr(OpaqueValueExpr *E) { + return TraverseStmt(E->getSourceExpr()); + } + + bool TraverseBoundsCheckExpr(BoundsCheckExpr *E) { + return TraverseStmt(E->getGuardedExpr()); + } + + bool TraverseMaterializeSequenceExpr(MaterializeSequenceExpr *E) { + return TraverseStmt(E->getWrappedExpr()); + } + + bool TraverseBoundsSafetyPointerPromotionExpr(BoundsSafetyPointerPromotionExpr *E) { + return TraverseStmt(E->getSubExpr()); + } + + void addReferencedDecl(Expr *E) { + AssignedDeclRefResult Result; + analyzeAssignedDeclRef(SemaRef, E, Result); + if (Result.IsTrackedVar && + DepGroupRef.matchKey(Result.Key, Result.ThisVD)) { + DepGroupRef.addReferencedDecl(Result.ThisVD, Result.Level); + } + } + } DeclRefVisitorInst{SemaRef, *CurrentDepGroup}; + DeclRefVisitorInst.TraverseStmt(E); +} + +void CheckCountAttributedDeclAssignments::HandleRHSOfAssignment(BinaryOperator *AssignOp) { + assert(InAssignToDepRHS); + ParentMap &PM = getParentMap(); + Expr *RHS = AssignOp->getRHS()->IgnoreParens(); + CastExpr *E = dyn_cast(RHS); + if (!E || E->getCastKind() != CK_BoundsSafetyPointerCast) { + if (!isa(AssignOp->getRHS())) { + auto *OVE = OpaqueValueExpr::Wrap(SemaRef.Context, AssignOp->getRHS()); + PM.setParent(OVE->getSourceExpr(), OVE); + CurrentDepGroup->insertMateredExpr(OVE); + AssignOp->setRHS(OVE); + PM.setParent(OVE, AssignOp); + } + return; + } + + if (!isa(E->getSubExpr())) { + auto *OVE = OpaqueValueExpr::Wrap(SemaRef.Context, E->getSubExpr()); + PM.setParent(OVE->getSourceExpr(), OVE); + CurrentDepGroup->insertMateredExpr(OVE); + E->setSubExpr(OVE); + PM.setParent(OVE, E); + } + return; +} + +void CheckCountAttributedDeclAssignments::HandleVarInit(VarDecl *VD) { + if (!VD->getInit() || !VD->hasLocalStorage()) + return; + Expr *Init = VD->getInit()->IgnoreParens(); + + // For variable initialization, we can just emit a check immediately because + // the length variable has necessarily been initialized before (else it + // couldn't have been referenced in the dynamic bound pointer type). + // XXX: this will not work for DynamicRangePointerType. + PreAssignCheck::DeclToNewValueTy EmptyMap; + PreAssignCheck Builder(SemaRef, getParentMap(), VD->getInit(), EmptyMap); + + CastExpr *E = dyn_cast(Init); + if (E && E->getType()->isBoundsAttributedType()) { + Init = E->getSubExpr(); + } + ExprResult Result = Builder.build(VD->getType(), Init); + if (Result.isInvalid()) + return; + + auto Res = Result.get(); + if (Res->getType()->isPointerTypeWithBounds()) { + assert(VD->getType()->isBoundsAttributedType()); + Res = SemaRef + .ImpCastExprToType(Res, VD->getType(), + CastKind::CK_BoundsSafetyPointerCast) + .get(); + } + VD->setInit(Res); +} + +// Emit a bounds check if the function returns bounds-attributed pointer type. +// The bounds check will load all dependent decls and verify if the dynamic +// bound pointer has enough bytes. +bool CheckCountAttributedDeclAssignments::TraverseReturnStmt(ReturnStmt *S) { + const auto *FD = cast(AC.getDecl()); + + QualType RetTy = FD->getReturnType(); + const auto *RetBATy = RetTy->getAs(); + if (!RetBATy) + return BaseVisitor::TraverseReturnStmt(S); + + auto *RetVal = S->getRetValue(); + if (!RetVal || RetVal->containsErrors()) { + // This should have been diagnosed earlier. + return true; + } + + // Return size checks are hidden behind a flag. + if (!SemaRef.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_ReturnSize)) { + return BaseVisitor::TraverseReturnStmt(S); + } + + // Remove the cast to return type (bounds attribute type) that can be added by + // Sema. This results in a more readable AST because + // the pointer used in bounds checks doesn't have the bounds attribute. E.g. + // having the `__counted_by` attribute on a pointer used in bounds checks + // before it's been checked is confusing. + auto *ICE = dyn_cast(RetVal); + if (ICE && RetVal->getType()->isBoundsAttributedType()) + RetVal = ICE->getSubExpr(); + + SmallVector OVEs; + + // Wrap the return value into OVE to evaluate the value only once. + auto *RetOVE = OpaqueValueExpr::Wrap(SemaRef.Context, RetVal); + OVEs.push_back(RetOVE); + + // Add implicit cast to the guarded expression so that it matches the return + // type. This makes the AST more readable but isn't necessarily required by + // codegen. + CastKind Kind = CK_BoundsSafetyPointerCast; + if (!RetVal->getType()->isPointerType()) { + assert(RetVal->isNullPointerConstant(SemaRef.getASTContext(), + Expr::NPC_NeverValueDependent)); + Kind = CK_NullToPointer; + } + ExprResult GuardedRes = SemaRef.ImpCastExprToType(RetOVE, RetTy, Kind); + if (GuardedRes.isInvalid()) + return true; + + PreAssignCheck::DeclToNewValueTy EmptyMap; + PreAssignCheck Builder(SemaRef, getParentMap(), GuardedRes.get(), EmptyMap); + + // Wrap all dependent decls into OVEs, since the bounds check for __ended_by() + // uses the end pointer value multiple times. + for (const auto &DI : RetBATy->dependent_decls()) { + ValueDecl *VD = DI.getDecl(); + + // The decl should be either ParmVarDecl or VarDecl that has + // __unsafe_late_const attribute. + assert(isa(VD)); + + // Load the decl. + DeclarationNameInfo NameInfo(VD->getDeclName(), RetVal->getBeginLoc()); + auto *DRE = DeclRefExpr::Create(SemaRef.Context, NestedNameSpecifierLoc(), + /*TemplateKWLoc=*/SourceLocation(), VD, + /*RefersToCapturedVariable=*/false, + NameInfo, VD->getType(), VK_LValue); + auto Res = SemaRef.DefaultLvalueConversion(DRE); + if (Res.isInvalid()) + return true; + Expr *E = Res.get(); + + if (DI.isDeref()) { + // Dereference the loaded pointer. + auto Res = + SemaRef.CreateBuiltinUnaryOp(RetVal->getBeginLoc(), UO_Deref, E); + if (Res.isInvalid()) + return true; + E = Res.get(); + } + + auto *OVE = OpaqueValueExpr::Wrap(SemaRef.Context, E); + OVEs.push_back(OVE); + + unsigned Level = DI.isDeref() ? 1 : 0; + Builder.insertDeclToNewValue(VD, OVE, Level); + } + + auto Res = Builder.build(RetTy, RetOVE, /*WrappedValue=*/nullptr, &OVEs); + if (Res.isInvalid()) + return true; + + S->setRetValue(Res.get()); + getParentMap().setParent(Res.get(), S); + return false; +} + +/// checkCountAttributedLocalsInBlock - This checks if dependent local variables +/// are declared within the same basic block. +bool checkCountAttributedLocalsInBlock(Sema &SemaRef, const CFGBlock *block) { + llvm::SmallPtrSet LocalDeclSet; + bool HadError = false; + + auto checkLocalDependentCountTypes = [&](const VarDecl *VD) { + QualType Ty = VD->getType(); + while (!Ty.isNull()) { + if (auto DCPTy = Ty->getAs()) { + for (const TypeCoupledDeclRefInfo &DI : DCPTy->dependent_decls()) { + if (IsModifiableValue(SemaRef, DI.getDecl()) && + LocalDeclSet.find(DI.getDecl()) == LocalDeclSet.end()) { + // The dependent decl can still be non-local variable if there was + // an erroneous condition. Skip it to avoid reporting an irrelevant + // error message. + if (auto *VD = dyn_cast(DI.getDecl())) { + if (!VD->isLocalVarDecl()) { + assert(SemaRef.hasUncompilableErrorOccurred()); + continue; + } + SemaRef.Diag(VD->getLocation(), + diag::err_bounds_safety_local_dependent_count_block); + HadError = true; + } + } + } + } + Ty = Ty->getPointeeType(); + } + }; + + for (CFGBlock::const_iterator BI = block->begin(), BE = block->end(); + BI != BE; ++BI) { + if (std::optional stmt = BI->getAs()) { + const DeclStmt *DS = dyn_cast(stmt->getStmt()); + if (!DS) + continue; + for (auto D : DS->decls()) { + auto VD = dyn_cast(D); + if (!VD) + continue; + checkLocalDependentCountTypes(VD); + LocalDeclSet.insert(VD); + } + } + } + return !HadError; +} + +namespace clang { + +void DynamicCountPointerAssignmentAnalysis::run() { + AnalysisDeclContext AC(/* AnalysisDeclContextManager */ nullptr, dcl); + + CFG *cfg = AC.getCFG(); + if (!cfg) + return; + + for (CFG::const_iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) { + const CFGBlock *block = *I; + checkCountAttributedLocalsInBlock(SemaRef, block); + CheckCountAttributedDeclAssignments checkDependents(SemaRef, AC); + /// Visiting Stmt within a CFGBlock backward. Doing so makes easier to + /// determine whether a function call is used in RHS of a tracked assignment + /// expression or the call is standalone. + for (CFGBlock::const_reverse_iterator BI = (*I)->rbegin(), + BE = (*I)->rend(); + BI != BE; ++BI) { + if (std::optional stmt = BI->getAs()) + checkDependents.BeginStmt(const_cast(stmt->getStmt())); + } + } +} + +RecordDecl *FlexibleArrayMemberUtils::GetFlexibleRecord(QualType QT) { + auto *RT = QT->getAs(); + if (!RT) + return nullptr; + + auto *RD = RT->getDecl(); + if (!RD->hasFlexibleArrayMember()) + return nullptr; + + const CountAttributedType *DCPTy = nullptr; + RecordDecl *FlexibleRecord = RD; + do { + if (FlexibleRecord->getTagKind() == TagTypeKind::Union) + return nullptr; + FieldDecl *FlexibleField = nullptr; + for (FieldDecl *FD : FlexibleRecord->fields()) { + FlexibleField = FD; + } + assert(FlexibleField); + QualType FieldType = FlexibleField->getType(); + if (auto *RT = FieldType->getAs()) { + FlexibleRecord = RT->getDecl(); + } else { + DCPTy = FieldType->isIncompleteArrayType() ? FieldType->getAs() : nullptr; + FlexibleRecord = nullptr; + } + } while (!DCPTy && FlexibleRecord); + + if (!DCPTy) + return nullptr; + + return RD; +} + +bool FlexibleArrayMemberUtils::Find( + RecordDecl *RD, SmallVectorImpl &PathToFlex, + ArrayRef &CountDecls) { + if (!RD->hasFlexibleArrayMember()) + return false; + + const CountAttributedType *DCPTy = nullptr; + RecordDecl *FlexibleRecord = RD; + do { + if (FlexibleRecord->getTagKind() == TagTypeKind::Union) + return false; + FieldDecl *FlexibleField = nullptr; + for (FieldDecl *FD : FlexibleRecord->fields()) { + FlexibleField = FD; + } + assert(FlexibleField); + PathToFlex.push_back(FlexibleField); + QualType FieldType = FlexibleField->getType(); + if (auto *RT = FieldType->getAs()) { + FlexibleRecord = RT->getDecl(); + } else { + DCPTy = FieldType->isIncompleteArrayType() ? FieldType->getAs() : nullptr; + FlexibleRecord = nullptr; + } + } while (!DCPTy && FlexibleRecord); + + if (!DCPTy) + return false; + + CountDecls = DCPTy->getCoupledDecls(); + return true; +} + +Expr *FlexibleArrayMemberUtils::SelectFlexibleObject( + const SmallVectorImpl &PathToFlex, Expr *Base) { + bool IsArrow = Base->getType()->isPointerType(); + Expr *FlexibleObj = Base; + for (unsigned I = 0; I < PathToFlex.size() - 1; ++I) { + if (IsArrow && !FlexibleObj->isPRValue()) { + FlexibleObj = ImplicitCastExpr::Create( + SemaRef.Context, FlexibleObj->getType(), CK_LValueToRValue, + FlexibleObj, nullptr, VK_PRValue, SemaRef.CurFPFeatureOverrides()); + } + auto *FlexibleField = PathToFlex[I]; + FlexibleObj = MemberExpr::CreateImplicit( + SemaRef.Context, FlexibleObj, IsArrow, FlexibleField, + FlexibleField->getType(), VK_LValue, OK_Ordinary); + IsArrow = false; + } + return FlexibleObj; +} + +ExprResult FlexibleArrayMemberUtils::BuildCountExpr( + FieldDecl *FlexibleField, const ArrayRef CountDecls, + Expr *StructBase, SmallVectorImpl &OVEs, + CopyExpr *DeclReplacer) { + QualType FlexType = FlexibleField->getType(); + const auto *DCPTy = FlexType->getAs(); + assert(DCPTy); + Expr *CountTemplate = DCPTy->getCountExpr(); + + CopyExpr Copy = DeclReplacer ? *DeclReplacer : CopyExpr(SemaRef); + CopyExpr CopyNoRepl(SemaRef); + bool IsArrow = StructBase->getType()->isPointerType(); + for (auto DeclRefInfo : CountDecls) { + auto *VD = DeclRefInfo.getDecl(); + // For `counted_by(hdr.len)` we don't need a replacement for the MemberExpr + // (`.len`), only the DeclRefExpr (`hdr`). + // For `counted_by(len + len)` we don't need to add `len` twice - the full + // count expression is transformed outside the loop and will replace both + // DeclRefs regardless. + if (DeclRefInfo.isMember() || Copy.HasDeclReplacement(VD)) + continue; + + if (auto *FD = dyn_cast(VD)) { + Expr *Base = CopyNoRepl.TransformExpr(StructBase).get(); + Base = SemaRef.DefaultLvalueConversion(Base).get(); + if (IsArrow && !Base->isPRValue()) { + Base = ImplicitCastExpr::Create( + SemaRef.Context, Base->getType(), CK_LValueToRValue, Base, nullptr, + VK_PRValue, SemaRef.CurFPFeatureOverrides()); + } + ExprObjectKind OK = FD->isBitField() ? OK_BitField : OK_Ordinary; + auto *NewMember = MemberExpr::CreateImplicit( + SemaRef.Context, Base, IsArrow, FD, FD->getType(), VK_LValue, OK); + ExprResult Lvalue = SemaRef.DefaultLvalueConversion(NewMember); + if (!Lvalue.get()) + return ExprError(); + + // Don't wrap the count member access in OVE. Instead, the user of + // this expression should make sure it's always rebuilt. The base pointer + // may be null so we guard member access to the base in the null check. + // However, a materialization expression that wraps the null check may + // still evaluate the member access, To avoid this, we do not wrap the + // count member access in OVE. + Copy.UnsafelyAddDeclSubstitution(FD, Lvalue.get()); + } + } + + ExprResult Transformed = Copy.TransformExpr(CountTemplate); + if (!Transformed.get()) + return ExprError(); + + return SemaRef.DefaultLvalueConversion(Transformed.get()); +} + +TransformDynamicCountWithFunctionArgument :: + TransformDynamicCountWithFunctionArgument( + Sema &SemaRef, const SmallVectorImpl &Args, unsigned FirstParam) + : BaseTransform(SemaRef), ActualArgs(Args), FirstParam(FirstParam) {} + +ExprResult TransformDynamicCountWithFunctionArgument::TransformDeclRefExpr( + DeclRefExpr *E) { + ParmVarDecl *FormalArg = dyn_cast(E->getDecl()); + if (!FormalArg) + return Owned(E); + + unsigned Index = FormalArg->getFunctionScopeIndex(); + assert(Index >= FirstParam); + Index -= FirstParam; + assert(Index < ActualArgs.size()); + return ActualArgs[Index]; +} + +} // namespace clang diff --git a/clang/lib/Sema/DynamicCountPointerAssignmentAnalysis.h b/clang/lib/Sema/DynamicCountPointerAssignmentAnalysis.h new file mode 100644 index 0000000000000..a0411f9a302cc --- /dev/null +++ b/clang/lib/Sema/DynamicCountPointerAssignmentAnalysis.h @@ -0,0 +1,387 @@ +//===--- DynamicCountPointerAssignmentAnalysis.h --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements interface of dynamic count pointer assignment analysis +// for -fbounds-safety. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_SEMA_DYNAMIC_COUNT_POINTER_ASSIGNMENT_ANALYSIS_H +#define LLVM_CLANG_SEMA_DYNAMIC_COUNT_POINTER_ASSIGNMENT_ANALYSIS_H + +#include "TreeTransform.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/Sema/Sema.h" +#include "llvm/Support/SaveAndRestore.h" + +namespace clang { + +class DynamicCountPointerAssignmentAnalysis { + Sema &SemaRef; + Decl *dcl; +public: + DynamicCountPointerAssignmentAnalysis(Sema &SemaRef, Decl *dcl) + : SemaRef(SemaRef), dcl(dcl) {} + + void run(); + + static RecordDecl *computeFlexBaseKey(Expr *InE, + llvm::raw_string_ostream *OS); +}; + +/// CopyExpr simply duplicates expressions up to OpaqueValueExpr, which are +/// used as-is. +class CopyExpr : public TreeTransform { + /// List of declarations that should be substituted for a given expression. + /// This is meant to assist with bounds checking on CountAttributedType + /// by allowing users to replace the reference declaration with the right + /// expression. + llvm::SmallVector, 1> Replacements; + bool InSyntacticTransformation = false; + + ValueDecl *SelectFirstDecl(ValueDecl *Decl) { + if (auto *VD = dyn_cast(Decl)) { + Decl = VD->getFirstDecl(); + } else if (auto *FD = dyn_cast(Decl)) { + Decl = FD->getFirstDecl(); + } + return Decl; + } +public: + using TreeTransform::TreeTransform; + + bool AlwaysRebuild() { return true; } + + void AddDeclSubstitution(ValueDecl *DeclToChange, + OpaqueValueExpr *ValueToUse) { + UnsafelyAddDeclSubstitution(DeclToChange, ValueToUse); + } + + // UnsafelyAddDeclSubstitution lets you replace declaration references with + // any expression that you like better, including expressions that would + // unsafely be used in multiple places in the AST. Prefer AddDeclSubstitution + // unless you expect the result to not actually be used in the final AST. + void UnsafelyAddDeclSubstitution(ValueDecl *DeclToChange, Expr *ValueToUse) { + Replacements.emplace_back(SelectFirstDecl(DeclToChange), ValueToUse); + } + + ExprResult TransformOpaqueValueExpr(OpaqueValueExpr *E) { + if (RemovedOVEs.count(E)) + return TreeTransform::TransformExpr(E->getSourceExpr()); + // Don't copy expressions inside opaque values. + return E; + } + + bool HasDeclReplacement(ValueDecl *VD) { + for (auto &Pair : Replacements) { + if (Pair.first == VD) { + return true; + } + } + return false; + } + + ExprResult RebuildDeclRefExpr(NestedNameSpecifierLoc QualifierLoc, + ValueDecl *VD, + const DeclarationNameInfo &NameInfo, + NamedDecl *Found, + TemplateArgumentListInfo *TemplateArgs) { + VD = SelectFirstDecl(VD); + for (auto &Pair : Replacements) { + if (Pair.first == VD) { + // Rebuild the replacement expression. To avoid recursion, we + // use a new instance of CopyExpr. + return CopyExpr(getSema()).TransformExpr(Pair.second); + } + } + + // TreeTransform::RebuildDeclRefExpr below does semantic analysis, and + // well-formed programs cannot have references to fields in C. However, in + // -fbounds-safety we can define a struct: + // struct { + // void *p1; + // void *__ended_by(p1) p2; + // void *__ended_by(p2-1) p3; + // }; + // p2 in p2-1 will be bounds-safety-promoted to __bidi_indexable during + // LValueToRValue. This will try to create copies of DeclRefExprs to p1 and + // p2, which would trigger an assert "building reference to field in C?' in + // TreeTransform::RebuildDeclRefExpr. To avoid this, we build the + // DeclRefExprs here manually. + if (isa(VD)) { + // Copy-paste from Sema::BuildDeclarationNameExpr, but without assert. + QualType Ty = VD->getType().getNonReferenceType(); + ExprValueKind VK = VK_LValue; + CXXScopeSpec SS; + SS.Adopt(QualifierLoc); + return getSema().BuildDeclRefExpr(VD, Ty, VK, NameInfo, &SS, Found, + /*TemplateKWLoc=*/SourceLocation(), + TemplateArgs); + } + + return TreeTransform::RebuildDeclRefExpr(QualifierLoc, VD, NameInfo, Found, + TemplateArgs); + } + + /// Super class returns the syntactic form only. Override it to ensure that + /// TransformMemberExpr can access the semantic form. + ExprResult TransformInitListExpr(InitListExpr *E) { + if (InSyntacticTransformation) + return TreeTransform::TransformInitListExpr(E); + assert(E->isSemanticForm()); + bool InitChanged = false; + + EnterExpressionEvaluationContext Context( + getSema(), EnterExpressionEvaluationContext::InitList); + + SmallVector Inits; + if (TransformExprs(E->getInits(), E->getNumInits(), false, Inits, + &InitChanged)) + return ExprError(); + + ExprResult Res = + RebuildInitList(E->getLBraceLoc(), Inits, E->getRBraceLoc()); + if (Res.isUsable()) { + SaveAndRestore SAR(InSyntacticTransformation, true); + ExprResult SyntacticRes = TreeTransform::TransformInitListExpr(E); + if (SyntacticRes.isUsable()) { + auto SyntacticIL = cast(SyntacticRes.get()); + assert(SyntacticIL->isSyntacticForm()); + cast(Res.get())->setSyntacticForm(SyntacticIL); + } + } + return Res; + } + + ExprResult RebuildMemberExpr(Expr *Base, SourceLocation OpLoc, + bool isArrow, + NestedNameSpecifierLoc QualifierLoc, + SourceLocation TemplateKWLoc, + const DeclarationNameInfo &MemberNameInfo, + ValueDecl *Member, + NamedDecl *FoundDecl, + const TemplateArgumentListInfo *ExplicitTemplateArgs, + NamedDecl *FirstQualifierInScope) { + if (auto IL = dyn_cast(Base); + IL && !InSyntacticTransformation) { + // We need field initializers to align with the struct type. + assert(IL->isSemanticForm()); + auto *VD = cast(Member); + Expr *Val = IL->getInitForField(VD); + assert(Val && !isa(Val)); + return Val; + } + + // XXX: this assumes that you'll only see the field accessed on the + // specific (and implicit) object you're interested in. It's not a problem + // with the current user-facing rules, which prohibit count expressions + // from referencing a field off of any other object, but if the rules + // change or the compiler starts synthesizing expressions that do so, this + // would need to change in difficult ways. + for (auto &Pair : Replacements) { + if (Pair.first == Member) { + // Rebuild the replacement expression. To avoid recursion, we + // use a new instance of CopyExpr. + return CopyExpr(getSema()).TransformExpr(Pair.second); + } + } + return TreeTransform::RebuildMemberExpr( + Base, OpLoc, isArrow, QualifierLoc, TemplateKWLoc, MemberNameInfo, + Member, FoundDecl, ExplicitTemplateArgs, FirstQualifierInScope); + } +}; + +class ForceRebuild : public TreeTransform { +public: + using BaseTransform = TreeTransform; + bool AlwaysRebuild() { return true; } + ForceRebuild(Sema &SemaRef) : TreeTransform(SemaRef) {} + ExprResult TransformOpaqueValueExpr(OpaqueValueExpr *E) { + if (RemovedOVEs.count(E)) + return TransformExpr(E->getSourceExpr()); + return E; + } + + // Workaround upstream behavior of TreeTransform. + // + // Normally rebuilding `CompoundLiteralExpr` runs + // `getSema().BuildCompoundLiteralExpr` which reruns Sema checks. That + // function checks `CurContext->isFunctionOrMethod();` which won't necessarily + // get the Context this CompoundLiteralExpr was originally built in which + // means we can emit spurious `diag::err_init_element_not_constant` + // diagnostics. To workaround this we construct the expression directly using + // the information on `CompoundLiteralExpr::isFileScope()`. + // + // FIXME: Is this a bug upstream or intentional behavior? + ExprResult RebuildCompoundLiteralExpr(SourceLocation LParenLoc, + TypeSourceInfo *TInfo, + SourceLocation RParenLoc, Expr *Init, + bool IsFileScope) { + + QualType literalType = TInfo->getType(); + // Copied from Sema::BuildCompoundLiteralExpr + ExprValueKind VK = (getSema().getLangOpts().CPlusPlus && + !(IsFileScope && literalType->isArrayType())) + ? VK_PRValue + : VK_LValue; + + auto *E = new (getSema().getASTContext()) CompoundLiteralExpr( + LParenLoc, TInfo, TInfo->getType(), VK, Init, IsFileScope); + return E; + } +}; + +/// Create a new expression in terms of pre-registered RHS of assignments by +/// replacing a DeclRef or MemberExpr with corresponding RHS. +/// +/// We are performing checks before any of the assignments are done. This means, +/// we can't simply load the updated \c f->cnt to perform count checks like this +/// \f[0 <= f->cnt + 2 <= bounds_of(new_ptr)\f] since we only have the old \c +/// f->cnt. Therefore, we create a new count expression in terms of \c new_cnt +/// by replacing \c f->cnt, which is \c new_cnt + 2 in the example. +/// +/// \code +/// struct Foo { int *__counted_by(cnt + 2) ptr; int cnt; }; +/// void Test(struct Foo *f) { +/// f->ptr = new_ptr; +/// f->cnt = new_cnt; +/// \endcode +struct ReplaceDeclRefWithRHS : public TreeTransform { + using BaseTransform = TreeTransform; + using ExprLevelPair = std::pair; + using DeclToNewValueTy = llvm::DenseMap; + + llvm::SmallPtrSet ReplacingValues; + unsigned LevelToUnwrap = 0; + DeclToNewValueTy &DeclToNewValue; + Expr *MemberBase = nullptr; + + explicit ReplaceDeclRefWithRHS(Sema &SemaRef, + DeclToNewValueTy &DeclToNewValue) + : BaseTransform(SemaRef), DeclToNewValue(DeclToNewValue) {} + + bool AlwaysRebuild() { return true; } + + ExprResult TransformOpaqueValueExpr(OpaqueValueExpr *E) { return Owned(E); } + + /// If we are replacing the value pointed to by DeclRef, we unwrap dereference + /// operator(s) from the original bound expression. + /// + /// In the following example, we replace \c *out_cnt the argument of \c + /// __counted_by with \c new_cnt, not just \c out_cnt. + /// + /// \code + /// void foo(int *__counted_by(*out_cnt) *out_buf, int *out_cnt) { + /// *out_buf = new_buf; + /// *out_cnt = new_cnt; + /// } + /// \endcode + ExprResult TransformUnaryOperator(UnaryOperator *E) { + if (E->getOpcode() != UO_Deref) + return BaseTransform::TransformUnaryOperator(E); + + ExprResult SubExpr = TransformExpr(E->getSubExpr()); + if (SubExpr.isInvalid()) + return ExprError(); + + if (LevelToUnwrap != 0) { + --LevelToUnwrap; + return SubExpr; + } + + return RebuildUnaryOperator(E->getOperatorLoc(), E->getOpcode(), + SubExpr.get()); + } + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + auto It = DeclToNewValue.find(E->getDecl()); + if (It == DeclToNewValue.end()) { + if (auto *Field = dyn_cast(E->getDecl())) { + assert(MemberBase); + bool IsArrow = MemberBase->getType()->isPointerType(); + return MemberExpr::CreateImplicit(getSema().Context, MemberBase, + IsArrow, Field, Field->getType(), + VK_LValue, OK_Ordinary); + } + return BaseTransform::TransformDeclRefExpr(E); + } + // Clone the new value. + return Replace(It->second.first, It->second.second); + } + + ExprResult TransformMemberExpr(MemberExpr *E) { + auto It = DeclToNewValue.find(E->getMemberDecl()); + if (It == DeclToNewValue.end()) + return BaseTransform::TransformMemberExpr(E); + return Replace(It->second.first, It->second.second); + } + + ExprResult Replace(Expr *New, unsigned Level) { + ExprResult Repl = ForceRebuild(SemaRef).TransformExpr(New); + + if (const Expr *E = Repl.get()) + ReplacingValues.insert(E); + + assert(LevelToUnwrap == 0); + LevelToUnwrap = Level; + return Repl; + } + + const llvm::SmallPtrSetImpl &GetReplacingValues() const { + return ReplacingValues; + } +}; + +class FlexibleArrayMemberUtils { + Sema &SemaRef; + +public: + FlexibleArrayMemberUtils(Sema &SemaRef) : SemaRef(SemaRef) + { } + + /// If RD is not a union and has a flexible array member with a dynamic count, + /// return true and fill PathToFlex with every field decl towards the flexible + /// field, and CountDecls with the list of declarations involved in the count + /// expression. Return false otherwise. + bool Find(RecordDecl *RD, llvm::SmallVectorImpl &PathToFlex, + ArrayRef &CountDecls); + + RecordDecl *GetFlexibleRecord(QualType QT); + + Expr *SelectFlexibleObject( + const llvm::SmallVectorImpl &PathToFlex, Expr *Base); + + ExprResult BuildCountExpr(FieldDecl *FlexibleField, + const ArrayRef CountDecls, + Expr *Base, + llvm::SmallVectorImpl &OVEs, + CopyExpr *Copy = nullptr); +}; + +/// Function return type or argument may have a '__counted_by()' attribute with +/// the count expression expressed with ParmVarDecl. Similarly to instantiating +/// count expression of struct fields expressed by FieldDecl, we replace +/// DeclRefExpr of ParmVarDecl with the actual argument of the function call. +class TransformDynamicCountWithFunctionArgument + : public TreeTransform { + typedef TreeTransform + BaseTransform; + const SmallVectorImpl &ActualArgs; + unsigned FirstParam = 0; + +public: + TransformDynamicCountWithFunctionArgument(Sema &SemaRef, + const SmallVectorImpl &Args, + unsigned FirstParam = 0); + + ExprResult TransformDeclRefExpr(DeclRefExpr *E); +}; + +} // end namespace clang + +#endif // LLVM_CLANG_SEMA_DYNAMIC_COUNT_POINTER_ASSIGNMENT_ANALYSIS_H diff --git a/clang/lib/Sema/DynamicCountPointerAssignmentAnalysisExported.cpp b/clang/lib/Sema/DynamicCountPointerAssignmentAnalysisExported.cpp new file mode 100644 index 0000000000000..20d93c5b0bb99 --- /dev/null +++ b/clang/lib/Sema/DynamicCountPointerAssignmentAnalysisExported.cpp @@ -0,0 +1,22 @@ +#include "clang/Sema/DynamicCountPointerAssignmentAnalysisExported.h" +#include "DynamicCountPointerAssignmentAnalysis.h" +#include "TreeTransform.h" +#include "clang/AST/Expr.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang { + +ExprResult ReplaceCountExprParamsWithArgsFromCall(const Expr *CountExpr, + const CallExpr *CE, Sema &S) { + // FIXME: Use of `const_cast` is here because it is hard to make + // TreeTransform work with `const Expr` but we also want to provide a sane + // public interface. + CallExpr *CENoConst = const_cast(CE); + SmallVector CallArgs( + CENoConst->getArgs(), CENoConst->getArgs() + CENoConst->getNumArgs()); + TransformDynamicCountWithFunctionArgument T(S, CallArgs); + ExprResult Replaced = T.TransformExpr(const_cast(CountExpr)); + return Replaced; +} +} // namespace clang diff --git a/clang/lib/Sema/IdentifierResolver.cpp b/clang/lib/Sema/IdentifierResolver.cpp index 2213c3c837243..0d2bc2069f691 100644 --- a/clang/lib/Sema/IdentifierResolver.cpp +++ b/clang/lib/Sema/IdentifierResolver.cpp @@ -228,7 +228,11 @@ void IdentifierResolver::RemoveDecl(NamedDecl *D) { assert(Ptr && "Didn't find this decl on its identifier's chain!"); if (isDeclPtr(Ptr)) { - assert(D == Ptr && "Didn't find this decl on its identifier's chain!"); + // FIXME: The following assert fires for ObjectiveC id, SEL, and Class + // declarations when the module is explicitly built. For implicit builds + // it works fine. rdar://58552906 + + // assert(D == Ptr && "Didn't find this decl on its identifier's chain!"); Name.setFETokenInfo(nullptr); return; } diff --git a/clang/lib/Sema/ScopeInfo.cpp b/clang/lib/Sema/ScopeInfo.cpp index d089836fa36dd..da2d574f41a7d 100644 --- a/clang/lib/Sema/ScopeInfo.cpp +++ b/clang/lib/Sema/ScopeInfo.cpp @@ -30,6 +30,7 @@ void FunctionScopeInfo::Clear() { HasFallthroughStmt = false; UsesFPIntrin = false; HasPotentialAvailabilityViolations = false; + HasPotentialFeatureAvailabilityViolations = false; ObjCShouldCallSuper = false; ObjCIsDesignatedInit = false; ObjCWarnForNoDesignatedInitChain = false; diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 4edcd3f945f6c..3a510153f8637 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -80,6 +80,20 @@ using namespace clang; using namespace sema; +/* TO_UPSTREAM(BoundsSafety) ON */ +bool Sema::isCXXSafeBuffersBoundsSafetyInteropEnabledAt( + SourceLocation Loc) const { + // Informations of '#pragma clang unsafe_buffer_usage begin/end' are stored + // in the Preprocessor. So we need also check + // `getPreprocessor().isSafeBufferOptOut`. + bool NotSupressedByPragmas = + Loc.isInvalid() || !getPreprocessor().isSafeBufferOptOut(SourceMgr, Loc); + return LangOpts.CPlusPlus && LangOpts.isBoundsSafetyAttributeOnlyMode() && + NotSupressedByPragmas && + !Diags.isIgnored(diag::warn_unsafe_buffer_operation, Loc); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + SourceLocation Sema::getLocForEndOfToken(SourceLocation Loc, unsigned Offset) { return Lexer::getLocForEndOfToken(Loc, Offset, SourceMgr, LangOpts); } @@ -156,11 +170,44 @@ namespace clang { namespace sema { class SemaPPCallbacks : public PPCallbacks { + /*TO_UPSTREAM(BoundsSafety) ON*/ + struct Include { + SourceLocation FileLoc; + BoundsSafetyPointerAttributes FAttr; + + Include(SourceLocation FileLoc) : FileLoc(FileLoc) {} + }; + /*TO_UPSTREAM(BoundsSafety) OFF*/ + Sema *S = nullptr; - llvm::SmallVector IncludeStack; + // TO_UPSTREAM(BoundsSafety): SourceLocation -> Include + llvm::SmallVector IncludeStack; llvm::SmallVector ProfilerStack; + // TO_UPSTREAM(BoundsSafety) + BoundsSafetyPointerAttributes RootFAttr; + + /*TO_UPSTREAM(BoundsSafety) ON*/ + void PushInclude(SourceLocation IncludeLoc, SourceLocation FileLoc) { + IncludeStack.push_back(Include(IncludeLoc)); + if (S->getSourceManager().isInSystemHeader(FileLoc)) { + IncludeStack.back().FAttr = SystemHeaderAttributes(); + } else { + IncludeStack.back().FAttr = S->CurPointerAbi; + } + } + + SourceLocation PopInclude() { + return IncludeStack.pop_back_val().FileLoc; + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ public: + /*TO_UPSTREAM(BoundsSafety) ON*/ + SemaPPCallbacks() { + RootFAttr.setSingle(); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + void set(Sema &S) { this->S = &S; } void reset() { S = nullptr; } @@ -173,15 +220,26 @@ class SemaPPCallbacks : public PPCallbacks { switch (Reason) { case EnterFile: { SourceManager &SM = S->getSourceManager(); - SourceLocation IncludeLoc = SM.getIncludeLoc(SM.getFileID(Loc)); + FileID FID = SM.getFileID(Loc); + SourceLocation IncludeLoc = SM.getIncludeLoc(FID); + /* TO_UPSTREAM(BoundsSafety) ON*/ + StringRef Filename(""); + // IncludeLoc may be invalid because of #line pragma. Retry with `PresumedLoc` which contains the location modified by #line. + if (IncludeLoc.isInvalid()) { + auto PresumedLoc = SM.getPresumedLoc(Loc); + IncludeLoc = PresumedLoc.getIncludeLoc(); + Filename = PresumedLoc.getFilename(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ if (IncludeLoc.isValid()) { if (llvm::timeTraceProfilerEnabled()) { - OptionalFileEntryRef FE = SM.getFileEntryRefForID(SM.getFileID(Loc)); + OptionalFileEntryRef FE = SM.getFileEntryRefForID(FID); ProfilerStack.push_back(llvm::timeTraceAsyncProfilerBegin( - "Source", FE ? FE->getName() : StringRef(""))); + "Source", FE ? FE->getName() : Filename)); } - IncludeStack.push_back(IncludeLoc); + // TO_UPSTREAM(BoundsSafety) + PushInclude(IncludeLoc, Loc); S->DiagnoseNonDefaultPragmaAlignPack( Sema::PragmaAlignPackDiagnoseKind::NonDefaultStateAtInclude, IncludeLoc); @@ -195,13 +253,68 @@ class SemaPPCallbacks : public PPCallbacks { S->DiagnoseNonDefaultPragmaAlignPack( Sema::PragmaAlignPackDiagnoseKind::ChangedStateAtExit, - IncludeStack.pop_back_val()); + PopInclude()); } break; + /* TO_UPSTREAM(BoundsSafety) ON*/ + case SystemHeaderPragma: + IncludeStack.back().FAttr = SystemHeaderAttributes(); + break; + /* TO_UPSTREAM(BoundsSafety) OFF*/ default: break; } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + S->CurPointerAbi = + IncludeStack.empty() ? RootFAttr : IncludeStack.back().FAttr; + /* TO_UPSTREAM(BoundsSafety) OFF*/ } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + void PragmaAbiPointerAttributesSet(SourceLocation Loc, + ArrayRef Spec) + override { + if (!S) + return; + BoundsSafetyPointerAttributes FAttr; + auto GNU = AttributeCommonInfo::AS_GNU; + for (const IdentifierInfo *II : Spec) { + auto Kind = AttributeCommonInfo::getParsedKind(II, /*ScopeName=*/nullptr, + GNU); + auto BoundsAttrPrev = FAttr.getBoundsAttr(); + switch (Kind) { + case AttributeCommonInfo::AT_PtrBidiIndexable: + FAttr.setBidiIndexable(); + break; + case AttributeCommonInfo::AT_PtrIndexable: + FAttr.setIndexable(); + break; + case AttributeCommonInfo::AT_PtrUnsafeIndexable: + FAttr.setUnsafeIndexable(); + break; + case AttributeCommonInfo::AT_PtrSingle: + FAttr.setSingle(); + break; + default: + S->Diag(Loc, diag::err_bounds_safety_abi_ptr_attr_invalid) << II; + break; + } + if (BoundsAttrPrev && BoundsAttrPrev != FAttr.getBoundsAttr()) { + S->Diag(Loc, diag::err_bounds_safety_conflicting_pointer_attributes) + << /* pointer */ 1 << /* bound */ 0; + continue; + } + } + (IncludeStack.empty() ? RootFAttr : IncludeStack.back().FAttr) = FAttr; + S->CurPointerAbi = FAttr; + } + + BoundsSafetyPointerAttributes SystemHeaderAttributes() const { + return BoundsSafetyPointerAttributes(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + void PragmaDiagnostic(SourceLocation Loc, StringRef Namespace, diag::Severity Mapping, StringRef Str) override { // If one of the analysis-based diagnostics was enabled while processing @@ -336,6 +449,10 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, PP.addPPCallbacks(std::move(Callbacks)); SemaPPCallbackHandler->set(*this); + /* TO_UPSTREAM(BoundsSafety) ON*/ + CurPointerAbi.setSingle(); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + CurFPFeatures.setFPEvalMethod(PP.getCurrentFPEvalMethod()); } @@ -729,10 +846,11 @@ void Sema::diagnoseZeroToNullptrConversion(CastKind Kind, const Expr *E) { /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit cast. /// If there is already an implicit cast, merge into the existing one. /// The result is of the given category. -ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, - CastKind Kind, ExprValueKind VK, - const CXXCastPath *BasePath, - CheckedConversionKind CCK) { +ExprResult +Sema::ImpCastExprToType(Expr *E, QualType Ty, CastKind Kind, ExprValueKind VK, + const CXXCastPath *BasePath, CheckedConversionKind CCK, + //TO_UPSTREAM(BoundsSafety) + bool DiagnoseBoundsSafetyIncompleteArrayPromotion) { #ifndef NDEBUG if (VK == VK_PRValue && !E->isPRValue()) { switch (Kind) { @@ -775,7 +893,10 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, return ImplicitCastExpr::Create(Context, Ty, Kind, E, BasePath, VK, CurFPFeatureOverrides()); - if (ExprTy == TypeTy) + // BoundsSafety: Even if source and destination have the same canonical type, + // we still insert ImplicitCastExpr for types with different -fbounds-safety pointer + // attributes. + if (ExprTy == TypeTy && Kind != CK_BoundsSafetyPointerCast) return E; if (Kind == CK_ArrayToPointerDecay) { @@ -810,8 +931,94 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, } } + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety: a reference to array decays into a bound pointer. + // FIXME: This should also be supported without '-fbounds-safety' when destination + // of this expression is annotated with '__bidi_indexable' or '__indexable' + // (rdar://70018442). + // XXX: We don't promote builtin va_list to bound type for now to preserve the + // compatibility with the builtin function prototype + auto PreservingAttributes = [=](QualType QT, auto Callback) -> QualType { + SmallVector AttrKinds; + while (auto AT = QT->getAs()) { + AttrKinds.push_back(AT->getAttrKind()); + QT = AT->getModifiedType(); + } + + QT = Callback(QT); + + QualType EquivQT = QT; + for (auto I = AttrKinds.rbegin(), E = AttrKinds.rend(); I != E; ++I) + QT = Context.getAttributedType(*I, QT, EquivQT); + return QT; + }; + + if (getLangOpts().BoundsSafety) { + switch (Kind) { + case CK_ArrayToPointerDecay: { + if (!Context.hasSameUnqualifiedType( + ExprTy, Context.getBuiltinVaListType())) { + if (const auto *VTT = E->getType()->getAs()) { + Ty = PreservingAttributes(Ty, [=](QualType QT) { + QualType PT = Context.getPointerType( + QT->getPointeeType(), BoundsSafetyPointerAttributes::single()); + return Context.getValueTerminatedType(PT, VTT->getTerminatorExpr()); + }); + break; + } + if (E->getType()->isIncompleteArrayType() && + !E->getType()->isCountAttributedType() && + DiagnoseBoundsSafetyIncompleteArrayPromotion) { + Diag(E->getExprLoc(), + diag::warn_bounds_safety_promoting_incomplete_array_without_count); + } + Ty = PreservingAttributes(Ty, [=](QualType QT) { + auto PT = QT->getAs(); + BoundsSafetyPointerAttributes FA; + FA.setBidiIndexable(); + return Context.getPointerType(PT->getPointeeType(), FA); + }); + } + break; + } + + case CK_FunctionToPointerDecay: { + Ty = PreservingAttributes(Ty, [=](QualType QT) { + BoundsSafetyPointerAttributes FA; + FA.setSingle(); + auto PT = Ty->getAs(); + return Context.getPointerType(PT->getPointeeType(), FA); + }); + break; + } + + default: + break; + } + } else if (getLangOpts().DebuggerSupport && + (Kind == CK_BitCast || Kind == CK_NoOp) && + E->getType()->isPointerType() && Ty->isPointerType()) { + auto SrcPTy = E->getType()->getAs(); + auto DstPTy = Ty->getAs(); + + if (SrcPTy->getPointerAttributes() != DstPTy->getPointerAttributes()) { + if (Kind == CK_BitCast) { + QualType BridgeDstTy = PreservingAttributes(Ty, [&](QualType QT) { + return Context.getPointerType(SrcPTy->getPointeeType(), + DstPTy->getPointerAttributes()); + }); + E = ImplicitCastExpr::Create(Context, BridgeDstTy, CK_BoundsSafetyPointerCast, E, BasePath, VK, + CurFPFeatureOverrides()); + } else { + Kind = CK_BoundsSafetyPointerCast; + } + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (ImplicitCastExpr *ImpCast = dyn_cast(E)) { - if (ImpCast->getCastKind() == Kind && (!BasePath || BasePath->empty())) { + if (ImpCast->getCastKind() == Kind && (!BasePath || BasePath->empty()) && + Kind != CK_BoundsSafetyPointerCast) { ImpCast->setType(Ty); ImpCast->setValueKind(VK); return E; @@ -1254,6 +1461,10 @@ void Sema::ActOnEndOfTranslationUnit() { } } + if (getASTContext().isObjCMsgSendUsageFileSpecified()) + getASTContext().writeObjCMsgSendUsages( + getASTContext().getObjCMsgSendUsageFilename()); + DiagnoseUnterminatedPragmaAlignPack(); DiagnoseUnterminatedPragmaAttribute(); OpenMP().DiagnoseUnterminatedOpenMPDeclareTarget(); @@ -1445,6 +1656,14 @@ void Sema::ActOnEndOfTranslationUnit() { diag::err_tentative_def_incomplete_type)) VD->setInvalidDecl(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Now perform VarDecl checks for tentative definitions. Non-tentative + // definitions were already handled earlier in + // `Sema::FinalizeDeclaratorGroup()`. + if (!BoundsSafetyCheckVarDecl(VD, /*CheckTentativeDefinitions=*/true)) + VD->setInvalidDecl(); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // No initialization is performed for a tentative definition. CheckCompleteVariableDeclaration(VD); @@ -2888,6 +3107,21 @@ bool Sema::isDeclaratorFunctionLike(Declarator &D) { return Result; } +/*TO_UPSTREAM(BoundsSafety) ON*/ +bool Sema::BoundsSafetyFixItWasEmittedFor(const DeclaratorDecl *DD, bool Set) { + assert(DD); + assert(isa(DD) || isa(DD)); + if (Set) { + // Record the Decl + BoundsSafetyDeclsWithFixIts.insert(DD); + return true; + } + + // Retrieve whether a FixIt was emitted for this VarDecl. + return BoundsSafetyDeclsWithFixIts.contains(DD); +} + /*TO_UPSTREAM(BoundsSafety) OFF*/ + Attr *Sema::CreateAnnotationAttr(const AttributeCommonInfo &CI, StringRef Annot, MutableArrayRef Args) { diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp index de6cf1ae9d7ed..2a7d52913e9a4 100644 --- a/clang/lib/Sema/SemaARM.cpp +++ b/clang/lib/Sema/SemaARM.cpp @@ -751,8 +751,8 @@ bool SemaARM::CheckNeonBuiltinFunctionCall(const TargetInfo &TI, if (PtrArgNum >= 0) { // Check that pointer arguments have the specified type. Expr *Arg = TheCall->getArg(PtrArgNum); - if (ImplicitCastExpr *ICE = dyn_cast(Arg)) - Arg = ICE->getSubExpr(); + // TO_UPSTREAM(BoundsSafety) + Arg = Arg->IgnoreImpCasts(); ExprResult RHS = SemaRef.DefaultFunctionArrayLvalueConversion(Arg); QualType RHSTy = RHS.get()->getType(); @@ -767,7 +767,11 @@ bool SemaARM::CheckNeonBuiltinFunctionCall(const TargetInfo &TI, EltTy = EltTy.withConst(); QualType LHSTy = getASTContext().getPointerType(EltTy); Sema::AssignConvertType ConvTy; - ConvTy = SemaRef.CheckSingleAssignmentConstraints(LHSTy, RHS); + /* TO_UPSTREAM(BoundsSafety) ON*/ + ConvTy = SemaRef.CheckSingleAssignmentConstraints( + LHSTy, RHS, /*Diagnose=*/true, /*DiagnoseCFA=*/false, + /*ConvertRHS=*/true); + /* TO_UPSTREAM(BoundsSafety) OFF*/ if (RHS.isInvalid()) return true; if (SemaRef.DiagnoseAssignmentResult(ConvTy, Arg->getBeginLoc(), LHSTy, diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp b/clang/lib/Sema/SemaBoundsSafety.cpp index 1b356eec6b78a..b343cc8e3ba44 100644 --- a/clang/lib/Sema/SemaBoundsSafety.cpp +++ b/clang/lib/Sema/SemaBoundsSafety.cpp @@ -18,7 +18,10 @@ namespace clang { -static CountAttributedType::DynamicCountPointerKind +// In upstream the return type is `CountAttributedType::DynamicCountPointerKind` +/* TO_UPSTREAM(BoundsSafety) ON*/ +CountAttributedType::BoundsAttrKind +/* TO_UPSTREAM(BoundsSafety) OFF*/ getCountAttrKind(bool CountInBytes, bool OrNull) { if (CountInBytes) return OrNull ? CountAttributedType::SizedByOrNull @@ -27,14 +30,60 @@ getCountAttrKind(bool CountInBytes, bool OrNull) { : CountAttributedType::CountedBy; } -static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) { +static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD, + // TO_UPSTREAM(BoundsSafety) + Sema &S) { const auto *RD = FD->getParent(); // An unnamed struct is anonymous struct only if it's not instantiated. // However, the struct may not be fully processed yet to determine // whether it's anonymous or not. In that case, this function treats it as // an anonymous struct and tries to find a named parent. - while (RD && (RD->isAnonymousStructOrUnion() || - (!RD->isCompleteDefinition() && RD->getName().empty()))) { + + /* TO_UPSTREAM(BoundsSafety) ON*/ + const auto *ParentOfDeclWithAttr = FD->getParent(); + auto ShouldGetParent = [&]() -> bool { + if (!S.getLangOpts().ExperimentalLateParseAttributes || + RD != ParentOfDeclWithAttr) { + // This is the condition in upstream + return (RD->isAnonymousStructOrUnion() || + (!RD->isCompleteDefinition() && RD->getName().empty())); + } + // In `Parser::ParseStructUnionBody` we have an Apple Internal change + // to call `Actions.ActOnFields` **before** late parsed attributes are + // semantically checked. In upstream `Action.ActOnFields` is called + // afterwards. The effect of this is observable in this function because + // `RD->isCompleteDefinition()` will return true for the struct we are + // processing the attributes on with the Apple Internal change and false + // in upstream. + // + // E.g.: + // + // struct on_pointer_anon_buf { + // int count; + // struct { + // struct size_known *buf __counted_by(count); + // }; // <<-- Processing late parsed attrs of this struct + // }; + // + // For this particular example what's also counter-intuitive is that + // `RD->isAnonymousStructOrUnion()` returns false for the anonymous + // struct, that's because the `;` after the struct hasn't been processed + // yet so it hasn't been marked as anonymous yet. + + // HACK: + // To make lit tests work we don't test `RD->isCompleteDefinition()` + // when it's the RecordDecl that contains the FieldDecl with a `counted_by` + // like attribute (that we are in the middle of checking). Once we've gone + // beyond that RecordDecl we traverse just like upstream clang does. + // + // TODO: Remove this hack once we upstream the `Actions.ActOnFields` + // change (rdar://133402603). + assert(RD == ParentOfDeclWithAttr); + return RD->isAnonymousStructOrUnion() || RD->getName().empty(); + }; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + while (RD && ShouldGetParent()) { const auto *Parent = dyn_cast(RD->getParent()); if (!Parent) break; @@ -203,8 +252,11 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, // Whether CountRD is an anonymous struct is not determined at this // point. Thus, an additional diagnostic in case it's not anonymous struct // is done later in `Parser::ParseStructDeclaration`. - auto *RD = GetEnclosingNamedOrTopAnonRecord(FD); - auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Upstream doesn't pass `*this`. + auto *RD = GetEnclosingNamedOrTopAnonRecord(FD, *this); + auto *CountRD = GetEnclosingNamedOrTopAnonRecord(CountFD, *this); + /* TO_UPSTREAM(BoundsSafety) OFF*/ if (RD != CountRD) { Diag(E->getBeginLoc(), diag::err_count_attr_param_not_in_same_struct) @@ -218,6 +270,163 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes, return false; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +static SourceRange SourceRangeFor(const CountAttributedType *CATy, Sema &S) { + // Note: This implementation relies on `CountAttributedType` being unique. + // E.g.: + // + // struct Foo { + // int count; + // char* __counted_by(count) buffer; + // char* __counted_by(count) buffer2; + // }; + // + // The types of `buffer` and `buffer2` are unique. The types being + // unique means the SourceLocation of the `counted_by` expression can be used + // to find where the attribute was written. + + auto Fallback = CATy->getCountExpr()->getSourceRange(); + auto CountExprBegin = CATy->getCountExpr()->getBeginLoc(); + + // FIXME: We currently don't support the count expression being a macro + // itself. E.g.: + // + // #define ZERO 0 + // int* __counted_by(ZERO) x; + // + if (S.SourceMgr.isMacroBodyExpansion(CountExprBegin)) + return Fallback; + + auto FetchIdentifierTokenFromOffset = + [&](ssize_t Offset) -> std::optional { + SourceLocation OffsetLoc = CountExprBegin.getLocWithOffset(Offset); + Token Result; + if (Lexer::getRawToken(OffsetLoc, Result, S.SourceMgr, S.getLangOpts())) + return std::optional(); // Failed + + if (!Result.isAnyIdentifier()) + return std::optional(); // Failed + + return Result; // Success + }; + + auto CountExprEnd = CATy->getCountExpr()->getEndLoc(); + auto FindRParenTokenAfter = [&]() -> std::optional { + auto CountExprEndSpelling = S.SourceMgr.getSpellingLoc(CountExprEnd); + auto MaybeRParenTok = Lexer::findNextToken( + CountExprEndSpelling, S.getSourceManager(), S.getLangOpts()); + + if (!MaybeRParenTok.has_value()) + return std::nullopt; + + if (!MaybeRParenTok->is(tok::r_paren)) + return std::nullopt; + + return *MaybeRParenTok; + }; + + // Step back two characters to point at the last character of the attribute + // text. + // + // __counted_by(count) + // ^ ^ + // | | + // --- + auto MaybeLastAttrCharToken = FetchIdentifierTokenFromOffset(-2); + if (!MaybeLastAttrCharToken) + return Fallback; + + auto LastAttrCharToken = MaybeLastAttrCharToken.value(); + + if (LastAttrCharToken.getLength() > 1) { + // Special case: When the character is part of a macro the Token we get + // is the whole macro name (e.g. `__counted_by`). + if (LastAttrCharToken.getRawIdentifier() != + CATy->getAttributeName(/*WithMacroPrefix=*/true)) + return Fallback; + + // Found the beginning of the `__counted_by` macro + SourceLocation Begin = LastAttrCharToken.getLocation(); + // Now try to find the closing `)` of the macro. + auto MaybeRParenTok = FindRParenTokenAfter(); + if (!MaybeRParenTok.has_value()) + return Fallback; + + return SourceRange(Begin, MaybeRParenTok->getLocation()); + } + + assert(LastAttrCharToken.getLength() == 1); + // The Token we got back is just the last character of the identifier. + // This means a macro is not being used and instead the attribute is being + // used directly. We need to find the beginning of the identifier. We support + // two cases: + // + // * Non-affixed version. E.g: `counted_by` + // * Affixed version. E.g.: `__counted_by__` + + // Try non-affixed version. E.g.: + // + // __attribute__((counted_by(count))) + // ^ ^ + // | | + // ------------ + + // +1 is for `(` + const ssize_t NonAffixedSkipCount = + CATy->getAttributeName(/*WithMacroPrefix=*/false).size() + 1; + auto MaybeNonAffixedBeginToken = + FetchIdentifierTokenFromOffset(-NonAffixedSkipCount); + if (!MaybeNonAffixedBeginToken) + return Fallback; + + auto NonAffixedBeginToken = MaybeNonAffixedBeginToken.value(); + if (NonAffixedBeginToken.getRawIdentifier() == + CATy->getAttributeName(/*WithMacroPrefix=*/false)) { + // Found the beginning of the `counted_by`-like attribute + auto SL = NonAffixedBeginToken.getLocation(); + + // Now try to find the closing `)` of the attribute + auto MaybeRParenTok = FindRParenTokenAfter(); + if (!MaybeRParenTok.has_value()) + return Fallback; + + return SourceRange(SL, MaybeRParenTok->getLocation()); + } + + // Try affixed version. E.g.: + // + // __attribute__((__counted_by__(count))) + // ^ ^ + // | | + // ---------------- + std::string AffixedTokenStr = + (llvm::Twine("__") + CATy->getAttributeName(/*WithMacroPrefix=*/false) + + llvm::Twine("__")) + .str(); + // +1 is for `(` + // +4 is for the 4 `_` characters + const ssize_t AffixedSkipCount = + CATy->getAttributeName(/*WithMacroPrefix=*/false).size() + 1 + 4; + auto MaybeAffixedBeginToken = + FetchIdentifierTokenFromOffset(-AffixedSkipCount); + if (!MaybeAffixedBeginToken) + return Fallback; + + auto AffixedBeginToken = MaybeAffixedBeginToken.value(); + if (AffixedBeginToken.getRawIdentifier() != AffixedTokenStr) + return Fallback; + + // Found the beginning of the `__counted_by__`-like like attribute. + auto SL = AffixedBeginToken.getLocation(); + // Now try to find the closing `)` of the attribute + auto MaybeRParenTok = FindRParenTokenAfter(); + if (!MaybeRParenTok.has_value()) + return Fallback; + + return SourceRange(SL, MaybeRParenTok->getLocation()); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + static void EmitIncompleteCountedByPointeeNotes(Sema &S, const CountAttributedType *CATy, NamedDecl *IncompleteTyDecl) { @@ -251,7 +460,13 @@ static void EmitIncompleteCountedByPointeeNotes(Sema &S, // get the SourceRange from that (#113582). // // TODO: We should emit a fix-it here. - SourceRange AttrSrcRange = CATy->getCountExpr()->getSourceRange(); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + // TODO: Upstream probably won't accept `SourceRangeFor` so we should consider + // removing it and its related tests. + // SourceRange AttrSrcRange = CATy->getCountExpr()->getSourceRange(); + SourceRange AttrSrcRange = SourceRangeFor(CATy, S); + /* TO_UPSTREAM(BoundsSafety) OFF*/ S.Diag(AttrSrcRange.getBegin(), diag::note_counted_by_consider_using_sized_by) << CATy->isOrNull() << AttrSrcRange; } @@ -396,4 +611,205 @@ bool Sema::BoundsSafetyCheckUseOfCountAttrPtr(const Expr *E) { return false; } +bool Sema::BoundsSafetyCheckResolvedCall(FunctionDecl *FDecl, CallExpr *Call, + const FunctionProtoType *ProtoType) { + if (!getLangOpts().hasBoundsSafety()) + return true; + + assert(Call); + if (Call->containsErrors()) + return false; + + // Report incomplete pointee types on `__counted_by(__or_null)` pointers. + bool ChecksPassed = true; + + // Check the return of the call. The call is treated as a "use" of + // the return type. + if (!BoundsSafetyCheckUseOfCountAttrPtr(Call)) + ChecksPassed = false; + + // Check parameters + if (!FDecl && !ProtoType) + return ChecksPassed; // Can't check any further so return early + + unsigned MinNumArgs = + std::min(Call->getNumArgs(), + FDecl ? FDecl->getNumParams() : ProtoType->getNumParams()); + + for (size_t ArgIdx = 0; ArgIdx < MinNumArgs; ++ArgIdx) { + Expr *CallArg = Call->getArg(ArgIdx); // FIXME: IgnoreImpCast()? + const ValueDecl *ParamVarDecl = nullptr; + QualType ParamTy; + if (FDecl) { + // Direct call + ParamVarDecl = FDecl->getParamDecl(ArgIdx); + ParamTy = ParamVarDecl->getType(); + } else { + // Indirect call. The parameter name isn't known + ParamTy = ProtoType->getParamType(ArgIdx); + } + + // Assigning to the parameter type is treated as a "use" of the type. + if (!BoundsSafetyCheckAssignmentToCountAttrPtr( + ParamTy, CallArg, AssignmentAction::Passing, CallArg->getBeginLoc(), + ParamVarDecl, /*ShowFullQualifiedAssigneeName=*/false)) + ChecksPassed = false; + } + return ChecksPassed; +} + +static bool BoundsSafetyCheckFunctionParamOrCountAttrWithIncompletePointeeTy( + Sema &S, QualType Ty, const ParmVarDecl *ParamDecl) { + NamedDecl *IncompleteTyDecl = nullptr; + auto [CATy, PointeeTy] = + GetCountedByAttrOnIncompletePointee(Ty, &IncompleteTyDecl); + if (!CATy) + return true; + // Emit Diagnostic + StringRef ParamName; + if (ParamDecl) + ParamName = ParamDecl->getName(); + + auto SR = SourceRangeFor(CATy, S); + S.Diag(SR.getBegin(), + diag::err_bounds_safety_counted_by_on_incomplete_type_on_func_def) + << /*0*/ CATy->getAttributeName(/*WithMacroPrefix*/ true) + << /*1*/ (ParamDecl ? 1 : 0) << /*2*/ (ParamName.size() > 0) + << /*3*/ ParamName << /*4*/ Ty << /*5*/ PointeeTy << SR; + + EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl); + return false; +} + +bool Sema::BoundsSafetyCheckParamForFunctionDef(const ParmVarDecl *PVD) { + if (!getLangOpts().hasBoundsSafety()) + return true; + + return BoundsSafetyCheckFunctionParamOrCountAttrWithIncompletePointeeTy( + *this, PVD->getType(), PVD); +} + +bool Sema::BoundsSafetyCheckReturnTyForFunctionDef(FunctionDecl *FD) { + if (!getLangOpts().hasBoundsSafety()) + return true; + + return BoundsSafetyCheckFunctionParamOrCountAttrWithIncompletePointeeTy( + *this, FD->getReturnType(), nullptr); +} + +static bool +BoundsSafetyCheckVarDeclCountAttrPtrWithIncompletePointeeTy(Sema &S, + const VarDecl *VD) { + NamedDecl *IncompleteTyDecl = nullptr; + auto [CATy, PointeeTy] = + GetCountedByAttrOnIncompletePointee(VD->getType(), &IncompleteTyDecl); + if (!CATy) + return true; + + SourceRange SR = SourceRangeFor(CATy, S); + S.Diag(SR.getBegin(), + diag::err_bounds_safety_counted_by_on_incomplete_type_on_var_decl) + << /*0*/ CATy->getAttributeName(/*WithMacroPrefix=*/true) + << /*1*/ (VD->isThisDeclarationADefinition() == + VarDecl::TentativeDefinition) + << /*2*/ VD->getName() << /*3*/ VD->getType() << /*4*/ PointeeTy + << /*5*/ CATy->isOrNull() << SR; + + EmitIncompleteCountedByPointeeNotes(S, CATy, IncompleteTyDecl); + return false; +} + +bool Sema::BoundsSafetyCheckVarDecl(const VarDecl *VD, + bool CheckTentativeDefinitions) { + if (!getLangOpts().hasBoundsSafety()) + return true; + + switch (VD->isThisDeclarationADefinition()) { + case VarDecl::DeclarationOnly: + // Using `__counted_by` on a pointer type with an incomplete pointee + // isn't considered an error for declarations. + return true; + case VarDecl::TentativeDefinition: + // A tentative definition may become an actual definition but this isn't + // known until the end of the translation unit. + // See `Sema::ActOnEndOfTranslationUnit()` + if (!CheckTentativeDefinitions) + return true; + LLVM_FALLTHROUGH; + case VarDecl::Definition: + // Using `__counted_by` on a pointer type with an incomplete pointee + // is considered an error for **definitions** so carry-on with checking. + break; + } + + return BoundsSafetyCheckVarDeclCountAttrPtrWithIncompletePointeeTy(*this, VD); +} + +bool Sema::BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp( + const CountAttributedType *CATTy, Expr *Operand, + std::variant OpInfo) { + + bool IsUnaryOp = std::holds_alternative(OpInfo); + int SelectOp = 0; + unsigned DiagID = 0; + if (IsUnaryOp) { + SelectOp = std::get(OpInfo); + DiagID = diag:: + warn_bounds_safety_count_attr_pointer_unary_arithmetic_constant_count; + } else { + // Binary operator + DiagID = diag:: + warn_bounds_safety_count_attr_pointer_binary_assign_constant_count; + switch (std::get(OpInfo)) { + case BO_AddAssign: // += + SelectOp = 1; + break; + case BO_SubAssign: // -= + SelectOp = 0; + break; + default: + // We shouldn't go down this path. Other operations on a + // CountAttributedType pointers have the pointer promoted to a + // __bidi_indexable first rather than keeping the CountAttributedType + // type. + llvm_unreachable("Unexpected BinaryOperatorKind"); + return true; + } + } + + Expr::EvalResult Result; + if (CATTy->getCountExpr()->EvaluateAsInt(Result, getASTContext())) { + // Count is constant + Diag(Operand->getExprLoc(), DiagID) << + /*0*/ SelectOp << + /*1*/ CATTy->getAttributeName(/*WithMacroPrefix=*/true) << + /*2*/ (CATTy->isCountInBytes() ? 1 : 0) << + /*3*/ 0 /* integer constant count*/ << + /*4*/ Result.Val.getAsString(getASTContext(), + CATTy->getCountExpr()->getType()); + Diag(CATTy->getCountExpr()->getExprLoc(), diag::note_named_attribute) + << CATTy->getAttributeName(/*WithMacroPrefix=*/true); + return false; + } + if (const auto *DRE = + dyn_cast(CATTy->getCountExpr()->IgnoreParenCasts())) { + const auto *VD = DRE->getDecl(); + if (VD->getType().isConstQualified()) { + // Count expression refers to a single decl that is `const` qualified + // which means it is effectively constant. + + Diag(Operand->getExprLoc(), DiagID) << + /*0*/ SelectOp << + /*1*/ CATTy->getAttributeName(/*WithMacroPrefix=*/true) << + /*2*/ (CATTy->isCountInBytes() ? 1 : 0) << + /*3*/ 1 /* const qualified declref*/ << + /*4*/ VD; + Diag(CATTy->getCountExpr()->getExprLoc(), diag::note_named_attribute) + << CATTy->getAttributeName(/*WithMacroPrefix=*/true); + return false; + } + } + return true; +} + } // namespace clang diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 14e16bc39eb3a..51c87c182ace8 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -171,6 +171,201 @@ namespace { SrcExpr = src; } + /* TO_UPSTREAM(BoundsSafety) ON*/ + void checkBoundsSafetyConversion(Sema &Self) { + if (SrcExpr.isInvalid()) + return; + + QualType SrcType = SrcExpr.get()->getType(); + + auto const *DestPTy = DestType->getAs(); + auto const *SrcPTy = SrcType->getAs(); + + bool SrcIsNull = false; + bool IsDstNullTerm = false; + bool IsSrcNullTerm = false; + + if (DestPTy && Self.getLangOpts().BoundsSafety) { + // Diagnose int to ptr cast. + // Without -fbounds-safety, int to wide ptr works as unsafe hatch is + // inserted. + Expr::EvalResult R; + SrcIsNull = SrcExpr.get()->isNullPointerConstant(Self.Context, + Expr::NPC_ValueDependentIsNotNull); + if (DestPTy->isSafePointer() && !SrcPTy && + !DestPTy->getPointeeType().isVolatileQualified() && + !SrcIsNull) { + Self.Diag(OpRange.getBegin(), diag::err_bounds_safety_non_to_pointer); + SrcExpr = ExprError(); + return; + } + } + if (!DestPTy || !SrcPTy) + return; + + /// BoundsSafety: Such case should be handled as BoundsSafetyPointerCast. + /// \code + /// int *__bidi_indexable p1; + /// int *__indexable p2 = (int *__indexable)p1; + /// \endcode + if (Self.getLangOpts().BoundsSafety || + Self.isCXXSafeBuffersBoundsSafetyInteropEnabledAt( + SrcExpr.get()->getBeginLoc())) { + unsigned DiagKind = 0; + bool isInvalid = false; + // The type error may be nested, so any pointer can result in VT errors + Sema::AssignConvertType ConvertTy = + Self.CheckValueTerminatedAssignmentConstraints(DestType, + SrcExpr.get()); + switch (ConvertTy) { + default: + assert(ConvertTy == Sema::Compatible); + break; + case Sema::IncompatibleStringLiteralToValueTerminatedPointer: + DiagKind = + diag::err_bounds_safety_incompatible_string_literal_to_terminated_by; + isInvalid = true; + break; + case Sema::IncompatibleValueTerminatedTerminators: + DiagKind = diag::err_bounds_safety_incompatible_terminated_by_terminators; + isInvalid = true; + break; + case Sema::IncompatibleValueTerminatedToNonValueTerminatedPointer: { + const auto *SrcPointerType = SrcType->getAs(); + IsSrcNullTerm = + SrcPointerType->getTerminatorValue(Self.Context).isZero(); + DiagKind = diag:: + err_bounds_safety_incompatible_terminated_by_to_non_terminated_by; + isInvalid = true; + break; + } + case Sema::IncompatibleNonValueTerminatedToValueTerminatedPointer: { + const auto *DstPointerType = DestType->getAs(); + IsDstNullTerm = + DstPointerType->getTerminatorValue(Self.Context).isZero(); + if (Self.getLangOpts().BoundsSafetyRelaxedSystemHeaders || + Self.isCXXSafeBuffersBoundsSafetyInteropEnabledAt( + OpRange.getBegin())) { + DiagKind = diag:: + warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by; + isInvalid = false; + } else { + DiagKind = diag:: + err_bounds_safety_incompatible_non_terminated_by_to_terminated_by; + isInvalid = true; + } + break; + } + case Sema::IncompatibleNestedValueTerminatedToNonValueTerminatedPointer: + DiagKind = diag:: + err_bounds_safety_incompatible_terminated_by_to_non_terminated_by_mismatch; + isInvalid = true; + break; + case Sema::IncompatibleNestedNonValueTerminatedToValueTerminatedPointer: + if (Self.getLangOpts().BoundsSafetyRelaxedSystemHeaders) { + DiagKind = diag:: + warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch; + isInvalid = false; + } else { + DiagKind = diag:: + err_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch; + isInvalid = true; + } + break; + } + if (DiagKind) { + if (DiagKind == + diag:: + err_bounds_safety_incompatible_terminated_by_to_non_terminated_by) { + auto FDiag = Self.Diag(OpRange.getBegin(), DiagKind) + << SrcType << DestType << AssignmentAction::Casting + << (IsSrcNullTerm ? /*null_terminated*/ 1 + : /*terminated_by*/ 0); + } else if ( + DiagKind == + diag:: + err_bounds_safety_incompatible_non_terminated_by_to_terminated_by || + DiagKind == + diag:: + warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by) { + auto FDiag = Self.Diag(OpRange.getBegin(), DiagKind) + << SrcType << DestType << AssignmentAction::Casting + << (!Self.isCXXSafeBuffersBoundsSafetyInteropEnabledAt( + OpRange.getBegin()) + ? (IsDstNullTerm ? /*null_terminated*/ 1 + : /*terminated_by*/ 0) + : 2 /* cut message irrelevant to that mode*/); + } else { + Self.Diag(OpRange.getBegin(), DiagKind) + << SrcType << DestType << AssignmentAction::Casting; + } + + Self.TryFixAssigningNullTerminatedToBidiIndexableExpr(SrcExpr.get(), + DestType); + + Self.TryFixAssigningImplicitBidiIndexableToNullTerminatedPtr( + SrcExpr.get(), DestType); + + Self.TryFixAssigningBidiIndexableExprToNullTerminated(SrcExpr.get(), + DestType); + + if (isInvalid) { + SrcExpr = ExprError(); + return; + } + } + + if (DestPTy->isSafePointer() && !SrcPTy->isSafePointer() && + !SrcIsNull) { + if (!SrcPTy->isUnsafeIndexable()) { + // Ensure that the diagnostic says "unsafe indexable" somewhere. + SrcType = Self.Context.getBoundsSafetyPointerType( + SrcType, BoundsSafetyPointerAttributes::unsafeIndexable()); + } + Self.Diag(OpRange.getBegin(), diag::err_bounds_safety_unsafe_to_safe) + << SrcType << DestType << AssignmentAction::Casting; + SrcExpr = ExprError(); + return; + } + + if (!SrcIsNull && DestPTy->isPointerTypeWithBounds() && + SrcPTy->isSingle() && !SrcType->isBoundsAttributedType()) { + if (SrcPTy->getPointeeType()->isIncompleteOrSizelessType()) { + Self.Diag(OpRange.getBegin(), + diag::err_bounds_safety_incomplete_single_to_indexable) + << SrcType << DestType << AssignmentAction::Casting; + } else { + Self.DiagnoseSingleToWideLosingBounds(DestType, SrcType, SrcExpr.get()); + } + } + } + + ResultType = Self.deduceCastPointerAttributes(ResultType, SrcType); + // Retake the pointer type since DestType has been updated. + const auto *ResultPTy = ResultType->getAs(); + assert(ResultPTy); + // Check if we need BoundsSafetyPointerCast by checking the outermost pointer + // attributes. + if (ResultPTy->getPointerAttributes() != SrcPTy->getPointerAttributes()) { + /// We also want to insert BoundsSafetyPointerCast in the cases like below: + /// \code + /// int *__indexable pi; + /// (char *__bidi_indexable) pi; + /// \endcode + bool SkipNoOp = Kind == CK_NoOp && + Self.Context.hasSameType(ResultPTy->getPointeeType(), + SrcPTy->getPointeeType()); + if (!SkipNoOp) { + QualType Ty = Self.Context.getPointerType( + ResultPTy->getPointeeType(), SrcPTy->getPointerAttributes()); + SrcExpr = Self.ImpCastExprToType(SrcExpr.get(), Ty, Kind, + SrcExpr.get()->getValueKind()); + } + Kind = CK_BoundsSafetyPointerCast; + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + void checkQualifiedDestType() { // Destination type may not be qualified with __ptrauth. if (DestType.getPointerAuth()) { @@ -216,6 +411,75 @@ namespace { }; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +QualType +Sema::deduceCastPointerAttributes(QualType ResultType, QualType SrcType) { + if (Context.hasSameBoundsSafetyPointerLayout(ResultType, SrcType)) + return ResultType; + + // We apply the following type attribute inheritance rules. + // If a pointer attribute for the cast type is unspecified, the cast + // type automatically inherits the source pointer attribute. For example, + // in the cases like below, '(int*)val' becomes '(int *__bidi_indexable)val' + // because val is 'int *__bidi_indexable'. + // int *val; // auto bound + // int *val2 = (int*)val; + // If the source is not a pointer, the cast type inherits the default ABI + // visible pointer attribute. + std::function + applyAttrToDestTy = [&](QualType DstTy, QualType SrcTy, + QualType MergePointeeTy, + QualType OrigDstTy) -> QualType { + const auto *DPTy = DstTy->getAs(); + if (DPTy->isUnspecified() || MergePointeeTy != DstTy->getPointeeType()) { + const auto *DVTT = DstTy->getAs(); + + const PointerType *SPTy = nullptr; + const ValueTerminatedType *SVTT = nullptr; + if (!SrcTy.isNull()) { + SPTy = SrcTy->getAs(); + SVTT = SrcTy->getAs(); + } + + if (DVTT) { + // DstTy is __terminated_by(), update only the pointee. + QualType Ty = Context.getPointerType( + MergePointeeTy, BoundsSafetyPointerAttributes::single()); + return Context.getValueTerminatedType(Ty, DVTT->getTerminatorExpr()); + } + + if (DPTy->isUnspecified() && SVTT && + Context.hasSameUnqualifiedType(SPTy->getPointeeType(), + MergePointeeTy)) { + // SrcTy is __terminated_by(), update the pointee and inherit the + // __terminated_by(). + assert(!DVTT && SPTy->isSingle()); + QualType Ty = Context.getPointerType( + MergePointeeTy, BoundsSafetyPointerAttributes::single()); + return Context.getValueTerminatedType(Ty, SVTT->getTerminatorExpr()); + } + + // Update the pointee and get a new attribute if unspecified. + BoundsSafetyPointerAttributes DstAttr; + if (!DPTy->isUnspecified()) { + DstAttr = DPTy->getPointerAttributes(); + } else if (SPTy) { + DstAttr = SPTy->getPointerAttributes(); + } else { + DstAttr = CurPointerAbi; + } + return Context.getPointerType(MergePointeeTy, DstAttr); + } + + return DstTy; + }; + + // Update Op.ResultType as it is used to create the cast expression. + return Context.mergeBoundsSafetyPointerTypes( + ResultType, SrcType, applyAttrToDestTy); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + static void DiagnoseCastQual(Sema &Self, const ExprResult &SrcExpr, QualType DestType); @@ -3234,6 +3498,11 @@ void CastOperation::CheckCStyleCast() { if ((Self.Context.getTypeSize(SrcType) > Self.Context.getTypeSize(DestType)) && + /* TO_UPSTREAM(BoundsSafety) ON*/ + (!SrcType->isPointerTypeWithBounds() || + Self.Context.getTypeSize(Self.Context.getIntPtrType()) > + Self.Context.getTypeSize(DestType)) && + /* TO_UPSTREAM(BoundsSafety) OFF*/ !DestType->isBooleanType()) { // C 6.3.2.3p6: Any pointer type may be converted to an integer type. // Except as previously specified, the result is implementation-defined. @@ -3419,6 +3688,10 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc, Op.CheckCStyleCast(); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + Op.checkBoundsSafetyConversion(*this); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (Op.SrcExpr.isInvalid()) return ExprError(); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index d2cad8a6fd608..798e05ec0da42 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1503,7 +1503,11 @@ bool Sema::checkPointerAuthEnabled(SourceLocation Loc, SourceRange Range) { if (getLangOpts().PointerAuthIntrinsics) return false; - Diag(Loc, diag::err_ptrauth_disabled) << Range; + if (!getLangOpts().SoftPointerAuth && + !Context.getTargetInfo().isPointerAuthSupported()) + Diag(Loc, diag::err_ptrauth_disabled_target) << Range; + else + Diag(Loc, diag::err_ptrauth_disabled) << Range; return true; } @@ -1542,7 +1546,7 @@ bool Sema::checkConstantPointerAuthKey(Expr *Arg, unsigned &Result) { } Diag(Arg->getExprLoc(), diag::err_ptrauth_invalid_key) - << Value << Arg->getSourceRange(); + << Value << Arg->getSourceRange(); return true; } @@ -2665,6 +2669,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, return PointerAuthSignGenericData(*this, TheCall); case Builtin::BI__builtin_ptrauth_auth_and_resign: return PointerAuthAuthAndResign(*this, TheCall); + case Builtin::BI__builtin_ptrauth_string_discriminator: return PointerAuthStringDiscriminator(*this, TheCall); // OpenCL v2.0, s6.13.16 - Pipe functions @@ -4076,6 +4081,43 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange, return ExprError(); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety) { + const auto *PtrTy = ValType->getAs(); + bool IsDBP = ValType->isBoundsAttributedType(); + if (PtrTy || IsDBP) { + // Check if an AddSub op uses a pointer to __unsafe_indexable pointer. + if (Form == Arithmetic && (IsDBP || PtrTy->isSafePointer())) { + // Non-AddSub ops require a pointer to an integer, they should have been + // handled. + Diag(ExprRange.getBegin(), + diag::err_bounds_safety_atomic_op_arithmetic_needs_unsafe_pointer) + << Ptr->getType() << Ptr->getSourceRange(); + return ExprError(); + } + // Non-arithmetic ops require a pointer to __unsafe_indexable or __single + // pointer. + if (IsDBP || (PtrTy->isSafePointer() && !PtrTy->isSingle())) { + unsigned DiagIndex; + if (PtrTy->isIndexable()) + DiagIndex = 0; + else if (PtrTy->isBidiIndexable()) + DiagIndex = 1; + else if (const auto *DCPTy = ValType->getAs()) + DiagIndex = DCPTy->getKind() + 2; + else if (ValType->isDynamicRangePointerType()) + DiagIndex = 6; + else + llvm_unreachable("Unknown pointer"); + Diag(ExprRange.getBegin(), + diag::err_bounds_safety_atomic_op_unsupported_attribute) + << DiagIndex << Ptr->getSourceRange(); + return ExprError(); + } + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // All atomic operations have an overload which takes a pointer to a volatile // 'A'. We shouldn't let the volatile-ness of the pointee-type inject itself // into the result or the other operands. Similarly atomic_load takes a @@ -6504,6 +6546,9 @@ bool Sema::CheckFormatArguments(ArrayRef Args, } const Expr *OrigFormatExpr = Args[format_idx]->IgnoreParenCasts(); + if (auto Forge = dyn_cast(OrigFormatExpr)) { + OrigFormatExpr = Forge->getAddr()->IgnoreParenCasts(); + } // CHECK: format string is not a string literal. // @@ -10135,6 +10180,30 @@ Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType, // here prevent the user from using a PPC MMA type as trailing return type. if (Context.getTargetInfo().getTriple().isPPC64()) PPC().CheckPPCMMAType(RetValExp->getType(), ReturnLoc); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + BoundsSafetyCheckAssignmentToCountAttrPtr( + lhsType, RetValExp, AssignmentAction::Returning, RetValExp->getBeginLoc(), + /*Assignee=*/nullptr, /*ShowFullQualifiedAssigneeName=*/false); + + // For a count-attributed return type, its dependent count variables can be + // assigned in arbitrary places. Don't try to find the assigned values, just + // assume we don't know them and pass an empty map. + // Pass an empty designator, since it won't be used in diagnostics for + // returning action. + // + // TODO: This diagnostic check should always be performed (rdar://138982703). + // The check is currently guarded because if it's always on several projects + // fail to build (rdar://138798562&138804573&138808353&138855224). + if (getLangOpts().BoundsSafety && + getLangOpts().hasNewBoundsSafetyCheck(LangOptions::BS_CHK_ReturnSize)) { + DependentValuesMap DependentValues; + CheckDynamicCountSizeForAssignment( + lhsType, RetValExp->IgnoreParenImpCasts(), AssignmentAction::Returning, + RetValExp->getBeginLoc(), + /*Designator=*/"", DependentValues); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ } void Sema::CheckFloatComparison(SourceLocation Loc, Expr *LHS, Expr *RHS, @@ -12379,7 +12448,8 @@ static void AnalyzeImplicitConversions( SourceLocation CC = Item.CC; QualType T = OrigE->getType(); - Expr *E = OrigE->IgnoreParenImpCasts(); + llvm::SmallPtrSet BoundValues; + Expr *E = OrigE->IgnoreParenImpCasts(BoundValues); // Propagate whether we are in a C++ list initialization expression. // If so, we do not issue warnings for implicit int-float conversion @@ -12517,6 +12587,9 @@ static void AnalyzeImplicitConversions( Expr *ChildExpr = dyn_cast_or_null(SubStmt); if (!ChildExpr) continue; + if (auto *OVE = dyn_cast(ChildExpr)) + if (BoundValues.count(OVE)) + ChildExpr = OVE->getSourceExpr(); if (auto *CSE = dyn_cast(E)) if (ChildExpr == CSE->getOperand()) @@ -13884,6 +13957,13 @@ bool Sema::CheckParmsForFunctionDef(ArrayRef Parameters, HasInvalidParm = true; Diag(Param->getLocation(), diag::err_wasm_table_as_function_parameter); } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (!Param->isInvalidDecl() && + !BoundsSafetyCheckParamForFunctionDef(Param)) { + Param->setInvalidDecl(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ } return HasInvalidParm; @@ -15224,6 +15304,13 @@ void Sema::DiagnoseMisalignedMembers() { void Sema::DiscardMisalignedMemberAddress(const Type *T, Expr *E) { E = E->IgnoreParens(); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (E->getType()->isSafePointerType()) { + E = E->IgnoreParenImpCasts(); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (!T->isPointerType() && !T->isIntegerType() && !T->isDependentType()) return; if (isa(E) && diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index ed10730ef986b..388ac11776d95 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -718,50 +718,6 @@ ResultBuilder::ShadowMapEntry::end() const { return iterator(cast(DeclOrVector)->end()); } -/// Compute the qualification required to get from the current context -/// (\p CurContext) to the target context (\p TargetContext). -/// -/// \param Context the AST context in which the qualification will be used. -/// -/// \param CurContext the context where an entity is being named, which is -/// typically based on the current scope. -/// -/// \param TargetContext the context in which the named entity actually -/// resides. -/// -/// \returns a nested name specifier that refers into the target context, or -/// NULL if no qualification is needed. -static NestedNameSpecifier * -getRequiredQualification(ASTContext &Context, const DeclContext *CurContext, - const DeclContext *TargetContext) { - SmallVector TargetParents; - - for (const DeclContext *CommonAncestor = TargetContext; - CommonAncestor && !CommonAncestor->Encloses(CurContext); - CommonAncestor = CommonAncestor->getLookupParent()) { - if (CommonAncestor->isTransparentContext() || - CommonAncestor->isFunctionOrMethod()) - continue; - - TargetParents.push_back(CommonAncestor); - } - - NestedNameSpecifier *Result = nullptr; - while (!TargetParents.empty()) { - const DeclContext *Parent = TargetParents.pop_back_val(); - - if (const auto *Namespace = dyn_cast(Parent)) { - if (!Namespace->getIdentifier()) - continue; - - Result = NestedNameSpecifier::Create(Context, Result, Namespace); - } else if (const auto *TD = dyn_cast(Parent)) - Result = NestedNameSpecifier::Create( - Context, Result, Context.getTypeDeclType(TD).getTypePtr()); - } - return Result; -} - // Some declarations have reserved names that we don't want to ever show. // Filter out names reserved for the implementation if they come from a // system header. @@ -860,8 +816,8 @@ bool ResultBuilder::CheckHiddenResult(Result &R, DeclContext *CurContext, R.QualifierIsInformative = false; if (!R.Qualifier) - R.Qualifier = getRequiredQualification(SemaRef.Context, CurContext, - R.Declaration->getDeclContext()); + R.Qualifier = NestedNameSpecifier::getRequiredQualification( + SemaRef.Context, CurContext, R.Declaration->getDeclContext()); return false; } @@ -4464,7 +4420,7 @@ static void MaybeAddOverrideCalls(Sema &S, DeclContext *InContext, // If we need a nested-name-specifier, add one now. if (!InContext) { - NestedNameSpecifier *NNS = getRequiredQualification( + NestedNameSpecifier *NNS = NestedNameSpecifier::getRequiredQualification( S.Context, CurContext, Overridden->getDeclContext()); if (NNS) { std::string Str; @@ -4891,7 +4847,8 @@ static void AddEnumerators(ResultBuilder &Results, ASTContext &Context, // If there are no prior enumerators in C++, check whether we have to // qualify the names of the enumerators that we suggest, because they // may not be visible in this scope. - Qualifier = getRequiredQualification(Context, CurContext, Enum); + Qualifier = NestedNameSpecifier::getRequiredQualification(Context, + CurContext, Enum); } Results.EnterNewScope(); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index dfc718eedc1d9..f3916174e73dc 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +#include "DynamicCountPointerAssignmentAnalysis.h" +#include "TreeTransform.h" #include "TypeLocBuilder.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -2329,13 +2331,67 @@ FunctionDecl *Sema::CreateBuiltin(IdentifierInfo *II, QualType Type, // FunctionDecl. if (const FunctionProtoType *FT = dyn_cast(Type)) { SmallVector Params; + // TO_UPSTREAM(BoundsSafety) + SmallVector NewParamTypes; for (unsigned i = 0, e = FT->getNumParams(); i != e; ++i) { ParmVarDecl *parm = ParmVarDecl::Create( Context, New, SourceLocation(), SourceLocation(), nullptr, FT->getParamType(i), /*TInfo=*/nullptr, SC_None, nullptr); parm->setScopeInfo(0, i); Params.push_back(parm); + // TO_UPSTREAM(BoundsSafety) + NewParamTypes.push_back(FT->getParamType(i)); + } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + // We add size annotations for builtin memory manipulation functions + // such that there is a semantic and runtime checks for pointer arguments + // with sizes. + unsigned MemFnKind = New->getMemoryFunctionKind(); + bool NeedReturnSize = MemFnKind == Builtin::BImemcpy || + MemFnKind == Builtin::BImemset || + MemFnKind == Builtin::BImemmove; + bool NeedDestSize = MemFnKind == Builtin::BImemcpy || + MemFnKind == Builtin::BImempcpy || + MemFnKind == Builtin::BImemset || + MemFnKind == Builtin::BImemmove; + bool NeedSrcSize = MemFnKind == Builtin::BImemcpy || + MemFnKind == Builtin::BImempcpy || + MemFnKind == Builtin::BImemmove; + + auto BuildNewDynamicSizeType = [&](QualType Ty) { + auto NewSizeRef = DeclRefExpr::Create(Context, Params[2]->getQualifierLoc(), + SourceLocation(), Params[2], true, SourceLocation(), Params[2]->getType(), VK_LValue, Params[2]); + return BuildCountAttributedType(Ty, NewSizeRef, /*CountInBytes*/true); + }; + if (NeedReturnSize || NeedDestSize || NeedSrcSize) { + assert(FT->getNumParams() >= 3); + if (NeedDestSize) { + QualType NewParmTy = BuildNewDynamicSizeType(Params[0]->getType()); + Params[0]->setType(NewParmTy); + NewParamTypes[0] = Params[0]->getType(); + const auto *CATy = NewParmTy->getAs(); + AttachDependerDeclsAttr(Params[0], CATy, /*Level*/ 0); + } + if (NeedSrcSize) { + QualType NewParmTy = BuildNewDynamicSizeType(Params[1]->getType()); + Params[1]->setType(NewParmTy); + NewParamTypes[1] = Params[1]->getType(); + const auto *CATy = NewParmTy->getAs(); + AttachDependerDeclsAttr(Params[1], CATy, /*Level*/ 0); + } + QualType NewReturnType = FT->getReturnType(); + if (NeedReturnSize) { + NewReturnType = BuildNewDynamicSizeType(NewReturnType); + } + QualType NewFuncType = Context.getFunctionType(NewReturnType, + NewParamTypes, FT->getExtProtoInfo()); + New->setType(NewFuncType); + } } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + New->setParams(Params); } @@ -2869,6 +2925,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, AMK == Sema::AMK_ProtocolImplementation || AMK == Sema::AMK_OptionalProtocolImplementation)) NewAttr = nullptr; + else if (isa(Attr) && AMK == Sema::AMK_Override) + NewAttr = nullptr; else if (const auto *UA = dyn_cast(Attr)) NewAttr = S.mergeUuidAttr(D, *UA, UA->getGuid(), UA->getGuidDecl()); else if (const auto *IMA = dyn_cast(Attr)) @@ -3232,6 +3290,9 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, Diag(Old->getLocation(), diag::note_previous_declaration); } + if (auto *ND = dyn_cast(Old)) + copyFeatureAvailabilityCheck(New, ND, true); + if (!Old->hasAttrs()) return; @@ -3590,6 +3651,618 @@ static void adjustDeclContextForDeclaratorDecl(DeclaratorDecl *NewD, FixSemaDC(VD->getDescribedVarTemplate()); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +struct TransposeDynamicBoundsExpr + : public TreeTransform { + using BaseClass = TreeTransform; + + FunctionDecl *NewFD; + bool Success = true; + + static bool Rewrite(Sema &SemaRef, FunctionDecl *NewFD, Expr *E, + std::string &Result) { + bool Invalid; + auto TokRange = CharSourceRange::getTokenRange(E->getSourceRange()); + Result = Lexer::getSourceText(TokRange, SemaRef.SourceMgr, + SemaRef.getLangOpts(), &Invalid); + if (Invalid) + return false; + + TransposeDynamicBoundsExpr Rewriter(SemaRef, NewFD); + Rewriter.TransformExpr(E); + return Rewriter.Success; + } + + TransposeDynamicBoundsExpr(Sema &SemaRef, FunctionDecl *NewFD) + : BaseClass(SemaRef), NewFD(NewFD) {} + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + assert(isa(E->getDecl())); + auto *OldParm = cast(E->getDecl()); + auto OldName = OldParm->getDeclName(); + auto *NewParm = NewFD->getParamDecl(OldParm->getFunctionScopeIndex()); + auto NewName = NewParm->getDeclName(); + // Parameter names have to match because we don't know if the only + // difference between the two prototypes are the pointer attributes. A + // developer can make other changes at the same time, like swapping + // arguments. It's also possible/common that the prototype in the header + // just doesn't have parameter names at all, in which case it's reckless to + // make them up–the header could be included by files that use macros with + // the same name as the parameter names we would like to use. + Success &= OldName == NewName; + return E; + } +}; + +static TypeLoc getPointeeTypeLoc(TypeLoc TL) { + if (TL) { + TL = TL.getAsAdjusted(); + + if (auto DCPTL = TL.getAs()) + return getPointeeTypeLoc(DCPTL.getInnerLoc()); + if (auto DRPTL = TL.getAs()) + return getPointeeTypeLoc(DRPTL.getInnerLoc()); + if (auto PTL = TL.getAs()) + return PTL.getPointeeLoc(); + } + return TypeLoc(); +} + +/// fixBoundsSafetyTypeLocs - emit fixits on diagnostic D to adjust type B/QB to +/// match the bounds indicated on type A/QA. A/QA is not reciprocally adjusted +/// if B/QB has qualifiers; this function needs to be called once for each +/// direction. +static void fixBoundsSafetyTypeLocs(Sema &S, Sema::SemaDiagnosticBuilder &D, + TypeLoc A, TypeLoc B, QualType QA, QualType QB, + FunctionDecl *ADecl, FunctionDecl *BDecl) { + if (ADecl->getNumParams() != BDecl->getNumParams()) + return; + + auto *PA = QA->getAs(); + auto *PB = QB->getAs(); + if (!PA || !PB) + return; + + // check inner pointer dimensions + TypeLoc APointee = getPointeeTypeLoc(A); + TypeLoc BPointee = getPointeeTypeLoc(B); + if (APointee && BPointee) { + fixBoundsSafetyTypeLocs(S, D, APointee, BPointee, PA->getPointeeType(), + PB->getPointeeType(), ADecl, BDecl); + } + + // And then try to adjust this pointer. No adjustments to make if B already + // has attributes. + // We do not touch the pointer if is __single or __unsafe_indexable, because + // PointerTypeLoc cannot tell us whether this was added implicitly or + // explicitly. when it is added explicitly, the fixit could suggest incorrect + // code, like `T *__bidi_indexable __single`. It's also technically a + // possibility for __indexable and __bidi_indexable, but using those as the + // default pointer attribute is not currently a common use case (although we + // think this could change). + if (QB->getAs() || + QB->getAs() || + !PB->getPointerAttributes().hasRawPointerLayout()) + return; + + auto GetEndPointer = [](QualType QT) -> Expr * { + if (auto DRPT = QT->getAs()) + return DRPT->getEndPointer(); + return nullptr; + }; + + std::string BoundsAnnotation; + llvm::raw_string_ostream KS(BoundsAnnotation); + if (PA->getPointerAttributes().isIndexable()) { + KS << "__indexable"; + } else if (PA->getPointerAttributes().isBidiIndexable()) { + KS << "__bidi_indexable"; + } else if (auto DCPTA = QA->getAs()) { + std::string Rewritten; + if (!TransposeDynamicBoundsExpr::Rewrite(S, BDecl, DCPTA->getCountExpr(), + Rewritten)) + return; + const char *Keyword = + DCPTA->isCountInBytes() ? "__sized_by" : "__counted_by"; + KS << Keyword; + if (DCPTA->isOrNull()) + KS << "_or_null"; + KS << '(' << Rewritten << ')'; + } else if (auto EndPointer = GetEndPointer(QA)) { + std::string Rewritten; + if (!TransposeDynamicBoundsExpr::Rewrite(S, BDecl, EndPointer, Rewritten)) + return; + KS << "__ended_by(" << Rewritten << ')'; + } else { + return; + } + + SourceLocation FixItLoc; + bool AddSpace = false; + std::tie(FixItLoc, AddSpace) = + BoundsSafetyFixItUtils::FindPointerAttrInsertPoint(B, S); + if (FixItLoc.isInvalid()) + return; + + if (AddSpace) + KS << " "; + + KS.flush(); + D << FixItHint::CreateInsertion(FixItLoc, BoundsAnnotation); +} + +static void fixBoundsSafetyFunctionDecl(Sema &S, Sema::SemaDiagnosticBuilder &D, + FunctionDecl *FromDecl, + FunctionDecl *ToDecl) { + if (FromDecl->getNumParams() != ToDecl->getNumParams()) + return; + + auto FromTL = FromDecl->getFunctionTypeLoc(); + auto ToTL = ToDecl->getFunctionTypeLoc(); + if (!FromTL || !ToTL) + return; + + fixBoundsSafetyTypeLocs(S, D, ToTL.getReturnLoc(), FromTL.getReturnLoc(), + ToDecl->getReturnType(), FromDecl->getReturnType(), + ToDecl, FromDecl); + for (unsigned I = 0; I < FromTL.getNumParams(); ++I) { + auto FromParam = FromTL.getParam(I); + auto ToParam = ToTL.getParam(I); + auto FromParamTL = FromParam->getTypeSourceInfo()->getTypeLoc(); + auto ToParamTL = ToParam->getTypeSourceInfo()->getTypeLoc(); + fixBoundsSafetyTypeLocs(S, D, ToParamTL, FromParamTL, ToParam->getType(), + FromParam->getType(), ToDecl, FromDecl); + } +} + +/// diagnoseFunctionConflictWithDynamicBoundTypes - diagnose if \p New +/// and \p Old have conflicting return or parameter types with repect to +/// '__counted_by', '__sized_by', or '__ended_by' attributes. +static bool diagnoseFunctionConflictWithDynamicBoundTypes(FunctionDecl *New, + FunctionDecl *Old, + Sema &Self) { + std::function checkCompatibleBoundExprs; + checkCompatibleBoundExprs = [&](const Expr *NewExpr, const Expr *OldExpr) -> bool { + llvm::FoldingSetNodeID NewID; + llvm::FoldingSetNodeID OldID; + if (NewExpr) + NewExpr->Profile(NewID, Self.Context, /*Canonical*/ true); + if (OldExpr) + OldExpr->Profile(OldID, Self.Context, /*Canonical*/ true); + if (NewID == OldID) + return true; + return false; + }; + std::unique_ptr D; + std::function + checkCompatibleDynamicBoundTypes = + [&](QualType NewTy, QualType OldTy, TypeLoc NewTL, TypeLoc OldTL, + SourceLocation NewLoc, SourceLocation OldLoc) -> bool { + if (!NewTy->isPointerType() || !OldTy->isPointerType()) + return true; + const auto *NewDCPTy = NewTy->getAs(); + const auto *OldDCPTy = OldTy->getAs(); + const auto *NewDRPTy = NewTy->getAs(); + const auto *OldDRPTy = OldTy->getAs(); + const auto *NewVTTy = NewTy->getAs(); + const auto *OldVTTy = OldTy->getAs(); + const unsigned CountDiagIndex = 0; + const unsigned SizeDiagIndex = 1; + const unsigned CountOrNullDiagIndex = 2; + const unsigned SizeOrNullDiagIndex = 3; + const unsigned RangeDiagIndex = 4; + const unsigned TermDiagIndex = 5; + + auto ReportConflict = [&](const unsigned Index) { + { + if (!D) + D.reset(new Sema::SemaDiagnosticBuilder( + Self.Diag(NewLoc, diag::err_bounds_safety_dynamic_bound_redeclaration) + << Index << 0)); + fixBoundsSafetyTypeLocs(Self, *D, NewTL, OldTL, NewTy, OldTy, New, Old); + fixBoundsSafetyTypeLocs(Self, *D, OldTL, NewTL, OldTy, NewTy, Old, New); + } + }; + auto ReportCountConflict = [&](const CountAttributedType *DCPTy) { + unsigned Index; + switch (DCPTy->getKind()) { + case CountAttributedType::CountedBy: + Index = CountDiagIndex; + break; + case CountAttributedType::CountedByOrNull: + Index = CountOrNullDiagIndex; + break; + case CountAttributedType::SizedBy: + Index = SizeDiagIndex; + break; + case CountAttributedType::SizedByOrNull: + Index = SizeOrNullDiagIndex; + break; + default: + llvm_unreachable("Unexpected BoundsAttrKind"); + } + ReportConflict(Index); + }; + + if (NewDCPTy && !OldDCPTy) { + ReportCountConflict(NewDCPTy); + return false; + } + + if (NewDRPTy && !OldDRPTy) { + ReportConflict(RangeDiagIndex); + return false; + } + + if (NewVTTy && !OldVTTy && + !OldTy->isUnspecifiedPointerType()) { + ReportConflict(TermDiagIndex); + return false; + } + + if (!NewDCPTy && OldDCPTy) { + ReportCountConflict(OldDCPTy); + return false; + } + // Implicit __started_by attributes have not yet been added to NewTy, so + // don't diagnose that. + if (!NewDRPTy && OldDRPTy && OldDRPTy->getEndPointer()) { + ReportConflict(RangeDiagIndex); + return false; + } + + if (!NewVTTy && OldVTTy) { + ReportConflict(TermDiagIndex); + return false; + } + + if (NewDCPTy && OldDCPTy) { + if (!checkCompatibleBoundExprs(NewDCPTy->getCountExpr(), OldDCPTy->getCountExpr())) { + ReportCountConflict(NewDCPTy); + return false; + } + // '__sized_by' and '__counted_by' + if (NewDCPTy->isCountInBytes() != OldDCPTy->isCountInBytes()) { + CharUnits NewPointeeSize = Self.Context.getTypeSizeInChars(NewDCPTy->getPointeeType()); + if (!NewPointeeSize.isOne() && !NewDCPTy->isVoidPointerType()) { + ReportCountConflict(NewDCPTy); + return false; + } + } + // '__counted_by_or_null' and '__counted_by' + if (NewDCPTy->isOrNull() != OldDCPTy->isOrNull()) { + ReportCountConflict(NewDCPTy); + return false; + } + } + if (NewDRPTy && OldDRPTy) { + // Start pointers are implicit so we don't check it here. + if (!checkCompatibleBoundExprs(NewDRPTy->getEndPointer(), OldDRPTy->getEndPointer())) { + ReportConflict(RangeDiagIndex); + return false; + } + } + if (NewVTTy && OldVTTy && + !llvm::APSInt::isSameValue(NewVTTy->getTerminatorValue(Self.Context), + OldVTTy->getTerminatorValue(Self.Context))) { + ReportConflict(TermDiagIndex); + return false; + } + TypeLoc NewSubTL, OldSubTL; + if (NewTL && OldTL) { + auto NewPTL = NewTL.getAs(); + auto OldPTL = OldTL.getAs(); + if (NewPTL && OldPTL) { + NewSubTL = NewPTL.getPointeeLoc(); + OldSubTL = OldPTL.getPointeeLoc(); + } + } + return checkCompatibleDynamicBoundTypes(NewTy->getPointeeType(), + OldTy->getPointeeType(), NewSubTL, + OldSubTL, NewLoc, OldLoc); + }; + + TypeLoc OldReturnTL, NewReturnTL; + // OldTL or NewTL will be missing if the function was declared without using + // function syntax, for instance with `typeof(snprintf) mysnprintf;`. + if (auto OldTL = Old->getFunctionTypeLoc()) + OldReturnTL = OldTL.getReturnLoc(); + if (auto NewTL = New->getFunctionTypeLoc()) + NewReturnTL = NewTL.getReturnLoc(); + bool Compatible = checkCompatibleDynamicBoundTypes( + New->getReturnType(), Old->getReturnType(), NewReturnTL, OldReturnTL, + New->getBeginLoc(), Old->getBeginLoc()); + + auto MinNumParams = std::min(New->getNumParams(), Old->getNumParams()); + for (unsigned i = 0; i < MinNumParams; ++i) { + auto *OldParam = Old->getParamDecl(i); + auto *NewParam = New->getParamDecl(i); + Compatible &= checkCompatibleDynamicBoundTypes( + NewParam->getType(), OldParam->getType(), + NewParam->getTypeSourceInfo()->getTypeLoc(), + OldParam->getTypeSourceInfo()->getTypeLoc(), + NewParam->getBeginLoc(), OldParam->getBeginLoc()); + } + + if (D) { + D.reset(); + Self.Diag(Old->getBeginLoc(), diag::note_previous_declaration); + } + + // New->getNumParams() != Old->getNumParams() should be handled in the other logic. + return Compatible; +} + +struct RebuildDynamicBoundsExpr + : public TreeTransform { + using BaseClass = TreeTransform; + FunctionDecl *New; + RebuildDynamicBoundsExpr(Sema &SemaRef, FunctionDecl *New) + : BaseClass(SemaRef), New(New) {} + bool AlwaysRebuild() const { return true; } + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + assert(isa(E->getDecl())); + auto *OldParm = cast(E->getDecl()); + auto *NewParm = New->getParamDecl(OldParm->getFunctionScopeIndex()); + DeclarationNameInfo NewNameInfo; + NewNameInfo.setName(NewParm->getDeclName()); + assert(!E->hasExplicitTemplateArgs()); + + // Temporarily put parameter variables in the translation unit, not the + // enclosing context. This is what Sema::ActOnParamDeclarator() already + // does when declaring a function. In BoundsSafety, we do it to allow + // creating references to the parameters when rebuilding the count + // expression. Otherwise, creating such references triggers an error. + // TODO: Check how this works for C++ methods and such. + DeclContext *DC = NewParm->getDeclContext(); + if (isa(DC)) + NewParm->setDeclContext(SemaRef.Context.getTranslationUnitDecl()); + ExprResult Res = RebuildDeclRefExpr(NewParm->getQualifierLoc(), NewParm, + NewNameInfo, NewParm, nullptr); + NewParm->setDeclContext(DC); + + return Res; + } +}; + +/// mergeFunctionDeclBoundsAttributes - merges +/// '__counted_by/__sized_by/__ended_by' attributes in \p Old to \p New. Returns +/// true if \p New has been updated. This doesn't diagnose conflicting +/// attributes but it merges those attributes on a best efforts basis. If the +/// merge was unsuccessful, the function declarations will remain incompatible +/// so they will be caught by following diagnostics in Sema::MergeFunctionDecl. +static bool mergeFunctionDeclBoundsAttributes(FunctionDecl *New, + FunctionDecl *Old, Sema &Self) { + if (New->getNumParams() != Old->getNumParams()) + return false; + + RebuildDynamicBoundsExpr ExprBuilder(Self, New); + std::function + mergeBoundsAttributes; + mergeBoundsAttributes = [&](QualType NewTy, QualType OldTy, + QualType MergePointeeTy, + QualType OrigNewTy) -> QualType { + if (OldTy.isNull()) + return QualType(); + if (MergePointeeTy.isNull()) + return QualType(); + if (NewTy->isBoundsAttributedType() || NewTy->isValueTerminatedType()) + return QualType(); + + // mergeBoundsSafetyPointerTypes removes any AttributedTypes from NewTy, + // calls mergeBoundsAttributes to merge the attributes at each level, and + // then reapplies the AttributedTypes to the merged type. OrigNewTy is the + // same as NewTy but without dropping the AttributedTypes. This allows us + // to check if the pointer has any attributes at all (such as __single + // applied using AttributedType). + if (Self.getLangOpts().isBoundsSafetyAttributeOnlyMode() && + !OrigNewTy->isUnspecifiedPointerType()) + return QualType(); + + BoundsSafetyPointerAttributes Attributes; + if (auto *NewPTy = NewTy->getAs()) + Attributes = NewPTy->getPointerAttributes(); + if (Attributes.isUnspecified()) + if (auto *OldPTy = NewTy->getAs()) + Attributes = OldPTy->getPointerAttributes(); + + QualType MergeTy = Self.Context.getPointerType(MergePointeeTy, Attributes); + if (auto *OldDCPTy = OldTy->getAs()) { + ExprResult NewCount = ExprBuilder.TransformExpr(OldDCPTy->getCountExpr()); + if (NewCount.isInvalid()) + return QualType(); + return Self.BuildCountAttributedType(MergeTy, NewCount.get(), + OldDCPTy->isCountInBytes(), + OldDCPTy->isOrNull()); + } + if (auto *OldDRPTy = OldTy->getAs()) { + Expr *StartPtr = OldDRPTy->getStartPointer(); + Expr *EndPtr = OldDRPTy->getEndPointer(); + if (StartPtr) { + ExprResult NewStart = ExprBuilder.TransformExpr(StartPtr); + if (NewStart.isInvalid()) + return QualType(); + StartPtr = NewStart.get(); + } + if (EndPtr) { + ExprResult NewEnd = + ExprBuilder.TransformExpr(OldDRPTy->getEndPointer()); + if (NewEnd.isInvalid()) + return QualType(); + EndPtr = NewEnd.get(); + } + return Self.BuildDynamicRangePointerType(MergeTy, StartPtr, EndPtr); + } + return MergeTy; + }; + QualType NewRetTy = New->getReturnType(); + if (NewRetTy->isBoundsAttributedType() || + NewRetTy->isValueTerminatedType()) + return false; + QualType MergeRetTy = Self.Context.mergeBoundsSafetyPointerTypes( + New->getReturnType(), Old->getReturnType(), mergeBoundsAttributes); + if (MergeRetTy.isNull()) + return false; + + llvm::SmallVector MergeParamTys; + for (unsigned i = 0; i < New->getNumParams(); ++i) { + QualType NewParmTy = New->getParamDecl(i)->getType(); + if (NewParmTy->isBoundsAttributedType() || + NewParmTy->isValueTerminatedType()) + return false; + QualType MergeParamTy = Self.Context.mergeBoundsSafetyPointerTypes( + NewParmTy, Old->getParamDecl(i)->getType(), + mergeBoundsAttributes); + if (MergeParamTy.isNull()) + return false; + if (const auto *CATy = MergeParamTy->getAs()) { + Self.AttachDependerDeclsAttr(New->getParamDecl(i), CATy, /*Level*/ 0); + } + MergeParamTys.push_back(MergeParamTy); + } + bool Updated = MergeRetTy != New->getReturnType(); + for (unsigned i = 0; i < New->getNumParams(); ++i) { + auto *NewParm = New->getParamDecl(i); + if (NewParm->getType() != MergeParamTys[i]) { + New->getParamDecl(i)->setType(MergeParamTys[i]); + Updated = true; + } + } + if (Updated) { + if (auto *FT = New->getType()->getAs()) { + QualType NewFuncTy = Self.Context.getFunctionType( + MergeRetTy, MergeParamTys, FT->getExtProtoInfo()); + New->setType(NewFuncTy); + } else if (New->getType()->getAs()) { + QualType NewFuncTy = Self.Context.getFunctionNoProtoType(MergeRetTy); + New->setType(NewFuncTy); + } + } + return Updated; +} + +static bool mergeFunctionDeclTerminatedByAttribute(FunctionDecl *New, + FunctionDecl *Old, Sema &Self) { + if (New->getNumParams() != Old->getNumParams()) + return false; + + ASTContext &Ctx = Self.Context; + SourceLocation NewLoc = New->getLocation(); + SourceLocation OldLoc = Old->getLocation(); + auto ReportConflict = [&]() { + Self.Diag(NewLoc, diag::err_bounds_safety_dynamic_bound_redeclaration) + << /*terminated_by_*/ 5 << 0; + Self.Diag(OldLoc, diag::note_previous_declaration); + }; + + std::function + mergeTerminatedByAttributes = [&](QualType NewTy, QualType OldTy, + QualType MergePointeeTy, + QualType OrigNewTy) -> QualType { + if (OldTy.isNull()) + return QualType(); + if (MergePointeeTy.isNull()) + return QualType(); + if (NewTy->isBoundsAttributedType() || + OldTy->isBoundsAttributedType()) + return QualType(); + + BoundsSafetyPointerAttributes Attributes; + const auto *NewVTTy = NewTy->getAs(); + const auto *OldVTTy = OldTy->getAs(); + if (NewVTTy && !OldVTTy) { + // Take the new type attribute + if (OldTy->isUnspecifiedPointerType()) { + Attributes = NewTy->getAs()->getPointerAttributes(); + QualType MergeTy = Ctx.getPointerType(MergePointeeTy, Attributes); + return Ctx.getValueTerminatedType(MergeTy, + NewVTTy->getTerminatorExpr()); + } + + ReportConflict(); + return QualType(); + } + + if (!NewVTTy && OldVTTy) { + // Take the old type attribute + if (NewTy->isUnspecifiedPointerType()) { + Attributes = OldTy->getAs()->getPointerAttributes(); + QualType MergeTy = Ctx.getPointerType(MergePointeeTy, Attributes); + return Ctx.getValueTerminatedType(MergeTy, + OldVTTy->getTerminatorExpr()); + } + + ReportConflict(); + return QualType(); + } + + if (NewVTTy && OldVTTy) { + if (llvm::APSInt::isSameValue(NewVTTy->getTerminatorValue(Ctx), + OldVTTy->getTerminatorValue(Ctx))) { + Attributes = NewTy->getAs()->getPointerAttributes(); + QualType MergeTy = Ctx.getPointerType(MergePointeeTy, Attributes); + return Ctx.getValueTerminatedType(MergeTy, + NewVTTy->getTerminatorExpr()); + } + + ReportConflict(); + return QualType(); + } + + if (auto *NewPTy = NewTy->getAs()) + Attributes = NewPTy->getPointerAttributes(); + if (Attributes.isUnspecified()) + if (auto *OldPTy = OldTy->getAs()) + Attributes = OldPTy->getPointerAttributes(); + + return Ctx.getPointerType(MergePointeeTy, Attributes); + }; + + if (New->getReturnType()->isBoundsAttributedType()) + return false; + QualType MergeRetTy = Ctx.mergeBoundsSafetyPointerTypes( + New->getReturnType(), Old->getReturnType(), mergeTerminatedByAttributes); + if (MergeRetTy.isNull()) + return false; + + llvm::SmallVector MergeParamTys; + for (unsigned i = 0; i < New->getNumParams(); ++i) { + NewLoc = New->getParamDecl(i)->getLocation(); + OldLoc = Old->getParamDecl(i)->getLocation(); + QualType NewParmTy = New->getParamDecl(i)->getType(); + if (NewParmTy->isBoundsAttributedType()) + return false; + QualType MergeParamTy = Self.Context.mergeBoundsSafetyPointerTypes( + NewParmTy, Old->getParamDecl(i)->getType(), + mergeTerminatedByAttributes); + if (MergeParamTy.isNull()) + return false; + MergeParamTys.push_back(MergeParamTy); + } + bool Updated = MergeRetTy != New->getReturnType(); + for (unsigned i = 0; i < New->getNumParams(); ++i) { + auto *NewParm = New->getParamDecl(i); + if (NewParm->getType() != MergeParamTys[i]) { + New->getParamDecl(i)->setType(MergeParamTys[i]); + Updated = true; + } + } + if (Updated) { + if (auto *FT = New->getType()->getAs()) { + QualType NewFuncTy = Self.Context.getFunctionType( + MergeRetTy, MergeParamTys, FT->getExtProtoInfo()); + New->setType(NewFuncTy); + } else if (New->getType()->isFunctionNoProtoType()) { + QualType NewFuncTy = Self.Context.getFunctionNoProtoType(MergeRetTy); + New->setType(NewFuncTy); + } + } + return Updated; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, bool MergeTypeWithOld, bool NewDeclIsDefn) { // Verify the old decl was also a function. @@ -3869,6 +4542,29 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, return true; } + /*TO_UPSTREAM(BoundsSafety) ON */ + if (getLangOpts().BoundsSafetyAttributes) { + // This is the same logic to suppress warnings in system headers. + // In case of system functions, we want to inherit counted_by attributes + // from the old declaration. + if (!Old->isImplicit() && + !getSourceManager().isInSystemHeader(New->getLocation()) && + !getSourceManager().isInSystemHeader(Old->getLocation())) { + if (!diagnoseFunctionConflictWithDynamicBoundTypes(New, Old, *this)) + return true; + } else if (mergeFunctionDeclBoundsAttributes(New, Old, *this)) { + NewQType = New->getType(); + } + } + + if (getLangOpts().BoundsSafety && + mergeFunctionDeclTerminatedByAttribute(New, Old, *this)) { + // TODO: Merge __terminated_by() attributes in attribute-only mode. + // rdar://137984921 + NewQType = New->getType(); + } + /*TO_UPSTREAM(BoundsSafety) OFF */ + QualType OldQTypeForComparison = OldQType; if (Context.hasAnyFunctionEffects()) { const auto OldFX = Old->getFunctionEffects(); @@ -4162,7 +4858,8 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, // we need to cover here is that the number of arguments agree as the // default argument promotion rules were already checked by // ASTContext::typesAreCompatible(). - if (Old->hasPrototype() && !New->hasWrittenPrototype() && NewDeclIsDefn && + if (!getLangOpts().IgnoreConflictingTypes && Old->hasPrototype() && + !New->hasWrittenPrototype() && NewDeclIsDefn && Old->getNumParams() != New->getNumParams() && !Old->isImplicit()) { if (Old->hasInheritedPrototype()) Old = Old->getCanonicalDecl(); @@ -4341,7 +5038,16 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, PrevDiag = diag::note_previous_builtin_declaration; } - Diag(New->getLocation(), diag::err_conflicting_types) << New->getDeclName(); + { + auto ConflictDiag = Diag(New->getLocation(), diag::err_conflicting_types) + << New->getDeclName(); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + fixBoundsSafetyFunctionDecl(*this, ConflictDiag, New, Old); + fixBoundsSafetyFunctionDecl(*this, ConflictDiag, Old, New); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + } Diag(OldLocation, PrevDiag) << Old << Old->getType(); return true; } @@ -6946,6 +7652,53 @@ void Sema::deduceOpenCLAddressSpace(ValueDecl *Decl) { } } +/* TO_UPSTREAM(BoundsSafety) ON*/ +void Sema::deduceBoundsSafetyPointerTypes(ValueDecl *Decl) { + bool ShouldAutoBound = false; + if (VarDecl *Var = dyn_cast(Decl)) { + // Bound automatically only the local variables that have local storage. + // Unspecified local variables with global storage (e.g. static local) + // should get the default ABI attributes. + ShouldAutoBound = Var->isLocalVarDecl() && Var->hasLocalStorage(); + } + Decl->setType(Context.getBoundsSafetyAutoPointerType( + Decl->getType(), CurPointerAbi, ShouldAutoBound)); +} + +static QualType deduceBoundsSafetyFuncType(Sema &S, const FunctionDecl *Decl, + QualType Ty) { + if (const auto *AttrTy = Ty->getAs()) { + QualType ModTy = + deduceBoundsSafetyFuncType(S, Decl, AttrTy->getModifiedType()); + QualType EquivTy = + deduceBoundsSafetyFuncType(S, Decl, AttrTy->getEquivalentType()); + return S.Context.getAttributedType(AttrTy->getAttrKind(), ModTy, EquivTy); + } + + QualType NewRetTy = S.Context.getBoundsSafetyAutoPointerType( + Decl->getReturnType(), S.CurPointerAbi, + /*ShouldAutoBound=*/false); + + if (NewRetTy == Decl->getReturnType()) + return Ty; + + const FunctionType *FuncTy = Decl->getFunctionType(); + + if (const auto *FuncNoProtoTy = dyn_cast(FuncTy)) + return S.Context.getFunctionNoProtoType(NewRetTy, + FuncNoProtoTy->getExtInfo()); + + const auto *FuncProtoTy = cast(FuncTy); + return S.Context.getFunctionType(NewRetTy, FuncProtoTy->getParamTypes(), + FuncProtoTy->getExtProtoInfo()); +} + +void Sema::deduceBoundsSafetyFunctionTypes(FunctionDecl *Decl) { + QualType NewTy = deduceBoundsSafetyFuncType(*this, Decl, Decl->getType()); + Decl->setType(NewTy); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + static void checkWeakAttr(Sema &S, NamedDecl &ND) { // 'weak' only applies to declarations with external linkage. if (WeakAttr *Attr = ND.getAttr()) { @@ -7238,6 +7991,50 @@ static void checkDLLAttributeRedeclaration(Sema &S, NamedDecl *OldDecl, } } +static void checkExternalBoundsRedeclaration(Sema &S, Decl *D) { + VarDecl *NewVD = dyn_cast(D); + if (!NewVD || NewVD->isFirstDecl()) + return; + + if (NewVD->isFirstDecl()) + return; + VarDecl *PrevVD = NewVD->getFirstDecl(); + + const auto *PrevTy = PrevVD->getType()->getAs(); + const auto *NewTy = NewVD->getType()->getAs(); + + if (!PrevTy && !NewTy) + return; + + auto DiagConflict = [&](unsigned Kind) { + S.Diag(NewVD->getLocation(), + diag::err_bounds_safety_dynamic_bound_redeclaration) + << Kind << 1; + S.Diag(PrevVD->getLocation(), diag::note_previous_decl) << PrevVD; + }; + + if (PrevTy && !NewTy && PrevVD->hasExternalStorage() && + NewVD->getType()->isConstantArrayType()) { + return; // rdar://129246717: remove this workaround + } + + if ((bool)PrevTy != (bool)NewTy) { + unsigned Kind = NewTy ? NewTy->getKind() : PrevTy->getKind(); + return DiagConflict(Kind); + } + + assert(PrevTy && NewTy); + if (PrevTy->getKind() != NewTy->getKind()) + return DiagConflict(NewTy->getKind()); + + llvm::FoldingSetNodeID PrevID; + llvm::FoldingSetNodeID NewID; + PrevTy->getCountExpr()->Profile(PrevID, S.Context, /*Canonical*/ true); + NewTy->getCountExpr()->Profile(NewID, S.Context, /*Canonical*/ true); + if (PrevID != NewID) + return DiagConflict(NewTy->getKind()); +} + /// Given that we are within the definition of the given function, /// will that definition behave like C99's 'inline', where the /// definition is discarded except for optimization purposes? @@ -7977,6 +8774,11 @@ NamedDecl *Sema::ActOnVariableDeclarator( } } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) + deduceBoundsSafetyPointerTypes(NewVD); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // WebAssembly tables are always in address space 1 (wasm_var). Don't apply // address space if the table has local storage (semantic checks elsewhere // will produce an error anyway). @@ -9849,6 +10651,77 @@ static bool isStdBuiltin(ASTContext &Ctx, FunctionDecl *FD, } } +static void rebuildBoundAttributedTypes(Sema &S, FunctionDecl *FD) { + struct ReplaceParams : public TreeTransform { + using BaseTransform = TreeTransform; + FunctionDecl *FD; + + explicit ReplaceParams(Sema &SemaRef, FunctionDecl *FD) + : BaseTransform(SemaRef), FD(FD) {} + + bool AlwaysRebuild() { return true; } + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + const auto *OldParam = dyn_cast(E->getDecl()); + if (!OldParam) + return BaseTransform::TransformDeclRefExpr(E); + auto *NewParam = FD->getParamDecl(OldParam->getFunctionScopeIndex()); + return SemaRef.BuildDeclRefExpr(NewParam, E->getType(), E->getValueKind(), + E->getNameInfo()); + } + } Transform(S, FD); + + auto RebuildBoundAttributeType = [&](QualType Ty) { + if (Ty->isBoundsAttributedType() || + (Ty->isPointerType() && + Ty->getPointeeType()->isBoundsAttributedType())) { + return Transform.TransformType(Ty); + } + return Ty; + }; + + QualType OldRetTy = FD->getReturnType(); + QualType NewRetTy = RebuildBoundAttributeType(OldRetTy); + bool Updated = NewRetTy != OldRetTy; + + llvm::SmallVector NewParamTys; + NewParamTys.reserve(FD->getNumParams()); + for (unsigned I = 0; I < FD->getNumParams(); ++I) { + ParmVarDecl *Param = FD->getParamDecl(I); + QualType OldTy = Param->getType(); + QualType NewTy = RebuildBoundAttributeType(OldTy); + + NewParamTys.push_back(NewTy); + + if (NewTy == OldTy) + continue; + + Param->setType(NewTy); + Updated = true; + + // If this param is a count attributed type or a pointer to it (Deref is + // true), we need to recreate DependerDeclsAttr for its dependent + // parameters. + bool Deref = false; + const auto *CATy = NewTy->getAs(); + if (!CATy && NewTy->isPointerType()) { + Deref = true; + CATy = NewTy->getPointeeType()->getAs(); + } + if (CATy) + S.AttachDependerDeclsAttr(Param, CATy, Deref); + } + + if (!Updated) + return; + + // TODO: We could keep the typeof/typedef sugars. + const auto *OldFPT = FD->getType()->castAs(); + QualType NewFPT = S.Context.getFunctionType(NewRetTy, NewParamTys, + OldFPT->getExtProtoInfo()); + FD->setType(NewFPT); +} + NamedDecl* Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, LookupResult &Previous, @@ -10404,11 +11277,64 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // @endcode // Synthesize a parameter for each argument type. - for (const auto &AI : FT->param_types()) { - ParmVarDecl *Param = - BuildParmVarDeclForTypedef(NewFD, D.getIdentifierLoc(), AI); - Param->setScopeInfo(0, Params.size()); - Params.push_back(Param); + + /* TO_UPSTREAM(BoundsSafety) ON */ + if (getLangOpts().BoundsSafetyAttributes) { + // If the parameters are used as dependent variables, we can synthesize + // named parameters for use in the declaration. Named dependent variables + // let us generate better diagnostics for the user (e.g., + // __counted_by(len) instead of __counted_by()). Otherwise, just + // synthesize unnamed parameters. + llvm::SmallVector NamedParams(FT->getNumParams(), + nullptr); + auto AddNamedParamFromDependentType = [&](QualType Ty) { + const auto *BAT = Ty->getAs(); + if (!BAT && Ty->isPointerType()) + BAT = Ty->getPointeeType()->getAs(); + if (!BAT) + return; + for (const auto &DD : BAT->dependent_decls()) { + if (const auto *PVD = dyn_cast(DD.getDecl())) + NamedParams[PVD->getFunctionScopeIndex()] = PVD; + } + }; + AddNamedParamFromDependentType(FT->getReturnType()); + for (QualType ParamTy : FT->param_types()) + AddNamedParamFromDependentType(ParamTy); + + // Temporarily put parameter variables in the translation unit, not the + // enclosing context. This is what Sema::ActOnParamDeclarator() already + // does when declaring a function without a typedef/typeof. In + // BoundsSafety, we do it to allow creating references to the parameters + // when rebuilding the count expression. Otherwise, creating such + // references triggers an error. + for (unsigned I = 0; I < FT->getNumParams(); ++I) { + QualType Ty = FT->getParamType(I); + const ParmVarDecl *NamedParam = NamedParams[I]; + ParmVarDecl *Param; + if (!NamedParam) { + Param = BuildParmVarDeclForTypedef(Context.getTranslationUnitDecl(), + D.getIdentifierLoc(), Ty); + } else { + SourceLocation Loc = D.getIdentifierLoc(); + Param = ParmVarDecl::Create(Context, Context.getTranslationUnitDecl(), + Loc, Loc, NamedParam->getIdentifier(), Ty, + Context.getTrivialTypeSourceInfo(Ty, Loc), + SC_None, nullptr); + Param->setImplicit(); + } + Param->setScopeInfo(0, Params.size()); + Params.push_back(Param); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + else { + for (const auto &AI : FT->param_types()) { + ParmVarDecl *Param = + BuildParmVarDeclForTypedef(NewFD, D.getIdentifierLoc(), AI); + Param->setScopeInfo(0, Params.size()); + Params.push_back(Param); + } } } else { assert(R->isFunctionNoProtoType() && NewFD->getNumParams() == 0 && @@ -10418,6 +11344,46 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // Finally, we know we have the right number of parameters, install them. NewFD->setParams(Params); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafetyAttributes) { + if (!D.isFunctionDeclarator() && R->isFunctionProtoType()) { + // The function is being declared with a typedef/typeof. We need to + // rebuild the bound attributed types, so that their expression refer the + // to the new parameters. + rebuildBoundAttributedTypes(*this, NewFD); + + for (ParmVarDecl *Param : Params) { + assert(Param->getDeclContext() == Context.getTranslationUnitDecl()); + Param->setDeclContext(NewFD); + } + } + } + + if (getLangOpts().BoundsSafety) { + // This is a good place to make sure that no array parameter has decayed + // to a __single pointer. Annoyingly, we can't do this when parameters are + // created because BoundsSafety count attributes are late-parsed and applied + // after. + for (ParmVarDecl *Param : Params) { + auto *TSInfo = Param->getTypeSourceInfo(); + QualType TST = TSInfo->getType(); + if (!Context.getAsArrayType(TST) || + TST->hasAttr(attr::ArrayDecayDiscardsCountInParameters)) + continue; // nothing to check + if (Param->getType()->isCountAttributedType() || + Param->getType()->isDynamicRangePointerType()) + continue; // has a dynamic count, no problem + auto FA = Param->getType()->getAs()->getPointerAttributes(); + if (FA.isSingle()) { + Diag(TSInfo->getTypeLoc().getBeginLoc(), + diag::err_bounds_safety_array_decay_to_single) << TST; + Diag(TSInfo->getTypeLoc().getBeginLoc(), + diag::note_bounds_safety_array_decay_use_count_annotation); + } + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (D.getDeclSpec().isNoreturnSpecified()) NewFD->addAttr( C11NoReturnAttr::Create(Context, D.getDeclSpec().getNoreturnSpecLoc())); @@ -10490,6 +11456,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, } } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) + deduceBoundsSafetyFunctionTypes(NewFD); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (!getLangOpts().CPlusPlus) { // Perform semantic checking on the function declaration. if (!NewFD->isInvalidDecl() && NewFD->isMain()) @@ -13873,6 +14844,22 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { } Init = Result.get(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety prevents passing flexible array members by copy. We still allow + // initializer lists (and no initializers), but that's it. + if (Init && getLangOpts().BoundsSafety && VDecl->hasLocalStorage()) { + auto *RecordT = VDecl->getType()->getAs(); + if (RecordT && RecordT->getDecl()->hasFlexibleArrayMember()) { + if (!isa(Init->IgnoreParenImpCasts())) { + Diag( + Init->getBeginLoc(), diag::err_flexible_array_member_passed_by_copy) + << Init->IgnoreParenImpCasts()->getType(); + // Recover by pretending this worked + } + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Attach the initializer to the decl. VDecl->setInit(Init); @@ -14126,6 +15113,11 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) { if (VarDecl *Var = dyn_cast(RealDecl)) { QualType Type = Var->getType(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) + CheckValueTerminatedUninitialized(Var); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // C++1z [dcl.dcl]p1 grammar implies that an initializer is mandatory. if (isa(RealDecl)) { Diag(Var->getLocation(), diag::err_decomp_decl_requires_init) << Var; @@ -14978,6 +15970,9 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) { } } + if (auto *Attr = VD->getAttr()) + getASTContext().addAvailabilityDomainMap(Attr->getName()->getName(), VD); + const DeclContext *DC = VD->getDeclContext(); // If there's a #pragma GCC visibility in scope, and this isn't a class // member, set the visibility of this variable. @@ -15026,6 +16021,39 @@ static bool hasDeducedAuto(DeclaratorDecl *DD) { return VD && !VD->getType()->hasAutoForTrailingReturnType(); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +static void checkAtomicAutoPointerAttrs(Sema &S, const Decl *D) { + const auto *VD = dyn_cast(D); + if (!VD) + return; + QualType Ty = VD->getType(); + for (;;) { + // If we auto bound an atomic pointer, we should have: + // AtomicType -> AttributedType -> PointerType. + bool HasAutoAttr = false; + if (const auto *AtomTy = Ty->getAs()) { + Ty = AtomTy->getValueType(); + if (const auto *AttrTy = Ty->getAs()) { + if (AttrTy->getAttrKind() == attr::PtrAutoAttr) + HasAutoAttr = true; + Ty = AttrTy->getModifiedType(); + } + } + const auto *PtrTy = Ty->getAs(); + if (!PtrTy) + break; + if (HasAutoAttr && !PtrTy->isUnspecified() && !PtrTy->isUnsafeIndexable() && + !PtrTy->isSingle()) { + assert(PtrTy->isIndexable() || PtrTy->isBidiIndexable()); + unsigned DiagIndex = PtrTy->isIndexable() ? 0 : 1; + S.Diag(D->getBeginLoc(), diag::err_bounds_safety_atomic_unsupported_attribute) + << DiagIndex; + } + Ty = PtrTy->getPointeeType(); + } +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + Sema::DeclGroupPtrTy Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS, ArrayRef Group) { SmallVector Decls; @@ -15085,6 +16113,33 @@ Sema::DeclGroupPtrTy Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS, } } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafetyAttributes) + checkExternalBoundsRedeclaration(*this, D); + // Check if an atomic pointer was auto bound to __bidi_indexable, and if + // so, emit an error, as atomic __bidi_indexable pointers are currently + // not supported. We must perform this check after handling dynamic bound + // pointers, since their construction can replace pointer attributes. + if (getLangOpts().BoundsSafety) { + checkAtomicAutoPointerAttrs(*this, D); + if (auto Var = dyn_cast(D)) { + DiagnoseDynamicCountVarZeroInit(Var); + + // This check is done now and not earlier in + // `Sema::ActOnVariableDeclarator()` because in that method attributes + // haven't yet been applied due to late parsing. + // + // Tentative definitions are skipped because it isn't known if + // the VarDecl is a definition until the end of the translation unit. + // Tentative definitions are instead checked in + // `Sema::ActOnEndOfTranslationUnit()`. + if (!BoundsSafetyCheckVarDecl(Var, + /*CheckTentativeDefinitions=*/false)) + Var->setInvalidDecl(); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + Decls.push_back(D); } @@ -15350,6 +16405,13 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D, CheckParameter(Context.getTranslationUnitDecl(), D.getBeginLoc(), D.getIdentifierLoc(), II, parmDeclType, TInfo, SC); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // We reuse the same function 'deduceBoundsSafetyPointerTypes' that deduces + // -fbounds-safety pointer types of Fields and VarDecl. + if (getLangOpts().BoundsSafety) + deduceBoundsSafetyPointerTypes(New); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (D.isInvalidType()) New->setInvalidDecl(); @@ -15443,36 +16505,54 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, SourceLocation NameLoc, const IdentifierInfo *Name, QualType T, TypeSourceInfo *TSInfo, StorageClass SC) { - // In ARC, infer a lifetime qualifier for appropriate parameter types. - if (getLangOpts().ObjCAutoRefCount && - T.getObjCLifetime() == Qualifiers::OCL_None && - T->isObjCLifetimeType()) { - - Qualifiers::ObjCLifetime lifetime; - - // Special cases for arrays: - // - if it's const, use __unsafe_unretained - // - otherwise, it's an error - if (T->isArrayType()) { - if (!T.isConstQualified()) { - if (DelayedDiagnostics.shouldDelayDiagnostics()) - DelayedDiagnostics.add( - sema::DelayedDiagnostic::makeForbiddenType( - NameLoc, diag::err_arc_array_param_no_ownership, T, false)); - else - Diag(NameLoc, diag::err_arc_array_param_no_ownership) - << TSInfo->getTypeLoc().getSourceRange(); + // Perform Objective-C ARC adjustments. + T = ObjC().AdjustParameterTypeForObjCAutoRefCount(T, NameLoc, TSInfo); + /*TO_UPSTREAM(BoundsSafety) ON*/ + QualType Adjusted = Context.getAdjustedParameterType(T); + + // Perform -fbounds-safety pointer type adjustments. + if (getLangOpts().BoundsSafety) { + auto *AT = Context.getAsArrayType(T); + if (AT && !T->isAnyVaListType(Context)) { + Expr *Count = nullptr; + if (!T->hasAttr(attr::ArrayDecayDiscardsCountInParameters)) { + if (auto *CAT = dyn_cast(AT)) { + ArrayTypeLoc ATL = TSInfo->getTypeLoc().getAsAdjusted(); + Count = ATL.isNull() + ? new (Context) IntegerLiteral(Context, CAT->getSize(), + Context.getSizeType(), + SourceLocation()) + : ATL.getSizeExpr(); + } else if (auto *VAT = dyn_cast(AT)) { + Count = VAT->getSizeExpr(); + } else if (auto *DAT = dyn_cast(AT)) { + Count = DAT->getSizeExpr(); + } } - lifetime = Qualifiers::OCL_ExplicitNone; - } else { - lifetime = T->getObjCARCImplicitLifetime(); + if (Count) { + Adjusted = BuildCountAttributedType(Adjusted, Count, false); + } + } + + auto *RecordT = T->getAs(); + if (RecordT && RecordT->getDecl()->hasFlexibleArrayMember()) { + // BoundsSafety prevents passing flexible array members by copy. + Diag(TSInfo->getTypeLoc().getBeginLoc(), + diag::err_flexible_array_member_passed_by_copy) << TSInfo->getType(); } - T = Context.getLifetimeQualifiedType(T, lifetime); } + /*TO_UPSTREAM(BoundsSafety) OFF*/ ParmVarDecl *New = ParmVarDecl::Create(Context, DC, StartLoc, NameLoc, Name, - Context.getAdjustedParameterType(T), - TSInfo, SC, nullptr); + Adjusted, TSInfo, SC, nullptr); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafetyAttributes) { + if (const auto *CATy = New->getType()->getAs()) { + AttachDependerDeclsAttr(New, CATy, /*Level*/ 0); + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ // Make a note if we created a new pack in the scope of a lambda, so that // we know that references to that pack must also be expanded within the @@ -15947,6 +17027,15 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D, AbstractReturnType))) FD->setInvalidDecl(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (!FD->isInvalidDecl()) { + // Note if the checks fail we don't make the decl invalid, + // otherwise we would stop diagnosing calls to this functions from other + // functions. + BoundsSafetyCheckReturnTyForFunctionDef(FD); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (FnBodyScope) PushDeclContext(FnBodyScope, FD); @@ -16556,6 +17645,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (Body && FSI->HasPotentialAvailabilityViolations) DiagnoseUnguardedAvailabilityViolations(dcl); + if (Body && FSI->HasPotentialFeatureAvailabilityViolations) + DiagnoseUnguardedFeatureAvailabilityViolations(dcl); + assert(!FSI->ObjCShouldCallSuper && "This should only be set for ObjC methods, which should have been " "handled in the block above."); @@ -16663,6 +17755,11 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (FD && !FD->isDeleted()) checkTypeSupport(FD->getType(), FD->getLocation(), FD); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety) + DynamicCountPointerAssignmentAnalysis(*this, dcl).run(); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + return dcl; } @@ -18940,6 +20037,7 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, if (D) { // FIXME: The current scope is almost... but not entirely... correct here. ProcessDeclAttributes(getCurScope(), NewFD, *D); + copyFeatureAvailabilityCheck(NewFD, Record); if (NewFD->hasAttrs()) CheckAlignasUnderalignment(NewFD); @@ -18958,6 +20056,11 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, PPC().CheckPPCMMAType(T, NewFD->getLocation())) NewFD->setInvalidDecl(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) + deduceBoundsSafetyPointerTypes(NewFD); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + NewFD->setAccess(AS); return NewFD; } @@ -20085,6 +21188,7 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst, // Process attributes. ProcessDeclAttributeList(S, New, Attrs); AddPragmaAttributes(S, New); + copyFeatureAvailabilityCheck(New, TheEnumDecl); ProcessAPINotes(New); // Register this decl in the current scope stack. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 8499141c30bce..557787cfe013f 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -22,7 +22,10 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/Mangle.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/StmtVisitor.h" #include "clang/AST/Type.h" +#include "clang/AST/TypeVisitor.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/Cuda.h" #include "clang/Basic/DarwinSDKInfo.h" @@ -68,6 +71,8 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Triple.h" +#include "TreeTransform.h" +#include "DynamicCountPointerAssignmentAnalysis.h" #include using namespace clang; @@ -1266,6 +1271,13 @@ static bool attrNonNullArgCheck(Sema &S, QualType T, const ParsedAttr &AL, << AL << AttrParmRange << TypeRange << 0; return false; } + if (auto DCPTy = T->getAs()) { + if (DCPTy->isOrNull()) { + S.Diag(AL.getLoc(), + diag::err_bounds_safety_nullable_dynamic_count_nonnullable) + << DCPTy->isCountInBytes() << AL << AttrParmRange << TypeRange; + } + } return true; } @@ -1301,8 +1313,19 @@ static void handleNonNullAttr(Sema &S, Decl *D, const ParsedAttr &AL) { for (unsigned I = 0, E = getFunctionOrMethodNumParams(D); I != E && !AnyPointers; ++I) { QualType T = getFunctionOrMethodParamType(D, I); - if (S.isValidPointerAttrType(T)) + if (T->isDependentType() || S.isValidPointerAttrType(T)) { AnyPointers = true; + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (auto DCPTy = T->getAs()) { + if (DCPTy->isOrNull()) { + S.Diag(AL.getLoc(), + diag::err_bounds_safety_nullable_dynamic_count_nonnullable) + << DCPTy->isCountInBytes() << AL + << getFunctionOrMethodParamRange(D, I); + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + } } if (!AnyPointers) @@ -1735,6 +1758,14 @@ static void handleTLSModelAttr(Sema &S, Decl *D, const ParsedAttr &AL) { static void handleRestrictAttr(Sema &S, Decl *D, const ParsedAttr &AL) { QualType ResultType = getFunctionOrMethodResultType(D); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Drop the restrict attribute for wide pointers. + // __indexable/__bidi_indexable turn the pointer into an aggregate type. + // restrict/__attribute__((malloc)) implies noalias attribute in LLVM IR, + // which is invalid on aggregate types. + if (ResultType->isPointerTypeWithBounds()) + return; + /* TO_UPSTREAM(BoundsSafety) OFF*/ if (!ResultType->isAnyPointerType() && !ResultType->isBlockPointerType()) { S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only) << AL << getFunctionOrMethodResultSourceRange(D); @@ -2346,7 +2377,72 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr( return nullptr; } +static void handleFeatureAvailabilityAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + if (S.getLangOpts().CPlusPlus) { + S.Diag(AL.getLoc(), diag::warn_attribute_ignored) << AL; + return; + } + + if (!AL.isArgIdent(0)) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_n_type) + << AL << 0 << AANT_ArgumentIdentifier; + return; + } + + StringRef InvalidDecl; + + if (isa(D)) + InvalidDecl = "ObjC class implementations"; + else if (isa(D)) + InvalidDecl = "ObjC category implementations"; + else if (auto *FD = dyn_cast(D)) { + if (FD->getParent()) + InvalidDecl = "struct members"; + } + + if (!InvalidDecl.empty()) { + S.Diag(AL.getLoc(), diag::err_features_invalid_for_decl) << InvalidDecl; + return; + } + + IdentifierInfo *II = AL.getArgAsIdent(0)->getIdentifierInfo(); + + if (S.Context.getFeatureAvailInfo(II->getName()).isInvalid()) { + S.Diag(AL.getLoc(), diag::err_features_invalid_name) << II->getName(); + return; + } + + int IsUnavailable = + AL.getArgAsExpr(1)->getIntegerConstantExpr(S.Context)->getExtValue(); + + if (IsUnavailable != 0 && IsUnavailable != 1) { + S.Diag(AL.getArgAsExpr(1)->getExprLoc(), diag::err_features_invalid__arg) + << IsUnavailable; + return; + } + + auto p = + S.Context.checkNewFeatureAvailability(D, II->getName(), IsUnavailable); + if (!p.second) { + auto *AA = DomainAvailabilityAttr::Create(S.Context, II->getName(), + IsUnavailable, AL); + S.Diag(D->getBeginLoc(), diag::err_feature_invalid_added); + S.Diag(AL.getLoc(), diag::note_feature_incompatible0) + << AA->getFeatureAttributeStr(); + S.Diag(p.first->getLocation(), diag::note_feature_incompatible1) + << p.first->getFeatureAttributeStr(); + } else if (!p.first) + D->addAttr(DomainAvailabilityAttr::Create(S.Context, II->getName(), + IsUnavailable, AL)); +} + static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + if (!AL.isAvailabilityAttribute()) { + handleFeatureAvailabilityAttr(S, D, AL); + return; + } + if (isa( D)) { S.Diag(AL.getRange().getBegin(), diag::warn_deprecated_ignored_on_using) @@ -2370,6 +2466,15 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { AvailabilityChange Introduced = AL.getAvailabilityIntroduced(); AvailabilityChange Deprecated = AL.getAvailabilityDeprecated(); AvailabilityChange Obsoleted = AL.getAvailabilityObsoleted(); + if (II->getName() == "macos" || II->getName() == "macos_app_extension") { + // Canonicalize macOS availability versions. + Introduced.Version = llvm::Triple::getCanonicalVersionForOS( + llvm::Triple::MacOSX, Introduced.Version); + Deprecated.Version = llvm::Triple::getCanonicalVersionForOS( + llvm::Triple::MacOSX, Deprecated.Version); + Obsoleted.Version = llvm::Triple::getCanonicalVersionForOS( + llvm::Triple::MacOSX, Obsoleted.Version); + } bool IsUnavailable = AL.getUnavailableLoc().isValid(); bool IsStrict = AL.getStrictLoc().isValid(); StringRef Str; @@ -2612,6 +2717,12 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { } } +static void handleAvailabilityDomainAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + IdentifierInfo *II = AL.getArgAsIdent(0)->getIdentifierInfo(); + D->addAttr(::new (S.Context) AvailabilityDomainAttr(S.Context, AL, II)); +} + static void handleExternalSourceSymbolAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (!AL.checkAtLeastNumArgs(S, 1) || !AL.checkAtMostNumArgs(S, 4)) @@ -3837,6 +3948,55 @@ struct FormatAttrCommon { unsigned FormatStringIdx; }; +void Sema::copyFeatureAvailability(Decl *Dst, Decl *Src) { + if (Dst->isInvalidDecl()) + return; + + for (auto *AA : Src->specific_attrs()) { + auto *NewAttr = AA->clone(Context); + NewAttr->setInherited(true); + Dst->addAttr(NewAttr); + } +} + +void Sema::copyFeatureAvailabilityCheck(Decl *Dst, NamedDecl *Src, + bool Redeclaration) { + if (Dst->isInvalidDecl()) + return; + + llvm::SmallDenseMap DstToAttr; + + for (auto *AA : Dst->specific_attrs()) + DstToAttr[AA->getDomain()] = AA; + + for (auto *AA : Src->specific_attrs()) { + auto I = DstToAttr.find(AA->getDomain()); + if (I == DstToAttr.end()) { + auto *NewAttr = AA->clone(Context); + NewAttr->setInherited(true); + Dst->addAttr(NewAttr); + } else { + if (I->second->getUnavailable() != AA->getUnavailable()) { + Diag(Dst->getBeginLoc(), diag::err_feature_merge_incompatible); + Diag(I->second->getLocation(), diag::note_feature_incompatible0) + << I->second->getFeatureAttributeStr(); + Diag(AA->getLocation(), diag::note_feature_incompatible1) + << AA->getFeatureAttributeStr(); + Dst->setInvalidDecl(); + } + DstToAttr.erase(I); + } + } + + if (Redeclaration) { + if (!DstToAttr.empty()) + Dst->setInvalidDecl(); + for (auto &P : DstToAttr) + Diag(P.second->getLocation(), diag::err_new_feature_redeclaration) + << P.second->getFeatureAttributeStr(); + } +} + /// Handle __attribute__((format(type,idx,firstarg))) attributes based on /// http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html static bool handleFormatAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL, @@ -5782,6 +5942,1266 @@ static void handleXRayLogArgsAttr(Sema &S, Decl *D, const ParsedAttr &AL) { XRayLogArgsAttr(S.Context, AL, ArgCount.getSourceIndex())); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +namespace { + +template +class ConstructDynamicBoundType + : public TypeVisitor { + using BaseClass = TypeVisitor; + + Derived *getDerived() { + return static_cast(this); + } + +protected: + Sema &S; + const ParsedAttr &AL; + Expr *ArgExpr; + const BoundsAttributedType *ConstructedType = nullptr; + unsigned Level; + bool ScopeCheck; + bool AutoPtrAttributed = false; + bool AllowCountRedecl = false; + bool AtomicErrorEmitted = false; + +public: + explicit ConstructDynamicBoundType(Sema &S, unsigned Level, + const ParsedAttr &AL, bool ScopeCheck) + : S(S), AL(AL), ArgExpr(AL.getArgAsExpr(0)), Level(Level), + ScopeCheck(ScopeCheck) {} + + QualType Visit(QualType T) { + SplitQualType SQT = T.split(); + QualType InnerTy = BaseClass::Visit(SQT.Ty); + if (InnerTy.isNull()) + return QualType(); + return S.Context.getQualifiedType(InnerTy, SQT.Quals); + } + + bool hadAtomicError() const { return AtomicErrorEmitted; } + const BoundsAttributedType *getConstructedType() const { + return ConstructedType; + } + + QualType VisitType(const Type *T) { + if (const auto *PTy = T->getAs()) + return VisitPointerType(PTy); + S.Diag(S.getAttrLoc(AL), diag::err_attribute_pointers_only) << AL << 0; + return QualType(); + } + + QualType VisitParenType(const ParenType *T) { + QualType InnerTy = Visit(T->getInnerType()); + return S.Context.getParenType(InnerTy); + } + + QualType VisitFunctionProtoType(const FunctionProtoType *FPT) { + // The attribute applies to the return type. + SaveAndRestore AllowCountRedeclLocal(AllowCountRedecl, true); + QualType QT = Visit(FPT->getReturnType()); + if (QT.isNull()) + return QualType(); + + return S.Context.getFunctionType(QT, + FPT->getParamTypes(), + FPT->getExtProtoInfo()); + } + + QualType VisitFunctionNoProtoType(const FunctionNoProtoType *FPT) { + // The attribute applies to the return type. + SaveAndRestore AllowCountRedeclLocal(AllowCountRedecl, true); + QualType QT = Visit(FPT->getReturnType()); + if (QT.isNull()) + return QualType(); + + return S.Context.getFunctionNoProtoType(QT, FPT->getExtInfo()); + } + + QualType BuildDynamicBoundType(QualType CanonTy) { return QualType(); } + + QualType VisitPointerType(const PointerType *T) { + BoundsSafetyPointerAttributes FAttr = T->getPointerAttributes(); + + if (FAttr.hasUpperBound() && !AutoPtrAttributed) { + S.Diag(S.getAttrLoc(AL), + diag::err_bounds_safety_conflicting_count_bound_attributes) + << AL << (FAttr.hasLowerBound() ? 0 : 1); + return QualType(); + } + + if (Level == 0) { + if (S.getLangOpts().BoundsSafetyAttributes && !S.getLangOpts().BoundsSafety) + FAttr.setUnspecified(); + else + FAttr.setSingle(); + QualType QTy = (FAttr != T->getPointerAttributes()) + ? S.Context.getPointerType(T->getPointeeType(), FAttr) + : QualType(T, 0); + return getDerived()->BuildDynamicBoundType(QTy); + } + Level--; + llvm::SaveAndRestore AutoPtrAttributedLocal(AutoPtrAttributed, false); + QualType NewPointeeTy = Visit(T->getPointeeType()); + if (NewPointeeTy.isNull()) + return QualType(); + + if (T->getPointeeType() != NewPointeeTy) + return S.Context.getPointerType(NewPointeeTy, FAttr); + + return QualType(T, 0); + } + + QualType DiagnoseConflictingType(const CountAttributedType *T) { return QualType(); } + QualType DiagnoseConflictingType(const DynamicRangePointerType *T) { return QualType(); } + + QualType VisitCountAttributedType(const CountAttributedType *T) { + if (Level == 0) { + return getDerived()->DiagnoseConflictingType(T); + } + + QualType PointerTy = Visit(T->desugar()); + if (PointerTy.isNull()) + return QualType(); + + if (PointerTy != T->desugar()) { + return S.Context.getCountAttributedType( + PointerTy, T->getCountExpr(), T->isCountInBytes(), T->isOrNull(), + T->getCoupledDecls()); + } + return QualType(T, 0); + } + + QualType VisitDynamicRangePointerType(const DynamicRangePointerType *T) { + if (Level == 0) { + return getDerived()->DiagnoseConflictingType(T); + } + + QualType PointerTy = Visit(T->desugar()); + if (PointerTy.isNull()) + return QualType(); + + if (PointerTy != T->desugar()) { + return S.Context.getDynamicRangePointerType(PointerTy, T->getStartPointer(), + T->getEndPointer(), + T->getStartPtrDecls(), + T->getEndPtrDecls()); + } + return QualType(T, 0); + } + + QualType VisitMacroQualifiedType(const MacroQualifiedType *T) { + QualType NewTy = Visit(T->desugar()); + return S.Context.getMacroQualifiedType(NewTy, T->getMacroIdentifier()); + } + + QualType VisitArrayType(const ArrayType *T) { + return VisitType(T); + } + + QualType VisitAttributedType(const AttributedType *T) { + llvm::SaveAndRestore AutoPtrAttributedLocal(AutoPtrAttributed); + if (T->getAttrKind() == attr::PtrAutoAttr) + AutoPtrAttributed = true; + + QualType NewModTy = Visit(T->getModifiedType()); + if (NewModTy.isNull()) + return QualType(); + + if (T->getAttrKind() == attr::PtrAutoAttr) + return NewModTy; + + if (NewModTy != T->getModifiedType()) { + QualType NewEqTy = T->getModifiedType() != T->getEquivalentType() ? + Visit(T->getEquivalentType()) : NewModTy; + return S.Context.getAttributedType(T->getAttrKind(), NewModTy, NewEqTy); + } + return QualType(T, 0); + } + + QualType VisitValueTerminatedType(const ValueTerminatedType *T) { + if (Level == 0 && !AutoPtrAttributed) { + S.Diag(AL.getLoc(), diag::err_bounds_safety_terminated_by_wrong_pointer_type); + return QualType(); + } + QualType NewTy = Visit(T->desugar()); + // Drop ValueTerminatedType if there was an auto ptr attribute and we are + // making the current pointer a dynamic bound pointer. + if (Level == 0) + return NewTy; + return S.Context.getValueTerminatedType(NewTy, T->getTerminatorExpr()); + } +}; + +class ConstructCountAttributedType : + public ConstructDynamicBoundType { + bool CountInBytes; + bool OrNull; + +public: + explicit ConstructCountAttributedType(Sema &S, unsigned Level, + const ParsedAttr &AL, bool CountInBytes, + bool OrNull, bool ScopeCheck = false) + : ConstructDynamicBoundType(S, Level, AL, ScopeCheck), + CountInBytes(CountInBytes), OrNull(OrNull) { + if (!ArgExpr->getType()->isIntegralOrEnumerationType()) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_type_for_bounds_safety_count) << AL; + // Recover by using the integer literal 0 as the count. If we get to + // DefaultLvalueConversion and the count is itself a __counted_by value, + // clang will go down a fiery stack overflow. + ArgExpr = S.ActOnIntegerConstant(ArgExpr->getBeginLoc(), 0).get(); + } + } + + QualType BuildDynamicBoundType(QualType CanonTy) { + if (!CountInBytes && CanonTy->isPointerType()) { + auto PointeeTy = CanonTy->getPointeeType(); + // NOTE: We don't error for incomplete types that might be later completed + // (e.g. a struct forward declaration). Instead for those + // completable-incomplete types we delay checking until the type is used + // see `HasCountedByAttrOnIncompletePointee()`. This allows the counted_by + // attribute to be used on code that prefers to keep its pointees + // incomplete until they need to be used. + if (PointeeTy->isAlwaysIncompleteType() || PointeeTy->isFunctionType() || + PointeeTy->isSizelessType() || + PointeeTy->isStructureTypeWithFlexibleArrayMember()) { + // Use unspecified pointer attributes for diagnostic purposes. + QualType Unsp = S.Context.getBoundsSafetyPointerType( + CanonTy, BoundsSafetyPointerAttributes::unspecified()); + + auto PD = S.PDiag(diag::err_bounds_safety_counted_by_without_size); + PD << Unsp << Unsp->getPointeeType() << OrNull; + // Suggest `__sized_by` if the`__counted_by` macro was used. + // We intentionally don't suggest a fixit if the attribute is used + // directly (i.e. without the macro) because it is not expected that + // users will use it. + int SuggestFixIt = 0; // Default don't suggest __sized_by + if (S.getAttrLoc(AL).isMacroID()) { + // FIXME(dliew): Use `AL.MacroII` to get the name. Unfortunately + // `AL.MacroII` is not set so we can't simply check the macro name is + // what we expect. So instead we have the lexer tell us the contents + // of the token and check against that. + // rdar://100631458 + auto MacroName = Lexer::getImmediateMacroName( + S.getAttrLoc(AL), S.SourceMgr, S.LangOpts); + if (MacroName == "__counted_by") { + SuggestFixIt = 1; // Emit text to suggest __sized_by + auto MacroLoc = S.SourceMgr.getExpansionLoc(S.getAttrLoc(AL)); + PD << FixItHint::CreateReplacement(MacroLoc, "__sized_by"); + } else if (MacroName == "__counted_by_or_null") { + SuggestFixIt = 1; // Emit text to suggest __sized_by_or_null + auto MacroLoc = S.SourceMgr.getExpansionLoc(S.getAttrLoc(AL)); + PD << FixItHint::CreateReplacement(MacroLoc, "__sized_by_or_null"); + } + } + PD << SuggestFixIt; + S.Diag(S.getAttrLoc(AL), PD); + + // Recover by assuming a byte count. + CountInBytes = true; + } + } + QualType Ty = S.BuildCountAttributedType(CanonTy, ArgExpr, CountInBytes, + OrNull, ScopeCheck); + assert(ConstructedType == nullptr); + ConstructedType = Ty->getAs(); + return Ty; + } + + QualType DiagnoseConflictingType(const CountAttributedType *T) { + if (AllowCountRedecl) { + QualType NewTy = BuildDynamicBoundType(T->desugar()); + const auto *NewDCPTy = NewTy->getAs(); + // We don't have a way to distinguish if '__counted_by' is conflicting or has been + // actually inherited from function declaration. Thus, we are now permitting + // redundant '__counted_by' as long as the resulting count expressions are equivalent + // and picking up the later one. + llvm::FoldingSetNodeID NewID; + llvm::FoldingSetNodeID OldID; + if (const auto *NewCnt = NewDCPTy->getCountExpr()) + NewCnt->Profile(NewID, S.Context, /*Canonical*/ true); + if (const auto *OldCnt = T->getCountExpr()) + OldCnt->Profile(OldID, S.Context, /*Canonical*/ true); + + if (NewID == OldID) + return NewTy; + } + + S.Diag(S.getAttrLoc(AL), diag::err_bounds_safety_conflicting_pointer_attributes) + << /* pointer */ T->isPointerType() << /* count */ 2; + return QualType(); + } + + QualType DiagnoseConflictingType(const DynamicRangePointerType *T) { + S.Diag(S.getAttrLoc(AL), + diag::err_bounds_safety_conflicting_count_range_attributes); + return QualType(); + } + + QualType VisitArrayType(const ArrayType *T) { + if (Level == 0) { + if (T->hasAttr(attr::ArrayDecayDiscardsCountInParameters)) { + return Visit(S.Context.getArrayDecayedType(QualType(T, 0))); + } else { + S.Diag(AL.getLoc(), diag::err_bounds_safety_complete_array_with_count); + return QualType(); + } + } + return TraverseArrayType(T); + } + + QualType TraverseArrayType(const ArrayType *T) { + Level--; + llvm::SaveAndRestore AutoPtrAttributedLocal(AutoPtrAttributed, false); + QualType NewElementTy = Visit(T->getElementType()); + if (NewElementTy.isNull()) + return QualType(); + + if (T->getPointeeType() != NewElementTy) { + // Count attributes on the element of an array type are not supported yet + S.Diag(AL.getLoc(), + diag::err_multiple_coupled_decls_in_bounds_safety_dynamic_count); + return QualType(); + } + + return QualType(T, 0); + } + + QualType VisitIncompleteArrayType(const IncompleteArrayType *T) { + if (Level == 0) { + if (CountInBytes) { + S.Diag(AL.getLoc(), diag::err_bounds_safety_sized_by_array) << AL; + return QualType(); + } + + return BuildDynamicBoundType(QualType(T, 0)); + } + return TraverseArrayType(T); + } + + QualType VisitAtomicType(const AtomicType *T) { + QualType ValTy = T->getValueType(); + if (ValTy->isPointerType()) { + if (!AtomicErrorEmitted) { + unsigned DiagIndex = CountInBytes ? 3 : 2; + if (OrNull) + DiagIndex += 2; + S.Diag(S.getAttrLoc(AL), + diag::err_bounds_safety_atomic_unsupported_attribute) + << DiagIndex; + AtomicErrorEmitted = true; + } + // Construct the atomic pointer anyway. + QualType NewValTy = Visit(ValTy); + return S.Context.getAtomicType(NewValTy); + } + return VisitType(T); + } +}; + +class MakeStartedByPointerType + : public TreeTransform { + using BaseClass = TreeTransform; + bool Done = false; + unsigned Level; + TypeCoupledDeclRefInfo NewStartPtrInfo; + +public: + explicit MakeStartedByPointerType(Sema &SemaRef, unsigned Level, + TypeCoupledDeclRefInfo NewStartPtrInfo) + : BaseClass(SemaRef), Level(Level), NewStartPtrInfo(NewStartPtrInfo) {} + + Expr *BuildStartPtrExpr() const { + auto *StartVD = cast(NewStartPtrInfo.getDecl()); + Expr *StartPtr = SemaRef.BuildDeclRefExpr(StartVD, StartVD->getType(), + VK_LValue, SourceLocation()); + if (NewStartPtrInfo.isDeref()) { + StartPtr = + SemaRef.CreateBuiltinUnaryOp(SourceLocation(), UO_Deref, StartPtr) + .get(); + } + return StartPtr; + } + + QualType TransformType(TypeLocBuilder &TLB, TypeLoc TL) { + QualType T = TL.getType(); + + if (Level > 0 || T->isDynamicRangePointerType() || Done) + return BaseClass::TransformType(TLB, TL); + + if (T->isCountAttributedType()) { + SemaRef.Diag(TL.getBeginLoc(), + diag::err_bounds_safety_conflicting_count_range_attributes); + return QualType(); + } + + return BaseClass::TransformType(TLB, TL); + } + + QualType TransformType(QualType T) { return BaseClass::TransformType(T); } + + TypeSourceInfo *TransformType(TypeSourceInfo *DI) { + return BaseClass::TransformType(DI); + } + + QualType TransformDynamicRangePointerType(TypeLocBuilder &TLB, + DynamicRangePointerTypeLoc TL) { + if (Level != 0 || Done) + return BaseClass::TransformDynamicRangePointerType(TLB, TL); + Done = true; + + // OldTy is an ended_by()/started_by() pointer, add a new start pointer and + // its decls. + + const DynamicRangePointerType *OldTy = TL.getTypePtr(); + + QualType InnerTy = BaseClass::TransformType(TLB, TL.getNextTypeLoc()); + + Expr *StartPtr = OldTy->getStartPointer(); + if (!StartPtr) { + StartPtr = BuildStartPtrExpr(); + if (!StartPtr) + return QualType(); + } + + llvm::SmallVector NewStartPtrDecls; + const auto &StartPtrDecls = OldTy->getStartPtrDecls(); + NewStartPtrDecls.reserve(StartPtrDecls.size() + 1); + NewStartPtrDecls.append(StartPtrDecls.begin(), StartPtrDecls.end()); + NewStartPtrDecls.push_back(NewStartPtrInfo.getDecl()); + + QualType NewTy = SemaRef.Context.getDynamicRangePointerType( + InnerTy, StartPtr, OldTy->getEndPointer(), NewStartPtrDecls, + OldTy->getEndPtrDecls()); + TLB.push(NewTy); + return NewTy; + } + + QualType TransformAttributedType(TypeLocBuilder &TLB, AttributedTypeLoc TL) { + // Strip attr::PtrAutoAttr because the pointer is now marked __started_by + // as it is referred to by __ended_by. + const AttributedType *oldType = TL.getTypePtr(); + if (oldType->getAttrKind() == attr::PtrAutoAttr && Level == 0 && !Done) { + return TransformType(TLB, TL.getModifiedLoc()); + } + return BaseClass::TransformAttributedType(TLB, TL); + } + + QualType TransformPointerType(TypeLocBuilder &TLB, PointerTypeLoc TL) { + llvm::SaveAndRestore LevelLocal(Level); + llvm::SaveAndRestore DoneLocal(Done); + if (!Done && Level == 0) { + Expr *StartPtr = BuildStartPtrExpr(); + if (!StartPtr) + return QualType(); + Done = true; + + QualType T = BaseClass::TransformPointerType(TLB, TL); + + QualType NewTy = SemaRef.Context.getDynamicRangePointerType( + T, StartPtr, /*EndPtr=*/nullptr, {NewStartPtrInfo}, {}); + TLB.push(NewTy); + return NewTy; + } + if (!Done) { + assert(Level > 0); + --Level; + } + return BaseClass::TransformPointerType(TLB, TL); + } +}; + +class ConstructDynamicRangePointerType : + public ConstructDynamicBoundType { + using BaseClass = ConstructDynamicBoundType; + std::optional StartPtrInfo; + +public: + explicit ConstructDynamicRangePointerType( + Sema &S, unsigned Level, const ParsedAttr &AL, bool ScopeCheck = false, + std::optional StartPtrInfo = std::nullopt) + : ConstructDynamicBoundType(S, Level, AL, ScopeCheck), + StartPtrInfo(StartPtrInfo) { + assert(ArgExpr->getType()->isPointerType()); + } + + QualType BuildDynamicBoundType(QualType CanonTy) { + QualType Ty = + S.BuildDynamicRangePointerType(CanonTy, nullptr, ArgExpr, ScopeCheck); + if (Ty.isNull()) + return QualType(); + + if (StartPtrInfo.has_value()) + MakeStartedByPointerTypes(cast(Ty)); + + assert(ConstructedType == nullptr); + ConstructedType = Ty->getAs(); + return Ty; + } + + void MakeStartedByPointerTypes(const DynamicRangePointerType *DRPT) const { + for (auto EndPtrInfo : DRPT->endptr_decls()) { + ValueDecl *EndPtrDecl = EndPtrInfo.getDecl(); + unsigned EndPtrLevel = EndPtrInfo.isDeref() ? 1 : 0; + + QualType Ty = EndPtrDecl->getType(); + assert(Ty->isSinglePointerType() || Ty->isUnspecifiedPointerType() || + Ty->isDynamicRangePointerType() || Ty->hasAttr(attr::PtrAutoAttr)); + if (!S.getLangOpts().isBoundsSafetyAttributeOnlyMode() && + !Ty->isSinglePointerType()) { + Ty = S.Context.getBoundsSafetyPointerType( + Ty, BoundsSafetyPointerAttributes::single()); + } + + MakeStartedByPointerType TT(S, EndPtrLevel, *StartPtrInfo); + QualType NewTy = TT.TransformType(Ty); + EndPtrDecl->setType(NewTy); + } + } + + QualType VisitDynamicRangePointerType(const DynamicRangePointerType *T) { + if (Level == 0 && T->getEndPointer() == nullptr) { + // T is a started_by() pointer type. + Expr *StartPtr = T->getStartPointer(); + auto StartPtrDecls = T->getStartPtrDecls(); + assert(StartPtr); + + assert(ConstructedType == nullptr); + // Construct an ended_by() pointer type. + QualType Ty = Visit(T->desugar()); + if (Ty.isNull()) + return QualType(); + + const auto *DRPT = cast(Ty); + assert(!DRPT->getStartPointer()); + Expr *EndPtr = DRPT->getEndPointer(); + auto EndPtrDecls = DRPT->getEndPtrDecls(); + assert(EndPtr); + + // ConstructType was already set while visiting the nested PointerType. + // Reconstruct DRPT by merging started_by and ended_by. + assert(ConstructedType == DRPT); + QualType RetTy = S.Context.getDynamicRangePointerType( + DRPT->desugar(), StartPtr, EndPtr, StartPtrDecls, EndPtrDecls); + ConstructedType = RetTy->getAs(); + return RetTy; + } + return BaseClass::VisitDynamicRangePointerType(T); + } + + QualType DiagnoseConflictingType(const CountAttributedType *T) { + S.Diag(S.getAttrLoc(AL), + diag::err_bounds_safety_conflicting_count_range_attributes); + return QualType(); + } + + QualType DiagnoseConflictingType(const DynamicRangePointerType *T) { + S.Diag(S.getAttrLoc(AL), diag::err_bounds_safety_conflicting_pointer_attributes) + << /* pointer */ 1 << /* end */ 3; + return QualType(); + } + + QualType VisitAtomicType(const AtomicType *T) { + QualType ValTy = T->getValueType(); + if (ValTy->isPointerType()) { + if (!AtomicErrorEmitted) { + S.Diag(S.getAttrLoc(AL), + diag::err_bounds_safety_atomic_unsupported_attribute) + << /*ended_by*/ 6; + AtomicErrorEmitted = true; + } + // Construct the atomic pointer anyway. + QualType NewValTy = Visit(ValTy); + return S.Context.getAtomicType(NewValTy); + } + return VisitType(T); + } +}; +} // namespace + +Sema::LifetimeCheckKind Sema::getLifetimeCheckKind(const VarDecl *Var) { + if (!Var) + return Sema::LifetimeCheckKind::None; + + if (Var->getStorageClass() == SC_Extern) + return Sema::LifetimeCheckKind::Extern; + if (Var->getStorageClass() == SC_PrivateExtern) + return Sema::LifetimeCheckKind::PrivateExtern; + if (Var->isLocalVarDecl()) { + if (Var->hasLocalStorage()) + return Sema::LifetimeCheckKind::NonStaticLocal; + else if (Var->isStaticLocal()) + return Sema::LifetimeCheckKind::StaticLocal; + } else if (Var->hasGlobalStorage()) { + if (Var->getStorageClass() == SC_Static) + return Sema::LifetimeCheckKind::StaticGlobal; + else + return Sema::LifetimeCheckKind::GlobalDefinition; + } + + return Sema::LifetimeCheckKind::None; +} + +namespace { + +class CollectBoundsSafetyImplicitOVEs + : public RecursiveASTVisitor { + std::set OVEs; + +public: + std::set collect(Expr *E) { + OVEs.clear(); + TraverseStmt(E); + return std::move(OVEs); + } + + bool VisitMaterializeSequenceExpr(const MaterializeSequenceExpr *E) { + if (E->isBinding()) + OVEs.insert(E->opaquevalues_begin(), E->opaquevalues_end()); + return true; + } + + bool VisitBoundsCheckExpr(const BoundsCheckExpr *E) { + OVEs.insert(E->opaquevalues_begin(), E->opaquevalues_end()); + return true; + } +}; + +class RemoveUnusedOVEs : public TreeTransform { + const std::set &UnusedOVEs; + +public: + explicit RemoveUnusedOVEs(Sema &SemaRef, + const std::set &UnusedOVEs) + : TreeTransform(SemaRef), UnusedOVEs(UnusedOVEs) {} + + ExprResult TransformOpaqueValueExpr(OpaqueValueExpr *E) { + if (RemovedOVEs.count(E) || UnusedOVEs.count(E)) + return TransformExpr(E->getSourceExpr()); + + assert((!E->getSourceExpr() || AlreadyTransformed(E->getType())) && + "opaque value expression requires transformation"); + return E; + } +}; + +// Collect the opaque values bound by MaterializeSequenceExpr and +// BoundsCheckExpr in OldE. Remove them from NewE if they are not bound by any +// MaterializeSequenceExpr or BoundsCheckExpr (i.e., they are unused). This is a +// workaround, because -fbounds-safety might leave some unused opaque values after +// dropping -fbounds-safety implicit casts (e.g., BoundsSafetyPointerPromotionExpr). +// Here, we don't drop every unused opaque value, but only those created by +// -fbounds-safety. +// rdar://113718929 +ExprResult removeUnusedOVEs(Sema &SemaRef, Expr *OldE, Expr *NewE) { + CollectBoundsSafetyImplicitOVEs Collector; + const auto OldOVEs = Collector.collect(OldE); + const auto NewOVEs = Collector.collect(NewE); + std::set UnusedOVEs; + std::set_difference(OldOVEs.begin(), OldOVEs.end(), NewOVEs.begin(), + NewOVEs.end(), + std::inserter(UnusedOVEs, UnusedOVEs.begin())); + if (UnusedOVEs.empty()) + return NewE; + RemoveUnusedOVEs Remover(SemaRef, UnusedOVEs); + return Remover.TransformExpr(NewE); +} + +} // namespace + +void Sema::AttachDependerDeclsAttr( + ValueDecl *NewDepender, const CountAttributedType *NewDependerCountTy, + unsigned Level) { + assert(NewDepender && NewDependerCountTy); + for (const TypeCoupledDeclRefInfo &DepDeclInfo : + NewDependerCountTy->dependent_decls()) { + Decl *Dependee = DepDeclInfo.getDecl(); + llvm::SmallVector DependerDecls; + llvm::SmallVector DependerLevels; + + if (auto *Att = Dependee->getAttr()) { + assert(Att->getIsDeref() == DepDeclInfo.isDeref()); + DependerDecls.assign(Att->dependerDecls_begin(), + Att->dependerDecls_end()); + DependerLevels.assign(Att->dependerLevels_begin(), + Att->dependerLevels_end()); + } + + DependerDecls.push_back(NewDepender); + DependerLevels.push_back(Level); + Dependee->dropAttr(); + Dependee->addAttr(DependerDeclsAttr::CreateImplicit( + Context, DepDeclInfo.isDeref(), DependerDecls.data(), + DependerDecls.size(), DependerLevels.data(), DependerLevels.size())); + } +} + +// LifetimeCheck on local variable implies ScopeCheck. +LLVM_ATTRIBUTE_UNUSED inline bool +shouldImplyScopeCheck(Sema::LifetimeCheckKind Kind) { + return Kind == Sema::LifetimeCheckKind::StaticLocal || + Kind == Sema::LifetimeCheckKind::NonStaticLocal; +} + +static bool CheckArgLifetimeAndScope( + Sema &SemaRef, const ValueDecl *VD, SourceLocation ExprLoc, bool ScopeCheck, + Sema::LifetimeCheckKind LifetimeCheck, + BoundsAttributedType::BoundsAttrKind AttrKind, bool IsArray = false) { + + assert(!shouldImplyScopeCheck(LifetimeCheck) || ScopeCheck); + + bool Invalid = false; + // We need scope check when the type is initially constructed + // for local variable. + if (ScopeCheck && !SemaRef.getCurScope()->isDeclScope(VD)) { + Invalid = true; + SemaRef.Diag(ExprLoc, diag::err_bounds_safety_dynamic_bound_arg_different_scope) + << AttrKind; + } + + // Check if the argument of __counted_by/__sized_by has the same lifetime + // as the annotated variable. + if (LifetimeCheck == Sema::LifetimeCheckKind::None) + return Invalid; + + if (const auto *Arg = dyn_cast(VD)) { + Sema::LifetimeCheckKind ArgLifetimeCheck = Sema::getLifetimeCheckKind(Arg); + if (LifetimeCheck != ArgLifetimeCheck) { + auto IsGlobalDefinition = [](Sema::LifetimeCheckKind Kind) { + return Kind == Sema::LifetimeCheckKind::GlobalDefinition; + }; + auto IsExtern = [](Sema::LifetimeCheckKind Kind) { + return Kind == Sema::LifetimeCheckKind::Extern; + }; + + if ((IsGlobalDefinition(ArgLifetimeCheck) && IsExtern(LifetimeCheck)) || + (IsExtern(ArgLifetimeCheck) && IsGlobalDefinition(LifetimeCheck))) { + if (!IsArray) { + Invalid = true; + SemaRef.Diag(ExprLoc, diag::err_bounds_safety_global_dynamic_bound) + << AttrKind; + } else { + assert(AttrKind != BoundsAttributedType::BoundsAttrKind::EndedBy); + assert(AttrKind != + BoundsAttributedType::BoundsAttrKind::CountedByOrNull); + assert(AttrKind != + BoundsAttributedType::BoundsAttrKind::SizedByOrNull); + SemaRef.Diag(ExprLoc, diag::warn_bounds_safety_extern_array_dynamic_count) + << AttrKind; + } + } else { + Invalid = true; + SemaRef.Diag(ExprLoc, + diag::err_bounds_safety_dynamic_bound_arg_different_lifetime) + << AttrKind; + } + } + } + return Invalid; +} + +static bool diagnoseBoundsAttrLifetimeAndScope( + Sema &SemaRef, const BoundsAttributedType *Ty, bool ScopeCheck, + Sema::LifetimeCheckKind LifetimeCheck) { + assert(Ty); + bool HadError = false; + if (const auto *CAT = dyn_cast(Ty)) { + for (const TypeCoupledDeclRefInfo &DepDeclInfo : CAT->dependent_decls()) { + const auto *Dependee = cast(DepDeclInfo.getDecl()); + // Variables marked as const or __unsafe_late_const are the exceptions + // that are allowed to be referred to by bounds annotations on a struct + // field or a function parameter. Thereby, skip diagnosis here. + if (clang::IsConstOrLateConst(Dependee)) + continue; + + HadError |= CheckArgLifetimeAndScope( + SemaRef, Dependee, CAT->getCountExpr()->getExprLoc(), ScopeCheck, + LifetimeCheck, CAT->getKind(), CAT->isArrayType()); + } + } else if (const auto *RAT = dyn_cast(Ty)) { + for (const TypeCoupledDeclRefInfo &EndPtrInfo : RAT->endptr_decls()) { + const auto *EndPtr = cast(EndPtrInfo.getDecl()); + if (clang::IsConstOrLateConst(EndPtr)) + continue; + + HadError |= CheckArgLifetimeAndScope( + SemaRef, EndPtr, RAT->getEndPointer()->getExprLoc(), ScopeCheck, + LifetimeCheck, RAT->getKind()); + } + } else { + llvm_unreachable("Unexpected bounds attribute kind"); + } + return HadError; +} + +unsigned TransitiveFieldCopyCount(const RecordDecl *RD, + const FieldDecl *Inner) { + auto DeclCtx = Inner->getDeclContext(); + if (DeclCtx == RD) + return 1; + unsigned Count = 0; + for (auto FD : RD->fields()) { + if (auto InnerStructTy = FD->getType()->getAs()) { + Count += TransitiveFieldCopyCount( + cast(InnerStructTy->getDecl()), Inner); + } + } + return Count; +} + +/// Diagnose compatibility between a declaration whose type is annotated with +/// \c counted_by or \c sized_by and the declarations referred to by the annotation. +/// +/// \param TheDepender Decl whose type contains a \c counted_by or \c sized_by +/// attribute. +/// +/// \param Level Indicates the nested level of \p PointerTy in \c +/// TheDepender->getType() . +/// +/// \param IsFPtr The attribute is added to a function pointer type. +/// +/// \returns \c true if the function emitted an error. +static bool diagnoseCountDependentDecls(Sema &S, const ValueDecl *TheDepender, + const CountAttributedType *PointerTy, + unsigned Level, bool IsFPtr) { + assert(TheDepender && PointerTy); + bool HadError = false; + for (const TypeCoupledDeclRefInfo &DepDeclInfo : + PointerTy->dependent_decls()) { + Decl *Dependee = DepDeclInfo.getDecl(); + // Variables marked as const or __unsafe_late_const are the exceptions that + // are allowed to be referred to by bounds annotations on a struct field + // or a function parameter. Thereby, skip diagnosis here. + if (Level == 0 && clang::IsConstOrLateConst(Dependee)) + continue; + + if (auto *Att = Dependee->getAttr()) { + if (!Att->isInherited() || isa(Dependee)) { + bool HadSharedDeclError = false; + for (unsigned i = 0; i < Att->dependerDecls_size(); ++i) { + const auto *OtherDepender = + cast(Att->dependerDecls_begin()[i]); + if (TheDepender != OtherDepender) { + QualType OtherDependerTy = OtherDepender->getType(); + + // Local variables are not allowed to share the same count variable. + const auto *DependeeVD = dyn_cast(Dependee); + if (DependeeVD && DependeeVD->isLocalVarDecl()) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_bounds_safety_external_bound_share) + << DependeeVD << /*variable*/ 0 << PointerTy->getKind(); + + if (unsigned DependerLevel = Att->dependerLevels_begin()[i]) { + assert(DependerLevel == 1); + OtherDependerTy = OtherDependerTy->getPointeeType(); + } + + const auto *DependerDCPT = + OtherDependerTy->getAs(); + assert(DependerDCPT); + S.Diag(DependerDCPT->getCountExpr()->getExprLoc(), + diag::note_previous_use); + HadError = HadSharedDeclError = true; + break; + } + + // Flexible array members are not allowed to share count with other + // fields + if (OtherDependerTy->isArrayType() && PointerTy->isArrayType()) { + if (auto FD = dyn_cast(TheDepender)) { + const RecordDecl *RD = FD->getParent(); + // FAM cannot share length field with other __counted_by. + // This filters out the case where FAMs in different struct + // types share a length field in a common nested struct. I.e. + // struct A and B both contain struct C. The FAMs in A and B + // both refer to the length field in C. The two FAMs do not + // exist in the same struct so the length isn't truly shared. In + // this case RD may be A, and OtherDepender may be the FAM + // in B. + if (RD->isParentStructOf(OtherDepender)) { + S.Diag(TheDepender->getLocation(), + diag::err_bounds_safety_external_bound_share) + << cast(Dependee) << /*field*/ 1 + << PointerTy->getKind(); + if (auto DependerLevel = Att->dependerLevels_begin()[i]) { + if (DependerLevel == 1) + OtherDependerTy = OtherDependerTy->getPointeeType(); + } + const auto *DependerDCPT = + OtherDependerTy->getAs(); + const auto *CountExpr = DependerDCPT->getCountExpr(); + S.Diag(CountExpr->getExprLoc(), diag::note_previous_use) + << CountExpr->getSourceRange(); + HadError = HadSharedDeclError = true; + break; + } + } + } + } + } + if (HadSharedDeclError) + continue; + } + } + if (auto FD = dyn_cast(TheDepender)) { + const RecordDecl *RD = FD->getParent(); + if (auto FD = dyn_cast(Dependee)) { + if (TransitiveFieldCopyCount(RD, FD) > 1) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_bounds_safety_dependent_field_duplicates) + << FD << RD; + HadError = true; + continue; + } + } + } + if (!IsFPtr && isa(TheDepender) && !isa(Dependee)) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_invalid_decl_kind_bounds_safety_dynamic_count) + << 0 /*Field*/; + HadError = true; + continue; + } + if ((IsFPtr || isa(TheDepender)) && + !isa(Dependee)) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_invalid_decl_kind_bounds_safety_dynamic_count) + << 1 /*Param*/; + HadError = true; + continue; + } + } + return HadError; +} + +/// Diagnose compatibility between a declaration whose type is annotated with +/// \c ended_by and the declarations referred to by the annotation. +/// +/// \param TheDepender Decl whose type contains a \c ended_by attribute. +/// +/// \param Level Indicates the nested level of \p PointerTy in \c +/// TheDepender->getType() . +/// +/// \param IsFPtr The attribute is added to a function pointer type. +/// +/// \returns \c true if the function emitted an error. +static bool +diagnoseRangeDependentDecls(Sema &S, const ValueDecl *TheDepender, + const DynamicRangePointerType *PointerTy, + unsigned Level, bool IsFPtr) { + assert(TheDepender && PointerTy); + bool HadError = false; + for (const TypeCoupledDeclRefInfo &EndPtrInfo : PointerTy->endptr_decls()) { + // Variables marked as const or __unsafe_late_const are the exceptions that + // are allowed to be referred to by bounds annotations on a struct field + // or a function parameter. Thereby, skip diagnosis here. + if (Level == 0 && clang::IsConstOrLateConst(EndPtrInfo.getDecl())) + continue; + + if (!IsFPtr && isa(TheDepender) && + !isa(EndPtrInfo.getDecl())) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_invalid_decl_kind_bounds_safety_dynamic_count) + << 0; + HadError = true; + continue; + } + if ((IsFPtr || isa(TheDepender)) && + !isa(EndPtrInfo.getDecl())) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_invalid_decl_kind_bounds_safety_dynamic_count) + << 1; + HadError = true; + continue; + } + QualType EndPtrTy = EndPtrInfo.getDecl()->getType(); + if (EndPtrInfo.isDeref()) { + EndPtrTy = EndPtrTy->getPointeeType(); + } + if (auto *EndDRPT = EndPtrTy->getAs()) { + for (auto StartPtrInfo : EndDRPT->startptr_decls()) { + if (StartPtrInfo.getDecl() != TheDepender) { + S.Diag(TheDepender->getBeginLoc(), + diag::err_bounds_safety_external_bound_share) + << EndPtrInfo.getDecl() << /*variable*/ 0 << /* ended_by */ 4; + QualType PrevUserTy = StartPtrInfo.getDecl()->getType(); + + if (StartPtrInfo.isDeref()) { + PrevUserTy = PrevUserTy->getPointeeType(); + } + auto *PrevUserDRPT = PrevUserTy->getAs(); + assert(PrevUserDRPT && PrevUserDRPT->getEndPointer()); + S.Diag(PrevUserDRPT->getEndPointer()->getExprLoc(), + diag::note_previous_use); + HadError = true; + continue; + } + } + } + } + return HadError; +} + +static void handlePtrCountedByEndedByAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + unsigned Level; + if (!S.checkUInt32Argument(AL, AL.getArgAsExpr(1), Level)) + return; + // If the decl is invalid, the indirection Level might not exist in the type, + // since the type may have not been constructed correctly. Example: + // 'int (*param)[__counted_by_or_null(10)][]' + // Since you cannot create an array of unsized arrays, the DeclTy ends + // up being 'int *'. The attribute's Level is 1, but we only have level 0. + if (D->isInvalidDecl()) + return; + + auto *TND = dyn_cast(D); + auto *VD = dyn_cast(D); + auto *Var = dyn_cast(D); + QualType DeclTy = TND ? TND->getUnderlyingType() : VD->getType(); + + bool IsFPtr = false; + unsigned EffectiveLevel = Level; + QualType Ty = DeclTy; + for (unsigned i = 0; i != Level; ++i) { + if (!Ty->isPointerType()) + break; + Ty = Ty->getPointeeType(); + if (Ty->isFunctionType()) { + IsFPtr = true; + EffectiveLevel = Level - i - 1; + break; + } + } + + // Don't allow typedefs with __counted_by on non-function types. + if (TND && (!DeclTy->isFunctionType() && !IsFPtr)) { + S.Diag(AL.getLoc(), diag::err_bounds_safety_typedef_dynamic_bound) << AL; + return; + } + + bool CountInBytes = false; + bool IsEndedBy = false; + bool OrNull = false; + switch (AL.getKind()) { + case ParsedAttr::AT_SizedBy: + CountInBytes = true; + break; + case ParsedAttr::AT_SizedByOrNull: + CountInBytes = true; + OrNull = true; + break; + case ParsedAttr::AT_CountedBy: + break; + case ParsedAttr::AT_CountedByOrNull: + OrNull = true; + break; + case ParsedAttr::AT_PtrEndedBy: + IsEndedBy = true; + break; + default: + llvm_unreachable("Invalid dynamic bound attribute"); + } + + if (!IsEndedBy) { + // Nullability as indicated by _Nonnull or _Nullable. Does not impact + // semantics, only warnings. + std::optional AttrNullability = DeclTy->getNullability(); + if (OrNull) { + // Function parameter/return value attribute that *does* impact semantics, + // letting the compiler elide null checks. This could remove bounds safety + // checks, so using it together with __counted_by_or_null is not safe. + // The nonnull attribute does not apply to return values, so ignore that + // if used as a function attribute: in that case the parameters will be + // checked in attrNonNullArgCheck & handleNonNullAttr. + if (auto NNAttr = D->getAttr(); + NNAttr && isa(D)) { + S.Diag(AL.getLoc(), + diag::err_bounds_safety_nullable_dynamic_count_nonnullable) + << CountInBytes << NNAttr << AL.getRange() << NNAttr->getRange(); + return; + } + if (auto RNNAttr = D->getAttr()) { + S.Diag(AL.getLoc(), + diag::err_bounds_safety_nullable_dynamic_count_nonnullable) + << CountInBytes << RNNAttr << AL.getRange() << RNNAttr->getRange(); + return; + } + + if (AttrNullability == NullabilityKind::NonNull) { + S.Diag(AL.getLoc(), + diag::warn_bounds_safety_nullable_dynamic_count_nonnullable) + << CountInBytes << AL; + } + } + + if (auto CountArg = AL.getArgAsExpr(0)->getIntegerConstantExpr(S.Context)) { + if (CountArg > 0 && !OrNull && + AttrNullability == NullabilityKind::Nullable) + S.Diag(AL.getArgAsExpr(0)->getExprLoc(), + diag::warn_bounds_safety_nonnullable_dynamic_count_nullable) + << CountInBytes << AL.getRange(); + } + } + + if (VD) { + const auto *FD = dyn_cast(VD); + if (FD && FD->getParent()->isUnion()) { + S.Diag(AL.getLoc(), diag::err_invalid_decl_kind_bounds_safety_union_count) + << AL; + return; + } + + if (EffectiveLevel != 0 && + (!isa(VD) || DeclTy->isBoundsAttributedType())) { + S.Diag(AL.getLoc(), diag::err_bounds_safety_nested_dynamic_bound) << AL; + return; + } + + // Clang causes array parameters to decay to pointers so quickly that + // attributes aren't even parsed yet. This causes arrays with both an + // explicit size and a count attribute to go to the CountAttributedType + // case of ConstructCountAttributedType, which complains that the type + // has two count attributes. See if we can produce a better diagnostic here + // instead. + if (const auto *PVD = dyn_cast_or_null(Var)) { + QualType TSITy = PVD->getTypeSourceInfo()->getType(); + if (IsEndedBy) { + if (Level == 0 && TSITy->isArrayType()) { + S.Diag(AL.getLoc(), diag::err_attribute_pointers_only) << AL << 0; + return; + } + } else { + const auto *ATy = S.Context.getAsArrayType(TSITy); + if (Level == 0 && ATy && !ATy->isIncompleteArrayType() && + !TSITy->hasAttr(attr::ArrayDecayDiscardsCountInParameters)) { + S.Diag(AL.getLoc(), diag::err_bounds_safety_complete_array_with_count); + return; + } + } + } + + if (Ty->isArrayType() && OrNull && + (FD || EffectiveLevel > 0 || (Var && Var->hasExternalStorage()))) { + auto ErrDiag = S.Diag(AL.getLoc(), diag::err_bounds_safety_nullable_fam); + // Pointers to dynamic count types are only allowed for parameters, so any + // FieldDecl containing a dynamic count type is a FAM. I.e. a struct field + // with type 'int(*)[__counted_by(...)]' is not valid. + ErrDiag << CountInBytes << /*is FAM?*/ !!FD << AL; + assert(!FD || EffectiveLevel == 0); + + SourceLocation FixItLoc = + S.getSourceManager().getExpansionLoc(AL.getLoc()); + SourceLocation EndLoc = + Lexer::getLocForEndOfToken(FixItLoc, /* Don't include '(' */ -1, + S.getSourceManager(), S.getLangOpts()); + std::string Attribute = CountInBytes ? "__sized_by" : "__counted_by"; + ErrDiag << FixItHint::CreateReplacement({FixItLoc, EndLoc}, Attribute); + + return; + } + + if (Ty->isArrayType() && EffectiveLevel > 0) { + auto ErrDiag = + S.Diag( + AL.getLoc(), + diag::err_bounds_safety_unsupported_address_of_incomplete_array_type) + << Ty; + // apply attribute anyways to avoid too misleading follow-up diagnostics + } + } + + QualType NewDeclTy{}; + bool ScopeCheck = (Var && Var->isLocalVarDecl()) || IsFPtr; + const BoundsAttributedType *ConstructedType = nullptr; + + bool HadAtomicError = false; + if (IsEndedBy) { + const Expr *ArgExpr = AL.getArgAsExpr(0); + if (ArgExpr && ArgExpr->getType()->isAtomicType()) { + S.Diag(AL.getLoc(), diag::err_bounds_safety_atomic_unsupported_attribute) + << /*started_by*/ 8; + return; + } + if (!ArgExpr || !ArgExpr->getType()->isPointerType()) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_type_for_bounds_safety_range) + << AL; + return; + } + + // Make started_by() pointers if VD is a field or variable. We don't want to + // create started_by(X) pointers where X is a function etc. + std::optional StartPtrInfo; + if (VD && (isa(VD) || isa(VD))) { + assert(Level <= 1); + StartPtrInfo = TypeCoupledDeclRefInfo(VD, /*Deref=*/Level != 0); + } + + auto TypeConstructor = ConstructDynamicRangePointerType( + S, Level, AL, ScopeCheck, StartPtrInfo); + NewDeclTy = TypeConstructor.Visit(DeclTy); + HadAtomicError = TypeConstructor.hadAtomicError(); + ConstructedType = TypeConstructor.getConstructedType(); + } else { + auto TypeConstructor = ConstructCountAttributedType( + S, Level, AL, CountInBytes, OrNull, ScopeCheck); + NewDeclTy = TypeConstructor.Visit(DeclTy); + HadAtomicError = TypeConstructor.hadAtomicError(); + ConstructedType = TypeConstructor.getConstructedType(); + } + + if (NewDeclTy.isNull()) + return; + + // `NewDeclTy` is the new type of the declaration `D`, whereas `ConstructedType` is exact + // `BoundsAttributedType` that's created for this attribute. The bounds + // attribute can be added to a nested pointer or some nested type, so + // `ConstructedType` can be different from `NewDeclTy`. + assert(ConstructedType); + + auto LifetimeCheck = + IsFPtr ? Sema::LifetimeCheckKind::None : Sema::getLifetimeCheckKind(Var); + if (diagnoseBoundsAttrLifetimeAndScope(S, ConstructedType, ScopeCheck, + LifetimeCheck)) + return; + + if (VD && !isa(VD) && !HadAtomicError) { + if (const auto *BDTy = dyn_cast(ConstructedType)) { + if (!diagnoseCountDependentDecls(S, VD, BDTy, EffectiveLevel, IsFPtr)) + S.AttachDependerDeclsAttr(VD, BDTy, EffectiveLevel); + } else if (const auto *BDTy = + dyn_cast(ConstructedType)) { + diagnoseRangeDependentDecls(S, VD, BDTy, EffectiveLevel, IsFPtr); + } else { + llvm_unreachable("Unexpected bounds attributed type"); + } + } + + if (TND) { + // We need to create a TypeSourceInfo, as TypedefNameDecl is not a ValueDecl + // and thus doesn't have setType() method. + SourceLocation Loc = TND->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); + TypeSourceInfo *Info = S.Context.getTrivialTypeSourceInfo(NewDeclTy, Loc); + TND->setTypeSourceInfo(Info); + } else { + VD->setType(NewDeclTy); + // Reconstruct implicit cast for initializer after variable type change. + if (Var && Var->hasInit()) { + Expr *Init = Var->getInit(); + ExprResult Res = removeUnusedOVEs(S, Init, Init->IgnoreImpCasts()); + if (Res.isInvalid()) + return; + S.AddInitializerToDecl(Var, Res.get(), Var->isDirectInit()); + } + } +} + +static void handleUnsafeLateConst(Sema &S, Decl *D, + const ParsedAttr &AL) { + D->addAttr(::new (S.Context) UnsafeLateConstAttr(S.Context, AL)); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + static void handlePatchableFunctionEntryAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (S.Context.getTargetInfo().getTriple().isOSAIX()) { @@ -6424,7 +7844,19 @@ static void handleZeroCallUsedRegsAttr(Sema &S, Decl *D, const ParsedAttr &AL) { static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) { auto *FD = dyn_cast(D); - assert(FD); + /* TO_UPSTREAM(BoundsSafety) ON */ + // In upstream 'counted_by' is subjected to FieldDecl only, whereas internally, + // the attribute applies to any other subjects. This code is to manually handle + // cases when the counted_by is applied to non-field decl without + // -fbounds-safety. + if (!isa(D)) { + S.Diag(AL.getLoc(), diag::err_attribute_wrong_decl_type_str) + << AL.getAttrName()->getName() << AL.isRegularKeywordAttribute() + << "non-static data members"; + AL.setInvalid(); + return; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ auto *CountExpr = AL.getArgAsExpr(0); if (!CountExpr) @@ -7315,6 +8747,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, S.ObjC().handleDirectMembersAttr(D, AL); handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_ObjCCompleteDefinition: + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_ObjCExplicitProtocolImpl: S.ObjC().handleSuppresProtocolAttr(D, AL); break; @@ -7360,6 +8795,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_NoDebug: handleNoDebugAttr(S, D, AL); break; + case ParsedAttr::AT_TransparentStepping: + S.Swift().handleTransparentStepping(D, AL); + break; case ParsedAttr::AT_CmseNSEntry: S.ARM().handleCmseNSEntryAttr(D, AL); break; @@ -7436,9 +8874,25 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_CountedByOrNull: case ParsedAttr::AT_SizedBy: case ParsedAttr::AT_SizedByOrNull: + /* TO_UPSTREAM(BoundsSafety) ON */ + if (S.getLangOpts().BoundsSafetyAttributes) { + handlePtrCountedByEndedByAttr(S, D, AL); + break; + } + /* TO_UPSTREAM(BoundsSafety) OFF */ handleCountedByAttrField(S, D, AL); break; + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety attributes: + case ParsedAttr::AT_PtrEndedBy: + handlePtrCountedByEndedByAttr(S, D, AL); + break; + case ParsedAttr::AT_UnsafeLateConst: + handleUnsafeLateConst(S, D, AL); + break; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Microsoft attributes: case ParsedAttr::AT_LayoutVersion: handleLayoutVersion(S, D, AL); @@ -7644,6 +9098,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_AvailabilityDomain: + handleAvailabilityDomainAttr(S, D, AL); + break; + case ParsedAttr::AT_ArmNew: S.ARM().handleNewAttr(D, AL); break; @@ -8093,6 +9551,12 @@ void Sema::PopParsingDeclaration(ParsingDeclState state, Decl *decl) { handleDelayedAvailabilityCheck(diag, decl); break; + case DelayedDiagnostic::FeatureAvailability: + // Don't bother giving diagnostics if the decl is invalid. + if (!decl->isInvalidDecl()) + handleDelayedFeatureAvailabilityCheck(diag, decl); + break; + case DelayedDiagnostic::Access: // Only produce one access control diagnostic for a structured binding // declaration: we don't need to tell the user that all the fields are diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 0a14ce23a396e..a703de37956dc 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -20,6 +20,7 @@ #include "clang/AST/ExprObjC.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Edit/RefactoringFixits.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Initialization.h" @@ -1050,6 +1051,9 @@ ObjCInterfaceDecl *SemaObjC::ActOnStartClassInterface( ObjCInterfaceDecl *IDecl = ObjCInterfaceDecl::Create(Context, SemaRef.CurContext, AtInterfaceLoc, ClassName, typeParamList, PrevIDecl, ClassLoc); + SemaRef.ProcessDeclAttributeList(SemaRef.TUScope, IDecl, AttrList); + SemaRef.ProcessAPINotes(IDecl); + if (PrevIDecl) { // Class already seen. Was it a definition? if (ObjCInterfaceDecl *Def = PrevIDecl->getDefinition()) { @@ -1066,9 +1070,7 @@ ObjCInterfaceDecl *SemaObjC::ActOnStartClassInterface( } } - SemaRef.ProcessDeclAttributeList(SemaRef.TUScope, IDecl, AttrList); SemaRef.AddPragmaAttributes(SemaRef.TUScope, IDecl); - SemaRef.ProcessAPINotes(IDecl); // Merge attributes from previous declarations. if (PrevIDecl) @@ -1179,8 +1181,9 @@ Decl *SemaObjC::ActOnCompatibilityAlias(SourceLocation AtLoc, } // Everything checked out, instantiate a new alias declaration AST. - ObjCCompatibleAliasDecl *AliasDecl = ObjCCompatibleAliasDecl::Create( - Context, SemaRef.CurContext, AtLoc, AliasName, CDecl); + ObjCCompatibleAliasDecl *AliasDecl = + ObjCCompatibleAliasDecl::Create(Context, SemaRef.CurContext, AliasLocation, + AliasName, CDecl, ClassLocation, AtLoc); if (!CheckObjCDeclScope(AliasDecl)) SemaRef.PushOnScopeChains(AliasDecl, SemaRef.TUScope); @@ -1893,6 +1896,7 @@ ObjCCategoryDecl *SemaObjC::ActOnStartCategoryInterface( // checking for protocol uses. SemaRef.ProcessDeclAttributeList(SemaRef.TUScope, CDecl, AttrList); SemaRef.AddPragmaAttributes(SemaRef.TUScope, CDecl); + SemaRef.copyFeatureAvailabilityCheck(CDecl, IDecl); if (NumProtoRefs) { diagnoseUseOfProtocols(SemaRef, CDecl, (ObjCProtocolDecl *const *)ProtoRefs, @@ -1948,6 +1952,8 @@ ObjCCategoryImplDecl *SemaObjC::ActOnStartCategoryImplementation( SemaRef.ProcessDeclAttributeList(SemaRef.TUScope, CDecl, Attrs); SemaRef.AddPragmaAttributes(SemaRef.TUScope, CDecl); + if (CatIDecl) + SemaRef.copyFeatureAvailabilityCheck(CDecl, CatIDecl); // FIXME: PushOnScopeChains? SemaRef.CurContext->addDecl(CDecl); @@ -2082,6 +2088,7 @@ ObjCImplementationDecl *SemaObjC::ActOnStartClassImplementation( SemaRef.ProcessDeclAttributeList(SemaRef.TUScope, IMPDecl, Attrs); SemaRef.AddPragmaAttributes(SemaRef.TUScope, IMPDecl); + SemaRef.copyFeatureAvailabilityCheck(IMPDecl, IDecl); if (CheckObjCDeclScope(IMPDecl)) { ActOnObjCContainerStartDefinition(IMPDecl); @@ -2718,7 +2725,8 @@ static void findProtocolsWithExplicitImpls(const ObjCInterfaceDecl *Super, static void CheckProtocolMethodDefs( Sema &S, ObjCImplDecl *Impl, ObjCProtocolDecl *PDecl, bool &IncompleteImpl, const SemaObjC::SelectorSet &InsMap, const SemaObjC::SelectorSet &ClsMap, - ObjCContainerDecl *CDecl, LazyProtocolNameSet &ProtocolsExplictImpl) { + ObjCContainerDecl *CDecl, LazyProtocolNameSet &ProtocolsExplictImpl, + llvm::SmallPtrSetImpl *MissingRequirements) { ObjCCategoryDecl *C = dyn_cast(CDecl); ObjCInterfaceDecl *IDecl = C ? C->getClassInterface() : dyn_cast(CDecl); @@ -2777,6 +2785,7 @@ static void CheckProtocolMethodDefs( // protocol. This lookup is slow, but occurs rarely in correct code // and otherwise would terminate in a warning. + bool HasMissingRequirements = false; // check unimplemented instance methods. if (!NSIDecl) for (auto *method : PDecl->instance_methods()) { @@ -2804,7 +2813,12 @@ static void CheckProtocolMethodDefs( continue; unsigned DIAG = diag::warn_unimplemented_protocol_method; if (!S.Diags.isIgnored(DIAG, Impl->getLocation())) { - WarnUndefinedMethod(S, Impl, method, IncompleteImpl, DIAG, PDecl); + if (MissingRequirements) { + if (!HasMissingRequirements) + HasMissingRequirements = shouldWarnUndefinedMethod(method); + } else { + WarnUndefinedMethod(S, Impl, method, IncompleteImpl, DIAG, PDecl); + } } } } @@ -2826,14 +2840,23 @@ static void CheckProtocolMethodDefs( unsigned DIAG = diag::warn_unimplemented_protocol_method; if (!S.Diags.isIgnored(DIAG, Impl->getLocation())) { - WarnUndefinedMethod(S, Impl, method, IncompleteImpl, DIAG, PDecl); + if (MissingRequirements) { + if (!HasMissingRequirements) + HasMissingRequirements = shouldWarnUndefinedMethod(method); + } else { + WarnUndefinedMethod(S, Impl, method, IncompleteImpl, DIAG, PDecl); + } } } } + if (HasMissingRequirements) { + assert(MissingRequirements != nullptr && "No missing requirements!"); + MissingRequirements->insert(PDecl); + } // Check on this protocols's referenced protocols, recursively. for (auto *PI : PDecl->protocols()) CheckProtocolMethodDefs(S, Impl, PI, IncompleteImpl, InsMap, ClsMap, CDecl, - ProtocolsExplictImpl); + ProtocolsExplictImpl, MissingRequirements); } /// MatchAllMethodDeclarations - Check methods declared in interface @@ -3047,22 +3070,50 @@ void SemaObjC::ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl *IMPDecl, LazyProtocolNameSet ExplicitImplProtocols; + bool UseEditorDiagnostics = !getDiagnostics() + .getDiagnosticOptions() + .DiagnosticSerializationFile.empty() || + getLangOpts().AllowEditorPlaceholders; + llvm::SmallPtrSet MissingRequirements; if (ObjCInterfaceDecl *I = dyn_cast (CDecl)) { for (auto *PI : I->all_referenced_protocols()) CheckProtocolMethodDefs(SemaRef, IMPDecl, PI, IncompleteImpl, InsMap, - ClsMap, I, ExplicitImplProtocols); + ClsMap, I, ExplicitImplProtocols, + UseEditorDiagnostics ? &MissingRequirements + : nullptr); } else if (ObjCCategoryDecl *C = dyn_cast(CDecl)) { // For extended class, unimplemented methods in its protocols will // be reported in the primary class. if (!C->IsClassExtension()) { for (auto *P : C->protocols()) CheckProtocolMethodDefs(SemaRef, IMPDecl, P, IncompleteImpl, InsMap, - ClsMap, CDecl, ExplicitImplProtocols); + ClsMap, CDecl, ExplicitImplProtocols, + UseEditorDiagnostics ? &MissingRequirements + : nullptr); DiagnoseUnimplementedProperties(S, IMPDecl, CDecl, /*SynthesizeProperties=*/false); } } else llvm_unreachable("invalid ObjCContainerDecl type."); + if (!MissingRequirements.empty()) { + { + auto DB = Diag(IMPDecl->getLocation(), + diag::warn_class_does_not_conform_protocol) + << (isa(CDecl) ? /*category=*/1 : /*class=*/0) + << CDecl << (unsigned)MissingRequirements.size(); + unsigned NumProtocols = 0; + for (const auto *PD : MissingRequirements) { + DB << PD; + if (++NumProtocols > 3) + break; + } + } + ASTContext &Context = getASTContext(); + auto DB = + Diag(IMPDecl->getLocation(), diag::note_add_missing_protocol_stubs); + edit::fillInMissingProtocolStubs::addMissingProtocolStubs( + Context, IMPDecl, [&](const FixItHint &Hint) { DB << Hint; }); + } } SemaObjC::DeclGroupPtrTy SemaObjC::ActOnForwardClassDeclaration( @@ -4139,7 +4190,7 @@ Decl *SemaObjC::ActOnAtEnd(Scope *S, SourceRange AtEnd, if (IDecl->getSuperClass() == nullptr) { // This class has no superclass, so check that it has been marked with // __attribute((objc_root_class)). - if (!HasRootClassAttr) { + if (!HasRootClassAttr && !IDecl->hasAttr()) { SourceLocation DeclLoc(IDecl->getLocation()); SourceLocation SuperClassLoc(SemaRef.getLocForEndOfToken(DeclLoc)); Diag(DeclLoc, diag::warn_objc_root_class_missing) @@ -5090,6 +5141,8 @@ Decl *SemaObjC::ActOnMethodDeclaration( // Insert the invisible arguments, self and _cmd! ObjCMethod->createImplicitParams(Context, ObjCMethod->getClassInterface()); + SemaRef.copyFeatureAvailabilityCheck(ObjCMethod, cast(ClassDecl)); + SemaRef.ActOnDocumentableDecl(ObjCMethod); return ObjCMethod; @@ -5662,6 +5715,7 @@ Decl *SemaObjC::ActOnIvar(Scope *S, SourceLocation DeclStart, Declarator &D, // Process attributes attached to the ivar. SemaRef.ProcessDeclAttributes(S, NewID, D); + SemaRef.copyFeatureAvailabilityCheck(NewID, EnclosingContext); if (D.isInvalidType()) NewID->setInvalidDecl(); diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index f358d8342e2f3..55c4709ee2502 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1285,6 +1285,15 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::StmtExprClass: case Expr::ConvertVectorExprClass: case Expr::VAArgExprClass: + case Expr::BoundsSafetyPointerPromotionExprClass: + case Expr::AssumptionExprClass: + case Expr::ForgePtrExprClass: + case Expr::GetBoundExprClass: + case Expr::PredefinedBoundsCheckExprClass: + case Expr::BoundsCheckExprClass: + case Expr::MaterializeSequenceExprClass: + case Expr::TerminatedByToIndexableExprClass: + case Expr::TerminatedByFromIndexableExprClass: case Expr::CXXParenListInitExprClass: return canSubStmtsThrow(*this, S); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 283d910a09d54..173de6c9ac240 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "DynamicCountPointerAssignmentAnalysis.h" #include "CheckExprLifetime.h" #include "TreeTransform.h" #include "UsedDeclVisitor.h" @@ -21,6 +22,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/DependenceFlags.h" #include "clang/AST/DynamicRecursiveASTVisitor.h" #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/Expr.h" @@ -30,9 +32,11 @@ #include "clang/AST/OperationKinds.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" +#include "clang/AST/TypeVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/PartialDiagnostic.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" @@ -371,6 +375,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef Locs, DiagnoseAvailabilityOfDecl(D, Locs, UnknownObjCClass, ObjCPropertyAccess, AvoidPartialAvailabilityChecks, ClassReceiver); + DiagnoseFeatureAvailabilityOfDecl(D, Locs); + DiagnoseUnusedOfDecl(*this, D, Loc); diagnoseUseOfInternalDeclInInlineFunction(*this, D, Loc); @@ -513,8 +519,245 @@ SourceRange Sema::getExprRange(Expr *E) const { // Standard Promotions and Conversions //===----------------------------------------------------------------------===// +/*TO_UPSTREAM(BoundsSafety) ON*/ +static ExprResult CastToCharPointer(Sema &S, Expr *PointerToCast, + BoundsSafetyPointerAttributes FA) { + SourceLocation Loc = PointerToCast->getBeginLoc(); + auto SQT = PointerToCast->getType()->getPointeeType().split(); + SQT.Ty = S.Context.CharTy.getTypePtr(); + QualType QualifiedChar = S.Context.getQualifiedType(SQT); + QualType CharPtrTy = S.Context.getPointerType(QualifiedChar, FA); + return S.BuildCStyleCastExpr( + Loc, S.Context.getTrivialTypeSourceInfo(CharPtrTy), Loc, PointerToCast); +} + +static ExprResult CastToCharPointer(Sema &S, Expr *PointerToCast) { + return CastToCharPointer(S, PointerToCast, + BoundsSafetyPointerAttributes::bidiIndexable()); +} + +static bool ShouldAssumeSGTZero(Sema &S, Expr *ValueToTest) { + QualType CountT = ValueToTest->getType(); + if (!CountT->isUnsignedIntegerOrEnumerationType()) + return false; + return S.Context.getTypeSize(CountT) >= S.Context.getTypeSize( + S.Context.getSignedSizeType()); +} + +static ExprResult AssumeSGTZero(Sema &S, Expr *ValueToTest, Expr *ValueToWrap) { + // If the count is unsigned and the same size as size_t, assume that it is + // signed-positive. + QualType CountT = ValueToTest->getType(); + CountT = S.Context.getCorrespondingSignedType(CountT); + ExprResult Cast = S.ImpCastExprToType( + ValueToTest, CountT, CK_IntegralCast); + if (!(ValueToTest = Cast.get())) + return ExprError(); + ExprResult Zero = S.ActOnIntegerConstant(ValueToTest->getBeginLoc(), 0); + ExprResult Comparison = S.CreateBuiltinBinOp( + ValueToTest->getBeginLoc(), BO_GE, ValueToTest, Zero.get()); + if (!Comparison.get()) + return ExprError(); + return AssumptionExpr::Create( + S.Context, ValueToWrap, Comparison.get()); +} + +static ExprResult PromoteBoundsSafetyPointerWithCount(Sema &S, + OpaqueValueExpr *BasePtr, + OpaqueValueExpr *Count, + bool IsCountInBytes, + bool NullCheck) { + QualType T = BasePtr->getType(); + + ExprResult PtrVal = BasePtr; + ExprResult Upper = BasePtr; + + auto TFA = T->getAs()->getPointerAttributes(); + if (!TFA.isUnsafeOrUnspecified()) { + T = S.Context.getPointerType( + T->getPointeeType(), BoundsSafetyPointerAttributes::unspecified()); + Upper = ImplicitCastExpr::Create( + S.Context, T, CK_BoundsSafetyPointerCast, Upper.get(), nullptr, + VK_PRValue, S.CurFPFeatureOverrides()); + } + + bool CastToChar = + IsCountInBytes && S.Context.getTypeSizeInCharsIfKnown(T->getPointeeType()) + .value_or(CharUnits::Zero()) + .getQuantity() != 1; + if (CastToChar) { + Upper = CastToCharPointer(S, Upper.get(), + T->getAs()->getPointerAttributes()); + if (!Upper.get()) + return ExprError(); + } + + Expr *CountE = Count; + if (ShouldAssumeSGTZero(S, CountE)) { + ExprResult Assumed = AssumeSGTZero(S, CountE, CountE); + if (!(CountE = Assumed.get())) + return ExprError(); + } + Upper = S.CreateBuiltinBinOp( + Upper.get()->getBeginLoc(), BO_Add, Upper.get(), CountE); + if (!Upper.get()) + return ExprError(); + + if (CastToChar) { + Upper = ImplicitCastExpr::Create( + S.Context, T, CK_BitCast, Upper.get(), nullptr, VK_PRValue, + S.CurFPFeatureOverrides()); + } + + QualType FPtrTy = S.Context.getPointerType( + T->getPointeeType(), BoundsSafetyPointerAttributes::bidiIndexable()); + return BoundsSafetyPointerPromotionExpr::Create(S.Context, FPtrTy, PtrVal.get(), + Upper.get(), nullptr, NullCheck); +} + +static ExprResult +PromoteBoundsSafetyPointerToFlexibleArrayMember(Sema &S, RecordDecl *RD, Expr *E, + bool NullCheck = true) { + FlexibleArrayMemberUtils FlexUtils(S); + SmallVector PathToFlex; + ArrayRef CountDecls; + if (!FlexUtils.Find(RD, PathToFlex, CountDecls)) { + // cannot promote; return original expression. this will be diagnosed + // elsewhere + return E; + } + + SmallVector OVEs; + + auto *OE = OpaqueValueExpr::EnsureWrapped(S.Context, E, OVEs); + Expr *FlexibleObj = FlexUtils.SelectFlexibleObject(PathToFlex, OE); + ExprResult CountExpr = FlexUtils.BuildCountExpr( + PathToFlex.back(), CountDecls, FlexibleObj, OVEs); + if (!CountExpr.get()) + return ExprError(); + + CopyExpr Copier(S); + Expr *Count = CountExpr.get(); + if (ShouldAssumeSGTZero(S, Count)) { + Expr *ClonedCount = S.DefaultLvalueConversion(Copier.TransformExpr(Count).get()).get(); + ExprResult Assumed = + AssumeSGTZero(S, ClonedCount, Count); + if (!(Count = Assumed.get())) + return ExprError(); + } + + // build end address + QualType FlexType = PathToFlex.back()->getType(); + Expr *AnyStructBase = FlexibleObj; + if (AnyStructBase->getType()->isPointerType() && !AnyStructBase->isPRValue()) + AnyStructBase = ImplicitCastExpr::Create( + S.Context, AnyStructBase->getType(), CK_LValueToRValue, + AnyStructBase, nullptr, VK_PRValue, S.CurFPFeatureOverrides()); + Expr *ObjEnd = MemberExpr::CreateImplicit( + S.Context, AnyStructBase, /*IsArrow*/ AnyStructBase->getType()->isPointerType(), + PathToFlex.back(), FlexType, VK_LValue, OK_Ordinary); + // Ensure that the pointer isn't __bidi_indexable, as that would be + // problematic; we're currently trying to compute what the bounds of that + // pointer should be. + QualType DecayedTy = S.Context.getArrayDecayedType(FlexType); + DecayedTy = S.Context.getBoundsSafetyPointerType(DecayedTy, {}); + ExprResult Res = S.ImpCastExprToType( + ObjEnd, DecayedTy, CK_ArrayToPointerDecay, VK_PRValue); + if (!(ObjEnd = Res.get())) + return ExprError(); + + // ArrayToPointerDecay overrides the pointer attributes to be bidi_indexable, + // force it to be unspecified. + ObjEnd->setType(DecayedTy); + + Res = S.CreateBuiltinBinOp(E->getBeginLoc(), BO_Add, ObjEnd, Count); + if (!(ObjEnd = Res.get())) + return ExprError(); + + // build FPPE and materializing expressions around it + auto Bidi = BoundsSafetyPointerAttributes::bidiIndexable(); + QualType FPtrTy = S.Context.getPointerType( + E->getType()->getPointeeType(), Bidi); + + // Promoting a pointer to struct with flexible array member to a wide + // pointer requires implicit count member access to the pointer. If the + // pointer is null, this will cause an uninteded null pointer dereference. + // To avoid this, we skip the evaluation of the bounds of the promotion + // expression if the base pointer is null. We control this by adding a + // 'NullCheck' member to `BoundsSafetyPointerPromotionExpr`. This is effectively + // same as adding this conditional operator (`OE ? Result : 0`). However, we + // don't add such an expression in the AST because the conditional operator + // will interfere our CFG analysis for dynamic count assignments. Also, we + // don't have a good way to remove the conditional operator along with the + // functionality to ignore implicit casts. A materialization expr wraps the + // promotion with null check and if the materialization expr materializes an + // OVE containing member access first, it will still introduce an unintended + // null pointer dereference. We prevent this by not wrapping the count member + // access in OVE but ensuring it's rebuilt for every reuse. + Expr *Result = BoundsSafetyPointerPromotionExpr::Create( + S.Context, FPtrTy, OE, ObjEnd, /*LowerBound*/ nullptr, NullCheck); + + if (!OVEs.empty()) { + Result = MaterializeSequenceExpr::Create(S.Context, Result, OVEs); + Result = MaterializeSequenceExpr::Create(S.Context, Result, OVEs, true); + } + return Result; +} + +static ExprResult +PromoteBoundsSafetyFlexibleArrayMember(Sema &S, MemberExpr *M, Expr *ArrayBase) { + SmallVector OVEs; + Expr *BasePtr = OpaqueValueExpr::EnsureWrapped(S.Context, M->getBase(), OVEs); + M->setBase(BasePtr); + + if (auto *PT = BasePtr->getType()->getAs()) { + if (!PT->getPointerAttributes().hasUpperBound()) { + auto *RecordPointee = PT->getPointeeType()->getAs(); + auto *RD = RecordPointee->getDecl(); + assert(RD->hasFlexibleArrayMember() && + RD->getTagKind() != TagTypeKind::Union); + // Skipping the null check for the struct base because it must have been + // explicitly dereferenced (e.g., base->array) to get here. + ExprResult Promoted = PromoteBoundsSafetyPointerToFlexibleArrayMember( + S, RD, BasePtr, /*NullCheck*/ false); + if (!(BasePtr = Promoted.get())) + return ExprError(); + } + } else { + assert(BasePtr->isLValue()); + // Using '&' on an object with a flexible array member will give us a + // properly promoted pointer to it. + ExprResult AddrOf = S.CreateBuiltinUnaryOp( + BasePtr->getBeginLoc(), UO_AddrOf, BasePtr); + if (!(BasePtr = AddrOf.get())) + return ExprError(); + } + + ExprResult Upper = S.BuildUpperBoundExpr( + BasePtr, ArrayBase->getBeginLoc(), SourceLocation()); + if (!Upper.get()) + return ExprError(); + + QualType FPtrTy = S.Context.getBoundsSafetyPointerType( + ArrayBase->getType(), BoundsSafetyPointerAttributes::bidiIndexable()); + Expr *Result = BoundsSafetyPointerPromotionExpr::Create( + S.Context, FPtrTy, ArrayBase, Upper.get()); + + if (!OVEs.empty()) { + Result = MaterializeSequenceExpr::Create(S.Context, Result, OVEs); + Result = MaterializeSequenceExpr::Create(S.Context, Result, OVEs, true); + } + return Result; +} +/*TO_UPSTREAM(BoundsSafety) OFF*/ + /// DefaultFunctionArrayConversion (C99 6.3.2.1p3, C99 6.3.2.1p4). -ExprResult Sema::DefaultFunctionArrayConversion(Expr *E, bool Diagnose) { +ExprResult Sema::DefaultFunctionArrayConversion( + Expr *E, bool Diagnose, + /* TO_UPSTREAM(BoundsSafety) ON */ + bool DiagnoseBoundsSafetyIncompleteArrayPromotion, + bool DisableFlexibleArrayPromotion) { + /* TO_UPSTREAM(BoundsSafety) OFF */ // Handle any placeholder expressions which made it here. if (E->hasPlaceholderType()) { ExprResult result = CheckPlaceholderExpr(E); @@ -531,7 +774,12 @@ ExprResult Sema::DefaultFunctionArrayConversion(Expr *E, bool Diagnose) { if (!checkAddressOfFunctionIsAvailable(FD, Diagnose, E->getExprLoc())) return ExprError(); - E = ImpCastExprToType(E, Context.getPointerType(Ty), + /* TO_UPSTREAM(BoundsSafety) ON*/ + BoundsSafetyPointerAttributes FA; + if (getLangOpts().BoundsSafety) + FA.setSingle(); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + E = ImpCastExprToType(E, Context.getPointerType(Ty, FA), CK_FunctionToPointerDecay).get(); } else if (Ty->isArrayType()) { // In C90 mode, arrays only promote to pointers if the array expression is @@ -547,9 +795,28 @@ ExprResult Sema::DefaultFunctionArrayConversion(Expr *E, bool Diagnose) { // if (getLangOpts().C99 || getLangOpts().CPlusPlus || E->isLValue()) { ExprResult Res = ImpCastExprToType(E, Context.getArrayDecayedType(Ty), - CK_ArrayToPointerDecay); + CK_ArrayToPointerDecay, VK_PRValue, nullptr, + CheckedConversionKind::Implicit, + // TO_UPSTREAM(BoundsSafety) + DiagnoseBoundsSafetyIncompleteArrayPromotion); if (Res.isInvalid()) return ExprError(); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety: if decaying a member access and that member has a flexible + // array member, we must wrap it in the appropriate promotion legalese. + if (getLangOpts().BoundsSafety && !DisableFlexibleArrayPromotion) { + if (Ty->isIncompleteArrayType() && Ty->isCountAttributedType()) { + if (auto *Member = dyn_cast(E->IgnoreParens())) { + Res.get()->setType(Context.getArrayDecayedType(Ty)); + Res = + PromoteBoundsSafetyFlexibleArrayMember(*this, Member, Res.get()); + if (Res.isInvalid()) + return ExprError(); + } + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ E = Res.get(); } } @@ -638,7 +905,25 @@ static void DiagnoseDirectIsaAccess(Sema &S, const ObjCIvarRefExpr *OIRE, } } -ExprResult Sema::DefaultLvalueConversion(Expr *E) { +/*TO_UPSTREAM(BoundsSafety) ON*/ +static RecordDecl *getImmediateDeclForFlexibleArrayPromotion(QualType T) { + if (T->isSinglePointerType() && !T->isBoundsAttributedType()) { + auto *PT = T->getAs(); + if (auto *RecordPointee = PT->getPointeeType()->getAs()) { + auto *RD = RecordPointee->getDecl(); + if (RD->hasFlexibleArrayMember() && + RD->getTagKind() != TagTypeKind::Union) { + return RD; + } + } + } + return nullptr; +} +/*TO_UPSTREAM(BoundsSafety) OFF*/ + +ExprResult Sema::DefaultLvalueConversion(Expr *E, + // TO_UPSTREAM(BoundsSafety) + bool DisableFlexibleArrayPromotion) { // Handle any placeholder expressions which made it here. if (E->hasPlaceholderType()) { ExprResult result = CheckPlaceholderExpr(E); @@ -732,8 +1017,135 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) { if (E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) Cleanup.setExprNeedsCleanups(true); - if (!BoundsSafetyCheckUseOfCountAttrPtr(Res.get())) - return ExprError(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety: rvalue of pointers with dynamic count or range will + // automatically form wide pointers. + auto *DBPT = T->getAs(); + if (getLangOpts().BoundsSafety && DBPT) { + CopyExpr Copy(*this); + SmallVector OpaqueValues; + // if the lvalue was a member access, it's possible its base expression + // has side effects, in which case we need to wrap it in an opaque value + // expression. + auto *UnwrappedExp = E; + bool IsMemberOVE = false; + if (const auto *MemberOVE = dyn_cast(UnwrappedExp)) { + UnwrappedExp = MemberOVE->getSourceExpr(); + IsMemberOVE = true; + } + auto *Member = dyn_cast(UnwrappedExp->IgnoreParenCasts()); + if (DBPT->referencesFieldDecls() && Member) { + auto *MemberBase = Member->getBase(); + if (!isa(MemberBase)) { + // When a MemberExpr is materialized, the base should also be. + (void)IsMemberOVE; + assert(!IsMemberOVE); + MemberBase = OpaqueValueExpr::EnsureWrapped( + Context, MemberBase, OpaqueValues); + // Rebuild the member expr with the new base so that when + // the caller of LvalueConversion throw away the result, + // the OVE also goes away. An example is 'rewriteBuiltinFunctionDecl()' + // which calls LvalueConversion just to check the resulting type. + Res = MemberExpr::Create( + Context, MemberBase, Member->isArrow(), Member->getOperatorLoc(), + Member->getQualifierLoc(), Member->getTemplateKeywordLoc(), + Member->getMemberDecl(), Member->getFoundDecl(), + Member->getMemberNameInfo(), /*CopiedTemplateArgs(Member)*/ nullptr, + Member->getType(), Member->getValueKind(), Member->getObjectKind(), + Member->isNonOdrUse()); + } + for (const auto &DeclRefInfo : DBPT->dependent_decls()) { + auto *Decl = DeclRefInfo.getDecl(); + ExprObjectKind OK = OK_Ordinary; + if (auto *FD = dyn_cast(Decl)) { + OK = FD->isBitField() ? OK_BitField : OK_Ordinary; + } + auto *NewMember = MemberExpr::CreateImplicit( + Context, MemberBase, Member->isArrow(), Decl, Decl->getType(), + VK_LValue, OK); + ExprResult Lvalue = ImplicitCastExpr::Create( + Context, NewMember->getType(), CK_LValueToRValue, NewMember, + nullptr, VK_PRValue, CurFPFeatureOverrides()); + if (!Lvalue.get()) + return ExprError(); + auto *OVE = OpaqueValueExpr::EnsureWrapped( + Context, Lvalue.get(), OpaqueValues); + Copy.AddDeclSubstitution(Decl, OVE); + } + } + + CastKind CK = T->isNullPtrType() ? CK_NullToPointer : CK_LValueToRValue; + Res = ImplicitCastExpr::Create(Context, T, CK, Res.get(), nullptr, + VK_PRValue, CurFPFeatureOverrides()); + + if (auto *DCPT = dyn_cast(DBPT)) { + ExprResult Count = Copy.TransformExpr(DCPT->getCountExpr()); + if (!Count.get()) + return ExprError(); + Count = DefaultLvalueConversion(Count.get()); + if (!Count.get()) + return ExprError(); + + // FIXME: Hoist this out of if-else blocks + if (!BoundsSafetyCheckUseOfCountAttrPtr(Res.get())) + return ExprError(); + + // Need to transform the base value so that + // PromoteBoundsSafetyPointerWithCount can reuse it with impunity + auto *PtrOVE = OpaqueValueExpr::EnsureWrapped( + Context, Res.get(), OpaqueValues); + auto *CountOVE = OpaqueValueExpr::EnsureWrapped( + Context, Count.get(), OpaqueValues); + Res = PromoteBoundsSafetyPointerWithCount( + *this, PtrOVE, CountOVE, DCPT->isCountInBytes(), DCPT->isOrNull()); + if (!Res.get()) + return ExprError(); + } else { + auto *DRPT = cast(DBPT); + // XXX: we would prefer to call DefaultLvalueConversion for Lower and + // Upper here, but sadly this would infinitely recurse. + ExprResult Lower; + if (auto *StartExpr = DRPT->getStartPointer()) + Lower = Copy.TransformExpr(StartExpr); + else + Lower = Copy.TransformExpr(E); + if (!Lower.get()) + return ExprError(); + + ExprResult Upper; + if (auto *EndExpr = DRPT->getEndPointer()) + Upper = Copy.TransformExpr(EndExpr); + else + Upper = Copy.TransformExpr(E); + if (!Upper.get()) + return ExprError(); + + if (Lower.get()->isGLValue()) + Lower = ImplicitCastExpr::Create( + Context, Lower.get()->getType(), CK_LValueToRValue, Lower.get(), + nullptr, VK_PRValue, CurFPFeatureOverrides()); + if (Upper.get()->isGLValue()) + Upper = ImplicitCastExpr::Create( + Context, Upper.get()->getType(), CK_LValueToRValue, Upper.get(), + nullptr, VK_PRValue, CurFPFeatureOverrides()); + + BoundsSafetyPointerAttributes AT; + AT.setBidiIndexable(); + QualType FPtrTy = Context.getPointerType(T->getPointeeType(), AT); + Res = BoundsSafetyPointerPromotionExpr::Create( + Context, FPtrTy, E, Upper.get(), Lower.get()); + } + + if (!OpaqueValues.empty()) { + Res = MaterializeSequenceExpr::Create(Context, Res.get(), OpaqueValues); + Res = MaterializeSequenceExpr::Create(Context, Res.get(), OpaqueValues, true); + } + return Res; + } else { + if (!BoundsSafetyCheckUseOfCountAttrPtr(Res.get())) + return ExprError(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ // C++ [conv.lval]p3: // If T is cv std::nullptr_t, the result is a null pointer constant. @@ -750,14 +1162,37 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) { nullptr, VK_PRValue, FPOptionsOverride()); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Promote a single pointer to struct with flexible array member. We do not + // promote unsafe_indexable here but if its flexible array member is + // annotated with counted_by, the checks will still be performed based on the + // count value when the member is dereferenced. + // `DisableFlexibleArrayPromotion` allows us to skip the promotion for a base + // of member expression as it can create a problem of incorrect bounds while + // the member is being updated and it is redundant. When the base is a + // single pointer to struct with flexible array member, other members are safe + // to access without extra checks just like any other single pointers. + // Directly accessing the flexible array member will still be promoted to + // bidi_indexable and will be safely handled. + if (getLangOpts().BoundsSafety && !DisableFlexibleArrayPromotion) { + if (auto *RD = getImmediateDeclForFlexibleArrayPromotion(T)) { + Res = PromoteBoundsSafetyPointerToFlexibleArrayMember(*this, RD, Res.get()); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + return Res; } -ExprResult Sema::DefaultFunctionArrayLvalueConversion(Expr *E, bool Diagnose) { - ExprResult Res = DefaultFunctionArrayConversion(E, Diagnose); +ExprResult Sema::DefaultFunctionArrayLvalueConversion( + Expr *E, bool Diagnose, bool DiagnoseBoundsSafetyIncompleteArrayPromotion, + bool DisableFlexibleArrayPromotion) { + ExprResult Res = DefaultFunctionArrayConversion( + E, Diagnose, DiagnoseBoundsSafetyIncompleteArrayPromotion, + DisableFlexibleArrayPromotion); if (Res.isInvalid()) return ExprError(); - Res = DefaultLvalueConversion(Res.get()); + Res = DefaultLvalueConversion(Res.get(), DisableFlexibleArrayPromotion); if (Res.isInvalid()) return ExprError(); return Res; @@ -891,6 +1326,22 @@ ExprResult Sema::DefaultArgumentPromotion(Expr *E) { return ExprError(); E = Res.get(); + // The argument we are processing is for a function without a prototype + // or a varargs argument. Passing a pointer with bounds will very likely lead + // to an ABI mismatch because there's a good chance the implementing function + // isn't using -fbounds-safety. Avoid this potential ABI mismatch by not allowing a + // pointer with bounds to passed by casting to an unsafeIndexable pointer + // which is ABI compatible with normal pointers. + if (E->getType()->isPointerTypeWithBounds()) { + QualType UnsafePointerTy = Context.getBoundsSafetyPointerType( + E->getType(), BoundsSafetyPointerAttributes::unsafeIndexable()); + ExprResult ExprRes = + ImpCastExprToType(E, UnsafePointerTy, CK_BoundsSafetyPointerCast); + if (ExprRes.isInvalid()) + return ExprError(); + E = ExprRes.get(); + } + // If this is a 'float' or '__fp16' (CVR qualified or typedef) // promote to double. // Note that default argument promotion applies only to float (and @@ -1591,12 +2042,31 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS, QualType LHSType = LHS.get()->getType().getUnqualifiedType(); QualType RHSType = RHS.get()->getType().getUnqualifiedType(); + // BoundsSafety: "pointer" op "pointer" -> cast it to raw pointer. + auto *LPTy = LHSType->getAs(); + auto *RPTy = RHSType->getAs(); + if (ACK != ACK_Conditional && LPTy && RPTy && + (!LPTy->hasRawPointerLayout() || !RPTy->hasRawPointerLayout())) { + if (!LPTy->hasRawPointerLayout()) + LHS = ImpCastExprToType(LHS.get(), + Context.getPointerType(LPTy->getPointeeType()), CK_BoundsSafetyPointerCast); + if (!RPTy->hasRawPointerLayout()) + RHS = ImpCastExprToType(RHS.get(), + Context.getPointerType(RPTy->getPointeeType()), CK_BoundsSafetyPointerCast); + } + // For conversion purposes, we ignore any atomic qualifier on the LHS. if (const AtomicType *AtomicLHS = LHSType->getAs()) LHSType = AtomicLHS->getValueType(); // If both types are identical, no conversion is needed. - if (Context.hasSameType(LHSType, RHSType)) + if (Context.hasSameType(LHSType, RHSType) + /* TO_UPSTREAM(BoundsSafety) ON*/ + && (!Context.getLangOpts().BoundsSafety || + Context.canMergeTypeBounds(LHSType, RHSType) == + ASTContext::BSPTMK_CanMerge) + /* TO_UPSTREAM(BoundsSafety) OFF*/ + ) return Context.getCommonSugaredType(LHSType, RHSType); // If either side is a non-arithmetic type (e.g. a pointer), we are done. @@ -1615,7 +2085,13 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS, LHS = ImpCastExprToType(LHS.get(), LHSType, CK_IntegralCast); // If both types are identical, no conversion is needed. - if (Context.hasSameType(LHSType, RHSType)) + if (Context.hasSameType(LHSType, RHSType) + /* TO_UPSTREAM(BoundsSafety) ON*/ + && (!Context.getLangOpts().BoundsSafety || + Context.canMergeTypeBounds(LHSType, RHSType) == + ASTContext::BSPTMK_CanMerge) + /* TO_UPSTREAM(BoundsSafety) OFF*/ + ) return Context.getCommonSugaredType(LHSType, RHSType); // At this point, we have two different arithmetic types. @@ -4542,6 +5018,10 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T, case Type::SubstTemplateTypeParm: case Type::MacroQualified: case Type::CountAttributed: + /* TO_UPSTREAM(BoundsSafety) ON */ + case Type::DynamicRangePointer: + case Type::ValueTerminated: + /* TO_UPSTREAM(BoundsSafety) OFF */ // Keep walking after single level desugaring. T = T.getSingleStepDesugaredType(Context); break; @@ -5221,6 +5701,45 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, // and index from the expression types. Expr *BaseExpr, *IndexExpr; QualType ResultType; + + /*TO_UPSTREAM(BoundsSafety) ON*/ + auto diagnoseBoundsSafetyPointerSubscript = [&](QualType Ty) -> bool { + const PointerType *PTy = Ty->getAs(); + // If the base of the array subscript expression is 'counted_by', it should + // have been promoted to __bidi_indexable. + assert(!Ty->isBoundsAttributedType()); + if (PTy->isSingle()) { + Expr::EvalResult RInt; + if (!RHSExp->EvaluateAsInt(RInt, Context) || RInt.Val.getInt() != 0) { + Diag(LLoc, diag::err_bounds_safety_pointer_subscript) + << Ty->isValueTerminatedType() << BaseExpr + << SourceRange(LHSExp->getBeginLoc(), RHSExp->getEndLoc()); + return false; + } + } else if (PTy->isIndexable() && + !IndexExpr->getType()->isUnsignedIntegerType()) { + Expr::EvalResult RInt; + // Runtime check will be inserted for the indices whose negativity can't + // be known statically. + if (RHSExp->EvaluateAsInt(RInt, Context) && RInt.Val.getInt() < 0) { + Diag(LLoc, diag::err_bounds_safety_indexable_pointer_subscript) + << BaseExpr + << SourceRange(LHSExp->getBeginLoc(), RHSExp->getEndLoc()); + return false; + } + } + return true; + }; + + auto castIndexableToBidiIndexable = [&](Expr *BaseExpr) -> Expr * { + if (!BaseExpr->getType()->isIndexablePointerType()) + return BaseExpr; + QualType Ty = Context.getBoundsSafetyPointerType( + BaseExpr->getType(), BoundsSafetyPointerAttributes::bidiIndexable()); + return ImpCastExprToType(BaseExpr, Ty, CK_BoundsSafetyPointerCast).get(); + }; + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (LHSTy->isDependentType() || RHSTy->isDependentType()) { BaseExpr = LHSExp; IndexExpr = RHSExp; @@ -5230,6 +5749,13 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, BaseExpr = LHSExp; IndexExpr = RHSExp; ResultType = PTy->getPointeeType(); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety && !diagnoseBoundsSafetyPointerSubscript(LHSTy)) + return ExprError(); + LHSExp = castIndexableToBidiIndexable(BaseExpr); + LHSTy = LHSExp->getType(); + BaseExpr = LHSExp; + /*TO_UPSTREAM(BoundsSafety) OFF*/ } else if (const ObjCObjectPointerType *PTy = LHSTy->getAs()) { BaseExpr = LHSExp; @@ -5247,6 +5773,13 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, BaseExpr = RHSExp; IndexExpr = LHSExp; ResultType = PTy->getPointeeType(); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety && !diagnoseBoundsSafetyPointerSubscript(RHSTy)) + return ExprError(); + RHSExp = castIndexableToBidiIndexable(BaseExpr); + RHSTy = RHSExp->getType(); + BaseExpr = RHSExp; + /*TO_UPSTREAM(BoundsSafety) OFF*/ } else if (const ObjCObjectPointerType *PTy = RHSTy->getAs()) { // Handle the uncommon case of "123[Ptr]". @@ -5817,6 +6350,50 @@ class FunctionCallCCC final : public FunctionCallFilterCCC { }; } +namespace { +class ContainsBoundsAttributedType final + : public TypeVisitor { + +public: + static bool check(QualType QT) { + return ContainsBoundsAttributedType().Visit(QT.getTypePtr()); + } + + bool VisitType(const Type *T) { + QualType Desugared = T->getLocallyUnqualifiedSingleStepDesugaredType(); + return Desugared.getTypePtr() == T ? false : Visit(Desugared.getTypePtr()); + } + + bool VisitPointerType(const PointerType *T) { + if (getImmediateDeclForFlexibleArrayPromotion(QualType(T,0))) + return true; + auto Pointee = T->getPointeeType(); + if (!Pointee->isIncompleteOrObjectType()) + return false; + return Visit(Pointee.getTypePtr()); + } + + bool VisitFunctionType(const FunctionType *T) { + return Visit(T->getReturnType().getTypePtr()); + } + + bool VisitFunctionProtoType(const FunctionProtoType *T) { + if (VisitFunctionType(T)) + return true; + for (QualType PT : T->getParamTypes()) + if (Visit(PT.getTypePtr())) + return true; + return false; + } + + bool VisitBoundsAttributedType(const BoundsAttributedType *T) { + return true; + } +}; + + +} + static TypoCorrection TryTypoCorrectionForCall(Sema &S, Expr *Fn, FunctionDecl *FDecl, ArrayRef Args) { @@ -6034,6 +6611,17 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, FunctionDecl *FDecl, if (ArgIx < Args.size()) { Arg = Args[ArgIx++]; + /*TO_UPSTREAM(BoundsSafety) ON*/ + // It's not useful to throw type errors based on the type of most + // expressions with errors. Make an exception for DeclRefExprs since the + // type of the Decl is explicit. The type of casts would be explicit also, + // but the bounds type can be implicit in -fbounds-safety, which is the + // target of this (normal C is way more lax with type errors) + if (getLangOpts().BoundsSafety && Arg->containsErrors() && + !isa(Arg->IgnoreParenCasts())) + continue; + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (RequireCompleteType(Arg->getBeginLoc(), ProtoArgType, diag::err_call_incomplete_argument, Arg)) return true; @@ -6305,8 +6893,10 @@ static FunctionDecl *rewriteBuiltinFunctionDecl(Sema *Sema, ASTContext &Context, for (QualType ParamType : FT->param_types()) { // Convert array arguments to pointer to simplify type lookup. - ExprResult ArgRes = - Sema->DefaultFunctionArrayLvalueConversion(ArgExprs[i++]); + ExprResult ArgRes = Sema->DefaultFunctionArrayLvalueConversion( + ArgExprs[i++], /*Diagnose=*/true, + /*DiagnoseBoundsSafetyIncompleteArrayPromotion=*/true, + /*DisableFlexibleArrayPromotion=*/true); if (ArgRes.isInvalid()) return nullptr; Expr *Arg = ArgRes.get(); @@ -6521,6 +7111,12 @@ ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, if (LangOpts.OpenMP) Call = OpenMP().ActOnOpenMPCall(Call, Scope, LParenLoc, ArgExprs, RParenLoc, ExecConfig); + + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety) + Call = ActOnBoundsSafetyCall(Call); + /*TO_UPSTREAM(BoundsSafety) OFF*/ + if (LangOpts.CPlusPlus) { if (const auto *CE = dyn_cast(Call.get())) DiagnosedUnqualifiedCallsToStdFunctions(*this, CE); @@ -6793,14590 +7389,19096 @@ ExprResult Sema::ActOnConvertVectorExpr(Expr *E, ParsedType ParsedDestTy, return ConvertVectorExpr(E, TInfo, BuiltinLoc, RParenLoc); } -ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, - SourceLocation LParenLoc, - ArrayRef Args, - SourceLocation RParenLoc, Expr *Config, - bool IsExecConfig, ADLCallKind UsesADL) { - FunctionDecl *FDecl = dyn_cast_or_null(NDecl); - unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0); - - // Functions with 'interrupt' attribute cannot be called directly. - if (FDecl) { - if (FDecl->hasAttr()) { - Diag(Fn->getExprLoc(), diag::err_anyx86_interrupt_called); - return ExprError(); - } - if (FDecl->hasAttr()) { - Diag(Fn->getExprLoc(), diag::err_arm_interrupt_called); - return ExprError(); +/* TO_UPSTREAM(BoundsSafety) ON*/ +static const ValueDecl *findValueDecl(const Expr *E, int *AddrOfDerefDiff) { + if (AddrOfDerefDiff) + *AddrOfDerefDiff = 0; + E = E->IgnoreParenCasts(); + int AddrOfDiff = 0; + while (auto UO = dyn_cast(E)) { + switch (UO->getOpcode()) { + case UO_AddrOf: + AddrOfDiff++; + break; + case UO_Deref: + AddrOfDiff--; + break; + default: + return nullptr; } + E = UO->getSubExpr()->IgnoreParenCasts(); } + /// Not supported as dynamic count or pointer arguments. + if (AddrOfDiff > 1 || AddrOfDiff < -1) + return nullptr; - // X86 interrupt handlers may only call routines with attribute - // no_caller_saved_registers since there is no efficient way to - // save and restore the non-GPR state. - if (auto *Caller = getCurFunctionDecl()) { - if (Caller->hasAttr() || - Caller->hasAttr()) { - const TargetInfo &TI = Context.getTargetInfo(); - bool HasNonGPRRegisters = - TI.hasFeature("sse") || TI.hasFeature("x87") || TI.hasFeature("mmx"); - if (HasNonGPRRegisters && - (!FDecl || !FDecl->hasAttr())) { - Diag(Fn->getExprLoc(), diag::warn_anyx86_excessive_regsave) - << (Caller->hasAttr() ? 0 : 1); - if (FDecl) - Diag(FDecl->getLocation(), diag::note_callee_decl) << FDecl; - } + if (AddrOfDerefDiff) + *AddrOfDerefDiff = AddrOfDiff; + + if (auto ME = dyn_cast(E)) + return ME->getMemberDecl(); + if (auto DRE = dyn_cast(E)) + return DRE->getDecl(); + return nullptr; +} + +static const RecordDecl *findBaseRecordDecl(const Expr *E) { + E = E->IgnoreParenCasts(); + while (auto UO = dyn_cast(E)) { + switch (UO->getOpcode()) { + case UO_AddrOf: + case UO_Deref: + break; + default: + return nullptr; } + E = UO->getSubExpr()->IgnoreParenCasts(); } - // Promote the function operand. - // We special-case function promotion here because we only allow promoting - // builtin functions to function pointers in the callee of a call. - ExprResult Result; - QualType ResultTy; - if (BuiltinID && - Fn->getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn)) { - // Extract the return type from the (builtin) function pointer type. - // FIXME Several builtins still have setType in - // Sema::CheckBuiltinFunctionCall. One should review their definitions in - // Builtins.td to ensure they are correct before removing setType calls. - QualType FnPtrTy = Context.getPointerType(FDecl->getType()); - Result = ImpCastExprToType(Fn, FnPtrTy, CK_BuiltinFnToFnPtr).get(); - ResultTy = FDecl->getCallResultType(); - } else { - Result = CallExprUnaryConversions(Fn); - ResultTy = Context.BoolTy; + RecordDecl *BaseRecord = nullptr; + while (auto ME = dyn_cast(E)) { + E = ME->getBase()->IgnoreParenCasts(); + const Type *BaseTy = E->getType().getTypePtr(); + if (ME->isArrow()) + BaseTy = BaseTy->getPointeeOrArrayElementType(); + BaseRecord = BaseTy->getAsRecordDecl(); } - if (Result.isInvalid()) - return ExprError(); - Fn = Result.get(); + return BaseRecord; +} - // Check for a valid function type, but only if it is not a builtin which - // requires custom type checking. These will be handled by - // CheckBuiltinFunctionCall below just after creation of the call expression. - const FunctionType *FuncT = nullptr; - if (!BuiltinID || !Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) { - retry: - if (const PointerType *PT = Fn->getType()->getAs()) { - // C99 6.5.2.2p1 - "The expression that denotes the called function shall - // have type pointer to function". - FuncT = PT->getPointeeType()->getAs(); - if (!FuncT) - return ExprError(Diag(LParenLoc, diag::err_typecheck_call_not_function) - << Fn->getType() << Fn->getSourceRange()); - } else if (const BlockPointerType *BPT = - Fn->getType()->getAs()) { - FuncT = BPT->getPointeeType()->castAs(); - } else { - // Handle calls to expressions of unknown-any type. - if (Fn->getType() == Context.UnknownAnyTy) { - ExprResult rewrite = rebuildUnknownAnyFunction(*this, Fn); - if (rewrite.isInvalid()) - return ExprError(); - Fn = rewrite.get(); - goto retry; - } +static const ValueDecl *findValueDecl(const Expr *E, bool *AsAddrOf = nullptr, + bool *AsDeref = nullptr) { + int AddrOfDerefDiff = 0; + const auto *VD = findValueDecl(E, &AddrOfDerefDiff); + if (AsAddrOf) + *AsAddrOf = AddrOfDerefDiff == 1; + if (AsDeref) + *AsDeref = AddrOfDerefDiff == -1; + return VD; +} - return ExprError(Diag(LParenLoc, diag::err_typecheck_call_not_function) - << Fn->getType() << Fn->getSourceRange()); - } +namespace { +class DynamicCountExprProfiler : public ConstStmtVisitor { + llvm::FoldingSetNodeID &ID; + const ASTContext &Context; +public: + DynamicCountExprProfiler(llvm::FoldingSetNodeID &ID, const ASTContext &Context) + : ID(ID), Context(Context) {} + + void VisitCastExpr(const CastExpr *E) { + Visit(E->getSubExpr()); } - // Get the number of parameters in the function prototype, if any. - // We will allocate space for max(Args.size(), NumParams) arguments - // in the call expression. - const auto *Proto = dyn_cast_or_null(FuncT); - unsigned NumParams = Proto ? Proto->getNumParams() : 0; + void VisitStmt(const Stmt *S) { + assert(S && "Requires non-null Stmt pointer"); - CallExpr *TheCall; - if (Config) { - assert(UsesADL == ADLCallKind::NotADL && - "CUDAKernelCallExpr should not use ADL"); - TheCall = CUDAKernelCallExpr::Create(Context, Fn, cast(Config), - Args, ResultTy, VK_PRValue, RParenLoc, - CurFPFeatureOverrides(), NumParams); - } else { - TheCall = - CallExpr::Create(Context, Fn, Args, ResultTy, VK_PRValue, RParenLoc, - CurFPFeatureOverrides(), NumParams, UsesADL); + HandleStmtClass(S->getStmtClass()); + + for (const Stmt *SubStmt : S->children()) { + if (SubStmt) + Visit(SubStmt); + else + ID.AddInteger(0); + } } - if (!Context.isDependenceAllowed()) { - // Forget about the nulled arguments since typo correction - // do not handle them well. - TheCall->shrinkNumArgs(Args.size()); - // C cannot always handle TypoExpr nodes in builtin calls and direct - // function calls as their argument checking don't necessarily handle - // dependent types properly, so make sure any TypoExprs have been - // dealt with. - ExprResult Result = CorrectDelayedTyposInExpr(TheCall); - if (!Result.isUsable()) return ExprError(); - CallExpr *TheOldCall = TheCall; - TheCall = dyn_cast(Result.get()); - bool CorrectedTypos = TheCall != TheOldCall; - if (!TheCall) return Result; - Args = llvm::ArrayRef(TheCall->getArgs(), TheCall->getNumArgs()); + void HandleStmtClass(Stmt::StmtClass SC) { + ID.AddInteger(SC); + } - // A new call expression node was created if some typos were corrected. - // However it may not have been constructed with enough storage. In this - // case, rebuild the node with enough storage. The waste of space is - // immaterial since this only happens when some typos were corrected. - if (CorrectedTypos && Args.size() < NumParams) { - if (Config) - TheCall = CUDAKernelCallExpr::Create( - Context, Fn, cast(Config), Args, ResultTy, VK_PRValue, - RParenLoc, CurFPFeatureOverrides(), NumParams); - else - TheCall = - CallExpr::Create(Context, Fn, Args, ResultTy, VK_PRValue, RParenLoc, - CurFPFeatureOverrides(), NumParams, UsesADL); + void HandleDeclReference(const Expr *E) { + ID.AddInteger(Stmt::DeclRefExprClass); + VisitType(E->getType()); + } + + void VisitType(QualType T) { + if (!T.isNull()) + T = Context.getCanonicalType(T); + + ID.AddPointer(T.getAsOpaquePtr()); + } + + void VisitIdentifierInfo(IdentifierInfo *II) { + ID.AddPointer(II); + } + void VisitDeclRefExpr(const DeclRefExpr *E) { + HandleDeclReference(E); + } + void VisitMemberExpr(const MemberExpr *E) { + HandleDeclReference(E); + } + void VisitUnaryOperator(const UnaryOperator *E) { + if (E->getOpcode() == UO_Deref) { + HandleDeclReference(E); + return; } - // We can now handle the nulled arguments for the default arguments. - TheCall->setNumArgsUnsafe(std::max(Args.size(), NumParams)); + VisitStmt(E); } +}; +} - // Bail out early if calling a builtin with custom type checking. - if (BuiltinID && Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) { - ExprResult E = CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall); - if (!E.isInvalid() && Context.BuiltinInfo.isImmediate(BuiltinID)) - E = CheckForImmediateInvocation(E, FDecl); - return E; +static bool compatibleDynamicCountExprs(const ASTContext &Context, Expr *LHS, Expr *RHS) { + llvm::FoldingSetNodeID LID; + llvm::FoldingSetNodeID RID; + DynamicCountExprProfiler(LID, Context).Visit(LHS); + DynamicCountExprProfiler(RID, Context).Visit(RHS); + return LID == RID; +} + +namespace { + +struct CheckSubstitutedCountExpr + : public ConstStmtVisitor { + using BaseVisitor = ConstStmtVisitor; + const ParmVarDecl *ArgPtrParmDecl; + const MemberExpr *ArgPtrMemberExpr; + + explicit CheckSubstitutedCountExpr(const ParmVarDecl *PtrParmDecl, + const MemberExpr *PtrMemberExpr) + : ArgPtrParmDecl(PtrParmDecl), ArgPtrMemberExpr(PtrMemberExpr){}; + + bool VisitStmt(const Stmt *S) { return false; } + + bool VisitImplicitCastExpr(const ImplicitCastExpr *E) { + return Visit(E->getSubExpr()); } - if (getLangOpts().CUDA) { - if (Config) { - // CUDA: Kernel calls must be to global functions - if (FDecl && !FDecl->hasAttr()) - return ExprError(Diag(LParenLoc,diag::err_kern_call_not_global_function) - << FDecl << Fn->getSourceRange()); + bool VisitParenExpr(const ParenExpr *E) { return Visit(E->getSubExpr()); } - // CUDA: Kernel function must have 'void' return type - if (!FuncT->getReturnType()->isVoidType() && - !FuncT->getReturnType()->getAs() && - !FuncT->getReturnType()->isInstantiationDependentType()) - return ExprError(Diag(LParenLoc, diag::err_kern_type_not_void_return) - << Fn->getType() << Fn->getSourceRange()); - } else { - // CUDA: Calls to global functions must be configured - if (FDecl && FDecl->hasAttr()) - return ExprError(Diag(LParenLoc, diag::err_global_call_not_config) - << FDecl << Fn->getSourceRange()); + bool VisitOpaqueValueExpr(const OpaqueValueExpr *E) { + return Visit(E->getSourceExpr()); + } + + bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; } + + bool VisitUnaryOperator(const UnaryOperator *E) { + switch (E->getOpcode()) { + case UO_AddrOf: + case UO_Deref: + return Visit(E->getSubExpr()); + default: + // An invalid opcode. + return false; } } - // Check for a valid return type - if (CheckCallReturnType(FuncT->getReturnType(), Fn->getBeginLoc(), TheCall, - FDecl)) - return ExprError(); + bool VisitBinaryOperator(const BinaryOperator *E) { + // TODO: This blocks any binary operator. This is done on purpose, since we + // don't have CodeGen tests with complex count expressions. Once we have + // them, we can use the commented out code below. + // rdar://119737451 + return false; - // We know the result type of the call, set it. - TheCall->setType(FuncT->getCallResultType(Context)); - TheCall->setValueKind(Expr::getValueKindForType(FuncT->getReturnType())); + /* + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); + if (!LHS->getType()->isIntegerType() || !RHS->getType()->isIntegerType()) + return false; + if (!Visit(LHS)) + return false; + return Visit(RHS); + */ + } - // WebAssembly tables can't be used as arguments. - if (Context.getTargetInfo().getTriple().isWasm()) { - for (const Expr *Arg : Args) { - if (Arg && Arg->getType()->isWebAssemblyTableType()) { - return ExprError(Diag(Arg->getExprLoc(), - diag::err_wasm_table_as_function_parameter)); + // Check if the decls in the count argument are parameters of the same + // function as the pointer argument. + bool VisitDeclRefExpr(const DeclRefExpr *E) { + if (!ArgPtrParmDecl) + return false; + const auto *PVD = dyn_cast(E->getDecl()); + if (!PVD || PVD->getDeclContext() != ArgPtrParmDecl->getDeclContext()) + return false; + return true; + } + + // Check if the member expressions in the count argument have the same base as + // the pointer argument. + bool VisitMemberExpr(const MemberExpr *E) { + if (!ArgPtrMemberExpr) + return false; + + const auto *Ptr = ArgPtrMemberExpr->getBase(); + const auto *Count = E->getBase(); + for (;;) { + if (Ptr == Count) + return true; + const auto *PtrICE = dyn_cast(Ptr); + const auto *CountICE = dyn_cast(Count); + if (PtrICE && CountICE && PtrICE->getCastKind() == CK_LValueToRValue && + CountICE->getCastKind() == CK_LValueToRValue) { + Ptr = PtrICE->getSubExpr(); + Count = CountICE->getSubExpr(); } + const auto *PtrDRE = dyn_cast(Ptr); + const auto *CountDRE = dyn_cast(Count); + if (PtrDRE && CountDRE) + return PtrDRE->getDecl() == CountDRE->getDecl(); + const auto *PtrME = dyn_cast(Ptr); + const auto *CountME = dyn_cast(Count); + if (!PtrME || !CountME) + return false; + if (PtrME->getMemberDecl() != CountME->getMemberDecl()) + return false; + Ptr = PtrME->getBase(); + Count = CountME->getBase(); } } +}; - if (Proto) { - if (ConvertArgumentsForCall(TheCall, Fn, FDecl, Proto, Args, RParenLoc, - IsExecConfig)) - return ExprError(); - } else { - assert(isa(FuncT) && "Unknown FunctionType!"); +struct SubstitutedCountExprPrinterHelper : public PrinterHelper { + const PrintingPolicy &Policy; + const llvm::SmallPtrSetImpl *ExprsNeedingParens; - if (FDecl) { - // Check if we have too few/too many template arguments, based - // on our knowledge of the function definition. - const FunctionDecl *Def = nullptr; - if (FDecl->hasBody(Def) && Args.size() != Def->param_size()) { - Proto = Def->getType()->getAs(); - if (!Proto || !(Proto->isVariadic() && Args.size() >= Def->param_size())) - Diag(RParenLoc, diag::warn_call_wrong_number_of_arguments) - << (Args.size() > Def->param_size()) << FDecl << Fn->getSourceRange(); + explicit SubstitutedCountExprPrinterHelper( + const PrintingPolicy &Policy, + const llvm::SmallPtrSetImpl *ExprsNeedingParens) + : Policy(Policy), ExprsNeedingParens(ExprsNeedingParens) {} + + bool handledStmt(Stmt *S, raw_ostream &OS) override { + // Print the field decl. + if (const auto *ME = dyn_cast(S)) { + ME->getMemberDecl()->printName(OS); + return true; + } + + // Simplify *&decl. + if (const auto *Outer = dyn_cast(S); + Outer && Outer->getOpcode() == UO_Deref) { + if (const auto *Inner = dyn_cast( + Outer->getSubExpr()->IgnoreParenImpCasts()); + Inner && Inner->getOpcode() == UO_AddrOf) { + if (const auto *DRE = dyn_cast( + Inner->getSubExpr()->IgnoreParenImpCasts())) { + DRE->getDecl()->printName(OS); + return true; + } } + } - // If the function we're calling isn't a function prototype, but we have - // a function prototype from a prior declaratiom, use that prototype. - if (!FDecl->hasPrototype()) - Proto = FDecl->getType()->getAs(); + // Wrap in parentheses if needed. + if (ExprsNeedingParens) { + const auto *E = dyn_cast(S); + if (ExprsNeedingParens->contains(E)) { + OS << '('; + SubstitutedCountExprPrinterHelper Helper(Policy, nullptr); + E->printPretty(OS, &Helper, Policy); + OS << ')'; + return true; + } } - // If we still haven't found a prototype to use but there are arguments to - // the call, diagnose this as calling a function without a prototype. - // However, if we found a function declaration, check to see if - // -Wdeprecated-non-prototype was disabled where the function was declared. - // If so, we will silence the diagnostic here on the assumption that this - // interface is intentional and the user knows what they're doing. We will - // also silence the diagnostic if there is a function declaration but it - // was implicitly defined (the user already gets diagnostics about the - // creation of the implicit function declaration, so the additional warning - // is not helpful). - if (!Proto && !Args.empty() && - (!FDecl || (!FDecl->isImplicit() && - !Diags.isIgnored(diag::warn_strict_uses_without_prototype, - FDecl->getLocation())))) - Diag(LParenLoc, diag::warn_strict_uses_without_prototype) - << (FDecl != nullptr) << FDecl; + // Print normally. + return false; + } +}; - // Promote the arguments (C99 6.5.2.2p6). - for (unsigned i = 0, e = Args.size(); i != e; i++) { - Expr *Arg = Args[i]; +// Try to emit a fixit when an implicit __single pointer is assigned to a +// dynamic count pointer. The fixit should contain an appropriate dynamic bound +// annotation that can be attached to the implicit __single pointer. +void TryFixSingleToDynamicCount( + Sema &S, const CountAttributedType *ParmPtrTy, const Expr *ArgPtrExpr, + const Expr *CountExpr, + const llvm::SmallPtrSetImpl &ReplacingValues) { + // Extract the decl from the pointer argument passed to dynamic count pointer + // param. + const ParmVarDecl *ArgPtrParmDecl = nullptr; + const MemberExpr *ArgPtrMemberExpr = nullptr; + const DeclaratorDecl *ArgPtrDecl = nullptr; + const Expr *P = ArgPtrExpr->IgnoreParenImpCasts(); + if (const auto *ME = dyn_cast(P)) { + ArgPtrMemberExpr = ME; + ArgPtrDecl = dyn_cast(ME->getMemberDecl()); + } else if (const auto *DRE = dyn_cast(P)) { + ArgPtrDecl = ArgPtrParmDecl = dyn_cast(DRE->getDecl()); + } + if (!ArgPtrDecl) + return; - if (Proto && i < Proto->getNumParams()) { - InitializedEntity Entity = InitializedEntity::InitializeParameter( - Context, Proto->getParamType(i), Proto->isParamConsumed(i)); - ExprResult ArgE = - PerformCopyInitialization(Entity, SourceLocation(), Arg); - if (ArgE.isInvalid()) - return true; + QualType ArgPtrTy = ArgPtrDecl->getType(); + // This should be called only if the pointer argument is 'unbounded'. + assert(ArgPtrTy->isSinglePointerType() && + !ArgPtrTy->isBoundsAttributedType() && + !ArgPtrTy->isValueTerminatedType()); - Arg = ArgE.getAs(); + // Don't emit a fixit if the decl has an explicit attr. + if (!ArgPtrTy->hasAttr(attr::PtrAutoAttr)) + return; - } else { - ExprResult ArgE = DefaultArgumentPromotion(Arg); + // Don't emit a fixit for __counted_by() when the argument pointer has a + // different pointee. + if (!ParmPtrTy->isCountInBytes() && + !S.Context.hasSameUnqualifiedType(ArgPtrTy->getPointeeType(), + ParmPtrTy->getPointeeType())) + return; - if (ArgE.isInvalid()) - return true; + // Check if the count argument is valid. + CheckSubstitutedCountExpr Check(ArgPtrParmDecl, ArgPtrMemberExpr); + bool Valid = Check.Visit(CountExpr); + if (!Valid) + return; - Arg = ArgE.getAs(); + // Wrap each count argument in parentheses if the callee's __counted_by() + // expression and the passed argument are not simple enough. + llvm::SmallPtrSet ExprsNeedingParens; + if (!isa(ParmPtrTy->getCountExpr()->IgnoreImpCasts())) { + for (const auto *Expr : ReplacingValues) { + const auto *E = Expr->IgnoreParenImpCasts(); + bool NeedsParens = true; + if (isa(E) || isa(E)) { + NeedsParens = false; + } else if (const auto *UO = dyn_cast(E)) { + NeedsParens = !(UO->getOpcode() == UO_AddrOf && + isa(UO->getSubExpr())); } + if (NeedsParens) + ExprsNeedingParens.insert(Expr); + } + } + + PrintingPolicy Policy(S.getLangOpts()); + SubstitutedCountExprPrinterHelper Helper(Policy, &ExprsNeedingParens); + + llvm::SmallString<32> Code; + llvm::raw_svector_ostream OS(Code); + OS << "__" + << (ParmPtrTy->isCountInBytes() + ? (ParmPtrTy->isOrNull() ? "sized_by_or_null" : "sized_by") + : (ParmPtrTy->isOrNull() ? "counted_by_or_null" : "counted_by")) + << '('; + CountExpr->printPretty(OS, &Helper, Policy); + OS << ") "; + StringRef FixIt = OS.str(); + StringRef Attr = FixIt.drop_back(); + + auto [FixItLoc, NeedsSpaceAfterAttr] = + BoundsSafetyFixItUtils::FindPointerAttrInsertPoint( + ArgPtrDecl->getTypeSourceInfo()->getTypeLoc(), S); + if (FixItLoc.isInvalid()) + return; + if (!NeedsSpaceAfterAttr) + FixIt = FixIt.drop_back(); - if (RequireCompleteType(Arg->getBeginLoc(), Arg->getType(), - diag::err_call_incomplete_argument, Arg)) - return ExprError(); + S.Diag(ArgPtrDecl->getLocation(), diag::note_bounds_safety_consider_adding_to) + << ArgPtrDecl->getName() << Attr + << FixItHint::CreateInsertion(FixItLoc, FixIt); +} - TheCall->setArg(i, Arg); - } - TheCall->computeDependence(); +bool checkDynamicCountSizeForAssignmentWithUnknownCount( + Sema &S, QualType LHSTy, const Expr *RHSExpr, AssignmentAction Action, + SourceLocation Loc, const Twine &Designator, bool UnboundedRHS, + const Expr *CountExpr, + const llvm::SmallPtrSetImpl &ReplacingValues) { + if (!UnboundedRHS) + return true; + + const auto *LDCPTy = LHSTy->getAs(); + QualType RHSTy = RHSExpr->getType(); + + QualType RHSElemTy = RHSTy->getPointeeType(); + int64_t ElementByteSize = S.Context.getTypeSizeInCharsIfKnown(RHSElemTy) + .value_or(CharUnits::Zero()) + .getQuantity(); + assert(ElementByteSize >= 0); + auto DesignatorStr = Designator.str(); + S.Diag(Loc, diag::warn_bounds_safety_dynamic_count_from_unbounded) + << Action << LHSTy << LDCPTy->isCountInBytes() << LDCPTy->isOrNull() + << RHSTy << !DesignatorStr.empty() << DesignatorStr << ElementByteSize + << CountExpr->getSourceRange(); + + // The count is not returned, don't say 'count returned here'. + if (Action != AssignmentAction::Returning) { + S.Diag(CountExpr->getBeginLoc(), + diag::note_bounds_safety_dynamic_count_from_unbounded_count_location) + << Action << LDCPTy->isCountInBytes(); } - if (CXXMethodDecl *Method = dyn_cast_or_null(FDecl)) - if (Method->isImplicitObjectMemberFunction()) - return ExprError(Diag(LParenLoc, diag::err_member_call_without_object) - << Fn->getSourceRange() << 0); + TryFixSingleToDynamicCount(S, LDCPTy, RHSExpr, CountExpr, ReplacingValues); + return true; +} - // Check for sentinels - if (NDecl) - DiagnoseSentinelCalls(NDecl, LParenLoc, Args); +bool checkDynamicCountSizeForAssignmentWithKnownCount( + Sema &S, QualType LHSTy, const Expr *RHSExpr, AssignmentAction Action, + SourceLocation Loc, const Twine &Designator, + const Sema::DependentValuesMap &DependentValues, bool UnboundedRHS, + const Expr *CountExpr, const llvm::APSInt &CountVal, + const llvm::SmallPtrSetImpl &ReplacingValues) { + QualType RHSTy = RHSExpr->getType(); + const auto *LDCPTy = LHSTy->getAs(); + const bool IsOrNull = LDCPTy->isOrNull(); + + const auto *DRE = dyn_cast(RHSExpr->IgnoreParenCasts()); + const ConstantArrayType *CAT = nullptr; + if (DRE) + CAT = S.Context.getAsConstantArrayType(DRE->getDecl()->getType()); + + const bool IsImplicitInitExpr = isa(RHSExpr); + const bool IsNull = + IsImplicitInitExpr || RHSExpr->isNullPointerConstantIgnoreCastsAndOVEs( + S.Context, Expr::NPC_ValueDependentIsNotNull); + const bool IsNonnull = CAT; // Array decays to nonnull pointer. + + // If the count is negative, always emit an error for __counted_by(). In + // addition, emit this error for __counted_by_or_null() if the RHS is known to + // be nonnull pointer. + if (CountVal < 0 && (!IsOrNull || IsNonnull)) { + auto DesignatorStr = Designator.str(); + S.Diag(Loc, diag::err_bounds_safety_dynamic_count_negative) + << LHSTy << LDCPTy->isCountInBytes() << CountVal.getSExtValue() + << !DesignatorStr.empty() << DesignatorStr + << CountExpr->getSourceRange(); + return false; + } - // Warn for unions passing across security boundary (CMSE). - if (FuncT != nullptr && FuncT->getCmseNSCallAttr()) { - for (unsigned i = 0, e = Args.size(); i != e; i++) { - if (const auto *RT = - dyn_cast(Args[i]->getType().getCanonicalType())) { - if (RT->getDecl()->isOrContainsUnion()) - Diag(Args[i]->getBeginLoc(), diag::warn_cmse_nonsecure_union) - << 0 << i; + if (CountVal > 0) { + const uint64_t LHSCount = CountVal.getZExtValue(); + uint64_t LHSSize = LHSCount; + if (!LDCPTy->isCountInBytes()) { + LHSSize *= S.Context.getTypeSizeInCharsIfKnown(LDCPTy->getPointeeType()) + .value_or(CharUnits::Zero()) + .getQuantity(); + } + + // The RHS is a constant array, check the count. + if (CAT) { + bool InBytes = LDCPTy->isCountInBytes() || + !S.Context.hasSameUnqualifiedType(LDCPTy->getPointeeType(), + CAT->getElementType()); + uint64_t NumLHS = LHSCount; + uint64_t NumRHS = CAT->getSize().getZExtValue(); + if (InBytes) { + NumLHS = LHSSize; + NumRHS *= + S.Context.getTypeSizeInChars(CAT->getElementType()).getQuantity(); + } + if (NumRHS < NumLHS) { + auto DesignatorStr = Designator.str(); + S.Diag(Loc, diag::err_bounds_safety_dynamic_count_from_array_bad_size) + << Action << LHSTy << InBytes << DRE->getDecl() << NumLHS << NumRHS + << !DesignatorStr.empty() << DesignatorStr + << CountExpr->getSourceRange(); + S.Diag(DRE->getDecl()->getLocation(), diag::note_entity_declared_at) + << DRE->getDecl(); + return false; } } - } - - // Do special checking on direct calls to functions. - if (FDecl) { - if (CheckFunctionCall(FDecl, TheCall, Proto)) - return ExprError(); - checkFortifiedBuiltinMemoryFunction(FDecl, TheCall); + // __single to __counted_by()/__sized_by(), check the pointee size. Don't + // emit an error for *_or_null(), since the RHS might be null. + if (UnboundedRHS && !IsOrNull) { + QualType RHSElemTy = RHSTy->getPointeeType(); + bool InBytes = LDCPTy->isCountInBytes() || + !S.Context.hasSameUnqualifiedType(LDCPTy->getPointeeType(), + RHSElemTy); + uint64_t NumLHS = LHSCount; + uint64_t NumRHS = 1; + if (InBytes) { + NumLHS = LHSSize; + // For types with unknown size (e.g., opaque types) we assume their size + // is 0 and emit an error right now rather than trapping at runtime + // (CodeGen would use 0 as their size anyway and this assignment would + // fail for any __sized_by() type with positive count). + CharUnits RHSElemSize = + S.Context.getTypeSizeInCharsIfKnown(RHSElemTy).value_or( + CharUnits::Zero()); + NumRHS *= RHSElemSize.getQuantity(); + } + if (NumRHS < NumLHS) { + auto DesignatorStr = Designator.str(); + S.Diag(Loc, diag::err_bounds_safety_dynamic_count_from_unbounded_bad_size) + << Action << LHSTy << InBytes << RHSTy << NumLHS << NumRHS + << !DesignatorStr.empty() << DesignatorStr + << CountExpr->getSourceRange(); + TryFixSingleToDynamicCount(S, LDCPTy, RHSExpr, CountExpr, + ReplacingValues); + return false; + } + } - if (BuiltinID) - return CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall); - } else if (NDecl) { - if (CheckPointerCall(NDecl, TheCall, Proto)) - return ExprError(); - } else { - if (CheckOtherCall(TheCall, Proto)) - return ExprError(); + // NULL to __counted_by()/__sized_by() with a positive count. + if (IsNull && !IsOrNull) { + auto DesignatorStr = Designator.str(); + S.Diag(Loc, diag::err_bounds_safety_dynamic_count_from_null_nonzero_count) + << Action << LHSTy << LDCPTy->isCountInBytes() + << CountVal.getZExtValue() << !DesignatorStr.empty() << DesignatorStr + << IsImplicitInitExpr << CountExpr->getSourceRange(); + return false; + } } - return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FDecl); -} - -ExprResult -Sema::ActOnCompoundLiteral(SourceLocation LParenLoc, ParsedType Ty, - SourceLocation RParenLoc, Expr *InitExpr) { - assert(Ty && "ActOnCompoundLiteral(): missing type"); - assert(InitExpr && "ActOnCompoundLiteral(): missing expression"); + // The pointer is set to some value, but the count is implicitly initialized + // to 0 (e.g., in struct initializer). This is likely to be a user's mistake, + // thus we emit a warning. + bool ImplicitCount = std::any_of(DependentValues.begin(), + DependentValues.end(), [](const auto &Item) { + const Expr *E = Item.second.first; + return isa(E); + }); + if (CountVal == 0 && !IsNull && ImplicitCount) { + auto DesignatorStr = Designator.str(); + S.Diag(Loc, + diag::warn_bounds_safety_dynamic_count_from_nonnull_implicit_zero_count) + << Action << LHSTy << LDCPTy->isCountInBytes() << !DesignatorStr.empty() + << DesignatorStr << CountExpr->getSourceRange(); + } - TypeSourceInfo *TInfo; - QualType literalType = GetTypeFromParser(Ty, &TInfo); - if (!TInfo) - TInfo = Context.getTrivialTypeSourceInfo(literalType); + // The __counted_by_or_null()/__sized_by_or_null() pointer is set to some + // unknown value with a negative count/size. Emit a warning, since this is + // likely a mistake. + if (CountVal < 0 && !IsNull && IsOrNull) { + auto DesignatorStr = Designator.str(); + S.Diag(Loc, diag::warn_bounds_safety_dynamic_count_from_nonnull_negative_count) + << Action << LHSTy << LDCPTy->isCountInBytes() + << CountVal.getSExtValue() << !DesignatorStr.empty() << DesignatorStr + << CountExpr->getSourceRange(); + } - return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, InitExpr); + return true; } -ExprResult -Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, - SourceLocation RParenLoc, Expr *LiteralExpr) { - QualType literalType = TInfo->getType(); +} // namespace - if (literalType->isArrayType()) { - if (RequireCompleteSizedType( - LParenLoc, Context.getBaseElementType(literalType), - diag::err_array_incomplete_or_sizeless_type, - SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()))) - return ExprError(); - if (literalType->isVariableArrayType()) { - // C23 6.7.10p4: An entity of variable length array type shall not be - // initialized except by an empty initializer. - // - // The C extension warnings are issued from ParseBraceInitializer() and - // do not need to be issued here. However, we continue to issue an error - // in the case there are initializers or we are compiling C++. We allow - // use of VLAs in C++, but it's not clear we want to allow {} to zero - // init a VLA in C++ in all cases (such as with non-trivial constructors). - // FIXME: should we allow this construct in C++ when it makes sense to do - // so? - // - // But: C99-C23 6.5.2.5 Compound literals constraint 1: The type name - // shall specify an object type or an array of unknown size, but not a - // variable length array type. This seems odd, as it allows 'int a[size] = - // {}', but forbids 'int *a = (int[size]){}'. As this is what the standard - // says, this is what's implemented here for C (except for the extension - // that permits constant foldable size arrays) +// Check dynamic count pointer assignment constrains. +// +// RHSExpr is the RHS of the assignment to the dynamic count pointer. RHSExpr +// can be an ImplicitValueInitExpr if the dynamic count pointer is implicitly +// initialized to null. +// +// Loc is the location where the diagnostic should be emitted. It points to the +// assign op for assignments, and dynamic count pointer argument for function +// calls. For initialization, it might point to the initializer or the '}' in +// the parent initializer if the initialization is implicit. +// +// DependentValues denotes the values assigned to dependent variables used in +// the dynamic count pointer's count expression. If the values cannot be +// inferred from the context, the map can be left empty, but the warnings will +// be less precise. +bool Sema::CheckDynamicCountSizeForAssignment( + QualType LHSTy, Expr *RHSExpr, AssignmentAction Action, + SourceLocation Loc, const Twine &Designator, + DependentValuesMap &DependentValues, Expr *LHSMemberBase) { + const auto *LDCPTy = LHSTy->getAs(); + // RHS may be a function or an array type. + // Do not diagnose here. It must be the second time to visit this conversion. + ExprResult RHS = DefaultFunctionArrayLvalueConversion( + RHSExpr, /*Diagnose*/ false, + /*DiagnoseBoundsSafetyIncompleteArrayPromotion*/ false); + if (RHS.isInvalid()) + return false; + RHSExpr = RHS.get(); - auto diagID = LangOpts.CPlusPlus - ? diag::err_variable_object_no_init - : diag::err_compound_literal_with_vla_type; - if (!tryToFixVariablyModifiedVarType(TInfo, literalType, LParenLoc, - diagID)) - return ExprError(); + QualType RHSTy = RHSExpr->getType(); + if (!LDCPTy) + return true; + + Expr *CountExpr = LDCPTy->getCountExpr()->IgnoreParenCasts(); + if (CountExpr->isValueDependent()) + return false; + + auto *RHSPTy = RHSTy->getAs(); + auto FA = + RHSPTy ? RHSPTy->getPointerAttributes() : BoundsSafetyPointerAttributes(); + // unsafe/unspecified counts as "bounded" because there shouldn't be a warning + // in manual adoption mode, else no one would be able to use it. In regular + // -fbounds-safety mode, some other part of Sema should have already have + // complained. + const auto *RDRPTy = RHSTy->getAs(); + bool UnboundedRHS = !RHSTy->isCountAttributedType() && + (!RDRPTy || !RDRPTy->getEndPointer()) && + !FA.hasUpperBound() && !FA.isUnsafeOrUnspecified(); + if (UnboundedRHS) + // It might still have a flexible array member + if (auto *RT = RHSTy->getPointeeType()->getAs()) + UnboundedRHS = !RT->getDecl()->hasFlexibleArrayMember(); + + ReplaceDeclRefWithRHS Transform(*this, DependentValues); + if (!DependentValues.empty()) { + if (LHSMemberBase) + Transform.MemberBase = LHSMemberBase; + ExprResult CountExprRes = Transform.TransformExpr(CountExpr); + if (CountExprRes.isInvalid()) + return false; + CountExpr = CountExprRes.get(); + + // The count expression might be valid, but the new value assigned may not + // be, so check again after transforming + if (CountExpr->isValueDependent()) + return false; + } + const auto &ReplacingValues = Transform.GetReplacingValues(); + + Expr::EvalResult Res; + if (CountExpr->EvaluateAsInt(Res, Context)) { + const llvm::APSInt &CountVal = Res.Val.getInt(); + return checkDynamicCountSizeForAssignmentWithKnownCount( + *this, LHSTy, RHSExpr, Action, Loc, Designator, DependentValues, + UnboundedRHS, CountExpr, CountVal, ReplacingValues); + } + + return checkDynamicCountSizeForAssignmentWithUnknownCount( + *this, LHSTy, RHSExpr, Action, Loc, Designator, UnboundedRHS, CountExpr, + ReplacingValues); +} + +enum class DAIKind { START_PTR, END_PTR, COUNT_PTR, COUNT }; + +// Information of declarations dependent on function argument or parameter. +struct DepArgInfo { + const ValueDecl *VD; + DAIKind Kind; + // Argument or Parameter index + unsigned Index; + // For COUNT_PTR/OUT_START `IsDeref` is the nested level at which the count + // attribute is added. + // In the following example, `IsDeref` is `true` for `out_buf` as `VD`. + // `void f(int *__counted_by(*out_len) *out_buf, int *out_len)` + // For COUNT/END_PTR, this is whether the value is dereferenced + // in the expression of the bounds attribute. Since `out_len` is dereferenced + // in `__counted_by` above, `IsDeref` is true for `out_len`. + bool IsDeref; + // This indicates a decl that has dependency with an argument/param but is not + // actually used for the function decl or the function call. + bool Unlisted; + + explicit DepArgInfo(const ValueDecl *VD, DAIKind Kind, unsigned Index, + bool IsDeref, bool Unlisted) + : VD(VD), Kind(Kind), Index(Index), IsDeref(IsDeref), Unlisted(Unlisted) { + } + + bool matches(const DepArgInfo &Other) const { + return Kind == Other.Kind && Index == Other.Index && + Unlisted == Other.Unlisted; + } + + const ValueDecl *getDecl() const { return VD; } + + bool isDeref() const { return IsDeref; } + bool isParam() const { return VD && isa(VD); } + bool isCountPointer() const { return Kind == DAIKind::COUNT_PTR; } + const CountAttributedType *getCountAttributedType() const { + if (!isCountPointer()) + return nullptr; + QualType Ty = getDecl()->getType(); + if (IsDeref) { + Ty = Ty->getPointeeType(); } - } else if (!literalType->isDependentType() && - RequireCompleteType(LParenLoc, literalType, - diag::err_typecheck_decl_incomplete_type, - SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()))) - return ExprError(); + return Ty->getAs(); + } +}; - InitializedEntity Entity - = InitializedEntity::InitializeCompoundLiteralInit(TInfo); - InitializationKind Kind - = InitializationKind::CreateCStyleCast(LParenLoc, - SourceRange(LParenLoc, RParenLoc), - /*InitList=*/true); - InitializationSequence InitSeq(*this, Entity, Kind, LiteralExpr); - ExprResult Result = InitSeq.Perform(*this, Entity, Kind, LiteralExpr, - &literalType); - if (Result.isInvalid()) - return ExprError(); - LiteralExpr = Result.get(); +// Argument/parameter information necessary for out parameter analysis. +class DynamicBoundArgumentInfo { - // We treat the compound literal as being at file scope if it's not in a - // function or method body, or within the function's prototype scope. This - // means the following compound literal is not at file scope: - // void func(char *para[(int [1]){ 0 }[0]); - const Scope *S = getCurScope(); - bool IsFileScope = !CurContext->isFunctionOrMethod() && - (!S || !S->isFunctionPrototypeScope()); + const BoundsAttributedType *DBPTy; + const ValueDecl *BaseDecl; - // In C, compound literals are l-values for some reason. - // For GCC compatibility, in C++, file-scope array compound literals with - // constant initializers are also l-values, and compound literals are - // otherwise prvalues. + unsigned Level; + bool IsAddrOf : 1; + + // IsCount is true iff there exists a pointer param that depends on this + // count. IsCountInRet/IsEndInRet is true iff the return type depends on this + // count/end. Example values for the len param: + // void f(int *__counted_by(len) p, int len): + // IsCountInParam=1 IsCountInRet=0 // - // (GCC also treats C++ list-initialized file-scope array prvalues with - // constant initializers as l-values, but that's non-conforming, so we don't - // follow it there.) + // int *__counted_by(len) f(int len): + // IsCountInParam=0 IsCountInRet=1 // - // FIXME: It would be better to handle the lvalue cases as materializing and - // lifetime-extending a temporary object, but our materialized temporaries - // representation only supports lifetime extension from a variable, not "out - // of thin air". - // FIXME: For C++, we might want to instead lifetime-extend only if a pointer - // is bound to the result of applying array-to-pointer decay to the compound - // literal. - // FIXME: GCC supports compound literals of reference type, which should - // obviously have a value kind derived from the kind of reference involved. - ExprValueKind VK = - (getLangOpts().CPlusPlus && !(IsFileScope && literalType->isArrayType())) - ? VK_PRValue - : VK_LValue; + // int *__counted_by(len) f(int *__counted_by(len) p, int len): + // IsCountInParam=1 IsCountInRet=1 + bool IsCountInParam : 1; + bool IsCountInRet : 1; + bool IsEndInRet : 1; - if (IsFileScope) - if (auto ILE = dyn_cast(LiteralExpr)) - for (unsigned i = 0, j = ILE->getNumInits(); i != j; i++) { - Expr *Init = ILE->getInit(i); - ILE->setInit(i, ConstantExpr::Create(Context, Init)); - } + // DepArgInfos for call arguments are produced after the constructor. This is + // to keep this state. + bool HasValidDepInfo : 1; + SmallVector DepArgInfos; - auto *E = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, VK, - LiteralExpr, IsFileScope); - if (IsFileScope) { - if (!LiteralExpr->isTypeDependent() && - !LiteralExpr->isValueDependent() && - !literalType->isDependentType()) // C99 6.5.2.5p3 - if (CheckForConstantInitializer(LiteralExpr)) - return ExprError(); - } else if (literalType.getAddressSpace() != LangAS::opencl_private && - literalType.getAddressSpace() != LangAS::Default) { - // Embedded-C extensions to C99 6.5.2.5: - // "If the compound literal occurs inside the body of a function, the - // type name shall not be qualified by an address-space qualifier." - Diag(LParenLoc, diag::err_compound_literal_with_address_space) - << SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()); - return ExprError(); + void processDependentParamOfReturnType() { + IsCountInRet = false; + IsEndInRet = false; + + const BoundsAttributedType *RetType = nullptr; + const TypeCoupledDeclRefInfo *Info = nullptr; + if (BaseDecl && BaseDecl->isDependentParamOfReturnType(&RetType, &Info)) { + bool IsCAT = isa(RetType); + IsCountInRet = IsCAT; + IsEndInRet = !IsCAT; + assert(!IsEndInRet || isa(RetType)); + assert(!IsCountInParam || Level == Info->isDeref()); + Level = Info->isDeref(); + } } - if (!IsFileScope && !getLangOpts().CPlusPlus) { - // Compound literals that have automatic storage duration are destroyed at - // the end of the scope in C; in C++, they're just temporaries. +public: + using DeclIndexMapTy = + llvm::DenseMap>; + + DynamicBoundArgumentInfo(ASTContext &Ctx, Expr *Arg) + : Level(0), HasValidDepInfo(false) { + Expr *E = Arg->IgnoreImpCasts(); + QualType ArgTy = E->getType(); + + bool LocalIsAddrOf = false; + bool LocalIsDeref = false; + BaseDecl = findValueDecl(E, &LocalIsAddrOf, &LocalIsDeref); + IsAddrOf = LocalIsAddrOf; + + DBPTy = ArgTy->getAs(); + if (!DBPTy && ArgTy->isPointerType() && + (DBPTy = ArgTy->getPointeeType()->getAs())) { + Level++; + } + if (BaseDecl && BaseDecl->hasAttr()) { + IsCountInParam = true; + if (BaseDecl->getType()->isPointerType() && !LocalIsDeref) + Level = 1; + } else { + IsCountInParam = false; + } - // Emit diagnostics if it is or contains a C union type that is non-trivial - // to destruct. - if (E->getType().hasNonTrivialToPrimitiveDestructCUnion()) - checkNonTrivialCUnion(E->getType(), E->getExprLoc(), - NTCUC_CompoundLiteral, NTCUK_Destruct); + processDependentParamOfReturnType(); - // Diagnose jumps that enter or exit the lifetime of the compound literal. - if (literalType.isDestructedType()) { - Cleanup.setExprNeedsCleanups(true); - ExprCleanupObjects.push_back(E); - getCurFunction()->setHasBranchProtectedScope(); - } + // Fill out DepArgInfos later via processDepArgInfos() after + // the declaration to argument index map has been produced. } - if (E->getType().hasNonTrivialToPrimitiveDefaultInitializeCUnion() || - E->getType().hasNonTrivialToPrimitiveCopyCUnion()) - checkNonTrivialCUnionInInitializer(E->getInitializer(), - E->getInitializer()->getExprLoc()); + DynamicBoundArgumentInfo(const ParmVarDecl *BaseDecl) + : BaseDecl(BaseDecl), Level(0), IsAddrOf(false), HasValidDepInfo(true) { + QualType ParmTy = BaseDecl->getType(); + DBPTy = ParmTy->getAs(); + if (!DBPTy && ParmTy->isPointerType() && + (DBPTy = ParmTy->getPointeeType()->getAs())) { + Level++; + } - return MaybeBindToTemporary(E); -} + if (BaseDecl->hasAttr()) { + IsCountInParam = true; + if (BaseDecl->getType()->isPointerType()) + Level = 1; + } else { + IsCountInParam = false; + } -ExprResult -Sema::ActOnInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList, - SourceLocation RBraceLoc) { - // Only produce each kind of designated initialization diagnostic once. - SourceLocation FirstDesignator; - bool DiagnosedArrayDesignator = false; - bool DiagnosedNestedDesignator = false; - bool DiagnosedMixedDesignator = false; + processDependentParamOfReturnType(); - // Check that any designated initializers are syntactically valid in the - // current language mode. - for (unsigned I = 0, E = InitArgList.size(); I != E; ++I) { - if (auto *DIE = dyn_cast(InitArgList[I])) { - if (FirstDesignator.isInvalid()) - FirstDesignator = DIE->getBeginLoc(); + auto processDepInfo = [&](const Decl *D, bool IsDeref, DAIKind Kind) { + const auto *VD = cast(D); + unsigned Index = 0; + bool Unlisted = false; + if (const auto *PVD = dyn_cast(VD)) { + Index = PVD->getFunctionScopeIndex(); + } else { + Unlisted = true; + } - if (!getLangOpts().CPlusPlus) - break; + DepArgInfos.emplace_back(VD, Kind, Index, IsDeref, Unlisted); + }; - if (!DiagnosedNestedDesignator && DIE->size() > 1) { - DiagnosedNestedDesignator = true; - Diag(DIE->getBeginLoc(), diag::ext_designated_init_nested) - << DIE->getDesignatorsSourceRange(); + if (isCountInParam()) { + if (const auto *Att = BaseDecl->getAttr()) { + processCountDepInfo(Att, processDepInfo); } + return; + } - for (auto &Desig : DIE->designators()) { - if (!Desig.isFieldDesignator() && !DiagnosedArrayDesignator) { - DiagnosedArrayDesignator = true; - Diag(Desig.getBeginLoc(), diag::ext_designated_init_array) - << Desig.getSourceRange(); - } + if (!DBPTy) + return; + + if (const auto *DCPTy = dyn_cast(DBPTy)) { + for (const auto &DI : DCPTy->dependent_decls()) { + processDepInfo(DI.getDecl(), DI.isDeref(), DAIKind::COUNT); } + return; + } - if (!DiagnosedMixedDesignator && - !isa(InitArgList[0])) { - DiagnosedMixedDesignator = true; - Diag(DIE->getBeginLoc(), diag::ext_designated_init_mixed) - << DIE->getSourceRange(); - Diag(InitArgList[0]->getBeginLoc(), diag::note_designated_init_mixed) - << InitArgList[0]->getSourceRange(); + if (const auto *DRPTy = dyn_cast(DBPTy)) { + for (const auto &DI : DRPTy->startptr_decls()) { + processDepInfo(DI.getDecl(), DI.isDeref(), DAIKind::START_PTR); } - } else if (getLangOpts().CPlusPlus && !DiagnosedMixedDesignator && - isa(InitArgList[0])) { - DiagnosedMixedDesignator = true; - auto *DIE = cast(InitArgList[0]); - Diag(DIE->getBeginLoc(), diag::ext_designated_init_mixed) - << DIE->getSourceRange(); - Diag(InitArgList[I]->getBeginLoc(), diag::note_designated_init_mixed) - << InitArgList[I]->getSourceRange(); + for (const auto &DI : DRPTy->endptr_decls()) { + processDepInfo(DI.getDecl(), DI.isDeref(), DAIKind::END_PTR); + } + return; } } - if (FirstDesignator.isValid()) { - // Only diagnose designated initiaization as a C++20 extension if we didn't - // already diagnose use of (non-C++20) C99 designator syntax. - if (getLangOpts().CPlusPlus && !DiagnosedArrayDesignator && - !DiagnosedNestedDesignator && !DiagnosedMixedDesignator) { - Diag(FirstDesignator, getLangOpts().CPlusPlus20 - ? diag::warn_cxx17_compat_designated_init - : diag::ext_cxx_designated_init); - } else if (!getLangOpts().CPlusPlus && !getLangOpts().C99) { - Diag(FirstDesignator, diag::ext_designated_init); - } + const SmallVectorImpl &getDepArgInfos() const { + return DepArgInfos; } - return BuildInitList(LBraceLoc, InitArgList, RBraceLoc); -} + SmallVectorImpl::iterator depinfo_begin() { + return DepArgInfos.begin(); + } -ExprResult -Sema::BuildInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList, - SourceLocation RBraceLoc) { - // Semantic analysis for initializers is done by ActOnDeclarator() and - // CheckInitializer() - it requires knowledge of the object being initialized. + SmallVectorImpl::iterator depinfo_end() { + return DepArgInfos.end(); + } - // Immediately handle non-overload placeholders. Overloads can be - // resolved contextually, but everything else here can't. - for (unsigned I = 0, E = InitArgList.size(); I != E; ++I) { - if (InitArgList[I]->getType()->isNonOverloadPlaceholderType()) { - ExprResult result = CheckPlaceholderExpr(InitArgList[I]); + SmallVectorImpl::const_iterator depinfo_begin() const { + return DepArgInfos.begin(); + } - // Ignore failures; dropping the entire initializer list because - // of one failure would be terrible for indexing/etc. - if (result.isInvalid()) continue; + SmallVectorImpl::const_iterator depinfo_end() const { + return DepArgInfos.end(); + } - InitArgList[I] = result.get(); - } + unsigned getLevel() const { return Level; } + + bool isBoundsAttributedType() const { return !!DBPTy; } + + bool isDynamicRangePointerType() const { + return DBPTy && isa(DBPTy); } - InitListExpr *E = - new (Context) InitListExpr(Context, LBraceLoc, InitArgList, RBraceLoc); - E->setType(Context.VoidTy); // FIXME: just a place holder for now. - return E; -} + bool isCountInParam() const { return IsCountInParam; } -void Sema::maybeExtendBlockObject(ExprResult &E) { - assert(E.get()->getType()->isBlockPointerType()); - assert(E.get()->isPRValue()); + bool isCountInRet() const { return IsCountInRet; } - // Only do this in an r-value context. - if (!getLangOpts().ObjCAutoRefCount) return; + bool isEndInRet() const { return IsEndInRet; } - E = ImplicitCastExpr::Create( - Context, E.get()->getType(), CK_ARCExtendBlockObject, E.get(), - /*base path*/ nullptr, VK_PRValue, FPOptionsOverride()); - Cleanup.setExprNeedsCleanups(true); -} - -CastKind Sema::PrepareScalarCast(ExprResult &Src, QualType DestTy) { - // Both Src and Dest are scalar types, i.e. arithmetic or pointer. - // Also, callers should have filtered out the invalid cases with - // pointers. Everything else should be possible. - - QualType SrcTy = Src.get()->getType(); - if (Context.hasSameUnqualifiedType(SrcTy, DestTy)) - return CK_NoOp; + bool isCountInParamOrCountPointer() const { + return IsCountInParam || isCountAttributedType(); + } - switch (Type::ScalarTypeKind SrcKind = SrcTy->getScalarTypeKind()) { - case Type::STK_MemberPointer: - llvm_unreachable("member pointer type in C"); + bool isCountAttributedType() const { + return DBPTy && isa(DBPTy); + } - case Type::STK_CPointer: - case Type::STK_BlockPointer: - case Type::STK_ObjCObjectPointer: - switch (DestTy->getScalarTypeKind()) { - case Type::STK_CPointer: { - LangAS SrcAS = SrcTy->getPointeeType().getAddressSpace(); - LangAS DestAS = DestTy->getPointeeType().getAddressSpace(); - if (SrcAS != DestAS) - return CK_AddressSpaceConversion; - if (Context.hasCvrSimilarType(SrcTy, DestTy)) - return CK_NoOp; - return CK_BitCast; - } - case Type::STK_BlockPointer: - return (SrcKind == Type::STK_BlockPointer - ? CK_BitCast : CK_AnyPointerToBlockPointerCast); - case Type::STK_ObjCObjectPointer: - if (SrcKind == Type::STK_ObjCObjectPointer) - return CK_BitCast; - if (SrcKind == Type::STK_CPointer) - return CK_CPointerToObjCPointerCast; - maybeExtendBlockObject(Src); - return CK_BlockPointerToObjCPointerCast; - case Type::STK_Bool: - return CK_PointerToBoolean; - case Type::STK_Integral: - return CK_PointerToIntegral; - case Type::STK_Floating: - case Type::STK_FloatingComplex: - case Type::STK_IntegralComplex: - case Type::STK_MemberPointer: - case Type::STK_FixedPoint: - llvm_unreachable("illegal cast from pointer"); - } - llvm_unreachable("Should have returned before this"); + const CountAttributedType *getCountAttributedType() const { + return dyn_cast_or_null(DBPTy); + } - case Type::STK_FixedPoint: - switch (DestTy->getScalarTypeKind()) { - case Type::STK_FixedPoint: - return CK_FixedPointCast; - case Type::STK_Bool: - return CK_FixedPointToBoolean; - case Type::STK_Integral: - return CK_FixedPointToIntegral; - case Type::STK_Floating: - return CK_FixedPointToFloating; - case Type::STK_IntegralComplex: - case Type::STK_FloatingComplex: - Diag(Src.get()->getExprLoc(), - diag::err_unimplemented_conversion_with_fixed_point_type) - << DestTy; - return CK_IntegralCast; - case Type::STK_CPointer: - case Type::STK_ObjCObjectPointer: - case Type::STK_BlockPointer: - case Type::STK_MemberPointer: - llvm_unreachable("illegal cast to pointer type"); - } - llvm_unreachable("Should have returned before this"); + bool isOutParameter() const { return Level != 0 || IsAddrOf; } - case Type::STK_Bool: // casting from bool is like casting from an integer - case Type::STK_Integral: - switch (DestTy->getScalarTypeKind()) { - case Type::STK_CPointer: - case Type::STK_ObjCObjectPointer: - case Type::STK_BlockPointer: - if (Src.get()->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNull)) - return CK_NullToPointer; - return CK_IntegralToPointer; - case Type::STK_Bool: - return CK_IntegralToBoolean; - case Type::STK_Integral: - return CK_IntegralCast; - case Type::STK_Floating: - return CK_IntegralToFloating; - case Type::STK_IntegralComplex: - Src = ImpCastExprToType(Src.get(), - DestTy->castAs()->getElementType(), - CK_IntegralCast); - return CK_IntegralRealToComplex; - case Type::STK_FloatingComplex: - Src = ImpCastExprToType(Src.get(), - DestTy->castAs()->getElementType(), - CK_IntegralToFloating); - return CK_FloatingRealToComplex; - case Type::STK_MemberPointer: - llvm_unreachable("member pointer type in C"); - case Type::STK_FixedPoint: - return CK_IntegralToFixedPoint; - } - llvm_unreachable("Should have returned before this"); + bool isOutRangePointer() const { + return isDynamicRangePointerType() && isOutParameter(); + } - case Type::STK_Floating: - switch (DestTy->getScalarTypeKind()) { - case Type::STK_Floating: - return CK_FloatingCast; - case Type::STK_Bool: - return CK_FloatingToBoolean; - case Type::STK_Integral: - return CK_FloatingToIntegral; - case Type::STK_FloatingComplex: - Src = ImpCastExprToType(Src.get(), - DestTy->castAs()->getElementType(), - CK_FloatingCast); - return CK_FloatingRealToComplex; - case Type::STK_IntegralComplex: - Src = ImpCastExprToType(Src.get(), - DestTy->castAs()->getElementType(), - CK_FloatingToIntegral); - return CK_IntegralRealToComplex; - case Type::STK_CPointer: - case Type::STK_ObjCObjectPointer: - case Type::STK_BlockPointer: - llvm_unreachable("valid float->pointer cast?"); - case Type::STK_MemberPointer: - llvm_unreachable("member pointer type in C"); - case Type::STK_FixedPoint: - return CK_FloatingToFixedPoint; - } - llvm_unreachable("Should have returned before this"); + bool isOutCountPointer() const { + return isCountAttributedType() && isOutParameter(); + } - case Type::STK_FloatingComplex: - switch (DestTy->getScalarTypeKind()) { - case Type::STK_FloatingComplex: - return CK_FloatingComplexCast; - case Type::STK_IntegralComplex: - return CK_FloatingComplexToIntegralComplex; - case Type::STK_Floating: { - QualType ET = SrcTy->castAs()->getElementType(); - if (Context.hasSameType(ET, DestTy)) - return CK_FloatingComplexToReal; - Src = ImpCastExprToType(Src.get(), ET, CK_FloatingComplexToReal); - return CK_FloatingCast; - } - case Type::STK_Bool: - return CK_FloatingComplexToBoolean; - case Type::STK_Integral: - Src = ImpCastExprToType(Src.get(), - SrcTy->castAs()->getElementType(), - CK_FloatingComplexToReal); - return CK_FloatingToIntegral; - case Type::STK_CPointer: - case Type::STK_ObjCObjectPointer: - case Type::STK_BlockPointer: - llvm_unreachable("valid complex float->pointer cast?"); - case Type::STK_MemberPointer: - llvm_unreachable("member pointer type in C"); - case Type::STK_FixedPoint: - Diag(Src.get()->getExprLoc(), - diag::err_unimplemented_conversion_with_fixed_point_type) - << SrcTy; - return CK_IntegralCast; - } - llvm_unreachable("Should have returned before this"); + bool isIncompleteArray() const { return DBPTy->isIncompleteArrayType(); } - case Type::STK_IntegralComplex: - switch (DestTy->getScalarTypeKind()) { - case Type::STK_FloatingComplex: - return CK_IntegralComplexToFloatingComplex; - case Type::STK_IntegralComplex: - return CK_IntegralComplexCast; - case Type::STK_Integral: { - QualType ET = SrcTy->castAs()->getElementType(); - if (Context.hasSameType(ET, DestTy)) - return CK_IntegralComplexToReal; - Src = ImpCastExprToType(Src.get(), ET, CK_IntegralComplexToReal); - return CK_IntegralCast; - } - case Type::STK_Bool: - return CK_IntegralComplexToBoolean; - case Type::STK_Floating: - Src = ImpCastExprToType(Src.get(), - SrcTy->castAs()->getElementType(), - CK_IntegralComplexToReal); - return CK_IntegralToFloating; - case Type::STK_CPointer: - case Type::STK_ObjCObjectPointer: - case Type::STK_BlockPointer: - llvm_unreachable("valid complex int->pointer cast?"); - case Type::STK_MemberPointer: - llvm_unreachable("member pointer type in C"); - case Type::STK_FixedPoint: - Diag(Src.get()->getExprLoc(), - diag::err_unimplemented_conversion_with_fixed_point_type) - << SrcTy; - return CK_IntegralCast; - } - llvm_unreachable("Should have returned before this"); + bool isInOutCountPointer() const { + if (!isCountAttributedType() || isOutParameter()) + return false; + return std::any_of(depinfo_begin(), depinfo_end(), + [](auto &DepInfo) { return DepInfo.isDeref(); }); } - llvm_unreachable("Unhandled scalar cast"); -} + bool isOutCountInParam() const { return IsCountInParam && isOutParameter(); } -static bool breakDownVectorType(QualType type, uint64_t &len, - QualType &eltType) { - // Vectors are simple. - if (const VectorType *vecType = type->getAs()) { - len = vecType->getNumElements(); - eltType = vecType->getElementType(); - assert(eltType->isScalarType() || eltType->isMFloat8Type()); - return true; + bool isOutCountInParamOrPointer() const { + return isOutCountInParam() || isOutCountPointer(); } - // We allow lax conversion to and from non-vector types, but only if - // they're real types (i.e. non-complex, non-pointer scalar types). - if (!type->isRealType()) return false; + bool isParmDecl() const { return BaseDecl && isa(BaseDecl); } - len = 1; - eltType = type; - return true; -} + bool isAddrOf() const { return IsAddrOf; } -bool Sema::isValidSveBitcast(QualType srcTy, QualType destTy) { - assert(srcTy->isVectorType() || destTy->isVectorType()); + bool isAddrOfInBufParam() const { + return isBoundsAttributedType() && isParmDecl() && isAddrOf() && + Level == 1; + } - auto ValidScalableConversion = [](QualType FirstType, QualType SecondType) { - if (!FirstType->isSVESizelessBuiltinType()) - return false; + QualType getType() const { return QualType(DBPTy, 0); } - const auto *VecTy = SecondType->getAs(); - return VecTy && VecTy->getVectorKind() == VectorKind::SveFixedLengthData; - }; + const ValueDecl *getDecl() const { return BaseDecl; } - return ValidScalableConversion(srcTy, destTy) || - ValidScalableConversion(destTy, srcTy); -} + void processCountDepInfo( + const DependerDeclsAttr *Att, + const std::function &processDepInfo) { + for (unsigned i = 0; i < Att->dependerDecls_size(); ++i) { + const auto *D = Att->dependerDecls_begin()[i]; + unsigned DependerLevel = Att->dependerLevels_begin()[i]; + assert(DependerLevel < 2 && + "External bounds attributes can only be added to" + "a pointer which is one-level nested at most"); + bool IsDeref = DependerLevel; + processDepInfo(D, IsDeref, DAIKind::COUNT_PTR); + } + } -bool Sema::areMatrixTypesOfTheSameDimension(QualType srcTy, QualType destTy) { - if (!destTy->isMatrixType() || !srcTy->isMatrixType()) - return false; + /// \p DeclIndexMap is a map from a decl used in an argument expression to the + /// call argument index. + void processDepArgInfos(const DeclIndexMapTy &DeclIndexMap) { + HasValidDepInfo = true; - const ConstantMatrixType *matSrcType = srcTy->getAs(); - const ConstantMatrixType *matDestType = destTy->getAs(); + auto processDepInfo = [&](const Decl *D, bool IsDeref, DAIKind Kind) { + const auto *VD = cast(D); + auto I = DeclIndexMap.find(VD); + if (I == DeclIndexMap.end()) { + DepArgInfos.emplace_back(VD, Kind, 0, IsDeref, /*Unlisted*/true); + return; + } + for (auto Index : I->second) { + DepArgInfos.emplace_back(VD, Kind, Index, IsDeref, /*Unlisted*/false); + } + }; - return matSrcType->getNumRows() == matDestType->getNumRows() && - matSrcType->getNumColumns() == matDestType->getNumColumns(); -} + if (isCountInParam()) { + if (const auto *Att = getDecl()->getAttr()) { + processCountDepInfo(Att, processDepInfo); + } + return; + } -bool Sema::areVectorTypesSameSize(QualType SrcTy, QualType DestTy) { - assert(DestTy->isVectorType() || SrcTy->isVectorType()); + if (!DBPTy) + return; - uint64_t SrcLen, DestLen; - QualType SrcEltTy, DestEltTy; - if (!breakDownVectorType(SrcTy, SrcLen, SrcEltTy)) - return false; - if (!breakDownVectorType(DestTy, DestLen, DestEltTy)) - return false; + if (const auto *DCPTy = dyn_cast(DBPTy)) { + for (const auto &DI : DCPTy->dependent_decls()) { + processDepInfo(DI.getDecl(), DI.isDeref(), DAIKind::COUNT); + } + return; + } - // ASTContext::getTypeSize will return the size rounded up to a - // power of 2, so instead of using that, we need to use the raw - // element size multiplied by the element count. - uint64_t SrcEltSize = Context.getTypeSize(SrcEltTy); - uint64_t DestEltSize = Context.getTypeSize(DestEltTy); + if (const auto *DRPTy = dyn_cast(DBPTy)) { + for (const auto &DI : DRPTy->startptr_decls()) { + processDepInfo(DI.getDecl(), DI.isDeref(), DAIKind::START_PTR); + } + for (const auto &DI : DRPTy->endptr_decls()) { + processDepInfo(DI.getDecl(), DI.isDeref(), DAIKind::END_PTR); + } + return; + } + } +}; - return (SrcLen * SrcEltSize == DestLen * DestEltSize); -} +static bool checkDynamicCountPointerAsParameter(Sema &S, FunctionDecl *FDecl, + CallExpr *Call) { + if (Call->containsErrors()) + return false; + // Initialize argument/parameter information + llvm::SmallPtrSet OutCountDecls; + using PointerOutPair = llvm::PointerIntPair; + llvm::SmallPtrSet PointerDecls; -bool Sema::anyAltivecTypes(QualType SrcTy, QualType DestTy) { - assert((DestTy->isVectorType() || SrcTy->isVectorType()) && - "expected at least one type to be a vector here"); + llvm::SmallVector CallArgInfos; + DynamicBoundArgumentInfo::DeclIndexMapTy DeclIndexMap; - bool IsSrcTyAltivec = - SrcTy->isVectorType() && ((SrcTy->castAs()->getVectorKind() == - VectorKind::AltiVecVector) || - (SrcTy->castAs()->getVectorKind() == - VectorKind::AltiVecBool) || - (SrcTy->castAs()->getVectorKind() == - VectorKind::AltiVecPixel)); + for (unsigned i = 0; i < Call->getNumArgs(); ++i) { + CallArgInfos.emplace_back(S.Context, Call->getArg(i)); + if (auto *D = CallArgInfos.back().getDecl()) + DeclIndexMap[D].push_back(i); + } - bool IsDestTyAltivec = DestTy->isVectorType() && - ((DestTy->castAs()->getVectorKind() == - VectorKind::AltiVecVector) || - (DestTy->castAs()->getVectorKind() == - VectorKind::AltiVecBool) || - (DestTy->castAs()->getVectorKind() == - VectorKind::AltiVecPixel)); + for (auto &ArgInfo : CallArgInfos) { + ArgInfo.processDepArgInfos(DeclIndexMap); + } - return (IsSrcTyAltivec || IsDestTyAltivec); -} + llvm::SmallVector FuncParmInfos; + for (unsigned i = 0; i < FDecl->getNumParams(); ++i) { + FuncParmInfos.emplace_back(FDecl->getParamDecl(i)); + } -bool Sema::areLaxCompatibleVectorTypes(QualType srcTy, QualType destTy) { - assert(destTy->isVectorType() || srcTy->isVectorType()); + unsigned MinNumArgs = std::min(Call->getNumArgs(), FDecl->getNumParams()); - // Disallow lax conversions between scalars and ExtVectors (these - // conversions are allowed for other vector types because common headers - // depend on them). Most scalar OP ExtVector cases are handled by the - // splat path anyway, which does what we want (convert, not bitcast). - // What this rules out for ExtVectors is crazy things like char4*float. - if (srcTy->isScalarType() && destTy->isExtVectorType()) return false; - if (destTy->isScalarType() && srcTy->isExtVectorType()) return false; + Sema::DependentValuesMap DependentValues; + for (unsigned i = 0; i < MinNumArgs; ++i) { + const ParmVarDecl *D = FDecl->getParamDecl(i); + Expr *Arg = Call->getArg(i); + DependentValues[D] = {Arg, /*Level=*/0}; + } - return areVectorTypesSameSize(srcTy, destTy); -} + // FIXME: Report an error when a pointer to dynamic count or a pointer to + // count pointer is passed as va_arg (rdar://97041755). + for (unsigned i = 0; i < MinNumArgs; ++i) { + const auto &ArgInfo = CallArgInfos[i]; + const auto &ParmInfo = FuncParmInfos[i]; + Expr *ActualArgExp = Call->getArg(i)->IgnoreImpCasts(); + SourceLocation ArgLoc = ActualArgExp->getExprLoc(); -bool Sema::isLaxVectorConversion(QualType srcTy, QualType destTy) { - assert(destTy->isVectorType() || srcTy->isVectorType()); + if (!ArgInfo.isCountInParamOrCountPointer() && !ArgInfo.isCountInRet() && + !ParmInfo.isCountInParamOrCountPointer() && !ParmInfo.isCountInRet()) + continue; - switch (Context.getLangOpts().getLaxVectorConversions()) { - case LangOptions::LaxVectorConversionKind::None: - return false; + // Disable these checks for attribute-only mode because we don't want + // non-type-incompatibility errors in that mode. + if (!S.getLangOpts().isBoundsSafetyAttributeOnlyMode() && + ParmInfo.isCountAttributedType() && !ParmInfo.isOutCountPointer() && + !S.CheckDynamicCountSizeForAssignment( + ParmInfo.getType(), ActualArgExp, AssignmentAction::Passing, + ActualArgExp->getBeginLoc(), ParmInfo.getDecl()->getName(), + DependentValues) && + !S.allowBoundsUnsafeFunctionArg(Call, i)) { + return false; + } - case LangOptions::LaxVectorConversionKind::Integer: - if (!srcTy->isIntegralOrEnumerationType()) { - auto *Vec = srcTy->getAs(); - if (!Vec || !Vec->getElementType()->isIntegralOrEnumerationType()) - return false; + if (ParmInfo.isOutCountPointer() && !ArgInfo.isOutCountPointer()) { + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_dynamic_count_argument) + << ParmInfo.getDecl()->getType() << Call->getSourceRange(); + return false; } - if (!destTy->isIntegralOrEnumerationType()) { - auto *Vec = destTy->getAs(); - if (!Vec || !Vec->getElementType()->isIntegralOrEnumerationType()) + + if (!ParmInfo.isOutCountPointer() && ArgInfo.isOutCountPointer()) { + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_dynamic_count_argument) + << ParmInfo.getDecl()->getType() << Call->getSourceRange(); + return false; + } + + // TODO: This diagnostic check should always be performed + // (rdar://138982703). The check is currently guarded to avoid potentially + // breaking the build. + if (!S.getLangOpts().isBoundsSafetyAttributeOnlyMode() && + ArgInfo.isCountInRet() && S.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_ReturnSize)) { + bool IsIndirect = ActualArgExp->getType()->isPointerType(); + bool IsOutCountInRetToOutCountInRet = + ArgInfo.getLevel() != 0 && ParmInfo.isCountInRet() && + ParmInfo.getLevel() != 0 && !ParmInfo.isOutCountInParam(); + bool IsOutCountInRetToInoutCount = + ArgInfo.getLevel() != 0 && ParmInfo.isOutCountInParam() && + std::none_of(ParmInfo.depinfo_begin(), ParmInfo.depinfo_end(), + [](const DepArgInfo &ParmDepInfo) { + return ParmDepInfo.isDeref(); + }); + if (!(!IsIndirect || IsOutCountInRetToOutCountInRet || + IsOutCountInRetToInoutCount)) { + const auto *ArgPVD = cast(ArgInfo.getDecl()); + const auto *Caller = cast(ArgPVD->getDeclContext()); + const auto *RetCATy = + Caller->getReturnType()->getAs(); + S.Diag( + ArgLoc, + diag::err_bounds_safety_cannot_pass_read_only_dependent_param_in_return) + << ArgPVD->getName() << RetCATy->getKind() << Caller->getName() + << Caller->getReturnType(); return false; + } } - // OK, integer (vector) -> integer (vector) bitcast. - break; - case LangOptions::LaxVectorConversionKind::All: - break; - } + if (ParmInfo.isOutCountInParam() && !ArgInfo.isOutCountInParam()) { + if (std::none_of(ParmInfo.depinfo_begin(), ParmInfo.depinfo_end(), + [](const DepArgInfo &ParmDepInfo) { + return ParmDepInfo.isDeref(); })) { + continue; + } + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_dynamic_count_argument) + << ParmInfo.getDecl()->getType() << Call->getSourceRange(); + return false; + } - return areLaxCompatibleVectorTypes(srcTy, destTy); -} + if (!ParmInfo.isOutCountInParam() && ArgInfo.isOutCountInParam()) { + const CountAttributedType *DCPTy = nullptr; + for (const auto &ArgDepInfo : ArgInfo.getDepArgInfos()) { + if (ArgDepInfo.isCountPointer()) { + DCPTy = ArgDepInfo.getCountAttributedType(); + break; + } + } + assert(DCPTy); + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_indirect_count_parameter) + << ArgInfo.getDecl() << ArgInfo.isAddrOf() << DCPTy->getKind() + << Call->getSourceRange(); + return false; + } -bool Sema::CheckMatrixCast(SourceRange R, QualType DestTy, QualType SrcTy, - CastKind &Kind) { - if (SrcTy->isMatrixType() && DestTy->isMatrixType()) { - if (!areMatrixTypesOfTheSameDimension(SrcTy, DestTy)) { - return Diag(R.getBegin(), diag::err_invalid_conversion_between_matrixes) - << DestTy << SrcTy << R; + if (ArgInfo.isAddrOfInBufParam()) { + // This is to handle the code like below: + // void foo(int *__counted_by(*len) buf, size_t *len) { + // bar(&buf, len); + // } + // `buf` is read-only so it should not be passed as an out parameter. + for (const auto &DI : ArgInfo.getDepArgInfos()) { + if (DI.Unlisted) + continue; + const auto &CallArgInfo = CallArgInfos[DI.Index]; + if (CallArgInfo.isOutCountInParam() && CallArgInfo.isParmDecl()) { + const auto *DCPTy = ArgInfo.getCountAttributedType(); + S.Diag( + ArgLoc, + diag::err_bounds_safety_cannot_pass_read_only_dynamic_bound_pointer) + << ArgInfo.getDecl()->getName() << DCPTy->getKind(); + return false; + } + } } - } else if (SrcTy->isMatrixType()) { - return Diag(R.getBegin(), - diag::err_invalid_conversion_between_matrix_and_type) - << SrcTy << DestTy << R; - } else if (DestTy->isMatrixType()) { - return Diag(R.getBegin(), - diag::err_invalid_conversion_between_matrix_and_type) - << DestTy << SrcTy << R; - } - Kind = CK_MatrixCast; - return false; -} + auto reportIncompatibleCountExprs = [&]() { + const auto *ParmDCPTy = ParmInfo.getCountAttributedType(); + const auto *ArgDCPTy = ArgInfo.getCountAttributedType(); + if (ParmDCPTy->isCountInBytes() != ArgDCPTy->isCountInBytes() || + !compatibleDynamicCountExprs(S.Context, ParmDCPTy->getCountExpr(), + ArgDCPTy->getCountExpr())) { + S.Diag(ActualArgExp->getExprLoc(), + diag::err_bounds_safety_incompatible_count_expression) + << ParmDCPTy->getCountExpr() << ArgDCPTy->getCountExpr(); + } + }; -bool Sema::CheckVectorCast(SourceRange R, QualType VectorTy, QualType Ty, - CastKind &Kind) { - assert(VectorTy->isVectorType() && "Not a vector type!"); + // check parameter relationship! + if (ParmInfo.isOutCountPointer()) { + assert(ArgInfo.isOutCountPointer()); + reportIncompatibleCountExprs(); + } + + // Should check compatibility of count expressions as well if this is a + // direct parameter using out count. + if (ParmInfo.isInOutCountPointer() && + ArgInfo.getCountAttributedType()) { + reportIncompatibleCountExprs(); + } + + if ((ParmInfo.isOutCountPointer() && !ParmInfo.isIncompleteArray()) || + ParmInfo.isOutCountInParam()) { + // For attribute only mode, we do not want to report + // 'err_bounds_safety_unsynchronized_indirect_param'. + if (!S.getLangOpts().isBoundsSafetyAttributeOnlyMode()) { + for (const auto &ArgDepInfo : ArgInfo.getDepArgInfos()) { + if (ArgDepInfo.Unlisted || + (ArgInfo.isOutCountPointer() && + !CallArgInfos[ArgDepInfo.Index].isOutCountInParam())) { + unsigned IsDependent = 0; + + QualType DiagTy = ArgInfo.getType(); + if (ArgInfo.isCountInParam()) { + IsDependent = 1; + DiagTy = ArgDepInfo.getDecl()->getType(); + } - if (Ty->isVectorType() || Ty->isIntegralType(Context)) { - if (!areLaxCompatibleVectorTypes(Ty, VectorTy)) - return Diag(R.getBegin(), - Ty->isVectorType() ? - diag::err_invalid_conversion_between_vectors : - diag::err_invalid_conversion_between_vector_and_integer) - << VectorTy << Ty << R; - } else - return Diag(R.getBegin(), - diag::err_invalid_conversion_between_vector_and_scalar) - << VectorTy << Ty << R; + S.Diag(Call->getExprLoc(), + diag::err_bounds_safety_unsynchronized_indirect_param) + << ArgInfo.getDecl() << ArgInfo.isAddrOf() << ArgDepInfo.getDecl() + << !ArgDepInfo.isDeref() << IsDependent << DiagTy; + return false; + } + } + } - Kind = CK_BitCast; - return false; + for (const auto &ParmDepInfo : ParmInfo.getDepArgInfos()) { + if (std::none_of(ArgInfo.depinfo_begin(), ArgInfo.depinfo_end(), + [&ParmDepInfo](const DepArgInfo &ArgDepInfo) { + return ParmDepInfo.matches(ArgDepInfo); + })) { + S.Diag(ArgLoc, + diag::err_bounds_safety_incompatible_dynamic_count_argument) + << ParmInfo.getDecl()->getType() << Call->getSourceRange(); + return false; + } + } + } + } // End for loop + + return true; } -ExprResult Sema::prepareVectorSplat(QualType VectorTy, Expr *SplattedExpr) { - QualType DestElemTy = VectorTy->castAs()->getElementType(); +static bool checkDynamicRangePointerAsParameter(Sema &S, FunctionDecl *FDecl, + CallExpr *Call) { + llvm::SmallPtrSet OutPointerDecls; - if (DestElemTy == SplattedExpr->getType()) - return SplattedExpr; + llvm::SmallVector CallArgInfos; + DynamicBoundArgumentInfo::DeclIndexMapTy DeclIndexMap; - assert(DestElemTy->isFloatingType() || - DestElemTy->isIntegralOrEnumerationType()); + for (unsigned i = 0; i < Call->getNumArgs(); ++i) { + CallArgInfos.emplace_back(S.Context, Call->getArg(i)); + DeclIndexMap[CallArgInfos.back().getDecl()].push_back(i); + } - CastKind CK; - if (VectorTy->isExtVectorType() && SplattedExpr->getType()->isBooleanType()) { - // OpenCL requires that we convert `true` boolean expressions to -1, but - // only when splatting vectors. - if (DestElemTy->isFloatingType()) { - // To avoid having to have a CK_BooleanToSignedFloating cast kind, we cast - // in two steps: boolean to signed integral, then to floating. - ExprResult CastExprRes = ImpCastExprToType(SplattedExpr, Context.IntTy, - CK_BooleanToSignedIntegral); - SplattedExpr = CastExprRes.get(); - CK = CK_IntegralToFloating; - } else { - CK = CK_BooleanToSignedIntegral; - } - } else { - ExprResult CastExprRes = SplattedExpr; - CK = PrepareScalarCast(CastExprRes, DestElemTy); - if (CastExprRes.isInvalid()) - return ExprError(); - SplattedExpr = CastExprRes.get(); + for (auto &ArgInfo : CallArgInfos) { + ArgInfo.processDepArgInfos(DeclIndexMap); } - return ImpCastExprToType(SplattedExpr, DestElemTy, CK); -} -ExprResult Sema::CheckExtVectorCast(SourceRange R, QualType DestTy, - Expr *CastExpr, CastKind &Kind) { - assert(DestTy->isExtVectorType() && "Not an extended vector type!"); + llvm::SmallVector FuncParmInfos; + for (unsigned i = 0; i < FDecl->getNumParams(); ++i) { + FuncParmInfos.emplace_back(FDecl->getParamDecl(i)); + } - QualType SrcTy = CastExpr->getType(); + unsigned MinNumArgs = std::min(Call->getNumArgs(), FDecl->getNumParams()); + // TODO: Report an error when a pointer to __ended_by is passed as va_arg + // (rdar://97041755). + for (unsigned i = 0; i < MinNumArgs; ++i) { + const auto &ArgInfo = CallArgInfos[i]; + const auto &ParmInfo = FuncParmInfos[i]; + Expr *ArgExp = Call->getArg(i)->IgnoreImpCasts(); + SourceLocation ArgLoc = ArgExp->getExprLoc(); - // If SrcTy is a VectorType, the total size must match to explicitly cast to - // an ExtVectorType. - // In OpenCL, casts between vectors of different types are not allowed. - // (See OpenCL 6.2). - if (SrcTy->isVectorType()) { - if (!areLaxCompatibleVectorTypes(SrcTy, DestTy) || - (getLangOpts().OpenCL && - !Context.hasSameUnqualifiedType(DestTy, SrcTy))) { - Diag(R.getBegin(),diag::err_invalid_conversion_between_ext_vectors) - << DestTy << SrcTy << R; - return ExprError(); + if (ParmInfo.isOutRangePointer() && !ArgInfo.isOutRangePointer()) { + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_dynamic_bound_argument) + << ArgInfo.getDecl() << ArgInfo.getDecl()->getType() + << ParmInfo.getType() << Call->getSourceRange(); + return false; } - Kind = CK_BitCast; - return CastExpr; - } - // All non-pointer scalars can be cast to ExtVector type. The appropriate - // conversion will take place first from scalar to elt type, and then - // splat from elt type to vector. - if (SrcTy->isPointerType()) - return Diag(R.getBegin(), - diag::err_invalid_conversion_between_vector_and_scalar) - << DestTy << SrcTy << R; + if (!ParmInfo.isOutRangePointer() && ArgInfo.isOutRangePointer()) { + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_dynamic_bound_argument) + << ArgInfo.getDecl() << ArgInfo.getType() + << ParmInfo.getDecl()->getType() << Call->getSourceRange(); + return false; + } - Kind = CK_VectorSplat; - return prepareVectorSplat(DestTy, CastExpr); -} + if (ArgInfo.isAddrOfInBufParam()) { + bool HasInOutRange = false; + for (const auto &DI : ArgInfo.getDepArgInfos()) { + if (DI.Unlisted) + continue; + if (CallArgInfos[DI.Index].isOutRangePointer()) { + HasInOutRange = true; + break; + } + } -ExprResult -Sema::ActOnCastExpr(Scope *S, SourceLocation LParenLoc, - Declarator &D, ParsedType &Ty, - SourceLocation RParenLoc, Expr *CastExpr) { - assert(!D.isInvalidType() && (CastExpr != nullptr) && - "ActOnCastExpr(): missing type or expr"); + if (HasInOutRange) { + S.Diag(ArgLoc, + diag::err_bounds_safety_cannot_pass_read_only_dynamic_bound_pointer) + << ArgInfo.getDecl()->getName() << /*ended_by*/ 4; + return false; + } + } - TypeSourceInfo *castTInfo = GetTypeForDeclaratorCast(D, CastExpr->getType()); - if (D.isInvalidType()) - return ExprError(); + // TODO: This diagnostic check should always be performed + // (rdar://138982703). The check is currently guarded to avoid potentially + // breaking the build. + if (ArgInfo.isEndInRet() && S.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_ReturnSize)) { + // Keep it simple for now and disallow passing by out parameter. + QualType Ty = ArgExp->getType(); + bool IsIndirect = + Ty->isPointerType() && Ty->getPointeeType()->isPointerType(); + if (IsIndirect) { + const auto *ArgPVD = cast(ArgInfo.getDecl()); + const auto *Caller = cast(ArgPVD->getDeclContext()); + S.Diag( + ArgLoc, + diag::err_bounds_safety_cannot_pass_read_only_dependent_param_in_return) + << ArgPVD->getName() << /* __ended_by() */ 4 << Caller->getName() + << Caller->getReturnType(); + return false; + } + } - if (getLangOpts().CPlusPlus) { - // Check that there are no default arguments (C++ only). - CheckExtraCXXDefaultArguments(D); - } else { - // Make sure any TypoExprs have been dealt with. - ExprResult Res = CorrectDelayedTyposInExpr(CastExpr); - if (!Res.isUsable()) - return ExprError(); - CastExpr = Res.get(); - } + // check parameter relationship + if (ParmInfo.isOutRangePointer()) { + assert(ArgInfo.isOutRangePointer()); + if (ParmInfo.getDepArgInfos().size() != ArgInfo.getDepArgInfos().size()) { + S.Diag(ArgLoc, diag::err_bounds_safety_incompatible_dynamic_bound_argument) + << ArgInfo.getDecl() << ArgInfo.getType() << ParmInfo.getType() + << Call->getSourceRange(); + return false; + } - checkUnusedDeclAttributes(D); + for (unsigned j = 0; j < ParmInfo.getDepArgInfos().size(); ++j) { + const auto &ArgDepInfo = ArgInfo.getDepArgInfos()[j]; + const auto &ParmDepInfo = ParmInfo.getDepArgInfos()[j]; + if (ArgDepInfo.Unlisted) { + const auto *DRPTy = + ArgInfo.getType()->getAs(); + assert(DRPTy); + unsigned IsDependent = 0; + + QualType DiagTy = ArgInfo.getType(); + if (!DRPTy->getEndPointer()) { + IsDependent = 1; + DiagTy = ArgDepInfo.getDecl()->getType(); + } - QualType castType = castTInfo->getType(); - Ty = CreateParsedType(castType, castTInfo); + S.Diag(Call->getExprLoc(), + diag::err_bounds_safety_unsynchronized_indirect_param) + << ArgInfo.getDecl() << ArgInfo.isAddrOf() << ArgDepInfo.getDecl() + << !ArgDepInfo.isDeref() << IsDependent << DiagTy; + return false; + } + if (!ParmDepInfo.matches(ArgDepInfo)) { + S.Diag(ArgLoc, + diag::err_bounds_safety_incompatible_dynamic_bound_argument) + << ArgInfo.getDecl() << ArgInfo.getType() << ParmInfo.getType() + << Call->getSourceRange(); + return false; + } + } + } + } // End for loop - bool isVectorLiteral = false; + return true; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ - // Check for an altivec or OpenCL literal, - // i.e. all the elements are integer constants. - ParenExpr *PE = dyn_cast(CastExpr); - ParenListExpr *PLE = dyn_cast(CastExpr); - if ((getLangOpts().AltiVec || getLangOpts().ZVector || getLangOpts().OpenCL) - && castType->isVectorType() && (PE || PLE)) { - if (PLE && PLE->getNumExprs() == 0) { - Diag(PLE->getExprLoc(), diag::err_altivec_empty_initializer); +ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, + SourceLocation LParenLoc, + ArrayRef Args, + SourceLocation RParenLoc, Expr *Config, + bool IsExecConfig, ADLCallKind UsesADL) { + FunctionDecl *FDecl = dyn_cast_or_null(NDecl); + unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0); + + // Functions with 'interrupt' attribute cannot be called directly. + if (FDecl) { + if (FDecl->hasAttr()) { + Diag(Fn->getExprLoc(), diag::err_anyx86_interrupt_called); return ExprError(); } - if (PE || PLE->getNumExprs() == 1) { - Expr *E = (PE ? PE->getSubExpr() : PLE->getExpr(0)); - if (!E->isTypeDependent() && !E->getType()->isVectorType()) - isVectorLiteral = true; + if (FDecl->hasAttr()) { + Diag(Fn->getExprLoc(), diag::err_arm_interrupt_called); + return ExprError(); } - else - isVectorLiteral = true; } - // If this is a vector initializer, '(' type ')' '(' init, ..., init ')' - // then handle it as such. - if (isVectorLiteral) - return BuildVectorLiteral(LParenLoc, RParenLoc, CastExpr, castTInfo); - - // If the Expr being casted is a ParenListExpr, handle it specially. - // This is not an AltiVec-style cast, so turn the ParenListExpr into a - // sequence of BinOp comma operators. - if (isa(CastExpr)) { - ExprResult Result = MaybeConvertParenListExprToParenExpr(S, CastExpr); - if (Result.isInvalid()) return ExprError(); - CastExpr = Result.get(); + // X86 interrupt handlers may only call routines with attribute + // no_caller_saved_registers since there is no efficient way to + // save and restore the non-GPR state. + if (auto *Caller = getCurFunctionDecl()) { + if (Caller->hasAttr() || + Caller->hasAttr()) { + const TargetInfo &TI = Context.getTargetInfo(); + bool HasNonGPRRegisters = + TI.hasFeature("sse") || TI.hasFeature("x87") || TI.hasFeature("mmx"); + if (HasNonGPRRegisters && + (!FDecl || !FDecl->hasAttr())) { + Diag(Fn->getExprLoc(), diag::warn_anyx86_excessive_regsave) + << (Caller->hasAttr() ? 0 : 1); + if (FDecl) + Diag(FDecl->getLocation(), diag::note_callee_decl) << FDecl; + } + } } - if (getLangOpts().CPlusPlus && !castType->isVoidType()) - Diag(LParenLoc, diag::warn_old_style_cast) << CastExpr->getSourceRange(); + // Promote the function operand. + // We special-case function promotion here because we only allow promoting + // builtin functions to function pointers in the callee of a call. + ExprResult Result; + QualType ResultTy; + if (BuiltinID && + Fn->getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn)) { + // Extract the return type from the (builtin) function pointer type. + // FIXME Several builtins still have setType in + // Sema::CheckBuiltinFunctionCall. One should review their definitions in + // Builtins.td to ensure they are correct before removing setType calls. + QualType FnPtrTy = Context.getPointerType(FDecl->getType()); + Result = ImpCastExprToType(Fn, FnPtrTy, CK_BuiltinFnToFnPtr).get(); + ResultTy = FDecl->getCallResultType(); + } else { + Result = CallExprUnaryConversions(Fn); + ResultTy = Context.BoolTy; + } + if (Result.isInvalid()) + return ExprError(); + Fn = Result.get(); - ObjC().CheckTollFreeBridgeCast(castType, CastExpr); + // Check for a valid function type, but only if it is not a builtin which + // requires custom type checking. These will be handled by + // CheckBuiltinFunctionCall below just after creation of the call expression. + const FunctionType *FuncT = nullptr; + if (!BuiltinID || !Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) { + retry: + if (const PointerType *PT = Fn->getType()->getAs()) { + // C99 6.5.2.2p1 - "The expression that denotes the called function shall + // have type pointer to function". + FuncT = PT->getPointeeType()->getAs(); + if (!FuncT) + return ExprError(Diag(LParenLoc, diag::err_typecheck_call_not_function) + << Fn->getType() << Fn->getSourceRange()); + } else if (const BlockPointerType *BPT = + Fn->getType()->getAs()) { + FuncT = BPT->getPointeeType()->castAs(); + } else { + // Handle calls to expressions of unknown-any type. + if (Fn->getType() == Context.UnknownAnyTy) { + ExprResult rewrite = rebuildUnknownAnyFunction(*this, Fn); + if (rewrite.isInvalid()) + return ExprError(); + Fn = rewrite.get(); + goto retry; + } - ObjC().CheckObjCBridgeRelatedCast(castType, CastExpr); + return ExprError(Diag(LParenLoc, diag::err_typecheck_call_not_function) + << Fn->getType() << Fn->getSourceRange()); + } + } - DiscardMisalignedMemberAddress(castType.getTypePtr(), CastExpr); + // Get the number of parameters in the function prototype, if any. + // We will allocate space for max(Args.size(), NumParams) arguments + // in the call expression. + const auto *Proto = dyn_cast_or_null(FuncT); + unsigned NumParams = Proto ? Proto->getNumParams() : 0; - return BuildCStyleCastExpr(LParenLoc, castTInfo, RParenLoc, CastExpr); -} + CallExpr *TheCall; + if (Config) { + assert(UsesADL == ADLCallKind::NotADL && + "CUDAKernelCallExpr should not use ADL"); + TheCall = CUDAKernelCallExpr::Create(Context, Fn, cast(Config), + Args, ResultTy, VK_PRValue, RParenLoc, + CurFPFeatureOverrides(), NumParams); + } else { + TheCall = + CallExpr::Create(Context, Fn, Args, ResultTy, VK_PRValue, RParenLoc, + CurFPFeatureOverrides(), NumParams, UsesADL); + } -ExprResult Sema::BuildVectorLiteral(SourceLocation LParenLoc, - SourceLocation RParenLoc, Expr *E, - TypeSourceInfo *TInfo) { - assert((isa(E) || isa(E)) && - "Expected paren or paren list expression"); + if (!Context.isDependenceAllowed()) { + // Forget about the nulled arguments since typo correction + // do not handle them well. + TheCall->shrinkNumArgs(Args.size()); + // C cannot always handle TypoExpr nodes in builtin calls and direct + // function calls as their argument checking don't necessarily handle + // dependent types properly, so make sure any TypoExprs have been + // dealt with. + ExprResult Result = CorrectDelayedTyposInExpr(TheCall); + if (!Result.isUsable()) return ExprError(); + CallExpr *TheOldCall = TheCall; + TheCall = dyn_cast(Result.get()); + bool CorrectedTypos = TheCall != TheOldCall; + if (!TheCall) return Result; + Args = llvm::ArrayRef(TheCall->getArgs(), TheCall->getNumArgs()); - Expr **exprs; - unsigned numExprs; - Expr *subExpr; - SourceLocation LiteralLParenLoc, LiteralRParenLoc; - if (ParenListExpr *PE = dyn_cast(E)) { - LiteralLParenLoc = PE->getLParenLoc(); - LiteralRParenLoc = PE->getRParenLoc(); - exprs = PE->getExprs(); - numExprs = PE->getNumExprs(); - } else { // isa by assertion at function entrance - LiteralLParenLoc = cast(E)->getLParen(); - LiteralRParenLoc = cast(E)->getRParen(); - subExpr = cast(E)->getSubExpr(); - exprs = &subExpr; - numExprs = 1; + // A new call expression node was created if some typos were corrected. + // However it may not have been constructed with enough storage. In this + // case, rebuild the node with enough storage. The waste of space is + // immaterial since this only happens when some typos were corrected. + if (CorrectedTypos && Args.size() < NumParams) { + if (Config) + TheCall = CUDAKernelCallExpr::Create( + Context, Fn, cast(Config), Args, ResultTy, VK_PRValue, + RParenLoc, CurFPFeatureOverrides(), NumParams); + else + TheCall = + CallExpr::Create(Context, Fn, Args, ResultTy, VK_PRValue, RParenLoc, + CurFPFeatureOverrides(), NumParams, UsesADL); + } + // We can now handle the nulled arguments for the default arguments. + TheCall->setNumArgsUnsafe(std::max(Args.size(), NumParams)); } - QualType Ty = TInfo->getType(); - assert(Ty->isVectorType() && "Expected vector type"); + // Bail out early if calling a builtin with custom type checking. + if (BuiltinID && Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) { + ExprResult E = CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall); + if (!E.isInvalid() && Context.BuiltinInfo.isImmediate(BuiltinID)) + E = CheckForImmediateInvocation(E, FDecl); + return E; + } - SmallVector initExprs; - const VectorType *VTy = Ty->castAs(); - unsigned numElems = VTy->getNumElements(); + if (getLangOpts().CUDA) { + if (Config) { + // CUDA: Kernel calls must be to global functions + if (FDecl && !FDecl->hasAttr()) + return ExprError(Diag(LParenLoc,diag::err_kern_call_not_global_function) + << FDecl << Fn->getSourceRange()); - // '(...)' form of vector initialization in AltiVec: the number of - // initializers must be one or must match the size of the vector. - // If a single value is specified in the initializer then it will be - // replicated to all the components of the vector - if (CheckAltivecInitFromScalar(E->getSourceRange(), Ty, - VTy->getElementType())) - return ExprError(); - if (ShouldSplatAltivecScalarInCast(VTy)) { - // The number of initializers must be one or must match the size of the - // vector. If a single value is specified in the initializer then it will - // be replicated to all the components of the vector - if (numExprs == 1) { - QualType ElemTy = VTy->getElementType(); - ExprResult Literal = DefaultLvalueConversion(exprs[0]); - if (Literal.isInvalid()) - return ExprError(); - Literal = ImpCastExprToType(Literal.get(), ElemTy, - PrepareScalarCast(Literal, ElemTy)); - return BuildCStyleCastExpr(LParenLoc, TInfo, RParenLoc, Literal.get()); - } - else if (numExprs < numElems) { - Diag(E->getExprLoc(), - diag::err_incorrect_number_of_vector_initializers); - return ExprError(); + // CUDA: Kernel function must have 'void' return type + if (!FuncT->getReturnType()->isVoidType() && + !FuncT->getReturnType()->getAs() && + !FuncT->getReturnType()->isInstantiationDependentType()) + return ExprError(Diag(LParenLoc, diag::err_kern_type_not_void_return) + << Fn->getType() << Fn->getSourceRange()); + } else { + // CUDA: Calls to global functions must be configured + if (FDecl && FDecl->hasAttr()) + return ExprError(Diag(LParenLoc, diag::err_global_call_not_config) + << FDecl << Fn->getSourceRange()); } - else - initExprs.append(exprs, exprs + numExprs); } - else { - // For OpenCL, when the number of initializers is a single value, - // it will be replicated to all components of the vector. - if (getLangOpts().OpenCL && VTy->getVectorKind() == VectorKind::Generic && - numExprs == 1) { - QualType ElemTy = VTy->getElementType(); - ExprResult Literal = DefaultLvalueConversion(exprs[0]); - if (Literal.isInvalid()) - return ExprError(); - Literal = ImpCastExprToType(Literal.get(), ElemTy, - PrepareScalarCast(Literal, ElemTy)); - return BuildCStyleCastExpr(LParenLoc, TInfo, RParenLoc, Literal.get()); - } - initExprs.append(exprs, exprs + numExprs); - } - // FIXME: This means that pretty-printing the final AST will produce curly - // braces instead of the original commas. - InitListExpr *initE = new (Context) InitListExpr(Context, LiteralLParenLoc, - initExprs, LiteralRParenLoc); - initE->setType(Ty); - return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, initE); -} + // Check for a valid return type + if (CheckCallReturnType(FuncT->getReturnType(), Fn->getBeginLoc(), TheCall, + FDecl)) + return ExprError(); -ExprResult -Sema::MaybeConvertParenListExprToParenExpr(Scope *S, Expr *OrigExpr) { - ParenListExpr *E = dyn_cast(OrigExpr); - if (!E) - return OrigExpr; + // We know the result type of the call, set it. + TheCall->setType(FuncT->getCallResultType(Context)); + TheCall->setValueKind(Expr::getValueKindForType(FuncT->getReturnType())); - ExprResult Result(E->getExpr(0)); + // WebAssembly tables can't be used as arguments. + if (Context.getTargetInfo().getTriple().isWasm()) { + for (const Expr *Arg : Args) { + if (Arg && Arg->getType()->isWebAssemblyTableType()) { + return ExprError(Diag(Arg->getExprLoc(), + diag::err_wasm_table_as_function_parameter)); + } + } + } - for (unsigned i = 1, e = E->getNumExprs(); i != e && !Result.isInvalid(); ++i) - Result = ActOnBinOp(S, E->getExprLoc(), tok::comma, Result.get(), - E->getExpr(i)); + if (Proto) { + if (ConvertArgumentsForCall(TheCall, Fn, FDecl, Proto, Args, RParenLoc, + IsExecConfig)) + return ExprError(); + } else { + assert(isa(FuncT) && "Unknown FunctionType!"); - if (Result.isInvalid()) return ExprError(); + if (FDecl) { + // Check if we have too few/too many template arguments, based + // on our knowledge of the function definition. + const FunctionDecl *Def = nullptr; + if (FDecl->hasBody(Def) && Args.size() != Def->param_size()) { + Proto = Def->getType()->getAs(); + if (!Proto || !(Proto->isVariadic() && Args.size() >= Def->param_size())) + Diag(RParenLoc, diag::warn_call_wrong_number_of_arguments) + << (Args.size() > Def->param_size()) << FDecl << Fn->getSourceRange(); + } - return ActOnParenExpr(E->getLParenLoc(), E->getRParenLoc(), Result.get()); -} + // If the function we're calling isn't a function prototype, but we have + // a function prototype from a prior declaratiom, use that prototype. + if (!FDecl->hasPrototype()) + Proto = FDecl->getType()->getAs(); + } -ExprResult Sema::ActOnParenListExpr(SourceLocation L, - SourceLocation R, - MultiExprArg Val) { - return ParenListExpr::Create(Context, L, Val, R); -} + // If we still haven't found a prototype to use but there are arguments to + // the call, diagnose this as calling a function without a prototype. + // However, if we found a function declaration, check to see if + // -Wdeprecated-non-prototype was disabled where the function was declared. + // If so, we will silence the diagnostic here on the assumption that this + // interface is intentional and the user knows what they're doing. We will + // also silence the diagnostic if there is a function declaration but it + // was implicitly defined (the user already gets diagnostics about the + // creation of the implicit function declaration, so the additional warning + // is not helpful). + if (!Proto && !Args.empty() && + (!FDecl || (!FDecl->isImplicit() && + !Diags.isIgnored(diag::warn_strict_uses_without_prototype, + FDecl->getLocation())))) + Diag(LParenLoc, diag::warn_strict_uses_without_prototype) + << (FDecl != nullptr) << FDecl; -bool Sema::DiagnoseConditionalForNull(const Expr *LHSExpr, const Expr *RHSExpr, - SourceLocation QuestionLoc) { - const Expr *NullExpr = LHSExpr; - const Expr *NonPointerExpr = RHSExpr; - Expr::NullPointerConstantKind NullKind = - NullExpr->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNotNull); + // Promote the arguments (C99 6.5.2.2p6). + for (unsigned i = 0, e = Args.size(); i != e; i++) { + Expr *Arg = Args[i]; - if (NullKind == Expr::NPCK_NotNull) { - NullExpr = RHSExpr; - NonPointerExpr = LHSExpr; - NullKind = - NullExpr->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNotNull); - } + if (Proto && i < Proto->getNumParams()) { + InitializedEntity Entity = InitializedEntity::InitializeParameter( + Context, Proto->getParamType(i), Proto->isParamConsumed(i)); + ExprResult ArgE = + PerformCopyInitialization(Entity, SourceLocation(), Arg); + if (ArgE.isInvalid()) + return true; - if (NullKind == Expr::NPCK_NotNull) - return false; + Arg = ArgE.getAs(); - if (NullKind == Expr::NPCK_ZeroExpression) - return false; + } else { + ExprResult ArgE = DefaultArgumentPromotion(Arg); - if (NullKind == Expr::NPCK_ZeroLiteral) { - // In this case, check to make sure that we got here from a "NULL" - // string in the source code. - NullExpr = NullExpr->IgnoreParenImpCasts(); - SourceLocation loc = NullExpr->getExprLoc(); - if (!findMacroSpelling(loc, "NULL")) - return false; - } + if (ArgE.isInvalid()) + return true; - int DiagType = (NullKind == Expr::NPCK_CXX11_nullptr); - Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands_null) - << NonPointerExpr->getType() << DiagType - << NonPointerExpr->getSourceRange(); - return true; -} + Arg = ArgE.getAs(); + } -/// Return false if the condition expression is valid, true otherwise. -static bool checkCondition(Sema &S, const Expr *Cond, - SourceLocation QuestionLoc) { - QualType CondTy = Cond->getType(); + if (RequireCompleteType(Arg->getBeginLoc(), Arg->getType(), + diag::err_call_incomplete_argument, Arg)) + return ExprError(); - // OpenCL v1.1 s6.3.i says the condition cannot be a floating point type. - if (S.getLangOpts().OpenCL && CondTy->isFloatingType()) { - S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_nonfloat) - << CondTy << Cond->getSourceRange(); - return true; + TheCall->setArg(i, Arg); + } + TheCall->computeDependence(); } - // C99 6.5.15p2 - if (CondTy->isScalarType()) return false; - - S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_scalar) - << CondTy << Cond->getSourceRange(); - return true; -} + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafetyAttributes && FDecl) { + // FIXME: We need to support function pointers and blocks that don't have + // function decl. -/// Return false if the NullExpr can be promoted to PointerTy, -/// true otherwise. -static bool checkConditionalNullPointer(Sema &S, ExprResult &NullExpr, - QualType PointerTy) { - if ((!PointerTy->isAnyPointerType() && !PointerTy->isBlockPointerType()) || - !NullExpr.get()->isNullPointerConstant(S.Context, - Expr::NPC_ValueDependentIsNull)) - return true; + // For C++, we don't want to check for any dependent constructs. + if (TheCall->getDependence() == ExprDependence::None && + !checkDynamicCountPointerAsParameter(*this, FDecl, TheCall)) + return ExprError(); + if (getLangOpts().BoundsSafety) + if (!checkDynamicRangePointerAsParameter(*this, FDecl, TheCall)) + return ExprError(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - NullExpr = S.ImpCastExprToType(NullExpr.get(), PointerTy, CK_NullToPointer); - return false; -} + if (CXXMethodDecl *Method = dyn_cast_or_null(FDecl)) + if (Method->isImplicitObjectMemberFunction()) + return ExprError(Diag(LParenLoc, diag::err_member_call_without_object) + << Fn->getSourceRange() << 0); -/// Checks compatibility between two pointers and return the resulting -/// type. -static QualType checkConditionalPointerCompatibility(Sema &S, ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc) { - QualType LHSTy = LHS.get()->getType(); - QualType RHSTy = RHS.get()->getType(); + // Check for sentinels + if (NDecl) + DiagnoseSentinelCalls(NDecl, LParenLoc, Args); - if (S.Context.hasSameType(LHSTy, RHSTy)) { - // Two identical pointers types are always compatible. - return S.Context.getCommonSugaredType(LHSTy, RHSTy); + // Warn for unions passing across security boundary (CMSE). + if (FuncT != nullptr && FuncT->getCmseNSCallAttr()) { + for (unsigned i = 0, e = Args.size(); i != e; i++) { + if (const auto *RT = + dyn_cast(Args[i]->getType().getCanonicalType())) { + if (RT->getDecl()->isOrContainsUnion()) + Diag(Args[i]->getBeginLoc(), diag::warn_cmse_nonsecure_union) + << 0 << i; + } + } } - QualType lhptee, rhptee; + // Do special checking on direct calls to functions. + if (FDecl) { + if (CheckFunctionCall(FDecl, TheCall, Proto)) + return ExprError(); - // Get the pointee types. - bool IsBlockPointer = false; - if (const BlockPointerType *LHSBTy = LHSTy->getAs()) { - lhptee = LHSBTy->getPointeeType(); - rhptee = RHSTy->castAs()->getPointeeType(); - IsBlockPointer = true; + checkFortifiedBuiltinMemoryFunction(FDecl, TheCall); + + if (BuiltinID) + return CheckBuiltinFunctionCall(FDecl, BuiltinID, TheCall); + } else if (NDecl) { + if (CheckPointerCall(NDecl, TheCall, Proto)) + return ExprError(); } else { - lhptee = LHSTy->castAs()->getPointeeType(); - rhptee = RHSTy->castAs()->getPointeeType(); + if (CheckOtherCall(TheCall, Proto)) + return ExprError(); } - // C99 6.5.15p6: If both operands are pointers to compatible types or to - // differently qualified versions of compatible types, the result type is - // a pointer to an appropriately qualified version of the composite - // type. + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (!BoundsSafetyCheckResolvedCall(FDecl, TheCall, Proto)) + return ExprError(); + /* TO_UPSTREAM(BoundsSafety) OFF*/ - // Only CVR-qualifiers exist in the standard, and the differently-qualified - // clause doesn't make sense for our extensions. E.g. address space 2 should - // be incompatible with address space 3: they may live on different devices or - // anything. - Qualifiers lhQual = lhptee.getQualifiers(); - Qualifiers rhQual = rhptee.getQualifiers(); + return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FDecl); +} - LangAS ResultAddrSpace = LangAS::Default; - LangAS LAddrSpace = lhQual.getAddressSpace(); - LangAS RAddrSpace = rhQual.getAddressSpace(); +ExprResult +Sema::ActOnCompoundLiteral(SourceLocation LParenLoc, ParsedType Ty, + SourceLocation RParenLoc, Expr *InitExpr) { + assert(Ty && "ActOnCompoundLiteral(): missing type"); + assert(InitExpr && "ActOnCompoundLiteral(): missing expression"); - // OpenCL v1.1 s6.5 - Conversion between pointers to distinct address - // spaces is disallowed. - if (lhQual.isAddressSpaceSupersetOf(rhQual, S.getASTContext())) - ResultAddrSpace = LAddrSpace; - else if (rhQual.isAddressSpaceSupersetOf(lhQual, S.getASTContext())) - ResultAddrSpace = RAddrSpace; - else { - S.Diag(Loc, diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) - << LHSTy << RHSTy << 2 << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); - } + TypeSourceInfo *TInfo; + QualType literalType = GetTypeFromParser(Ty, &TInfo); + if (!TInfo) + TInfo = Context.getTrivialTypeSourceInfo(literalType); - unsigned MergedCVRQual = lhQual.getCVRQualifiers() | rhQual.getCVRQualifiers(); - auto LHSCastKind = CK_BitCast, RHSCastKind = CK_BitCast; - lhQual.removeCVRQualifiers(); - rhQual.removeCVRQualifiers(); + return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, InitExpr); +} - if (!lhQual.getPointerAuth().isEquivalent(rhQual.getPointerAuth())) { - S.Diag(Loc, diag::err_typecheck_cond_incompatible_ptrauth) - << LHSTy << RHSTy << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); - } +ExprResult +Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, + SourceLocation RParenLoc, Expr *LiteralExpr) { + QualType literalType = TInfo->getType(); - // OpenCL v2.0 specification doesn't extend compatibility of type qualifiers - // (C99 6.7.3) for address spaces. We assume that the check should behave in - // the same manner as it's defined for CVR qualifiers, so for OpenCL two - // qual types are compatible iff - // * corresponded types are compatible - // * CVR qualifiers are equal - // * address spaces are equal - // Thus for conditional operator we merge CVR and address space unqualified - // pointees and if there is a composite type we return a pointer to it with - // merged qualifiers. - LHSCastKind = - LAddrSpace == ResultAddrSpace ? CK_BitCast : CK_AddressSpaceConversion; - RHSCastKind = - RAddrSpace == ResultAddrSpace ? CK_BitCast : CK_AddressSpaceConversion; - lhQual.removeAddressSpace(); - rhQual.removeAddressSpace(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // XXX: as a hack, with -fbounds-safety enabled, we try to peek into the type of + // the expression being used in the initializer list. Clang's initialization + // system doesn't have hooks that allow inference to work, the type that we're + // going to pass here will be piped through deeply; type deduction is generally + // not possible. + if (getLangOpts().BoundsSafety && literalType->isPointerType()) { + bool InferredTypeFromInitList = false; + if (auto *InitList = dyn_cast(LiteralExpr)) { + if (InitList->getNumInits() > 0) { + QualType InitTy = InitList->getInit(0)->getType(); + if (InitTy->isPointerType() && !InitTy->isBoundsAttributedType()) { + literalType = deduceCastPointerAttributes( + literalType, InitList->getInit(0)->getType()); + InferredTypeFromInitList = true; + } + } + } + // If we don't find anything to start from, assume __bidi_indexable. + if (!InferredTypeFromInitList) { + literalType = Context.getBoundsSafetyPointerType( + literalType, BoundsSafetyPointerAttributes::bidiIndexable()); + } + TInfo->overrideType(literalType); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - lhptee = S.Context.getQualifiedType(lhptee.getUnqualifiedType(), lhQual); - rhptee = S.Context.getQualifiedType(rhptee.getUnqualifiedType(), rhQual); + if (literalType->isArrayType()) { + if (RequireCompleteSizedType( + LParenLoc, Context.getBaseElementType(literalType), + diag::err_array_incomplete_or_sizeless_type, + SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()))) + return ExprError(); + if (literalType->isVariableArrayType()) { + // C23 6.7.10p4: An entity of variable length array type shall not be + // initialized except by an empty initializer. + // + // The C extension warnings are issued from ParseBraceInitializer() and + // do not need to be issued here. However, we continue to issue an error + // in the case there are initializers or we are compiling C++. We allow + // use of VLAs in C++, but it's not clear we want to allow {} to zero + // init a VLA in C++ in all cases (such as with non-trivial constructors). + // FIXME: should we allow this construct in C++ when it makes sense to do + // so? + // + // But: C99-C23 6.5.2.5 Compound literals constraint 1: The type name + // shall specify an object type or an array of unknown size, but not a + // variable length array type. This seems odd, as it allows 'int a[size] = + // {}', but forbids 'int *a = (int[size]){}'. As this is what the standard + // says, this is what's implemented here for C (except for the extension + // that permits constant foldable size arrays) - QualType CompositeTy = S.Context.mergeTypes( - lhptee, rhptee, /*OfBlockPointer=*/false, /*Unqualified=*/false, - /*BlockReturnType=*/false, /*IsConditionalOperator=*/true); + auto diagID = LangOpts.CPlusPlus + ? diag::err_variable_object_no_init + : diag::err_compound_literal_with_vla_type; + if (!tryToFixVariablyModifiedVarType(TInfo, literalType, LParenLoc, + diagID)) + return ExprError(); + } + } else if (!literalType->isDependentType() && + RequireCompleteType(LParenLoc, literalType, + diag::err_typecheck_decl_incomplete_type, + SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()))) + return ExprError(); - if (CompositeTy.isNull()) { - // In this situation, we assume void* type. No especially good - // reason, but this is what gcc does, and we do have to pick - // to get a consistent AST. - QualType incompatTy; - incompatTy = S.Context.getPointerType( - S.Context.getAddrSpaceQualType(S.Context.VoidTy, ResultAddrSpace)); - LHS = S.ImpCastExprToType(LHS.get(), incompatTy, LHSCastKind); - RHS = S.ImpCastExprToType(RHS.get(), incompatTy, RHSCastKind); + InitializedEntity Entity + = InitializedEntity::InitializeCompoundLiteralInit(TInfo); + InitializationKind Kind + = InitializationKind::CreateCStyleCast(LParenLoc, + SourceRange(LParenLoc, RParenLoc), + /*InitList=*/true); + InitializationSequence InitSeq(*this, Entity, Kind, LiteralExpr); + ExprResult Result = InitSeq.Perform(*this, Entity, Kind, LiteralExpr, + &literalType); + if (Result.isInvalid()) + return ExprError(); + LiteralExpr = Result.get(); - // FIXME: For OpenCL the warning emission and cast to void* leaves a room - // for casts between types with incompatible address space qualifiers. - // For the following code the compiler produces casts between global and - // local address spaces of the corresponded innermost pointees: - // local int *global *a; - // global int *global *b; - // a = (0 ? a : b); // see C99 6.5.16.1.p1. - S.Diag(Loc, diag::ext_typecheck_cond_incompatible_pointers) - << LHSTy << RHSTy << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); + // We treat the compound literal as being at file scope if it's not in a + // function or method body, or within the function's prototype scope. This + // means the following compound literal is not at file scope: + // void func(char *para[(int [1]){ 0 }[0]); + const Scope *S = getCurScope(); + bool IsFileScope = !CurContext->isFunctionOrMethod() && + (!S || !S->isFunctionPrototypeScope()); - return incompatTy; - } + // In C, compound literals are l-values for some reason. + // For GCC compatibility, in C++, file-scope array compound literals with + // constant initializers are also l-values, and compound literals are + // otherwise prvalues. + // + // (GCC also treats C++ list-initialized file-scope array prvalues with + // constant initializers as l-values, but that's non-conforming, so we don't + // follow it there.) + // + // FIXME: It would be better to handle the lvalue cases as materializing and + // lifetime-extending a temporary object, but our materialized temporaries + // representation only supports lifetime extension from a variable, not "out + // of thin air". + // FIXME: For C++, we might want to instead lifetime-extend only if a pointer + // is bound to the result of applying array-to-pointer decay to the compound + // literal. + // FIXME: GCC supports compound literals of reference type, which should + // obviously have a value kind derived from the kind of reference involved. + ExprValueKind VK = + (getLangOpts().CPlusPlus && !(IsFileScope && literalType->isArrayType())) + ? VK_PRValue + : VK_LValue; - // The pointer types are compatible. - // In case of OpenCL ResultTy should have the address space qualifier - // which is a superset of address spaces of both the 2nd and the 3rd - // operands of the conditional operator. - QualType ResultTy = [&, ResultAddrSpace]() { - if (S.getLangOpts().OpenCL) { - Qualifiers CompositeQuals = CompositeTy.getQualifiers(); - CompositeQuals.setAddressSpace(ResultAddrSpace); - return S.Context - .getQualifiedType(CompositeTy.getUnqualifiedType(), CompositeQuals) - .withCVRQualifiers(MergedCVRQual); - } - return CompositeTy.withCVRQualifiers(MergedCVRQual); - }(); - if (IsBlockPointer) - ResultTy = S.Context.getBlockPointerType(ResultTy); - else - ResultTy = S.Context.getPointerType(ResultTy); + if (IsFileScope) + if (auto ILE = dyn_cast(LiteralExpr)) + for (unsigned i = 0, j = ILE->getNumInits(); i != j; i++) { + Expr *Init = ILE->getInit(i); + ILE->setInit(i, ConstantExpr::Create(Context, Init)); + } - LHS = S.ImpCastExprToType(LHS.get(), ResultTy, LHSCastKind); - RHS = S.ImpCastExprToType(RHS.get(), ResultTy, RHSCastKind); - return ResultTy; -} + auto *E = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, VK, + LiteralExpr, IsFileScope); + if (IsFileScope) { + if (!LiteralExpr->isTypeDependent() && + !LiteralExpr->isValueDependent() && + !literalType->isDependentType()) // C99 6.5.2.5p3 + if (CheckForConstantInitializer(LiteralExpr)) + return ExprError(); + } else if (literalType.getAddressSpace() != LangAS::opencl_private && + literalType.getAddressSpace() != LangAS::Default) { + // Embedded-C extensions to C99 6.5.2.5: + // "If the compound literal occurs inside the body of a function, the + // type name shall not be qualified by an address-space qualifier." + Diag(LParenLoc, diag::err_compound_literal_with_address_space) + << SourceRange(LParenLoc, LiteralExpr->getSourceRange().getEnd()); + return ExprError(); + } -/// Return the resulting type when the operands are both block pointers. -static QualType checkConditionalBlockPointerCompatibility(Sema &S, - ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc) { - QualType LHSTy = LHS.get()->getType(); - QualType RHSTy = RHS.get()->getType(); + if (!IsFileScope && !getLangOpts().CPlusPlus) { + // Compound literals that have automatic storage duration are destroyed at + // the end of the scope in C; in C++, they're just temporaries. - if (!LHSTy->isBlockPointerType() || !RHSTy->isBlockPointerType()) { - if (LHSTy->isVoidPointerType() || RHSTy->isVoidPointerType()) { - QualType destType = S.Context.getPointerType(S.Context.VoidTy); - LHS = S.ImpCastExprToType(LHS.get(), destType, CK_BitCast); - RHS = S.ImpCastExprToType(RHS.get(), destType, CK_BitCast); - return destType; + // Emit diagnostics if it is or contains a C union type that is non-trivial + // to destruct. + if (E->getType().hasNonTrivialToPrimitiveDestructCUnion()) + checkNonTrivialCUnion(E->getType(), E->getExprLoc(), + NTCUC_CompoundLiteral, NTCUK_Destruct); + + // Diagnose jumps that enter or exit the lifetime of the compound literal. + if (literalType.isDestructedType()) { + Cleanup.setExprNeedsCleanups(true); + ExprCleanupObjects.push_back(E); + getCurFunction()->setHasBranchProtectedScope(); } - S.Diag(Loc, diag::err_typecheck_cond_incompatible_operands) - << LHSTy << RHSTy << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); } - // We have 2 block pointer types. - return checkConditionalPointerCompatibility(S, LHS, RHS, Loc); + if (E->getType().hasNonTrivialToPrimitiveDefaultInitializeCUnion() || + E->getType().hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnionInInitializer(E->getInitializer(), + E->getInitializer()->getExprLoc()); + + return MaybeBindToTemporary(E); } -/// Return the resulting type when the operands are both pointers. -static QualType -checkConditionalObjectPointersCompatibility(Sema &S, ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc) { - // get the pointer types - QualType LHSTy = LHS.get()->getType(); - QualType RHSTy = RHS.get()->getType(); +ExprResult +Sema::ActOnInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList, + SourceLocation RBraceLoc) { + // Only produce each kind of designated initialization diagnostic once. + SourceLocation FirstDesignator; + bool DiagnosedArrayDesignator = false; + bool DiagnosedNestedDesignator = false; + bool DiagnosedMixedDesignator = false; - // get the "pointed to" types - QualType lhptee = LHSTy->castAs()->getPointeeType(); - QualType rhptee = RHSTy->castAs()->getPointeeType(); + // Check that any designated initializers are syntactically valid in the + // current language mode. + for (unsigned I = 0, E = InitArgList.size(); I != E; ++I) { + if (auto *DIE = dyn_cast(InitArgList[I])) { + if (FirstDesignator.isInvalid()) + FirstDesignator = DIE->getBeginLoc(); - // ignore qualifiers on void (C99 6.5.15p3, clause 6) - if (lhptee->isVoidType() && rhptee->isIncompleteOrObjectType()) { - // Figure out necessary qualifiers (C99 6.5.15p6) - QualType destPointee - = S.Context.getQualifiedType(lhptee, rhptee.getQualifiers()); - QualType destType = S.Context.getPointerType(destPointee); - // Add qualifiers if necessary. - LHS = S.ImpCastExprToType(LHS.get(), destType, CK_NoOp); - // Promote to void*. - RHS = S.ImpCastExprToType(RHS.get(), destType, CK_BitCast); - return destType; - } - if (rhptee->isVoidType() && lhptee->isIncompleteOrObjectType()) { - QualType destPointee - = S.Context.getQualifiedType(rhptee, lhptee.getQualifiers()); - QualType destType = S.Context.getPointerType(destPointee); - // Add qualifiers if necessary. - RHS = S.ImpCastExprToType(RHS.get(), destType, CK_NoOp); - // Promote to void*. - LHS = S.ImpCastExprToType(LHS.get(), destType, CK_BitCast); - return destType; - } + if (!getLangOpts().CPlusPlus) + break; - return checkConditionalPointerCompatibility(S, LHS, RHS, Loc); -} + if (!DiagnosedNestedDesignator && DIE->size() > 1) { + DiagnosedNestedDesignator = true; + Diag(DIE->getBeginLoc(), diag::ext_designated_init_nested) + << DIE->getDesignatorsSourceRange(); + } -/// Return false if the first expression is not an integer and the second -/// expression is not a pointer, true otherwise. -static bool checkPointerIntegerMismatch(Sema &S, ExprResult &Int, - Expr* PointerExpr, SourceLocation Loc, - bool IsIntFirstExpr) { - if (!PointerExpr->getType()->isPointerType() || - !Int.get()->getType()->isIntegerType()) - return false; - - Expr *Expr1 = IsIntFirstExpr ? Int.get() : PointerExpr; - Expr *Expr2 = IsIntFirstExpr ? PointerExpr : Int.get(); - - S.Diag(Loc, diag::ext_typecheck_cond_pointer_integer_mismatch) - << Expr1->getType() << Expr2->getType() - << Expr1->getSourceRange() << Expr2->getSourceRange(); - Int = S.ImpCastExprToType(Int.get(), PointerExpr->getType(), - CK_IntegralToPointer); - return true; -} - -/// Simple conversion between integer and floating point types. -/// -/// Used when handling the OpenCL conditional operator where the -/// condition is a vector while the other operands are scalar. -/// -/// OpenCL v1.1 s6.3.i and s6.11.6 together require that the scalar -/// types are either integer or floating type. Between the two -/// operands, the type with the higher rank is defined as the "result -/// type". The other operand needs to be promoted to the same type. No -/// other type promotion is allowed. We cannot use -/// UsualArithmeticConversions() for this purpose, since it always -/// promotes promotable types. -static QualType OpenCLArithmeticConversions(Sema &S, ExprResult &LHS, - ExprResult &RHS, - SourceLocation QuestionLoc) { - LHS = S.DefaultFunctionArrayLvalueConversion(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - RHS = S.DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); - - // For conversion purposes, we ignore any qualifiers. - // For example, "const float" and "float" are equivalent. - QualType LHSType = - S.Context.getCanonicalType(LHS.get()->getType()).getUnqualifiedType(); - QualType RHSType = - S.Context.getCanonicalType(RHS.get()->getType()).getUnqualifiedType(); + for (auto &Desig : DIE->designators()) { + if (!Desig.isFieldDesignator() && !DiagnosedArrayDesignator) { + DiagnosedArrayDesignator = true; + Diag(Desig.getBeginLoc(), diag::ext_designated_init_array) + << Desig.getSourceRange(); + } + } - if (!LHSType->isIntegerType() && !LHSType->isRealFloatingType()) { - S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_int_float) - << LHSType << LHS.get()->getSourceRange(); - return QualType(); + if (!DiagnosedMixedDesignator && + !isa(InitArgList[0])) { + DiagnosedMixedDesignator = true; + Diag(DIE->getBeginLoc(), diag::ext_designated_init_mixed) + << DIE->getSourceRange(); + Diag(InitArgList[0]->getBeginLoc(), diag::note_designated_init_mixed) + << InitArgList[0]->getSourceRange(); + } + } else if (getLangOpts().CPlusPlus && !DiagnosedMixedDesignator && + isa(InitArgList[0])) { + DiagnosedMixedDesignator = true; + auto *DIE = cast(InitArgList[0]); + Diag(DIE->getBeginLoc(), diag::ext_designated_init_mixed) + << DIE->getSourceRange(); + Diag(InitArgList[I]->getBeginLoc(), diag::note_designated_init_mixed) + << InitArgList[I]->getSourceRange(); + } } - if (!RHSType->isIntegerType() && !RHSType->isRealFloatingType()) { - S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_int_float) - << RHSType << RHS.get()->getSourceRange(); - return QualType(); + if (FirstDesignator.isValid()) { + // Only diagnose designated initiaization as a C++20 extension if we didn't + // already diagnose use of (non-C++20) C99 designator syntax. + if (getLangOpts().CPlusPlus && !DiagnosedArrayDesignator && + !DiagnosedNestedDesignator && !DiagnosedMixedDesignator) { + Diag(FirstDesignator, getLangOpts().CPlusPlus20 + ? diag::warn_cxx17_compat_designated_init + : diag::ext_cxx_designated_init); + } else if (!getLangOpts().CPlusPlus && !getLangOpts().C99) { + Diag(FirstDesignator, diag::ext_designated_init); + } } - // If both types are identical, no conversion is needed. - if (LHSType == RHSType) - return LHSType; - - // Now handle "real" floating types (i.e. float, double, long double). - if (LHSType->isRealFloatingType() || RHSType->isRealFloatingType()) - return handleFloatConversion(S, LHS, RHS, LHSType, RHSType, - /*IsCompAssign = */ false); - - // Finally, we have two differing integer types. - return handleIntegerConversion - (S, LHS, RHS, LHSType, RHSType, /*IsCompAssign = */ false); + return BuildInitList(LBraceLoc, InitArgList, RBraceLoc); } -/// Convert scalar operands to a vector that matches the -/// condition in length. -/// -/// Used when handling the OpenCL conditional operator where the -/// condition is a vector while the other operands are scalar. -/// -/// We first compute the "result type" for the scalar operands -/// according to OpenCL v1.1 s6.3.i. Both operands are then converted -/// into a vector of that type where the length matches the condition -/// vector type. s6.11.6 requires that the element types of the result -/// and the condition must have the same number of bits. -static QualType -OpenCLConvertScalarsToVectors(Sema &S, ExprResult &LHS, ExprResult &RHS, - QualType CondTy, SourceLocation QuestionLoc) { - QualType ResTy = OpenCLArithmeticConversions(S, LHS, RHS, QuestionLoc); - if (ResTy.isNull()) return QualType(); +ExprResult +Sema::BuildInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList, + SourceLocation RBraceLoc) { + // Semantic analysis for initializers is done by ActOnDeclarator() and + // CheckInitializer() - it requires knowledge of the object being initialized. - const VectorType *CV = CondTy->getAs(); - assert(CV); + // Immediately handle non-overload placeholders. Overloads can be + // resolved contextually, but everything else here can't. + for (unsigned I = 0, E = InitArgList.size(); I != E; ++I) { + if (InitArgList[I]->getType()->isNonOverloadPlaceholderType()) { + ExprResult result = CheckPlaceholderExpr(InitArgList[I]); - // Determine the vector result type - unsigned NumElements = CV->getNumElements(); - QualType VectorTy = S.Context.getExtVectorType(ResTy, NumElements); + // Ignore failures; dropping the entire initializer list because + // of one failure would be terrible for indexing/etc. + if (result.isInvalid()) continue; - // Ensure that all types have the same number of bits - if (S.Context.getTypeSize(CV->getElementType()) - != S.Context.getTypeSize(ResTy)) { - // Since VectorTy is created internally, it does not pretty print - // with an OpenCL name. Instead, we just print a description. - std::string EleTyName = ResTy.getUnqualifiedType().getAsString(); - SmallString<64> Str; - llvm::raw_svector_ostream OS(Str); - OS << "(vector of " << NumElements << " '" << EleTyName << "' values)"; - S.Diag(QuestionLoc, diag::err_conditional_vector_element_size) - << CondTy << OS.str(); - return QualType(); + InitArgList[I] = result.get(); + } } - // Convert operands to the vector result type - LHS = S.ImpCastExprToType(LHS.get(), VectorTy, CK_VectorSplat); - RHS = S.ImpCastExprToType(RHS.get(), VectorTy, CK_VectorSplat); - - return VectorTy; -} - -/// Return false if this is a valid OpenCL condition vector -static bool checkOpenCLConditionVector(Sema &S, Expr *Cond, - SourceLocation QuestionLoc) { - // OpenCL v1.1 s6.11.6 says the elements of the vector must be of - // integral type. - const VectorType *CondTy = Cond->getType()->getAs(); - assert(CondTy); - QualType EleTy = CondTy->getElementType(); - if (EleTy->isIntegerType()) return false; - - S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_nonfloat) - << Cond->getType() << Cond->getSourceRange(); - return true; + InitListExpr *E = + new (Context) InitListExpr(Context, LBraceLoc, InitArgList, RBraceLoc); + E->setType(Context.VoidTy); // FIXME: just a place holder for now. + return E; } -/// Return false if the vector condition type and the vector -/// result type are compatible. -/// -/// OpenCL v1.1 s6.11.6 requires that both vector types have the same -/// number of elements, and their element types have the same number -/// of bits. -static bool checkVectorResult(Sema &S, QualType CondTy, QualType VecResTy, - SourceLocation QuestionLoc) { - const VectorType *CV = CondTy->getAs(); - const VectorType *RV = VecResTy->getAs(); - assert(CV && RV); - - if (CV->getNumElements() != RV->getNumElements()) { - S.Diag(QuestionLoc, diag::err_conditional_vector_size) - << CondTy << VecResTy; - return true; - } - - QualType CVE = CV->getElementType(); - QualType RVE = RV->getElementType(); +void Sema::maybeExtendBlockObject(ExprResult &E) { + assert(E.get()->getType()->isBlockPointerType()); + assert(E.get()->isPRValue()); - if (S.Context.getTypeSize(CVE) != S.Context.getTypeSize(RVE)) { - S.Diag(QuestionLoc, diag::err_conditional_vector_element_size) - << CondTy << VecResTy; - return true; - } + // Only do this in an r-value context. + if (!getLangOpts().ObjCAutoRefCount) return; - return false; + E = ImplicitCastExpr::Create( + Context, E.get()->getType(), CK_ARCExtendBlockObject, E.get(), + /*base path*/ nullptr, VK_PRValue, FPOptionsOverride()); + Cleanup.setExprNeedsCleanups(true); } -/// Return the resulting type for the conditional operator in -/// OpenCL (aka "ternary selection operator", OpenCL v1.1 -/// s6.3.i) when the condition is a vector type. -static QualType -OpenCLCheckVectorConditional(Sema &S, ExprResult &Cond, - ExprResult &LHS, ExprResult &RHS, - SourceLocation QuestionLoc) { - Cond = S.DefaultFunctionArrayLvalueConversion(Cond.get()); - if (Cond.isInvalid()) - return QualType(); - QualType CondTy = Cond.get()->getType(); +CastKind Sema::PrepareScalarCast(ExprResult &Src, QualType DestTy) { + // Both Src and Dest are scalar types, i.e. arithmetic or pointer. + // Also, callers should have filtered out the invalid cases with + // pointers. Everything else should be possible. - if (checkOpenCLConditionVector(S, Cond.get(), QuestionLoc)) - return QualType(); + QualType SrcTy = Src.get()->getType(); + if (Context.hasSameUnqualifiedType(SrcTy, DestTy)) + return CK_NoOp; - // If either operand is a vector then find the vector type of the - // result as specified in OpenCL v1.1 s6.3.i. - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) { - bool IsBoolVecLang = - !S.getLangOpts().OpenCL && !S.getLangOpts().OpenCLCPlusPlus; - QualType VecResTy = - S.CheckVectorOperands(LHS, RHS, QuestionLoc, - /*isCompAssign*/ false, - /*AllowBothBool*/ true, - /*AllowBoolConversions*/ false, - /*AllowBooleanOperation*/ IsBoolVecLang, - /*ReportInvalid*/ true); - if (VecResTy.isNull()) - return QualType(); - // The result type must match the condition type as specified in - // OpenCL v1.1 s6.11.6. - if (checkVectorResult(S, CondTy, VecResTy, QuestionLoc)) - return QualType(); - return VecResTy; - } + switch (Type::ScalarTypeKind SrcKind = SrcTy->getScalarTypeKind()) { + case Type::STK_MemberPointer: + llvm_unreachable("member pointer type in C"); - // Both operands are scalar. - return OpenCLConvertScalarsToVectors(S, LHS, RHS, CondTy, QuestionLoc); -} - -/// Return true if the Expr is block type -static bool checkBlockType(Sema &S, const Expr *E) { - if (E->getType()->isBlockPointerType()) { - S.Diag(E->getExprLoc(), diag::err_opencl_ternary_with_block); - return true; - } + case Type::STK_CPointer: + case Type::STK_BlockPointer: + case Type::STK_ObjCObjectPointer: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_CPointer: { + LangAS SrcAS = SrcTy->getPointeeType().getAddressSpace(); + LangAS DestAS = DestTy->getPointeeType().getAddressSpace(); + if (SrcAS != DestAS) + return CK_AddressSpaceConversion; + if (Context.hasCvrSimilarType(SrcTy, DestTy)) + return CK_NoOp; + return CK_BitCast; + } + case Type::STK_BlockPointer: + return (SrcKind == Type::STK_BlockPointer + ? CK_BitCast : CK_AnyPointerToBlockPointerCast); + case Type::STK_ObjCObjectPointer: + if (SrcKind == Type::STK_ObjCObjectPointer) + return CK_BitCast; + if (SrcKind == Type::STK_CPointer) + return CK_CPointerToObjCPointerCast; + maybeExtendBlockObject(Src); + return CK_BlockPointerToObjCPointerCast; + case Type::STK_Bool: + return CK_PointerToBoolean; + case Type::STK_Integral: + return CK_PointerToIntegral; + case Type::STK_Floating: + case Type::STK_FloatingComplex: + case Type::STK_IntegralComplex: + case Type::STK_MemberPointer: + case Type::STK_FixedPoint: + llvm_unreachable("illegal cast from pointer"); + } + llvm_unreachable("Should have returned before this"); - if (const CallExpr *CE = dyn_cast(E)) { - QualType Ty = CE->getCallee()->getType(); - if (Ty->isBlockPointerType()) { - S.Diag(E->getExprLoc(), diag::err_opencl_ternary_with_block); - return true; + case Type::STK_FixedPoint: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_FixedPoint: + return CK_FixedPointCast; + case Type::STK_Bool: + return CK_FixedPointToBoolean; + case Type::STK_Integral: + return CK_FixedPointToIntegral; + case Type::STK_Floating: + return CK_FixedPointToFloating; + case Type::STK_IntegralComplex: + case Type::STK_FloatingComplex: + Diag(Src.get()->getExprLoc(), + diag::err_unimplemented_conversion_with_fixed_point_type) + << DestTy; + return CK_IntegralCast; + case Type::STK_CPointer: + case Type::STK_ObjCObjectPointer: + case Type::STK_BlockPointer: + case Type::STK_MemberPointer: + llvm_unreachable("illegal cast to pointer type"); } - } - return false; -} + llvm_unreachable("Should have returned before this"); -/// Note that LHS is not null here, even if this is the gnu "x ?: y" extension. -/// In that case, LHS = cond. -/// C99 6.5.15 -QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, - ExprResult &RHS, ExprValueKind &VK, - ExprObjectKind &OK, - SourceLocation QuestionLoc) { + case Type::STK_Bool: // casting from bool is like casting from an integer + case Type::STK_Integral: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_CPointer: + case Type::STK_ObjCObjectPointer: + case Type::STK_BlockPointer: + if (Src.get()->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNull)) + return CK_NullToPointer; + return CK_IntegralToPointer; + case Type::STK_Bool: + return CK_IntegralToBoolean; + case Type::STK_Integral: + return CK_IntegralCast; + case Type::STK_Floating: + return CK_IntegralToFloating; + case Type::STK_IntegralComplex: + Src = ImpCastExprToType(Src.get(), + DestTy->castAs()->getElementType(), + CK_IntegralCast); + return CK_IntegralRealToComplex; + case Type::STK_FloatingComplex: + Src = ImpCastExprToType(Src.get(), + DestTy->castAs()->getElementType(), + CK_IntegralToFloating); + return CK_FloatingRealToComplex; + case Type::STK_MemberPointer: + llvm_unreachable("member pointer type in C"); + case Type::STK_FixedPoint: + return CK_IntegralToFixedPoint; + } + llvm_unreachable("Should have returned before this"); - ExprResult LHSResult = CheckPlaceholderExpr(LHS.get()); - if (!LHSResult.isUsable()) return QualType(); - LHS = LHSResult; + case Type::STK_Floating: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_Floating: + return CK_FloatingCast; + case Type::STK_Bool: + return CK_FloatingToBoolean; + case Type::STK_Integral: + return CK_FloatingToIntegral; + case Type::STK_FloatingComplex: + Src = ImpCastExprToType(Src.get(), + DestTy->castAs()->getElementType(), + CK_FloatingCast); + return CK_FloatingRealToComplex; + case Type::STK_IntegralComplex: + Src = ImpCastExprToType(Src.get(), + DestTy->castAs()->getElementType(), + CK_FloatingToIntegral); + return CK_IntegralRealToComplex; + case Type::STK_CPointer: + case Type::STK_ObjCObjectPointer: + case Type::STK_BlockPointer: + llvm_unreachable("valid float->pointer cast?"); + case Type::STK_MemberPointer: + llvm_unreachable("member pointer type in C"); + case Type::STK_FixedPoint: + return CK_FloatingToFixedPoint; + } + llvm_unreachable("Should have returned before this"); - ExprResult RHSResult = CheckPlaceholderExpr(RHS.get()); - if (!RHSResult.isUsable()) return QualType(); - RHS = RHSResult; + case Type::STK_FloatingComplex: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_FloatingComplex: + return CK_FloatingComplexCast; + case Type::STK_IntegralComplex: + return CK_FloatingComplexToIntegralComplex; + case Type::STK_Floating: { + QualType ET = SrcTy->castAs()->getElementType(); + if (Context.hasSameType(ET, DestTy)) + return CK_FloatingComplexToReal; + Src = ImpCastExprToType(Src.get(), ET, CK_FloatingComplexToReal); + return CK_FloatingCast; + } + case Type::STK_Bool: + return CK_FloatingComplexToBoolean; + case Type::STK_Integral: + Src = ImpCastExprToType(Src.get(), + SrcTy->castAs()->getElementType(), + CK_FloatingComplexToReal); + return CK_FloatingToIntegral; + case Type::STK_CPointer: + case Type::STK_ObjCObjectPointer: + case Type::STK_BlockPointer: + llvm_unreachable("valid complex float->pointer cast?"); + case Type::STK_MemberPointer: + llvm_unreachable("member pointer type in C"); + case Type::STK_FixedPoint: + Diag(Src.get()->getExprLoc(), + diag::err_unimplemented_conversion_with_fixed_point_type) + << SrcTy; + return CK_IntegralCast; + } + llvm_unreachable("Should have returned before this"); - // C++ is sufficiently different to merit its own checker. - if (getLangOpts().CPlusPlus) - return CXXCheckConditionalOperands(Cond, LHS, RHS, VK, OK, QuestionLoc); + case Type::STK_IntegralComplex: + switch (DestTy->getScalarTypeKind()) { + case Type::STK_FloatingComplex: + return CK_IntegralComplexToFloatingComplex; + case Type::STK_IntegralComplex: + return CK_IntegralComplexCast; + case Type::STK_Integral: { + QualType ET = SrcTy->castAs()->getElementType(); + if (Context.hasSameType(ET, DestTy)) + return CK_IntegralComplexToReal; + Src = ImpCastExprToType(Src.get(), ET, CK_IntegralComplexToReal); + return CK_IntegralCast; + } + case Type::STK_Bool: + return CK_IntegralComplexToBoolean; + case Type::STK_Floating: + Src = ImpCastExprToType(Src.get(), + SrcTy->castAs()->getElementType(), + CK_IntegralComplexToReal); + return CK_IntegralToFloating; + case Type::STK_CPointer: + case Type::STK_ObjCObjectPointer: + case Type::STK_BlockPointer: + llvm_unreachable("valid complex int->pointer cast?"); + case Type::STK_MemberPointer: + llvm_unreachable("member pointer type in C"); + case Type::STK_FixedPoint: + Diag(Src.get()->getExprLoc(), + diag::err_unimplemented_conversion_with_fixed_point_type) + << SrcTy; + return CK_IntegralCast; + } + llvm_unreachable("Should have returned before this"); + } - VK = VK_PRValue; - OK = OK_Ordinary; + llvm_unreachable("Unhandled scalar cast"); +} - if (Context.isDependenceAllowed() && - (Cond.get()->isTypeDependent() || LHS.get()->isTypeDependent() || - RHS.get()->isTypeDependent())) { - assert(!getLangOpts().CPlusPlus); - assert((Cond.get()->containsErrors() || LHS.get()->containsErrors() || - RHS.get()->containsErrors()) && - "should only occur in error-recovery path."); - return Context.DependentTy; +static bool breakDownVectorType(QualType type, uint64_t &len, + QualType &eltType) { + // Vectors are simple. + if (const VectorType *vecType = type->getAs()) { + len = vecType->getNumElements(); + eltType = vecType->getElementType(); + assert(eltType->isScalarType() || eltType->isMFloat8Type()); + return true; } - // The OpenCL operator with a vector condition is sufficiently - // different to merit its own checker. - if ((getLangOpts().OpenCL && Cond.get()->getType()->isVectorType()) || - Cond.get()->getType()->isExtVectorType()) - return OpenCLCheckVectorConditional(*this, Cond, LHS, RHS, QuestionLoc); + // We allow lax conversion to and from non-vector types, but only if + // they're real types (i.e. non-complex, non-pointer scalar types). + if (!type->isRealType()) return false; - // First, check the condition. - Cond = UsualUnaryConversions(Cond.get()); - if (Cond.isInvalid()) - return QualType(); - if (checkCondition(*this, Cond.get(), QuestionLoc)) - return QualType(); + len = 1; + eltType = type; + return true; +} - // Handle vectors. - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) - return CheckVectorOperands(LHS, RHS, QuestionLoc, /*isCompAssign*/ false, - /*AllowBothBool*/ true, - /*AllowBoolConversions*/ false, - /*AllowBooleanOperation*/ false, - /*ReportInvalid*/ true); +bool Sema::isValidSveBitcast(QualType srcTy, QualType destTy) { + assert(srcTy->isVectorType() || destTy->isVectorType()); - QualType ResTy = - UsualArithmeticConversions(LHS, RHS, QuestionLoc, ACK_Conditional); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); + auto ValidScalableConversion = [](QualType FirstType, QualType SecondType) { + if (!FirstType->isSVESizelessBuiltinType()) + return false; - // WebAssembly tables are not allowed as conditional LHS or RHS. - QualType LHSTy = LHS.get()->getType(); - QualType RHSTy = RHS.get()->getType(); - if (LHSTy->isWebAssemblyTableType() || RHSTy->isWebAssemblyTableType()) { - Diag(QuestionLoc, diag::err_wasm_table_conditional_expression) - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return QualType(); - } + const auto *VecTy = SecondType->getAs(); + return VecTy && VecTy->getVectorKind() == VectorKind::SveFixedLengthData; + }; - // Diagnose attempts to convert between __ibm128, __float128 and long double - // where such conversions currently can't be handled. - if (unsupportedTypeConversion(*this, LHSTy, RHSTy)) { - Diag(QuestionLoc, - diag::err_typecheck_cond_incompatible_operands) << LHSTy << RHSTy - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return QualType(); - } + return ValidScalableConversion(srcTy, destTy) || + ValidScalableConversion(destTy, srcTy); +} - // OpenCL v2.0 s6.12.5 - Blocks cannot be used as expressions of the ternary - // selection operator (?:). - if (getLangOpts().OpenCL && - ((int)checkBlockType(*this, LHS.get()) | (int)checkBlockType(*this, RHS.get()))) { - return QualType(); - } +bool Sema::areMatrixTypesOfTheSameDimension(QualType srcTy, QualType destTy) { + if (!destTy->isMatrixType() || !srcTy->isMatrixType()) + return false; - // If both operands have arithmetic type, do the usual arithmetic conversions - // to find a common type: C99 6.5.15p3,5. - if (LHSTy->isArithmeticType() && RHSTy->isArithmeticType()) { - // Disallow invalid arithmetic conversions, such as those between bit- - // precise integers types of different sizes, or between a bit-precise - // integer and another type. - if (ResTy.isNull() && (LHSTy->isBitIntType() || RHSTy->isBitIntType())) { - Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands) - << LHSTy << RHSTy << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); - } + const ConstantMatrixType *matSrcType = srcTy->getAs(); + const ConstantMatrixType *matDestType = destTy->getAs(); - LHS = ImpCastExprToType(LHS.get(), ResTy, PrepareScalarCast(LHS, ResTy)); - RHS = ImpCastExprToType(RHS.get(), ResTy, PrepareScalarCast(RHS, ResTy)); + return matSrcType->getNumRows() == matDestType->getNumRows() && + matSrcType->getNumColumns() == matDestType->getNumColumns(); +} - return ResTy; - } +bool Sema::areVectorTypesSameSize(QualType SrcTy, QualType DestTy) { + assert(DestTy->isVectorType() || SrcTy->isVectorType()); - // If both operands are the same structure or union type, the result is that - // type. - if (const RecordType *LHSRT = LHSTy->getAs()) { // C99 6.5.15p3 - if (const RecordType *RHSRT = RHSTy->getAs()) - if (LHSRT->getDecl() == RHSRT->getDecl()) - // "If both the operands have structure or union type, the result has - // that type." This implies that CV qualifiers are dropped. - return Context.getCommonSugaredType(LHSTy.getUnqualifiedType(), - RHSTy.getUnqualifiedType()); - // FIXME: Type of conditional expression must be complete in C mode. - } + uint64_t SrcLen, DestLen; + QualType SrcEltTy, DestEltTy; + if (!breakDownVectorType(SrcTy, SrcLen, SrcEltTy)) + return false; + if (!breakDownVectorType(DestTy, DestLen, DestEltTy)) + return false; - // C99 6.5.15p5: "If both operands have void type, the result has void type." - // The following || allows only one side to be void (a GCC-ism). - if (LHSTy->isVoidType() || RHSTy->isVoidType()) { - QualType ResTy; - if (LHSTy->isVoidType() && RHSTy->isVoidType()) { - ResTy = Context.getCommonSugaredType(LHSTy, RHSTy); - } else if (RHSTy->isVoidType()) { - ResTy = RHSTy; - Diag(RHS.get()->getBeginLoc(), diag::ext_typecheck_cond_one_void) - << RHS.get()->getSourceRange(); - } else { - ResTy = LHSTy; - Diag(LHS.get()->getBeginLoc(), diag::ext_typecheck_cond_one_void) - << LHS.get()->getSourceRange(); - } - LHS = ImpCastExprToType(LHS.get(), ResTy, CK_ToVoid); - RHS = ImpCastExprToType(RHS.get(), ResTy, CK_ToVoid); - return ResTy; - } + // ASTContext::getTypeSize will return the size rounded up to a + // power of 2, so instead of using that, we need to use the raw + // element size multiplied by the element count. + uint64_t SrcEltSize = Context.getTypeSize(SrcEltTy); + uint64_t DestEltSize = Context.getTypeSize(DestEltTy); - // C23 6.5.15p7: - // ... if both the second and third operands have nullptr_t type, the - // result also has that type. - if (LHSTy->isNullPtrType() && Context.hasSameType(LHSTy, RHSTy)) - return ResTy; + return (SrcLen * SrcEltSize == DestLen * DestEltSize); +} - // C99 6.5.15p6 - "if one operand is a null pointer constant, the result has - // the type of the other operand." - if (!checkConditionalNullPointer(*this, RHS, LHSTy)) return LHSTy; - if (!checkConditionalNullPointer(*this, LHS, RHSTy)) return RHSTy; +bool Sema::anyAltivecTypes(QualType SrcTy, QualType DestTy) { + assert((DestTy->isVectorType() || SrcTy->isVectorType()) && + "expected at least one type to be a vector here"); - // All objective-c pointer type analysis is done here. - QualType compositeType = - ObjC().FindCompositeObjCPointerType(LHS, RHS, QuestionLoc); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); - if (!compositeType.isNull()) - return compositeType; + bool IsSrcTyAltivec = + SrcTy->isVectorType() && ((SrcTy->castAs()->getVectorKind() == + VectorKind::AltiVecVector) || + (SrcTy->castAs()->getVectorKind() == + VectorKind::AltiVecBool) || + (SrcTy->castAs()->getVectorKind() == + VectorKind::AltiVecPixel)); + bool IsDestTyAltivec = DestTy->isVectorType() && + ((DestTy->castAs()->getVectorKind() == + VectorKind::AltiVecVector) || + (DestTy->castAs()->getVectorKind() == + VectorKind::AltiVecBool) || + (DestTy->castAs()->getVectorKind() == + VectorKind::AltiVecPixel)); - // Handle block pointer types. - if (LHSTy->isBlockPointerType() || RHSTy->isBlockPointerType()) - return checkConditionalBlockPointerCompatibility(*this, LHS, RHS, - QuestionLoc); + return (IsSrcTyAltivec || IsDestTyAltivec); +} - // Check constraints for C object pointers types (C99 6.5.15p3,6). - if (LHSTy->isPointerType() && RHSTy->isPointerType()) - return checkConditionalObjectPointersCompatibility(*this, LHS, RHS, - QuestionLoc); +bool Sema::areLaxCompatibleVectorTypes(QualType srcTy, QualType destTy) { + assert(destTy->isVectorType() || srcTy->isVectorType()); - // GCC compatibility: soften pointer/integer mismatch. Note that - // null pointers have been filtered out by this point. - if (checkPointerIntegerMismatch(*this, LHS, RHS.get(), QuestionLoc, - /*IsIntFirstExpr=*/true)) - return RHSTy; - if (checkPointerIntegerMismatch(*this, RHS, LHS.get(), QuestionLoc, - /*IsIntFirstExpr=*/false)) - return LHSTy; + // Disallow lax conversions between scalars and ExtVectors (these + // conversions are allowed for other vector types because common headers + // depend on them). Most scalar OP ExtVector cases are handled by the + // splat path anyway, which does what we want (convert, not bitcast). + // What this rules out for ExtVectors is crazy things like char4*float. + if (srcTy->isScalarType() && destTy->isExtVectorType()) return false; + if (destTy->isScalarType() && srcTy->isExtVectorType()) return false; - // Emit a better diagnostic if one of the expressions is a null pointer - // constant and the other is not a pointer type. In this case, the user most - // likely forgot to take the address of the other expression. - if (DiagnoseConditionalForNull(LHS.get(), RHS.get(), QuestionLoc)) - return QualType(); + return areVectorTypesSameSize(srcTy, destTy); +} - // Finally, if the LHS and RHS types are canonically the same type, we can - // use the common sugared type. - if (Context.hasSameType(LHSTy, RHSTy)) - return Context.getCommonSugaredType(LHSTy, RHSTy); +bool Sema::isLaxVectorConversion(QualType srcTy, QualType destTy) { + assert(destTy->isVectorType() || srcTy->isVectorType()); - // Otherwise, the operands are not compatible. - Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands) - << LHSTy << RHSTy << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); -} + switch (Context.getLangOpts().getLaxVectorConversions()) { + case LangOptions::LaxVectorConversionKind::None: + return false; -/// SuggestParentheses - Emit a note with a fixit hint that wraps -/// ParenRange in parentheses. -static void SuggestParentheses(Sema &Self, SourceLocation Loc, - const PartialDiagnostic &Note, - SourceRange ParenRange) { - SourceLocation EndLoc = Self.getLocForEndOfToken(ParenRange.getEnd()); - if (ParenRange.getBegin().isFileID() && ParenRange.getEnd().isFileID() && - EndLoc.isValid()) { - Self.Diag(Loc, Note) - << FixItHint::CreateInsertion(ParenRange.getBegin(), "(") - << FixItHint::CreateInsertion(EndLoc, ")"); - } else { - // We can't display the parentheses, so just show the bare note. - Self.Diag(Loc, Note) << ParenRange; + case LangOptions::LaxVectorConversionKind::Integer: + if (!srcTy->isIntegralOrEnumerationType()) { + auto *Vec = srcTy->getAs(); + if (!Vec || !Vec->getElementType()->isIntegralOrEnumerationType()) + return false; + } + if (!destTy->isIntegralOrEnumerationType()) { + auto *Vec = destTy->getAs(); + if (!Vec || !Vec->getElementType()->isIntegralOrEnumerationType()) + return false; + } + // OK, integer (vector) -> integer (vector) bitcast. + break; + + case LangOptions::LaxVectorConversionKind::All: + break; } + + return areLaxCompatibleVectorTypes(srcTy, destTy); } -static bool IsArithmeticOp(BinaryOperatorKind Opc) { - return BinaryOperator::isAdditiveOp(Opc) || - BinaryOperator::isMultiplicativeOp(Opc) || - BinaryOperator::isShiftOp(Opc) || Opc == BO_And || Opc == BO_Or; - // This only checks for bitwise-or and bitwise-and, but not bitwise-xor and - // not any of the logical operators. Bitwise-xor is commonly used as a - // logical-xor because there is no logical-xor operator. The logical - // operators, including uses of xor, have a high false positive rate for - // precedence warnings. +bool Sema::CheckMatrixCast(SourceRange R, QualType DestTy, QualType SrcTy, + CastKind &Kind) { + if (SrcTy->isMatrixType() && DestTy->isMatrixType()) { + if (!areMatrixTypesOfTheSameDimension(SrcTy, DestTy)) { + return Diag(R.getBegin(), diag::err_invalid_conversion_between_matrixes) + << DestTy << SrcTy << R; + } + } else if (SrcTy->isMatrixType()) { + return Diag(R.getBegin(), + diag::err_invalid_conversion_between_matrix_and_type) + << SrcTy << DestTy << R; + } else if (DestTy->isMatrixType()) { + return Diag(R.getBegin(), + diag::err_invalid_conversion_between_matrix_and_type) + << DestTy << SrcTy << R; + } + + Kind = CK_MatrixCast; + return false; } -/// IsArithmeticBinaryExpr - Returns true if E is an arithmetic binary -/// expression, either using a built-in or overloaded operator, -/// and sets *OpCode to the opcode and *RHSExprs to the right-hand side -/// expression. -static bool IsArithmeticBinaryExpr(const Expr *E, BinaryOperatorKind *Opcode, - const Expr **RHSExprs) { - // Don't strip parenthesis: we should not warn if E is in parenthesis. - E = E->IgnoreImpCasts(); - E = E->IgnoreConversionOperatorSingleStep(); - E = E->IgnoreImpCasts(); - if (const auto *MTE = dyn_cast(E)) { - E = MTE->getSubExpr(); - E = E->IgnoreImpCasts(); - } +bool Sema::CheckVectorCast(SourceRange R, QualType VectorTy, QualType Ty, + CastKind &Kind) { + assert(VectorTy->isVectorType() && "Not a vector type!"); - // Built-in binary operator. - if (const auto *OP = dyn_cast(E); - OP && IsArithmeticOp(OP->getOpcode())) { - *Opcode = OP->getOpcode(); - *RHSExprs = OP->getRHS(); - return true; - } + if (Ty->isVectorType() || Ty->isIntegralType(Context)) { + if (!areLaxCompatibleVectorTypes(Ty, VectorTy)) + return Diag(R.getBegin(), + Ty->isVectorType() ? + diag::err_invalid_conversion_between_vectors : + diag::err_invalid_conversion_between_vector_and_integer) + << VectorTy << Ty << R; + } else + return Diag(R.getBegin(), + diag::err_invalid_conversion_between_vector_and_scalar) + << VectorTy << Ty << R; - // Overloaded operator. - if (const auto *Call = dyn_cast(E)) { - if (Call->getNumArgs() != 2) - return false; + Kind = CK_BitCast; + return false; +} - // Make sure this is really a binary operator that is safe to pass into - // BinaryOperator::getOverloadedOpcode(), e.g. it's not a subscript op. - OverloadedOperatorKind OO = Call->getOperator(); - if (OO < OO_Plus || OO > OO_Arrow || - OO == OO_PlusPlus || OO == OO_MinusMinus) - return false; +ExprResult Sema::prepareVectorSplat(QualType VectorTy, Expr *SplattedExpr) { + QualType DestElemTy = VectorTy->castAs()->getElementType(); - BinaryOperatorKind OpKind = BinaryOperator::getOverloadedOpcode(OO); - if (IsArithmeticOp(OpKind)) { - *Opcode = OpKind; - *RHSExprs = Call->getArg(1); - return true; + if (DestElemTy == SplattedExpr->getType()) + return SplattedExpr; + + assert(DestElemTy->isFloatingType() || + DestElemTy->isIntegralOrEnumerationType()); + + CastKind CK; + if (VectorTy->isExtVectorType() && SplattedExpr->getType()->isBooleanType()) { + // OpenCL requires that we convert `true` boolean expressions to -1, but + // only when splatting vectors. + if (DestElemTy->isFloatingType()) { + // To avoid having to have a CK_BooleanToSignedFloating cast kind, we cast + // in two steps: boolean to signed integral, then to floating. + ExprResult CastExprRes = ImpCastExprToType(SplattedExpr, Context.IntTy, + CK_BooleanToSignedIntegral); + SplattedExpr = CastExprRes.get(); + CK = CK_IntegralToFloating; + } else { + CK = CK_BooleanToSignedIntegral; } + } else { + ExprResult CastExprRes = SplattedExpr; + CK = PrepareScalarCast(CastExprRes, DestElemTy); + if (CastExprRes.isInvalid()) + return ExprError(); + SplattedExpr = CastExprRes.get(); } - - return false; + return ImpCastExprToType(SplattedExpr, DestElemTy, CK); } -/// ExprLooksBoolean - Returns true if E looks boolean, i.e. it has boolean type -/// or is a logical expression such as (x==y) which has int type, but is -/// commonly interpreted as boolean. -static bool ExprLooksBoolean(const Expr *E) { - E = E->IgnoreParenImpCasts(); +ExprResult Sema::CheckExtVectorCast(SourceRange R, QualType DestTy, + Expr *CastExpr, CastKind &Kind) { + assert(DestTy->isExtVectorType() && "Not an extended vector type!"); - if (E->getType()->isBooleanType()) - return true; - if (const auto *OP = dyn_cast(E)) - return OP->isComparisonOp() || OP->isLogicalOp(); - if (const auto *OP = dyn_cast(E)) - return OP->getOpcode() == UO_LNot; - if (E->getType()->isPointerType()) - return true; - // FIXME: What about overloaded operator calls returning "unspecified boolean - // type"s (commonly pointer-to-members)? + QualType SrcTy = CastExpr->getType(); - return false; -} + // If SrcTy is a VectorType, the total size must match to explicitly cast to + // an ExtVectorType. + // In OpenCL, casts between vectors of different types are not allowed. + // (See OpenCL 6.2). + if (SrcTy->isVectorType()) { + if (!areLaxCompatibleVectorTypes(SrcTy, DestTy) || + (getLangOpts().OpenCL && + !Context.hasSameUnqualifiedType(DestTy, SrcTy))) { + Diag(R.getBegin(),diag::err_invalid_conversion_between_ext_vectors) + << DestTy << SrcTy << R; + return ExprError(); + } + Kind = CK_BitCast; + return CastExpr; + } -/// DiagnoseConditionalPrecedence - Emit a warning when a conditional operator -/// and binary operator are mixed in a way that suggests the programmer assumed -/// the conditional operator has higher precedence, for example: -/// "int x = a + someBinaryCondition ? 1 : 2". -static void DiagnoseConditionalPrecedence(Sema &Self, SourceLocation OpLoc, - Expr *Condition, const Expr *LHSExpr, - const Expr *RHSExpr) { - BinaryOperatorKind CondOpcode; - const Expr *CondRHS; + // All non-pointer scalars can be cast to ExtVector type. The appropriate + // conversion will take place first from scalar to elt type, and then + // splat from elt type to vector. + if (SrcTy->isPointerType()) + return Diag(R.getBegin(), + diag::err_invalid_conversion_between_vector_and_scalar) + << DestTy << SrcTy << R; - if (!IsArithmeticBinaryExpr(Condition, &CondOpcode, &CondRHS)) - return; - if (!ExprLooksBoolean(CondRHS)) - return; + Kind = CK_VectorSplat; + return prepareVectorSplat(DestTy, CastExpr); +} - // The condition is an arithmetic binary expression, with a right- - // hand side that looks boolean, so warn. +ExprResult +Sema::ActOnCastExpr(Scope *S, SourceLocation LParenLoc, + Declarator &D, ParsedType &Ty, + SourceLocation RParenLoc, Expr *CastExpr) { + assert(!D.isInvalidType() && (CastExpr != nullptr) && + "ActOnCastExpr(): missing type or expr"); - unsigned DiagID = BinaryOperator::isBitwiseOp(CondOpcode) - ? diag::warn_precedence_bitwise_conditional - : diag::warn_precedence_conditional; + TypeSourceInfo *castTInfo = GetTypeForDeclaratorCast(D, CastExpr->getType()); + if (D.isInvalidType()) + return ExprError(); - Self.Diag(OpLoc, DiagID) - << Condition->getSourceRange() - << BinaryOperator::getOpcodeStr(CondOpcode); + if (getLangOpts().CPlusPlus) { + // Check that there are no default arguments (C++ only). + CheckExtraCXXDefaultArguments(D); + } else { + // Make sure any TypoExprs have been dealt with. + ExprResult Res = CorrectDelayedTyposInExpr(CastExpr); + if (!Res.isUsable()) + return ExprError(); + CastExpr = Res.get(); + } - SuggestParentheses( - Self, OpLoc, - Self.PDiag(diag::note_precedence_silence) - << BinaryOperator::getOpcodeStr(CondOpcode), - SourceRange(Condition->getBeginLoc(), Condition->getEndLoc())); + checkUnusedDeclAttributes(D); - SuggestParentheses(Self, OpLoc, - Self.PDiag(diag::note_precedence_conditional_first), - SourceRange(CondRHS->getBeginLoc(), RHSExpr->getEndLoc())); -} + QualType castType = castTInfo->getType(); + Ty = CreateParsedType(castType, castTInfo); -/// Compute the nullability of a conditional expression. -static QualType computeConditionalNullability(QualType ResTy, bool IsBin, - QualType LHSTy, QualType RHSTy, - ASTContext &Ctx) { - if (!ResTy->isAnyPointerType()) - return ResTy; + bool isVectorLiteral = false; - auto GetNullability = [](QualType Ty) { - std::optional Kind = Ty->getNullability(); - if (Kind) { - // For our purposes, treat _Nullable_result as _Nullable. - if (*Kind == NullabilityKind::NullableResult) - return NullabilityKind::Nullable; - return *Kind; + // Check for an altivec or OpenCL literal, + // i.e. all the elements are integer constants. + ParenExpr *PE = dyn_cast(CastExpr); + ParenListExpr *PLE = dyn_cast(CastExpr); + if ((getLangOpts().AltiVec || getLangOpts().ZVector || getLangOpts().OpenCL) + && castType->isVectorType() && (PE || PLE)) { + if (PLE && PLE->getNumExprs() == 0) { + Diag(PLE->getExprLoc(), diag::err_altivec_empty_initializer); + return ExprError(); } - return NullabilityKind::Unspecified; - }; + if (PE || PLE->getNumExprs() == 1) { + Expr *E = (PE ? PE->getSubExpr() : PLE->getExpr(0)); + if (!E->isTypeDependent() && !E->getType()->isVectorType()) + isVectorLiteral = true; + } + else + isVectorLiteral = true; + } - auto LHSKind = GetNullability(LHSTy), RHSKind = GetNullability(RHSTy); - NullabilityKind MergedKind; + // If this is a vector initializer, '(' type ')' '(' init, ..., init ')' + // then handle it as such. + if (isVectorLiteral) + return BuildVectorLiteral(LParenLoc, RParenLoc, CastExpr, castTInfo); - // Compute nullability of a binary conditional expression. - if (IsBin) { - if (LHSKind == NullabilityKind::NonNull) - MergedKind = NullabilityKind::NonNull; - else - MergedKind = RHSKind; - // Compute nullability of a normal conditional expression. - } else { - if (LHSKind == NullabilityKind::Nullable || - RHSKind == NullabilityKind::Nullable) - MergedKind = NullabilityKind::Nullable; - else if (LHSKind == NullabilityKind::NonNull) - MergedKind = RHSKind; - else if (RHSKind == NullabilityKind::NonNull) - MergedKind = LHSKind; - else - MergedKind = NullabilityKind::Unspecified; + // If the Expr being casted is a ParenListExpr, handle it specially. + // This is not an AltiVec-style cast, so turn the ParenListExpr into a + // sequence of BinOp comma operators. + if (isa(CastExpr)) { + ExprResult Result = MaybeConvertParenListExprToParenExpr(S, CastExpr); + if (Result.isInvalid()) return ExprError(); + CastExpr = Result.get(); } - // Return if ResTy already has the correct nullability. - if (GetNullability(ResTy) == MergedKind) - return ResTy; + if (getLangOpts().CPlusPlus && !castType->isVoidType()) + Diag(LParenLoc, diag::warn_old_style_cast) << CastExpr->getSourceRange(); - // Strip all nullability from ResTy. - while (ResTy->getNullability()) - ResTy = ResTy.getSingleStepDesugaredType(Ctx); + ObjC().CheckTollFreeBridgeCast(castType, CastExpr); - // Create a new AttributedType with the new nullability kind. - return Ctx.getAttributedType(MergedKind, ResTy, ResTy); -} + ObjC().CheckObjCBridgeRelatedCast(castType, CastExpr); -ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc, - SourceLocation ColonLoc, - Expr *CondExpr, Expr *LHSExpr, - Expr *RHSExpr) { - if (!Context.isDependenceAllowed()) { - // C cannot handle TypoExpr nodes in the condition because it - // doesn't handle dependent types properly, so make sure any TypoExprs have - // been dealt with before checking the operands. - ExprResult CondResult = CorrectDelayedTyposInExpr(CondExpr); - ExprResult LHSResult = CorrectDelayedTyposInExpr(LHSExpr); - ExprResult RHSResult = CorrectDelayedTyposInExpr(RHSExpr); - - if (!CondResult.isUsable()) - return ExprError(); + DiscardMisalignedMemberAddress(castType.getTypePtr(), CastExpr); - if (LHSExpr) { - if (!LHSResult.isUsable()) - return ExprError(); - } + return BuildCStyleCastExpr(LParenLoc, castTInfo, RParenLoc, CastExpr); +} - if (!RHSResult.isUsable()) - return ExprError(); +ExprResult Sema::BuildVectorLiteral(SourceLocation LParenLoc, + SourceLocation RParenLoc, Expr *E, + TypeSourceInfo *TInfo) { + assert((isa(E) || isa(E)) && + "Expected paren or paren list expression"); - CondExpr = CondResult.get(); - LHSExpr = LHSResult.get(); - RHSExpr = RHSResult.get(); + Expr **exprs; + unsigned numExprs; + Expr *subExpr; + SourceLocation LiteralLParenLoc, LiteralRParenLoc; + if (ParenListExpr *PE = dyn_cast(E)) { + LiteralLParenLoc = PE->getLParenLoc(); + LiteralRParenLoc = PE->getRParenLoc(); + exprs = PE->getExprs(); + numExprs = PE->getNumExprs(); + } else { // isa by assertion at function entrance + LiteralLParenLoc = cast(E)->getLParen(); + LiteralRParenLoc = cast(E)->getRParen(); + subExpr = cast(E)->getSubExpr(); + exprs = &subExpr; + numExprs = 1; } - // If this is the gnu "x ?: y" extension, analyze the types as though the LHS - // was the condition. - OpaqueValueExpr *opaqueValue = nullptr; - Expr *commonExpr = nullptr; - if (!LHSExpr) { - commonExpr = CondExpr; - // Lower out placeholder types first. This is important so that we don't - // try to capture a placeholder. This happens in few cases in C++; such - // as Objective-C++'s dictionary subscripting syntax. - if (commonExpr->hasPlaceholderType()) { - ExprResult result = CheckPlaceholderExpr(commonExpr); - if (!result.isUsable()) return ExprError(); - commonExpr = result.get(); - } - // We usually want to apply unary conversions *before* saving, except - // in the special case of a C++ l-value conditional. - if (!(getLangOpts().CPlusPlus - && !commonExpr->isTypeDependent() - && commonExpr->getValueKind() == RHSExpr->getValueKind() - && commonExpr->isGLValue() - && commonExpr->isOrdinaryOrBitFieldObject() - && RHSExpr->isOrdinaryOrBitFieldObject() - && Context.hasSameType(commonExpr->getType(), RHSExpr->getType()))) { - ExprResult commonRes = UsualUnaryConversions(commonExpr); - if (commonRes.isInvalid()) + QualType Ty = TInfo->getType(); + assert(Ty->isVectorType() && "Expected vector type"); + + SmallVector initExprs; + const VectorType *VTy = Ty->castAs(); + unsigned numElems = VTy->getNumElements(); + + // '(...)' form of vector initialization in AltiVec: the number of + // initializers must be one or must match the size of the vector. + // If a single value is specified in the initializer then it will be + // replicated to all the components of the vector + if (CheckAltivecInitFromScalar(E->getSourceRange(), Ty, + VTy->getElementType())) + return ExprError(); + if (ShouldSplatAltivecScalarInCast(VTy)) { + // The number of initializers must be one or must match the size of the + // vector. If a single value is specified in the initializer then it will + // be replicated to all the components of the vector + if (numExprs == 1) { + QualType ElemTy = VTy->getElementType(); + ExprResult Literal = DefaultLvalueConversion(exprs[0]); + if (Literal.isInvalid()) return ExprError(); - commonExpr = commonRes.get(); + Literal = ImpCastExprToType(Literal.get(), ElemTy, + PrepareScalarCast(Literal, ElemTy)); + return BuildCStyleCastExpr(LParenLoc, TInfo, RParenLoc, Literal.get()); } - - // If the common expression is a class or array prvalue, materialize it - // so that we can safely refer to it multiple times. - if (commonExpr->isPRValue() && (commonExpr->getType()->isRecordType() || - commonExpr->getType()->isArrayType())) { - ExprResult MatExpr = TemporaryMaterializationConversion(commonExpr); - if (MatExpr.isInvalid()) + else if (numExprs < numElems) { + Diag(E->getExprLoc(), + diag::err_incorrect_number_of_vector_initializers); + return ExprError(); + } + else + initExprs.append(exprs, exprs + numExprs); + } + else { + // For OpenCL, when the number of initializers is a single value, + // it will be replicated to all components of the vector. + if (getLangOpts().OpenCL && VTy->getVectorKind() == VectorKind::Generic && + numExprs == 1) { + QualType ElemTy = VTy->getElementType(); + ExprResult Literal = DefaultLvalueConversion(exprs[0]); + if (Literal.isInvalid()) return ExprError(); - commonExpr = MatExpr.get(); + Literal = ImpCastExprToType(Literal.get(), ElemTy, + PrepareScalarCast(Literal, ElemTy)); + return BuildCStyleCastExpr(LParenLoc, TInfo, RParenLoc, Literal.get()); } - opaqueValue = new (Context) OpaqueValueExpr(commonExpr->getExprLoc(), - commonExpr->getType(), - commonExpr->getValueKind(), - commonExpr->getObjectKind(), - commonExpr); - LHSExpr = CondExpr = opaqueValue; + initExprs.append(exprs, exprs + numExprs); } + // FIXME: This means that pretty-printing the final AST will produce curly + // braces instead of the original commas. + InitListExpr *initE = new (Context) InitListExpr(Context, LiteralLParenLoc, + initExprs, LiteralRParenLoc); + initE->setType(Ty); + return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, initE); +} - QualType LHSTy = LHSExpr->getType(), RHSTy = RHSExpr->getType(); - ExprValueKind VK = VK_PRValue; - ExprObjectKind OK = OK_Ordinary; - ExprResult Cond = CondExpr, LHS = LHSExpr, RHS = RHSExpr; - QualType result = CheckConditionalOperands(Cond, LHS, RHS, - VK, OK, QuestionLoc); - if (result.isNull() || Cond.isInvalid() || LHS.isInvalid() || - RHS.isInvalid()) - return ExprError(); - - DiagnoseConditionalPrecedence(*this, QuestionLoc, Cond.get(), LHS.get(), - RHS.get()); +ExprResult +Sema::MaybeConvertParenListExprToParenExpr(Scope *S, Expr *OrigExpr) { + ParenListExpr *E = dyn_cast(OrigExpr); + if (!E) + return OrigExpr; - CheckBoolLikeConversion(Cond.get(), QuestionLoc); + ExprResult Result(E->getExpr(0)); - result = computeConditionalNullability(result, commonExpr, LHSTy, RHSTy, - Context); + for (unsigned i = 1, e = E->getNumExprs(); i != e && !Result.isInvalid(); ++i) + Result = ActOnBinOp(S, E->getExprLoc(), tok::comma, Result.get(), + E->getExpr(i)); - if (!commonExpr) - return new (Context) - ConditionalOperator(Cond.get(), QuestionLoc, LHS.get(), ColonLoc, - RHS.get(), result, VK, OK); + if (Result.isInvalid()) return ExprError(); - return new (Context) BinaryConditionalOperator( - commonExpr, opaqueValue, Cond.get(), LHS.get(), RHS.get(), QuestionLoc, - ColonLoc, result, VK, OK); + return ActOnParenExpr(E->getLParenLoc(), E->getRParenLoc(), Result.get()); } -bool Sema::IsInvalidSMECallConversion(QualType FromType, QualType ToType) { - unsigned FromAttributes = 0, ToAttributes = 0; - if (const auto *FromFn = - dyn_cast(Context.getCanonicalType(FromType))) - FromAttributes = - FromFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask; - if (const auto *ToFn = - dyn_cast(Context.getCanonicalType(ToType))) - ToAttributes = - ToFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask; - - return FromAttributes != ToAttributes; +ExprResult Sema::ActOnParenListExpr(SourceLocation L, + SourceLocation R, + MultiExprArg Val) { + return ParenListExpr::Create(Context, L, Val, R); } -// Check if we have a conversion between incompatible cmse function pointer -// types, that is, a conversion between a function pointer with the -// cmse_nonsecure_call attribute and one without. -static bool IsInvalidCmseNSCallConversion(Sema &S, QualType FromType, - QualType ToType) { - if (const auto *ToFn = - dyn_cast(S.Context.getCanonicalType(ToType))) { - if (const auto *FromFn = - dyn_cast(S.Context.getCanonicalType(FromType))) { - FunctionType::ExtInfo ToEInfo = ToFn->getExtInfo(); - FunctionType::ExtInfo FromEInfo = FromFn->getExtInfo(); +bool Sema::DiagnoseConditionalForNull(const Expr *LHSExpr, const Expr *RHSExpr, + SourceLocation QuestionLoc) { + const Expr *NullExpr = LHSExpr; + const Expr *NonPointerExpr = RHSExpr; + Expr::NullPointerConstantKind NullKind = + NullExpr->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNotNull); - return ToEInfo.getCmseNSCall() != FromEInfo.getCmseNSCall(); - } + if (NullKind == Expr::NPCK_NotNull) { + NullExpr = RHSExpr; + NonPointerExpr = LHSExpr; + NullKind = + NullExpr->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNotNull); } - return false; -} - -// checkPointerTypesForAssignment - This is a very tricky routine (despite -// being closely modeled after the C99 spec:-). The odd characteristic of this -// routine is it effectively iqnores the qualifiers on the top level pointee. -// This circumvents the usual type rules specified in 6.2.7p1 & 6.7.5.[1-3]. -// FIXME: add a couple examples in this comment. -static Sema::AssignConvertType -checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType, - SourceLocation Loc) { - assert(LHSType.isCanonical() && "LHS not canonicalized!"); - assert(RHSType.isCanonical() && "RHS not canonicalized!"); - - // get the "pointed to" type (ignoring qualifiers at the top level) - const Type *lhptee, *rhptee; - Qualifiers lhq, rhq; - std::tie(lhptee, lhq) = - cast(LHSType)->getPointeeType().split().asPair(); - std::tie(rhptee, rhq) = - cast(RHSType)->getPointeeType().split().asPair(); - Sema::AssignConvertType ConvTy = Sema::Compatible; + if (NullKind == Expr::NPCK_NotNull) + return false; - // C99 6.5.16.1p1: This following citation is common to constraints - // 3 & 4 (below). ...and the type *pointed to* by the left has all the - // qualifiers of the type *pointed to* by the right; + if (NullKind == Expr::NPCK_ZeroExpression) + return false; - // As a special case, 'non-__weak A *' -> 'non-__weak const *' is okay. - if (lhq.getObjCLifetime() != rhq.getObjCLifetime() && - lhq.compatiblyIncludesObjCLifetime(rhq)) { - // Ignore lifetime for further calculation. - lhq.removeObjCLifetime(); - rhq.removeObjCLifetime(); + if (NullKind == Expr::NPCK_ZeroLiteral) { + // In this case, check to make sure that we got here from a "NULL" + // string in the source code. + NullExpr = NullExpr->IgnoreParenImpCasts(); + SourceLocation loc = NullExpr->getExprLoc(); + if (!findMacroSpelling(loc, "NULL")) + return false; } - if (!lhq.compatiblyIncludes(rhq, S.getASTContext())) { - // Treat address-space mismatches as fatal. - if (!lhq.isAddressSpaceSupersetOf(rhq, S.getASTContext())) - return Sema::IncompatiblePointerDiscardsQualifiers; + int DiagType = (NullKind == Expr::NPCK_CXX11_nullptr); + Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands_null) + << NonPointerExpr->getType() << DiagType + << NonPointerExpr->getSourceRange(); + return true; +} - // It's okay to add or remove GC or lifetime qualifiers when converting to - // and from void*. - else if (lhq.withoutObjCGCAttr().withoutObjCLifetime().compatiblyIncludes( - rhq.withoutObjCGCAttr().withoutObjCLifetime(), - S.getASTContext()) && - (lhptee->isVoidType() || rhptee->isVoidType())) - ; // keep old +/// Return false if the condition expression is valid, true otherwise. +static bool checkCondition(Sema &S, const Expr *Cond, + SourceLocation QuestionLoc) { + QualType CondTy = Cond->getType(); - // Treat lifetime mismatches as fatal. - else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) - ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; + // OpenCL v1.1 s6.3.i says the condition cannot be a floating point type. + if (S.getLangOpts().OpenCL && CondTy->isFloatingType()) { + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_nonfloat) + << CondTy << Cond->getSourceRange(); + return true; + } - // Treat pointer-auth mismatches as fatal. - else if (!lhq.getPointerAuth().isEquivalent(rhq.getPointerAuth())) - ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; + // C99 6.5.15p2 + if (CondTy->isScalarType()) return false; - // For GCC/MS compatibility, other qualifier mismatches are treated - // as still compatible in C. - else ConvTy = Sema::CompatiblePointerDiscardsQualifiers; - } + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_scalar) + << CondTy << Cond->getSourceRange(); + return true; +} - // C99 6.5.16.1p1 (constraint 4): If one operand is a pointer to an object or - // incomplete type and the other is a pointer to a qualified or unqualified - // version of void... - if (lhptee->isVoidType()) { - if (rhptee->isIncompleteOrObjectType()) - return ConvTy; +/// Return false if the NullExpr can be promoted to PointerTy, +/// true otherwise. +static bool checkConditionalNullPointer(Sema &S, ExprResult &NullExpr, + QualType PointerTy) { + if ((!PointerTy->isAnyPointerType() && !PointerTy->isBlockPointerType()) || + !NullExpr.get()->isNullPointerConstant(S.Context, + Expr::NPC_ValueDependentIsNull)) + return true; - // As an extension, we allow cast to/from void* to function pointer. - assert(rhptee->isFunctionType()); - return Sema::FunctionVoidPointer; - } + NullExpr = S.ImpCastExprToType(NullExpr.get(), PointerTy, CK_NullToPointer); + return false; +} - if (rhptee->isVoidType()) { - // In C, void * to another pointer type is compatible, but we want to note - // that there will be an implicit conversion happening here. - if (lhptee->isIncompleteOrObjectType()) - return ConvTy == Sema::Compatible && !S.getLangOpts().CPlusPlus - ? Sema::CompatibleVoidPtrToNonVoidPtr - : ConvTy; +/// Checks compatibility between two pointers and return the resulting +/// type. +static QualType checkConditionalPointerCompatibility(Sema &S, ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc) { + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); - // As an extension, we allow cast to/from void* to function pointer. - assert(lhptee->isFunctionType()); - return Sema::FunctionVoidPointer; + if (S.Context.hasSameType(LHSTy, RHSTy)) { + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (S.Context.getLangOpts().BoundsSafety) { + auto MergeResult = S.Context.canMergeTypeBounds(LHSTy, RHSTy); + if (MergeResult != ASTContext::BSPTMK_CanMerge) { + S.Diag( + Loc, + diag::err_cond_expr_nested_bounds_safety_pointer_attribute_mismatch) + << LHSTy << RHSTy << MergeResult << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Two identical pointers types are always compatible. + return S.Context.getCommonSugaredType(LHSTy, RHSTy); } - if (!S.Diags.isIgnored( - diag::warn_typecheck_convert_incompatible_function_pointer_strict, - Loc) && - RHSType->isFunctionPointerType() && LHSType->isFunctionPointerType() && - !S.IsFunctionConversion(RHSType, LHSType, RHSType)) - return Sema::IncompatibleFunctionPointerStrict; + QualType lhptee, rhptee; - // C99 6.5.16.1p1 (constraint 3): both operands are pointers to qualified or - // unqualified versions of compatible types, ... - QualType ltrans = QualType(lhptee, 0), rtrans = QualType(rhptee, 0); - if (!S.Context.typesAreCompatible(ltrans, rtrans)) { - // Check if the pointee types are compatible ignoring the sign. - // We explicitly check for char so that we catch "char" vs - // "unsigned char" on systems where "char" is unsigned. - if (lhptee->isCharType()) - ltrans = S.Context.UnsignedCharTy; - else if (lhptee->hasSignedIntegerRepresentation()) - ltrans = S.Context.getCorrespondingUnsignedType(ltrans); + BoundsSafetyPointerAttributes CompositeFPAttr; - if (rhptee->isCharType()) - rtrans = S.Context.UnsignedCharTy; - else if (rhptee->hasSignedIntegerRepresentation()) - rtrans = S.Context.getCorrespondingUnsignedType(rtrans); + // Get the pointee types. + bool IsBlockPointer = false; + if (const BlockPointerType *LHSBTy = LHSTy->getAs()) { + lhptee = LHSBTy->getPointeeType(); + rhptee = RHSTy->castAs()->getPointeeType(); + IsBlockPointer = true; + } else { + lhptee = LHSTy->castAs()->getPointeeType(); + rhptee = RHSTy->castAs()->getPointeeType(); - if (ltrans == rtrans) { - // Types are compatible ignoring the sign. Qualifier incompatibility - // takes priority over sign incompatibility because the sign - // warning can be disabled. - if (!S.IsAssignConvertCompatible(ConvTy)) - return ConvTy; + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety: If LHS and RHS have different -fbounds-safety pointer attributes, + // take the attribute with higher precedence and convert the other into it. + // The bounds-only attributes have precedence in the following order: + // 1) __unsafe_indexable or unspecified + // 2) __bidi_indexable + // 3) __indexable + // 4) __single + // The operands are considered incompatible if an operand has unsafe pointer + // type and the other has bounds. + auto lFPAttr = LHSTy->castAs()->getPointerAttributes(); + auto rFPAttr = RHSTy->castAs()->getPointerAttributes(); + CompositeFPAttr = BoundsSafetyPointerAttributes::merge(lFPAttr, rFPAttr); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + } - return Sema::IncompatiblePointerSign; - } + // C99 6.5.15p6: If both operands are pointers to compatible types or to + // differently qualified versions of compatible types, the result type is + // a pointer to an appropriately qualified version of the composite + // type. - // If we are a multi-level pointer, it's possible that our issue is simply - // one of qualification - e.g. char ** -> const char ** is not allowed. If - // the eventual target type is the same and the pointers have the same - // level of indirection, this must be the issue. - if (isa(lhptee) && isa(rhptee)) { - do { - std::tie(lhptee, lhq) = - cast(lhptee)->getPointeeType().split().asPair(); - std::tie(rhptee, rhq) = - cast(rhptee)->getPointeeType().split().asPair(); + // Only CVR-qualifiers exist in the standard, and the differently-qualified + // clause doesn't make sense for our extensions. E.g. address space 2 should + // be incompatible with address space 3: they may live on different devices or + // anything. + Qualifiers lhQual = lhptee.getQualifiers(); + Qualifiers rhQual = rhptee.getQualifiers(); - // Inconsistent address spaces at this point is invalid, even if the - // address spaces would be compatible. - // FIXME: This doesn't catch address space mismatches for pointers of - // different nesting levels, like: - // __local int *** a; - // int ** b = a; - // It's not clear how to actually determine when such pointers are - // invalidly incompatible. - if (lhq.getAddressSpace() != rhq.getAddressSpace()) - return Sema::IncompatibleNestedPointerAddressSpaceMismatch; + LangAS ResultAddrSpace = LangAS::Default; + LangAS LAddrSpace = lhQual.getAddressSpace(); + LangAS RAddrSpace = rhQual.getAddressSpace(); - } while (isa(lhptee) && isa(rhptee)); + // OpenCL v1.1 s6.5 - Conversion between pointers to distinct address + // spaces is disallowed. + if (lhQual.isAddressSpaceSupersetOf(rhQual, S.getASTContext())) + ResultAddrSpace = LAddrSpace; + else if (rhQual.isAddressSpaceSupersetOf(lhQual, S.getASTContext())) + ResultAddrSpace = RAddrSpace; + else { + S.Diag(Loc, diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) + << LHSTy << RHSTy << 2 << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } - if (lhptee == rhptee) - return Sema::IncompatibleNestedPointerQualifiers; - } + unsigned MergedCVRQual = lhQual.getCVRQualifiers() | rhQual.getCVRQualifiers(); + auto LHSCastKind = CK_BitCast, RHSCastKind = CK_BitCast; + lhQual.removeCVRQualifiers(); + rhQual.removeCVRQualifiers(); - // General pointer incompatibility takes priority over qualifiers. - if (RHSType->isFunctionPointerType() && LHSType->isFunctionPointerType()) - return Sema::IncompatibleFunctionPointer; - return Sema::IncompatiblePointer; + if (!lhQual.getPointerAuth().isEquivalent(rhQual.getPointerAuth())) { + S.Diag(Loc, diag::err_typecheck_cond_incompatible_ptrauth) + << LHSTy << RHSTy << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); } - if (!S.getLangOpts().CPlusPlus && - S.IsFunctionConversion(ltrans, rtrans, ltrans)) - return Sema::IncompatibleFunctionPointer; - if (IsInvalidCmseNSCallConversion(S, ltrans, rtrans)) - return Sema::IncompatibleFunctionPointer; - if (S.IsInvalidSMECallConversion(rtrans, ltrans)) - return Sema::IncompatibleFunctionPointer; - return ConvTy; -} -/// checkBlockPointerTypesForAssignment - This routine determines whether two -/// block pointer types are compatible or whether a block and normal pointer -/// are compatible. It is more restrict than comparing two function pointer -// types. -static Sema::AssignConvertType -checkBlockPointerTypesForAssignment(Sema &S, QualType LHSType, - QualType RHSType) { - assert(LHSType.isCanonical() && "LHS not canonicalized!"); - assert(RHSType.isCanonical() && "RHS not canonicalized!"); + // OpenCL v2.0 specification doesn't extend compatibility of type qualifiers + // (C99 6.7.3) for address spaces. We assume that the check should behave in + // the same manner as it's defined for CVR qualifiers, so for OpenCL two + // qual types are compatible iff + // * corresponded types are compatible + // * CVR qualifiers are equal + // * address spaces are equal + // Thus for conditional operator we merge CVR and address space unqualified + // pointees and if there is a composite type we return a pointer to it with + // merged qualifiers. + LHSCastKind = + LAddrSpace == ResultAddrSpace ? CK_BitCast : CK_AddressSpaceConversion; + RHSCastKind = + RAddrSpace == ResultAddrSpace ? CK_BitCast : CK_AddressSpaceConversion; + lhQual.removeAddressSpace(); + rhQual.removeAddressSpace(); - QualType lhptee, rhptee; + lhptee = S.Context.getQualifiedType(lhptee.getUnqualifiedType(), lhQual); + rhptee = S.Context.getQualifiedType(rhptee.getUnqualifiedType(), rhQual); - // get the "pointed to" type (ignoring qualifiers at the top level) - lhptee = cast(LHSType)->getPointeeType(); - rhptee = cast(RHSType)->getPointeeType(); + QualType CompositeTy = S.Context.mergeTypes( + lhptee, rhptee, /*OfBlockPointer=*/false, /*Unqualified=*/false, + /*BlockReturnType=*/false, /*IsConditionalOperator=*/true); - // In C++, the types have to match exactly. - if (S.getLangOpts().CPlusPlus) - return Sema::IncompatibleBlockPointer; + if (CompositeTy.isNull()) { + // In this situation, we assume void* type. No especially good + // reason, but this is what gcc does, and we do have to pick + // to get a consistent AST. + QualType incompatTy; + incompatTy = S.Context.getPointerType( + S.Context.getAddrSpaceQualType(S.Context.VoidTy, ResultAddrSpace), + CompositeFPAttr); - Sema::AssignConvertType ConvTy = Sema::Compatible; + LHS = S.ImpCastExprToType(LHS.get(), incompatTy, LHSCastKind); + RHS = S.ImpCastExprToType(RHS.get(), incompatTy, RHSCastKind); - // For blocks we enforce that qualifiers are identical. - Qualifiers LQuals = lhptee.getLocalQualifiers(); - Qualifiers RQuals = rhptee.getLocalQualifiers(); - if (S.getLangOpts().OpenCL) { - LQuals.removeAddressSpace(); - RQuals.removeAddressSpace(); - } - if (LQuals != RQuals) - ConvTy = Sema::CompatiblePointerDiscardsQualifiers; + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (S.getLangOpts().BoundsSafety) { + // Although -fbounds-safety makes no type safety guarantees, + // this is a likely footgun and is troublesome when comparing + // terminator values for __terminated_by of e.g. signed and unsigned. + S.Diag(Loc, diag::err_typecheck_cond_incompatible_pointers) + << LHSTy << RHSTy << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ - // FIXME: OpenCL doesn't define the exact compile time semantics for a block - // assignment. - // The current behavior is similar to C++ lambdas. A block might be - // assigned to a variable iff its return type and parameters are compatible - // (C99 6.2.7) with the corresponding return type and parameters of the LHS of - // an assignment. Presumably it should behave in way that a function pointer - // assignment does in C, so for each parameter and return type: - // * CVR and address space of LHS should be a superset of CVR and address - // space of RHS. - // * unqualified types should be compatible. - if (S.getLangOpts().OpenCL) { - if (!S.Context.typesAreBlockPointerCompatible( - S.Context.getQualifiedType(LHSType.getUnqualifiedType(), LQuals), - S.Context.getQualifiedType(RHSType.getUnqualifiedType(), RQuals))) - return Sema::IncompatibleBlockPointer; - } else if (!S.Context.typesAreBlockPointerCompatible(LHSType, RHSType)) - return Sema::IncompatibleBlockPointer; + // FIXME: For OpenCL the warning emission and cast to void* leaves a room + // for casts between types with incompatible address space qualifiers. + // For the following code the compiler produces casts between global and + // local address spaces of the corresponded innermost pointees: + // local int *global *a; + // global int *global *b; + // a = (0 ? a : b); // see C99 6.5.16.1.p1. + S.Diag(Loc, diag::ext_typecheck_cond_incompatible_pointers) + << LHSTy << RHSTy << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); - return ConvTy; -} + return incompatTy; + } -/// checkObjCPointerTypesForAssignment - Compares two objective-c pointer types -/// for assignment compatibility. -static Sema::AssignConvertType -checkObjCPointerTypesForAssignment(Sema &S, QualType LHSType, - QualType RHSType) { - assert(LHSType.isCanonical() && "LHS was not canonicalized!"); - assert(RHSType.isCanonical() && "RHS was not canonicalized!"); + // The pointer types are compatible. + // In case of OpenCL ResultTy should have the address space qualifier + // which is a superset of address spaces of both the 2nd and the 3rd + // operands of the conditional operator. + QualType ResultTy = [&, ResultAddrSpace]() { + if (S.getLangOpts().OpenCL) { + Qualifiers CompositeQuals = CompositeTy.getQualifiers(); + CompositeQuals.setAddressSpace(ResultAddrSpace); + return S.Context + .getQualifiedType(CompositeTy.getUnqualifiedType(), CompositeQuals) + .withCVRQualifiers(MergedCVRQual); + } + return CompositeTy.withCVRQualifiers(MergedCVRQual); + }(); + if (IsBlockPointer) + ResultTy = S.Context.getBlockPointerType(ResultTy); + else + ResultTy = S.Context.getPointerType(ResultTy, CompositeFPAttr); - if (LHSType->isObjCBuiltinType()) { - // Class is not compatible with ObjC object pointers. - if (LHSType->isObjCClassType() && !RHSType->isObjCBuiltinType() && - !RHSType->isObjCQualifiedClassType()) - return Sema::IncompatiblePointer; - return Sema::Compatible; - } - if (RHSType->isObjCBuiltinType()) { - if (RHSType->isObjCClassType() && !LHSType->isObjCBuiltinType() && - !LHSType->isObjCQualifiedClassType()) - return Sema::IncompatiblePointer; - return Sema::Compatible; + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (S.Context.checkTerminatedByMismatch(LHSTy, RHSTy) != + ASTContext::BSPTMK_CanMerge) { + S.Diag(Loc, diag::err_cond_expr_nested_bounds_safety_pointer_attribute_mismatch) + << LHSTy << RHSTy << ASTContext::BSPTMK_TerminatedByMismatch + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); } - QualType lhptee = LHSType->castAs()->getPointeeType(); - QualType rhptee = RHSType->castAs()->getPointeeType(); - if (!lhptee.isAtLeastAsQualifiedAs(rhptee, S.getASTContext()) && - // make an exception for id

- !LHSType->isObjCQualifiedIdType()) - return Sema::CompatiblePointerDiscardsQualifiers; + if (auto LVTTy = LHSTy->getAs()) { + assert(RHSTy->getAs()); + ResultTy = + S.Context.getValueTerminatedType(ResultTy, LVTTy->getTerminatorExpr()); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - if (S.Context.typesAreCompatible(LHSType, RHSType)) - return Sema::Compatible; - if (LHSType->isObjCQualifiedIdType() || RHSType->isObjCQualifiedIdType()) - return Sema::IncompatibleObjCQualifiedId; - return Sema::IncompatiblePointer; + LHS = S.ImpCastExprToType(LHS.get(), ResultTy, LHSCastKind); + RHS = S.ImpCastExprToType(RHS.get(), ResultTy, RHSCastKind); + return ResultTy; } -Sema::AssignConvertType -Sema::CheckAssignmentConstraints(SourceLocation Loc, - QualType LHSType, QualType RHSType) { - // Fake up an opaque expression. We don't actually care about what - // cast operations are required, so if CheckAssignmentConstraints - // adds casts to this they'll be wasted, but fortunately that doesn't - // usually happen on valid code. - OpaqueValueExpr RHSExpr(Loc, RHSType, VK_PRValue); - ExprResult RHSPtr = &RHSExpr; - CastKind K; +/// Return the resulting type when the operands are both block pointers. +static QualType checkConditionalBlockPointerCompatibility(Sema &S, + ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc) { + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); - return CheckAssignmentConstraints(LHSType, RHSPtr, K, /*ConvertRHS=*/false); -} + if (!LHSTy->isBlockPointerType() || !RHSTy->isBlockPointerType()) { + if (LHSTy->isVoidPointerType() || RHSTy->isVoidPointerType()) { + QualType destType = S.Context.getPointerType(S.Context.VoidTy); + LHS = S.ImpCastExprToType(LHS.get(), destType, CK_BitCast); + RHS = S.ImpCastExprToType(RHS.get(), destType, CK_BitCast); + return destType; + } + S.Diag(Loc, diag::err_typecheck_cond_incompatible_operands) + << LHSTy << RHSTy << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } -/// This helper function returns true if QT is a vector type that has element -/// type ElementType. -static bool isVector(QualType QT, QualType ElementType) { - if (const VectorType *VT = QT->getAs()) - return VT->getElementType().getCanonicalType() == ElementType; - return false; + // We have 2 block pointer types. + return checkConditionalPointerCompatibility(S, LHS, RHS, Loc); } -/// CheckAssignmentConstraints (C99 6.5.16) - This routine currently -/// has code to accommodate several GCC extensions when type checking -/// pointers. Here are some objectionable examples that GCC considers warnings: -/// -/// int a, *pint; -/// short *pshort; -/// struct foo *pfoo; -/// -/// pint = pshort; // warning: assignment from incompatible pointer type -/// a = pint; // warning: assignment makes integer from pointer without a cast -/// pint = a; // warning: assignment makes pointer from integer without a cast -/// pint = pfoo; // warning: assignment from incompatible pointer type -/// -/// As a result, the code for dealing with pointers is more complex than the -/// C99 spec dictates. -/// -/// Sets 'Kind' for any result kind except Incompatible. -Sema::AssignConvertType -Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, - CastKind &Kind, bool ConvertRHS) { - QualType RHSType = RHS.get()->getType(); - QualType OrigLHSType = LHSType; +static bool haveCommonTerminator(const ASTContext &Ctx, const Expr *LHS, + const Expr *RHS) { + assert(LHS->getType()->isValueTerminatedType() || + RHS->getType()->isValueTerminatedType()); + Expr::EvalResult LHSRes; + if (!LHS->tryEvaluateTerminatorElement(LHSRes, Ctx) || !LHSRes.Val.isInt()) + return false; - // Get canonical types. We're not formatting these types, just comparing - // them. - LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType(); - RHSType = Context.getCanonicalType(RHSType).getUnqualifiedType(); + Expr::EvalResult RHSRes; + if (!RHS->tryEvaluateTerminatorElement(RHSRes, Ctx) || !RHSRes.Val.isInt()) + return false; - // Common case: no conversion required. - if (LHSType == RHSType) { - Kind = CK_NoOp; - return Compatible; - } + return llvm::APSInt::isSameValue(LHSRes.Val.getInt(), RHSRes.Val.getInt()); +} - // If the LHS has an __auto_type, there are no additional type constraints - // to be worried about. - if (const auto *AT = dyn_cast(LHSType)) { - if (AT->isGNUAutoType()) { - Kind = CK_NoOp; - return Compatible; - } - } +/// Return the resulting type when the operands are both pointers. +static QualType +checkConditionalObjectPointersCompatibility(Sema &S, ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc) { + // get the pointer types + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); - // If we have an atomic type, try a non-atomic assignment, then just add an - // atomic qualification step. - if (const AtomicType *AtomicTy = dyn_cast(LHSType)) { - Sema::AssignConvertType result = - CheckAssignmentConstraints(AtomicTy->getValueType(), RHS, Kind); - if (result != Compatible) - return result; - if (Kind != CK_NoOp && ConvertRHS) - RHS = ImpCastExprToType(RHS.get(), AtomicTy->getValueType(), Kind); - Kind = CK_NonAtomicToAtomic; - return Compatible; - } + // get the "pointed to" types + QualType lhptee = LHSTy->castAs()->getPointeeType(); + QualType rhptee = RHSTy->castAs()->getPointeeType(); - // If the left-hand side is a reference type, then we are in a - // (rare!) case where we've allowed the use of references in C, - // e.g., as a parameter type in a built-in function. In this case, - // just make sure that the type referenced is compatible with the - // right-hand side type. The caller is responsible for adjusting - // LHSType so that the resulting expression does not have reference - // type. - if (const ReferenceType *LHSTypeRef = LHSType->getAs()) { - if (Context.typesAreCompatible(LHSTypeRef->getPointeeType(), RHSType)) { - Kind = CK_LValueBitCast; - return Compatible; + /* TO_UPSTREAM(BoundsSafety) ON*/ + BoundsSafetyPointerAttributes destFPAttr; + // Do -fbounds-safety pointer conversions ahead of any other conversions; otherwise + // we risk doing a BitCast across pointer attributes, which is bad. + BoundsSafetyPointerAttributes lFPAttr = + LHSTy->castAs()->getPointerAttributes(); + BoundsSafetyPointerAttributes rFPAttr = + RHSTy->castAs()->getPointerAttributes(); + destFPAttr = BoundsSafetyPointerAttributes::merge(lFPAttr, rFPAttr); + + // If either type is value terminated, the other also needs to be, meaning + // they will both be __single pointers. A VTT and a non-single pointer is + // always a mismatch. Exception: if a constant array or string literal + // can be evaluated and is terminated with the right value. + if (LHSTy->isValueTerminatedType() != RHSTy->isValueTerminatedType()) { + destFPAttr.setSingle(); + } + + if (lFPAttr != destFPAttr) { + QualType destType = S.Context.getPointerType(lhptee, destFPAttr); + SplitQualType Split = LHSTy.getSplitUnqualifiedType(); + destType = S.Context.getQualifiedType(destType, Split.Quals); + + if (auto RHSVTTy = RHSTy->getAs()) { + // The other type is value terminated. Try to cast this to VTT also. + destType = S.Context.getValueTerminatedType(destType, + RHSVTTy->getTerminatorExpr()); + if (!haveCommonTerminator(S.Context, LHS.get(), RHS.get())) { + const auto *DstPointerType = destType->getAs(); + int SelectIsNullTerm = + DstPointerType->getTerminatorValue(S.getASTContext()).isZero() + ? /*null_terminated*/ 1 + : /*terminated_by*/ 0; + S.Diag( + Loc, + diag::err_bounds_safety_incompatible_non_terminated_by_to_terminated_by) + << LHSTy << destType << /*converting*/ 3 + << LHS.get()->getSourceRange() << SelectIsNullTerm; + S.TryFixAssigningImplicitBidiIndexableToNullTerminatedPtr(LHS.get(), + destType); + S.TryFixAssigningBidiIndexableExprToNullTerminated(LHS.get(), destType); + return QualType(); + } } - return Incompatible; + LHS = S.ImpCastExprToType(LHS.get(), destType, CK_BoundsSafetyPointerCast); + } + + if (rFPAttr != destFPAttr) { + QualType destType = S.Context.getPointerType(rhptee, destFPAttr); + SplitQualType Split = RHSTy.getSplitUnqualifiedType(); + destType = S.Context.getQualifiedType(destType, Split.Quals); + + if (auto LHSVTTy = LHSTy->getAs()) { + // The other type is value terminated. Try to cast this to VTT also. + destType = S.Context.getValueTerminatedType(destType, + LHSVTTy->getTerminatorExpr()); + if (!haveCommonTerminator(S.Context, LHS.get(), RHS.get())) { + const auto *DstPointerType = destType->getAs(); + int SelectIsNullTerm = + DstPointerType->getTerminatorValue(S.getASTContext()).isZero() + ? /*null_terminated*/ 1 + : /*terminated_by*/ 0; + S.Diag( + Loc, + diag::err_bounds_safety_incompatible_non_terminated_by_to_terminated_by) + << RHSTy << destType << /*converting*/ 3 + << RHS.get()->getSourceRange() << SelectIsNullTerm; + + S.TryFixAssigningImplicitBidiIndexableToNullTerminatedPtr(RHS.get(), destType); + S.TryFixAssigningBidiIndexableExprToNullTerminated(RHS.get(), destType); + return QualType(); + } + } + RHS = S.ImpCastExprToType(RHS.get(), destType, CK_BoundsSafetyPointerCast); } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - // Allow scalar to ExtVector assignments, and assignments of an ExtVector type - // to the same ExtVector type. - if (LHSType->isExtVectorType()) { - if (RHSType->isExtVectorType()) - return Incompatible; - if (RHSType->isArithmeticType()) { - // CK_VectorSplat does T -> vector T, so first cast to the element type. - if (ConvertRHS) - RHS = prepareVectorSplat(LHSType, RHS.get()); - Kind = CK_VectorSplat; - return Compatible; - } + // ignore qualifiers on void (C99 6.5.15p3, clause 6) + if (lhptee->isVoidType() && rhptee->isIncompleteOrObjectType()) { + // Figure out necessary qualifiers (C99 6.5.15p6) + QualType destPointee + = S.Context.getQualifiedType(lhptee, rhptee.getQualifiers()); + QualType destType = S.Context.getPointerType(destPointee, destFPAttr); + // Add qualifiers or pointer attributes if necessary. + LHS = S.ImpCastExprToType(LHS.get(), destType, CK_NoOp); + // Promote to void*. + RHS = S.ImpCastExprToType(RHS.get(), destType, CK_BitCast); + return destType; + } + if (rhptee->isVoidType() && lhptee->isIncompleteOrObjectType()) { + QualType destPointee + = S.Context.getQualifiedType(rhptee, lhptee.getQualifiers()); + QualType destType = S.Context.getPointerType(destPointee, destFPAttr); + // Add qualifiers or pointer attributes if necessary. + RHS = S.ImpCastExprToType(RHS.get(), destType, CK_NoOp); + // Promote to void*. + LHS = S.ImpCastExprToType(LHS.get(), destType, CK_BitCast); + return destType; } - // Conversions to or from vector type. - if (LHSType->isVectorType() || RHSType->isVectorType()) { - if (LHSType->isVectorType() && RHSType->isVectorType()) { - // Allow assignments of an AltiVec vector type to an equivalent GCC - // vector type and vice versa - if (Context.areCompatibleVectorTypes(LHSType, RHSType)) { - Kind = CK_BitCast; - return Compatible; - } + return checkConditionalPointerCompatibility(S, LHS, RHS, Loc); +} - // If we are allowing lax vector conversions, and LHS and RHS are both - // vectors, the total size only needs to be the same. This is a bitcast; - // no bits are changed but the result type is different. - if (isLaxVectorConversion(RHSType, LHSType)) { - // The default for lax vector conversions with Altivec vectors will - // change, so if we are converting between vector types where - // at least one is an Altivec vector, emit a warning. - if (Context.getTargetInfo().getTriple().isPPC() && - anyAltivecTypes(RHSType, LHSType) && - !Context.areCompatibleVectorTypes(RHSType, LHSType)) - Diag(RHS.get()->getExprLoc(), diag::warn_deprecated_lax_vec_conv_all) - << RHSType << LHSType; - Kind = CK_BitCast; - return IncompatibleVectors; - } - } - - // When the RHS comes from another lax conversion (e.g. binops between - // scalars and vectors) the result is canonicalized as a vector. When the - // LHS is also a vector, the lax is allowed by the condition above. Handle - // the case where LHS is a scalar. - if (LHSType->isScalarType()) { - const VectorType *VecType = RHSType->getAs(); - if (VecType && VecType->getNumElements() == 1 && - isLaxVectorConversion(RHSType, LHSType)) { - if (Context.getTargetInfo().getTriple().isPPC() && - (VecType->getVectorKind() == VectorKind::AltiVecVector || - VecType->getVectorKind() == VectorKind::AltiVecBool || - VecType->getVectorKind() == VectorKind::AltiVecPixel)) - Diag(RHS.get()->getExprLoc(), diag::warn_deprecated_lax_vec_conv_all) - << RHSType << LHSType; - ExprResult *VecExpr = &RHS; - *VecExpr = ImpCastExprToType(VecExpr->get(), LHSType, CK_BitCast); - Kind = CK_BitCast; - return Compatible; - } - } +/// Return false if the first expression is not an integer and the second +/// expression is not a pointer, true otherwise. +static bool checkPointerIntegerMismatch(Sema &S, ExprResult &Int, + Expr* PointerExpr, SourceLocation Loc, + bool IsIntFirstExpr) { + if (!PointerExpr->getType()->isPointerType() || + !Int.get()->getType()->isIntegerType()) + return false; - // Allow assignments between fixed-length and sizeless SVE vectors. - if ((LHSType->isSVESizelessBuiltinType() && RHSType->isVectorType()) || - (LHSType->isVectorType() && RHSType->isSVESizelessBuiltinType())) - if (Context.areCompatibleSveTypes(LHSType, RHSType) || - Context.areLaxCompatibleSveTypes(LHSType, RHSType)) { - Kind = CK_BitCast; - return Compatible; - } + Expr *Expr1 = IsIntFirstExpr ? Int.get() : PointerExpr; + Expr *Expr2 = IsIntFirstExpr ? PointerExpr : Int.get(); - // Allow assignments between fixed-length and sizeless RVV vectors. - if ((LHSType->isRVVSizelessBuiltinType() && RHSType->isVectorType()) || - (LHSType->isVectorType() && RHSType->isRVVSizelessBuiltinType())) { - if (Context.areCompatibleRVVTypes(LHSType, RHSType) || - Context.areLaxCompatibleRVVTypes(LHSType, RHSType)) { - Kind = CK_BitCast; - return Compatible; - } - } + S.Diag(Loc, diag::ext_typecheck_cond_pointer_integer_mismatch) + << Expr1->getType() << Expr2->getType() + << Expr1->getSourceRange() << Expr2->getSourceRange(); + Int = S.ImpCastExprToType(Int.get(), PointerExpr->getType(), + CK_IntegralToPointer); + return true; +} - return Incompatible; - } +/// Simple conversion between integer and floating point types. +/// +/// Used when handling the OpenCL conditional operator where the +/// condition is a vector while the other operands are scalar. +/// +/// OpenCL v1.1 s6.3.i and s6.11.6 together require that the scalar +/// types are either integer or floating type. Between the two +/// operands, the type with the higher rank is defined as the "result +/// type". The other operand needs to be promoted to the same type. No +/// other type promotion is allowed. We cannot use +/// UsualArithmeticConversions() for this purpose, since it always +/// promotes promotable types. +static QualType OpenCLArithmeticConversions(Sema &S, ExprResult &LHS, + ExprResult &RHS, + SourceLocation QuestionLoc) { + LHS = S.DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + RHS = S.DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); - // Diagnose attempts to convert between __ibm128, __float128 and long double - // where such conversions currently can't be handled. - if (unsupportedTypeConversion(*this, LHSType, RHSType)) - return Incompatible; + // For conversion purposes, we ignore any qualifiers. + // For example, "const float" and "float" are equivalent. + QualType LHSType = + S.Context.getCanonicalType(LHS.get()->getType()).getUnqualifiedType(); + QualType RHSType = + S.Context.getCanonicalType(RHS.get()->getType()).getUnqualifiedType(); - // Disallow assigning a _Complex to a real type in C++ mode since it simply - // discards the imaginary part. - if (getLangOpts().CPlusPlus && RHSType->getAs() && - !LHSType->getAs()) - return Incompatible; + if (!LHSType->isIntegerType() && !LHSType->isRealFloatingType()) { + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_int_float) + << LHSType << LHS.get()->getSourceRange(); + return QualType(); + } - // Arithmetic conversions. - if (LHSType->isArithmeticType() && RHSType->isArithmeticType() && - !(getLangOpts().CPlusPlus && LHSType->isEnumeralType())) { - if (ConvertRHS) - Kind = PrepareScalarCast(RHS, LHSType); - return Compatible; + if (!RHSType->isIntegerType() && !RHSType->isRealFloatingType()) { + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_int_float) + << RHSType << RHS.get()->getSourceRange(); + return QualType(); } - // Conversions to normal pointers. - if (const PointerType *LHSPointer = dyn_cast(LHSType)) { - // U* -> T* - if (isa(RHSType)) { - LangAS AddrSpaceL = LHSPointer->getPointeeType().getAddressSpace(); - LangAS AddrSpaceR = RHSType->getPointeeType().getAddressSpace(); - if (AddrSpaceL != AddrSpaceR) - Kind = CK_AddressSpaceConversion; - else if (Context.hasCvrSimilarType(RHSType, LHSType)) - Kind = CK_NoOp; - else - Kind = CK_BitCast; - return checkPointerTypesForAssignment(*this, LHSType, RHSType, - RHS.get()->getBeginLoc()); - } + // If both types are identical, no conversion is needed. + if (LHSType == RHSType) + return LHSType; - // int -> T* - if (RHSType->isIntegerType()) { - Kind = CK_IntegralToPointer; // FIXME: null? - return IntToPointer; - } + // Now handle "real" floating types (i.e. float, double, long double). + if (LHSType->isRealFloatingType() || RHSType->isRealFloatingType()) + return handleFloatConversion(S, LHS, RHS, LHSType, RHSType, + /*IsCompAssign = */ false); - // C pointers are not compatible with ObjC object pointers, - // with two exceptions: - if (isa(RHSType)) { - // - conversions to void* - if (LHSPointer->getPointeeType()->isVoidType()) { - Kind = CK_BitCast; - return Compatible; - } + // Finally, we have two differing integer types. + return handleIntegerConversion + (S, LHS, RHS, LHSType, RHSType, /*IsCompAssign = */ false); +} - // - conversions from 'Class' to the redefinition type - if (RHSType->isObjCClassType() && - Context.hasSameType(LHSType, - Context.getObjCClassRedefinitionType())) { - Kind = CK_BitCast; - return Compatible; - } +/// Convert scalar operands to a vector that matches the +/// condition in length. +/// +/// Used when handling the OpenCL conditional operator where the +/// condition is a vector while the other operands are scalar. +/// +/// We first compute the "result type" for the scalar operands +/// according to OpenCL v1.1 s6.3.i. Both operands are then converted +/// into a vector of that type where the length matches the condition +/// vector type. s6.11.6 requires that the element types of the result +/// and the condition must have the same number of bits. +static QualType +OpenCLConvertScalarsToVectors(Sema &S, ExprResult &LHS, ExprResult &RHS, + QualType CondTy, SourceLocation QuestionLoc) { + QualType ResTy = OpenCLArithmeticConversions(S, LHS, RHS, QuestionLoc); + if (ResTy.isNull()) return QualType(); - Kind = CK_BitCast; - return IncompatiblePointer; - } + const VectorType *CV = CondTy->getAs(); + assert(CV); - // U^ -> void* - if (RHSType->getAs()) { - if (LHSPointer->getPointeeType()->isVoidType()) { - LangAS AddrSpaceL = LHSPointer->getPointeeType().getAddressSpace(); - LangAS AddrSpaceR = RHSType->getAs() - ->getPointeeType() - .getAddressSpace(); - Kind = - AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion : CK_BitCast; - return Compatible; - } - } + // Determine the vector result type + unsigned NumElements = CV->getNumElements(); + QualType VectorTy = S.Context.getExtVectorType(ResTy, NumElements); - return Incompatible; + // Ensure that all types have the same number of bits + if (S.Context.getTypeSize(CV->getElementType()) + != S.Context.getTypeSize(ResTy)) { + // Since VectorTy is created internally, it does not pretty print + // with an OpenCL name. Instead, we just print a description. + std::string EleTyName = ResTy.getUnqualifiedType().getAsString(); + SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + OS << "(vector of " << NumElements << " '" << EleTyName << "' values)"; + S.Diag(QuestionLoc, diag::err_conditional_vector_element_size) + << CondTy << OS.str(); + return QualType(); } - // Conversions to block pointers. - if (isa(LHSType)) { - // U^ -> T^ - if (RHSType->isBlockPointerType()) { - LangAS AddrSpaceL = LHSType->getAs() - ->getPointeeType() - .getAddressSpace(); - LangAS AddrSpaceR = RHSType->getAs() - ->getPointeeType() - .getAddressSpace(); - Kind = AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion : CK_BitCast; - return checkBlockPointerTypesForAssignment(*this, LHSType, RHSType); - } - - // int or null -> T^ - if (RHSType->isIntegerType()) { - Kind = CK_IntegralToPointer; // FIXME: null - return IntToBlockPointer; - } + // Convert operands to the vector result type + LHS = S.ImpCastExprToType(LHS.get(), VectorTy, CK_VectorSplat); + RHS = S.ImpCastExprToType(RHS.get(), VectorTy, CK_VectorSplat); - // id -> T^ - if (getLangOpts().ObjC && RHSType->isObjCIdType()) { - Kind = CK_AnyPointerToBlockPointerCast; - return Compatible; - } + return VectorTy; +} - // void* -> T^ - if (const PointerType *RHSPT = RHSType->getAs()) - if (RHSPT->getPointeeType()->isVoidType()) { - Kind = CK_AnyPointerToBlockPointerCast; - return Compatible; - } +/// Return false if this is a valid OpenCL condition vector +static bool checkOpenCLConditionVector(Sema &S, Expr *Cond, + SourceLocation QuestionLoc) { + // OpenCL v1.1 s6.11.6 says the elements of the vector must be of + // integral type. + const VectorType *CondTy = Cond->getType()->getAs(); + assert(CondTy); + QualType EleTy = CondTy->getElementType(); + if (EleTy->isIntegerType()) return false; - return Incompatible; - } + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_nonfloat) + << Cond->getType() << Cond->getSourceRange(); + return true; +} - // Conversions to Objective-C pointers. - if (isa(LHSType)) { - // A* -> B* - if (RHSType->isObjCObjectPointerType()) { - Kind = CK_BitCast; - Sema::AssignConvertType result = - checkObjCPointerTypesForAssignment(*this, LHSType, RHSType); - if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && - result == Compatible && - !ObjC().CheckObjCARCUnavailableWeakConversion(OrigLHSType, RHSType)) - result = IncompatibleObjCWeakRef; - return result; - } +/// Return false if the vector condition type and the vector +/// result type are compatible. +/// +/// OpenCL v1.1 s6.11.6 requires that both vector types have the same +/// number of elements, and their element types have the same number +/// of bits. +static bool checkVectorResult(Sema &S, QualType CondTy, QualType VecResTy, + SourceLocation QuestionLoc) { + const VectorType *CV = CondTy->getAs(); + const VectorType *RV = VecResTy->getAs(); + assert(CV && RV); - // int or null -> A* - if (RHSType->isIntegerType()) { - Kind = CK_IntegralToPointer; // FIXME: null - return IntToPointer; - } + if (CV->getNumElements() != RV->getNumElements()) { + S.Diag(QuestionLoc, diag::err_conditional_vector_size) + << CondTy << VecResTy; + return true; + } - // In general, C pointers are not compatible with ObjC object pointers, - // with two exceptions: - if (isa(RHSType)) { - Kind = CK_CPointerToObjCPointerCast; + QualType CVE = CV->getElementType(); + QualType RVE = RV->getElementType(); - // - conversions from 'void*' - if (RHSType->isVoidPointerType()) { - return Compatible; - } + if (S.Context.getTypeSize(CVE) != S.Context.getTypeSize(RVE)) { + S.Diag(QuestionLoc, diag::err_conditional_vector_element_size) + << CondTy << VecResTy; + return true; + } - // - conversions to 'Class' from its redefinition type - if (LHSType->isObjCClassType() && - Context.hasSameType(RHSType, - Context.getObjCClassRedefinitionType())) { - return Compatible; - } + return false; +} - return IncompatiblePointer; - } +/// Return the resulting type for the conditional operator in +/// OpenCL (aka "ternary selection operator", OpenCL v1.1 +/// s6.3.i) when the condition is a vector type. +static QualType +OpenCLCheckVectorConditional(Sema &S, ExprResult &Cond, + ExprResult &LHS, ExprResult &RHS, + SourceLocation QuestionLoc) { + Cond = S.DefaultFunctionArrayLvalueConversion(Cond.get()); + if (Cond.isInvalid()) + return QualType(); + QualType CondTy = Cond.get()->getType(); - // Only under strict condition T^ is compatible with an Objective-C pointer. - if (RHSType->isBlockPointerType() && - LHSType->isBlockCompatibleObjCPointerType(Context)) { - if (ConvertRHS) - maybeExtendBlockObject(RHS); - Kind = CK_BlockPointerToObjCPointerCast; - return Compatible; - } + if (checkOpenCLConditionVector(S, Cond.get(), QuestionLoc)) + return QualType(); - return Incompatible; + // If either operand is a vector then find the vector type of the + // result as specified in OpenCL v1.1 s6.3.i. + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) { + bool IsBoolVecLang = + !S.getLangOpts().OpenCL && !S.getLangOpts().OpenCLCPlusPlus; + QualType VecResTy = + S.CheckVectorOperands(LHS, RHS, QuestionLoc, + /*isCompAssign*/ false, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ IsBoolVecLang, + /*ReportInvalid*/ true); + if (VecResTy.isNull()) + return QualType(); + // The result type must match the condition type as specified in + // OpenCL v1.1 s6.11.6. + if (checkVectorResult(S, CondTy, VecResTy, QuestionLoc)) + return QualType(); + return VecResTy; } - // Conversion to nullptr_t (C23 only) - if (getLangOpts().C23 && LHSType->isNullPtrType() && - RHS.get()->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNull)) { - // null -> nullptr_t - Kind = CK_NullToPointer; - return Compatible; + // Both operands are scalar. + return OpenCLConvertScalarsToVectors(S, LHS, RHS, CondTy, QuestionLoc); +} + +/// Return true if the Expr is block type +static bool checkBlockType(Sema &S, const Expr *E) { + if (E->getType()->isBlockPointerType()) { + S.Diag(E->getExprLoc(), diag::err_opencl_ternary_with_block); + return true; } - // Conversions from pointers that are not covered by the above. - if (isa(RHSType)) { - // T* -> _Bool - if (LHSType == Context.BoolTy) { - Kind = CK_PointerToBoolean; - return Compatible; + if (const CallExpr *CE = dyn_cast(E)) { + QualType Ty = CE->getCallee()->getType(); + if (Ty->isBlockPointerType()) { + S.Diag(E->getExprLoc(), diag::err_opencl_ternary_with_block); + return true; } + } + return false; +} - // T* -> int - if (LHSType->isIntegerType()) { - Kind = CK_PointerToIntegral; - return PointerToInt; - } +/// Note that LHS is not null here, even if this is the gnu "x ?: y" extension. +/// In that case, LHS = cond. +/// C99 6.5.15 +QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, + ExprResult &RHS, ExprValueKind &VK, + ExprObjectKind &OK, + SourceLocation QuestionLoc) { - return Incompatible; - } + ExprResult LHSResult = CheckPlaceholderExpr(LHS.get()); + if (!LHSResult.isUsable()) return QualType(); + LHS = LHSResult; - // Conversions from Objective-C pointers that are not covered by the above. - if (isa(RHSType)) { - // T* -> _Bool - if (LHSType == Context.BoolTy) { - Kind = CK_PointerToBoolean; - return Compatible; - } + ExprResult RHSResult = CheckPlaceholderExpr(RHS.get()); + if (!RHSResult.isUsable()) return QualType(); + RHS = RHSResult; - // T* -> int - if (LHSType->isIntegerType()) { - Kind = CK_PointerToIntegral; - return PointerToInt; - } + // C++ is sufficiently different to merit its own checker. + if (getLangOpts().CPlusPlus) + return CXXCheckConditionalOperands(Cond, LHS, RHS, VK, OK, QuestionLoc); - return Incompatible; - } + VK = VK_PRValue; + OK = OK_Ordinary; - // struct A -> struct B - if (isa(LHSType) && isa(RHSType)) { - if (Context.typesAreCompatible(LHSType, RHSType)) { - Kind = CK_NoOp; - return Compatible; - } + if (Context.isDependenceAllowed() && + (Cond.get()->isTypeDependent() || LHS.get()->isTypeDependent() || + RHS.get()->isTypeDependent())) { + assert(!getLangOpts().CPlusPlus); + assert((Cond.get()->containsErrors() || LHS.get()->containsErrors() || + RHS.get()->containsErrors()) && + "should only occur in error-recovery path."); + return Context.DependentTy; } - if (LHSType->isSamplerT() && RHSType->isIntegerType()) { - Kind = CK_IntToOCLSampler; - return Compatible; - } + // The OpenCL operator with a vector condition is sufficiently + // different to merit its own checker. + if ((getLangOpts().OpenCL && Cond.get()->getType()->isVectorType()) || + Cond.get()->getType()->isExtVectorType()) + return OpenCLCheckVectorConditional(*this, Cond, LHS, RHS, QuestionLoc); - return Incompatible; -} + // First, check the condition. + Cond = UsualUnaryConversions(Cond.get()); + if (Cond.isInvalid()) + return QualType(); + if (checkCondition(*this, Cond.get(), QuestionLoc)) + return QualType(); -/// Constructs a transparent union from an expression that is -/// used to initialize the transparent union. -static void ConstructTransparentUnion(Sema &S, ASTContext &C, - ExprResult &EResult, QualType UnionType, - FieldDecl *Field) { - // Build an initializer list that designates the appropriate member - // of the transparent union. - Expr *E = EResult.get(); - InitListExpr *Initializer = new (C) InitListExpr(C, SourceLocation(), - E, SourceLocation()); - Initializer->setType(UnionType); - Initializer->setInitializedFieldInUnion(Field); + // Handle vectors. + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) + return CheckVectorOperands(LHS, RHS, QuestionLoc, /*isCompAssign*/ false, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); - // Build a compound literal constructing a value of the transparent - // union type from this initializer list. - TypeSourceInfo *unionTInfo = C.getTrivialTypeSourceInfo(UnionType); - EResult = new (C) CompoundLiteralExpr(SourceLocation(), unionTInfo, UnionType, - VK_PRValue, Initializer, false); -} + QualType ResTy = + UsualArithmeticConversions(LHS, RHS, QuestionLoc, ACK_Conditional); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); -Sema::AssignConvertType -Sema::CheckTransparentUnionArgumentConstraints(QualType ArgType, - ExprResult &RHS) { - QualType RHSType = RHS.get()->getType(); + // WebAssembly tables are not allowed as conditional LHS or RHS. + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); + if (LHSTy->isWebAssemblyTableType() || RHSTy->isWebAssemblyTableType()) { + Diag(QuestionLoc, diag::err_wasm_table_conditional_expression) + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); + } - // If the ArgType is a Union type, we want to handle a potential - // transparent_union GCC extension. - const RecordType *UT = ArgType->getAsUnionType(); - if (!UT || !UT->getDecl()->hasAttr()) - return Incompatible; + // Diagnose attempts to convert between __ibm128, __float128 and long double + // where such conversions currently can't be handled. + if (unsupportedTypeConversion(*this, LHSTy, RHSTy)) { + Diag(QuestionLoc, + diag::err_typecheck_cond_incompatible_operands) << LHSTy << RHSTy + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); + } - // The field to initialize within the transparent union. - RecordDecl *UD = UT->getDecl(); - FieldDecl *InitField = nullptr; - // It's compatible if the expression matches any of the fields. - for (auto *it : UD->fields()) { - if (it->getType()->isPointerType()) { - // If the transparent union contains a pointer type, we allow: - // 1) void pointer - // 2) null pointer constant - if (RHSType->isPointerType()) - if (RHSType->castAs()->getPointeeType()->isVoidType()) { - RHS = ImpCastExprToType(RHS.get(), it->getType(), CK_BitCast); - InitField = it; - break; - } - - if (RHS.get()->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNull)) { - RHS = ImpCastExprToType(RHS.get(), it->getType(), - CK_NullToPointer); - InitField = it; - break; - } - } - - CastKind Kind; - if (CheckAssignmentConstraints(it->getType(), RHS, Kind) - == Compatible) { - RHS = ImpCastExprToType(RHS.get(), it->getType(), Kind); - InitField = it; - break; - } + // OpenCL v2.0 s6.12.5 - Blocks cannot be used as expressions of the ternary + // selection operator (?:). + if (getLangOpts().OpenCL && + ((int)checkBlockType(*this, LHS.get()) | (int)checkBlockType(*this, RHS.get()))) { + return QualType(); } - if (!InitField) - return Incompatible; - - ConstructTransparentUnion(*this, Context, RHS, ArgType, InitField); - return Compatible; -} + // If both operands have arithmetic type, do the usual arithmetic conversions + // to find a common type: C99 6.5.15p3,5. + if (LHSTy->isArithmeticType() && RHSTy->isArithmeticType()) { + // Disallow invalid arithmetic conversions, such as those between bit- + // precise integers types of different sizes, or between a bit-precise + // integer and another type. + if (ResTy.isNull() && (LHSTy->isBitIntType() || RHSTy->isBitIntType())) { + Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands) + << LHSTy << RHSTy << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } -Sema::AssignConvertType -Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, - bool Diagnose, - bool DiagnoseCFAudited, - bool ConvertRHS) { - // We need to be able to tell the caller whether we diagnosed a problem, if - // they ask us to issue diagnostics. - assert((ConvertRHS || !Diagnose) && "can't indicate whether we diagnosed"); + LHS = ImpCastExprToType(LHS.get(), ResTy, PrepareScalarCast(LHS, ResTy)); + RHS = ImpCastExprToType(RHS.get(), ResTy, PrepareScalarCast(RHS, ResTy)); - // If ConvertRHS is false, we want to leave the caller's RHS untouched. Sadly, - // we can't avoid *all* modifications at the moment, so we need some somewhere - // to put the updated value. - ExprResult LocalRHS = CallerRHS; - ExprResult &RHS = ConvertRHS ? CallerRHS : LocalRHS; + return ResTy; + } - if (const auto *LHSPtrType = LHSType->getAs()) { - if (const auto *RHSPtrType = RHS.get()->getType()->getAs()) { - if (RHSPtrType->getPointeeType()->hasAttr(attr::NoDeref) && - !LHSPtrType->getPointeeType()->hasAttr(attr::NoDeref)) { - Diag(RHS.get()->getExprLoc(), - diag::warn_noderef_to_dereferenceable_pointer) - << RHS.get()->getSourceRange(); - } - } + // If both operands are the same structure or union type, the result is that + // type. + if (const RecordType *LHSRT = LHSTy->getAs()) { // C99 6.5.15p3 + if (const RecordType *RHSRT = RHSTy->getAs()) + if (LHSRT->getDecl() == RHSRT->getDecl()) + // "If both the operands have structure or union type, the result has + // that type." This implies that CV qualifiers are dropped. + return Context.getCommonSugaredType(LHSTy.getUnqualifiedType(), + RHSTy.getUnqualifiedType()); + // FIXME: Type of conditional expression must be complete in C mode. } - if (getLangOpts().CPlusPlus) { - if (!LHSType->isRecordType() && !LHSType->isAtomicType()) { - // C++ 5.17p3: If the left operand is not of class type, the - // expression is implicitly converted (C++ 4) to the - // cv-unqualified type of the left operand. - QualType RHSType = RHS.get()->getType(); - if (Diagnose) { - RHS = PerformImplicitConversion(RHS.get(), LHSType.getUnqualifiedType(), - AssignmentAction::Assigning); - } else { - ImplicitConversionSequence ICS = - TryImplicitConversion(RHS.get(), LHSType.getUnqualifiedType(), - /*SuppressUserConversions=*/false, - AllowedExplicit::None, - /*InOverloadResolution=*/false, - /*CStyle=*/false, - /*AllowObjCWritebackConversion=*/false); - if (ICS.isFailure()) - return Incompatible; - RHS = PerformImplicitConversion(RHS.get(), LHSType.getUnqualifiedType(), - ICS, AssignmentAction::Assigning); - } - if (RHS.isInvalid()) - return Incompatible; - Sema::AssignConvertType result = Compatible; - if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && - !ObjC().CheckObjCARCUnavailableWeakConversion(LHSType, RHSType)) - result = IncompatibleObjCWeakRef; - return result; + // C99 6.5.15p5: "If both operands have void type, the result has void type." + // The following || allows only one side to be void (a GCC-ism). + if (LHSTy->isVoidType() || RHSTy->isVoidType()) { + QualType ResTy; + if (LHSTy->isVoidType() && RHSTy->isVoidType()) { + ResTy = Context.getCommonSugaredType(LHSTy, RHSTy); + } else if (RHSTy->isVoidType()) { + ResTy = RHSTy; + Diag(RHS.get()->getBeginLoc(), diag::ext_typecheck_cond_one_void) + << RHS.get()->getSourceRange(); + } else { + ResTy = LHSTy; + Diag(LHS.get()->getBeginLoc(), diag::ext_typecheck_cond_one_void) + << LHS.get()->getSourceRange(); } - - // FIXME: Currently, we fall through and treat C++ classes like C - // structures. - // FIXME: We also fall through for atomics; not sure what should - // happen there, though. - } else if (RHS.get()->getType() == Context.OverloadTy) { - // As a set of extensions to C, we support overloading on functions. These - // functions need to be resolved here. - DeclAccessPair DAP; - if (FunctionDecl *FD = ResolveAddressOfOverloadedFunction( - RHS.get(), LHSType, /*Complain=*/false, DAP)) - RHS = FixOverloadedFunctionReference(RHS.get(), DAP, FD); - else - return Incompatible; + LHS = ImpCastExprToType(LHS.get(), ResTy, CK_ToVoid); + RHS = ImpCastExprToType(RHS.get(), ResTy, CK_ToVoid); + return ResTy; } - // This check seems unnatural, however it is necessary to ensure the proper - // conversion of functions/arrays. If the conversion were done for all - // DeclExpr's (created by ActOnIdExpression), it would mess up the unary - // expressions that suppress this implicit conversion (&, sizeof). This needs - // to happen before we check for null pointer conversions because C does not - // undergo the same implicit conversions as C++ does above (by the calls to - // TryImplicitConversion() and PerformImplicitConversion()) which insert the - // lvalue to rvalue cast before checking for null pointer constraints. This - // addresses code like: nullptr_t val; int *ptr; ptr = val; - // - // Suppress this for references: C++ 8.5.3p5. - if (!LHSType->isReferenceType()) { - // FIXME: We potentially allocate here even if ConvertRHS is false. - RHS = DefaultFunctionArrayLvalueConversion(RHS.get(), Diagnose); - if (RHS.isInvalid()) - return Incompatible; - } + // C23 6.5.15p7: + // ... if both the second and third operands have nullptr_t type, the + // result also has that type. + if (LHSTy->isNullPtrType() && Context.hasSameType(LHSTy, RHSTy)) + return ResTy; - // The constraints are expressed in terms of the atomic, qualified, or - // unqualified type of the LHS. - QualType LHSTypeAfterConversion = LHSType.getAtomicUnqualifiedType(); + // C99 6.5.15p6 - "if one operand is a null pointer constant, the result has + // the type of the other operand." + if (!checkConditionalNullPointer(*this, RHS, LHSTy)) return LHSTy; + if (!checkConditionalNullPointer(*this, LHS, RHSTy)) return RHSTy; - // C99 6.5.16.1p1: the left operand is a pointer and the right is - // a null pointer constant or its type is nullptr_t;. - if ((LHSTypeAfterConversion->isPointerType() || - LHSTypeAfterConversion->isObjCObjectPointerType() || - LHSTypeAfterConversion->isBlockPointerType()) && - ((getLangOpts().C23 && RHS.get()->getType()->isNullPtrType()) || - RHS.get()->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNull))) { - if (Diagnose || ConvertRHS) { - CastKind Kind; - CXXCastPath Path; - CheckPointerConversion(RHS.get(), LHSType, Kind, Path, - /*IgnoreBaseAccess=*/false, Diagnose); - if (ConvertRHS) - RHS = ImpCastExprToType(RHS.get(), LHSType, Kind, VK_PRValue, &Path); - } - return Compatible; - } - // C23 6.5.16.1p1: the left operand has type atomic, qualified, or - // unqualified bool, and the right operand is a pointer or its type is - // nullptr_t. - if (getLangOpts().C23 && LHSType->isBooleanType() && - RHS.get()->getType()->isNullPtrType()) { - // NB: T* -> _Bool is handled in CheckAssignmentConstraints, this only - // only handles nullptr -> _Bool due to needing an extra conversion - // step. - // We model this by converting from nullptr -> void * and then let the - // conversion from void * -> _Bool happen naturally. - if (Diagnose || ConvertRHS) { - CastKind Kind; - CXXCastPath Path; - CheckPointerConversion(RHS.get(), Context.VoidPtrTy, Kind, Path, - /*IgnoreBaseAccess=*/false, Diagnose); - if (ConvertRHS) - RHS = ImpCastExprToType(RHS.get(), Context.VoidPtrTy, Kind, VK_PRValue, - &Path); - } - } + // All objective-c pointer type analysis is done here. + QualType compositeType = + ObjC().FindCompositeObjCPointerType(LHS, RHS, QuestionLoc); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); + if (!compositeType.isNull()) + return compositeType; - // OpenCL queue_t type assignment. - if (LHSType->isQueueT() && RHS.get()->isNullPointerConstant( - Context, Expr::NPC_ValueDependentIsNull)) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return Compatible; - } - CastKind Kind; - Sema::AssignConvertType result = - CheckAssignmentConstraints(LHSType, RHS, Kind, ConvertRHS); + // Handle block pointer types. + if (LHSTy->isBlockPointerType() || RHSTy->isBlockPointerType()) + return checkConditionalBlockPointerCompatibility(*this, LHS, RHS, + QuestionLoc); - // C99 6.5.16.1p2: The value of the right operand is converted to the - // type of the assignment expression. - // CheckAssignmentConstraints allows the left-hand side to be a reference, - // so that we can use references in built-in functions even in C. - // The getNonReferenceType() call makes sure that the resulting expression - // does not have reference type. - if (result != Incompatible && RHS.get()->getType() != LHSType) { - QualType Ty = LHSType.getNonLValueExprType(Context); - Expr *E = RHS.get(); + // Check constraints for C object pointers types (C99 6.5.15p3,6). + if (LHSTy->isPointerType() && RHSTy->isPointerType()) + return checkConditionalObjectPointersCompatibility(*this, LHS, RHS, + QuestionLoc); - // Check for various Objective-C errors. If we are not reporting - // diagnostics and just checking for errors, e.g., during overload - // resolution, return Incompatible to indicate the failure. - if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && - ObjC().CheckObjCConversion(SourceRange(), Ty, E, - CheckedConversionKind::Implicit, Diagnose, - DiagnoseCFAudited) != SemaObjC::ACR_okay) { - if (!Diagnose) - return Incompatible; - } - if (getLangOpts().ObjC && - (ObjC().CheckObjCBridgeRelatedConversions(E->getBeginLoc(), LHSType, - E->getType(), E, Diagnose) || - ObjC().CheckConversionToObjCLiteral(LHSType, E, Diagnose))) { - if (!Diagnose) - return Incompatible; - // Replace the expression with a corrected version and continue so we - // can find further errors. - RHS = E; - return Compatible; - } - - if (ConvertRHS) - RHS = ImpCastExprToType(E, Ty, Kind); - } - - return result; -} + // GCC compatibility: soften pointer/integer mismatch. Note that + // null pointers have been filtered out by this point. + if (checkPointerIntegerMismatch(*this, LHS, RHS.get(), QuestionLoc, + /*IsIntFirstExpr=*/true)) + return RHSTy; + if (checkPointerIntegerMismatch(*this, RHS, LHS.get(), QuestionLoc, + /*IsIntFirstExpr=*/false)) + return LHSTy; -namespace { -/// The original operand to an operator, prior to the application of the usual -/// arithmetic conversions and converting the arguments of a builtin operator -/// candidate. -struct OriginalOperand { - explicit OriginalOperand(Expr *Op) : Orig(Op), Conversion(nullptr) { - if (auto *MTE = dyn_cast(Op)) - Op = MTE->getSubExpr(); - if (auto *BTE = dyn_cast(Op)) - Op = BTE->getSubExpr(); - if (auto *ICE = dyn_cast(Op)) { - Orig = ICE->getSubExprAsWritten(); - Conversion = ICE->getConversionFunction(); - } - } + // Emit a better diagnostic if one of the expressions is a null pointer + // constant and the other is not a pointer type. In this case, the user most + // likely forgot to take the address of the other expression. + if (DiagnoseConditionalForNull(LHS.get(), RHS.get(), QuestionLoc)) + return QualType(); - QualType getType() const { return Orig->getType(); } + // Finally, if the LHS and RHS types are canonically the same type, we can + // use the common sugared type. + if (Context.hasSameType(LHSTy, RHSTy)) + return Context.getCommonSugaredType(LHSTy, RHSTy); - Expr *Orig; - NamedDecl *Conversion; -}; + // Otherwise, the operands are not compatible. + Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands) + << LHSTy << RHSTy << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); } -QualType Sema::InvalidOperands(SourceLocation Loc, ExprResult &LHS, - ExprResult &RHS) { - OriginalOperand OrigLHS(LHS.get()), OrigRHS(RHS.get()); - - Diag(Loc, diag::err_typecheck_invalid_operands) - << OrigLHS.getType() << OrigRHS.getType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - - // If a user-defined conversion was applied to either of the operands prior - // to applying the built-in operator rules, tell the user about it. - if (OrigLHS.Conversion) { - Diag(OrigLHS.Conversion->getLocation(), - diag::note_typecheck_invalid_operands_converted) - << 0 << LHS.get()->getType(); - } - if (OrigRHS.Conversion) { - Diag(OrigRHS.Conversion->getLocation(), - diag::note_typecheck_invalid_operands_converted) - << 1 << RHS.get()->getType(); +/// SuggestParentheses - Emit a note with a fixit hint that wraps +/// ParenRange in parentheses. +static void SuggestParentheses(Sema &Self, SourceLocation Loc, + const PartialDiagnostic &Note, + SourceRange ParenRange) { + SourceLocation EndLoc = Self.getLocForEndOfToken(ParenRange.getEnd()); + if (ParenRange.getBegin().isFileID() && ParenRange.getEnd().isFileID() && + EndLoc.isValid()) { + Self.Diag(Loc, Note) + << FixItHint::CreateInsertion(ParenRange.getBegin(), "(") + << FixItHint::CreateInsertion(EndLoc, ")"); + } else { + // We can't display the parentheses, so just show the bare note. + Self.Diag(Loc, Note) << ParenRange; } - - return QualType(); } -QualType Sema::InvalidLogicalVectorOperands(SourceLocation Loc, ExprResult &LHS, - ExprResult &RHS) { - QualType LHSType = LHS.get()->IgnoreImpCasts()->getType(); - QualType RHSType = RHS.get()->IgnoreImpCasts()->getType(); - - bool LHSNatVec = LHSType->isVectorType(); - bool RHSNatVec = RHSType->isVectorType(); +static bool IsArithmeticOp(BinaryOperatorKind Opc) { + return BinaryOperator::isAdditiveOp(Opc) || + BinaryOperator::isMultiplicativeOp(Opc) || + BinaryOperator::isShiftOp(Opc) || Opc == BO_And || Opc == BO_Or; + // This only checks for bitwise-or and bitwise-and, but not bitwise-xor and + // not any of the logical operators. Bitwise-xor is commonly used as a + // logical-xor because there is no logical-xor operator. The logical + // operators, including uses of xor, have a high false positive rate for + // precedence warnings. +} - if (!(LHSNatVec && RHSNatVec)) { - Expr *Vector = LHSNatVec ? LHS.get() : RHS.get(); - Expr *NonVector = !LHSNatVec ? LHS.get() : RHS.get(); - Diag(Loc, diag::err_typecheck_logical_vector_expr_gnu_cpp_restrict) - << 0 << Vector->getType() << NonVector->IgnoreImpCasts()->getType() - << Vector->getSourceRange(); - return QualType(); +/// IsArithmeticBinaryExpr - Returns true if E is an arithmetic binary +/// expression, either using a built-in or overloaded operator, +/// and sets *OpCode to the opcode and *RHSExprs to the right-hand side +/// expression. +static bool IsArithmeticBinaryExpr(const Expr *E, BinaryOperatorKind *Opcode, + const Expr **RHSExprs) { + // Don't strip parenthesis: we should not warn if E is in parenthesis. + E = E->IgnoreImpCasts(); + E = E->IgnoreConversionOperatorSingleStep(); + E = E->IgnoreImpCasts(); + if (const auto *MTE = dyn_cast(E)) { + E = MTE->getSubExpr(); + E = E->IgnoreImpCasts(); } - Diag(Loc, diag::err_typecheck_logical_vector_expr_gnu_cpp_restrict) - << 1 << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); + // Built-in binary operator. + if (const auto *OP = dyn_cast(E); + OP && IsArithmeticOp(OP->getOpcode())) { + *Opcode = OP->getOpcode(); + *RHSExprs = OP->getRHS(); + return true; + } - return QualType(); -} + // Overloaded operator. + if (const auto *Call = dyn_cast(E)) { + if (Call->getNumArgs() != 2) + return false; -/// Try to convert a value of non-vector type to a vector type by converting -/// the type to the element type of the vector and then performing a splat. -/// If the language is OpenCL, we only use conversions that promote scalar -/// rank; for C, Obj-C, and C++ we allow any real scalar conversion except -/// for float->int. -/// -/// OpenCL V2.0 6.2.6.p2: -/// An error shall occur if any scalar operand type has greater rank -/// than the type of the vector element. -/// -/// \param scalar - if non-null, actually perform the conversions -/// \return true if the operation fails (but without diagnosing the failure) -static bool tryVectorConvertAndSplat(Sema &S, ExprResult *scalar, - QualType scalarTy, - QualType vectorEltTy, - QualType vectorTy, - unsigned &DiagID) { - // The conversion to apply to the scalar before splatting it, - // if necessary. - CastKind scalarCast = CK_NoOp; + // Make sure this is really a binary operator that is safe to pass into + // BinaryOperator::getOverloadedOpcode(), e.g. it's not a subscript op. + OverloadedOperatorKind OO = Call->getOperator(); + if (OO < OO_Plus || OO > OO_Arrow || + OO == OO_PlusPlus || OO == OO_MinusMinus) + return false; - if (vectorEltTy->isBooleanType() && scalarTy->isIntegralType(S.Context)) { - scalarCast = CK_IntegralToBoolean; - } else if (vectorEltTy->isIntegralType(S.Context)) { - if (S.getLangOpts().OpenCL && (scalarTy->isRealFloatingType() || - (scalarTy->isIntegerType() && - S.Context.getIntegerTypeOrder(vectorEltTy, scalarTy) < 0))) { - DiagID = diag::err_opencl_scalar_type_rank_greater_than_vector_type; - return true; - } - if (!scalarTy->isIntegralType(S.Context)) + BinaryOperatorKind OpKind = BinaryOperator::getOverloadedOpcode(OO); + if (IsArithmeticOp(OpKind)) { + *Opcode = OpKind; + *RHSExprs = Call->getArg(1); return true; - scalarCast = CK_IntegralCast; - } else if (vectorEltTy->isRealFloatingType()) { - if (scalarTy->isRealFloatingType()) { - if (S.getLangOpts().OpenCL && - S.Context.getFloatingTypeOrder(vectorEltTy, scalarTy) < 0) { - DiagID = diag::err_opencl_scalar_type_rank_greater_than_vector_type; - return true; - } - scalarCast = CK_FloatingCast; } - else if (scalarTy->isIntegralType(S.Context)) - scalarCast = CK_IntegralToFloating; - else - return true; - } else { - return true; } - // Adjust scalar if desired. - if (scalar) { - if (scalarCast != CK_NoOp) - *scalar = S.ImpCastExprToType(scalar->get(), vectorEltTy, scalarCast); - *scalar = S.ImpCastExprToType(scalar->get(), vectorTy, CK_VectorSplat); - } return false; } -/// Convert vector E to a vector with the same number of elements but different -/// element type. -static ExprResult convertVector(Expr *E, QualType ElementType, Sema &S) { - const auto *VecTy = E->getType()->getAs(); - assert(VecTy && "Expression E must be a vector"); - QualType NewVecTy = - VecTy->isExtVectorType() - ? S.Context.getExtVectorType(ElementType, VecTy->getNumElements()) - : S.Context.getVectorType(ElementType, VecTy->getNumElements(), - VecTy->getVectorKind()); +/// ExprLooksBoolean - Returns true if E looks boolean, i.e. it has boolean type +/// or is a logical expression such as (x==y) which has int type, but is +/// commonly interpreted as boolean. +static bool ExprLooksBoolean(const Expr *E) { + E = E->IgnoreParenImpCasts(); - // Look through the implicit cast. Return the subexpression if its type is - // NewVecTy. - if (auto *ICE = dyn_cast(E)) - if (ICE->getSubExpr()->getType() == NewVecTy) - return ICE->getSubExpr(); + if (E->getType()->isBooleanType()) + return true; + if (const auto *OP = dyn_cast(E)) + return OP->isComparisonOp() || OP->isLogicalOp(); + if (const auto *OP = dyn_cast(E)) + return OP->getOpcode() == UO_LNot; + if (E->getType()->isPointerType()) + return true; + // FIXME: What about overloaded operator calls returning "unspecified boolean + // type"s (commonly pointer-to-members)? - auto Cast = ElementType->isIntegerType() ? CK_IntegralCast : CK_FloatingCast; - return S.ImpCastExprToType(E, NewVecTy, Cast); + return false; } -/// Test if a (constant) integer Int can be casted to another integer type -/// IntTy without losing precision. -static bool canConvertIntToOtherIntTy(Sema &S, ExprResult *Int, - QualType OtherIntTy) { - if (Int->get()->containsErrors()) - return false; +/// DiagnoseConditionalPrecedence - Emit a warning when a conditional operator +/// and binary operator are mixed in a way that suggests the programmer assumed +/// the conditional operator has higher precedence, for example: +/// "int x = a + someBinaryCondition ? 1 : 2". +static void DiagnoseConditionalPrecedence(Sema &Self, SourceLocation OpLoc, + Expr *Condition, const Expr *LHSExpr, + const Expr *RHSExpr) { + BinaryOperatorKind CondOpcode; + const Expr *CondRHS; - QualType IntTy = Int->get()->getType().getUnqualifiedType(); + if (!IsArithmeticBinaryExpr(Condition, &CondOpcode, &CondRHS)) + return; + if (!ExprLooksBoolean(CondRHS)) + return; - // Reject cases where the value of the Int is unknown as that would - // possibly cause truncation, but accept cases where the scalar can be - // demoted without loss of precision. - Expr::EvalResult EVResult; - bool CstInt = Int->get()->EvaluateAsInt(EVResult, S.Context); - int Order = S.Context.getIntegerTypeOrder(OtherIntTy, IntTy); - bool IntSigned = IntTy->hasSignedIntegerRepresentation(); - bool OtherIntSigned = OtherIntTy->hasSignedIntegerRepresentation(); + // The condition is an arithmetic binary expression, with a right- + // hand side that looks boolean, so warn. - if (CstInt) { - // If the scalar is constant and is of a higher order and has more active - // bits that the vector element type, reject it. - llvm::APSInt Result = EVResult.Val.getInt(); - unsigned NumBits = IntSigned - ? (Result.isNegative() ? Result.getSignificantBits() - : Result.getActiveBits()) - : Result.getActiveBits(); - if (Order < 0 && S.Context.getIntWidth(OtherIntTy) < NumBits) - return true; + unsigned DiagID = BinaryOperator::isBitwiseOp(CondOpcode) + ? diag::warn_precedence_bitwise_conditional + : diag::warn_precedence_conditional; - // If the signedness of the scalar type and the vector element type - // differs and the number of bits is greater than that of the vector - // element reject it. - return (IntSigned != OtherIntSigned && - NumBits > S.Context.getIntWidth(OtherIntTy)); - } + Self.Diag(OpLoc, DiagID) + << Condition->getSourceRange() + << BinaryOperator::getOpcodeStr(CondOpcode); - // Reject cases where the value of the scalar is not constant and it's - // order is greater than that of the vector element type. - return (Order < 0); + SuggestParentheses( + Self, OpLoc, + Self.PDiag(diag::note_precedence_silence) + << BinaryOperator::getOpcodeStr(CondOpcode), + SourceRange(Condition->getBeginLoc(), Condition->getEndLoc())); + + SuggestParentheses(Self, OpLoc, + Self.PDiag(diag::note_precedence_conditional_first), + SourceRange(CondRHS->getBeginLoc(), RHSExpr->getEndLoc())); } -/// Test if a (constant) integer Int can be casted to floating point type -/// FloatTy without losing precision. -static bool canConvertIntTyToFloatTy(Sema &S, ExprResult *Int, - QualType FloatTy) { - if (Int->get()->containsErrors()) - return false; +/// Compute the nullability of a conditional expression. +static QualType computeConditionalNullability(QualType ResTy, bool IsBin, + QualType LHSTy, QualType RHSTy, + ASTContext &Ctx) { + if (!ResTy->isAnyPointerType()) + return ResTy; - QualType IntTy = Int->get()->getType().getUnqualifiedType(); + auto GetNullability = [](QualType Ty) { + std::optional Kind = Ty->getNullability(); + if (Kind) { + // For our purposes, treat _Nullable_result as _Nullable. + if (*Kind == NullabilityKind::NullableResult) + return NullabilityKind::Nullable; + return *Kind; + } + return NullabilityKind::Unspecified; + }; - // Determine if the integer constant can be expressed as a floating point - // number of the appropriate type. - Expr::EvalResult EVResult; - bool CstInt = Int->get()->EvaluateAsInt(EVResult, S.Context); + auto LHSKind = GetNullability(LHSTy), RHSKind = GetNullability(RHSTy); + NullabilityKind MergedKind; - uint64_t Bits = 0; - if (CstInt) { - // Reject constants that would be truncated if they were converted to - // the floating point type. Test by simple to/from conversion. - // FIXME: Ideally the conversion to an APFloat and from an APFloat - // could be avoided if there was a convertFromAPInt method - // which could signal back if implicit truncation occurred. - llvm::APSInt Result = EVResult.Val.getInt(); - llvm::APFloat Float(S.Context.getFloatTypeSemantics(FloatTy)); - Float.convertFromAPInt(Result, IntTy->hasSignedIntegerRepresentation(), - llvm::APFloat::rmTowardZero); - llvm::APSInt ConvertBack(S.Context.getIntWidth(IntTy), - !IntTy->hasSignedIntegerRepresentation()); - bool Ignored = false; - Float.convertToInteger(ConvertBack, llvm::APFloat::rmNearestTiesToEven, - &Ignored); - if (Result != ConvertBack) - return true; + // Compute nullability of a binary conditional expression. + if (IsBin) { + if (LHSKind == NullabilityKind::NonNull) + MergedKind = NullabilityKind::NonNull; + else + MergedKind = RHSKind; + // Compute nullability of a normal conditional expression. } else { - // Reject types that cannot be fully encoded into the mantissa of - // the float. - Bits = S.Context.getTypeSize(IntTy); - unsigned FloatPrec = llvm::APFloat::semanticsPrecision( - S.Context.getFloatTypeSemantics(FloatTy)); - if (Bits > FloatPrec) - return true; + if (LHSKind == NullabilityKind::Nullable || + RHSKind == NullabilityKind::Nullable) + MergedKind = NullabilityKind::Nullable; + else if (LHSKind == NullabilityKind::NonNull) + MergedKind = RHSKind; + else if (RHSKind == NullabilityKind::NonNull) + MergedKind = LHSKind; + else + MergedKind = NullabilityKind::Unspecified; } - return false; -} + // Return if ResTy already has the correct nullability. + if (GetNullability(ResTy) == MergedKind) + return ResTy; -/// Attempt to convert and splat Scalar into a vector whose types matches -/// Vector following GCC conversion rules. The rule is that implicit -/// conversion can occur when Scalar can be casted to match Vector's element -/// type without causing truncation of Scalar. -static bool tryGCCVectorConvertAndSplat(Sema &S, ExprResult *Scalar, - ExprResult *Vector) { - QualType ScalarTy = Scalar->get()->getType().getUnqualifiedType(); - QualType VectorTy = Vector->get()->getType().getUnqualifiedType(); - QualType VectorEltTy; + // Strip all nullability from ResTy. + while (ResTy->getNullability()) + ResTy = ResTy.getSingleStepDesugaredType(Ctx); - if (const auto *VT = VectorTy->getAs()) { - assert(!isa(VT) && - "ExtVectorTypes should not be handled here!"); - VectorEltTy = VT->getElementType(); - } else if (VectorTy->isSveVLSBuiltinType()) { - VectorEltTy = - VectorTy->castAs()->getSveEltType(S.getASTContext()); - } else { - llvm_unreachable("Only Fixed-Length and SVE Vector types are handled here"); - } + // Create a new AttributedType with the new nullability kind. + return Ctx.getAttributedType(MergedKind, ResTy, ResTy); +} - // Reject cases where the vector element type or the scalar element type are - // not integral or floating point types. - if (!VectorEltTy->isArithmeticType() || !ScalarTy->isArithmeticType()) - return true; +ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc, + SourceLocation ColonLoc, + Expr *CondExpr, Expr *LHSExpr, + Expr *RHSExpr) { + if (!Context.isDependenceAllowed()) { + // C cannot handle TypoExpr nodes in the condition because it + // doesn't handle dependent types properly, so make sure any TypoExprs have + // been dealt with before checking the operands. + ExprResult CondResult = CorrectDelayedTyposInExpr(CondExpr); + ExprResult LHSResult = CorrectDelayedTyposInExpr(LHSExpr); + ExprResult RHSResult = CorrectDelayedTyposInExpr(RHSExpr); - // The conversion to apply to the scalar before splatting it, - // if necessary. - CastKind ScalarCast = CK_NoOp; + if (!CondResult.isUsable()) + return ExprError(); - // Accept cases where the vector elements are integers and the scalar is - // an integer. - // FIXME: Notionally if the scalar was a floating point value with a precise - // integral representation, we could cast it to an appropriate integer - // type and then perform the rest of the checks here. GCC will perform - // this conversion in some cases as determined by the input language. - // We should accept it on a language independent basis. - if (VectorEltTy->isIntegralType(S.Context) && - ScalarTy->isIntegralType(S.Context) && - S.Context.getIntegerTypeOrder(VectorEltTy, ScalarTy)) { + if (LHSExpr) { + if (!LHSResult.isUsable()) + return ExprError(); + } - if (canConvertIntToOtherIntTy(S, Scalar, VectorEltTy)) - return true; + if (!RHSResult.isUsable()) + return ExprError(); - ScalarCast = CK_IntegralCast; - } else if (VectorEltTy->isIntegralType(S.Context) && - ScalarTy->isRealFloatingType()) { - if (S.Context.getTypeSize(VectorEltTy) == S.Context.getTypeSize(ScalarTy)) - ScalarCast = CK_FloatingToIntegral; - else - return true; - } else if (VectorEltTy->isRealFloatingType()) { - if (ScalarTy->isRealFloatingType()) { + CondExpr = CondResult.get(); + LHSExpr = LHSResult.get(); + RHSExpr = RHSResult.get(); + } - // Reject cases where the scalar type is not a constant and has a higher - // Order than the vector element type. - llvm::APFloat Result(0.0); + // If this is the gnu "x ?: y" extension, analyze the types as though the LHS + // was the condition. + OpaqueValueExpr *opaqueValue = nullptr; + Expr *commonExpr = nullptr; + if (!LHSExpr) { + commonExpr = CondExpr; + // Lower out placeholder types first. This is important so that we don't + // try to capture a placeholder. This happens in few cases in C++; such + // as Objective-C++'s dictionary subscripting syntax. + if (commonExpr->hasPlaceholderType()) { + ExprResult result = CheckPlaceholderExpr(commonExpr); + if (!result.isUsable()) return ExprError(); + commonExpr = result.get(); + } + // We usually want to apply unary conversions *before* saving, except + // in the special case of a C++ l-value conditional. + if (!(getLangOpts().CPlusPlus + && !commonExpr->isTypeDependent() + && commonExpr->getValueKind() == RHSExpr->getValueKind() + && commonExpr->isGLValue() + && commonExpr->isOrdinaryOrBitFieldObject() + && RHSExpr->isOrdinaryOrBitFieldObject() + && Context.hasSameType(commonExpr->getType(), RHSExpr->getType()))) { + ExprResult commonRes = UsualUnaryConversions(commonExpr); + if (commonRes.isInvalid()) + return ExprError(); + commonExpr = commonRes.get(); + } - // Determine whether this is a constant scalar. In the event that the - // value is dependent (and thus cannot be evaluated by the constant - // evaluator), skip the evaluation. This will then diagnose once the - // expression is instantiated. - bool CstScalar = Scalar->get()->isValueDependent() || - Scalar->get()->EvaluateAsFloat(Result, S.Context); - int Order = S.Context.getFloatingTypeOrder(VectorEltTy, ScalarTy); - if (!CstScalar && Order < 0) - return true; + // If the common expression is a class or array prvalue, materialize it + // so that we can safely refer to it multiple times. + if (commonExpr->isPRValue() && (commonExpr->getType()->isRecordType() || + commonExpr->getType()->isArrayType())) { + ExprResult MatExpr = TemporaryMaterializationConversion(commonExpr); + if (MatExpr.isInvalid()) + return ExprError(); + commonExpr = MatExpr.get(); + } - // If the scalar cannot be safely casted to the vector element type, - // reject it. - if (CstScalar) { - bool Truncated = false; - Result.convert(S.Context.getFloatTypeSemantics(VectorEltTy), - llvm::APFloat::rmNearestTiesToEven, &Truncated); - if (Truncated) - return true; - } + opaqueValue = new (Context) OpaqueValueExpr(commonExpr->getExprLoc(), + commonExpr->getType(), + commonExpr->getValueKind(), + commonExpr->getObjectKind(), + commonExpr); + LHSExpr = CondExpr = opaqueValue; + } - ScalarCast = CK_FloatingCast; - } else if (ScalarTy->isIntegralType(S.Context)) { - if (canConvertIntTyToFloatTy(S, Scalar, VectorEltTy)) - return true; + QualType LHSTy = LHSExpr->getType(), RHSTy = RHSExpr->getType(); + ExprValueKind VK = VK_PRValue; + ExprObjectKind OK = OK_Ordinary; + ExprResult Cond = CondExpr, LHS = LHSExpr, RHS = RHSExpr; + QualType result = CheckConditionalOperands(Cond, LHS, RHS, + VK, OK, QuestionLoc); + if (result.isNull() || Cond.isInvalid() || LHS.isInvalid() || + RHS.isInvalid()) + return ExprError(); - ScalarCast = CK_IntegralToFloating; - } else - return true; - } else if (ScalarTy->isEnumeralType()) - return true; + DiagnoseConditionalPrecedence(*this, QuestionLoc, Cond.get(), LHS.get(), + RHS.get()); - // Adjust scalar if desired. - if (ScalarCast != CK_NoOp) - *Scalar = S.ImpCastExprToType(Scalar->get(), VectorEltTy, ScalarCast); - *Scalar = S.ImpCastExprToType(Scalar->get(), VectorTy, CK_VectorSplat); - return false; -} + CheckBoolLikeConversion(Cond.get(), QuestionLoc); -QualType Sema::CheckVectorOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, bool IsCompAssign, - bool AllowBothBool, - bool AllowBoolConversions, - bool AllowBoolOperation, - bool ReportInvalid) { - if (!IsCompAssign) { - LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - } - RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); + result = computeConditionalNullability(result, commonExpr, LHSTy, RHSTy, + Context); - // For conversion purposes, we ignore any qualifiers. - // For example, "const float" and "float" are equivalent. - QualType LHSType = LHS.get()->getType().getUnqualifiedType(); - QualType RHSType = RHS.get()->getType().getUnqualifiedType(); + if (!commonExpr) + return new (Context) + ConditionalOperator(Cond.get(), QuestionLoc, LHS.get(), ColonLoc, + RHS.get(), result, VK, OK); - const VectorType *LHSVecType = LHSType->getAs(); - const VectorType *RHSVecType = RHSType->getAs(); - assert(LHSVecType || RHSVecType); + return new (Context) BinaryConditionalOperator( + commonExpr, opaqueValue, Cond.get(), LHS.get(), RHS.get(), QuestionLoc, + ColonLoc, result, VK, OK); +} - if (getLangOpts().HLSL) - return HLSL().handleVectorBinOpConversion(LHS, RHS, LHSType, RHSType, - IsCompAssign); +bool Sema::IsInvalidSMECallConversion(QualType FromType, QualType ToType) { + unsigned FromAttributes = 0, ToAttributes = 0; + if (const auto *FromFn = + dyn_cast(Context.getCanonicalType(FromType))) + FromAttributes = + FromFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask; + if (const auto *ToFn = + dyn_cast(Context.getCanonicalType(ToType))) + ToAttributes = + ToFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask; - // Any operation with MFloat8 type is only possible with C intrinsics - if ((LHSVecType && LHSVecType->getElementType()->isMFloat8Type()) || - (RHSVecType && RHSVecType->getElementType()->isMFloat8Type())) - return InvalidOperands(Loc, LHS, RHS); + return FromAttributes != ToAttributes; +} - // AltiVec-style "vector bool op vector bool" combinations are allowed - // for some operators but not others. - if (!AllowBothBool && LHSVecType && - LHSVecType->getVectorKind() == VectorKind::AltiVecBool && RHSVecType && - RHSVecType->getVectorKind() == VectorKind::AltiVecBool) - return ReportInvalid ? InvalidOperands(Loc, LHS, RHS) : QualType(); +// Check if we have a conversion between incompatible cmse function pointer +// types, that is, a conversion between a function pointer with the +// cmse_nonsecure_call attribute and one without. +static bool IsInvalidCmseNSCallConversion(Sema &S, QualType FromType, + QualType ToType) { + if (const auto *ToFn = + dyn_cast(S.Context.getCanonicalType(ToType))) { + if (const auto *FromFn = + dyn_cast(S.Context.getCanonicalType(FromType))) { + FunctionType::ExtInfo ToEInfo = ToFn->getExtInfo(); + FunctionType::ExtInfo FromEInfo = FromFn->getExtInfo(); - // This operation may not be performed on boolean vectors. - if (!AllowBoolOperation && - (LHSType->isExtVectorBoolType() || RHSType->isExtVectorBoolType())) - return ReportInvalid ? InvalidOperands(Loc, LHS, RHS) : QualType(); + return ToEInfo.getCmseNSCall() != FromEInfo.getCmseNSCall(); + } + } + return false; +} - // If the vector types are identical, return. - if (Context.hasSameType(LHSType, RHSType)) - return Context.getCommonSugaredType(LHSType, RHSType); +/* TO_UPSTREAM(BoundsSafety) ON*/ +static bool checkBoundsSafetyFunctionPointerForAssignment(Sema &S, + QualType LHSType, + QualType RHSType) { - // If we have compatible AltiVec and GCC vector types, use the AltiVec type. - if (LHSVecType && RHSVecType && - Context.areCompatibleVectorTypes(LHSType, RHSType)) { - if (isa(LHSVecType)) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return LHSType; + auto GetFunctionBaseAndProto = [](QualType Ty, const FunctionType *&Base, + const FunctionProtoType *&Proto) { + if (!Ty->isFunctionPointerType()) { + Base = nullptr; + Proto = nullptr; + return; } + auto PTy = Ty->getAs(); + Base = PTy->getPointeeType()->getAs(); + Proto = dyn_cast(Base); + }; - if (!IsCompAssign) - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); - return RHSType; - } + const FunctionType *LBase; + const FunctionType *RBase; + const FunctionProtoType *LProto; + const FunctionProtoType *RProto; - // AllowBoolConversions says that bool and non-bool AltiVec vectors - // can be mixed, with the result being the non-bool type. The non-bool - // operand must have integer element type. - if (AllowBoolConversions && LHSVecType && RHSVecType && - LHSVecType->getNumElements() == RHSVecType->getNumElements() && - (Context.getTypeSize(LHSVecType->getElementType()) == - Context.getTypeSize(RHSVecType->getElementType()))) { - if (LHSVecType->getVectorKind() == VectorKind::AltiVecVector && - LHSVecType->getElementType()->isIntegerType() && - RHSVecType->getVectorKind() == VectorKind::AltiVecBool) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return LHSType; - } - if (!IsCompAssign && - LHSVecType->getVectorKind() == VectorKind::AltiVecBool && - RHSVecType->getVectorKind() == VectorKind::AltiVecVector && - RHSVecType->getElementType()->isIntegerType()) { - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); - return RHSType; - } - } + GetFunctionBaseAndProto(LHSType, LBase, LProto); + GetFunctionBaseAndProto(RHSType, RBase, RProto); - // Expressions containing fixed-length and sizeless SVE/RVV vectors are - // invalid since the ambiguity can affect the ABI. - auto IsSveRVVConversion = [](QualType FirstType, QualType SecondType, - unsigned &SVEorRVV) { - const VectorType *VecType = SecondType->getAs(); - SVEorRVV = 0; - if (FirstType->isSizelessBuiltinType() && VecType) { - if (VecType->getVectorKind() == VectorKind::SveFixedLengthData || - VecType->getVectorKind() == VectorKind::SveFixedLengthPredicate) - return true; - if (VecType->getVectorKind() == VectorKind::RVVFixedLengthData || - VecType->getVectorKind() == VectorKind::RVVFixedLengthMask || - VecType->getVectorKind() == VectorKind::RVVFixedLengthMask_1 || - VecType->getVectorKind() == VectorKind::RVVFixedLengthMask_2 || - VecType->getVectorKind() == VectorKind::RVVFixedLengthMask_4) { - SVEorRVV = 1; - return true; - } - } + if (!LBase || !RBase) + return true; + // Check return types + // Return types are covariant. + if (!S.getASTContext().hasCompatibleBoundsSafetyPointerLayout( + LBase->getReturnType(), RBase->getReturnType())) return false; - }; - unsigned SVEorRVV; - if (IsSveRVVConversion(LHSType, RHSType, SVEorRVV) || - IsSveRVVConversion(RHSType, LHSType, SVEorRVV)) { - Diag(Loc, diag::err_typecheck_sve_rvv_ambiguous) - << SVEorRVV << LHSType << RHSType; - return QualType(); + // Check parameter types + unsigned LNumParams = LProto ? LProto->getNumParams() : 0; + unsigned RNumParams = RProto ? RProto->getNumParams() : 0; + unsigned MinNumParams = LNumParams <= RNumParams ? LNumParams : RNumParams; + + for (unsigned i = 0; i < MinNumParams; ++i) { + // We switch left and right for the compatibility check because + // function parameters are contravariant. + // I.e., if R -> L then 'f(L)' -> 'f(R)' + if (!S.getASTContext().hasCompatibleBoundsSafetyPointerLayout( + RProto->getParamType(i), LProto->getParamType(i))) + return false; } - // Expressions containing GNU and SVE or RVV (fixed or sizeless) vectors are - // invalid since the ambiguity can affect the ABI. - auto IsSveRVVGnuConversion = [](QualType FirstType, QualType SecondType, - unsigned &SVEorRVV) { - const VectorType *FirstVecType = FirstType->getAs(); - const VectorType *SecondVecType = SecondType->getAs(); + auto CheckRemaningParams = [MinNumParams](const FunctionProtoType *Proto) { + if (!Proto || Proto->getNumParams() == MinNumParams) + return true; - SVEorRVV = 0; - if (FirstVecType && SecondVecType) { - if (FirstVecType->getVectorKind() == VectorKind::Generic) { - if (SecondVecType->getVectorKind() == VectorKind::SveFixedLengthData || - SecondVecType->getVectorKind() == - VectorKind::SveFixedLengthPredicate) - return true; - if (SecondVecType->getVectorKind() == VectorKind::RVVFixedLengthData || - SecondVecType->getVectorKind() == VectorKind::RVVFixedLengthMask || - SecondVecType->getVectorKind() == - VectorKind::RVVFixedLengthMask_1 || - SecondVecType->getVectorKind() == - VectorKind::RVVFixedLengthMask_2 || - SecondVecType->getVectorKind() == - VectorKind::RVVFixedLengthMask_4) { - SVEorRVV = 1; + auto HasBoundsSafetyAttributeRecursive = [](QualType Ty) { + auto PT = Ty->getAs(); + while (PT) { + if (!PT->hasRawPointerLayout()) return true; - } + PT = PT->getPointeeType()->getAs(); } return false; - } - - if (SecondVecType && - SecondVecType->getVectorKind() == VectorKind::Generic) { - if (FirstType->isSVESizelessBuiltinType()) - return true; - if (FirstType->isRVVSizelessBuiltinType()) { - SVEorRVV = 1; - return true; - } - } + }; - return false; + for (unsigned i = MinNumParams; i < Proto->getNumParams(); ++i) + if (HasBoundsSafetyAttributeRecursive(Proto->getParamType(i))) + return false; + return true; }; - if (IsSveRVVGnuConversion(LHSType, RHSType, SVEorRVV) || - IsSveRVVGnuConversion(RHSType, LHSType, SVEorRVV)) { - Diag(Loc, diag::err_typecheck_sve_rvv_gnu_ambiguous) - << SVEorRVV << LHSType << RHSType; - return QualType(); + return CheckRemaningParams(LProto) || CheckRemaningParams(RProto); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + +// checkPointerTypesForAssignment - This is a very tricky routine (despite +// being closely modeled after the C99 spec:-). The odd characteristic of this +// routine is it effectively iqnores the qualifiers on the top level pointee. +// This circumvents the usual type rules specified in 6.2.7p1 & 6.7.5.[1-3]. +// FIXME: add a couple examples in this comment. +static Sema::AssignConvertType +checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType, + SourceLocation Loc) { + assert(LHSType.isCanonical() && "LHS not canonicalized!"); + assert(RHSType.isCanonical() && "RHS not canonicalized!"); + + // get the "pointed to" type (ignoring qualifiers at the top level) + const Type *lhptee, *rhptee; + Qualifiers lhq, rhq; + std::tie(lhptee, lhq) = + cast(LHSType)->getPointeeType().split().asPair(); + std::tie(rhptee, rhq) = + cast(RHSType)->getPointeeType().split().asPair(); + + Sema::AssignConvertType ConvTy = Sema::Compatible; + + // C99 6.5.16.1p1: This following citation is common to constraints + // 3 & 4 (below). ...and the type *pointed to* by the left has all the + // qualifiers of the type *pointed to* by the right; + + // As a special case, 'non-__weak A *' -> 'non-__weak const *' is okay. + if (lhq.getObjCLifetime() != rhq.getObjCLifetime() && + lhq.compatiblyIncludesObjCLifetime(rhq)) { + // Ignore lifetime for further calculation. + lhq.removeObjCLifetime(); + rhq.removeObjCLifetime(); } - // If there's a vector type and a scalar, try to convert the scalar to - // the vector element type and splat. - unsigned DiagID = diag::err_typecheck_vector_not_convertable; - if (!RHSVecType) { - if (isa(LHSVecType)) { - if (!tryVectorConvertAndSplat(*this, &RHS, RHSType, - LHSVecType->getElementType(), LHSType, - DiagID)) - return LHSType; - } else { - if (!tryGCCVectorConvertAndSplat(*this, &RHS, &LHS)) - return LHSType; - } - } - if (!LHSVecType) { - if (isa(RHSVecType)) { - if (!tryVectorConvertAndSplat(*this, (IsCompAssign ? nullptr : &LHS), - LHSType, RHSVecType->getElementType(), - RHSType, DiagID)) - return RHSType; - } else { - if (LHS.get()->isLValue() || - !tryGCCVectorConvertAndSplat(*this, &LHS, &RHS)) - return RHSType; - } - } + if (!lhq.compatiblyIncludes(rhq, S.getASTContext())) { + // Treat address-space mismatches as fatal. + if (!lhq.isAddressSpaceSupersetOf(rhq, S.getASTContext())) + return Sema::IncompatiblePointerDiscardsQualifiers; - // FIXME: The code below also handles conversion between vectors and - // non-scalars, we should break this down into fine grained specific checks - // and emit proper diagnostics. - QualType VecType = LHSVecType ? LHSType : RHSType; - const VectorType *VT = LHSVecType ? LHSVecType : RHSVecType; - QualType OtherType = LHSVecType ? RHSType : LHSType; - ExprResult *OtherExpr = LHSVecType ? &RHS : &LHS; - if (isLaxVectorConversion(OtherType, VecType)) { - if (Context.getTargetInfo().getTriple().isPPC() && - anyAltivecTypes(RHSType, LHSType) && - !Context.areCompatibleVectorTypes(RHSType, LHSType)) - Diag(Loc, diag::warn_deprecated_lax_vec_conv_all) << RHSType << LHSType; - // If we're allowing lax vector conversions, only the total (data) size - // needs to be the same. For non compound assignment, if one of the types is - // scalar, the result is always the vector type. - if (!IsCompAssign) { - *OtherExpr = ImpCastExprToType(OtherExpr->get(), VecType, CK_BitCast); - return VecType; - // In a compound assignment, lhs += rhs, 'lhs' is a lvalue src, forbidding - // any implicit cast. Here, the 'rhs' should be implicit casted to 'lhs' - // type. Note that this is already done by non-compound assignments in - // CheckAssignmentConstraints. If it's a scalar type, only bitcast for - // <1 x T> -> T. The result is also a vector type. - } else if (OtherType->isExtVectorType() || OtherType->isVectorType() || - (OtherType->isScalarType() && VT->getNumElements() == 1)) { - ExprResult *RHSExpr = &RHS; - *RHSExpr = ImpCastExprToType(RHSExpr->get(), LHSType, CK_BitCast); - return VecType; - } - } + // It's okay to add or remove GC or lifetime qualifiers when converting to + // and from void*. + else if (lhq.withoutObjCGCAttr().withoutObjCLifetime().compatiblyIncludes( + rhq.withoutObjCGCAttr().withoutObjCLifetime(), + S.getASTContext()) && + (lhptee->isVoidType() || rhptee->isVoidType())) + ; // keep old - // Okay, the expression is invalid. + // Treat lifetime mismatches as fatal. + else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) + ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; - // If there's a non-vector, non-real operand, diagnose that. - if ((!RHSVecType && !RHSType->isRealType()) || - (!LHSVecType && !LHSType->isRealType())) { - Diag(Loc, diag::err_typecheck_vector_not_convertable_non_scalar) - << LHSType << RHSType - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return QualType(); - } + // Treat pointer-auth mismatches as fatal. + else if (!lhq.getPointerAuth().isEquivalent(rhq.getPointerAuth())) + ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; - // OpenCL V1.1 6.2.6.p1: - // If the operands are of more than one vector type, then an error shall - // occur. Implicit conversions between vector types are not permitted, per - // section 6.2.1. - if (getLangOpts().OpenCL && - RHSVecType && isa(RHSVecType) && - LHSVecType && isa(LHSVecType)) { - Diag(Loc, diag::err_opencl_implicit_vector_conversion) << LHSType - << RHSType; - return QualType(); + // For GCC/MS compatibility, other qualifier mismatches are treated + // as still compatible in C. + else ConvTy = Sema::CompatiblePointerDiscardsQualifiers; } + // C99 6.5.16.1p1 (constraint 4): If one operand is a pointer to an object or + // incomplete type and the other is a pointer to a qualified or unqualified + // version of void... + if (lhptee->isVoidType()) { + if (rhptee->isIncompleteOrObjectType()) + return ConvTy; - // If there is a vector type that is not a ExtVector and a scalar, we reach - // this point if scalar could not be converted to the vector's element type - // without truncation. - if ((RHSVecType && !isa(RHSVecType)) || - (LHSVecType && !isa(LHSVecType))) { - QualType Scalar = LHSVecType ? RHSType : LHSType; - QualType Vector = LHSVecType ? LHSType : RHSType; - unsigned ScalarOrVector = LHSVecType && RHSVecType ? 1 : 0; - Diag(Loc, - diag::err_typecheck_vector_not_convertable_implict_truncation) - << ScalarOrVector << Scalar << Vector; - - return QualType(); + // As an extension, we allow cast to/from void* to function pointer. + assert(rhptee->isFunctionType()); + return Sema::FunctionVoidPointer; } - // Otherwise, use the generic diagnostic. - Diag(Loc, DiagID) - << LHSType << RHSType - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return QualType(); -} + if (rhptee->isVoidType()) { + // In C, void * to another pointer type is compatible, but we want to note + // that there will be an implicit conversion happening here. + if (lhptee->isIncompleteOrObjectType()) + return ConvTy == Sema::Compatible && !S.getLangOpts().CPlusPlus + ? Sema::CompatibleVoidPtrToNonVoidPtr + : ConvTy; -QualType Sema::CheckSizelessVectorOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - bool IsCompAssign, - ArithConvKind OperationKind) { - if (!IsCompAssign) { - LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); - if (LHS.isInvalid()) - return QualType(); + // As an extension, we allow cast to/from void* to function pointer. + assert(lhptee->isFunctionType()); + return Sema::FunctionVoidPointer; } - RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); - QualType LHSType = LHS.get()->getType().getUnqualifiedType(); - QualType RHSType = RHS.get()->getType().getUnqualifiedType(); + if (!S.Diags.isIgnored( + diag::warn_typecheck_convert_incompatible_function_pointer_strict, + Loc) && + RHSType->isFunctionPointerType() && LHSType->isFunctionPointerType() && + !S.IsFunctionConversion(RHSType, LHSType, RHSType)) + return Sema::IncompatibleFunctionPointerStrict; - const BuiltinType *LHSBuiltinTy = LHSType->getAs(); - const BuiltinType *RHSBuiltinTy = RHSType->getAs(); + // C99 6.5.16.1p1 (constraint 3): both operands are pointers to qualified or + // unqualified versions of compatible types, ... + QualType ltrans = QualType(lhptee, 0), rtrans = QualType(rhptee, 0); + if (!S.Context.typesAreCompatible(ltrans, rtrans, + // TO_UPSTREAM(BoundsSafety) + /*CompareUnqualified=*/false)) { + // Check if the pointee types are compatible ignoring the sign. + // We explicitly check for char so that we catch "char" vs + // "unsigned char" on systems where "char" is unsigned. + if (lhptee->isCharType()) + ltrans = S.Context.UnsignedCharTy; + else if (lhptee->hasSignedIntegerRepresentation()) + ltrans = S.Context.getCorrespondingUnsignedType(ltrans); - unsigned DiagID = diag::err_typecheck_invalid_operands; - if ((OperationKind == ACK_Arithmetic) && - ((LHSBuiltinTy && LHSBuiltinTy->isSVEBool()) || - (RHSBuiltinTy && RHSBuiltinTy->isSVEBool()))) { - Diag(Loc, DiagID) << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); - } + if (rhptee->isCharType()) + rtrans = S.Context.UnsignedCharTy; + else if (rhptee->hasSignedIntegerRepresentation()) + rtrans = S.Context.getCorrespondingUnsignedType(rtrans); - if (Context.hasSameType(LHSType, RHSType)) - return LHSType; + if (ltrans == rtrans) { + // Types are compatible ignoring the sign. Qualifier incompatibility + // takes priority over sign incompatibility because the sign + // warning can be disabled. + if (!S.IsAssignConvertCompatible(ConvTy)) + return ConvTy; - if (LHSType->isSveVLSBuiltinType() && !RHSType->isSveVLSBuiltinType()) { - if (!tryGCCVectorConvertAndSplat(*this, &RHS, &LHS)) - return LHSType; - } - if (RHSType->isSveVLSBuiltinType() && !LHSType->isSveVLSBuiltinType()) { - if (LHS.get()->isLValue() || - !tryGCCVectorConvertAndSplat(*this, &LHS, &RHS)) - return RHSType; - } + return Sema::IncompatiblePointerSign; + } - if ((!LHSType->isSveVLSBuiltinType() && !LHSType->isRealType()) || - (!RHSType->isSveVLSBuiltinType() && !RHSType->isRealType())) { - Diag(Loc, diag::err_typecheck_vector_not_convertable_non_scalar) - << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); - } + /* TO_UPSTREAM(BoundsSafety) ON*/ + const Type *lht = lhptee; + const Type *rht = rhptee; + for (;;) { + const auto *lhpt = dyn_cast(lht->getBaseElementTypeUnsafe()); + const auto *rhpt = dyn_cast(rht->getBaseElementTypeUnsafe()); + if (!lhpt || !rhpt) + break; + if (!BoundsSafetyPointerAttributes::areCompatible( + lhpt->getPointerAttributes(), rhpt->getPointerAttributes())) { + return Sema::IncompatibleNestedBoundsSafetyPointerAttributes; + } + lht = lhpt->getPointeeType().getTypePtr(); + rht = rhpt->getPointeeType().getTypePtr(); + } - if (LHSType->isSveVLSBuiltinType() && RHSType->isSveVLSBuiltinType() && - Context.getBuiltinVectorTypeInfo(LHSBuiltinTy).EC != - Context.getBuiltinVectorTypeInfo(RHSBuiltinTy).EC) { - Diag(Loc, diag::err_typecheck_vector_lengths_not_equal) - << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); - } + auto hasNestedBoundsSafetyAttribute = [](const Type *ptee) -> bool { + while (const auto *pt = + dyn_cast(ptee->getBaseElementTypeUnsafe())) { + // FIXME: Should not trigger an error for auto-bound type yet this will + // be relevant once we have a tree transform to fix auto bound types. + // rdar://71269324 + if (!pt->hasRawPointerLayout()) + return true; + ptee = pt->getPointeeType().getTypePtr(); + } + return false; + }; + if (S.getLangOpts().BoundsSafety && (hasNestedBoundsSafetyAttribute(lht) || + hasNestedBoundsSafetyAttribute(rht))) { + return Sema::IncompatibleNestedBoundsSafetyPointerAttributes; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - if (LHSType->isSveVLSBuiltinType() || RHSType->isSveVLSBuiltinType()) { - QualType Scalar = LHSType->isSveVLSBuiltinType() ? RHSType : LHSType; - QualType Vector = LHSType->isSveVLSBuiltinType() ? LHSType : RHSType; - bool ScalarOrVector = - LHSType->isSveVLSBuiltinType() && RHSType->isSveVLSBuiltinType(); + // If we are a multi-level pointer, it's possible that our issue is simply + // one of qualification - e.g. char ** -> const char ** is not allowed. If + // the eventual target type is the same and the pointers have the same + // level of indirection, this must be the issue. + if (isa(lhptee) && isa(rhptee)) { + do { + std::tie(lhptee, lhq) = + cast(lhptee)->getPointeeType().split().asPair(); + std::tie(rhptee, rhq) = + cast(rhptee)->getPointeeType().split().asPair(); - Diag(Loc, diag::err_typecheck_vector_not_convertable_implict_truncation) - << ScalarOrVector << Scalar << Vector; + // Inconsistent address spaces at this point is invalid, even if the + // address spaces would be compatible. + // FIXME: This doesn't catch address space mismatches for pointers of + // different nesting levels, like: + // __local int *** a; + // int ** b = a; + // It's not clear how to actually determine when such pointers are + // invalidly incompatible. + if (lhq.getAddressSpace() != rhq.getAddressSpace()) + return Sema::IncompatibleNestedPointerAddressSpaceMismatch; - return QualType(); - } + } while (isa(lhptee) && isa(rhptee)); - Diag(Loc, DiagID) << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); -} + if (lhptee == rhptee) + return Sema::IncompatibleNestedPointerQualifiers; + } -// checkArithmeticNull - Detect when a NULL constant is used improperly in an -// expression. These are mainly cases where the null pointer is used as an -// integer instead of a pointer. -static void checkArithmeticNull(Sema &S, ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, bool IsCompare) { - // The canonical way to check for a GNU null is with isNullPointerConstant, - // but we use a bit of a hack here for speed; this is a relatively - // hot path, and isNullPointerConstant is slow. - bool LHSNull = isa(LHS.get()->IgnoreParenImpCasts()); - bool RHSNull = isa(RHS.get()->IgnoreParenImpCasts()); + // General pointer incompatibility takes priority over qualifiers. + if (RHSType->isFunctionPointerType() && LHSType->isFunctionPointerType()) { + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (S.getLangOpts().BoundsSafety && + !checkBoundsSafetyFunctionPointerForAssignment(S, LHSType, RHSType)) + return Sema::IncompatibleBoundsSafetyFunctionPointer; + /*TO_UPSTREAM(BoundsSafety) OFF*/ + return Sema::IncompatibleFunctionPointer; + } + return Sema::IncompatiblePointer; + } + if (!S.getLangOpts().CPlusPlus && + S.IsFunctionConversion(ltrans, rtrans, ltrans)) + return Sema::IncompatibleFunctionPointer; + if (IsInvalidCmseNSCallConversion(S, ltrans, rtrans)) + return Sema::IncompatibleFunctionPointer; + if (S.IsInvalidSMECallConversion(rtrans, ltrans)) + return Sema::IncompatibleFunctionPointer; + return ConvTy; +} - QualType NonNullType = LHSNull ? RHS.get()->getType() : LHS.get()->getType(); +/// checkBlockPointerTypesForAssignment - This routine determines whether two +/// block pointer types are compatible or whether a block and normal pointer +/// are compatible. It is more restrict than comparing two function pointer +// types. +static Sema::AssignConvertType +checkBlockPointerTypesForAssignment(Sema &S, QualType LHSType, + QualType RHSType) { + assert(LHSType.isCanonical() && "LHS not canonicalized!"); + assert(RHSType.isCanonical() && "RHS not canonicalized!"); - // Avoid analyzing cases where the result will either be invalid (and - // diagnosed as such) or entirely valid and not something to warn about. - if ((!LHSNull && !RHSNull) || NonNullType->isBlockPointerType() || - NonNullType->isMemberPointerType() || NonNullType->isFunctionType()) - return; + QualType lhptee, rhptee; - // Comparison operations would not make sense with a null pointer no matter - // what the other expression is. - if (!IsCompare) { - S.Diag(Loc, diag::warn_null_in_arithmetic_operation) - << (LHSNull ? LHS.get()->getSourceRange() : SourceRange()) - << (RHSNull ? RHS.get()->getSourceRange() : SourceRange()); - return; + // get the "pointed to" type (ignoring qualifiers at the top level) + lhptee = cast(LHSType)->getPointeeType(); + rhptee = cast(RHSType)->getPointeeType(); + + // In C++, the types have to match exactly. + if (S.getLangOpts().CPlusPlus) + return Sema::IncompatibleBlockPointer; + + Sema::AssignConvertType ConvTy = Sema::Compatible; + + // For blocks we enforce that qualifiers are identical. + Qualifiers LQuals = lhptee.getLocalQualifiers(); + Qualifiers RQuals = rhptee.getLocalQualifiers(); + if (S.getLangOpts().OpenCL) { + LQuals.removeAddressSpace(); + RQuals.removeAddressSpace(); } + if (LQuals != RQuals) + ConvTy = Sema::CompatiblePointerDiscardsQualifiers; - // The rest of the operations only make sense with a null pointer - // if the other expression is a pointer. - if (LHSNull == RHSNull || NonNullType->isAnyPointerType() || - NonNullType->canDecayToPointerType()) - return; + // FIXME: OpenCL doesn't define the exact compile time semantics for a block + // assignment. + // The current behavior is similar to C++ lambdas. A block might be + // assigned to a variable iff its return type and parameters are compatible + // (C99 6.2.7) with the corresponding return type and parameters of the LHS of + // an assignment. Presumably it should behave in way that a function pointer + // assignment does in C, so for each parameter and return type: + // * CVR and address space of LHS should be a superset of CVR and address + // space of RHS. + // * unqualified types should be compatible. + if (S.getLangOpts().OpenCL) { + if (!S.Context.typesAreBlockPointerCompatible( + S.Context.getQualifiedType(LHSType.getUnqualifiedType(), LQuals), + S.Context.getQualifiedType(RHSType.getUnqualifiedType(), RQuals))) + return Sema::IncompatibleBlockPointer; + } else if (!S.Context.typesAreBlockPointerCompatible(LHSType, RHSType)) + return Sema::IncompatibleBlockPointer; - S.Diag(Loc, diag::warn_null_in_comparison_operation) - << LHSNull /* LHS is NULL */ << NonNullType - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return ConvTy; } -static void DetectPrecisionLossInComplexDivision(Sema &S, QualType DivisorTy, - SourceLocation OpLoc) { - // If the divisor is real, then this is real/real or complex/real division. - // Either way there can be no precision loss. - auto *CT = DivisorTy->getAs(); - if (!CT) - return; +/// checkObjCPointerTypesForAssignment - Compares two objective-c pointer types +/// for assignment compatibility. +static Sema::AssignConvertType +checkObjCPointerTypesForAssignment(Sema &S, QualType LHSType, + QualType RHSType) { + assert(LHSType.isCanonical() && "LHS was not canonicalized!"); + assert(RHSType.isCanonical() && "RHS was not canonicalized!"); - QualType ElementType = CT->getElementType(); - bool IsComplexRangePromoted = S.getLangOpts().getComplexRange() == - LangOptions::ComplexRangeKind::CX_Promoted; - if (!ElementType->isFloatingType() || !IsComplexRangePromoted) - return; + if (LHSType->isObjCBuiltinType()) { + // Class is not compatible with ObjC object pointers. + if (LHSType->isObjCClassType() && !RHSType->isObjCBuiltinType() && + !RHSType->isObjCQualifiedClassType()) + return Sema::IncompatiblePointer; + return Sema::Compatible; + } + if (RHSType->isObjCBuiltinType()) { + if (RHSType->isObjCClassType() && !LHSType->isObjCBuiltinType() && + !LHSType->isObjCQualifiedClassType()) + return Sema::IncompatiblePointer; + return Sema::Compatible; + } + QualType lhptee = LHSType->castAs()->getPointeeType(); + QualType rhptee = RHSType->castAs()->getPointeeType(); - ASTContext &Ctx = S.getASTContext(); - QualType HigherElementType = Ctx.GetHigherPrecisionFPType(ElementType); - const llvm::fltSemantics &ElementTypeSemantics = - Ctx.getFloatTypeSemantics(ElementType); - const llvm::fltSemantics &HigherElementTypeSemantics = - Ctx.getFloatTypeSemantics(HigherElementType); + if (!lhptee.isAtLeastAsQualifiedAs(rhptee, S.getASTContext()) && + // make an exception for id

+ !LHSType->isObjCQualifiedIdType()) + return Sema::CompatiblePointerDiscardsQualifiers; - if ((llvm::APFloat::semanticsMaxExponent(ElementTypeSemantics) * 2 + 1 > - llvm::APFloat::semanticsMaxExponent(HigherElementTypeSemantics)) || - (HigherElementType == Ctx.LongDoubleTy && - !Ctx.getTargetInfo().hasLongDoubleType())) { - // Retain the location of the first use of higher precision type. - if (!S.LocationOfExcessPrecisionNotSatisfied.isValid()) - S.LocationOfExcessPrecisionNotSatisfied = OpLoc; - for (auto &[Type, Num] : S.ExcessPrecisionNotSatisfied) { - if (Type == HigherElementType) { - Num++; - return; - } - } - S.ExcessPrecisionNotSatisfied.push_back(std::make_pair( - HigherElementType, S.ExcessPrecisionNotSatisfied.size())); - } + if (S.Context.typesAreCompatible(LHSType, RHSType)) + return Sema::Compatible; + if (LHSType->isObjCQualifiedIdType() || RHSType->isObjCQualifiedIdType()) + return Sema::IncompatibleObjCQualifiedId; + return Sema::IncompatiblePointer; } -static void DiagnoseDivisionSizeofPointerOrArray(Sema &S, Expr *LHS, Expr *RHS, - SourceLocation Loc) { - const auto *LUE = dyn_cast(LHS); - const auto *RUE = dyn_cast(RHS); - if (!LUE || !RUE) - return; - if (LUE->getKind() != UETT_SizeOf || LUE->isArgumentType() || - RUE->getKind() != UETT_SizeOf) - return; +Sema::AssignConvertType +Sema::CheckAssignmentConstraints(SourceLocation Loc, + QualType LHSType, QualType RHSType) { + // Fake up an opaque expression. We don't actually care about what + // cast operations are required, so if CheckAssignmentConstraints + // adds casts to this they'll be wasted, but fortunately that doesn't + // usually happen on valid code. + OpaqueValueExpr RHSExpr(Loc, RHSType, VK_PRValue); + ExprResult RHSPtr = &RHSExpr; + CastKind K; - const Expr *LHSArg = LUE->getArgumentExpr()->IgnoreParens(); - QualType LHSTy = LHSArg->getType(); - QualType RHSTy; + return CheckAssignmentConstraints(LHSType, RHSPtr, K, /*ConvertRHS=*/false); +} - if (RUE->isArgumentType()) - RHSTy = RUE->getArgumentType().getNonReferenceType(); - else - RHSTy = RUE->getArgumentExpr()->IgnoreParens()->getType(); +/// This helper function returns true if QT is a vector type that has element +/// type ElementType. +static bool isVector(QualType QT, QualType ElementType) { + if (const VectorType *VT = QT->getAs()) + return VT->getElementType().getCanonicalType() == ElementType; + return false; +} - if (LHSTy->isPointerType() && !RHSTy->isPointerType()) { - if (!S.Context.hasSameUnqualifiedType(LHSTy->getPointeeType(), RHSTy)) - return; +/// CheckAssignmentConstraints (C99 6.5.16) - This routine currently +/// has code to accommodate several GCC extensions when type checking +/// pointers. Here are some objectionable examples that GCC considers warnings: +/// +/// int a, *pint; +/// short *pshort; +/// struct foo *pfoo; +/// +/// pint = pshort; // warning: assignment from incompatible pointer type +/// a = pint; // warning: assignment makes integer from pointer without a cast +/// pint = a; // warning: assignment makes pointer from integer without a cast +/// pint = pfoo; // warning: assignment from incompatible pointer type +/// +/// As a result, the code for dealing with pointers is more complex than the +/// C99 spec dictates. +/// +/// Sets 'Kind' for any result kind except Incompatible. +Sema::AssignConvertType +Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, + CastKind &Kind, bool ConvertRHS) { + QualType RHSType = RHS.get()->getType(); + QualType OrigLHSType = LHSType; - S.Diag(Loc, diag::warn_division_sizeof_ptr) << LHS << LHS->getSourceRange(); - if (const auto *DRE = dyn_cast(LHSArg)) { - if (const ValueDecl *LHSArgDecl = DRE->getDecl()) - S.Diag(LHSArgDecl->getLocation(), diag::note_pointer_declared_here) - << LHSArgDecl; - } - } else if (const auto *ArrayTy = S.Context.getAsArrayType(LHSTy)) { - QualType ArrayElemTy = ArrayTy->getElementType(); - if (ArrayElemTy != S.Context.getBaseElementType(ArrayTy) || - ArrayElemTy->isDependentType() || RHSTy->isDependentType() || - RHSTy->isReferenceType() || ArrayElemTy->isCharType() || - S.Context.getTypeSize(ArrayElemTy) == S.Context.getTypeSize(RHSTy)) - return; - S.Diag(Loc, diag::warn_division_sizeof_array) - << LHSArg->getSourceRange() << ArrayElemTy << RHSTy; - if (const auto *DRE = dyn_cast(LHSArg)) { - if (const ValueDecl *LHSArgDecl = DRE->getDecl()) - S.Diag(LHSArgDecl->getLocation(), diag::note_array_declared_here) - << LHSArgDecl; + // Get canonical types. We're not formatting these types, just comparing + // them. + LHSType = Context.getCanonicalType(LHSType).getUnqualifiedType(); + RHSType = Context.getCanonicalType(RHSType).getUnqualifiedType(); + + // Common case: no conversion required. + if (LHSType == RHSType) { + Kind = CK_NoOp; + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety && + !Context.canMergeTypeBounds(OrigLHSType, RHS.get()->getType())) { + Kind = CK_BoundsSafetyPointerCast; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + } else { + return Compatible; } + } - S.Diag(Loc, diag::note_precedence_silence) << RHS; + // If the LHS has an __auto_type, there are no additional type constraints + // to be worried about. + if (const auto *AT = dyn_cast(LHSType)) { + if (AT->isGNUAutoType()) { + Kind = CK_NoOp; + return Compatible; + } } -} -static void DiagnoseBadDivideOrRemainderValues(Sema& S, ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc, bool IsDiv) { - // Check for division/remainder by zero. - Expr::EvalResult RHSValue; - if (!RHS.get()->isValueDependent() && - RHS.get()->EvaluateAsInt(RHSValue, S.Context) && - RHSValue.Val.getInt() == 0) - S.DiagRuntimeBehavior(Loc, RHS.get(), - S.PDiag(diag::warn_remainder_division_by_zero) - << IsDiv << RHS.get()->getSourceRange()); -} + // If we have an atomic type, try a non-atomic assignment, then just add an + // atomic qualification step. + if (const AtomicType *AtomicTy = dyn_cast(LHSType)) { + Sema::AssignConvertType result = + CheckAssignmentConstraints(AtomicTy->getValueType(), RHS, Kind); + if (result != Compatible) + return result; + if (Kind != CK_NoOp && ConvertRHS) + RHS = ImpCastExprToType(RHS.get(), AtomicTy->getValueType(), Kind); + Kind = CK_NonAtomicToAtomic; + return Compatible; + } -QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - bool IsCompAssign, bool IsDiv) { - checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); + // If the left-hand side is a reference type, then we are in a + // (rare!) case where we've allowed the use of references in C, + // e.g., as a parameter type in a built-in function. In this case, + // just make sure that the type referenced is compatible with the + // right-hand side type. The caller is responsible for adjusting + // LHSType so that the resulting expression does not have reference + // type. + if (const ReferenceType *LHSTypeRef = LHSType->getAs()) { + if (Context.typesAreCompatible(LHSTypeRef->getPointeeType(), RHSType)) { + Kind = CK_LValueBitCast; + return Compatible; + } + return Incompatible; + } - QualType LHSTy = LHS.get()->getType(); - QualType RHSTy = RHS.get()->getType(); - if (LHSTy->isVectorType() || RHSTy->isVectorType()) - return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, - /*AllowBothBool*/ getLangOpts().AltiVec, - /*AllowBoolConversions*/ false, - /*AllowBooleanOperation*/ false, - /*ReportInvalid*/ true); - if (LHSTy->isSveVLSBuiltinType() || RHSTy->isSveVLSBuiltinType()) - return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, - ACK_Arithmetic); - if (!IsDiv && - (LHSTy->isConstantMatrixType() || RHSTy->isConstantMatrixType())) - return CheckMatrixMultiplyOperands(LHS, RHS, Loc, IsCompAssign); - // For division, only matrix-by-scalar is supported. Other combinations with - // matrix types are invalid. - if (IsDiv && LHSTy->isConstantMatrixType() && RHSTy->isArithmeticType()) - return CheckMatrixElementwiseOperands(LHS, RHS, Loc, IsCompAssign); + // Allow scalar to ExtVector assignments, and assignments of an ExtVector type + // to the same ExtVector type. + if (LHSType->isExtVectorType()) { + if (RHSType->isExtVectorType()) + return Incompatible; + if (RHSType->isArithmeticType()) { + // CK_VectorSplat does T -> vector T, so first cast to the element type. + if (ConvertRHS) + RHS = prepareVectorSplat(LHSType, RHS.get()); + Kind = CK_VectorSplat; + return Compatible; + } + } - QualType compType = UsualArithmeticConversions( - LHS, RHS, Loc, IsCompAssign ? ACK_CompAssign : ACK_Arithmetic); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); + // Conversions to or from vector type. + if (LHSType->isVectorType() || RHSType->isVectorType()) { + if (LHSType->isVectorType() && RHSType->isVectorType()) { + // Allow assignments of an AltiVec vector type to an equivalent GCC + // vector type and vice versa + if (Context.areCompatibleVectorTypes(LHSType, RHSType)) { + Kind = CK_BitCast; + return Compatible; + } + // If we are allowing lax vector conversions, and LHS and RHS are both + // vectors, the total size only needs to be the same. This is a bitcast; + // no bits are changed but the result type is different. + if (isLaxVectorConversion(RHSType, LHSType)) { + // The default for lax vector conversions with Altivec vectors will + // change, so if we are converting between vector types where + // at least one is an Altivec vector, emit a warning. + if (Context.getTargetInfo().getTriple().isPPC() && + anyAltivecTypes(RHSType, LHSType) && + !Context.areCompatibleVectorTypes(RHSType, LHSType)) + Diag(RHS.get()->getExprLoc(), diag::warn_deprecated_lax_vec_conv_all) + << RHSType << LHSType; + Kind = CK_BitCast; + return IncompatibleVectors; + } + } - if (compType.isNull() || !compType->isArithmeticType()) - return InvalidOperands(Loc, LHS, RHS); - if (IsDiv) { - DetectPrecisionLossInComplexDivision(*this, RHS.get()->getType(), Loc); - DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv); - DiagnoseDivisionSizeofPointerOrArray(*this, LHS.get(), RHS.get(), Loc); - } - return compType; -} + // When the RHS comes from another lax conversion (e.g. binops between + // scalars and vectors) the result is canonicalized as a vector. When the + // LHS is also a vector, the lax is allowed by the condition above. Handle + // the case where LHS is a scalar. + if (LHSType->isScalarType()) { + const VectorType *VecType = RHSType->getAs(); + if (VecType && VecType->getNumElements() == 1 && + isLaxVectorConversion(RHSType, LHSType)) { + if (Context.getTargetInfo().getTriple().isPPC() && + (VecType->getVectorKind() == VectorKind::AltiVecVector || + VecType->getVectorKind() == VectorKind::AltiVecBool || + VecType->getVectorKind() == VectorKind::AltiVecPixel)) + Diag(RHS.get()->getExprLoc(), diag::warn_deprecated_lax_vec_conv_all) + << RHSType << LHSType; + ExprResult *VecExpr = &RHS; + *VecExpr = ImpCastExprToType(VecExpr->get(), LHSType, CK_BitCast); + Kind = CK_BitCast; + return Compatible; + } + } -QualType Sema::CheckRemainderOperands( - ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, bool IsCompAssign) { - checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); + // Allow assignments between fixed-length and sizeless SVE vectors. + if ((LHSType->isSVESizelessBuiltinType() && RHSType->isVectorType()) || + (LHSType->isVectorType() && RHSType->isSVESizelessBuiltinType())) + if (Context.areCompatibleSveTypes(LHSType, RHSType) || + Context.areLaxCompatibleSveTypes(LHSType, RHSType)) { + Kind = CK_BitCast; + return Compatible; + } - // Note: This check is here to simplify the double exclusions of - // scalar and vector HLSL checks. No getLangOpts().HLSL - // is needed since all languages exlcude doubles. - if (LHS.get()->getType()->isDoubleType() || - RHS.get()->getType()->isDoubleType() || - (LHS.get()->getType()->isVectorType() && LHS.get() - ->getType() - ->getAs() - ->getElementType() - ->isDoubleType()) || - (RHS.get()->getType()->isVectorType() && RHS.get() - ->getType() - ->getAs() - ->getElementType() - ->isDoubleType())) - return InvalidOperands(Loc, LHS, RHS); + // Allow assignments between fixed-length and sizeless RVV vectors. + if ((LHSType->isRVVSizelessBuiltinType() && RHSType->isVectorType()) || + (LHSType->isVectorType() && RHSType->isRVVSizelessBuiltinType())) { + if (Context.areCompatibleRVVTypes(LHSType, RHSType) || + Context.areLaxCompatibleRVVTypes(LHSType, RHSType)) { + Kind = CK_BitCast; + return Compatible; + } + } - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) { - if ((LHS.get()->getType()->hasIntegerRepresentation() && - RHS.get()->getType()->hasIntegerRepresentation()) || - (getLangOpts().HLSL && - (LHS.get()->getType()->hasFloatingRepresentation() || - RHS.get()->getType()->hasFloatingRepresentation()))) - return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, - /*AllowBothBool*/ getLangOpts().AltiVec, - /*AllowBoolConversions*/ false, - /*AllowBooleanOperation*/ false, - /*ReportInvalid*/ true); - return InvalidOperands(Loc, LHS, RHS); + return Incompatible; } - if (LHS.get()->getType()->isSveVLSBuiltinType() || - RHS.get()->getType()->isSveVLSBuiltinType()) { - if (LHS.get()->getType()->hasIntegerRepresentation() && - RHS.get()->getType()->hasIntegerRepresentation()) - return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, - ACK_Arithmetic); + // Diagnose attempts to convert between __ibm128, __float128 and long double + // where such conversions currently can't be handled. + if (unsupportedTypeConversion(*this, LHSType, RHSType)) + return Incompatible; - return InvalidOperands(Loc, LHS, RHS); + // Disallow assigning a _Complex to a real type in C++ mode since it simply + // discards the imaginary part. + if (getLangOpts().CPlusPlus && RHSType->getAs() && + !LHSType->getAs()) + return Incompatible; + + // Arithmetic conversions. + if (LHSType->isArithmeticType() && RHSType->isArithmeticType() && + !(getLangOpts().CPlusPlus && LHSType->isEnumeralType())) { + if (ConvertRHS) + Kind = PrepareScalarCast(RHS, LHSType); + return Compatible; } - QualType compType = UsualArithmeticConversions( - LHS, RHS, Loc, IsCompAssign ? ACK_CompAssign : ACK_Arithmetic); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); + // Conversions to normal pointers. + if (const PointerType *LHSPointer = dyn_cast(LHSType)) { + // U* -> T* + if (const PointerType *RHSPointer = dyn_cast(RHSType)) { + LangAS AddrSpaceL = LHSPointer->getPointeeType().getAddressSpace(); + LangAS AddrSpaceR = RHSPointer->getPointeeType().getAddressSpace(); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + bool IsSrcNull = RHS.get()->IgnoreParenCasts()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull); + if (!IsSrcNull && !RHSType->isSafePointerType() && + !RHS.get()->getType()->isBoundsAttributedType() && + LHSType->isSafePointerType() && + !OrigLHSType->isValueTerminatedType()) { + return LHSPointer->isSingle() ? IncompatibleUnsafeToSafePointer + : IncompatibleUnsafeToIndexablePointer; + } + if (!IsSrcNull && RHSPointer->isSingle() && + LHSType->isPointerTypeWithBounds() && + !RHS.get()->getType()->isBoundsAttributedType()) { + if (RHSPointer->getPointeeType()->isIncompleteOrSizelessType()) + return IncompleteSingleToIndexablePointer; - if (compType.isNull() || - (!compType->isIntegerType() && - !(getLangOpts().HLSL && compType->isFloatingType()))) - return InvalidOperands(Loc, LHS, RHS); - DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, false /* IsDiv */); - return compType; -} + DiagnoseSingleToWideLosingBounds(LHSType, RHSType, RHS.get()); + } + } -/// Diagnose invalid arithmetic on two void pointers. -static void diagnoseArithmeticOnTwoVoidPointers(Sema &S, SourceLocation Loc, - Expr *LHSExpr, Expr *RHSExpr) { - S.Diag(Loc, S.getLangOpts().CPlusPlus - ? diag::err_typecheck_pointer_arith_void_type - : diag::ext_gnu_void_ptr) - << 1 /* two pointers */ << LHSExpr->getSourceRange() - << RHSExpr->getSourceRange(); -} + auto RHSFA = RHSPointer->getPointerAttributes(); + if (LHSPointer->getPointerAttributes() != RHSFA) { + // First convert with the left-hand type taking the -fbounds-safety + // attributes of the right hand type, and then do a + // BoundsSafetyPointerCast from the result of that to the left-hand type. + // This ensures that places that need two implicit casts (like + // `int *__single = (char *__bidi)x`) will first BitCast to + // `int *__bidi` and then FPC to `int *__single`, which will CodeGen + // the bounds check correctly. + auto LHSIntermediateType = Context.getPointerType( + LHSPointer->getPointeeType(), RHSFA); + auto Result = CheckAssignmentConstraints(LHSIntermediateType, RHS, + Kind, ConvertRHS); + bool SkipNoOp = Kind == CK_NoOp && + Context.hasSameType(LHSPointer->getPointeeType(), + RHSPointer->getPointeeType()); + if (ConvertRHS && !SkipNoOp) + RHS = ImpCastExprToType(RHS.get(), LHSIntermediateType, Kind); + Kind = CK_BoundsSafetyPointerCast; + + // Look for cases where a __single pointer is implicitly converted to an + // explicitly indexable pointer. We do this here rather than earlier in + // the function because returning earlier would cause the extra implicit + // cast logic above to be missed which would change the generated AST. + bool IsSrcNull = RHS.get()->IgnoreParenCasts()->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNotNull); + if (!IsSrcNull && RHSPointer->isSingle() && + !RHS.get()->getType()->isBoundsAttributedType()) { + bool IsExplicitlyBoundedPointer = LHSType->isPointerTypeWithBounds(); + if (OrigLHSType->hasAttr(attr::PtrAutoAttr)) { + // We avoid implicitly indexable (i.e. implicit __bidi_indexable + // local vars) because warning about these would likely be too + // noisy. + IsExplicitlyBoundedPointer = false; + } + if (IsExplicitlyBoundedPointer) { + assert(Kind == CK_BoundsSafetyPointerCast); + // Return this for the purposes of later emitting a warning. + return CompatibleSingleToExplicitIndexablePointer; + } + } -/// Diagnose invalid arithmetic on a void pointer. -static void diagnoseArithmeticOnVoidPointer(Sema &S, SourceLocation Loc, - Expr *Pointer) { - S.Diag(Loc, S.getLangOpts().CPlusPlus - ? diag::err_typecheck_pointer_arith_void_type - : diag::ext_gnu_void_ptr) - << 0 /* one pointer */ << Pointer->getSourceRange(); -} + if (Result != Compatible) + return Result; + } else if (OrigLHSType->isBoundsAttributedType() && + RHSType->isSinglePointerType()) { + // Single to dynamic bounds pointer should first create an implicit cast + // to `__bidi_indexable` and then create any necessary cast such as + // bitcast in the following example. `void *__sized_by(len) dst = (int + // *__single)src` + assert(!RHS.get()->getType()->isBoundsAttributedType()); + if (ConvertRHS) { + QualType BidiRTy = Context.getPointerType( + RHSPointer->getPointeeType(), + BoundsSafetyPointerAttributes::bidiIndexable()); + RHS = ImpCastExprToType(RHS.get(), BidiRTy, CK_BoundsSafetyPointerCast); + auto Result = + CheckAssignmentConstraints(OrigLHSType, RHS, Kind, ConvertRHS); + if (Result != Compatible) + return Result; + } else { + Kind = CK_BitCast; + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ + } else if (AddrSpaceL != AddrSpaceR) + Kind = CK_AddressSpaceConversion; + else if (Context.hasCvrSimilarType(RHSType, LHSType)) + Kind = CK_NoOp; + else + Kind = CK_BitCast; + return checkPointerTypesForAssignment(*this, LHSType, RHSType, + RHS.get()->getBeginLoc()); + } -/// Diagnose invalid arithmetic on a null pointer. -/// -/// If \p IsGNUIdiom is true, the operation is using the 'p = (i8*)nullptr + n' -/// idiom, which we recognize as a GNU extension. -/// -static void diagnoseArithmeticOnNullPointer(Sema &S, SourceLocation Loc, - Expr *Pointer, bool IsGNUIdiom) { - if (IsGNUIdiom) - S.Diag(Loc, diag::warn_gnu_null_ptr_arith) - << Pointer->getSourceRange(); - else - S.Diag(Loc, diag::warn_pointer_arith_null_ptr) - << S.getLangOpts().CPlusPlus << Pointer->getSourceRange(); -} + // int -> T* + if (RHSType->isIntegerType()) { + Kind = CK_IntegralToPointer; // FIXME: null? + return LHSPointer->isSafePointer() + ? IncompatibleIntToSafePointer : IntToPointer; + } -/// Diagnose invalid subraction on a null pointer. -/// -static void diagnoseSubtractionOnNullPointer(Sema &S, SourceLocation Loc, - Expr *Pointer, bool BothNull) { - // Null - null is valid in C++ [expr.add]p7 - if (BothNull && S.getLangOpts().CPlusPlus) - return; + // C pointers are not compatible with ObjC object pointers, + // with two exceptions: + if (isa(RHSType)) { + // - conversions to void* + if (LHSPointer->getPointeeType()->isVoidType()) { + Kind = CK_BitCast; + return Compatible; + } - // Is this s a macro from a system header? - if (S.Diags.getSuppressSystemWarnings() && S.SourceMgr.isInSystemMacro(Loc)) - return; + // - conversions from 'Class' to the redefinition type + if (RHSType->isObjCClassType() && + Context.hasSameType(LHSType, + Context.getObjCClassRedefinitionType())) { + Kind = CK_BitCast; + return Compatible; + } - S.DiagRuntimeBehavior(Loc, Pointer, - S.PDiag(diag::warn_pointer_sub_null_ptr) - << S.getLangOpts().CPlusPlus - << Pointer->getSourceRange()); -} + Kind = CK_BitCast; + return IncompatiblePointer; + } -/// Diagnose invalid arithmetic on two function pointers. -static void diagnoseArithmeticOnTwoFunctionPointers(Sema &S, SourceLocation Loc, - Expr *LHS, Expr *RHS) { - assert(LHS->getType()->isAnyPointerType()); - assert(RHS->getType()->isAnyPointerType()); - S.Diag(Loc, S.getLangOpts().CPlusPlus - ? diag::err_typecheck_pointer_arith_function_type - : diag::ext_gnu_ptr_func_arith) - << 1 /* two pointers */ << LHS->getType()->getPointeeType() - // We only show the second type if it differs from the first. - << (unsigned)!S.Context.hasSameUnqualifiedType(LHS->getType(), - RHS->getType()) - << RHS->getType()->getPointeeType() - << LHS->getSourceRange() << RHS->getSourceRange(); -} + // U^ -> void* + if (RHSType->getAs()) { + if (LHSPointer->getPointeeType()->isVoidType()) { + LangAS AddrSpaceL = LHSPointer->getPointeeType().getAddressSpace(); + LangAS AddrSpaceR = RHSType->getAs() + ->getPointeeType() + .getAddressSpace(); + Kind = + AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion : CK_BitCast; + return Compatible; + } + } -/// Diagnose invalid arithmetic on a function pointer. -static void diagnoseArithmeticOnFunctionPointer(Sema &S, SourceLocation Loc, - Expr *Pointer) { - assert(Pointer->getType()->isAnyPointerType()); - S.Diag(Loc, S.getLangOpts().CPlusPlus - ? diag::err_typecheck_pointer_arith_function_type - : diag::ext_gnu_ptr_func_arith) - << 0 /* one pointer */ << Pointer->getType()->getPointeeType() - << 0 /* one pointer, so only one type */ - << Pointer->getSourceRange(); -} + return Incompatible; + } -/// Emit error if Operand is incomplete pointer type -/// -/// \returns True if pointer has incomplete type -static bool checkArithmeticIncompletePointerType(Sema &S, SourceLocation Loc, - Expr *Operand) { - QualType ResType = Operand->getType(); - if (const AtomicType *ResAtomicType = ResType->getAs()) - ResType = ResAtomicType->getValueType(); + // Conversions to block pointers. + if (isa(LHSType)) { + // U^ -> T^ + if (RHSType->isBlockPointerType()) { + LangAS AddrSpaceL = LHSType->getAs() + ->getPointeeType() + .getAddressSpace(); + LangAS AddrSpaceR = RHSType->getAs() + ->getPointeeType() + .getAddressSpace(); + Kind = AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion : CK_BitCast; + return checkBlockPointerTypesForAssignment(*this, LHSType, RHSType); + } - assert(ResType->isAnyPointerType()); - QualType PointeeTy = ResType->getPointeeType(); - return S.RequireCompleteSizedType( - Loc, PointeeTy, - diag::err_typecheck_arithmetic_incomplete_or_sizeless_type, - Operand->getSourceRange()); -} + // int or null -> T^ + if (RHSType->isIntegerType()) { + Kind = CK_IntegralToPointer; // FIXME: null + return IntToBlockPointer; + } -/// Check the validity of an arithmetic pointer operand. -/// -/// If the operand has pointer type, this code will check for pointer types -/// which are invalid in arithmetic operations. These will be diagnosed -/// appropriately, including whether or not the use is supported as an -/// extension. -/// -/// \returns True when the operand is valid to use (even if as an extension). -static bool checkArithmeticOpPointerOperand(Sema &S, SourceLocation Loc, - Expr *Operand) { - QualType ResType = Operand->getType(); - if (const AtomicType *ResAtomicType = ResType->getAs()) - ResType = ResAtomicType->getValueType(); + // id -> T^ + if (getLangOpts().ObjC && RHSType->isObjCIdType()) { + Kind = CK_AnyPointerToBlockPointerCast; + return Compatible; + } - if (!ResType->isAnyPointerType()) return true; + // void* -> T^ + if (const PointerType *RHSPT = RHSType->getAs()) + if (RHSPT->getPointeeType()->isVoidType()) { + Kind = CK_AnyPointerToBlockPointerCast; + return Compatible; + } - QualType PointeeTy = ResType->getPointeeType(); - if (PointeeTy->isVoidType()) { - diagnoseArithmeticOnVoidPointer(S, Loc, Operand); - return !S.getLangOpts().CPlusPlus; - } - if (PointeeTy->isFunctionType()) { - diagnoseArithmeticOnFunctionPointer(S, Loc, Operand); - return !S.getLangOpts().CPlusPlus; + return Incompatible; } - if (checkArithmeticIncompletePointerType(S, Loc, Operand)) return false; + // Conversions to Objective-C pointers. + if (isa(LHSType)) { + // A* -> B* + if (RHSType->isObjCObjectPointerType()) { + Kind = CK_BitCast; + Sema::AssignConvertType result = + checkObjCPointerTypesForAssignment(*this, LHSType, RHSType); + if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && + result == Compatible && + !ObjC().CheckObjCARCUnavailableWeakConversion(OrigLHSType, RHSType)) + result = IncompatibleObjCWeakRef; + return result; + } - return true; -} + // int or null -> A* + if (RHSType->isIntegerType()) { + Kind = CK_IntegralToPointer; // FIXME: null + return IntToPointer; + } -/// Check the validity of a binary arithmetic operation w.r.t. pointer -/// operands. -/// -/// This routine will diagnose any invalid arithmetic on pointer operands much -/// like \see checkArithmeticOpPointerOperand. However, it has special logic -/// for emitting a single diagnostic even for operations where both LHS and RHS -/// are (potentially problematic) pointers. -/// -/// \returns True when the operand is valid to use (even if as an extension). -static bool checkArithmeticBinOpPointerOperands(Sema &S, SourceLocation Loc, - Expr *LHSExpr, Expr *RHSExpr) { - bool isLHSPointer = LHSExpr->getType()->isAnyPointerType(); - bool isRHSPointer = RHSExpr->getType()->isAnyPointerType(); - if (!isLHSPointer && !isRHSPointer) return true; + // In general, C pointers are not compatible with ObjC object pointers, + // with two exceptions: + if (isa(RHSType)) { + Kind = CK_CPointerToObjCPointerCast; - QualType LHSPointeeTy, RHSPointeeTy; - if (isLHSPointer) LHSPointeeTy = LHSExpr->getType()->getPointeeType(); - if (isRHSPointer) RHSPointeeTy = RHSExpr->getType()->getPointeeType(); + // - conversions from 'void*' + if (RHSType->isVoidPointerType()) { + return Compatible; + } - // if both are pointers check if operation is valid wrt address spaces - if (isLHSPointer && isRHSPointer) { - if (!LHSPointeeTy.isAddressSpaceOverlapping(RHSPointeeTy, - S.getASTContext())) { - S.Diag(Loc, - diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) - << LHSExpr->getType() << RHSExpr->getType() << 1 /*arithmetic op*/ - << LHSExpr->getSourceRange() << RHSExpr->getSourceRange(); - return false; + // - conversions to 'Class' from its redefinition type + if (LHSType->isObjCClassType() && + Context.hasSameType(RHSType, + Context.getObjCClassRedefinitionType())) { + return Compatible; + } + + return IncompatiblePointer; } - } - // Check for arithmetic on pointers to incomplete types. - bool isLHSVoidPtr = isLHSPointer && LHSPointeeTy->isVoidType(); - bool isRHSVoidPtr = isRHSPointer && RHSPointeeTy->isVoidType(); - if (isLHSVoidPtr || isRHSVoidPtr) { - if (!isRHSVoidPtr) diagnoseArithmeticOnVoidPointer(S, Loc, LHSExpr); - else if (!isLHSVoidPtr) diagnoseArithmeticOnVoidPointer(S, Loc, RHSExpr); - else diagnoseArithmeticOnTwoVoidPointers(S, Loc, LHSExpr, RHSExpr); + // Only under strict condition T^ is compatible with an Objective-C pointer. + if (RHSType->isBlockPointerType() && + LHSType->isBlockCompatibleObjCPointerType(Context)) { + if (ConvertRHS) + maybeExtendBlockObject(RHS); + Kind = CK_BlockPointerToObjCPointerCast; + return Compatible; + } - return !S.getLangOpts().CPlusPlus; + return Incompatible; } - bool isLHSFuncPtr = isLHSPointer && LHSPointeeTy->isFunctionType(); - bool isRHSFuncPtr = isRHSPointer && RHSPointeeTy->isFunctionType(); - if (isLHSFuncPtr || isRHSFuncPtr) { - if (!isRHSFuncPtr) diagnoseArithmeticOnFunctionPointer(S, Loc, LHSExpr); - else if (!isLHSFuncPtr) diagnoseArithmeticOnFunctionPointer(S, Loc, - RHSExpr); - else diagnoseArithmeticOnTwoFunctionPointers(S, Loc, LHSExpr, RHSExpr); - - return !S.getLangOpts().CPlusPlus; + // Conversion to nullptr_t (C23 only) + if (getLangOpts().C23 && LHSType->isNullPtrType() && + RHS.get()->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNull)) { + // null -> nullptr_t + Kind = CK_NullToPointer; + return Compatible; } - if (isLHSPointer && checkArithmeticIncompletePointerType(S, Loc, LHSExpr)) - return false; - if (isRHSPointer && checkArithmeticIncompletePointerType(S, Loc, RHSExpr)) - return false; - - return true; -} - -/// diagnoseStringPlusInt - Emit a warning when adding an integer to a string -/// literal. -static void diagnoseStringPlusInt(Sema &Self, SourceLocation OpLoc, - Expr *LHSExpr, Expr *RHSExpr) { - StringLiteral* StrExpr = dyn_cast(LHSExpr->IgnoreImpCasts()); - Expr* IndexExpr = RHSExpr; - if (!StrExpr) { - StrExpr = dyn_cast(RHSExpr->IgnoreImpCasts()); - IndexExpr = LHSExpr; + // Conversions from pointers that are not covered by the above. + if (isa(RHSType)) { + // T* -> _Bool + if (LHSType == Context.BoolTy) { + Kind = CK_PointerToBoolean; + return Compatible; + } + + // T* -> int + if (LHSType->isIntegerType()) { + Kind = CK_PointerToIntegral; + return PointerToInt; + } + + return Incompatible; } - bool IsStringPlusInt = StrExpr && - IndexExpr->getType()->isIntegralOrUnscopedEnumerationType(); - if (!IsStringPlusInt || IndexExpr->isValueDependent()) - return; + // Conversions from Objective-C pointers that are not covered by the above. + if (isa(RHSType)) { + // T* -> _Bool + if (LHSType == Context.BoolTy) { + Kind = CK_PointerToBoolean; + return Compatible; + } - SourceRange DiagRange(LHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); - Self.Diag(OpLoc, diag::warn_string_plus_int) - << DiagRange << IndexExpr->IgnoreImpCasts()->getType(); + // T* -> int + if (LHSType->isIntegerType()) { + Kind = CK_PointerToIntegral; + return PointerToInt; + } - // Only print a fixit for "str" + int, not for int + "str". - if (IndexExpr == RHSExpr) { - SourceLocation EndLoc = Self.getLocForEndOfToken(RHSExpr->getEndLoc()); - Self.Diag(OpLoc, diag::note_string_plus_scalar_silence) - << FixItHint::CreateInsertion(LHSExpr->getBeginLoc(), "&") - << FixItHint::CreateReplacement(SourceRange(OpLoc), "[") - << FixItHint::CreateInsertion(EndLoc, "]"); - } else - Self.Diag(OpLoc, diag::note_string_plus_scalar_silence); -} + return Incompatible; + } -/// Emit a warning when adding a char literal to a string. -static void diagnoseStringPlusChar(Sema &Self, SourceLocation OpLoc, - Expr *LHSExpr, Expr *RHSExpr) { - const Expr *StringRefExpr = LHSExpr; - const CharacterLiteral *CharExpr = - dyn_cast(RHSExpr->IgnoreImpCasts()); + // struct A -> struct B + if (isa(LHSType) && isa(RHSType)) { + if (Context.typesAreCompatible(LHSType, RHSType)) { + Kind = CK_NoOp; + return Compatible; + } + } - if (!CharExpr) { - CharExpr = dyn_cast(LHSExpr->IgnoreImpCasts()); - StringRefExpr = RHSExpr; + if (LHSType->isSamplerT() && RHSType->isIntegerType()) { + Kind = CK_IntToOCLSampler; + return Compatible; } - if (!CharExpr || !StringRefExpr) - return; + return Incompatible; +} - const QualType StringType = StringRefExpr->getType(); +/* TO_UPSTREAM(BoundsSafety) ON*/ +void Sema::DiagnoseSingleToWideLosingBounds(QualType LHSType, + QualType RHSType, + const Expr *RHSExp) { + assert(LHSType->isPointerType() && RHSType->isPointerType()); + // We don't need to specialize it for structs with flexible array + // members because when LHSType is a flexible array member smaller + // than the source type, that is still a problem. + auto LHSSize = Context.getTypeSizeOrNull(LHSType->getPointeeType()); + auto RHSSize = Context.getTypeSizeOrNull(RHSType->getPointeeType()); - // Return if not a PointerType. - if (!StringType->isAnyPointerType()) + if (LHSSize >= RHSSize) return; - // Return if not a CharacterType. - if (!StringType->getPointeeType()->isAnyCharacterType()) - return; + const auto *LHSPointer = LHSType->getAs(); + QualType LHSPointeeTy = LHSPointer->getPointeeType(); + Diag(RHSExp->getExprLoc(), + diag::warn_bounds_safety_single_bitcast_lose_bounds) + << RHSType << LHSType << LHSPointer->isBidiIndexable() + << LHSPointeeTy << LHSPointeeTy->isIncompleteType(); - ASTContext &Ctx = Self.getASTContext(); - SourceRange DiagRange(LHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); + QualType RHSTyBidi = Context.getBoundsSafetyPointerType(RHSType, + BoundsSafetyPointerAttributes::bidiIndexable()); + std::string RHSTyBidiStr = "(" + RHSTyBidi.getAsString() + ")"; - const QualType CharType = CharExpr->getType(); - if (!CharType->isAnyCharacterType() && - CharType->isIntegerType() && - llvm::isUIntN(Ctx.getCharWidth(), CharExpr->getValue())) { - Self.Diag(OpLoc, diag::warn_string_plus_char) - << DiagRange << Ctx.CharTy; - } else { - Self.Diag(OpLoc, diag::warn_string_plus_char) - << DiagRange << CharExpr->getType(); - } + CharSourceRange ExprRange = + CharSourceRange::getCharRange(RHSExp->getBeginLoc(), + getLocForEndOfToken(RHSExp->getExprLoc())); - // Only print a fixit for str + char, not for char + str. - if (isa(RHSExpr->IgnoreImpCasts())) { - SourceLocation EndLoc = Self.getLocForEndOfToken(RHSExpr->getEndLoc()); - Self.Diag(OpLoc, diag::note_string_plus_scalar_silence) - << FixItHint::CreateInsertion(LHSExpr->getBeginLoc(), "&") - << FixItHint::CreateReplacement(SourceRange(OpLoc), "[") - << FixItHint::CreateInsertion(EndLoc, "]"); - } else { - Self.Diag(OpLoc, diag::note_string_plus_scalar_silence); - } -} + std::string ExprStr = "'" + + std::string(Lexer::getSourceText(ExprRange, getSourceManager(), getLangOpts())) + + "'"; -/// Emit error when two pointers are incompatible. -static void diagnosePointerIncompatibility(Sema &S, SourceLocation Loc, - Expr *LHSExpr, Expr *RHSExpr) { - assert(LHSExpr->getType()->isAnyPointerType()); - assert(RHSExpr->getType()->isAnyPointerType()); - S.Diag(Loc, diag::err_typecheck_sub_ptr_compatible) - << LHSExpr->getType() << RHSExpr->getType() << LHSExpr->getSourceRange() - << RHSExpr->getSourceRange(); -} + Diag(RHSExp->getExprLoc(), + diag::note_bounds_safety_single_bitcast_lose_bounds_keep_bound) + << RHSTyBidi << ExprStr + << FixItHint::CreateInsertion(RHSExp->getBeginLoc(), RHSTyBidiStr); -// C99 6.5.6 -QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, BinaryOperatorKind Opc, - QualType* CompLHSTy) { - checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); + auto PD = PDiag(diag::note_bounds_safety_single_bitcast_lose_bounds_silence); - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) { - QualType compType = - CheckVectorOperands(LHS, RHS, Loc, CompLHSTy, - /*AllowBothBool*/ getLangOpts().AltiVec, - /*AllowBoolConversions*/ getLangOpts().ZVector, - /*AllowBooleanOperation*/ false, - /*ReportInvalid*/ true); - if (CompLHSTy) *CompLHSTy = compType; - return compType; - } + if (!LHSType->getPointeeType()->isSizeMeaningless()) { - if (LHS.get()->getType()->isSveVLSBuiltinType() || - RHS.get()->getType()->isSveVLSBuiltinType()) { - QualType compType = - CheckSizelessVectorOperands(LHS, RHS, Loc, CompLHSTy, ACK_Arithmetic); - if (CompLHSTy) - *CompLHSTy = compType; - return compType; + QualType LHSTySingle = Context.getBoundsSafetyPointerType(LHSType, + BoundsSafetyPointerAttributes::single()); + std::string LHSTySingleStr = "(" + LHSTySingle.getAsString() + ")"; + PD << FixItHint::CreateInsertion(RHSExp->getBeginLoc(), LHSTySingleStr); } + Diag(RHSExp->getExprLoc(), PD); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ - if (LHS.get()->getType()->isConstantMatrixType() || - RHS.get()->getType()->isConstantMatrixType()) { - QualType compType = - CheckMatrixElementwiseOperands(LHS, RHS, Loc, CompLHSTy); - if (CompLHSTy) - *CompLHSTy = compType; - return compType; - } +/// Constructs a transparent union from an expression that is +/// used to initialize the transparent union. +static void ConstructTransparentUnion(Sema &S, ASTContext &C, + ExprResult &EResult, QualType UnionType, + FieldDecl *Field) { + // Build an initializer list that designates the appropriate member + // of the transparent union. + Expr *E = EResult.get(); + InitListExpr *Initializer = new (C) InitListExpr(C, SourceLocation(), + E, SourceLocation()); + Initializer->setType(UnionType); + Initializer->setInitializedFieldInUnion(Field); - QualType compType = UsualArithmeticConversions( - LHS, RHS, Loc, CompLHSTy ? ACK_CompAssign : ACK_Arithmetic); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); + // Build a compound literal constructing a value of the transparent + // union type from this initializer list. + TypeSourceInfo *unionTInfo = C.getTrivialTypeSourceInfo(UnionType); + EResult = new (C) CompoundLiteralExpr(SourceLocation(), unionTInfo, UnionType, + VK_PRValue, Initializer, false); +} - // Diagnose "string literal" '+' int and string '+' "char literal". - if (Opc == BO_Add) { - diagnoseStringPlusInt(*this, Loc, LHS.get(), RHS.get()); - diagnoseStringPlusChar(*this, Loc, LHS.get(), RHS.get()); - } +Sema::AssignConvertType +Sema::CheckTransparentUnionArgumentConstraints(QualType ArgType, + ExprResult &RHS) { + QualType RHSType = RHS.get()->getType(); - // handle the common case first (both operands are arithmetic). - if (!compType.isNull() && compType->isArithmeticType()) { - if (CompLHSTy) *CompLHSTy = compType; - return compType; - } + // If the ArgType is a Union type, we want to handle a potential + // transparent_union GCC extension. + const RecordType *UT = ArgType->getAsUnionType(); + if (!UT || !UT->getDecl()->hasAttr()) + return Incompatible; - // Type-checking. Ultimately the pointer's going to be in PExp; - // note that we bias towards the LHS being the pointer. - Expr *PExp = LHS.get(), *IExp = RHS.get(); + // The field to initialize within the transparent union. + RecordDecl *UD = UT->getDecl(); + FieldDecl *InitField = nullptr; + // It's compatible if the expression matches any of the fields. + for (auto *it : UD->fields()) { + if (it->getType()->isPointerType()) { + // If the transparent union contains a pointer type, we allow: + // 1) void pointer + // 2) null pointer constant + if (RHSType->isPointerType()) + if (RHSType->castAs()->getPointeeType()->isVoidType()) { + RHS = ImpCastExprToType(RHS.get(), it->getType(), CK_BitCast); + InitField = it; + break; + } - bool isObjCPointer; - if (PExp->getType()->isPointerType()) { - isObjCPointer = false; - } else if (PExp->getType()->isObjCObjectPointerType()) { - isObjCPointer = true; - } else { - std::swap(PExp, IExp); - if (PExp->getType()->isPointerType()) { - isObjCPointer = false; - } else if (PExp->getType()->isObjCObjectPointerType()) { - isObjCPointer = true; - } else { - return InvalidOperands(Loc, LHS, RHS); + if (RHS.get()->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNull)) { + RHS = ImpCastExprToType(RHS.get(), it->getType(), + CK_NullToPointer); + InitField = it; + break; + } } - } - assert(PExp->getType()->isAnyPointerType()); - - if (!IExp->getType()->isIntegerType()) - return InvalidOperands(Loc, LHS, RHS); - // Adding to a null pointer results in undefined behavior. - if (PExp->IgnoreParenCasts()->isNullPointerConstant( - Context, Expr::NPC_ValueDependentIsNotNull)) { - // In C++ adding zero to a null pointer is defined. - Expr::EvalResult KnownVal; - if (!getLangOpts().CPlusPlus || - (!IExp->isValueDependent() && - (!IExp->EvaluateAsInt(KnownVal, Context) || - KnownVal.Val.getInt() != 0))) { - // Check the conditions to see if this is the 'p = nullptr + n' idiom. - bool IsGNUIdiom = BinaryOperator::isNullPointerArithmeticExtension( - Context, BO_Add, PExp, IExp); - diagnoseArithmeticOnNullPointer(*this, Loc, PExp, IsGNUIdiom); + CastKind Kind; + if (CheckAssignmentConstraints(it->getType(), RHS, Kind) + == Compatible) { + RHS = ImpCastExprToType(RHS.get(), it->getType(), Kind); + InitField = it; + break; } } - if (!checkArithmeticOpPointerOperand(*this, Loc, PExp)) - return QualType(); - - if (isObjCPointer && checkArithmeticOnObjCPointer(*this, Loc, PExp)) - return QualType(); + if (!InitField) + return Incompatible; - // Arithmetic on label addresses is normally allowed, except when we add - // a ptrauth signature to the addresses. - if (isa(PExp) && getLangOpts().PointerAuthIndirectGotos) { - Diag(Loc, diag::err_ptrauth_indirect_goto_addrlabel_arithmetic) - << /*addition*/ 1; - return QualType(); - } + ConstructTransparentUnion(*this, Context, RHS, ArgType, InitField); + return Compatible; +} - // Check array bounds for pointer arithemtic - CheckArrayAccess(PExp, IExp); +/* TO_UPSTREAM(BoundsSafety) ON*/ +// Check dependent variables that are used in a return type. Those variables +// don't have DependerDeclsAttr or __started_by() type, since currently we +// cannot express referring to function return (e.g., we cannot create +// __started_by(func ret)). +static bool checkDynamicBoundVariableEscapeInRetType(Sema &S, + const Expr *RHSExp, + const ValueDecl *VD, + const int ExpAddrOfLevel) { + const BoundsAttributedType *RetType = nullptr; + const TypeCoupledDeclRefInfo *Info = nullptr; + if (!VD->isDependentParamOfReturnType(&RetType, &Info)) + return true; - if (CompLHSTy) { - QualType LHSTy = Context.isPromotableBitField(LHS.get()); - if (LHSTy.isNull()) { - LHSTy = LHS.get()->getType(); - if (Context.isPromotableIntegerType(LHSTy)) - LHSTy = Context.getPromotedIntegerType(LHSTy); - } - *CompLHSTy = LHSTy; - } + int AttAddrOfLevel = Info->isDeref() ? -1 : 0; + if (ExpAddrOfLevel <= AttAddrOfLevel) + return true; - return PExp->getType(); + const auto *CATy = dyn_cast(RetType); + BoundsAttributedType::BoundsAttrKind Kind = + CATy ? CATy->getKind() : BoundsAttributedType::BoundsAttrKind::EndedBy; + S.Diag(RHSExp->getExprLoc(), + diag::err_bounds_safety_taking_address_dynamic_bound_dependent) + << Kind << /*variable*/ 0 << RHSExp->getSourceRange(); + return false; } -// C99 6.5.6 -QualType Sema::CheckSubtractionOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - QualType* CompLHSTy) { - checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); - - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) { - QualType compType = - CheckVectorOperands(LHS, RHS, Loc, CompLHSTy, - /*AllowBothBool*/ getLangOpts().AltiVec, - /*AllowBoolConversions*/ getLangOpts().ZVector, - /*AllowBooleanOperation*/ false, - /*ReportInvalid*/ true); - if (CompLHSTy) *CompLHSTy = compType; - return compType; - } +// BoundsSafety: a dynamic count variable cannot be pointed to by any other +// variable. Exception is when the variable is passed as a compatible argument +// to a function. +bool Sema::CheckDynamicBoundVariableEscape(QualType LHSType, Expr *RHSExp) { + RHSExp = RHSExp->IgnoreParenCasts(); + int ExpAddrOfLevel = 0; + auto VD = findValueDecl(RHSExp, &ExpAddrOfLevel); + auto BaseRecord = findBaseRecordDecl(RHSExp); + if (!VD) + return true; - if (LHS.get()->getType()->isSveVLSBuiltinType() || - RHS.get()->getType()->isSveVLSBuiltinType()) { - QualType compType = - CheckSizelessVectorOperands(LHS, RHS, Loc, CompLHSTy, ACK_Arithmetic); - if (CompLHSTy) - *CompLHSTy = compType; - return compType; + if (const auto *Att = VD->getAttr()) { + int AttAddrOfLevel = Att->getIsDeref() ? -1 : 0; + if (ExpAddrOfLevel > AttAddrOfLevel) { + assert(Att->dependerDecls_size() > 0); + for (const auto DepDecl : Att->dependerDecls()) { + /// If DepDecl is part of a count expression through a nested struct we + /// need to check whether the FAM exists in the current context. + /// Example of irrelevant dep decl (SimpleOuter::fam): + /// \code + /// struct SimpleInner { + /// int dummy; + /// int len; + /// }; + /// struct SimpleOuter { + /// struct SimpleInner hdr; + /// char fam[__counted_by(hdr.len)]; + /// }; + /// void set_len(struct SimpleInner * p) { + /// // struct SimpleInner doesn't contain any FAMs, so not checked + /// // -fbounds-safety doesn't allow taking address of + /// // SimpleOuter::hdr though, so this is never referred to by a + /// // FAM and thus safe + /// p->len = 2; + /// } + /// \endcode + if (BaseRecord && !BaseRecord->isParentStructOf(DepDecl)) + continue; + QualType DepTy = cast(DepDecl)->getType(); + auto *DCPTy = DepTy->getAs(); + if (!DCPTy) + DCPTy = DepTy->getPointeeType()->getAs(); + assert(DCPTy); + // Error has already been emitted for simply taking the address of the + // count field + if (BaseRecord && DCPTy->isIncompleteArrayType()) + continue; + Diag(RHSExp->getExprLoc(), + diag::err_bounds_safety_taking_address_dynamic_bound_dependent) + << DCPTy->getKind() << isa(VD) + << RHSExp->getSourceRange(); + return false; + } + } } - if (LHS.get()->getType()->isConstantMatrixType() || - RHS.get()->getType()->isConstantMatrixType()) { - QualType compType = - CheckMatrixElementwiseOperands(LHS, RHS, Loc, CompLHSTy); - if (CompLHSTy) - *CompLHSTy = compType; - return compType; + QualType Ty = RHSExp->getType(); + while (Ty->isPointerType()) { + Ty = Ty->getPointeeType(); + if (!Ty->isBoundsAttributedType()) + continue; + if (BaseRecord && Ty->isIncompleteArrayType()) + break; + if (const auto *DCPT = Ty->getAs()) { + Diag(RHSExp->getExprLoc(), + diag::err_bounds_safety_taking_address_dynamic_bound_pointer) + << DCPT->getKind() << DCPT->isArrayType() << RHSExp->getSourceRange(); + return false; + } else { + const auto *DRPTy = Ty->getAs(); + assert(DRPTy); + if (!DRPTy->getEndPointer()) + Diag(RHSExp->getExprLoc(), + diag::err_bounds_safety_taking_address_dynamic_bound_dependent) + << /*ended_by*/ 4 << isa(VD) << RHSExp->getSourceRange(); + else + Diag(RHSExp->getExprLoc(), + diag::err_bounds_safety_taking_address_dynamic_bound_pointer) + << /*ended_by*/ 4 << /*pointer*/ 0 << RHSExp->getSourceRange(); + return false; + } } - QualType compType = UsualArithmeticConversions( - LHS, RHS, Loc, CompLHSTy ? ACK_CompAssign : ACK_Arithmetic); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); + return checkDynamicBoundVariableEscapeInRetType(*this, RHSExp, VD, + ExpAddrOfLevel); +} - // Enforce type constraints: C99 6.5.6p3. +Sema::AssignConvertType +Sema::CheckValueTerminatedAssignmentConstraints(QualType LHSType, + Expr *RHSExpr) { + const auto *LVTT = LHSType->getAs(); + if (LVTT && LVTT->isPointerType()) { + const auto *SL = dyn_cast(RHSExpr->IgnoreParenImpCasts()); + if (!SL) { + if (const auto *PDE = + dyn_cast(RHSExpr->IgnoreParenImpCasts())) { + SL = PDE->getFunctionName(); + } + } + if (SL) { + QualType CharT = Context.getAsArrayType(SL->getType())->getElementType(); + if (Context.hasSameUnqualifiedType(LVTT->getPointeeType(), CharT)) { + return LVTT->getTerminatorValue(Context).isZero() + ? Sema::Compatible + : Sema::IncompatibleStringLiteralToValueTerminatedPointer; + } + } + } - // Handle the common case first (both operands are arithmetic). - if (!compType.isNull() && compType->isArithmeticType()) { - if (CompLHSTy) *CompLHSTy = compType; - return compType; + QualType L = LHSType; + QualType R = RHSExpr->getType(); + bool Nested = false; + while (L->isPointerType() && R->isPointerType()) { + QualType LPointee = L->getPointeeType(); + QualType RPointee = R->getPointeeType(); + const auto *LVTT = L->getAs(); + const auto *RVTT = R->getAs(); + if (LVTT && RVTT) { + if (LVTT != RVTT && + !llvm::APSInt::isSameValue(LVTT->getTerminatorValue(Context), + RVTT->getTerminatorValue(Context))) { + return Sema::IncompatibleValueTerminatedTerminators; + } + } else if (!LVTT && RVTT && L->isSafePointerType()) { + return Nested + ? Sema:: + IncompatibleNestedValueTerminatedToNonValueTerminatedPointer + : Sema::IncompatibleValueTerminatedToNonValueTerminatedPointer; + } else if (LVTT && !RVTT) { + // If the expression is a constant expression, try to redeem it by + // checking if it is terminated with the right value. + // Don't try to evaluate the terminator for nested pointers, since + // RHSExpr matches the assignment only for the first level pointers (we + // don't have an expression for nested pointers). + // Ensure that the pointees are compatible (including sugar types), + // since we return Sema::Compatible here immediately without iterating + // nested levels. + if (!Nested) { + Expr::EvalResult Evald; + bool CompatiblePointees = + (Context.hasSameUnqualifiedType(LPointee, RPointee) && + Context.canMergeInnerTypeBounds(LPointee, RPointee)) || + (LPointee->isIntegerType() && RPointee->isIntegerType() && + Context.hasSameUnqualifiedType( + Context.getCorrespondingSignedType(LPointee), + Context.getCorrespondingSignedType(RPointee))); + if (CompatiblePointees && RHSExpr->isPRValue() && + RHSExpr->tryEvaluateTerminatorElement(Evald, Context) && + Evald.Val.isInt() && + llvm::APSInt::isSameValue(LVTT->getTerminatorValue(Context), + Evald.Val.getInt())) { + return Sema::Compatible; + } + // If in C++ Safe Buffers/Bounds Safety interoperation mode, the RHS can + // be 'std::string::c_str/data': + bool isNullTerm = LVTT->getTerminatorValue(Context).isZero(); + + if (isNullTerm && isCXXSafeBuffersBoundsSafetyInteropEnabledAt( + RHSExpr->getBeginLoc())) { + if (const auto *MCE = + dyn_cast(RHSExpr->IgnoreParenImpCasts())) { + IdentifierInfo *MethodDeclII = nullptr, *ObjTypeII = nullptr; + + if (MCE->getMethodDecl()) + MethodDeclII = MCE->getMethodDecl()->getIdentifier(); + if (const auto *RecordDecl = + MCE->getObjectType()->getAsRecordDecl(); + RecordDecl && RecordDecl->isInStdNamespace()) + // If not in std namespace, let `ObjTypeII` be null so that the + // match fails: + ObjTypeII = RecordDecl->getIdentifier(); + if (MethodDeclII && ObjTypeII && + (MethodDeclII->getName() == "c_str" || + // std::string::data is also null-terminated since C++11: + MethodDeclII->getName() == "data") && + ObjTypeII->getName() == "basic_string") + return Sema::Compatible; + } + } + } + return Nested + ? Sema:: + IncompatibleNestedNonValueTerminatedToValueTerminatedPointer + : Sema:: + IncompatibleNonValueTerminatedToValueTerminatedPointer; + } + L = LPointee; + R = RPointee; + Nested = true; } - // Either ptr - int or ptr - ptr. - if (LHS.get()->getType()->isAnyPointerType()) { - QualType lpointee = LHS.get()->getType()->getPointeeType(); + return Sema::Compatible; +} - // Diagnose bad cases where we step over interface counts. - if (LHS.get()->getType()->isObjCObjectPointerType() && - checkArithmeticOnObjCPointer(*this, Loc, LHS.get())) - return QualType(); +bool Sema::isCompatibleBoundsUnsafeAssignment( + Sema::AssignConvertType ConvTy) const { + // This switch intentionally has no default case so that it emits + // a warning if a new AssignConvertType is added without being checked here. + switch (ConvTy) { + case IncompatibleIntToSafePointer: + case IncompatibleStringLiteralToValueTerminatedPointer: + case IncompatibleValueTerminatedTerminators: + case IncompatibleValueTerminatedToNonValueTerminatedPointer: + case IncompatibleNonValueTerminatedToValueTerminatedPointer: + case IncompatibleNestedValueTerminatedToNonValueTerminatedPointer: + case IncompatibleNestedNonValueTerminatedToValueTerminatedPointer: + case IncompatibleBoundsSafetyFunctionPointer: + case IncompatibleUnsafeToSafePointer: + return true; - // Arithmetic on label addresses is normally allowed, except when we add - // a ptrauth signature to the addresses. - if (isa(LHS.get()) && - getLangOpts().PointerAuthIndirectGotos) { - Diag(Loc, diag::err_ptrauth_indirect_goto_addrlabel_arithmetic) - << /*subtraction*/ 0; - return QualType(); - } + // These -fbounds-safety errors shouldn't be ignored because of ABI mismatch. + // Codegen support has not been tested. + case IncompatibleUnsafeToIndexablePointer: + case IncompleteSingleToIndexablePointer: + case IncompatibleNestedBoundsSafetyPointerAttributes: + // Non bounds-safety errors + case IncompatibleVectors: + case IntToBlockPointer: + case IncompatibleBlockPointer: + case IncompatibleObjCQualifiedId: + case IncompatibleObjCWeakRef: + case FunctionVoidPointer: + case IncompatiblePointer: + case IncompatibleFunctionPointer: + case IncompatibleFunctionPointerStrict: + case IncompatiblePointerSign: + case CompatiblePointerDiscardsQualifiers: + case IncompatiblePointerDiscardsQualifiers: + case IncompatibleNestedPointerAddressSpaceMismatch: + case IncompatibleNestedPointerQualifiers: + case PointerToInt: + case IntToPointer: + case Incompatible: + // Not errors + case CompatibleSingleToExplicitIndexablePointer: + case Compatible: + return false; + } + llvm_unreachable("Unhandled Sema::AssignConvertType"); +} - // The result type of a pointer-int computation is the pointer type. - if (RHS.get()->getType()->isIntegerType()) { - // Subtracting from a null pointer should produce a warning. - // The last argument to the diagnose call says this doesn't match the - // GNU int-to-pointer idiom. - if (LHS.get()->IgnoreParenCasts()->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNotNull)) { - // In C++ adding zero to a null pointer is defined. - Expr::EvalResult KnownVal; - if (!getLangOpts().CPlusPlus || - (!RHS.get()->isValueDependent() && - (!RHS.get()->EvaluateAsInt(KnownVal, Context) || - KnownVal.Val.getInt() != 0))) { - diagnoseArithmeticOnNullPointer(*this, Loc, LHS.get(), false); - } - } +bool Sema::allowBoundsUnsafeFunctionArg(const CallExpr *CallE, + unsigned ParamIdx) const { + const auto *Callee = CallE->getCallee(); - if (!checkArithmeticOpPointerOperand(*this, Loc, LHS.get())) - return QualType(); + QualType CalleeTy = Callee->getType(); + if (CalleeTy->isFunctionPointerType() || CalleeTy->isFunctionReferenceType()) + CalleeTy = CalleeTy->getPointeeType(); - // Check array bounds for pointer arithemtic - CheckArrayAccess(LHS.get(), RHS.get(), /*ArraySubscriptExpr*/nullptr, - /*AllowOnePastEnd*/true, /*IndexNegated*/true); + const auto *FuncTy = CalleeTy->getAs(); + if (!FuncTy) + return false; - if (CompLHSTy) *CompLHSTy = LHS.get()->getType(); - return LHS.get()->getType(); - } + return allowBoundsUnsafePointerAssignment(FuncTy->getParamType(ParamIdx), + CallE->getArg(ParamIdx), + CallE->getExprLoc()); +} - // Handle pointer-pointer subtractions. - if (const PointerType *RHSPTy - = RHS.get()->getType()->getAs()) { - QualType rpointee = RHSPTy->getPointeeType(); +bool Sema::allowBoundsUnsafePointerAssignment( + const QualType DestTy, const Expr *SourceValue, + SourceLocation AssignmentLoc) const { - if (getLangOpts().CPlusPlus) { - // Pointee types must be the same: C++ [expr.add] - if (!Context.hasSameUnqualifiedType(lpointee, rpointee)) { - diagnosePointerIncompatibility(*this, Loc, LHS.get(), RHS.get()); - } - } else { - // Pointee types must be compatible C99 6.5.6p3 - if (!Context.typesAreCompatible( - Context.getCanonicalType(lpointee).getUnqualifiedType(), - Context.getCanonicalType(rpointee).getUnqualifiedType())) { - diagnosePointerIncompatibility(*this, Loc, LHS.get(), RHS.get()); - return QualType(); - } - } + SourceValue = SourceValue->IgnoreParenImpCasts(); - if (!checkArithmeticBinOpPointerOperands(*this, Loc, - LHS.get(), RHS.get())) - return QualType(); + // Integer to pointer conversions are ABI compatible, but make no exception + // to allow them for now. This may change later if a use case is found. + auto DestPtrTy = DestTy->getAs(); + if (!DestPtrTy) + return false; + auto SourcePtrTy = SourceValue->getType()->getAs(); + if (!SourcePtrTy) + return false; - bool LHSIsNullPtr = LHS.get()->IgnoreParenCasts()->isNullPointerConstant( - Context, Expr::NPC_ValueDependentIsNotNull); - bool RHSIsNullPtr = RHS.get()->IgnoreParenCasts()->isNullPointerConstant( - Context, Expr::NPC_ValueDependentIsNotNull); + bool DestSafe = DestPtrTy->isSafePointer(); + bool SourceSafe = SourcePtrTy->isSafePointer(); + // Assignments between unsafe pointers will be allowed regardless, no need to + // make an exception. + if (!DestSafe && !SourceSafe) + return false; - // Subtracting nullptr or from nullptr is suspect - if (LHSIsNullPtr) - diagnoseSubtractionOnNullPointer(*this, Loc, LHS.get(), RHSIsNullPtr); - if (RHSIsNullPtr) - diagnoseSubtractionOnNullPointer(*this, Loc, RHS.get(), LHSIsNullPtr); + // All safe pointers ABI compatible with unsafe pointers (and each other) are + // __single pointers. Make no exception for ABI incompatible pointer type + // mismatch. + if (!DestPtrTy->isSingle() && DestSafe) + return false; + if (!SourcePtrTy->isSingle() && SourceSafe) + return false; - // The pointee type may have zero size. As an extension, a structure or - // union may have zero size or an array may have zero length. In this - // case subtraction does not make sense. - if (!rpointee->isVoidType() && !rpointee->isFunctionType()) { - CharUnits ElementSize = Context.getTypeSizeInChars(rpointee); - if (ElementSize.isZero()) { - Diag(Loc,diag::warn_sub_ptr_zero_size_types) - << rpointee.getUnqualifiedType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - } - } + return allowBoundsUnsafeAssignment(AssignmentLoc); +} - if (CompLHSTy) *CompLHSTy = LHS.get()->getType(); - return Context.getPointerDiffType(); - } - } +bool Sema::allowBoundsUnsafeAssignment(SourceLocation AssignmentLoc) const { + // System headers are excepted from respecting bounds safety rules + // since they are external to the main project (and outside of its control) + // and may not have adopted -fbounds-safety even when the main project and/or + // transitive dependencies have done so. Code in system headers without + // -fbounds-safety adoption is compiled as if -fbounds-safety was disabled. + if (!SourceMgr.isInSystemHeader(AssignmentLoc)) + return false; - return InvalidOperands(Loc, LHS, RHS); -} + // If the default pointer ABI in the header is not unsafe_indexable or + // unspecified the header has adopted -fbounds-safety and making exceptions to the + // safety rules would be counter-productive. + if (!CurPointerAbi.isUnsafeOrUnspecified()) + return false; -static bool isScopedEnumerationType(QualType T) { - if (const EnumType *ET = T->getAs()) - return ET->getDecl()->isScoped(); - return false; + return getLangOpts().BoundsSafetyRelaxedSystemHeaders; } +/* TO_UPSTREAM(BoundsSafety) OFF*/ -static void DiagnoseBadShiftValues(Sema& S, ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, BinaryOperatorKind Opc, - QualType LHSType) { - // OpenCL 6.3j: shift values are effectively % word size of LHS (more defined), - // so skip remaining warnings as we don't want to modify values within Sema. - if (S.getLangOpts().OpenCL) - return; +Sema::AssignConvertType +Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, + bool Diagnose, + bool DiagnoseCFAudited, + bool ConvertRHS) { + // We need to be able to tell the caller whether we diagnosed a problem, if + // they ask us to issue diagnostics. + assert((ConvertRHS || !Diagnose) && "can't indicate whether we diagnosed"); - if (Opc == BO_Shr && - LHS.get()->IgnoreParenImpCasts()->getType()->isBooleanType()) - S.Diag(Loc, diag::warn_shift_bool) << LHS.get()->getSourceRange(); + // If ConvertRHS is false, we want to leave the caller's RHS untouched. Sadly, + // we can't avoid *all* modifications at the moment, so we need some somewhere + // to put the updated value. + ExprResult LocalRHS = CallerRHS; + ExprResult &RHS = ConvertRHS ? CallerRHS : LocalRHS; - // Check right/shifter operand - Expr::EvalResult RHSResult; - if (RHS.get()->isValueDependent() || - !RHS.get()->EvaluateAsInt(RHSResult, S.Context)) - return; - llvm::APSInt Right = RHSResult.Val.getInt(); + if (const auto *LHSPtrType = LHSType->getAs()) { + if (const auto *RHSPtrType = RHS.get()->getType()->getAs()) { + if (RHSPtrType->getPointeeType()->hasAttr(attr::NoDeref) && + !LHSPtrType->getPointeeType()->hasAttr(attr::NoDeref)) { + Diag(RHS.get()->getExprLoc(), + diag::warn_noderef_to_dereferenceable_pointer) + << RHS.get()->getSourceRange(); + } + } + } - if (Right.isNegative()) { - S.DiagRuntimeBehavior(Loc, RHS.get(), - S.PDiag(diag::warn_shift_negative) - << RHS.get()->getSourceRange()); - return; - } + if (getLangOpts().CPlusPlus) { + if (!LHSType->isRecordType() && !LHSType->isAtomicType()) { + // C++ 5.17p3: If the left operand is not of class type, the + // expression is implicitly converted (C++ 4) to the + // cv-unqualified type of the left operand. + QualType RHSType = RHS.get()->getType(); + if (Diagnose) { + RHS = PerformImplicitConversion(RHS.get(), LHSType.getUnqualifiedType(), + AssignmentAction::Assigning); + } else { + ImplicitConversionSequence ICS = + TryImplicitConversion(RHS.get(), LHSType.getUnqualifiedType(), + /*SuppressUserConversions=*/false, + AllowedExplicit::None, + /*InOverloadResolution=*/false, + /*CStyle=*/false, + /*AllowObjCWritebackConversion=*/false); + if (ICS.isFailure()) + return Incompatible; + RHS = PerformImplicitConversion(RHS.get(), LHSType.getUnqualifiedType(), + ICS, AssignmentAction::Assigning); + } + if (RHS.isInvalid()) + return Incompatible; + Sema::AssignConvertType result = Compatible; + if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && + !ObjC().CheckObjCARCUnavailableWeakConversion(LHSType, RHSType)) + result = IncompatibleObjCWeakRef; + return result; + } - QualType LHSExprType = LHS.get()->getType(); - uint64_t LeftSize = S.Context.getTypeSize(LHSExprType); - if (LHSExprType->isBitIntType()) - LeftSize = S.Context.getIntWidth(LHSExprType); - else if (LHSExprType->isFixedPointType()) { - auto FXSema = S.Context.getFixedPointSemantics(LHSExprType); - LeftSize = FXSema.getWidth() - (unsigned)FXSema.hasUnsignedPadding(); + // FIXME: Currently, we fall through and treat C++ classes like C + // structures. + // FIXME: We also fall through for atomics; not sure what should + // happen there, though. + } else if (RHS.get()->getType() == Context.OverloadTy) { + // As a set of extensions to C, we support overloading on functions. These + // functions need to be resolved here. + DeclAccessPair DAP; + if (FunctionDecl *FD = ResolveAddressOfOverloadedFunction( + RHS.get(), LHSType, /*Complain=*/false, DAP)) + RHS = FixOverloadedFunctionReference(RHS.get(), DAP, FD); + else + return Incompatible; } - if (Right.uge(LeftSize)) { - S.DiagRuntimeBehavior(Loc, RHS.get(), - S.PDiag(diag::warn_shift_gt_typewidth) - << RHS.get()->getSourceRange()); - return; + + // This check seems unnatural, however it is necessary to ensure the proper + // conversion of functions/arrays. If the conversion were done for all + // DeclExpr's (created by ActOnIdExpression), it would mess up the unary + // expressions that suppress this implicit conversion (&, sizeof). This needs + // to happen before we check for null pointer conversions because C does not + // undergo the same implicit conversions as C++ does above (by the calls to + // TryImplicitConversion() and PerformImplicitConversion()) which insert the + // lvalue to rvalue cast before checking for null pointer constraints. This + // addresses code like: nullptr_t val; int *ptr; ptr = val; + // + // Suppress this for references: C++ 8.5.3p5. + if (!LHSType->isReferenceType()) { + // FIXME: We potentially allocate here even if ConvertRHS is false. + RHS = DefaultFunctionArrayLvalueConversion(RHS.get(), Diagnose); + if (RHS.isInvalid()) + return Incompatible; } - // FIXME: We probably need to handle fixed point types specially here. - if (Opc != BO_Shl || LHSExprType->isFixedPointType()) - return; + // The constraints are expressed in terms of the atomic, qualified, or + // unqualified type of the LHS. + QualType LHSTypeAfterConversion = LHSType.getAtomicUnqualifiedType(); - // When left shifting an ICE which is signed, we can check for overflow which - // according to C++ standards prior to C++2a has undefined behavior - // ([expr.shift] 5.8/2). Unsigned integers have defined behavior modulo one - // more than the maximum value representable in the result type, so never - // warn for those. (FIXME: Unsigned left-shift overflow in a constant - // expression is still probably a bug.) - Expr::EvalResult LHSResult; - if (LHS.get()->isValueDependent() || - LHSType->hasUnsignedIntegerRepresentation() || - !LHS.get()->EvaluateAsInt(LHSResult, S.Context)) - return; - llvm::APSInt Left = LHSResult.Val.getInt(); + // C99 6.5.16.1p1: the left operand is a pointer and the right is + // a null pointer constant or its type is nullptr_t;. + if ((LHSTypeAfterConversion->isPointerType() || + LHSTypeAfterConversion->isObjCObjectPointerType() || + LHSTypeAfterConversion->isBlockPointerType()) && + ((getLangOpts().C23 && RHS.get()->getType()->isNullPtrType()) || + RHS.get()->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNull))) { + if (Diagnose || ConvertRHS) { + CastKind Kind; + CXXCastPath Path; + CheckPointerConversion(RHS.get(), LHSType, Kind, Path, + /*IgnoreBaseAccess=*/false, Diagnose); + if (ConvertRHS) + RHS = ImpCastExprToType(RHS.get(), LHSType, Kind, VK_PRValue, &Path); + } + return Compatible; + } + // C23 6.5.16.1p1: the left operand has type atomic, qualified, or + // unqualified bool, and the right operand is a pointer or its type is + // nullptr_t. + if (getLangOpts().C23 && LHSType->isBooleanType() && + RHS.get()->getType()->isNullPtrType()) { + // NB: T* -> _Bool is handled in CheckAssignmentConstraints, this only + // only handles nullptr -> _Bool due to needing an extra conversion + // step. + // We model this by converting from nullptr -> void * and then let the + // conversion from void * -> _Bool happen naturally. + if (Diagnose || ConvertRHS) { + CastKind Kind; + CXXCastPath Path; + CheckPointerConversion(RHS.get(), Context.VoidPtrTy, Kind, Path, + /*IgnoreBaseAccess=*/false, Diagnose); + if (ConvertRHS) + RHS = ImpCastExprToType(RHS.get(), Context.VoidPtrTy, Kind, VK_PRValue, + &Path); + } + } - // Don't warn if signed overflow is defined, then all the rest of the - // diagnostics will not be triggered because the behavior is defined. - // Also don't warn in C++20 mode (and newer), as signed left shifts - // always wrap and never overflow. - if (S.getLangOpts().isSignedOverflowDefined() || S.getLangOpts().CPlusPlus20) - return; + // OpenCL queue_t type assignment. + if (LHSType->isQueueT() && RHS.get()->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNull)) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return Compatible; + } - // If LHS does not have a non-negative value then, the - // behavior is undefined before C++2a. Warn about it. - if (Left.isNegative()) { - S.DiagRuntimeBehavior(Loc, LHS.get(), - S.PDiag(diag::warn_shift_lhs_negative) - << LHS.get()->getSourceRange()); - return; + Expr *PRE = RHS.get()->IgnoreParenCasts(); + if (Diagnose && isa(PRE)) { + ObjCProtocolDecl *PDecl = cast(PRE)->getProtocol(); + if (PDecl && !PDecl->hasDefinition()) { + Diag(PRE->getExprLoc(), diag::warn_atprotocol_protocol) << PDecl; + Diag(PDecl->getLocation(), diag::note_entity_declared_at) << PDecl; + } } - llvm::APInt ResultBits = - static_cast(Right) + Left.getSignificantBits(); - if (ResultBits.ule(LeftSize)) - return; - llvm::APSInt Result = Left.extend(ResultBits.getLimitedValue()); - Result = Result.shl(Right); + Sema::AssignConvertType result = Compatible; + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety) + result = CheckValueTerminatedAssignmentConstraints(LHSType, RHS.get()); - // Print the bit representation of the signed integer as an unsigned - // hexadecimal number. - SmallString<40> HexResult; - Result.toString(HexResult, 16, /*Signed =*/false, /*Literal =*/true); + CastKind Kind = CK_NoOp; + if (result == Compatible) + /*TO_UPSTREAM(BoundsSafety) OFF*/ + result = CheckAssignmentConstraints(LHSType, RHS, Kind, ConvertRHS); - // If we are only missing a sign bit, this is less likely to result in actual - // bugs -- if the result is cast back to an unsigned type, it will have the - // expected value. Thus we place this behind a different warning that can be - // turned off separately if needed. - if (ResultBits - 1 == LeftSize) { - S.Diag(Loc, diag::warn_shift_result_sets_sign_bit) - << HexResult << LHSType - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return; + // C99 6.5.16.1p2: The value of the right operand is converted to the + // type of the assignment expression. + // CheckAssignmentConstraints allows the left-hand side to be a reference, + // so that we can use references in built-in functions even in C. + // The getNonReferenceType() call makes sure that the resulting expression + // does not have reference type. + if (result != Incompatible && RHS.get()->getType() != LHSType) { + QualType Ty = LHSType.getNonLValueExprType(Context); + Expr *E = RHS.get(); + + // Check for various Objective-C errors. If we are not reporting + // diagnostics and just checking for errors, e.g., during overload + // resolution, return Incompatible to indicate the failure. + if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() && + ObjC().CheckObjCConversion(SourceRange(), Ty, E, + CheckedConversionKind::Implicit, Diagnose, + DiagnoseCFAudited) != SemaObjC::ACR_okay) { + if (!Diagnose) + return Incompatible; + } + if (getLangOpts().ObjC && + (ObjC().CheckObjCBridgeRelatedConversions(E->getBeginLoc(), LHSType, + E->getType(), E, Diagnose) || + ObjC().CheckConversionToObjCLiteral(LHSType, E, Diagnose))) { + if (!Diagnose) + return Incompatible; + // Replace the expression with a corrected version and continue so we + // can find further errors. + RHS = E; + return Compatible; + } + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety && + isCompatibleBoundsUnsafeAssignment(result)) { + // Leave the AST intact and let the caller decide how to handle the error + if (ConvertRHS && !E->getType()->isPointerType()) + E = ImpCastExprToType(E, QualType(Ty->getUnqualifiedDesugaredType(), 0), + CK_IntegralToPointer) + .get(); + Kind = CK_BoundsSafetyPointerCast; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + if (ConvertRHS) + RHS = ImpCastExprToType(E, Ty, Kind); } - S.Diag(Loc, diag::warn_shift_result_gt_typewidth) - << HexResult.str() << Result.getSignificantBits() << LHSType - << Left.getBitWidth() << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); + return result; } -/// Return the resulting type when a vector is shifted -/// by a scalar or vector shift amount. -static QualType checkVectorShift(Sema &S, ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, bool IsCompAssign) { - // OpenCL v1.1 s6.3.j says RHS can be a vector only if LHS is a vector. - if ((S.LangOpts.OpenCL || S.LangOpts.ZVector) && - !LHS.get()->getType()->isVectorType()) { - S.Diag(Loc, diag::err_shift_rhs_only_vector) - << RHS.get()->getType() << LHS.get()->getType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return QualType(); +namespace { +/// The original operand to an operator, prior to the application of the usual +/// arithmetic conversions and converting the arguments of a builtin operator +/// candidate. +struct OriginalOperand { + explicit OriginalOperand(Expr *Op) : Orig(Op), Conversion(nullptr) { + if (auto *MTE = dyn_cast(Op)) + Op = MTE->getSubExpr(); + if (auto *BTE = dyn_cast(Op)) + Op = BTE->getSubExpr(); + if (auto *ICE = dyn_cast(Op)) { + Orig = ICE->getSubExprAsWritten(); + Conversion = ICE->getConversionFunction(); + } } - if (!IsCompAssign) { - LHS = S.UsualUnaryConversions(LHS.get()); - if (LHS.isInvalid()) return QualType(); - } + QualType getType() const { return Orig->getType(); } - RHS = S.UsualUnaryConversions(RHS.get()); - if (RHS.isInvalid()) return QualType(); + Expr *Orig; + NamedDecl *Conversion; +}; +} - QualType LHSType = LHS.get()->getType(); - // Note that LHS might be a scalar because the routine calls not only in - // OpenCL case. - const VectorType *LHSVecTy = LHSType->getAs(); - QualType LHSEleType = LHSVecTy ? LHSVecTy->getElementType() : LHSType; +QualType Sema::InvalidOperands(SourceLocation Loc, ExprResult &LHS, + ExprResult &RHS) { + OriginalOperand OrigLHS(LHS.get()), OrigRHS(RHS.get()); - // Note that RHS might not be a vector. - QualType RHSType = RHS.get()->getType(); - const VectorType *RHSVecTy = RHSType->getAs(); - QualType RHSEleType = RHSVecTy ? RHSVecTy->getElementType() : RHSType; + Diag(Loc, diag::err_typecheck_invalid_operands) + << OrigLHS.getType() << OrigRHS.getType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - // Do not allow shifts for boolean vectors. - if ((LHSVecTy && LHSVecTy->isExtVectorBoolType()) || - (RHSVecTy && RHSVecTy->isExtVectorBoolType())) { - S.Diag(Loc, diag::err_typecheck_invalid_operands) - << LHS.get()->getType() << RHS.get()->getType() - << LHS.get()->getSourceRange(); - return QualType(); + // If a user-defined conversion was applied to either of the operands prior + // to applying the built-in operator rules, tell the user about it. + if (OrigLHS.Conversion) { + Diag(OrigLHS.Conversion->getLocation(), + diag::note_typecheck_invalid_operands_converted) + << 0 << LHS.get()->getType(); } - - // The operands need to be integers. - if (!LHSEleType->isIntegerType()) { - S.Diag(Loc, diag::err_typecheck_expect_int) - << LHS.get()->getType() << LHS.get()->getSourceRange(); - return QualType(); + if (OrigRHS.Conversion) { + Diag(OrigRHS.Conversion->getLocation(), + diag::note_typecheck_invalid_operands_converted) + << 1 << RHS.get()->getType(); } - if (!RHSEleType->isIntegerType()) { - S.Diag(Loc, diag::err_typecheck_expect_int) - << RHS.get()->getType() << RHS.get()->getSourceRange(); + return QualType(); +} + +QualType Sema::InvalidLogicalVectorOperands(SourceLocation Loc, ExprResult &LHS, + ExprResult &RHS) { + QualType LHSType = LHS.get()->IgnoreImpCasts()->getType(); + QualType RHSType = RHS.get()->IgnoreImpCasts()->getType(); + + bool LHSNatVec = LHSType->isVectorType(); + bool RHSNatVec = RHSType->isVectorType(); + + if (!(LHSNatVec && RHSNatVec)) { + Expr *Vector = LHSNatVec ? LHS.get() : RHS.get(); + Expr *NonVector = !LHSNatVec ? LHS.get() : RHS.get(); + Diag(Loc, diag::err_typecheck_logical_vector_expr_gnu_cpp_restrict) + << 0 << Vector->getType() << NonVector->IgnoreImpCasts()->getType() + << Vector->getSourceRange(); return QualType(); } - if (!LHSVecTy) { - assert(RHSVecTy); - if (IsCompAssign) - return RHSType; - if (LHSEleType != RHSEleType) { - LHS = S.ImpCastExprToType(LHS.get(),RHSEleType, CK_IntegralCast); - LHSEleType = RHSEleType; - } - QualType VecTy = - S.Context.getExtVectorType(LHSEleType, RHSVecTy->getNumElements()); - LHS = S.ImpCastExprToType(LHS.get(), VecTy, CK_VectorSplat); - LHSType = VecTy; - } else if (RHSVecTy) { - // OpenCL v1.1 s6.3.j says that for vector types, the operators - // are applied component-wise. So if RHS is a vector, then ensure - // that the number of elements is the same as LHS... - if (RHSVecTy->getNumElements() != LHSVecTy->getNumElements()) { - S.Diag(Loc, diag::err_typecheck_vector_lengths_not_equal) - << LHS.get()->getType() << RHS.get()->getType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - return QualType(); + Diag(Loc, diag::err_typecheck_logical_vector_expr_gnu_cpp_restrict) + << 1 << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + + return QualType(); +} + +/// Try to convert a value of non-vector type to a vector type by converting +/// the type to the element type of the vector and then performing a splat. +/// If the language is OpenCL, we only use conversions that promote scalar +/// rank; for C, Obj-C, and C++ we allow any real scalar conversion except +/// for float->int. +/// +/// OpenCL V2.0 6.2.6.p2: +/// An error shall occur if any scalar operand type has greater rank +/// than the type of the vector element. +/// +/// \param scalar - if non-null, actually perform the conversions +/// \return true if the operation fails (but without diagnosing the failure) +static bool tryVectorConvertAndSplat(Sema &S, ExprResult *scalar, + QualType scalarTy, + QualType vectorEltTy, + QualType vectorTy, + unsigned &DiagID) { + // The conversion to apply to the scalar before splatting it, + // if necessary. + CastKind scalarCast = CK_NoOp; + + if (vectorEltTy->isBooleanType() && scalarTy->isIntegralType(S.Context)) { + scalarCast = CK_IntegralToBoolean; + } else if (vectorEltTy->isIntegralType(S.Context)) { + if (S.getLangOpts().OpenCL && (scalarTy->isRealFloatingType() || + (scalarTy->isIntegerType() && + S.Context.getIntegerTypeOrder(vectorEltTy, scalarTy) < 0))) { + DiagID = diag::err_opencl_scalar_type_rank_greater_than_vector_type; + return true; } - if (!S.LangOpts.OpenCL && !S.LangOpts.ZVector) { - const BuiltinType *LHSBT = LHSEleType->getAs(); - const BuiltinType *RHSBT = RHSEleType->getAs(); - if (LHSBT != RHSBT && - S.Context.getTypeSize(LHSBT) != S.Context.getTypeSize(RHSBT)) { - S.Diag(Loc, diag::warn_typecheck_vector_element_sizes_not_equal) - << LHS.get()->getType() << RHS.get()->getType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + if (!scalarTy->isIntegralType(S.Context)) + return true; + scalarCast = CK_IntegralCast; + } else if (vectorEltTy->isRealFloatingType()) { + if (scalarTy->isRealFloatingType()) { + if (S.getLangOpts().OpenCL && + S.Context.getFloatingTypeOrder(vectorEltTy, scalarTy) < 0) { + DiagID = diag::err_opencl_scalar_type_rank_greater_than_vector_type; + return true; } + scalarCast = CK_FloatingCast; } + else if (scalarTy->isIntegralType(S.Context)) + scalarCast = CK_IntegralToFloating; + else + return true; } else { - // ...else expand RHS to match the number of elements in LHS. - QualType VecTy = - S.Context.getExtVectorType(RHSEleType, LHSVecTy->getNumElements()); - RHS = S.ImpCastExprToType(RHS.get(), VecTy, CK_VectorSplat); + return true; } - return LHSType; + // Adjust scalar if desired. + if (scalar) { + if (scalarCast != CK_NoOp) + *scalar = S.ImpCastExprToType(scalar->get(), vectorEltTy, scalarCast); + *scalar = S.ImpCastExprToType(scalar->get(), vectorTy, CK_VectorSplat); + } + return false; } -static QualType checkSizelessVectorShift(Sema &S, ExprResult &LHS, - ExprResult &RHS, SourceLocation Loc, - bool IsCompAssign) { - if (!IsCompAssign) { - LHS = S.UsualUnaryConversions(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - } +/// Convert vector E to a vector with the same number of elements but different +/// element type. +static ExprResult convertVector(Expr *E, QualType ElementType, Sema &S) { + const auto *VecTy = E->getType()->getAs(); + assert(VecTy && "Expression E must be a vector"); + QualType NewVecTy = + VecTy->isExtVectorType() + ? S.Context.getExtVectorType(ElementType, VecTy->getNumElements()) + : S.Context.getVectorType(ElementType, VecTy->getNumElements(), + VecTy->getVectorKind()); - RHS = S.UsualUnaryConversions(RHS.get()); - if (RHS.isInvalid()) - return QualType(); + // Look through the implicit cast. Return the subexpression if its type is + // NewVecTy. + if (auto *ICE = dyn_cast(E)) + if (ICE->getSubExpr()->getType() == NewVecTy) + return ICE->getSubExpr(); - QualType LHSType = LHS.get()->getType(); - const BuiltinType *LHSBuiltinTy = LHSType->castAs(); - QualType LHSEleType = LHSType->isSveVLSBuiltinType() - ? LHSBuiltinTy->getSveEltType(S.getASTContext()) - : LHSType; + auto Cast = ElementType->isIntegerType() ? CK_IntegralCast : CK_FloatingCast; + return S.ImpCastExprToType(E, NewVecTy, Cast); +} - // Note that RHS might not be a vector - QualType RHSType = RHS.get()->getType(); - const BuiltinType *RHSBuiltinTy = RHSType->castAs(); - QualType RHSEleType = RHSType->isSveVLSBuiltinType() - ? RHSBuiltinTy->getSveEltType(S.getASTContext()) - : RHSType; +/// Test if a (constant) integer Int can be casted to another integer type +/// IntTy without losing precision. +static bool canConvertIntToOtherIntTy(Sema &S, ExprResult *Int, + QualType OtherIntTy) { + if (Int->get()->containsErrors()) + return false; - if ((LHSBuiltinTy && LHSBuiltinTy->isSVEBool()) || - (RHSBuiltinTy && RHSBuiltinTy->isSVEBool())) { - S.Diag(Loc, diag::err_typecheck_invalid_operands) - << LHSType << RHSType << LHS.get()->getSourceRange(); - return QualType(); - } + QualType IntTy = Int->get()->getType().getUnqualifiedType(); - if (!LHSEleType->isIntegerType()) { - S.Diag(Loc, diag::err_typecheck_expect_int) - << LHS.get()->getType() << LHS.get()->getSourceRange(); - return QualType(); - } + // Reject cases where the value of the Int is unknown as that would + // possibly cause truncation, but accept cases where the scalar can be + // demoted without loss of precision. + Expr::EvalResult EVResult; + bool CstInt = Int->get()->EvaluateAsInt(EVResult, S.Context); + int Order = S.Context.getIntegerTypeOrder(OtherIntTy, IntTy); + bool IntSigned = IntTy->hasSignedIntegerRepresentation(); + bool OtherIntSigned = OtherIntTy->hasSignedIntegerRepresentation(); - if (!RHSEleType->isIntegerType()) { - S.Diag(Loc, diag::err_typecheck_expect_int) - << RHS.get()->getType() << RHS.get()->getSourceRange(); - return QualType(); - } + if (CstInt) { + // If the scalar is constant and is of a higher order and has more active + // bits that the vector element type, reject it. + llvm::APSInt Result = EVResult.Val.getInt(); + unsigned NumBits = IntSigned + ? (Result.isNegative() ? Result.getSignificantBits() + : Result.getActiveBits()) + : Result.getActiveBits(); + if (Order < 0 && S.Context.getIntWidth(OtherIntTy) < NumBits) + return true; - if (LHSType->isSveVLSBuiltinType() && RHSType->isSveVLSBuiltinType() && - (S.Context.getBuiltinVectorTypeInfo(LHSBuiltinTy).EC != - S.Context.getBuiltinVectorTypeInfo(RHSBuiltinTy).EC)) { - S.Diag(Loc, diag::err_typecheck_invalid_operands) - << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); + // If the signedness of the scalar type and the vector element type + // differs and the number of bits is greater than that of the vector + // element reject it. + return (IntSigned != OtherIntSigned && + NumBits > S.Context.getIntWidth(OtherIntTy)); } - if (!LHSType->isSveVLSBuiltinType()) { - assert(RHSType->isSveVLSBuiltinType()); - if (IsCompAssign) - return RHSType; - if (LHSEleType != RHSEleType) { - LHS = S.ImpCastExprToType(LHS.get(), RHSEleType, clang::CK_IntegralCast); - LHSEleType = RHSEleType; - } - const llvm::ElementCount VecSize = - S.Context.getBuiltinVectorTypeInfo(RHSBuiltinTy).EC; - QualType VecTy = - S.Context.getScalableVectorType(LHSEleType, VecSize.getKnownMinValue()); - LHS = S.ImpCastExprToType(LHS.get(), VecTy, clang::CK_VectorSplat); - LHSType = VecTy; - } else if (RHSBuiltinTy && RHSBuiltinTy->isSveVLSBuiltinType()) { - if (S.Context.getTypeSize(RHSBuiltinTy) != - S.Context.getTypeSize(LHSBuiltinTy)) { - S.Diag(Loc, diag::err_typecheck_vector_lengths_not_equal) - << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - return QualType(); - } + // Reject cases where the value of the scalar is not constant and it's + // order is greater than that of the vector element type. + return (Order < 0); +} + +/// Test if a (constant) integer Int can be casted to floating point type +/// FloatTy without losing precision. +static bool canConvertIntTyToFloatTy(Sema &S, ExprResult *Int, + QualType FloatTy) { + if (Int->get()->containsErrors()) + return false; + + QualType IntTy = Int->get()->getType().getUnqualifiedType(); + + // Determine if the integer constant can be expressed as a floating point + // number of the appropriate type. + Expr::EvalResult EVResult; + bool CstInt = Int->get()->EvaluateAsInt(EVResult, S.Context); + + uint64_t Bits = 0; + if (CstInt) { + // Reject constants that would be truncated if they were converted to + // the floating point type. Test by simple to/from conversion. + // FIXME: Ideally the conversion to an APFloat and from an APFloat + // could be avoided if there was a convertFromAPInt method + // which could signal back if implicit truncation occurred. + llvm::APSInt Result = EVResult.Val.getInt(); + llvm::APFloat Float(S.Context.getFloatTypeSemantics(FloatTy)); + Float.convertFromAPInt(Result, IntTy->hasSignedIntegerRepresentation(), + llvm::APFloat::rmTowardZero); + llvm::APSInt ConvertBack(S.Context.getIntWidth(IntTy), + !IntTy->hasSignedIntegerRepresentation()); + bool Ignored = false; + Float.convertToInteger(ConvertBack, llvm::APFloat::rmNearestTiesToEven, + &Ignored); + if (Result != ConvertBack) + return true; } else { - const llvm::ElementCount VecSize = - S.Context.getBuiltinVectorTypeInfo(LHSBuiltinTy).EC; - if (LHSEleType != RHSEleType) { - RHS = S.ImpCastExprToType(RHS.get(), LHSEleType, clang::CK_IntegralCast); - RHSEleType = LHSEleType; - } - QualType VecTy = - S.Context.getScalableVectorType(RHSEleType, VecSize.getKnownMinValue()); - RHS = S.ImpCastExprToType(RHS.get(), VecTy, CK_VectorSplat); + // Reject types that cannot be fully encoded into the mantissa of + // the float. + Bits = S.Context.getTypeSize(IntTy); + unsigned FloatPrec = llvm::APFloat::semanticsPrecision( + S.Context.getFloatTypeSemantics(FloatTy)); + if (Bits > FloatPrec) + return true; } - return LHSType; + return false; } -// C99 6.5.7 -QualType Sema::CheckShiftOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, BinaryOperatorKind Opc, - bool IsCompAssign) { - checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); +/// Attempt to convert and splat Scalar into a vector whose types matches +/// Vector following GCC conversion rules. The rule is that implicit +/// conversion can occur when Scalar can be casted to match Vector's element +/// type without causing truncation of Scalar. +static bool tryGCCVectorConvertAndSplat(Sema &S, ExprResult *Scalar, + ExprResult *Vector) { + QualType ScalarTy = Scalar->get()->getType().getUnqualifiedType(); + QualType VectorTy = Vector->get()->getType().getUnqualifiedType(); + QualType VectorEltTy; - // Vector shifts promote their scalar inputs to vector type. - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) { - if (LangOpts.ZVector) { - // The shift operators for the z vector extensions work basically - // like general shifts, except that neither the LHS nor the RHS is - // allowed to be a "vector bool". - if (auto LHSVecType = LHS.get()->getType()->getAs()) - if (LHSVecType->getVectorKind() == VectorKind::AltiVecBool) - return InvalidOperands(Loc, LHS, RHS); - if (auto RHSVecType = RHS.get()->getType()->getAs()) - if (RHSVecType->getVectorKind() == VectorKind::AltiVecBool) - return InvalidOperands(Loc, LHS, RHS); - } - return checkVectorShift(*this, LHS, RHS, Loc, IsCompAssign); + if (const auto *VT = VectorTy->getAs()) { + assert(!isa(VT) && + "ExtVectorTypes should not be handled here!"); + VectorEltTy = VT->getElementType(); + } else if (VectorTy->isSveVLSBuiltinType()) { + VectorEltTy = + VectorTy->castAs()->getSveEltType(S.getASTContext()); + } else { + llvm_unreachable("Only Fixed-Length and SVE Vector types are handled here"); } - if (LHS.get()->getType()->isSveVLSBuiltinType() || - RHS.get()->getType()->isSveVLSBuiltinType()) - return checkSizelessVectorShift(*this, LHS, RHS, Loc, IsCompAssign); - - // Shifts don't perform usual arithmetic conversions, they just do integer - // promotions on each operand. C99 6.5.7p3 + // Reject cases where the vector element type or the scalar element type are + // not integral or floating point types. + if (!VectorEltTy->isArithmeticType() || !ScalarTy->isArithmeticType()) + return true; - // For the LHS, do usual unary conversions, but then reset them away - // if this is a compound assignment. - ExprResult OldLHS = LHS; - LHS = UsualUnaryConversions(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - QualType LHSType = LHS.get()->getType(); - if (IsCompAssign) LHS = OldLHS; + // The conversion to apply to the scalar before splatting it, + // if necessary. + CastKind ScalarCast = CK_NoOp; - // The RHS is simpler. - RHS = UsualUnaryConversions(RHS.get()); - if (RHS.isInvalid()) - return QualType(); - QualType RHSType = RHS.get()->getType(); + // Accept cases where the vector elements are integers and the scalar is + // an integer. + // FIXME: Notionally if the scalar was a floating point value with a precise + // integral representation, we could cast it to an appropriate integer + // type and then perform the rest of the checks here. GCC will perform + // this conversion in some cases as determined by the input language. + // We should accept it on a language independent basis. + if (VectorEltTy->isIntegralType(S.Context) && + ScalarTy->isIntegralType(S.Context) && + S.Context.getIntegerTypeOrder(VectorEltTy, ScalarTy)) { - // C99 6.5.7p2: Each of the operands shall have integer type. - // Embedded-C 4.1.6.2.2: The LHS may also be fixed-point. - if ((!LHSType->isFixedPointOrIntegerType() && - !LHSType->hasIntegerRepresentation()) || - !RHSType->hasIntegerRepresentation()) - return InvalidOperands(Loc, LHS, RHS); + if (canConvertIntToOtherIntTy(S, Scalar, VectorEltTy)) + return true; - // C++0x: Don't allow scoped enums. FIXME: Use something better than - // hasIntegerRepresentation() above instead of this. - if (isScopedEnumerationType(LHSType) || - isScopedEnumerationType(RHSType)) { - return InvalidOperands(Loc, LHS, RHS); - } - DiagnoseBadShiftValues(*this, LHS, RHS, Loc, Opc, LHSType); + ScalarCast = CK_IntegralCast; + } else if (VectorEltTy->isIntegralType(S.Context) && + ScalarTy->isRealFloatingType()) { + if (S.Context.getTypeSize(VectorEltTy) == S.Context.getTypeSize(ScalarTy)) + ScalarCast = CK_FloatingToIntegral; + else + return true; + } else if (VectorEltTy->isRealFloatingType()) { + if (ScalarTy->isRealFloatingType()) { - // "The type of the result is that of the promoted left operand." - return LHSType; -} + // Reject cases where the scalar type is not a constant and has a higher + // Order than the vector element type. + llvm::APFloat Result(0.0); -/// Diagnose bad pointer comparisons. -static void diagnoseDistinctPointerComparison(Sema &S, SourceLocation Loc, - ExprResult &LHS, ExprResult &RHS, - bool IsError) { - S.Diag(Loc, IsError ? diag::err_typecheck_comparison_of_distinct_pointers - : diag::ext_typecheck_comparison_of_distinct_pointers) - << LHS.get()->getType() << RHS.get()->getType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); -} + // Determine whether this is a constant scalar. In the event that the + // value is dependent (and thus cannot be evaluated by the constant + // evaluator), skip the evaluation. This will then diagnose once the + // expression is instantiated. + bool CstScalar = Scalar->get()->isValueDependent() || + Scalar->get()->EvaluateAsFloat(Result, S.Context); + int Order = S.Context.getFloatingTypeOrder(VectorEltTy, ScalarTy); + if (!CstScalar && Order < 0) + return true; -/// Returns false if the pointers are converted to a composite type, -/// true otherwise. -static bool convertPointersToCompositeType(Sema &S, SourceLocation Loc, - ExprResult &LHS, ExprResult &RHS) { - // C++ [expr.rel]p2: - // [...] Pointer conversions (4.10) and qualification - // conversions (4.4) are performed on pointer operands (or on - // a pointer operand and a null pointer constant) to bring - // them to their composite pointer type. [...] - // - // C++ [expr.eq]p1 uses the same notion for (in)equality - // comparisons of pointers. + // If the scalar cannot be safely casted to the vector element type, + // reject it. + if (CstScalar) { + bool Truncated = false; + Result.convert(S.Context.getFloatTypeSemantics(VectorEltTy), + llvm::APFloat::rmNearestTiesToEven, &Truncated); + if (Truncated) + return true; + } - QualType LHSType = LHS.get()->getType(); - QualType RHSType = RHS.get()->getType(); - assert(LHSType->isPointerType() || RHSType->isPointerType() || - LHSType->isMemberPointerType() || RHSType->isMemberPointerType()); + ScalarCast = CK_FloatingCast; + } else if (ScalarTy->isIntegralType(S.Context)) { + if (canConvertIntTyToFloatTy(S, Scalar, VectorEltTy)) + return true; - QualType T = S.FindCompositePointerType(Loc, LHS, RHS); - if (T.isNull()) { - if ((LHSType->isAnyPointerType() || LHSType->isMemberPointerType()) && - (RHSType->isAnyPointerType() || RHSType->isMemberPointerType())) - diagnoseDistinctPointerComparison(S, Loc, LHS, RHS, /*isError*/true); - else - S.InvalidOperands(Loc, LHS, RHS); + ScalarCast = CK_IntegralToFloating; + } else + return true; + } else if (ScalarTy->isEnumeralType()) return true; - } + // Adjust scalar if desired. + if (ScalarCast != CK_NoOp) + *Scalar = S.ImpCastExprToType(Scalar->get(), VectorEltTy, ScalarCast); + *Scalar = S.ImpCastExprToType(Scalar->get(), VectorTy, CK_VectorSplat); return false; } -static void diagnoseFunctionPointerToVoidComparison(Sema &S, SourceLocation Loc, - ExprResult &LHS, - ExprResult &RHS, - bool IsError) { - S.Diag(Loc, IsError ? diag::err_typecheck_comparison_of_fptr_to_void - : diag::ext_typecheck_comparison_of_fptr_to_void) - << LHS.get()->getType() << RHS.get()->getType() - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); -} - -static bool isObjCObjectLiteral(ExprResult &E) { - switch (E.get()->IgnoreParenImpCasts()->getStmtClass()) { - case Stmt::ObjCArrayLiteralClass: - case Stmt::ObjCDictionaryLiteralClass: - case Stmt::ObjCStringLiteralClass: - case Stmt::ObjCBoxedExprClass: - return true; - default: - // Note that ObjCBoolLiteral is NOT an object literal! - return false; +QualType Sema::CheckVectorOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, bool IsCompAssign, + bool AllowBothBool, + bool AllowBoolConversions, + bool AllowBoolOperation, + bool ReportInvalid) { + if (!IsCompAssign) { + LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); } -} + RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); -static bool hasIsEqualMethod(Sema &S, const Expr *LHS, const Expr *RHS) { - const ObjCObjectPointerType *Type = - LHS->getType()->getAs(); - - // If this is not actually an Objective-C object, bail out. - if (!Type) - return false; + // For conversion purposes, we ignore any qualifiers. + // For example, "const float" and "float" are equivalent. + QualType LHSType = LHS.get()->getType().getUnqualifiedType(); + QualType RHSType = RHS.get()->getType().getUnqualifiedType(); - // Get the LHS object's interface type. - QualType InterfaceType = Type->getPointeeType(); + const VectorType *LHSVecType = LHSType->getAs(); + const VectorType *RHSVecType = RHSType->getAs(); + assert(LHSVecType || RHSVecType); - // If the RHS isn't an Objective-C object, bail out. - if (!RHS->getType()->isObjCObjectPointerType()) - return false; + if (getLangOpts().HLSL) + return HLSL().handleVectorBinOpConversion(LHS, RHS, LHSType, RHSType, + IsCompAssign); - // Try to find the -isEqual: method. - Selector IsEqualSel = S.ObjC().NSAPIObj->getIsEqualSelector(); - ObjCMethodDecl *Method = - S.ObjC().LookupMethodInObjectType(IsEqualSel, InterfaceType, - /*IsInstance=*/true); - if (!Method) { - if (Type->isObjCIdType()) { - // For 'id', just check the global pool. - Method = - S.ObjC().LookupInstanceMethodInGlobalPool(IsEqualSel, SourceRange(), - /*receiverId=*/true); - } else { - // Check protocols. - Method = S.ObjC().LookupMethodInQualifiedType(IsEqualSel, Type, - /*IsInstance=*/true); - } - } + // Any operation with MFloat8 type is only possible with C intrinsics + if ((LHSVecType && LHSVecType->getElementType()->isMFloat8Type()) || + (RHSVecType && RHSVecType->getElementType()->isMFloat8Type())) + return InvalidOperands(Loc, LHS, RHS); - if (!Method) - return false; + // AltiVec-style "vector bool op vector bool" combinations are allowed + // for some operators but not others. + if (!AllowBothBool && LHSVecType && + LHSVecType->getVectorKind() == VectorKind::AltiVecBool && RHSVecType && + RHSVecType->getVectorKind() == VectorKind::AltiVecBool) + return ReportInvalid ? InvalidOperands(Loc, LHS, RHS) : QualType(); - QualType T = Method->parameters()[0]->getType(); - if (!T->isObjCObjectPointerType()) - return false; + // This operation may not be performed on boolean vectors. + if (!AllowBoolOperation && + (LHSType->isExtVectorBoolType() || RHSType->isExtVectorBoolType())) + return ReportInvalid ? InvalidOperands(Loc, LHS, RHS) : QualType(); - QualType R = Method->getReturnType(); - if (!R->isScalarType()) - return false; + // If the vector types are identical, return. + if (Context.hasSameType(LHSType, RHSType)) + return Context.getCommonSugaredType(LHSType, RHSType); - return true; -} + // If we have compatible AltiVec and GCC vector types, use the AltiVec type. + if (LHSVecType && RHSVecType && + Context.areCompatibleVectorTypes(LHSType, RHSType)) { + if (isa(LHSVecType)) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); + return LHSType; + } -static void diagnoseObjCLiteralComparison(Sema &S, SourceLocation Loc, - ExprResult &LHS, ExprResult &RHS, - BinaryOperator::Opcode Opc){ - Expr *Literal; - Expr *Other; - if (isObjCObjectLiteral(LHS)) { - Literal = LHS.get(); - Other = RHS.get(); - } else { - Literal = RHS.get(); - Other = LHS.get(); + if (!IsCompAssign) + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); + return RHSType; } - // Don't warn on comparisons against nil. - Other = Other->IgnoreParenCasts(); - if (Other->isNullPointerConstant(S.getASTContext(), - Expr::NPC_ValueDependentIsNotNull)) - return; - - // This should be kept in sync with warn_objc_literal_comparison. - // LK_String should always be after the other literals, since it has its own - // warning flag. - SemaObjC::ObjCLiteralKind LiteralKind = S.ObjC().CheckLiteralKind(Literal); - assert(LiteralKind != SemaObjC::LK_Block); - if (LiteralKind == SemaObjC::LK_None) { - llvm_unreachable("Unknown Objective-C object literal kind"); + // AllowBoolConversions says that bool and non-bool AltiVec vectors + // can be mixed, with the result being the non-bool type. The non-bool + // operand must have integer element type. + if (AllowBoolConversions && LHSVecType && RHSVecType && + LHSVecType->getNumElements() == RHSVecType->getNumElements() && + (Context.getTypeSize(LHSVecType->getElementType()) == + Context.getTypeSize(RHSVecType->getElementType()))) { + if (LHSVecType->getVectorKind() == VectorKind::AltiVecVector && + LHSVecType->getElementType()->isIntegerType() && + RHSVecType->getVectorKind() == VectorKind::AltiVecBool) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); + return LHSType; + } + if (!IsCompAssign && + LHSVecType->getVectorKind() == VectorKind::AltiVecBool && + RHSVecType->getVectorKind() == VectorKind::AltiVecVector && + RHSVecType->getElementType()->isIntegerType()) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); + return RHSType; + } } - if (LiteralKind == SemaObjC::LK_String) - S.Diag(Loc, diag::warn_objc_string_literal_comparison) - << Literal->getSourceRange(); - else - S.Diag(Loc, diag::warn_objc_literal_comparison) - << LiteralKind << Literal->getSourceRange(); + // Expressions containing fixed-length and sizeless SVE/RVV vectors are + // invalid since the ambiguity can affect the ABI. + auto IsSveRVVConversion = [](QualType FirstType, QualType SecondType, + unsigned &SVEorRVV) { + const VectorType *VecType = SecondType->getAs(); + SVEorRVV = 0; + if (FirstType->isSizelessBuiltinType() && VecType) { + if (VecType->getVectorKind() == VectorKind::SveFixedLengthData || + VecType->getVectorKind() == VectorKind::SveFixedLengthPredicate) + return true; + if (VecType->getVectorKind() == VectorKind::RVVFixedLengthData || + VecType->getVectorKind() == VectorKind::RVVFixedLengthMask || + VecType->getVectorKind() == VectorKind::RVVFixedLengthMask_1 || + VecType->getVectorKind() == VectorKind::RVVFixedLengthMask_2 || + VecType->getVectorKind() == VectorKind::RVVFixedLengthMask_4) { + SVEorRVV = 1; + return true; + } + } - if (BinaryOperator::isEqualityOp(Opc) && - hasIsEqualMethod(S, LHS.get(), RHS.get())) { - SourceLocation Start = LHS.get()->getBeginLoc(); - SourceLocation End = S.getLocForEndOfToken(RHS.get()->getEndLoc()); - CharSourceRange OpRange = - CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); + return false; + }; - S.Diag(Loc, diag::note_objc_literal_comparison_isequal) - << FixItHint::CreateInsertion(Start, Opc == BO_EQ ? "[" : "![") - << FixItHint::CreateReplacement(OpRange, " isEqual:") - << FixItHint::CreateInsertion(End, "]"); + unsigned SVEorRVV; + if (IsSveRVVConversion(LHSType, RHSType, SVEorRVV) || + IsSveRVVConversion(RHSType, LHSType, SVEorRVV)) { + Diag(Loc, diag::err_typecheck_sve_rvv_ambiguous) + << SVEorRVV << LHSType << RHSType; + return QualType(); } -} - -/// Warns on !x < y, !x & y where !(x < y), !(x & y) was probably intended. -static void diagnoseLogicalNotOnLHSofCheck(Sema &S, ExprResult &LHS, - ExprResult &RHS, SourceLocation Loc, - BinaryOperatorKind Opc) { - // Check that left hand side is !something. - UnaryOperator *UO = dyn_cast(LHS.get()->IgnoreImpCasts()); - if (!UO || UO->getOpcode() != UO_LNot) return; - - // Only check if the right hand side is non-bool arithmetic type. - if (RHS.get()->isKnownToHaveBooleanValue()) return; - - // Make sure that the something in !something is not bool. - Expr *SubExpr = UO->getSubExpr()->IgnoreImpCasts(); - if (SubExpr->isKnownToHaveBooleanValue()) return; - // Emit warning. - bool IsBitwiseOp = Opc == BO_And || Opc == BO_Or || Opc == BO_Xor; - S.Diag(UO->getOperatorLoc(), diag::warn_logical_not_on_lhs_of_check) - << Loc << IsBitwiseOp; + // Expressions containing GNU and SVE or RVV (fixed or sizeless) vectors are + // invalid since the ambiguity can affect the ABI. + auto IsSveRVVGnuConversion = [](QualType FirstType, QualType SecondType, + unsigned &SVEorRVV) { + const VectorType *FirstVecType = FirstType->getAs(); + const VectorType *SecondVecType = SecondType->getAs(); - // First note suggest !(x < y) - SourceLocation FirstOpen = SubExpr->getBeginLoc(); - SourceLocation FirstClose = RHS.get()->getEndLoc(); - FirstClose = S.getLocForEndOfToken(FirstClose); - if (FirstClose.isInvalid()) - FirstOpen = SourceLocation(); - S.Diag(UO->getOperatorLoc(), diag::note_logical_not_fix) - << IsBitwiseOp - << FixItHint::CreateInsertion(FirstOpen, "(") - << FixItHint::CreateInsertion(FirstClose, ")"); + SVEorRVV = 0; + if (FirstVecType && SecondVecType) { + if (FirstVecType->getVectorKind() == VectorKind::Generic) { + if (SecondVecType->getVectorKind() == VectorKind::SveFixedLengthData || + SecondVecType->getVectorKind() == + VectorKind::SveFixedLengthPredicate) + return true; + if (SecondVecType->getVectorKind() == VectorKind::RVVFixedLengthData || + SecondVecType->getVectorKind() == VectorKind::RVVFixedLengthMask || + SecondVecType->getVectorKind() == + VectorKind::RVVFixedLengthMask_1 || + SecondVecType->getVectorKind() == + VectorKind::RVVFixedLengthMask_2 || + SecondVecType->getVectorKind() == + VectorKind::RVVFixedLengthMask_4) { + SVEorRVV = 1; + return true; + } + } + return false; + } - // Second note suggests (!x) < y - SourceLocation SecondOpen = LHS.get()->getBeginLoc(); - SourceLocation SecondClose = LHS.get()->getEndLoc(); - SecondClose = S.getLocForEndOfToken(SecondClose); - if (SecondClose.isInvalid()) - SecondOpen = SourceLocation(); - S.Diag(UO->getOperatorLoc(), diag::note_logical_not_silence_with_parens) - << FixItHint::CreateInsertion(SecondOpen, "(") - << FixItHint::CreateInsertion(SecondClose, ")"); -} + if (SecondVecType && + SecondVecType->getVectorKind() == VectorKind::Generic) { + if (FirstType->isSVESizelessBuiltinType()) + return true; + if (FirstType->isRVVSizelessBuiltinType()) { + SVEorRVV = 1; + return true; + } + } -// Returns true if E refers to a non-weak array. -static bool checkForArray(const Expr *E) { - const ValueDecl *D = nullptr; - if (const DeclRefExpr *DR = dyn_cast(E)) { - D = DR->getDecl(); - } else if (const MemberExpr *Mem = dyn_cast(E)) { - if (Mem->isImplicitAccess()) - D = Mem->getMemberDecl(); - } - if (!D) return false; - return D->getType()->isArrayType() && !D->isWeak(); -} + }; -/// Detect patterns ptr + size >= ptr and ptr + size < ptr, where ptr is a -/// pointer and size is an unsigned integer. Return whether the result is -/// always true/false. -static std::optional isTautologicalBoundsCheck(Sema &S, const Expr *LHS, - const Expr *RHS, - BinaryOperatorKind Opc) { - if (!LHS->getType()->isPointerType() || - S.getLangOpts().PointerOverflowDefined) - return std::nullopt; + if (IsSveRVVGnuConversion(LHSType, RHSType, SVEorRVV) || + IsSveRVVGnuConversion(RHSType, LHSType, SVEorRVV)) { + Diag(Loc, diag::err_typecheck_sve_rvv_gnu_ambiguous) + << SVEorRVV << LHSType << RHSType; + return QualType(); + } - // Canonicalize to >= or < predicate. - switch (Opc) { - case BO_GE: - case BO_LT: - break; - case BO_GT: - std::swap(LHS, RHS); - Opc = BO_LT; - break; - case BO_LE: - std::swap(LHS, RHS); - Opc = BO_GE; - break; - default: - return std::nullopt; + // If there's a vector type and a scalar, try to convert the scalar to + // the vector element type and splat. + unsigned DiagID = diag::err_typecheck_vector_not_convertable; + if (!RHSVecType) { + if (isa(LHSVecType)) { + if (!tryVectorConvertAndSplat(*this, &RHS, RHSType, + LHSVecType->getElementType(), LHSType, + DiagID)) + return LHSType; + } else { + if (!tryGCCVectorConvertAndSplat(*this, &RHS, &LHS)) + return LHSType; + } + } + if (!LHSVecType) { + if (isa(RHSVecType)) { + if (!tryVectorConvertAndSplat(*this, (IsCompAssign ? nullptr : &LHS), + LHSType, RHSVecType->getElementType(), + RHSType, DiagID)) + return RHSType; + } else { + if (LHS.get()->isLValue() || + !tryGCCVectorConvertAndSplat(*this, &LHS, &RHS)) + return RHSType; + } } - auto *BO = dyn_cast(LHS); - if (!BO || BO->getOpcode() != BO_Add) - return std::nullopt; + // FIXME: The code below also handles conversion between vectors and + // non-scalars, we should break this down into fine grained specific checks + // and emit proper diagnostics. + QualType VecType = LHSVecType ? LHSType : RHSType; + const VectorType *VT = LHSVecType ? LHSVecType : RHSVecType; + QualType OtherType = LHSVecType ? RHSType : LHSType; + ExprResult *OtherExpr = LHSVecType ? &RHS : &LHS; + if (isLaxVectorConversion(OtherType, VecType)) { + if (Context.getTargetInfo().getTriple().isPPC() && + anyAltivecTypes(RHSType, LHSType) && + !Context.areCompatibleVectorTypes(RHSType, LHSType)) + Diag(Loc, diag::warn_deprecated_lax_vec_conv_all) << RHSType << LHSType; + // If we're allowing lax vector conversions, only the total (data) size + // needs to be the same. For non compound assignment, if one of the types is + // scalar, the result is always the vector type. + if (!IsCompAssign) { + *OtherExpr = ImpCastExprToType(OtherExpr->get(), VecType, CK_BitCast); + return VecType; + // In a compound assignment, lhs += rhs, 'lhs' is a lvalue src, forbidding + // any implicit cast. Here, the 'rhs' should be implicit casted to 'lhs' + // type. Note that this is already done by non-compound assignments in + // CheckAssignmentConstraints. If it's a scalar type, only bitcast for + // <1 x T> -> T. The result is also a vector type. + } else if (OtherType->isExtVectorType() || OtherType->isVectorType() || + (OtherType->isScalarType() && VT->getNumElements() == 1)) { + ExprResult *RHSExpr = &RHS; + *RHSExpr = ImpCastExprToType(RHSExpr->get(), LHSType, CK_BitCast); + return VecType; + } + } - Expr *Other; - if (Expr::isSameComparisonOperand(BO->getLHS(), RHS)) - Other = BO->getRHS(); - else if (Expr::isSameComparisonOperand(BO->getRHS(), RHS)) - Other = BO->getLHS(); - else - return std::nullopt; + // Okay, the expression is invalid. - if (!Other->getType()->isUnsignedIntegerType()) - return std::nullopt; + // If there's a non-vector, non-real operand, diagnose that. + if ((!RHSVecType && !RHSType->isRealType()) || + (!LHSVecType && !LHSType->isRealType())) { + Diag(Loc, diag::err_typecheck_vector_not_convertable_non_scalar) + << LHSType << RHSType + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); + } - return Opc == BO_GE; + // OpenCL V1.1 6.2.6.p1: + // If the operands are of more than one vector type, then an error shall + // occur. Implicit conversions between vector types are not permitted, per + // section 6.2.1. + if (getLangOpts().OpenCL && + RHSVecType && isa(RHSVecType) && + LHSVecType && isa(LHSVecType)) { + Diag(Loc, diag::err_opencl_implicit_vector_conversion) << LHSType + << RHSType; + return QualType(); + } + + + // If there is a vector type that is not a ExtVector and a scalar, we reach + // this point if scalar could not be converted to the vector's element type + // without truncation. + if ((RHSVecType && !isa(RHSVecType)) || + (LHSVecType && !isa(LHSVecType))) { + QualType Scalar = LHSVecType ? RHSType : LHSType; + QualType Vector = LHSVecType ? LHSType : RHSType; + unsigned ScalarOrVector = LHSVecType && RHSVecType ? 1 : 0; + Diag(Loc, + diag::err_typecheck_vector_not_convertable_implict_truncation) + << ScalarOrVector << Scalar << Vector; + + return QualType(); + } + + // Otherwise, use the generic diagnostic. + Diag(Loc, DiagID) + << LHSType << RHSType + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); } -/// Diagnose some forms of syntactically-obvious tautological comparison. -static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc, - Expr *LHS, Expr *RHS, - BinaryOperatorKind Opc) { - Expr *LHSStripped = LHS->IgnoreParenImpCasts(); - Expr *RHSStripped = RHS->IgnoreParenImpCasts(); +QualType Sema::CheckSizelessVectorOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + bool IsCompAssign, + ArithConvKind OperationKind) { + if (!IsCompAssign) { + LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + } + RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); - QualType LHSType = LHS->getType(); - QualType RHSType = RHS->getType(); - if (LHSType->hasFloatingRepresentation() || - (LHSType->isBlockPointerType() && !BinaryOperator::isEqualityOp(Opc)) || - S.inTemplateInstantiation()) - return; + QualType LHSType = LHS.get()->getType().getUnqualifiedType(); + QualType RHSType = RHS.get()->getType().getUnqualifiedType(); - // WebAssembly Tables cannot be compared, therefore shouldn't emit - // Tautological diagnostics. - if (LHSType->isWebAssemblyTableType() || RHSType->isWebAssemblyTableType()) - return; + const BuiltinType *LHSBuiltinTy = LHSType->getAs(); + const BuiltinType *RHSBuiltinTy = RHSType->getAs(); - // Comparisons between two array types are ill-formed for operator<=>, so - // we shouldn't emit any additional warnings about it. - if (Opc == BO_Cmp && LHSType->isArrayType() && RHSType->isArrayType()) - return; + unsigned DiagID = diag::err_typecheck_invalid_operands; + if ((OperationKind == ACK_Arithmetic) && + ((LHSBuiltinTy && LHSBuiltinTy->isSVEBool()) || + (RHSBuiltinTy && RHSBuiltinTy->isSVEBool()))) { + Diag(Loc, DiagID) << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } - // For non-floating point types, check for self-comparisons of the form - // x == x, x != x, x < x, etc. These always evaluate to a constant, and - // often indicate logic errors in the program. - // - // NOTE: Don't warn about comparison expressions resulting from macro - // expansion. Also don't warn about comparisons which are only self - // comparisons within a template instantiation. The warnings should catch - // obvious cases in the definition of the template anyways. The idea is to - // warn when the typed comparison operator will always evaluate to the same - // result. + if (Context.hasSameType(LHSType, RHSType)) + return LHSType; - // Used for indexing into %select in warn_comparison_always - enum { - AlwaysConstant, - AlwaysTrue, - AlwaysFalse, - AlwaysEqual, // std::strong_ordering::equal from operator<=> - }; + if (LHSType->isSveVLSBuiltinType() && !RHSType->isSveVLSBuiltinType()) { + if (!tryGCCVectorConvertAndSplat(*this, &RHS, &LHS)) + return LHSType; + } + if (RHSType->isSveVLSBuiltinType() && !LHSType->isSveVLSBuiltinType()) { + if (LHS.get()->isLValue() || + !tryGCCVectorConvertAndSplat(*this, &LHS, &RHS)) + return RHSType; + } - // C++1a [array.comp]: - // Equality and relational comparisons ([expr.eq], [expr.rel]) between two - // operands of array type. - // C++2a [depr.array.comp]: - // Equality and relational comparisons ([expr.eq], [expr.rel]) between two - // operands of array type are deprecated. - if (S.getLangOpts().CPlusPlus && LHSStripped->getType()->isArrayType() && - RHSStripped->getType()->isArrayType()) { - auto IsDeprArrayComparionIgnored = - S.getDiagnostics().isIgnored(diag::warn_depr_array_comparison, Loc); - auto DiagID = S.getLangOpts().CPlusPlus26 - ? diag::warn_array_comparison_cxx26 - : !S.getLangOpts().CPlusPlus20 || IsDeprArrayComparionIgnored - ? diag::warn_array_comparison - : diag::warn_depr_array_comparison; - S.Diag(Loc, DiagID) << LHS->getSourceRange() << RHS->getSourceRange() - << LHSStripped->getType() << RHSStripped->getType(); - // Carry on to produce the tautological comparison warning, if this - // expression is potentially-evaluated, we can resolve the array to a - // non-weak declaration, and so on. + if ((!LHSType->isSveVLSBuiltinType() && !LHSType->isRealType()) || + (!RHSType->isSveVLSBuiltinType() && !RHSType->isRealType())) { + Diag(Loc, diag::err_typecheck_vector_not_convertable_non_scalar) + << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); } - if (!LHS->getBeginLoc().isMacroID() && !RHS->getBeginLoc().isMacroID()) { - if (Expr::isSameComparisonOperand(LHS, RHS)) { - unsigned Result; - switch (Opc) { - case BO_EQ: - case BO_LE: - case BO_GE: - Result = AlwaysTrue; - break; - case BO_NE: - case BO_LT: - case BO_GT: - Result = AlwaysFalse; - break; - case BO_Cmp: - Result = AlwaysEqual; - break; - default: - Result = AlwaysConstant; - break; - } - S.DiagRuntimeBehavior(Loc, nullptr, - S.PDiag(diag::warn_comparison_always) - << 0 /*self-comparison*/ - << Result); - } else if (checkForArray(LHSStripped) && checkForArray(RHSStripped)) { - // What is it always going to evaluate to? - unsigned Result; - switch (Opc) { - case BO_EQ: // e.g. array1 == array2 - Result = AlwaysFalse; - break; - case BO_NE: // e.g. array1 != array2 - Result = AlwaysTrue; - break; - default: // e.g. array1 <= array2 - // The best we can say is 'a constant' - Result = AlwaysConstant; - break; - } - S.DiagRuntimeBehavior(Loc, nullptr, - S.PDiag(diag::warn_comparison_always) - << 1 /*array comparison*/ - << Result); - } else if (std::optional Res = - isTautologicalBoundsCheck(S, LHS, RHS, Opc)) { - S.DiagRuntimeBehavior(Loc, nullptr, - S.PDiag(diag::warn_comparison_always) - << 2 /*pointer comparison*/ - << (*Res ? AlwaysTrue : AlwaysFalse)); - } + if (LHSType->isSveVLSBuiltinType() && RHSType->isSveVLSBuiltinType() && + Context.getBuiltinVectorTypeInfo(LHSBuiltinTy).EC != + Context.getBuiltinVectorTypeInfo(RHSBuiltinTy).EC) { + Diag(Loc, diag::err_typecheck_vector_lengths_not_equal) + << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); } - if (isa(LHSStripped)) - LHSStripped = LHSStripped->IgnoreParenCasts(); - if (isa(RHSStripped)) - RHSStripped = RHSStripped->IgnoreParenCasts(); + if (LHSType->isSveVLSBuiltinType() || RHSType->isSveVLSBuiltinType()) { + QualType Scalar = LHSType->isSveVLSBuiltinType() ? RHSType : LHSType; + QualType Vector = LHSType->isSveVLSBuiltinType() ? LHSType : RHSType; + bool ScalarOrVector = + LHSType->isSveVLSBuiltinType() && RHSType->isSveVLSBuiltinType(); - // Warn about comparisons against a string constant (unless the other - // operand is null); the user probably wants string comparison function. - Expr *LiteralString = nullptr; - Expr *LiteralStringStripped = nullptr; - if ((isa(LHSStripped) || isa(LHSStripped)) && - !RHSStripped->isNullPointerConstant(S.Context, - Expr::NPC_ValueDependentIsNull)) { - LiteralString = LHS; - LiteralStringStripped = LHSStripped; - } else if ((isa(RHSStripped) || - isa(RHSStripped)) && - !LHSStripped->isNullPointerConstant(S.Context, - Expr::NPC_ValueDependentIsNull)) { - LiteralString = RHS; - LiteralStringStripped = RHSStripped; - } + Diag(Loc, diag::err_typecheck_vector_not_convertable_implict_truncation) + << ScalarOrVector << Scalar << Vector; - if (LiteralString) { - S.DiagRuntimeBehavior(Loc, nullptr, - S.PDiag(diag::warn_stringcompare) - << isa(LiteralStringStripped) - << LiteralString->getSourceRange()); + return QualType(); } -} -static ImplicitConversionKind castKindToImplicitConversionKind(CastKind CK) { - switch (CK) { - default: { -#ifndef NDEBUG - llvm::errs() << "unhandled cast kind: " << CastExpr::getCastKindName(CK) - << "\n"; -#endif - llvm_unreachable("unhandled cast kind"); - } - case CK_UserDefinedConversion: - return ICK_Identity; - case CK_LValueToRValue: - return ICK_Lvalue_To_Rvalue; - case CK_ArrayToPointerDecay: - return ICK_Array_To_Pointer; - case CK_FunctionToPointerDecay: - return ICK_Function_To_Pointer; - case CK_IntegralCast: - return ICK_Integral_Conversion; - case CK_FloatingCast: - return ICK_Floating_Conversion; - case CK_IntegralToFloating: - case CK_FloatingToIntegral: - return ICK_Floating_Integral; - case CK_IntegralComplexCast: - case CK_FloatingComplexCast: - case CK_FloatingComplexToIntegralComplex: - case CK_IntegralComplexToFloatingComplex: - return ICK_Complex_Conversion; - case CK_FloatingComplexToReal: - case CK_FloatingRealToComplex: - case CK_IntegralComplexToReal: - case CK_IntegralRealToComplex: - return ICK_Complex_Real; - case CK_HLSLArrayRValue: - return ICK_HLSL_Array_RValue; - } + Diag(Loc, DiagID) << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); } -static bool checkThreeWayNarrowingConversion(Sema &S, QualType ToType, Expr *E, - QualType FromType, - SourceLocation Loc) { - // Check for a narrowing implicit conversion. - StandardConversionSequence SCS; - SCS.setAsIdentityConversion(); - SCS.setToType(0, FromType); - SCS.setToType(1, ToType); - if (const auto *ICE = dyn_cast(E)) - SCS.Second = castKindToImplicitConversionKind(ICE->getCastKind()); +// checkArithmeticNull - Detect when a NULL constant is used improperly in an +// expression. These are mainly cases where the null pointer is used as an +// integer instead of a pointer. +static void checkArithmeticNull(Sema &S, ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, bool IsCompare) { + // The canonical way to check for a GNU null is with isNullPointerConstant, + // but we use a bit of a hack here for speed; this is a relatively + // hot path, and isNullPointerConstant is slow. + bool LHSNull = isa(LHS.get()->IgnoreParenImpCasts()); + bool RHSNull = isa(RHS.get()->IgnoreParenImpCasts()); - APValue PreNarrowingValue; - QualType PreNarrowingType; - switch (SCS.getNarrowingKind(S.Context, E, PreNarrowingValue, - PreNarrowingType, - /*IgnoreFloatToIntegralConversion*/ true)) { - case NK_Dependent_Narrowing: - // Implicit conversion to a narrower type, but the expression is - // value-dependent so we can't tell whether it's actually narrowing. - case NK_Not_Narrowing: - return false; + QualType NonNullType = LHSNull ? RHS.get()->getType() : LHS.get()->getType(); - case NK_Constant_Narrowing: - // Implicit conversion to a narrower type, and the value is not a constant - // expression. - S.Diag(E->getBeginLoc(), diag::err_spaceship_argument_narrowing) - << /*Constant*/ 1 - << PreNarrowingValue.getAsString(S.Context, PreNarrowingType) << ToType; - return true; + // Avoid analyzing cases where the result will either be invalid (and + // diagnosed as such) or entirely valid and not something to warn about. + if ((!LHSNull && !RHSNull) || NonNullType->isBlockPointerType() || + NonNullType->isMemberPointerType() || NonNullType->isFunctionType()) + return; - case NK_Variable_Narrowing: - // Implicit conversion to a narrower type, and the value is not a constant - // expression. - case NK_Type_Narrowing: - S.Diag(E->getBeginLoc(), diag::err_spaceship_argument_narrowing) - << /*Constant*/ 0 << FromType << ToType; - // TODO: It's not a constant expression, but what if the user intended it - // to be? Can we produce notes to help them figure out why it isn't? - return true; + // Comparison operations would not make sense with a null pointer no matter + // what the other expression is. + if (!IsCompare) { + S.Diag(Loc, diag::warn_null_in_arithmetic_operation) + << (LHSNull ? LHS.get()->getSourceRange() : SourceRange()) + << (RHSNull ? RHS.get()->getSourceRange() : SourceRange()); + return; } - llvm_unreachable("unhandled case in switch"); + + // The rest of the operations only make sense with a null pointer + // if the other expression is a pointer. + if (LHSNull == RHSNull || NonNullType->isAnyPointerType() || + NonNullType->canDecayToPointerType()) + return; + + S.Diag(Loc, diag::warn_null_in_comparison_operation) + << LHSNull /* LHS is NULL */ << NonNullType + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); } -static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S, - ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc) { - QualType LHSType = LHS.get()->getType(); - QualType RHSType = RHS.get()->getType(); - // Dig out the original argument type and expression before implicit casts - // were applied. These are the types/expressions we need to check the - // [expr.spaceship] requirements against. - ExprResult LHSStripped = LHS.get()->IgnoreParenImpCasts(); - ExprResult RHSStripped = RHS.get()->IgnoreParenImpCasts(); - QualType LHSStrippedType = LHSStripped.get()->getType(); - QualType RHSStrippedType = RHSStripped.get()->getType(); +static void DetectPrecisionLossInComplexDivision(Sema &S, QualType DivisorTy, + SourceLocation OpLoc) { + // If the divisor is real, then this is real/real or complex/real division. + // Either way there can be no precision loss. + auto *CT = DivisorTy->getAs(); + if (!CT) + return; - // C++2a [expr.spaceship]p3: If one of the operands is of type bool and the - // other is not, the program is ill-formed. - if (LHSStrippedType->isBooleanType() != RHSStrippedType->isBooleanType()) { - S.InvalidOperands(Loc, LHSStripped, RHSStripped); - return QualType(); - } + QualType ElementType = CT->getElementType(); + bool IsComplexRangePromoted = S.getLangOpts().getComplexRange() == + LangOptions::ComplexRangeKind::CX_Promoted; + if (!ElementType->isFloatingType() || !IsComplexRangePromoted) + return; - // FIXME: Consider combining this with checkEnumArithmeticConversions. - int NumEnumArgs = (int)LHSStrippedType->isEnumeralType() + - RHSStrippedType->isEnumeralType(); - if (NumEnumArgs == 1) { - bool LHSIsEnum = LHSStrippedType->isEnumeralType(); - QualType OtherTy = LHSIsEnum ? RHSStrippedType : LHSStrippedType; - if (OtherTy->hasFloatingRepresentation()) { - S.InvalidOperands(Loc, LHSStripped, RHSStripped); - return QualType(); + ASTContext &Ctx = S.getASTContext(); + QualType HigherElementType = Ctx.GetHigherPrecisionFPType(ElementType); + const llvm::fltSemantics &ElementTypeSemantics = + Ctx.getFloatTypeSemantics(ElementType); + const llvm::fltSemantics &HigherElementTypeSemantics = + Ctx.getFloatTypeSemantics(HigherElementType); + + if ((llvm::APFloat::semanticsMaxExponent(ElementTypeSemantics) * 2 + 1 > + llvm::APFloat::semanticsMaxExponent(HigherElementTypeSemantics)) || + (HigherElementType == Ctx.LongDoubleTy && + !Ctx.getTargetInfo().hasLongDoubleType())) { + // Retain the location of the first use of higher precision type. + if (!S.LocationOfExcessPrecisionNotSatisfied.isValid()) + S.LocationOfExcessPrecisionNotSatisfied = OpLoc; + for (auto &[Type, Num] : S.ExcessPrecisionNotSatisfied) { + if (Type == HigherElementType) { + Num++; + return; + } } + S.ExcessPrecisionNotSatisfied.push_back(std::make_pair( + HigherElementType, S.ExcessPrecisionNotSatisfied.size())); } - if (NumEnumArgs == 2) { - // C++2a [expr.spaceship]p5: If both operands have the same enumeration - // type E, the operator yields the result of converting the operands - // to the underlying type of E and applying <=> to the converted operands. - if (!S.Context.hasSameUnqualifiedType(LHSStrippedType, RHSStrippedType)) { - S.InvalidOperands(Loc, LHS, RHS); - return QualType(); - } - QualType IntType = - LHSStrippedType->castAs()->getDecl()->getIntegerType(); - assert(IntType->isArithmeticType()); +} - // We can't use `CK_IntegralCast` when the underlying type is 'bool', so we - // promote the boolean type, and all other promotable integer types, to - // avoid this. - if (S.Context.isPromotableIntegerType(IntType)) - IntType = S.Context.getPromotedIntegerType(IntType); +static void DiagnoseDivisionSizeofPointerOrArray(Sema &S, Expr *LHS, Expr *RHS, + SourceLocation Loc) { + const auto *LUE = dyn_cast(LHS); + const auto *RUE = dyn_cast(RHS); + if (!LUE || !RUE) + return; + if (LUE->getKind() != UETT_SizeOf || LUE->isArgumentType() || + RUE->getKind() != UETT_SizeOf) + return; - LHS = S.ImpCastExprToType(LHS.get(), IntType, CK_IntegralCast); - RHS = S.ImpCastExprToType(RHS.get(), IntType, CK_IntegralCast); - LHSType = RHSType = IntType; - } + const Expr *LHSArg = LUE->getArgumentExpr()->IgnoreParens(); + QualType LHSTy = LHSArg->getType(); + QualType RHSTy; - // C++2a [expr.spaceship]p4: If both operands have arithmetic types, the - // usual arithmetic conversions are applied to the operands. - QualType Type = - S.UsualArithmeticConversions(LHS, RHS, Loc, Sema::ACK_Comparison); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); - if (Type.isNull()) - return S.InvalidOperands(Loc, LHS, RHS); + if (RUE->isArgumentType()) + RHSTy = RUE->getArgumentType().getNonReferenceType(); + else + RHSTy = RUE->getArgumentExpr()->IgnoreParens()->getType(); - std::optional CCT = - getComparisonCategoryForBuiltinCmp(Type); - if (!CCT) - return S.InvalidOperands(Loc, LHS, RHS); + if (LHSTy->isPointerType() && !RHSTy->isPointerType()) { + if (!S.Context.hasSameUnqualifiedType(LHSTy->getPointeeType(), RHSTy)) + return; - bool HasNarrowing = checkThreeWayNarrowingConversion( - S, Type, LHS.get(), LHSType, LHS.get()->getBeginLoc()); - HasNarrowing |= checkThreeWayNarrowingConversion(S, Type, RHS.get(), RHSType, - RHS.get()->getBeginLoc()); - if (HasNarrowing) - return QualType(); + S.Diag(Loc, diag::warn_division_sizeof_ptr) << LHS << LHS->getSourceRange(); + if (const auto *DRE = dyn_cast(LHSArg)) { + if (const ValueDecl *LHSArgDecl = DRE->getDecl()) + S.Diag(LHSArgDecl->getLocation(), diag::note_pointer_declared_here) + << LHSArgDecl; + } + } else if (const auto *ArrayTy = S.Context.getAsArrayType(LHSTy)) { + QualType ArrayElemTy = ArrayTy->getElementType(); + if (ArrayElemTy != S.Context.getBaseElementType(ArrayTy) || + ArrayElemTy->isDependentType() || RHSTy->isDependentType() || + RHSTy->isReferenceType() || ArrayElemTy->isCharType() || + S.Context.getTypeSize(ArrayElemTy) == S.Context.getTypeSize(RHSTy)) + return; + S.Diag(Loc, diag::warn_division_sizeof_array) + << LHSArg->getSourceRange() << ArrayElemTy << RHSTy; + if (const auto *DRE = dyn_cast(LHSArg)) { + if (const ValueDecl *LHSArgDecl = DRE->getDecl()) + S.Diag(LHSArgDecl->getLocation(), diag::note_array_declared_here) + << LHSArgDecl; + } - assert(!Type.isNull() && "composite type for <=> has not been set"); + S.Diag(Loc, diag::note_precedence_silence) << RHS; + } +} - return S.CheckComparisonCategoryType( - *CCT, Loc, Sema::ComparisonCategoryUsage::OperatorInExpression); +static void DiagnoseBadDivideOrRemainderValues(Sema& S, ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc, bool IsDiv) { + // Check for division/remainder by zero. + Expr::EvalResult RHSValue; + if (!RHS.get()->isValueDependent() && + RHS.get()->EvaluateAsInt(RHSValue, S.Context) && + RHSValue.Val.getInt() == 0) + S.DiagRuntimeBehavior(Loc, RHS.get(), + S.PDiag(diag::warn_remainder_division_by_zero) + << IsDiv << RHS.get()->getSourceRange()); } -static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc, - BinaryOperatorKind Opc) { - if (Opc == BO_Cmp) - return checkArithmeticOrEnumeralThreeWayCompare(S, LHS, RHS, Loc); +QualType Sema::CheckMultiplyDivideOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + bool IsCompAssign, bool IsDiv) { + checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); - // C99 6.5.8p3 / C99 6.5.9p4 - QualType Type = - S.UsualArithmeticConversions(LHS, RHS, Loc, Sema::ACK_Comparison); + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); + if (LHSTy->isVectorType() || RHSTy->isVectorType()) + return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, + /*AllowBothBool*/ getLangOpts().AltiVec, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); + if (LHSTy->isSveVLSBuiltinType() || RHSTy->isSveVLSBuiltinType()) + return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, + ACK_Arithmetic); + if (!IsDiv && + (LHSTy->isConstantMatrixType() || RHSTy->isConstantMatrixType())) + return CheckMatrixMultiplyOperands(LHS, RHS, Loc, IsCompAssign); + // For division, only matrix-by-scalar is supported. Other combinations with + // matrix types are invalid. + if (IsDiv && LHSTy->isConstantMatrixType() && RHSTy->isArithmeticType()) + return CheckMatrixElementwiseOperands(LHS, RHS, Loc, IsCompAssign); + + QualType compType = UsualArithmeticConversions( + LHS, RHS, Loc, IsCompAssign ? ACK_CompAssign : ACK_Arithmetic); if (LHS.isInvalid() || RHS.isInvalid()) return QualType(); - if (Type.isNull()) - return S.InvalidOperands(Loc, LHS, RHS); - assert(Type->isArithmeticType() || Type->isEnumeralType()); - - if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc)) - return S.InvalidOperands(Loc, LHS, RHS); - // Check for comparisons of floating point operands using != and ==. - if (Type->hasFloatingRepresentation()) - S.CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); - - // The result of comparisons is 'bool' in C++, 'int' in C. - return S.Context.getLogicalOperationType(); -} -void Sema::CheckPtrComparisonWithNullChar(ExprResult &E, ExprResult &NullE) { - if (!NullE.get()->getType()->isAnyPointerType()) - return; - int NullValue = PP.isMacroDefined("NULL") ? 0 : 1; - if (!E.get()->getType()->isAnyPointerType() && - E.get()->isNullPointerConstant(Context, - Expr::NPC_ValueDependentIsNotNull) == - Expr::NPCK_ZeroExpression) { - if (const auto *CL = dyn_cast(E.get())) { - if (CL->getValue() == 0) - Diag(E.get()->getExprLoc(), diag::warn_pointer_compare) - << NullValue - << FixItHint::CreateReplacement(E.get()->getExprLoc(), - NullValue ? "NULL" : "(void *)0"); - } else if (const auto *CE = dyn_cast(E.get())) { - TypeSourceInfo *TI = CE->getTypeInfoAsWritten(); - QualType T = Context.getCanonicalType(TI->getType()).getUnqualifiedType(); - if (T == Context.CharTy) - Diag(E.get()->getExprLoc(), diag::warn_pointer_compare) - << NullValue - << FixItHint::CreateReplacement(E.get()->getExprLoc(), - NullValue ? "NULL" : "(void *)0"); - } + if (compType.isNull() || !compType->isArithmeticType()) + return InvalidOperands(Loc, LHS, RHS); + if (IsDiv) { + DetectPrecisionLossInComplexDivision(*this, RHS.get()->getType(), Loc); + DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, IsDiv); + DiagnoseDivisionSizeofPointerOrArray(*this, LHS.get(), RHS.get(), Loc); } + return compType; } -// C99 6.5.8, C++ [expr.rel] -QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - BinaryOperatorKind Opc) { - bool IsRelational = BinaryOperator::isRelationalOp(Opc); - bool IsThreeWay = Opc == BO_Cmp; - bool IsOrdered = IsRelational || IsThreeWay; - auto IsAnyPointerType = [](ExprResult E) { - QualType Ty = E.get()->getType(); - return Ty->isPointerType() || Ty->isMemberPointerType(); - }; - - // C++2a [expr.spaceship]p6: If at least one of the operands is of pointer - // type, array-to-pointer, ..., conversions are performed on both operands to - // bring them to their composite type. - // Otherwise, all comparisons expect an rvalue, so convert to rvalue before - // any type-related checks. - if (!IsThreeWay || IsAnyPointerType(LHS) || IsAnyPointerType(RHS)) { - LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); - } else { - LHS = DefaultLvalueConversion(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - RHS = DefaultLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); - } +QualType Sema::CheckRemainderOperands( + ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, bool IsCompAssign) { + checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); - checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/true); - if (!getLangOpts().CPlusPlus && BinaryOperator::isEqualityOp(Opc)) { - CheckPtrComparisonWithNullChar(LHS, RHS); - CheckPtrComparisonWithNullChar(RHS, LHS); - } + // Note: This check is here to simplify the double exclusions of + // scalar and vector HLSL checks. No getLangOpts().HLSL + // is needed since all languages exlcude doubles. + if (LHS.get()->getType()->isDoubleType() || + RHS.get()->getType()->isDoubleType() || + (LHS.get()->getType()->isVectorType() && LHS.get() + ->getType() + ->getAs() + ->getElementType() + ->isDoubleType()) || + (RHS.get()->getType()->isVectorType() && RHS.get() + ->getType() + ->getAs() + ->getElementType() + ->isDoubleType())) + return InvalidOperands(Loc, LHS, RHS); - // Handle vector comparisons separately. if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) - return CheckVectorCompareOperands(LHS, RHS, Loc, Opc); + RHS.get()->getType()->isVectorType()) { + if ((LHS.get()->getType()->hasIntegerRepresentation() && + RHS.get()->getType()->hasIntegerRepresentation()) || + (getLangOpts().HLSL && + (LHS.get()->getType()->hasFloatingRepresentation() || + RHS.get()->getType()->hasFloatingRepresentation()))) + return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, + /*AllowBothBool*/ getLangOpts().AltiVec, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); + return InvalidOperands(Loc, LHS, RHS); + } if (LHS.get()->getType()->isSveVLSBuiltinType() || - RHS.get()->getType()->isSveVLSBuiltinType()) - return CheckSizelessVectorCompareOperands(LHS, RHS, Loc, Opc); + RHS.get()->getType()->isSveVLSBuiltinType()) { + if (LHS.get()->getType()->hasIntegerRepresentation() && + RHS.get()->getType()->hasIntegerRepresentation()) + return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, + ACK_Arithmetic); - diagnoseLogicalNotOnLHSofCheck(*this, LHS, RHS, Loc, Opc); - diagnoseTautologicalComparison(*this, Loc, LHS.get(), RHS.get(), Opc); + return InvalidOperands(Loc, LHS, RHS); + } - QualType LHSType = LHS.get()->getType(); - QualType RHSType = RHS.get()->getType(); - if ((LHSType->isArithmeticType() || LHSType->isEnumeralType()) && - (RHSType->isArithmeticType() || RHSType->isEnumeralType())) - return checkArithmeticOrEnumeralCompare(*this, LHS, RHS, Loc, Opc); + QualType compType = UsualArithmeticConversions( + LHS, RHS, Loc, IsCompAssign ? ACK_CompAssign : ACK_Arithmetic); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); - if ((LHSType->isPointerType() && - LHSType->getPointeeType().isWebAssemblyReferenceType()) || - (RHSType->isPointerType() && - RHSType->getPointeeType().isWebAssemblyReferenceType())) + if (compType.isNull() || + (!compType->isIntegerType() && + !(getLangOpts().HLSL && compType->isFloatingType()))) return InvalidOperands(Loc, LHS, RHS); + DiagnoseBadDivideOrRemainderValues(*this, LHS, RHS, Loc, false /* IsDiv */); + return compType; +} - const Expr::NullPointerConstantKind LHSNullKind = - LHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); - const Expr::NullPointerConstantKind RHSNullKind = - RHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); - bool LHSIsNull = LHSNullKind != Expr::NPCK_NotNull; - bool RHSIsNull = RHSNullKind != Expr::NPCK_NotNull; +/// Diagnose invalid arithmetic on two void pointers. +static void diagnoseArithmeticOnTwoVoidPointers(Sema &S, SourceLocation Loc, + Expr *LHSExpr, Expr *RHSExpr) { + S.Diag(Loc, S.getLangOpts().CPlusPlus + ? diag::err_typecheck_pointer_arith_void_type + : diag::ext_gnu_void_ptr) + << 1 /* two pointers */ << LHSExpr->getSourceRange() + << RHSExpr->getSourceRange(); +} - auto computeResultTy = [&]() { - if (Opc != BO_Cmp) - return Context.getLogicalOperationType(); - assert(getLangOpts().CPlusPlus); - assert(Context.hasSameType(LHS.get()->getType(), RHS.get()->getType())); +/// Diagnose invalid arithmetic on a void pointer. +static void diagnoseArithmeticOnVoidPointer(Sema &S, SourceLocation Loc, + Expr *Pointer) { + S.Diag(Loc, S.getLangOpts().CPlusPlus + ? diag::err_typecheck_pointer_arith_void_type + : diag::ext_gnu_void_ptr) + << 0 /* one pointer */ << Pointer->getSourceRange(); +} - QualType CompositeTy = LHS.get()->getType(); - assert(!CompositeTy->isReferenceType()); +/// Diagnose invalid arithmetic on a null pointer. +/// +/// If \p IsGNUIdiom is true, the operation is using the 'p = (i8*)nullptr + n' +/// idiom, which we recognize as a GNU extension. +/// +static void diagnoseArithmeticOnNullPointer(Sema &S, SourceLocation Loc, + Expr *Pointer, bool IsGNUIdiom) { + if (IsGNUIdiom) + S.Diag(Loc, diag::warn_gnu_null_ptr_arith) + << Pointer->getSourceRange(); + else + S.Diag(Loc, diag::warn_pointer_arith_null_ptr) + << S.getLangOpts().CPlusPlus << Pointer->getSourceRange(); +} - std::optional CCT = - getComparisonCategoryForBuiltinCmp(CompositeTy); - if (!CCT) - return InvalidOperands(Loc, LHS, RHS); +/// Diagnose invalid subraction on a null pointer. +/// +static void diagnoseSubtractionOnNullPointer(Sema &S, SourceLocation Loc, + Expr *Pointer, bool BothNull) { + // Null - null is valid in C++ [expr.add]p7 + if (BothNull && S.getLangOpts().CPlusPlus) + return; - if (CompositeTy->isPointerType() && LHSIsNull != RHSIsNull) { - // P0946R0: Comparisons between a null pointer constant and an object - // pointer result in std::strong_equality, which is ill-formed under - // P1959R0. - Diag(Loc, diag::err_typecheck_three_way_comparison_of_pointer_and_zero) - << (LHSIsNull ? LHS.get()->getSourceRange() - : RHS.get()->getSourceRange()); - return QualType(); - } + // Is this s a macro from a system header? + if (S.Diags.getSuppressSystemWarnings() && S.SourceMgr.isInSystemMacro(Loc)) + return; - return CheckComparisonCategoryType( - *CCT, Loc, ComparisonCategoryUsage::OperatorInExpression); - }; + S.DiagRuntimeBehavior(Loc, Pointer, + S.PDiag(diag::warn_pointer_sub_null_ptr) + << S.getLangOpts().CPlusPlus + << Pointer->getSourceRange()); +} - if (!IsOrdered && LHSIsNull != RHSIsNull) { - bool IsEquality = Opc == BO_EQ; - if (RHSIsNull) - DiagnoseAlwaysNonNullPointer(LHS.get(), RHSNullKind, IsEquality, - RHS.get()->getSourceRange()); - else - DiagnoseAlwaysNonNullPointer(RHS.get(), LHSNullKind, IsEquality, - LHS.get()->getSourceRange()); - } +/// Diagnose invalid arithmetic on two function pointers. +static void diagnoseArithmeticOnTwoFunctionPointers(Sema &S, SourceLocation Loc, + Expr *LHS, Expr *RHS) { + assert(LHS->getType()->isAnyPointerType()); + assert(RHS->getType()->isAnyPointerType()); + S.Diag(Loc, S.getLangOpts().CPlusPlus + ? diag::err_typecheck_pointer_arith_function_type + : diag::ext_gnu_ptr_func_arith) + << 1 /* two pointers */ << LHS->getType()->getPointeeType() + // We only show the second type if it differs from the first. + << (unsigned)!S.Context.hasSameUnqualifiedType(LHS->getType(), + RHS->getType()) + << RHS->getType()->getPointeeType() + << LHS->getSourceRange() << RHS->getSourceRange(); +} - if (IsOrdered && LHSType->isFunctionPointerType() && - RHSType->isFunctionPointerType()) { - // Valid unless a relational comparison of function pointers - bool IsError = Opc == BO_Cmp; - auto DiagID = - IsError ? diag::err_typecheck_ordered_comparison_of_function_pointers - : getLangOpts().CPlusPlus - ? diag::warn_typecheck_ordered_comparison_of_function_pointers - : diag::ext_typecheck_ordered_comparison_of_function_pointers; - Diag(Loc, DiagID) << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - if (IsError) - return QualType(); +/// Diagnose invalid arithmetic on a function pointer. +static void diagnoseArithmeticOnFunctionPointer(Sema &S, SourceLocation Loc, + Expr *Pointer) { + assert(Pointer->getType()->isAnyPointerType()); + S.Diag(Loc, S.getLangOpts().CPlusPlus + ? diag::err_typecheck_pointer_arith_function_type + : diag::ext_gnu_ptr_func_arith) + << 0 /* one pointer */ << Pointer->getType()->getPointeeType() + << 0 /* one pointer, so only one type */ + << Pointer->getSourceRange(); +} + +/// Emit error if Operand is incomplete pointer type +/// +/// \returns True if pointer has incomplete type +static bool checkArithmeticIncompletePointerType(Sema &S, SourceLocation Loc, + Expr *Operand) { + QualType ResType = Operand->getType(); + if (const AtomicType *ResAtomicType = ResType->getAs()) + ResType = ResAtomicType->getValueType(); + + assert(ResType->isAnyPointerType()); + QualType PointeeTy = ResType->getPointeeType(); + return S.RequireCompleteSizedType( + Loc, PointeeTy, + diag::err_typecheck_arithmetic_incomplete_or_sizeless_type, + Operand->getSourceRange()); +} + +/* TO_UPSTREAM(BoundsSafety) ON*/ +// Looks at the expression and determine if it's a declaration that we +// could suggest adding `__counted_by` too. +// +// Returns a tuple of pointers that will be non null if +// the attribute should be suggested. +static std::tuple +shouldSuggestBoundsSafetyCountedBy(Expr *E) { + ValueDecl *decl = nullptr; + Expr *declRef = nullptr; + if (auto asDeclRefExpr = dyn_cast(E->IgnoreParenImpCasts())) { + declRef = asDeclRefExpr; + decl = asDeclRefExpr->getDecl(); + } else if (auto asMemberExpr = + dyn_cast(E->IgnoreParenImpCasts())) { + // Reference to a struct member + declRef = asMemberExpr; + decl = asMemberExpr->getMemberDecl(); + } + + if (declRef && declRef->getType()->isAtomicType()) { + // We don't suggest for atomic decls because they don't support the + // `__counted_by` attribute. + declRef = nullptr; + decl = nullptr; + } + return std::make_tuple(decl, declRef); +} + +static void emitBoundsSafetySinglePointerArithmeticError(Sema &S, Expr *Operand) { + // Try to find a declaration that we can suggest adding `__counted_by` too. + ValueDecl *decl = nullptr; + Expr *declRef = nullptr; + std::tie(decl, declRef) = shouldSuggestBoundsSafetyCountedBy(Operand); + + if (!declRef) { + // Didn't find a suitable a decl so emit error without suggesting the + // attribute. + S.Diag(Operand->getExprLoc(), diag::err_bounds_safety_single_pointer_arithmetic) + << Operand << /* %2 */ 0 << Operand->getSourceRange(); + return; } - if ((LHSType->isIntegerType() && !LHSIsNull) || - (RHSType->isIntegerType() && !RHSIsNull)) { - // Skip normal pointer conversion checks in this case; we have better - // diagnostics for this below. - } else if (getLangOpts().CPlusPlus) { - // Equality comparison of a function pointer to a void pointer is invalid, - // but we allow it as an extension. - // FIXME: If we really want to allow this, should it be part of composite - // pointer type computation so it works in conditionals too? - if (!IsOrdered && - ((LHSType->isFunctionPointerType() && RHSType->isVoidPointerType()) || - (RHSType->isFunctionPointerType() && LHSType->isVoidPointerType()))) { - // This is a gcc extension compatibility comparison. - // In a SFINAE context, we treat this as a hard error to maintain - // conformance with the C++ standard. - diagnoseFunctionPointerToVoidComparison( - *this, Loc, LHS, RHS, /*isError*/ (bool)isSFINAEContext()); + // Emit error suggesting the attribute. + assert(declRef->getType()->isPointerType()); + auto qualifiedName = decl->getQualifiedNameAsString(); + S.Diag(Operand->getExprLoc(), diag::err_bounds_safety_single_pointer_arithmetic) + << Operand << /* %2 */ 1 << qualifiedName << Operand->getSourceRange(); - if (isSFINAEContext()) - return QualType(); + // Emit note about where the decl is declared. + S.Diag(decl->getBeginLoc(), diag::note_pointer_declared_here_quoted) + << qualifiedName << decl->getSourceRange(); +} - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return computeResultTy(); - } +/// Check the validity of -fbounds-safety pointer arithmetic on increment/decrement. +static bool checkArithmeticUnaryOpBoundsSafetyPointer(Sema &S, Expr *Operand, bool IsInc) { + QualType ResType = Operand->getType(); + if (const AtomicType *ResAtomicType = ResType->getAs()) + ResType = ResAtomicType->getValueType(); - // C++ [expr.eq]p2: - // If at least one operand is a pointer [...] bring them to their - // composite pointer type. - // C++ [expr.spaceship]p6 - // If at least one of the operands is of pointer type, [...] bring them - // to their composite pointer type. - // C++ [expr.rel]p2: - // If both operands are pointers, [...] bring them to their composite - // pointer type. - // For <=>, the only valid non-pointer types are arrays and functions, and - // we already decayed those, so this is really the same as the relational - // comparison rule. - if ((int)LHSType->isPointerType() + (int)RHSType->isPointerType() >= - (IsOrdered ? 2 : 1) && - (!LangOpts.ObjCAutoRefCount || !(LHSType->isObjCObjectPointerType() || - RHSType->isObjCObjectPointerType()))) { - if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) - return QualType(); - return computeResultTy(); - } - } else if (LHSType->isPointerType() && - RHSType->isPointerType()) { // C99 6.5.8p2 - // All of the following pointer-related warnings are GCC extensions, except - // when handling null pointer constants. - QualType LCanPointeeTy = - LHSType->castAs()->getPointeeType().getCanonicalType(); - QualType RCanPointeeTy = - RHSType->castAs()->getPointeeType().getCanonicalType(); + if (!ResType->isAnyPointerType()) + return true; - // C99 6.5.9p2 and C99 6.5.8p2 - if (Context.typesAreCompatible(LCanPointeeTy.getUnqualifiedType(), - RCanPointeeTy.getUnqualifiedType())) { - if (IsRelational) { - // Pointers both need to point to complete or incomplete types - if ((LCanPointeeTy->isIncompleteType() != - RCanPointeeTy->isIncompleteType()) && - !getLangOpts().C11) { - Diag(Loc, diag::ext_typecheck_compare_complete_incomplete_pointers) - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange() - << LHSType << RHSType << LCanPointeeTy->isIncompleteType() - << RCanPointeeTy->isIncompleteType(); - } + if (ResType->isBoundsAttributedType()) { + if (const auto *DCPTy = ResType->getAs()) { + if (!IsInc) { + S.Diag(Operand->getExprLoc(), + diag::err_bounds_safety_dynamic_bound_pointer_unary_arithmetic) + << IsInc << DCPTy->getKind() << Operand->getSourceRange(); + return false; } - } else if (!IsRelational && - (LCanPointeeTy->isVoidType() || RCanPointeeTy->isVoidType())) { - // Valid unless comparison between non-null pointer and function pointer - if ((LCanPointeeTy->isFunctionType() || RCanPointeeTy->isFunctionType()) - && !LHSIsNull && !RHSIsNull) - diagnoseFunctionPointerToVoidComparison(*this, Loc, LHS, RHS, - /*isError*/false); - } else { - // Invalid - diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, /*isError*/false); - } - if (LCanPointeeTy != RCanPointeeTy) { - // Treat NULL constant as a special case in OpenCL. - if (getLangOpts().OpenCL && !LHSIsNull && !RHSIsNull) { - if (!LCanPointeeTy.isAddressSpaceOverlapping(RCanPointeeTy, - getASTContext())) { - Diag(Loc, - diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) - << LHSType << RHSType << 0 /* comparison */ - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - } + + // Note when the new bounds check is off the check is handled in + // `CheckCountAttributedDeclAssignments::TraverseUnaryOperator`. + if (S.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_IndirectCountUpdate)) { + // TODO: When the diagnostic emitted by + // `BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp` + // is a hard error we can return false here. However, we can't do that + // right now because the diagnostic is a warning which can be ignored. + // So even if the a warning was issued we need to generate a valid AST. + S.BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp( + DCPTy, Operand, IsInc); + } + + } else if (const auto *DRPTy = ResType->getAs()) { + if (!IsInc && !DRPTy->getStartPointer()) { + S.Diag(Operand->getExprLoc(), + diag::err_bounds_safety_dynamic_bound_pointer_unary_arithmetic) + << IsInc << /*ended_by*/ 5 << Operand->getSourceRange(); + return false; + } + if (IsInc && !DRPTy->getEndPointer()) { + S.Diag(Operand->getExprLoc(), + diag::err_bounds_safety_dynamic_bound_pointer_unary_arithmetic) + << IsInc << /*end*/ 4 << Operand->getSourceRange(); + return false; } - LangAS AddrSpaceL = LCanPointeeTy.getAddressSpace(); - LangAS AddrSpaceR = RCanPointeeTy.getAddressSpace(); - CastKind Kind = AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion - : CK_BitCast; - if (LHSIsNull && !RHSIsNull) - LHS = ImpCastExprToType(LHS.get(), RHSType, Kind); - else - RHS = ImpCastExprToType(RHS.get(), LHSType, Kind); } - return computeResultTy(); + return true; } + if (ResType->isValueTerminatedType()) { + if (IsInc) + return true; + S.Diag(Operand->getExprLoc(), + diag::err_bounds_safety_terminated_by_pointer_arithmetic_dec) + << Operand << Operand->getSourceRange(); + return false; + } - // C++ [expr.eq]p4: - // Two operands of type std::nullptr_t or one operand of type - // std::nullptr_t and the other a null pointer constant compare - // equal. - // C23 6.5.9p5: - // If both operands have type nullptr_t or one operand has type nullptr_t - // and the other is a null pointer constant, they compare equal if the - // former is a null pointer. - if (!IsOrdered && LHSIsNull && RHSIsNull) { - if (LHSType->isNullPtrType()) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return computeResultTy(); - } - if (RHSType->isNullPtrType()) { - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return computeResultTy(); - } + if (ResType->isSinglePointerType()) { + emitBoundsSafetySinglePointerArithmeticError(S, Operand); + return false; } - if (!getLangOpts().CPlusPlus && !IsOrdered && (LHSIsNull || RHSIsNull)) { - // C23 6.5.9p6: - // Otherwise, at least one operand is a pointer. If one is a pointer and - // the other is a null pointer constant or has type nullptr_t, they - // compare equal - if (LHSIsNull && RHSType->isPointerType()) { - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return computeResultTy(); - } - if (RHSIsNull && LHSType->isPointerType()) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return computeResultTy(); + if (auto *RD = ResType->getPointeeType()->getAs()) { + if (RD->getDecl()->hasFlexibleArrayMember()) { + S.Diag(Operand->getExprLoc(), + diag::err_bounds_safety_flexible_array_member_record_pointer_arithmetic); + return false; } } - // Comparison of Objective-C pointers and block pointers against nullptr_t. - // These aren't covered by the composite pointer type rules. - if (!IsOrdered && RHSType->isNullPtrType() && - (LHSType->isObjCObjectPointerType() || LHSType->isBlockPointerType())) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return computeResultTy(); - } - if (!IsOrdered && LHSType->isNullPtrType() && - (RHSType->isObjCObjectPointerType() || RHSType->isBlockPointerType())) { - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return computeResultTy(); + if (!IsInc && ResType->isIndexablePointerType()) { + S.Diag(Operand->getExprLoc(), diag::err_bounds_safety_indexable_pointer_arithmetic) + << Operand << Operand->getSourceRange(); + return false; } + return true; +} - if (getLangOpts().CPlusPlus) { - if (IsRelational && - ((LHSType->isNullPtrType() && RHSType->isPointerType()) || - (RHSType->isNullPtrType() && LHSType->isPointerType()))) { - // HACK: Relational comparison of nullptr_t against a pointer type is - // invalid per DR583, but we allow it within std::less<> and friends, - // since otherwise common uses of it break. - // FIXME: Consider removing this hack once LWG fixes std::less<> and - // friends to have std::nullptr_t overload candidates. - DeclContext *DC = CurContext; - if (isa(DC)) - DC = DC->getParent(); - if (auto *CTSD = dyn_cast(DC)) { - if (CTSD->isInStdNamespace() && - llvm::StringSwitch(CTSD->getName()) - .Cases("less", "less_equal", "greater", "greater_equal", true) - .Default(false)) { - if (RHSType->isNullPtrType()) - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - else - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return computeResultTy(); - } - } - } +/// Check the validity of -fbounds-safety pointer arithmetic. +static bool checkArithmeticBinOpBoundsSafetyPointer(Sema &S, Expr *Base, + Expr *Index, bool IndexNegated, + BinaryOperatorKind OpKind, + SourceLocation OpLoc) { + QualType BaseType = Base->getType(); + QualType IndexType = Index->getType(); + SourceLocation Loc = Base->getEndLoc(); - // C++ [expr.eq]p2: - // If at least one operand is a pointer to member, [...] bring them to - // their composite pointer type. - if (!IsOrdered && - (LHSType->isMemberPointerType() || RHSType->isMemberPointerType())) { - if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) - return QualType(); - else - return computeResultTy(); - } - } + assert((!BaseType->isAtomicType() || + !BaseType->getAs()->getValueType()->isPointerType()) && + "Should have been checked before"); + assert(IndexType->isIntegerType() && "Should have been checked before"); - // Handle block pointer types. - if (!IsOrdered && LHSType->isBlockPointerType() && - RHSType->isBlockPointerType()) { - QualType lpointee = LHSType->castAs()->getPointeeType(); - QualType rpointee = RHSType->castAs()->getPointeeType(); + auto PT = BaseType->getAs(); + if (!PT) + return true; - if (!LHSIsNull && !RHSIsNull && - !Context.typesAreCompatible(lpointee, rpointee)) { - Diag(Loc, diag::err_typecheck_comparison_of_distinct_blocks) - << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); + if (BaseType->isValueTerminatedType()) { + Expr::EvalResult Result; + if (Index->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects)) { + llvm::APSInt index = Result.Val.getInt(); + if (IndexNegated) + index = -index; + if (index.isZero() || index.isOne()) + return true; } - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return computeResultTy(); + S.Diag(Loc, diag::err_bounds_safety_terminated_by_pointer_arithmetic) + << Base << SourceRange(Base->getBeginLoc(), Index->getEndLoc()); + return false; } - // Allow block pointers to be compared with null pointer constants. - if (!IsOrdered - && ((LHSType->isBlockPointerType() && RHSType->isPointerType()) - || (LHSType->isPointerType() && RHSType->isBlockPointerType()))) { - if (!LHSIsNull && !RHSIsNull) { - if (!((RHSType->isPointerType() && RHSType->castAs() - ->getPointeeType()->isVoidType()) - || (LHSType->isPointerType() && LHSType->castAs() - ->getPointeeType()->isVoidType()))) - Diag(Loc, diag::err_typecheck_comparison_of_distinct_blocks) - << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - } - if (LHSIsNull && !RHSIsNull) - LHS = ImpCastExprToType(LHS.get(), RHSType, - RHSType->isPointerType() ? CK_BitCast - : CK_AnyPointerToBlockPointerCast); - else - RHS = ImpCastExprToType(RHS.get(), LHSType, - LHSType->isPointerType() ? CK_BitCast - : CK_AnyPointerToBlockPointerCast); - return computeResultTy(); + if (PT->isSingle() && !BaseType->isBoundsAttributedType()) { + emitBoundsSafetySinglePointerArithmeticError(S, Base); + return false; } - if (LHSType->isObjCObjectPointerType() || - RHSType->isObjCObjectPointerType()) { - const PointerType *LPT = LHSType->getAs(); - const PointerType *RPT = RHSType->getAs(); - if (LPT || RPT) { - bool LPtrToVoid = LPT ? LPT->getPointeeType()->isVoidType() : false; - bool RPtrToVoid = RPT ? RPT->getPointeeType()->isVoidType() : false; - - if (!LPtrToVoid && !RPtrToVoid && - !Context.typesAreCompatible(LHSType, RHSType)) { - diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, - /*isError*/false); - } - // FIXME: If LPtrToVoid, we should presumably convert the LHS rather than - // the RHS, but we have test coverage for this behavior. - // FIXME: Consider using convertPointersToCompositeType in C++. - if (LHSIsNull && !RHSIsNull) { - Expr *E = LHS.get(); - if (getLangOpts().ObjCAutoRefCount) - ObjC().CheckObjCConversion(SourceRange(), RHSType, E, - CheckedConversionKind::Implicit); - LHS = ImpCastExprToType(E, RHSType, - RPT ? CK_BitCast :CK_CPointerToObjCPointerCast); - } - else { - Expr *E = RHS.get(); - if (getLangOpts().ObjCAutoRefCount) - ObjC().CheckObjCConversion(SourceRange(), LHSType, E, - CheckedConversionKind::Implicit, - /*Diagnose=*/true, - /*DiagnoseCFAudited=*/false, Opc); - RHS = ImpCastExprToType(E, LHSType, - LPT ? CK_BitCast :CK_CPointerToObjCPointerCast); - } - return computeResultTy(); - } - if (LHSType->isObjCObjectPointerType() && - RHSType->isObjCObjectPointerType()) { - if (!Context.areComparableObjCPointerTypes(LHSType, RHSType)) - diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, - /*isError*/false); - if (isObjCObjectLiteral(LHS) || isObjCObjectLiteral(RHS)) - diagnoseObjCLiteralComparison(*this, Loc, LHS, RHS, Opc); - - if (LHSIsNull && !RHSIsNull) - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); - else - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return computeResultTy(); - } - - if (!IsOrdered && LHSType->isBlockPointerType() && - RHSType->isBlockCompatibleObjCPointerType(Context)) { - LHS = ImpCastExprToType(LHS.get(), RHSType, - CK_BlockPointerToObjCPointerCast); - return computeResultTy(); - } else if (!IsOrdered && - LHSType->isBlockCompatibleObjCPointerType(Context) && - RHSType->isBlockPointerType()) { - RHS = ImpCastExprToType(RHS.get(), LHSType, - CK_BlockPointerToObjCPointerCast); - return computeResultTy(); + // Note when the new bounds check is off the check is handled in + // `CheckCountAttributedDeclAssignments::TraverseBinaryOperator`. + if (const auto *CATTy = BaseType->getAs()) { + if (S.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_IndirectCountUpdate)) { + // TODO: When the diagnostic emitted by + // `BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp` + // is a hard error we can return false here. However, we can't do that + // right now because the diagnostic is a warning which can be ignored. So + // even if the a warning was issued we need to generate a valid AST. + S.BoundsSafetyCheckCountAttributedTypeHasConstantCountForAssignmentOp( + CATTy, Base, OpKind); } } - if ((LHSType->isAnyPointerType() && RHSType->isIntegerType()) || - (LHSType->isIntegerType() && RHSType->isAnyPointerType())) { - unsigned DiagID = 0; - bool isError = false; - if (LangOpts.DebuggerSupport) { - // Under a debugger, allow the comparison of pointers to integers, - // since users tend to want to compare addresses. - } else if ((LHSIsNull && LHSType->isIntegerType()) || - (RHSIsNull && RHSType->isIntegerType())) { - if (IsOrdered) { - isError = getLangOpts().CPlusPlus; - DiagID = - isError ? diag::err_typecheck_ordered_comparison_of_pointer_and_zero - : diag::ext_typecheck_ordered_comparison_of_pointer_and_zero; - } - } else if (getLangOpts().CPlusPlus) { - DiagID = diag::err_typecheck_comparison_of_pointer_integer; - isError = true; - } else if (IsOrdered) - DiagID = diag::ext_typecheck_ordered_comparison_of_pointer_integer; - else - DiagID = diag::ext_typecheck_comparison_of_pointer_integer; - if (DiagID) { - Diag(Loc, DiagID) - << LHSType << RHSType << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - if (isError) - return QualType(); + if (auto *RD = PT->getPointeeType()->getAs()) { + if (RD->getDecl()->hasFlexibleArrayMember()) { + S.Diag(Base->getExprLoc(), + diag::err_bounds_safety_flexible_array_member_record_pointer_arithmetic); + return false; } - - if (LHSType->isIntegerType()) - LHS = ImpCastExprToType(LHS.get(), RHSType, - LHSIsNull ? CK_NullToPointer : CK_IntegralToPointer); - else - RHS = ImpCastExprToType(RHS.get(), LHSType, - RHSIsNull ? CK_NullToPointer : CK_IntegralToPointer); - return computeResultTy(); } - // Handle block pointers. - if (!IsOrdered && RHSIsNull - && LHSType->isBlockPointerType() && RHSType->isIntegerType()) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return computeResultTy(); - } - if (!IsOrdered && LHSIsNull - && LHSType->isIntegerType() && RHSType->isBlockPointerType()) { - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return computeResultTy(); - } + Index = Index->IgnoreParenImpCasts(); + if (Index->isValueDependent()) + return true; - if (getLangOpts().getOpenCLCompatibleVersion() >= 200) { - if (LHSType->isClkEventT() && RHSType->isClkEventT()) { - return computeResultTy(); - } + if (!PT->isIndexable()) + return true; - if (LHSType->isQueueT() && RHSType->isQueueT()) { - return computeResultTy(); + if (IndexNegated) { + auto *RHSBInTy = dyn_cast(IndexType); + if (RHSBInTy && RHSBInTy->isUnsignedInteger()) { + S.Diag(Loc, diag::err_bounds_safety_indexable_pointer_arithmetic) + << Base << SourceRange(Base->getBeginLoc(), Index->getEndLoc()); + return false; } + } - if (LHSIsNull && RHSType->isQueueT()) { - LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return computeResultTy(); - } + Expr::EvalResult Result; + if (!Index->EvaluateAsInt(Result, S.getASTContext(), + Expr::SE_AllowSideEffects)) + // Render it to a runtime check. + return true; - if (LHSType->isQueueT() && RHSIsNull) { - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return computeResultTy(); - } + llvm::APSInt index = Result.Val.getInt(); + if (IndexNegated) + index = -index; + + if (index.isNegative()) { + S.Diag(Loc, diag::err_bounds_safety_indexable_pointer_arithmetic) + << Base << SourceRange(Base->getBeginLoc(), Index->getEndLoc()); + return false; } - return InvalidOperands(Loc, LHS, RHS); + return true; } +/* TO_UPSTREAM(BoundsSafety) OFF*/ -QualType Sema::GetSignedVectorType(QualType V) { - const VectorType *VTy = V->castAs(); - unsigned TypeSize = Context.getTypeSize(VTy->getElementType()); +/// Check the validity of an arithmetic pointer operand. +/// +/// If the operand has pointer type, this code will check for pointer types +/// which are invalid in arithmetic operations. These will be diagnosed +/// appropriately, including whether or not the use is supported as an +/// extension. +/// +/// \returns True when the operand is valid to use (even if as an extension). +static bool checkArithmeticOpPointerOperand(Sema &S, SourceLocation Loc, + Expr *Operand) { + QualType ResType = Operand->getType(); + if (const AtomicType *ResAtomicType = ResType->getAs()) + ResType = ResAtomicType->getValueType(); - if (isa(VTy)) { - if (VTy->isExtVectorBoolType()) - return Context.getExtVectorType(Context.BoolTy, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.CharTy)) - return Context.getExtVectorType(Context.CharTy, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.ShortTy)) - return Context.getExtVectorType(Context.ShortTy, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.IntTy)) - return Context.getExtVectorType(Context.IntTy, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.Int128Ty)) - return Context.getExtVectorType(Context.Int128Ty, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.LongTy)) - return Context.getExtVectorType(Context.LongTy, VTy->getNumElements()); - assert(TypeSize == Context.getTypeSize(Context.LongLongTy) && - "Unhandled vector element size in vector compare"); - return Context.getExtVectorType(Context.LongLongTy, VTy->getNumElements()); + if (!ResType->isAnyPointerType()) return true; + + QualType PointeeTy = ResType->getPointeeType(); + if (PointeeTy->isVoidType()) { + diagnoseArithmeticOnVoidPointer(S, Loc, Operand); + return !S.getLangOpts().CPlusPlus; + } + if (PointeeTy->isFunctionType()) { + diagnoseArithmeticOnFunctionPointer(S, Loc, Operand); + return !S.getLangOpts().CPlusPlus; } - if (TypeSize == Context.getTypeSize(Context.Int128Ty)) - return Context.getVectorType(Context.Int128Ty, VTy->getNumElements(), - VectorKind::Generic); - if (TypeSize == Context.getTypeSize(Context.LongLongTy)) - return Context.getVectorType(Context.LongLongTy, VTy->getNumElements(), - VectorKind::Generic); - if (TypeSize == Context.getTypeSize(Context.LongTy)) - return Context.getVectorType(Context.LongTy, VTy->getNumElements(), - VectorKind::Generic); - if (TypeSize == Context.getTypeSize(Context.IntTy)) - return Context.getVectorType(Context.IntTy, VTy->getNumElements(), - VectorKind::Generic); - if (TypeSize == Context.getTypeSize(Context.ShortTy)) - return Context.getVectorType(Context.ShortTy, VTy->getNumElements(), - VectorKind::Generic); - assert(TypeSize == Context.getTypeSize(Context.CharTy) && - "Unhandled vector element size in vector compare"); - return Context.getVectorType(Context.CharTy, VTy->getNumElements(), - VectorKind::Generic); -} + if (checkArithmeticIncompletePointerType(S, Loc, Operand)) return false; -QualType Sema::GetSignedSizelessVectorType(QualType V) { - const BuiltinType *VTy = V->castAs(); - assert(VTy->isSizelessBuiltinType() && "expected sizeless type"); + return true; +} - const QualType ETy = V->getSveEltType(Context); - const auto TypeSize = Context.getTypeSize(ETy); +/// Check the validity of a binary arithmetic operation w.r.t. pointer +/// operands. +/// +/// This routine will diagnose any invalid arithmetic on pointer operands much +/// like \see checkArithmeticOpPointerOperand. However, it has special logic +/// for emitting a single diagnostic even for operations where both LHS and RHS +/// are (potentially problematic) pointers. +/// +/// \returns True when the operand is valid to use (even if as an extension). +static bool checkArithmeticBinOpPointerOperands(Sema &S, SourceLocation Loc, + Expr *LHSExpr, Expr *RHSExpr) { + bool isLHSPointer = LHSExpr->getType()->isAnyPointerType(); + bool isRHSPointer = RHSExpr->getType()->isAnyPointerType(); + if (!isLHSPointer && !isRHSPointer) return true; - const QualType IntTy = Context.getIntTypeForBitwidth(TypeSize, true); - const llvm::ElementCount VecSize = Context.getBuiltinVectorTypeInfo(VTy).EC; - return Context.getScalableVectorType(IntTy, VecSize.getKnownMinValue()); -} + QualType LHSPointeeTy, RHSPointeeTy; + if (isLHSPointer) LHSPointeeTy = LHSExpr->getType()->getPointeeType(); + if (isRHSPointer) RHSPointeeTy = RHSExpr->getType()->getPointeeType(); -QualType Sema::CheckVectorCompareOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - BinaryOperatorKind Opc) { - if (Opc == BO_Cmp) { - Diag(Loc, diag::err_three_way_vector_comparison); - return QualType(); + // if both are pointers check if operation is valid wrt address spaces + if (isLHSPointer && isRHSPointer) { + if (!LHSPointeeTy.isAddressSpaceOverlapping(RHSPointeeTy, + S.getASTContext())) { + S.Diag(Loc, + diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) + << LHSExpr->getType() << RHSExpr->getType() << 1 /*arithmetic op*/ + << LHSExpr->getSourceRange() << RHSExpr->getSourceRange(); + return false; + } } - // Check to make sure we're operating on vectors of the same type and width, - // Allowing one side to be a scalar of element type. - QualType vType = - CheckVectorOperands(LHS, RHS, Loc, /*isCompAssign*/ false, - /*AllowBothBool*/ true, - /*AllowBoolConversions*/ getLangOpts().ZVector, - /*AllowBooleanOperation*/ true, - /*ReportInvalid*/ true); - if (vType.isNull()) - return vType; - - QualType LHSType = LHS.get()->getType(); + // Check for arithmetic on pointers to incomplete types. + bool isLHSVoidPtr = isLHSPointer && LHSPointeeTy->isVoidType(); + bool isRHSVoidPtr = isRHSPointer && RHSPointeeTy->isVoidType(); + if (isLHSVoidPtr || isRHSVoidPtr) { + if (!isRHSVoidPtr) diagnoseArithmeticOnVoidPointer(S, Loc, LHSExpr); + else if (!isLHSVoidPtr) diagnoseArithmeticOnVoidPointer(S, Loc, RHSExpr); + else diagnoseArithmeticOnTwoVoidPointers(S, Loc, LHSExpr, RHSExpr); - // Determine the return type of a vector compare. By default clang will return - // a scalar for all vector compares except vector bool and vector pixel. - // With the gcc compiler we will always return a vector type and with the xl - // compiler we will always return a scalar type. This switch allows choosing - // which behavior is prefered. - if (getLangOpts().AltiVec) { - switch (getLangOpts().getAltivecSrcCompat()) { - case LangOptions::AltivecSrcCompatKind::Mixed: - // If AltiVec, the comparison results in a numeric type, i.e. - // bool for C++, int for C - if (vType->castAs()->getVectorKind() == - VectorKind::AltiVecVector) - return Context.getLogicalOperationType(); - else - Diag(Loc, diag::warn_deprecated_altivec_src_compat); - break; - case LangOptions::AltivecSrcCompatKind::GCC: - // For GCC we always return the vector type. - break; - case LangOptions::AltivecSrcCompatKind::XL: - return Context.getLogicalOperationType(); - break; - } + return !S.getLangOpts().CPlusPlus; } - // For non-floating point types, check for self-comparisons of the form - // x == x, x != x, x < x, etc. These always evaluate to a constant, and - // often indicate logic errors in the program. - diagnoseTautologicalComparison(*this, Loc, LHS.get(), RHS.get(), Opc); + bool isLHSFuncPtr = isLHSPointer && LHSPointeeTy->isFunctionType(); + bool isRHSFuncPtr = isRHSPointer && RHSPointeeTy->isFunctionType(); + if (isLHSFuncPtr || isRHSFuncPtr) { + if (!isRHSFuncPtr) diagnoseArithmeticOnFunctionPointer(S, Loc, LHSExpr); + else if (!isLHSFuncPtr) diagnoseArithmeticOnFunctionPointer(S, Loc, + RHSExpr); + else diagnoseArithmeticOnTwoFunctionPointers(S, Loc, LHSExpr, RHSExpr); - // Check for comparisons of floating point operands using != and ==. - if (LHSType->hasFloatingRepresentation()) { - assert(RHS.get()->getType()->hasFloatingRepresentation()); - CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); + return !S.getLangOpts().CPlusPlus; } - // Return a signed type for the vector. - return GetSignedVectorType(vType); + if (isLHSPointer && checkArithmeticIncompletePointerType(S, Loc, LHSExpr)) + return false; + if (isRHSPointer && checkArithmeticIncompletePointerType(S, Loc, RHSExpr)) + return false; + + return true; } -QualType Sema::CheckSizelessVectorCompareOperands(ExprResult &LHS, - ExprResult &RHS, - SourceLocation Loc, - BinaryOperatorKind Opc) { - if (Opc == BO_Cmp) { - Diag(Loc, diag::err_three_way_vector_comparison); - return QualType(); +/// diagnoseStringPlusInt - Emit a warning when adding an integer to a string +/// literal. +static void diagnoseStringPlusInt(Sema &Self, SourceLocation OpLoc, + Expr *LHSExpr, Expr *RHSExpr) { + StringLiteral* StrExpr = dyn_cast(LHSExpr->IgnoreImpCasts()); + Expr* IndexExpr = RHSExpr; + if (!StrExpr) { + StrExpr = dyn_cast(RHSExpr->IgnoreImpCasts()); + IndexExpr = LHSExpr; } - // Check to make sure we're operating on vectors of the same type and width, - // Allowing one side to be a scalar of element type. - QualType vType = CheckSizelessVectorOperands( - LHS, RHS, Loc, /*isCompAssign*/ false, ACK_Comparison); + bool IsStringPlusInt = StrExpr && + IndexExpr->getType()->isIntegralOrUnscopedEnumerationType(); + if (!IsStringPlusInt || IndexExpr->isValueDependent()) + return; - if (vType.isNull()) - return vType; + SourceRange DiagRange(LHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); + Self.Diag(OpLoc, diag::warn_string_plus_int) + << DiagRange << IndexExpr->IgnoreImpCasts()->getType(); - QualType LHSType = LHS.get()->getType(); + // Only print a fixit for "str" + int, not for int + "str". + if (IndexExpr == RHSExpr) { + SourceLocation EndLoc = Self.getLocForEndOfToken(RHSExpr->getEndLoc()); + Self.Diag(OpLoc, diag::note_string_plus_scalar_silence) + << FixItHint::CreateInsertion(LHSExpr->getBeginLoc(), "&") + << FixItHint::CreateReplacement(SourceRange(OpLoc), "[") + << FixItHint::CreateInsertion(EndLoc, "]"); + } else + Self.Diag(OpLoc, diag::note_string_plus_scalar_silence); +} - // For non-floating point types, check for self-comparisons of the form - // x == x, x != x, x < x, etc. These always evaluate to a constant, and - // often indicate logic errors in the program. - diagnoseTautologicalComparison(*this, Loc, LHS.get(), RHS.get(), Opc); +/// Emit a warning when adding a char literal to a string. +static void diagnoseStringPlusChar(Sema &Self, SourceLocation OpLoc, + Expr *LHSExpr, Expr *RHSExpr) { + const Expr *StringRefExpr = LHSExpr; + const CharacterLiteral *CharExpr = + dyn_cast(RHSExpr->IgnoreImpCasts()); - // Check for comparisons of floating point operands using != and ==. - if (LHSType->hasFloatingRepresentation()) { - assert(RHS.get()->getType()->hasFloatingRepresentation()); - CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); + if (!CharExpr) { + CharExpr = dyn_cast(LHSExpr->IgnoreImpCasts()); + StringRefExpr = RHSExpr; } - const BuiltinType *LHSBuiltinTy = LHSType->getAs(); - const BuiltinType *RHSBuiltinTy = RHS.get()->getType()->getAs(); - - if (LHSBuiltinTy && RHSBuiltinTy && LHSBuiltinTy->isSVEBool() && - RHSBuiltinTy->isSVEBool()) - return LHSType; + if (!CharExpr || !StringRefExpr) + return; - // Return a signed type for the vector. - return GetSignedSizelessVectorType(vType); -} + const QualType StringType = StringRefExpr->getType(); -static void diagnoseXorMisusedAsPow(Sema &S, const ExprResult &XorLHS, - const ExprResult &XorRHS, - const SourceLocation Loc) { - // Do not diagnose macros. - if (Loc.isMacroID()) + // Return if not a PointerType. + if (!StringType->isAnyPointerType()) return; - // Do not diagnose if both LHS and RHS are macros. - if (XorLHS.get()->getExprLoc().isMacroID() && - XorRHS.get()->getExprLoc().isMacroID()) + // Return if not a CharacterType. + if (!StringType->getPointeeType()->isAnyCharacterType()) return; - bool Negative = false; - bool ExplicitPlus = false; - const auto *LHSInt = dyn_cast(XorLHS.get()); - const auto *RHSInt = dyn_cast(XorRHS.get()); + ASTContext &Ctx = Self.getASTContext(); + SourceRange DiagRange(LHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); - if (!LHSInt) - return; - if (!RHSInt) { - // Check negative literals. - if (const auto *UO = dyn_cast(XorRHS.get())) { - UnaryOperatorKind Opc = UO->getOpcode(); - if (Opc != UO_Minus && Opc != UO_Plus) - return; - RHSInt = dyn_cast(UO->getSubExpr()); - if (!RHSInt) - return; - Negative = (Opc == UO_Minus); - ExplicitPlus = !Negative; - } else { - return; - } + const QualType CharType = CharExpr->getType(); + if (!CharType->isAnyCharacterType() && + CharType->isIntegerType() && + llvm::isUIntN(Ctx.getCharWidth(), CharExpr->getValue())) { + Self.Diag(OpLoc, diag::warn_string_plus_char) + << DiagRange << Ctx.CharTy; + } else { + Self.Diag(OpLoc, diag::warn_string_plus_char) + << DiagRange << CharExpr->getType(); } - const llvm::APInt &LeftSideValue = LHSInt->getValue(); - llvm::APInt RightSideValue = RHSInt->getValue(); - if (LeftSideValue != 2 && LeftSideValue != 10) - return; + // Only print a fixit for str + char, not for char + str. + if (isa(RHSExpr->IgnoreImpCasts())) { + SourceLocation EndLoc = Self.getLocForEndOfToken(RHSExpr->getEndLoc()); + Self.Diag(OpLoc, diag::note_string_plus_scalar_silence) + << FixItHint::CreateInsertion(LHSExpr->getBeginLoc(), "&") + << FixItHint::CreateReplacement(SourceRange(OpLoc), "[") + << FixItHint::CreateInsertion(EndLoc, "]"); + } else { + Self.Diag(OpLoc, diag::note_string_plus_scalar_silence); + } +} - if (LeftSideValue.getBitWidth() != RightSideValue.getBitWidth()) - return; +/// Emit error when two pointers are incompatible. +static void diagnosePointerIncompatibility(Sema &S, SourceLocation Loc, + Expr *LHSExpr, Expr *RHSExpr) { + assert(LHSExpr->getType()->isAnyPointerType()); + assert(RHSExpr->getType()->isAnyPointerType()); + S.Diag(Loc, diag::err_typecheck_sub_ptr_compatible) + << LHSExpr->getType() << RHSExpr->getType() << LHSExpr->getSourceRange() + << RHSExpr->getSourceRange(); +} - CharSourceRange ExprRange = CharSourceRange::getCharRange( - LHSInt->getBeginLoc(), S.getLocForEndOfToken(RHSInt->getLocation())); - llvm::StringRef ExprStr = - Lexer::getSourceText(ExprRange, S.getSourceManager(), S.getLangOpts()); +// C99 6.5.6 +QualType Sema::CheckAdditionOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, BinaryOperatorKind Opc, + QualType* CompLHSTy) { + checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); - CharSourceRange XorRange = - CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); - llvm::StringRef XorStr = - Lexer::getSourceText(XorRange, S.getSourceManager(), S.getLangOpts()); - // Do not diagnose if xor keyword/macro is used. - if (XorStr == "xor") - return; + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) { + QualType compType = + CheckVectorOperands(LHS, RHS, Loc, CompLHSTy, + /*AllowBothBool*/ getLangOpts().AltiVec, + /*AllowBoolConversions*/ getLangOpts().ZVector, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); + if (CompLHSTy) *CompLHSTy = compType; + return compType; + } - std::string LHSStr = std::string(Lexer::getSourceText( - CharSourceRange::getTokenRange(LHSInt->getSourceRange()), - S.getSourceManager(), S.getLangOpts())); - std::string RHSStr = std::string(Lexer::getSourceText( - CharSourceRange::getTokenRange(RHSInt->getSourceRange()), - S.getSourceManager(), S.getLangOpts())); + if (LHS.get()->getType()->isSveVLSBuiltinType() || + RHS.get()->getType()->isSveVLSBuiltinType()) { + QualType compType = + CheckSizelessVectorOperands(LHS, RHS, Loc, CompLHSTy, ACK_Arithmetic); + if (CompLHSTy) + *CompLHSTy = compType; + return compType; + } - if (Negative) { - RightSideValue = -RightSideValue; - RHSStr = "-" + RHSStr; - } else if (ExplicitPlus) { - RHSStr = "+" + RHSStr; + if (LHS.get()->getType()->isConstantMatrixType() || + RHS.get()->getType()->isConstantMatrixType()) { + QualType compType = + CheckMatrixElementwiseOperands(LHS, RHS, Loc, CompLHSTy); + if (CompLHSTy) + *CompLHSTy = compType; + return compType; } - StringRef LHSStrRef = LHSStr; - StringRef RHSStrRef = RHSStr; - // Do not diagnose literals with digit separators, binary, hexadecimal, octal - // literals. - if (LHSStrRef.starts_with("0b") || LHSStrRef.starts_with("0B") || - RHSStrRef.starts_with("0b") || RHSStrRef.starts_with("0B") || - LHSStrRef.starts_with("0x") || LHSStrRef.starts_with("0X") || - RHSStrRef.starts_with("0x") || RHSStrRef.starts_with("0X") || - (LHSStrRef.size() > 1 && LHSStrRef.starts_with("0")) || - (RHSStrRef.size() > 1 && RHSStrRef.starts_with("0")) || - LHSStrRef.contains('\'') || RHSStrRef.contains('\'')) - return; + QualType compType = UsualArithmeticConversions( + LHS, RHS, Loc, CompLHSTy ? ACK_CompAssign : ACK_Arithmetic); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); - bool SuggestXor = - S.getLangOpts().CPlusPlus || S.getPreprocessor().isMacroDefined("xor"); - const llvm::APInt XorValue = LeftSideValue ^ RightSideValue; - int64_t RightSideIntValue = RightSideValue.getSExtValue(); - if (LeftSideValue == 2 && RightSideIntValue >= 0) { - std::string SuggestedExpr = "1 << " + RHSStr; - bool Overflow = false; - llvm::APInt One = (LeftSideValue - 1); - llvm::APInt PowValue = One.sshl_ov(RightSideValue, Overflow); - if (Overflow) { - if (RightSideIntValue < 64) - S.Diag(Loc, diag::warn_xor_used_as_pow_base) - << ExprStr << toString(XorValue, 10, true) << ("1LL << " + RHSStr) - << FixItHint::CreateReplacement(ExprRange, "1LL << " + RHSStr); - else if (RightSideIntValue == 64) - S.Diag(Loc, diag::warn_xor_used_as_pow) - << ExprStr << toString(XorValue, 10, true); - else - return; + // Diagnose "string literal" '+' int and string '+' "char literal". + if (Opc == BO_Add) { + diagnoseStringPlusInt(*this, Loc, LHS.get(), RHS.get()); + diagnoseStringPlusChar(*this, Loc, LHS.get(), RHS.get()); + } + + // handle the common case first (both operands are arithmetic). + if (!compType.isNull() && compType->isArithmeticType()) { + if (CompLHSTy) *CompLHSTy = compType; + return compType; + } + + // Type-checking. Ultimately the pointer's going to be in PExp; + // note that we bias towards the LHS being the pointer. + Expr *PExp = LHS.get(), *IExp = RHS.get(); + + bool isObjCPointer; + if (PExp->getType()->isPointerType()) { + isObjCPointer = false; + } else if (PExp->getType()->isObjCObjectPointerType()) { + isObjCPointer = true; + } else { + std::swap(PExp, IExp); + if (PExp->getType()->isPointerType()) { + isObjCPointer = false; + } else if (PExp->getType()->isObjCObjectPointerType()) { + isObjCPointer = true; } else { - S.Diag(Loc, diag::warn_xor_used_as_pow_base_extra) - << ExprStr << toString(XorValue, 10, true) << SuggestedExpr - << toString(PowValue, 10, true) - << FixItHint::CreateReplacement( - ExprRange, (RightSideIntValue == 0) ? "1" : SuggestedExpr); + return InvalidOperands(Loc, LHS, RHS); } - - S.Diag(Loc, diag::note_xor_used_as_pow_silence) - << ("0x2 ^ " + RHSStr) << SuggestXor; - } else if (LeftSideValue == 10) { - std::string SuggestedValue = "1e" + std::to_string(RightSideIntValue); - S.Diag(Loc, diag::warn_xor_used_as_pow_base) - << ExprStr << toString(XorValue, 10, true) << SuggestedValue - << FixItHint::CreateReplacement(ExprRange, SuggestedValue); - S.Diag(Loc, diag::note_xor_used_as_pow_silence) - << ("0xA ^ " + RHSStr) << SuggestXor; } -} + assert(PExp->getType()->isAnyPointerType()); -QualType Sema::CheckVectorLogicalOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - BinaryOperatorKind Opc) { - // Ensure that either both operands are of the same vector type, or - // one operand is of a vector type and the other is of its element type. - QualType vType = CheckVectorOperands(LHS, RHS, Loc, false, - /*AllowBothBool*/ true, - /*AllowBoolConversions*/ false, - /*AllowBooleanOperation*/ false, - /*ReportInvalid*/ false); - if (vType.isNull()) - return InvalidOperands(Loc, LHS, RHS); - if (getLangOpts().OpenCL && - getLangOpts().getOpenCLCompatibleVersion() < 120 && - vType->hasFloatingRepresentation()) + if (!IExp->getType()->isIntegerType()) return InvalidOperands(Loc, LHS, RHS); - // FIXME: The check for C++ here is for GCC compatibility. GCC rejects the - // usage of the logical operators && and || with vectors in C. This - // check could be notionally dropped. - if (!getLangOpts().CPlusPlus && - !(isa(vType->getAs()))) - return InvalidLogicalVectorOperands(Loc, LHS, RHS); - // Beginning with HLSL 2021, HLSL disallows logical operators on vector - // operands and instead requires the use of the `and`, `or`, `any`, `all`, and - // `select` functions. - if (getLangOpts().HLSL && - getLangOpts().getHLSLVersion() >= LangOptionsBase::HLSL_2021) { - (void)InvalidOperands(Loc, LHS, RHS); - HLSL().emitLogicalOperatorFixIt(LHS.get(), RHS.get(), Opc); - return QualType(); + + // Adding to a null pointer results in undefined behavior. + if (PExp->IgnoreParenCasts()->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNotNull)) { + // In C++ adding zero to a null pointer is defined. + Expr::EvalResult KnownVal; + if (!getLangOpts().CPlusPlus || + (!IExp->isValueDependent() && + (!IExp->EvaluateAsInt(KnownVal, Context) || + KnownVal.Val.getInt() != 0))) { + // Check the conditions to see if this is the 'p = nullptr + n' idiom. + bool IsGNUIdiom = BinaryOperator::isNullPointerArithmeticExtension( + Context, BO_Add, PExp, IExp); + diagnoseArithmeticOnNullPointer(*this, Loc, PExp, IsGNUIdiom); + } } - return GetSignedVectorType(LHS.get()->getType()); -} + if (!checkArithmeticOpPointerOperand(*this, Loc, PExp)) + return QualType(); -QualType Sema::CheckMatrixElementwiseOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - bool IsCompAssign) { - if (!IsCompAssign) { - LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - } - RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) + if (isObjCPointer && checkArithmeticOnObjCPointer(*this, Loc, PExp)) return QualType(); - // For conversion purposes, we ignore any qualifiers. - // For example, "const float" and "float" are equivalent. - QualType LHSType = LHS.get()->getType().getUnqualifiedType(); - QualType RHSType = RHS.get()->getType().getUnqualifiedType(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety && + !checkArithmeticBinOpBoundsSafetyPointer(*this, PExp, IExp, + /*IndexNegated*/ false, Opc, Loc)) + return QualType(); + // Cast __indexable to __bidi_indexable pointers for non-assignment + // addition. + if (Opc == BO_Add && PExp->getType()->isIndexablePointerType()) { + QualType Ty = Context.getBoundsSafetyPointerType( + PExp->getType(), BoundsSafetyPointerAttributes::bidiIndexable()); + ExprResult Res = ImpCastExprToType(PExp, Ty, CK_BoundsSafetyPointerCast); + if (PExp == LHS.get()) + LHS = Res; + else + RHS = Res; + PExp = Res.get(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - const MatrixType *LHSMatType = LHSType->getAs(); - const MatrixType *RHSMatType = RHSType->getAs(); - assert((LHSMatType || RHSMatType) && "At least one operand must be a matrix"); - - if (Context.hasSameType(LHSType, RHSType)) - return Context.getCommonSugaredType(LHSType, RHSType); - - // Type conversion may change LHS/RHS. Keep copies to the original results, in - // case we have to return InvalidOperands. - ExprResult OriginalLHS = LHS; - ExprResult OriginalRHS = RHS; - if (LHSMatType && !RHSMatType) { - RHS = tryConvertExprToType(RHS.get(), LHSMatType->getElementType()); - if (!RHS.isInvalid()) - return LHSType; - - return InvalidOperands(Loc, OriginalLHS, OriginalRHS); - } - - if (!LHSMatType && RHSMatType) { - LHS = tryConvertExprToType(LHS.get(), RHSMatType->getElementType()); - if (!LHS.isInvalid()) - return RHSType; - return InvalidOperands(Loc, OriginalLHS, OriginalRHS); - } - - return InvalidOperands(Loc, LHS, RHS); -} - -QualType Sema::CheckMatrixMultiplyOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - bool IsCompAssign) { - if (!IsCompAssign) { - LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); - if (LHS.isInvalid()) - return QualType(); - } - RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) + // Arithmetic on label addresses is normally allowed, except when we add + // a ptrauth signature to the addresses. + if (isa(PExp) && getLangOpts().PointerAuthIndirectGotos) { + Diag(Loc, diag::err_ptrauth_indirect_goto_addrlabel_arithmetic) + << /*addition*/ 1; return QualType(); + } - auto *LHSMatType = LHS.get()->getType()->getAs(); - auto *RHSMatType = RHS.get()->getType()->getAs(); - assert((LHSMatType || RHSMatType) && "At least one operand must be a matrix"); - - if (LHSMatType && RHSMatType) { - if (LHSMatType->getNumColumns() != RHSMatType->getNumRows()) - return InvalidOperands(Loc, LHS, RHS); - - if (Context.hasSameType(LHSMatType, RHSMatType)) - return Context.getCommonSugaredType( - LHS.get()->getType().getUnqualifiedType(), - RHS.get()->getType().getUnqualifiedType()); - - QualType LHSELTy = LHSMatType->getElementType(), - RHSELTy = RHSMatType->getElementType(); - if (!Context.hasSameType(LHSELTy, RHSELTy)) - return InvalidOperands(Loc, LHS, RHS); + // Check array bounds for pointer arithemtic + CheckArrayAccess(PExp, IExp); - return Context.getConstantMatrixType( - Context.getCommonSugaredType(LHSELTy, RHSELTy), - LHSMatType->getNumRows(), RHSMatType->getNumColumns()); + if (CompLHSTy) { + QualType LHSTy = Context.isPromotableBitField(LHS.get()); + if (LHSTy.isNull()) { + LHSTy = LHS.get()->getType(); + if (Context.isPromotableIntegerType(LHSTy)) + LHSTy = Context.getPromotedIntegerType(LHSTy); + } + *CompLHSTy = LHSTy; } - return CheckMatrixElementwiseOperands(LHS, RHS, Loc, IsCompAssign); -} -static bool isLegalBoolVectorBinaryOp(BinaryOperatorKind Opc) { - switch (Opc) { - default: - return false; - case BO_And: - case BO_AndAssign: - case BO_Or: - case BO_OrAssign: - case BO_Xor: - case BO_XorAssign: - return true; - } + return PExp->getType(); } -inline QualType Sema::CheckBitwiseOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - BinaryOperatorKind Opc) { +// C99 6.5.6 +QualType Sema::CheckSubtractionOperands( + ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, + /*TO_UPSTREAM(BoundsSafety) ON*/ + BinaryOperatorKind Opc, + /*TO_UPSTREAM(BoundsSafety) OFF*/ + QualType *CompLHSTy) { checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); - bool IsCompAssign = - Opc == BO_AndAssign || Opc == BO_OrAssign || Opc == BO_XorAssign; - - bool LegalBoolVecOperator = isLegalBoolVectorBinaryOp(Opc); - if (LHS.get()->getType()->isVectorType() || RHS.get()->getType()->isVectorType()) { - if (LHS.get()->getType()->hasIntegerRepresentation() && - RHS.get()->getType()->hasIntegerRepresentation()) - return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, - /*AllowBothBool*/ true, - /*AllowBoolConversions*/ getLangOpts().ZVector, - /*AllowBooleanOperation*/ LegalBoolVecOperator, - /*ReportInvalid*/ true); - return InvalidOperands(Loc, LHS, RHS); + QualType compType = + CheckVectorOperands(LHS, RHS, Loc, CompLHSTy, + /*AllowBothBool*/ getLangOpts().AltiVec, + /*AllowBoolConversions*/ getLangOpts().ZVector, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); + if (CompLHSTy) *CompLHSTy = compType; + return compType; } if (LHS.get()->getType()->isSveVLSBuiltinType() || RHS.get()->getType()->isSveVLSBuiltinType()) { - if (LHS.get()->getType()->hasIntegerRepresentation() && - RHS.get()->getType()->hasIntegerRepresentation()) - return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, - ACK_BitwiseOp); - return InvalidOperands(Loc, LHS, RHS); + QualType compType = + CheckSizelessVectorOperands(LHS, RHS, Loc, CompLHSTy, ACK_Arithmetic); + if (CompLHSTy) + *CompLHSTy = compType; + return compType; } - if (LHS.get()->getType()->isSveVLSBuiltinType() || - RHS.get()->getType()->isSveVLSBuiltinType()) { - if (LHS.get()->getType()->hasIntegerRepresentation() && - RHS.get()->getType()->hasIntegerRepresentation()) - return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, - ACK_BitwiseOp); - return InvalidOperands(Loc, LHS, RHS); + if (LHS.get()->getType()->isConstantMatrixType() || + RHS.get()->getType()->isConstantMatrixType()) { + QualType compType = + CheckMatrixElementwiseOperands(LHS, RHS, Loc, CompLHSTy); + if (CompLHSTy) + *CompLHSTy = compType; + return compType; } - if (Opc == BO_And) - diagnoseLogicalNotOnLHSofCheck(*this, LHS, RHS, Loc, Opc); - - if (LHS.get()->getType()->hasFloatingRepresentation() || - RHS.get()->getType()->hasFloatingRepresentation()) - return InvalidOperands(Loc, LHS, RHS); - - ExprResult LHSResult = LHS, RHSResult = RHS; QualType compType = UsualArithmeticConversions( - LHSResult, RHSResult, Loc, IsCompAssign ? ACK_CompAssign : ACK_BitwiseOp); - if (LHSResult.isInvalid() || RHSResult.isInvalid()) + LHS, RHS, Loc, CompLHSTy ? ACK_CompAssign : ACK_Arithmetic); + if (LHS.isInvalid() || RHS.isInvalid()) return QualType(); - LHS = LHSResult.get(); - RHS = RHSResult.get(); - if (Opc == BO_Xor) - diagnoseXorMisusedAsPow(*this, LHS, RHS, Loc); + // Enforce type constraints: C99 6.5.6p3. - if (!compType.isNull() && compType->isIntegralOrUnscopedEnumerationType()) + // Handle the common case first (both operands are arithmetic). + if (!compType.isNull() && compType->isArithmeticType()) { + if (CompLHSTy) *CompLHSTy = compType; return compType; - return InvalidOperands(Loc, LHS, RHS); -} - -// C99 6.5.[13,14] -inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc, - BinaryOperatorKind Opc) { - // Check vector operands differently. - if (LHS.get()->getType()->isVectorType() || - RHS.get()->getType()->isVectorType()) - return CheckVectorLogicalOperands(LHS, RHS, Loc, Opc); - - bool EnumConstantInBoolContext = false; - for (const ExprResult &HS : {LHS, RHS}) { - if (const auto *DREHS = dyn_cast(HS.get())) { - const auto *ECDHS = dyn_cast(DREHS->getDecl()); - if (ECDHS && ECDHS->getInitVal() != 0 && ECDHS->getInitVal() != 1) - EnumConstantInBoolContext = true; - } - } - - if (EnumConstantInBoolContext) - Diag(Loc, diag::warn_enum_constant_in_bool_context); - - // WebAssembly tables can't be used with logical operators. - QualType LHSTy = LHS.get()->getType(); - QualType RHSTy = RHS.get()->getType(); - const auto *LHSATy = dyn_cast(LHSTy); - const auto *RHSATy = dyn_cast(RHSTy); - if ((LHSATy && LHSATy->getElementType().isWebAssemblyReferenceType()) || - (RHSATy && RHSATy->getElementType().isWebAssemblyReferenceType())) { - return InvalidOperands(Loc, LHS, RHS); - } - - // Diagnose cases where the user write a logical and/or but probably meant a - // bitwise one. We do this when the LHS is a non-bool integer and the RHS - // is a constant. - if (!EnumConstantInBoolContext && LHS.get()->getType()->isIntegerType() && - !LHS.get()->getType()->isBooleanType() && - RHS.get()->getType()->isIntegerType() && !RHS.get()->isValueDependent() && - // Don't warn in macros or template instantiations. - !Loc.isMacroID() && !inTemplateInstantiation()) { - // If the RHS can be constant folded, and if it constant folds to something - // that isn't 0 or 1 (which indicate a potential logical operation that - // happened to fold to true/false) then warn. - // Parens on the RHS are ignored. - Expr::EvalResult EVResult; - if (RHS.get()->EvaluateAsInt(EVResult, Context)) { - llvm::APSInt Result = EVResult.Val.getInt(); - if ((getLangOpts().CPlusPlus && !RHS.get()->getType()->isBooleanType() && - !RHS.get()->getExprLoc().isMacroID()) || - (Result != 0 && Result != 1)) { - Diag(Loc, diag::warn_logical_instead_of_bitwise) - << RHS.get()->getSourceRange() << (Opc == BO_LAnd ? "&&" : "||"); - // Suggest replacing the logical operator with the bitwise version - Diag(Loc, diag::note_logical_instead_of_bitwise_change_operator) - << (Opc == BO_LAnd ? "&" : "|") - << FixItHint::CreateReplacement( - SourceRange(Loc, getLocForEndOfToken(Loc)), - Opc == BO_LAnd ? "&" : "|"); - if (Opc == BO_LAnd) - // Suggest replacing "Foo() && kNonZero" with "Foo()" - Diag(Loc, diag::note_logical_instead_of_bitwise_remove_constant) - << FixItHint::CreateRemoval( - SourceRange(getLocForEndOfToken(LHS.get()->getEndLoc()), - RHS.get()->getEndLoc())); - } - } } - if (!Context.getLangOpts().CPlusPlus) { - // OpenCL v1.1 s6.3.g: The logical operators and (&&), or (||) do - // not operate on the built-in scalar and vector float types. - if (Context.getLangOpts().OpenCL && - Context.getLangOpts().OpenCLVersion < 120) { - if (LHS.get()->getType()->isFloatingType() || - RHS.get()->getType()->isFloatingType()) - return InvalidOperands(Loc, LHS, RHS); - } + // Either ptr - int or ptr - ptr. + if (LHS.get()->getType()->isAnyPointerType()) { + QualType lpointee = LHS.get()->getType()->getPointeeType(); - LHS = UsualUnaryConversions(LHS.get()); - if (LHS.isInvalid()) + // Diagnose bad cases where we step over interface counts. + if (LHS.get()->getType()->isObjCObjectPointerType() && + checkArithmeticOnObjCPointer(*this, Loc, LHS.get())) return QualType(); - RHS = UsualUnaryConversions(RHS.get()); - if (RHS.isInvalid()) + // Arithmetic on label addresses is normally allowed, except when we add + // a ptrauth signature to the addresses. + if (isa(LHS.get()) && + getLangOpts().PointerAuthIndirectGotos) { + Diag(Loc, diag::err_ptrauth_indirect_goto_addrlabel_arithmetic) + << /*subtraction*/ 0; return QualType(); + } - if (!LHS.get()->getType()->isScalarType() || - !RHS.get()->getType()->isScalarType()) - return InvalidOperands(Loc, LHS, RHS); - - return Context.IntTy; - } - - // The following is safe because we only use this method for - // non-overloadable operands. - - // C++ [expr.log.and]p1 - // C++ [expr.log.or]p1 - // The operands are both contextually converted to type bool. - ExprResult LHSRes = PerformContextuallyConvertToBool(LHS.get()); - if (LHSRes.isInvalid()) - return InvalidOperands(Loc, LHS, RHS); - LHS = LHSRes; + // The result type of a pointer-int computation is the pointer type. + if (RHS.get()->getType()->isIntegerType()) { + // Subtracting from a null pointer should produce a warning. + // The last argument to the diagnose call says this doesn't match the + // GNU int-to-pointer idiom. + if (LHS.get()->IgnoreParenCasts()->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNotNull)) { + // In C++ adding zero to a null pointer is defined. + Expr::EvalResult KnownVal; + if (!getLangOpts().CPlusPlus || + (!RHS.get()->isValueDependent() && + (!RHS.get()->EvaluateAsInt(KnownVal, Context) || + KnownVal.Val.getInt() != 0))) { + diagnoseArithmeticOnNullPointer(*this, Loc, LHS.get(), false); + } + } - ExprResult RHSRes = PerformContextuallyConvertToBool(RHS.get()); - if (RHSRes.isInvalid()) - return InvalidOperands(Loc, LHS, RHS); - RHS = RHSRes; + if (!checkArithmeticOpPointerOperand(*this, Loc, LHS.get())) + return QualType(); - // C++ [expr.log.and]p2 - // C++ [expr.log.or]p2 - // The result is a bool. - return Context.BoolTy; -} + // Check array bounds for pointer arithemtic + CheckArrayAccess(LHS.get(), RHS.get(), /*ArraySubscriptExpr*/nullptr, + /*AllowOnePastEnd*/true, /*IndexNegated*/true); -static bool IsReadonlyMessage(Expr *E, Sema &S) { - const MemberExpr *ME = dyn_cast(E); - if (!ME) return false; - if (!isa(ME->getMemberDecl())) return false; - ObjCMessageExpr *Base = dyn_cast( - ME->getBase()->IgnoreImplicit()->IgnoreParenImpCasts()); - if (!Base) return false; - return Base->getMethodDecl() != nullptr; -} + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety && !checkArithmeticBinOpBoundsSafetyPointer( + *this, LHS.get(), RHS.get(), + /*IndexNegated*/ true, Opc, Loc)) + return QualType(); + // If the pointer in pointer-int substractions is __indexable, cast it + // to __bidi_indexable. Ignore pointer-pointer substractions here. + bool IsSubAssign = CompLHSTy; + if (!IsSubAssign && LHS.get()->getType()->isIndexablePointerType() && + !RHS.get()->getType()->isPointerType()) { + QualType Ty = Context.getBoundsSafetyPointerType( + LHS.get()->getType(), BoundsSafetyPointerAttributes::bidiIndexable()); + LHS = ImpCastExprToType(LHS.get(), Ty, CK_BoundsSafetyPointerCast); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ -/// Is the given expression (which must be 'const') a reference to a -/// variable which was originally non-const, but which has become -/// 'const' due to being captured within a block? -enum NonConstCaptureKind { NCCK_None, NCCK_Block, NCCK_Lambda }; -static NonConstCaptureKind isReferenceToNonConstCapture(Sema &S, Expr *E) { - assert(E->isLValue() && E->getType().isConstQualified()); - E = E->IgnoreParens(); + if (CompLHSTy) *CompLHSTy = LHS.get()->getType(); + return LHS.get()->getType(); + } - // Must be a reference to a declaration from an enclosing scope. - DeclRefExpr *DRE = dyn_cast(E); - if (!DRE) return NCCK_None; - if (!DRE->refersToEnclosingVariableOrCapture()) return NCCK_None; + // Handle pointer-pointer subtractions. + if (const PointerType *RHSPTy + = RHS.get()->getType()->getAs()) { + QualType rpointee = RHSPTy->getPointeeType(); - ValueDecl *Value = dyn_cast(DRE->getDecl()); + if (getLangOpts().CPlusPlus) { + // Pointee types must be the same: C++ [expr.add] + if (!Context.hasSameUnqualifiedType(lpointee, rpointee)) { + diagnosePointerIncompatibility(*this, Loc, LHS.get(), RHS.get()); + } + } else { + // Pointee types must be compatible C99 6.5.6p3 + if (!Context.typesAreCompatible( + Context.getCanonicalType(lpointee).getUnqualifiedType(), + Context.getCanonicalType(rpointee).getUnqualifiedType())) { + diagnosePointerIncompatibility(*this, Loc, LHS.get(), RHS.get()); + return QualType(); + } + } - // The declaration must be a value which is not declared 'const'. - if (!Value || Value->getType().isConstQualified()) - return NCCK_None; + if (!checkArithmeticBinOpPointerOperands(*this, Loc, + LHS.get(), RHS.get())) + return QualType(); - BindingDecl *Binding = dyn_cast(Value); - if (Binding) { - assert(S.getLangOpts().CPlusPlus && "BindingDecl outside of C++?"); - assert(!isa(Binding->getDeclContext())); - return NCCK_Lambda; - } + bool LHSIsNullPtr = LHS.get()->IgnoreParenCasts()->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNotNull); + bool RHSIsNullPtr = RHS.get()->IgnoreParenCasts()->isNullPointerConstant( + Context, Expr::NPC_ValueDependentIsNotNull); - VarDecl *Var = dyn_cast(Value); - if (!Var) - return NCCK_None; + // Subtracting nullptr or from nullptr is suspect + if (LHSIsNullPtr) + diagnoseSubtractionOnNullPointer(*this, Loc, LHS.get(), RHSIsNullPtr); + if (RHSIsNullPtr) + diagnoseSubtractionOnNullPointer(*this, Loc, RHS.get(), LHSIsNullPtr); - assert(Var->hasLocalStorage() && "capture added 'const' to non-local?"); + // The pointee type may have zero size. As an extension, a structure or + // union may have zero size or an array may have zero length. In this + // case subtraction does not make sense. + if (!rpointee->isVoidType() && !rpointee->isFunctionType()) { + CharUnits ElementSize = Context.getTypeSizeInChars(rpointee); + if (ElementSize.isZero()) { + Diag(Loc,diag::warn_sub_ptr_zero_size_types) + << rpointee.getUnqualifiedType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + } + } - // Decide whether the first capture was for a block or a lambda. - DeclContext *DC = S.CurContext, *Prev = nullptr; - // Decide whether the first capture was for a block or a lambda. - while (DC) { - // For init-capture, it is possible that the variable belongs to the - // template pattern of the current context. - if (auto *FD = dyn_cast(DC)) - if (Var->isInitCapture() && - FD->getTemplateInstantiationPattern() == Var->getDeclContext()) - break; - if (DC == Var->getDeclContext()) - break; - Prev = DC; - DC = DC->getParent(); + if (CompLHSTy) *CompLHSTy = LHS.get()->getType(); + return Context.getPointerDiffType(); + } } - // Unless we have an init-capture, we've gone one step too far. - if (!Var->isInitCapture()) - DC = Prev; - return (isa(DC) ? NCCK_Block : NCCK_Lambda); + + return InvalidOperands(Loc, LHS, RHS); } -static bool IsTypeModifiable(QualType Ty, bool IsDereference) { - Ty = Ty.getNonReferenceType(); - if (IsDereference && Ty->isPointerType()) - Ty = Ty->getPointeeType(); - return !Ty.isConstQualified(); +static bool isScopedEnumerationType(QualType T) { + if (const EnumType *ET = T->getAs()) + return ET->getDecl()->isScoped(); + return false; } -// Update err_typecheck_assign_const and note_typecheck_assign_const -// when this enum is changed. -enum { - ConstFunction, - ConstVariable, - ConstMember, - ConstMethod, - NestedConstMember, - ConstUnknown, // Keep as last element -}; +static void DiagnoseBadShiftValues(Sema& S, ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, BinaryOperatorKind Opc, + QualType LHSType) { + // OpenCL 6.3j: shift values are effectively % word size of LHS (more defined), + // so skip remaining warnings as we don't want to modify values within Sema. + if (S.getLangOpts().OpenCL) + return; -/// Emit the "read-only variable not assignable" error and print notes to give -/// more information about why the variable is not assignable, such as pointing -/// to the declaration of a const variable, showing that a method is const, or -/// that the function is returning a const reference. -static void DiagnoseConstAssignment(Sema &S, const Expr *E, - SourceLocation Loc) { - SourceRange ExprRange = E->getSourceRange(); + if (Opc == BO_Shr && + LHS.get()->IgnoreParenImpCasts()->getType()->isBooleanType()) + S.Diag(Loc, diag::warn_shift_bool) << LHS.get()->getSourceRange(); - // Only emit one error on the first const found. All other consts will emit - // a note to the error. - bool DiagnosticEmitted = false; + // Check right/shifter operand + Expr::EvalResult RHSResult; + if (RHS.get()->isValueDependent() || + !RHS.get()->EvaluateAsInt(RHSResult, S.Context)) + return; + llvm::APSInt Right = RHSResult.Val.getInt(); - // Track if the current expression is the result of a dereference, and if the - // next checked expression is the result of a dereference. - bool IsDereference = false; - bool NextIsDereference = false; + if (Right.isNegative()) { + S.DiagRuntimeBehavior(Loc, RHS.get(), + S.PDiag(diag::warn_shift_negative) + << RHS.get()->getSourceRange()); + return; + } - // Loop to process MemberExpr chains. - while (true) { - IsDereference = NextIsDereference; + QualType LHSExprType = LHS.get()->getType(); + uint64_t LeftSize = S.Context.getTypeSize(LHSExprType); + if (LHSExprType->isBitIntType()) + LeftSize = S.Context.getIntWidth(LHSExprType); + else if (LHSExprType->isFixedPointType()) { + auto FXSema = S.Context.getFixedPointSemantics(LHSExprType); + LeftSize = FXSema.getWidth() - (unsigned)FXSema.hasUnsignedPadding(); + } + if (Right.uge(LeftSize)) { + S.DiagRuntimeBehavior(Loc, RHS.get(), + S.PDiag(diag::warn_shift_gt_typewidth) + << RHS.get()->getSourceRange()); + return; + } - E = E->IgnoreImplicit()->IgnoreParenImpCasts(); - if (const MemberExpr *ME = dyn_cast(E)) { - NextIsDereference = ME->isArrow(); - const ValueDecl *VD = ME->getMemberDecl(); - if (const FieldDecl *Field = dyn_cast(VD)) { - // Mutable fields can be modified even if the class is const. - if (Field->isMutable()) { - assert(DiagnosticEmitted && "Expected diagnostic not emitted."); - break; - } + // FIXME: We probably need to handle fixed point types specially here. + if (Opc != BO_Shl || LHSExprType->isFixedPointType()) + return; - if (!IsTypeModifiable(Field->getType(), IsDereference)) { - if (!DiagnosticEmitted) { - S.Diag(Loc, diag::err_typecheck_assign_const) - << ExprRange << ConstMember << false /*static*/ << Field - << Field->getType(); - DiagnosticEmitted = true; - } - S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) - << ConstMember << false /*static*/ << Field << Field->getType() - << Field->getSourceRange(); - } - E = ME->getBase(); - continue; - } else if (const VarDecl *VDecl = dyn_cast(VD)) { - if (VDecl->getType().isConstQualified()) { - if (!DiagnosticEmitted) { - S.Diag(Loc, diag::err_typecheck_assign_const) - << ExprRange << ConstMember << true /*static*/ << VDecl - << VDecl->getType(); - DiagnosticEmitted = true; - } - S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) - << ConstMember << true /*static*/ << VDecl << VDecl->getType() - << VDecl->getSourceRange(); - } - // Static fields do not inherit constness from parents. - break; - } - break; // End MemberExpr - } else if (const ArraySubscriptExpr *ASE = - dyn_cast(E)) { - E = ASE->getBase()->IgnoreParenImpCasts(); - continue; - } else if (const ExtVectorElementExpr *EVE = - dyn_cast(E)) { - E = EVE->getBase()->IgnoreParenImpCasts(); - continue; - } - break; - } + // When left shifting an ICE which is signed, we can check for overflow which + // according to C++ standards prior to C++2a has undefined behavior + // ([expr.shift] 5.8/2). Unsigned integers have defined behavior modulo one + // more than the maximum value representable in the result type, so never + // warn for those. (FIXME: Unsigned left-shift overflow in a constant + // expression is still probably a bug.) + Expr::EvalResult LHSResult; + if (LHS.get()->isValueDependent() || + LHSType->hasUnsignedIntegerRepresentation() || + !LHS.get()->EvaluateAsInt(LHSResult, S.Context)) + return; + llvm::APSInt Left = LHSResult.Val.getInt(); - if (const CallExpr *CE = dyn_cast(E)) { - // Function calls - const FunctionDecl *FD = CE->getDirectCallee(); - if (FD && !IsTypeModifiable(FD->getReturnType(), IsDereference)) { - if (!DiagnosticEmitted) { - S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange - << ConstFunction << FD; - DiagnosticEmitted = true; - } - S.Diag(FD->getReturnTypeSourceRange().getBegin(), - diag::note_typecheck_assign_const) - << ConstFunction << FD << FD->getReturnType() - << FD->getReturnTypeSourceRange(); - } - } else if (const DeclRefExpr *DRE = dyn_cast(E)) { - // Point to variable declaration. - if (const ValueDecl *VD = DRE->getDecl()) { - if (!IsTypeModifiable(VD->getType(), IsDereference)) { - if (!DiagnosticEmitted) { - S.Diag(Loc, diag::err_typecheck_assign_const) - << ExprRange << ConstVariable << VD << VD->getType(); - DiagnosticEmitted = true; - } - S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) - << ConstVariable << VD << VD->getType() << VD->getSourceRange(); - } - } - } else if (isa(E)) { - if (const DeclContext *DC = S.getFunctionLevelDeclContext()) { - if (const CXXMethodDecl *MD = dyn_cast(DC)) { - if (MD->isConst()) { - if (!DiagnosticEmitted) { - S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange - << ConstMethod << MD; - DiagnosticEmitted = true; - } - S.Diag(MD->getLocation(), diag::note_typecheck_assign_const) - << ConstMethod << MD << MD->getSourceRange(); - } - } - } + // Don't warn if signed overflow is defined, then all the rest of the + // diagnostics will not be triggered because the behavior is defined. + // Also don't warn in C++20 mode (and newer), as signed left shifts + // always wrap and never overflow. + if (S.getLangOpts().isSignedOverflowDefined() || S.getLangOpts().CPlusPlus20) + return; + + // If LHS does not have a non-negative value then, the + // behavior is undefined before C++2a. Warn about it. + if (Left.isNegative()) { + S.DiagRuntimeBehavior(Loc, LHS.get(), + S.PDiag(diag::warn_shift_lhs_negative) + << LHS.get()->getSourceRange()); + return; } - if (DiagnosticEmitted) + llvm::APInt ResultBits = + static_cast(Right) + Left.getSignificantBits(); + if (ResultBits.ule(LeftSize)) return; + llvm::APSInt Result = Left.extend(ResultBits.getLimitedValue()); + Result = Result.shl(Right); - // Can't determine a more specific message, so display the generic error. - S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange << ConstUnknown; -} + // Print the bit representation of the signed integer as an unsigned + // hexadecimal number. + SmallString<40> HexResult; + Result.toString(HexResult, 16, /*Signed =*/false, /*Literal =*/true); -enum OriginalExprKind { - OEK_Variable, - OEK_Member, - OEK_LValue -}; + // If we are only missing a sign bit, this is less likely to result in actual + // bugs -- if the result is cast back to an unsigned type, it will have the + // expected value. Thus we place this behind a different warning that can be + // turned off separately if needed. + if (ResultBits - 1 == LeftSize) { + S.Diag(Loc, diag::warn_shift_result_sets_sign_bit) + << HexResult << LHSType + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return; + } -static void DiagnoseRecursiveConstFields(Sema &S, const ValueDecl *VD, - const RecordType *Ty, - SourceLocation Loc, SourceRange Range, - OriginalExprKind OEK, - bool &DiagnosticEmitted) { - std::vector RecordTypeList; - RecordTypeList.push_back(Ty); - unsigned NextToCheckIndex = 0; - // We walk the record hierarchy breadth-first to ensure that we print - // diagnostics in field nesting order. - while (RecordTypeList.size() > NextToCheckIndex) { - bool IsNested = NextToCheckIndex > 0; - for (const FieldDecl *Field : - RecordTypeList[NextToCheckIndex]->getDecl()->fields()) { - // First, check every field for constness. - QualType FieldTy = Field->getType(); - if (FieldTy.isConstQualified()) { - if (!DiagnosticEmitted) { - S.Diag(Loc, diag::err_typecheck_assign_const) - << Range << NestedConstMember << OEK << VD - << IsNested << Field; - DiagnosticEmitted = true; - } - S.Diag(Field->getLocation(), diag::note_typecheck_assign_const) - << NestedConstMember << IsNested << Field - << FieldTy << Field->getSourceRange(); - } + S.Diag(Loc, diag::warn_shift_result_gt_typewidth) + << HexResult.str() << Result.getSignificantBits() << LHSType + << Left.getBitWidth() << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); +} - // Then we append it to the list to check next in order. - FieldTy = FieldTy.getCanonicalType(); - if (const auto *FieldRecTy = FieldTy->getAs()) { - if (!llvm::is_contained(RecordTypeList, FieldRecTy)) - RecordTypeList.push_back(FieldRecTy); - } - } - ++NextToCheckIndex; +/// Return the resulting type when a vector is shifted +/// by a scalar or vector shift amount. +static QualType checkVectorShift(Sema &S, ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, bool IsCompAssign) { + // OpenCL v1.1 s6.3.j says RHS can be a vector only if LHS is a vector. + if ((S.LangOpts.OpenCL || S.LangOpts.ZVector) && + !LHS.get()->getType()->isVectorType()) { + S.Diag(Loc, diag::err_shift_rhs_only_vector) + << RHS.get()->getType() << LHS.get()->getType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); } -} -/// Emit an error for the case where a record we are trying to assign to has a -/// const-qualified field somewhere in its hierarchy. -static void DiagnoseRecursiveConstFields(Sema &S, const Expr *E, - SourceLocation Loc) { - QualType Ty = E->getType(); - assert(Ty->isRecordType() && "lvalue was not record?"); - SourceRange Range = E->getSourceRange(); - const RecordType *RTy = Ty.getCanonicalType()->getAs(); - bool DiagEmitted = false; + if (!IsCompAssign) { + LHS = S.UsualUnaryConversions(LHS.get()); + if (LHS.isInvalid()) return QualType(); + } - if (const MemberExpr *ME = dyn_cast(E)) - DiagnoseRecursiveConstFields(S, ME->getMemberDecl(), RTy, Loc, - Range, OEK_Member, DiagEmitted); - else if (const DeclRefExpr *DRE = dyn_cast(E)) - DiagnoseRecursiveConstFields(S, DRE->getDecl(), RTy, Loc, - Range, OEK_Variable, DiagEmitted); - else - DiagnoseRecursiveConstFields(S, nullptr, RTy, Loc, - Range, OEK_LValue, DiagEmitted); - if (!DiagEmitted) - DiagnoseConstAssignment(S, E, Loc); -} + RHS = S.UsualUnaryConversions(RHS.get()); + if (RHS.isInvalid()) return QualType(); -/// CheckForModifiableLvalue - Verify that E is a modifiable lvalue. If not, -/// emit an error and return true. If so, return false. -static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { - assert(!E->hasPlaceholderType(BuiltinType::PseudoObject)); + QualType LHSType = LHS.get()->getType(); + // Note that LHS might be a scalar because the routine calls not only in + // OpenCL case. + const VectorType *LHSVecTy = LHSType->getAs(); + QualType LHSEleType = LHSVecTy ? LHSVecTy->getElementType() : LHSType; - S.CheckShadowingDeclModification(E, Loc); + // Note that RHS might not be a vector. + QualType RHSType = RHS.get()->getType(); + const VectorType *RHSVecTy = RHSType->getAs(); + QualType RHSEleType = RHSVecTy ? RHSVecTy->getElementType() : RHSType; - SourceLocation OrigLoc = Loc; - Expr::isModifiableLvalueResult IsLV = E->isModifiableLvalue(S.Context, - &Loc); - if (IsLV == Expr::MLV_ClassTemporary && IsReadonlyMessage(E, S)) - IsLV = Expr::MLV_InvalidMessageExpression; - if (IsLV == Expr::MLV_Valid) - return false; + // Do not allow shifts for boolean vectors. + if ((LHSVecTy && LHSVecTy->isExtVectorBoolType()) || + (RHSVecTy && RHSVecTy->isExtVectorBoolType())) { + S.Diag(Loc, diag::err_typecheck_invalid_operands) + << LHS.get()->getType() << RHS.get()->getType() + << LHS.get()->getSourceRange(); + return QualType(); + } - unsigned DiagID = 0; - bool NeedType = false; - switch (IsLV) { // C99 6.5.16p2 - case Expr::MLV_ConstQualified: - // Use a specialized diagnostic when we're assigning to an object - // from an enclosing function or block. - if (NonConstCaptureKind NCCK = isReferenceToNonConstCapture(S, E)) { - if (NCCK == NCCK_Block) - DiagID = diag::err_block_decl_ref_not_modifiable_lvalue; - else - DiagID = diag::err_lambda_decl_ref_not_modifiable_lvalue; - break; - } + // The operands need to be integers. + if (!LHSEleType->isIntegerType()) { + S.Diag(Loc, diag::err_typecheck_expect_int) + << LHS.get()->getType() << LHS.get()->getSourceRange(); + return QualType(); + } - // In ARC, use some specialized diagnostics for occasions where we - // infer 'const'. These are always pseudo-strong variables. - if (S.getLangOpts().ObjCAutoRefCount) { - DeclRefExpr *declRef = dyn_cast(E->IgnoreParenCasts()); - if (declRef && isa(declRef->getDecl())) { - VarDecl *var = cast(declRef->getDecl()); + if (!RHSEleType->isIntegerType()) { + S.Diag(Loc, diag::err_typecheck_expect_int) + << RHS.get()->getType() << RHS.get()->getSourceRange(); + return QualType(); + } - // Use the normal diagnostic if it's pseudo-__strong but the - // user actually wrote 'const'. - if (var->isARCPseudoStrong() && - (!var->getTypeSourceInfo() || - !var->getTypeSourceInfo()->getType().isConstQualified())) { - // There are three pseudo-strong cases: - // - self - ObjCMethodDecl *method = S.getCurMethodDecl(); - if (method && var == method->getSelfDecl()) { - DiagID = method->isClassMethod() - ? diag::err_typecheck_arc_assign_self_class_method - : diag::err_typecheck_arc_assign_self; - - // - Objective-C externally_retained attribute. - } else if (var->hasAttr() || - isa(var)) { - DiagID = diag::err_typecheck_arc_assign_externally_retained; - - // - fast enumeration variables - } else { - DiagID = diag::err_typecheck_arr_assign_enumeration; - } - - SourceRange Assign; - if (Loc != OrigLoc) - Assign = SourceRange(OrigLoc, OrigLoc); - S.Diag(Loc, DiagID) << E->getSourceRange() << Assign; - // We need to preserve the AST regardless, so migration tool - // can do its job. - return false; - } - } + if (!LHSVecTy) { + assert(RHSVecTy); + if (IsCompAssign) + return RHSType; + if (LHSEleType != RHSEleType) { + LHS = S.ImpCastExprToType(LHS.get(),RHSEleType, CK_IntegralCast); + LHSEleType = RHSEleType; } - - // If none of the special cases above are triggered, then this is a - // simple const assignment. - if (DiagID == 0) { - DiagnoseConstAssignment(S, E, Loc); - return true; + QualType VecTy = + S.Context.getExtVectorType(LHSEleType, RHSVecTy->getNumElements()); + LHS = S.ImpCastExprToType(LHS.get(), VecTy, CK_VectorSplat); + LHSType = VecTy; + } else if (RHSVecTy) { + // OpenCL v1.1 s6.3.j says that for vector types, the operators + // are applied component-wise. So if RHS is a vector, then ensure + // that the number of elements is the same as LHS... + if (RHSVecTy->getNumElements() != LHSVecTy->getNumElements()) { + S.Diag(Loc, diag::err_typecheck_vector_lengths_not_equal) + << LHS.get()->getType() << RHS.get()->getType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); } - - break; - case Expr::MLV_ConstAddrSpace: - DiagnoseConstAssignment(S, E, Loc); - return true; - case Expr::MLV_ConstQualifiedField: - DiagnoseRecursiveConstFields(S, E, Loc); - return true; - case Expr::MLV_ArrayType: - case Expr::MLV_ArrayTemporary: - DiagID = diag::err_typecheck_array_not_modifiable_lvalue; - NeedType = true; - break; - case Expr::MLV_NotObjectType: - DiagID = diag::err_typecheck_non_object_not_modifiable_lvalue; - NeedType = true; - break; - case Expr::MLV_LValueCast: - DiagID = diag::err_typecheck_lvalue_casts_not_supported; - break; - case Expr::MLV_Valid: - llvm_unreachable("did not take early return for MLV_Valid"); - case Expr::MLV_InvalidExpression: - case Expr::MLV_MemberFunction: - case Expr::MLV_ClassTemporary: - DiagID = diag::err_typecheck_expression_not_modifiable_lvalue; - break; - case Expr::MLV_IncompleteType: - case Expr::MLV_IncompleteVoidType: - return S.RequireCompleteType(Loc, E->getType(), - diag::err_typecheck_incomplete_type_not_modifiable_lvalue, E); - case Expr::MLV_DuplicateVectorComponents: - DiagID = diag::err_typecheck_duplicate_vector_components_not_mlvalue; - break; - case Expr::MLV_NoSetterProperty: - llvm_unreachable("readonly properties should be processed differently"); - case Expr::MLV_InvalidMessageExpression: - DiagID = diag::err_readonly_message_assignment; - break; - case Expr::MLV_SubObjCPropertySetting: - DiagID = diag::err_no_subobject_property_setting; - break; + if (!S.LangOpts.OpenCL && !S.LangOpts.ZVector) { + const BuiltinType *LHSBT = LHSEleType->getAs(); + const BuiltinType *RHSBT = RHSEleType->getAs(); + if (LHSBT != RHSBT && + S.Context.getTypeSize(LHSBT) != S.Context.getTypeSize(RHSBT)) { + S.Diag(Loc, diag::warn_typecheck_vector_element_sizes_not_equal) + << LHS.get()->getType() << RHS.get()->getType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + } + } + } else { + // ...else expand RHS to match the number of elements in LHS. + QualType VecTy = + S.Context.getExtVectorType(RHSEleType, LHSVecTy->getNumElements()); + RHS = S.ImpCastExprToType(RHS.get(), VecTy, CK_VectorSplat); } - SourceRange Assign; - if (Loc != OrigLoc) - Assign = SourceRange(OrigLoc, OrigLoc); - if (NeedType) - S.Diag(Loc, DiagID) << E->getType() << E->getSourceRange() << Assign; - else - S.Diag(Loc, DiagID) << E->getSourceRange() << Assign; - return true; + return LHSType; } -static void CheckIdentityFieldAssignment(Expr *LHSExpr, Expr *RHSExpr, - SourceLocation Loc, - Sema &Sema) { - if (Sema.inTemplateInstantiation()) - return; - if (Sema.isUnevaluatedContext()) - return; - if (Loc.isInvalid() || Loc.isMacroID()) - return; - if (LHSExpr->getExprLoc().isMacroID() || RHSExpr->getExprLoc().isMacroID()) - return; - - // C / C++ fields - MemberExpr *ML = dyn_cast(LHSExpr); - MemberExpr *MR = dyn_cast(RHSExpr); - if (ML && MR) { - if (!(isa(ML->getBase()) && isa(MR->getBase()))) - return; - const ValueDecl *LHSDecl = - cast(ML->getMemberDecl()->getCanonicalDecl()); - const ValueDecl *RHSDecl = - cast(MR->getMemberDecl()->getCanonicalDecl()); - if (LHSDecl != RHSDecl) - return; - if (LHSDecl->getType().isVolatileQualified()) - return; - if (const ReferenceType *RefTy = LHSDecl->getType()->getAs()) - if (RefTy->getPointeeType().isVolatileQualified()) - return; - - Sema.Diag(Loc, diag::warn_identity_field_assign) << 0; - } - - // Objective-C instance variables - ObjCIvarRefExpr *OL = dyn_cast(LHSExpr); - ObjCIvarRefExpr *OR = dyn_cast(RHSExpr); - if (OL && OR && OL->getDecl() == OR->getDecl()) { - DeclRefExpr *RL = dyn_cast(OL->getBase()->IgnoreImpCasts()); - DeclRefExpr *RR = dyn_cast(OR->getBase()->IgnoreImpCasts()); - if (RL && RR && RL->getDecl() == RR->getDecl()) - Sema.Diag(Loc, diag::warn_identity_field_assign) << 1; +static QualType checkSizelessVectorShift(Sema &S, ExprResult &LHS, + ExprResult &RHS, SourceLocation Loc, + bool IsCompAssign) { + if (!IsCompAssign) { + LHS = S.UsualUnaryConversions(LHS.get()); + if (LHS.isInvalid()) + return QualType(); } -} - -// C99 6.5.16.1 -QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS, - SourceLocation Loc, - QualType CompoundType, - BinaryOperatorKind Opc) { - assert(!LHSExpr->hasPlaceholderType(BuiltinType::PseudoObject)); - // Verify that LHS is a modifiable lvalue, and emit error if not. - if (CheckForModifiableLvalue(LHSExpr, Loc, *this)) + RHS = S.UsualUnaryConversions(RHS.get()); + if (RHS.isInvalid()) return QualType(); - QualType LHSType = LHSExpr->getType(); - QualType RHSType = CompoundType.isNull() ? RHS.get()->getType() : - CompoundType; + QualType LHSType = LHS.get()->getType(); + const BuiltinType *LHSBuiltinTy = LHSType->castAs(); + QualType LHSEleType = LHSType->isSveVLSBuiltinType() + ? LHSBuiltinTy->getSveEltType(S.getASTContext()) + : LHSType; - if (RHS.isUsable()) { - // Even if this check fails don't return early to allow the best - // possible error recovery and to allow any subsequent diagnostics to - // work. - const ValueDecl *Assignee = nullptr; - bool ShowFullyQualifiedAssigneeName = false; - // In simple cases describe what is being assigned to - if (auto *DR = dyn_cast(LHSExpr->IgnoreParenCasts())) { - Assignee = DR->getDecl(); - } else if (auto *ME = dyn_cast(LHSExpr->IgnoreParenCasts())) { - Assignee = ME->getMemberDecl(); - ShowFullyQualifiedAssigneeName = true; - } + // Note that RHS might not be a vector + QualType RHSType = RHS.get()->getType(); + const BuiltinType *RHSBuiltinTy = RHSType->castAs(); + QualType RHSEleType = RHSType->isSveVLSBuiltinType() + ? RHSBuiltinTy->getSveEltType(S.getASTContext()) + : RHSType; - BoundsSafetyCheckAssignmentToCountAttrPtr( - LHSType, RHS.get(), AssignmentAction::Assigning, Loc, Assignee, - ShowFullyQualifiedAssigneeName); + if ((LHSBuiltinTy && LHSBuiltinTy->isSVEBool()) || + (RHSBuiltinTy && RHSBuiltinTy->isSVEBool())) { + S.Diag(Loc, diag::err_typecheck_invalid_operands) + << LHSType << RHSType << LHS.get()->getSourceRange(); + return QualType(); } - // OpenCL v1.2 s6.1.1.1 p2: - // The half data type can only be used to declare a pointer to a buffer that - // contains half values - if (getLangOpts().OpenCL && - !getOpenCLOptions().isAvailableOption("cl_khr_fp16", getLangOpts()) && - LHSType->isHalfType()) { - Diag(Loc, diag::err_opencl_half_load_store) << 1 - << LHSType.getUnqualifiedType(); + if (!LHSEleType->isIntegerType()) { + S.Diag(Loc, diag::err_typecheck_expect_int) + << LHS.get()->getType() << LHS.get()->getSourceRange(); return QualType(); } - // WebAssembly tables can't be used on RHS of an assignment expression. - if (RHSType->isWebAssemblyTableType()) { - Diag(Loc, diag::err_wasm_table_art) << 0; + if (!RHSEleType->isIntegerType()) { + S.Diag(Loc, diag::err_typecheck_expect_int) + << RHS.get()->getType() << RHS.get()->getSourceRange(); return QualType(); } - AssignConvertType ConvTy; - if (CompoundType.isNull()) { - Expr *RHSCheck = RHS.get(); - - CheckIdentityFieldAssignment(LHSExpr, RHSCheck, Loc, *this); - - QualType LHSTy(LHSType); - ConvTy = CheckSingleAssignmentConstraints(LHSTy, RHS); - if (RHS.isInvalid()) - return QualType(); - // Special case of NSObject attributes on c-style pointer types. - if (ConvTy == IncompatiblePointer && - ((Context.isObjCNSObjectType(LHSType) && - RHSType->isObjCObjectPointerType()) || - (Context.isObjCNSObjectType(RHSType) && - LHSType->isObjCObjectPointerType()))) - ConvTy = Compatible; + if (LHSType->isSveVLSBuiltinType() && RHSType->isSveVLSBuiltinType() && + (S.Context.getBuiltinVectorTypeInfo(LHSBuiltinTy).EC != + S.Context.getBuiltinVectorTypeInfo(RHSBuiltinTy).EC)) { + S.Diag(Loc, diag::err_typecheck_invalid_operands) + << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } - if (ConvTy == Compatible && - LHSType->isObjCObjectType()) - Diag(Loc, diag::err_objc_object_assignment) - << LHSType; - - // If the RHS is a unary plus or minus, check to see if they = and + are - // right next to each other. If so, the user may have typo'd "x =+ 4" - // instead of "x += 4". - if (ImplicitCastExpr *ICE = dyn_cast(RHSCheck)) - RHSCheck = ICE->getSubExpr(); - if (UnaryOperator *UO = dyn_cast(RHSCheck)) { - if ((UO->getOpcode() == UO_Plus || UO->getOpcode() == UO_Minus) && - Loc.isFileID() && UO->getOperatorLoc().isFileID() && - // Only if the two operators are exactly adjacent. - Loc.getLocWithOffset(1) == UO->getOperatorLoc() && - // And there is a space or other character before the subexpr of the - // unary +/-. We don't want to warn on "x=-1". - Loc.getLocWithOffset(2) != UO->getSubExpr()->getBeginLoc() && - UO->getSubExpr()->getBeginLoc().isFileID()) { - Diag(Loc, diag::warn_not_compound_assign) - << (UO->getOpcode() == UO_Plus ? "+" : "-") - << SourceRange(UO->getOperatorLoc(), UO->getOperatorLoc()); - } + if (!LHSType->isSveVLSBuiltinType()) { + assert(RHSType->isSveVLSBuiltinType()); + if (IsCompAssign) + return RHSType; + if (LHSEleType != RHSEleType) { + LHS = S.ImpCastExprToType(LHS.get(), RHSEleType, clang::CK_IntegralCast); + LHSEleType = RHSEleType; + } + const llvm::ElementCount VecSize = + S.Context.getBuiltinVectorTypeInfo(RHSBuiltinTy).EC; + QualType VecTy = + S.Context.getScalableVectorType(LHSEleType, VecSize.getKnownMinValue()); + LHS = S.ImpCastExprToType(LHS.get(), VecTy, clang::CK_VectorSplat); + LHSType = VecTy; + } else if (RHSBuiltinTy && RHSBuiltinTy->isSveVLSBuiltinType()) { + if (S.Context.getTypeSize(RHSBuiltinTy) != + S.Context.getTypeSize(LHSBuiltinTy)) { + S.Diag(Loc, diag::err_typecheck_vector_lengths_not_equal) + << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + return QualType(); + } + } else { + const llvm::ElementCount VecSize = + S.Context.getBuiltinVectorTypeInfo(LHSBuiltinTy).EC; + if (LHSEleType != RHSEleType) { + RHS = S.ImpCastExprToType(RHS.get(), LHSEleType, clang::CK_IntegralCast); + RHSEleType = LHSEleType; } + QualType VecTy = + S.Context.getScalableVectorType(RHSEleType, VecSize.getKnownMinValue()); + RHS = S.ImpCastExprToType(RHS.get(), VecTy, CK_VectorSplat); + } - if (ConvTy == Compatible) { - if (LHSType.getObjCLifetime() == Qualifiers::OCL_Strong) { - // Warn about retain cycles where a block captures the LHS, but - // not if the LHS is a simple variable into which the block is - // being stored...unless that variable can be captured by reference! - const Expr *InnerLHS = LHSExpr->IgnoreParenCasts(); - const DeclRefExpr *DRE = dyn_cast(InnerLHS); - if (!DRE || DRE->getDecl()->hasAttr()) - ObjC().checkRetainCycles(LHSExpr, RHS.get()); - } + return LHSType; +} - if (LHSType.getObjCLifetime() == Qualifiers::OCL_Strong || - LHSType.isNonWeakInMRRWithObjCWeak(Context)) { - // It is safe to assign a weak reference into a strong variable. - // Although this code can still have problems: - // id x = self.weakProp; - // id y = self.weakProp; - // we do not warn to warn spuriously when 'x' and 'y' are on separate - // paths through the function. This should be revisited if - // -Wrepeated-use-of-weak is made flow-sensitive. - // For ObjCWeak only, we do not warn if the assign is to a non-weak - // variable, which will be valid for the current autorelease scope. - if (!Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, - RHS.get()->getBeginLoc())) - getCurFunction()->markSafeWeakUse(RHS.get()); +// C99 6.5.7 +QualType Sema::CheckShiftOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, BinaryOperatorKind Opc, + bool IsCompAssign) { + checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); - } else if (getLangOpts().ObjCAutoRefCount || getLangOpts().ObjCWeak) { - checkUnsafeExprAssigns(Loc, LHSExpr, RHS.get()); - } + // Vector shifts promote their scalar inputs to vector type. + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) { + if (LangOpts.ZVector) { + // The shift operators for the z vector extensions work basically + // like general shifts, except that neither the LHS nor the RHS is + // allowed to be a "vector bool". + if (auto LHSVecType = LHS.get()->getType()->getAs()) + if (LHSVecType->getVectorKind() == VectorKind::AltiVecBool) + return InvalidOperands(Loc, LHS, RHS); + if (auto RHSVecType = RHS.get()->getType()->getAs()) + if (RHSVecType->getVectorKind() == VectorKind::AltiVecBool) + return InvalidOperands(Loc, LHS, RHS); } - } else { - // Compound assignment "x += y" - ConvTy = CheckAssignmentConstraints(Loc, LHSType, RHSType); + return checkVectorShift(*this, LHS, RHS, Loc, IsCompAssign); } - if (DiagnoseAssignmentResult(ConvTy, Loc, LHSType, RHSType, RHS.get(), - AssignmentAction::Assigning)) + if (LHS.get()->getType()->isSveVLSBuiltinType() || + RHS.get()->getType()->isSveVLSBuiltinType()) + return checkSizelessVectorShift(*this, LHS, RHS, Loc, IsCompAssign); + + // Shifts don't perform usual arithmetic conversions, they just do integer + // promotions on each operand. C99 6.5.7p3 + + // For the LHS, do usual unary conversions, but then reset them away + // if this is a compound assignment. + ExprResult OldLHS = LHS; + LHS = UsualUnaryConversions(LHS.get()); + if (LHS.isInvalid()) return QualType(); + QualType LHSType = LHS.get()->getType(); + if (IsCompAssign) LHS = OldLHS; - CheckForNullPointerDereference(*this, LHSExpr); + // The RHS is simpler. + RHS = UsualUnaryConversions(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + QualType RHSType = RHS.get()->getType(); - AssignedEntity AE{LHSExpr}; - checkAssignmentLifetime(*this, AE, RHS.get()); + // C99 6.5.7p2: Each of the operands shall have integer type. + // Embedded-C 4.1.6.2.2: The LHS may also be fixed-point. + if ((!LHSType->isFixedPointOrIntegerType() && + !LHSType->hasIntegerRepresentation()) || + !RHSType->hasIntegerRepresentation()) + return InvalidOperands(Loc, LHS, RHS); - if (getLangOpts().CPlusPlus20 && LHSType.isVolatileQualified()) { - if (CompoundType.isNull()) { - // C++2a [expr.ass]p5: - // A simple-assignment whose left operand is of a volatile-qualified - // type is deprecated unless the assignment is either a discarded-value - // expression or an unevaluated operand - ExprEvalContexts.back().VolatileAssignmentLHSs.push_back(LHSExpr); - } + // C++0x: Don't allow scoped enums. FIXME: Use something better than + // hasIntegerRepresentation() above instead of this. + if (isScopedEnumerationType(LHSType) || + isScopedEnumerationType(RHSType)) { + return InvalidOperands(Loc, LHS, RHS); } + DiagnoseBadShiftValues(*this, LHS, RHS, Loc, Opc, LHSType); - // C11 6.5.16p3: The type of an assignment expression is the type of the - // left operand would have after lvalue conversion. - // C11 6.3.2.1p2: ...this is called lvalue conversion. If the lvalue has - // qualified type, the value has the unqualified version of the type of the - // lvalue; additionally, if the lvalue has atomic type, the value has the - // non-atomic version of the type of the lvalue. - // C++ 5.17p1: the type of the assignment expression is that of its left - // operand. - return getLangOpts().CPlusPlus ? LHSType : LHSType.getAtomicUnqualifiedType(); + // "The type of the result is that of the promoted left operand." + return LHSType; } -// Scenarios to ignore if expression E is: -// 1. an explicit cast expression into void -// 2. a function call expression that returns void -static bool IgnoreCommaOperand(const Expr *E, const ASTContext &Context) { - E = E->IgnoreParens(); +/// Diagnose bad pointer comparisons. +static void diagnoseDistinctPointerComparison(Sema &S, SourceLocation Loc, + ExprResult &LHS, ExprResult &RHS, + bool IsError) { + S.Diag(Loc, IsError ? diag::err_typecheck_comparison_of_distinct_pointers + : diag::ext_typecheck_comparison_of_distinct_pointers) + << LHS.get()->getType() << RHS.get()->getType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); +} - if (const CastExpr *CE = dyn_cast(E)) { - if (CE->getCastKind() == CK_ToVoid) { - return true; - } +/// Returns false if the pointers are converted to a composite type, +/// true otherwise. +static bool convertPointersToCompositeType(Sema &S, SourceLocation Loc, + ExprResult &LHS, ExprResult &RHS) { + // C++ [expr.rel]p2: + // [...] Pointer conversions (4.10) and qualification + // conversions (4.4) are performed on pointer operands (or on + // a pointer operand and a null pointer constant) to bring + // them to their composite pointer type. [...] + // + // C++ [expr.eq]p1 uses the same notion for (in)equality + // comparisons of pointers. - // static_cast on a dependent type will not show up as CK_ToVoid. - if (CE->getCastKind() == CK_Dependent && E->getType()->isVoidType() && - CE->getSubExpr()->getType()->isDependentType()) { - return true; - } + QualType LHSType = LHS.get()->getType(); + QualType RHSType = RHS.get()->getType(); + assert(LHSType->isPointerType() || RHSType->isPointerType() || + LHSType->isMemberPointerType() || RHSType->isMemberPointerType()); + + QualType T = S.FindCompositePointerType(Loc, LHS, RHS); + if (T.isNull()) { + if ((LHSType->isAnyPointerType() || LHSType->isMemberPointerType()) && + (RHSType->isAnyPointerType() || RHSType->isMemberPointerType())) + diagnoseDistinctPointerComparison(S, Loc, LHS, RHS, /*isError*/true); + else + S.InvalidOperands(Loc, LHS, RHS); + return true; } - if (const auto *CE = dyn_cast(E)) - return CE->getCallReturnType(Context)->isVoidType(); return false; } -void Sema::DiagnoseCommaOperator(const Expr *LHS, SourceLocation Loc) { - // No warnings in macros - if (Loc.isMacroID()) - return; +static void diagnoseFunctionPointerToVoidComparison(Sema &S, SourceLocation Loc, + ExprResult &LHS, + ExprResult &RHS, + bool IsError) { + S.Diag(Loc, IsError ? diag::err_typecheck_comparison_of_fptr_to_void + : diag::ext_typecheck_comparison_of_fptr_to_void) + << LHS.get()->getType() << RHS.get()->getType() + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); +} - // Don't warn in template instantiations. - if (inTemplateInstantiation()) - return; +static bool isObjCObjectLiteral(ExprResult &E) { + switch (E.get()->IgnoreParenImpCasts()->getStmtClass()) { + case Stmt::ObjCArrayLiteralClass: + case Stmt::ObjCDictionaryLiteralClass: + case Stmt::ObjCStringLiteralClass: + case Stmt::ObjCBoxedExprClass: + return true; + default: + // Note that ObjCBoolLiteral is NOT an object literal! + return false; + } +} - // Scope isn't fine-grained enough to explicitly list the specific cases, so - // instead, skip more than needed, then call back into here with the - // CommaVisitor in SemaStmt.cpp. - // The listed locations are the initialization and increment portions - // of a for loop. The additional checks are on the condition of - // if statements, do/while loops, and for loops. - // Differences in scope flags for C89 mode requires the extra logic. - const unsigned ForIncrementFlags = - getLangOpts().C99 || getLangOpts().CPlusPlus - ? Scope::ControlScope | Scope::ContinueScope | Scope::BreakScope - : Scope::ContinueScope | Scope::BreakScope; - const unsigned ForInitFlags = Scope::ControlScope | Scope::DeclScope; - const unsigned ScopeFlags = getCurScope()->getFlags(); - if ((ScopeFlags & ForIncrementFlags) == ForIncrementFlags || - (ScopeFlags & ForInitFlags) == ForInitFlags) - return; +static bool hasIsEqualMethod(Sema &S, const Expr *LHS, const Expr *RHS) { + const ObjCObjectPointerType *Type = + LHS->getType()->getAs(); - // If there are multiple comma operators used together, get the RHS of the - // of the comma operator as the LHS. - while (const BinaryOperator *BO = dyn_cast(LHS)) { - if (BO->getOpcode() != BO_Comma) - break; - LHS = BO->getRHS(); + // If this is not actually an Objective-C object, bail out. + if (!Type) + return false; + + // Get the LHS object's interface type. + QualType InterfaceType = Type->getPointeeType(); + + // If the RHS isn't an Objective-C object, bail out. + if (!RHS->getType()->isObjCObjectPointerType()) + return false; + + // Try to find the -isEqual: method. + Selector IsEqualSel = S.ObjC().NSAPIObj->getIsEqualSelector(); + ObjCMethodDecl *Method = + S.ObjC().LookupMethodInObjectType(IsEqualSel, InterfaceType, + /*IsInstance=*/true); + if (!Method) { + if (Type->isObjCIdType()) { + // For 'id', just check the global pool. + Method = + S.ObjC().LookupInstanceMethodInGlobalPool(IsEqualSel, SourceRange(), + /*receiverId=*/true); + } else { + // Check protocols. + Method = S.ObjC().LookupMethodInQualifiedType(IsEqualSel, Type, + /*IsInstance=*/true); + } } - // Only allow some expressions on LHS to not warn. - if (IgnoreCommaOperand(LHS, Context)) - return; + if (!Method) + return false; - Diag(Loc, diag::warn_comma_operator); - Diag(LHS->getBeginLoc(), diag::note_cast_to_void) - << LHS->getSourceRange() - << FixItHint::CreateInsertion(LHS->getBeginLoc(), - LangOpts.CPlusPlus ? "static_cast(" - : "(void)(") - << FixItHint::CreateInsertion(PP.getLocForEndOfToken(LHS->getEndLoc()), - ")"); -} + QualType T = Method->parameters()[0]->getType(); + if (!T->isObjCObjectPointerType()) + return false; -// C99 6.5.17 -static QualType CheckCommaOperands(Sema &S, ExprResult &LHS, ExprResult &RHS, - SourceLocation Loc) { - LHS = S.CheckPlaceholderExpr(LHS.get()); - RHS = S.CheckPlaceholderExpr(RHS.get()); - if (LHS.isInvalid() || RHS.isInvalid()) - return QualType(); + QualType R = Method->getReturnType(); + if (!R->isScalarType()) + return false; - // C's comma performs lvalue conversion (C99 6.3.2.1) on both its - // operands, but not unary promotions. - // C++'s comma does not do any conversions at all (C++ [expr.comma]p1). + return true; +} - // So we treat the LHS as a ignored value, and in C++ we allow the - // containing site to determine what should be done with the RHS. - LHS = S.IgnoredValueConversions(LHS.get()); - if (LHS.isInvalid()) - return QualType(); +static void diagnoseObjCLiteralComparison(Sema &S, SourceLocation Loc, + ExprResult &LHS, ExprResult &RHS, + BinaryOperator::Opcode Opc){ + Expr *Literal; + Expr *Other; + if (isObjCObjectLiteral(LHS)) { + Literal = LHS.get(); + Other = RHS.get(); + } else { + Literal = RHS.get(); + Other = LHS.get(); + } - S.DiagnoseUnusedExprResult(LHS.get(), diag::warn_unused_comma_left_operand); + // Don't warn on comparisons against nil. + Other = Other->IgnoreParenCasts(); + if (Other->isNullPointerConstant(S.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) + return; - if (!S.getLangOpts().CPlusPlus) { - RHS = S.DefaultFunctionArrayLvalueConversion(RHS.get()); - if (RHS.isInvalid()) - return QualType(); - if (!RHS.get()->getType()->isVoidType()) - S.RequireCompleteType(Loc, RHS.get()->getType(), - diag::err_incomplete_type); + // This should be kept in sync with warn_objc_literal_comparison. + // LK_String should always be after the other literals, since it has its own + // warning flag. + SemaObjC::ObjCLiteralKind LiteralKind = S.ObjC().CheckLiteralKind(Literal); + assert(LiteralKind != SemaObjC::LK_Block); + if (LiteralKind == SemaObjC::LK_None) { + llvm_unreachable("Unknown Objective-C object literal kind"); } - if (!S.getDiagnostics().isIgnored(diag::warn_comma_operator, Loc)) - S.DiagnoseCommaOperator(LHS.get(), Loc); + if (LiteralKind == SemaObjC::LK_String) + S.Diag(Loc, diag::warn_objc_string_literal_comparison) + << Literal->getSourceRange(); + else + S.Diag(Loc, diag::warn_objc_literal_comparison) + << LiteralKind << Literal->getSourceRange(); - return RHS.get()->getType(); + if (BinaryOperator::isEqualityOp(Opc) && + hasIsEqualMethod(S, LHS.get(), RHS.get())) { + SourceLocation Start = LHS.get()->getBeginLoc(); + SourceLocation End = S.getLocForEndOfToken(RHS.get()->getEndLoc()); + CharSourceRange OpRange = + CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); + + S.Diag(Loc, diag::note_objc_literal_comparison_isequal) + << FixItHint::CreateInsertion(Start, Opc == BO_EQ ? "[" : "![") + << FixItHint::CreateReplacement(OpRange, " isEqual:") + << FixItHint::CreateInsertion(End, "]"); + } } -/// CheckIncrementDecrementOperand - unlike most "Check" methods, this routine -/// doesn't need to call UsualUnaryConversions or UsualArithmeticConversions. -static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op, - ExprValueKind &VK, - ExprObjectKind &OK, - SourceLocation OpLoc, bool IsInc, - bool IsPrefix) { - QualType ResType = Op->getType(); - // Atomic types can be used for increment / decrement where the non-atomic - // versions can, so ignore the _Atomic() specifier for the purpose of - // checking. - if (const AtomicType *ResAtomicType = ResType->getAs()) - ResType = ResAtomicType->getValueType(); +/// Warns on !x < y, !x & y where !(x < y), !(x & y) was probably intended. +static void diagnoseLogicalNotOnLHSofCheck(Sema &S, ExprResult &LHS, + ExprResult &RHS, SourceLocation Loc, + BinaryOperatorKind Opc) { + // Check that left hand side is !something. + UnaryOperator *UO = dyn_cast(LHS.get()->IgnoreImpCasts()); + if (!UO || UO->getOpcode() != UO_LNot) return; - assert(!ResType.isNull() && "no type for increment/decrement expression"); + // Only check if the right hand side is non-bool arithmetic type. + if (RHS.get()->isKnownToHaveBooleanValue()) return; - if (S.getLangOpts().CPlusPlus && ResType->isBooleanType()) { - // Decrement of bool is not allowed. - if (!IsInc) { - S.Diag(OpLoc, diag::err_decrement_bool) << Op->getSourceRange(); - return QualType(); - } - // Increment of bool sets it to true, but is deprecated. - S.Diag(OpLoc, S.getLangOpts().CPlusPlus17 ? diag::ext_increment_bool - : diag::warn_increment_bool) - << Op->getSourceRange(); - } else if (S.getLangOpts().CPlusPlus && ResType->isEnumeralType()) { - // Error on enum increments and decrements in C++ mode - S.Diag(OpLoc, diag::err_increment_decrement_enum) << IsInc << ResType; - return QualType(); - } else if (ResType->isRealType()) { - // OK! - } else if (ResType->isPointerType()) { - // C99 6.5.2.4p2, 6.5.6p2 - if (!checkArithmeticOpPointerOperand(S, OpLoc, Op)) - return QualType(); - } else if (ResType->isObjCObjectPointerType()) { - // On modern runtimes, ObjC pointer arithmetic is forbidden. - // Otherwise, we just need a complete type. - if (checkArithmeticIncompletePointerType(S, OpLoc, Op) || - checkArithmeticOnObjCPointer(S, OpLoc, Op)) - return QualType(); - } else if (ResType->isAnyComplexType()) { - // C99 does not support ++/-- on complex types, we allow as an extension. - S.Diag(OpLoc, S.getLangOpts().C2y ? diag::warn_c2y_compat_increment_complex - : diag::ext_c2y_increment_complex) - << IsInc << Op->getSourceRange(); - } else if (ResType->isPlaceholderType()) { - ExprResult PR = S.CheckPlaceholderExpr(Op); - if (PR.isInvalid()) return QualType(); - return CheckIncrementDecrementOperand(S, PR.get(), VK, OK, OpLoc, - IsInc, IsPrefix); - } else if (S.getLangOpts().AltiVec && ResType->isVectorType()) { - // OK! ( C/C++ Language Extensions for CBEA(Version 2.6) 10.3 ) - } else if (S.getLangOpts().ZVector && ResType->isVectorType() && - (ResType->castAs()->getVectorKind() != - VectorKind::AltiVecBool)) { - // The z vector extensions allow ++ and -- for non-bool vectors. - } else if (S.getLangOpts().OpenCL && ResType->isVectorType() && - ResType->castAs()->getElementType()->isIntegerType()) { - // OpenCL V1.2 6.3 says dec/inc ops operate on integer vector types. - } else { - S.Diag(OpLoc, diag::err_typecheck_illegal_increment_decrement) - << ResType << int(IsInc) << Op->getSourceRange(); - return QualType(); - } - // At this point, we know we have a real, complex or pointer type. - // Now make sure the operand is a modifiable lvalue. - if (CheckForModifiableLvalue(Op, OpLoc, S)) - return QualType(); - if (S.getLangOpts().CPlusPlus20 && ResType.isVolatileQualified()) { - // C++2a [expr.pre.inc]p1, [expr.post.inc]p1: - // An operand with volatile-qualified type is deprecated - S.Diag(OpLoc, diag::warn_deprecated_increment_decrement_volatile) - << IsInc << ResType; - } - // In C++, a prefix increment is the same type as the operand. Otherwise - // (in C or with postfix), the increment is the unqualified type of the - // operand. - if (IsPrefix && S.getLangOpts().CPlusPlus) { - VK = VK_LValue; - OK = Op->getObjectKind(); - return ResType; - } else { - VK = VK_PRValue; - return ResType.getUnqualifiedType(); + // Make sure that the something in !something is not bool. + Expr *SubExpr = UO->getSubExpr()->IgnoreImpCasts(); + if (SubExpr->isKnownToHaveBooleanValue()) return; + + // Emit warning. + bool IsBitwiseOp = Opc == BO_And || Opc == BO_Or || Opc == BO_Xor; + S.Diag(UO->getOperatorLoc(), diag::warn_logical_not_on_lhs_of_check) + << Loc << IsBitwiseOp; + + // First note suggest !(x < y) + SourceLocation FirstOpen = SubExpr->getBeginLoc(); + SourceLocation FirstClose = RHS.get()->getEndLoc(); + FirstClose = S.getLocForEndOfToken(FirstClose); + if (FirstClose.isInvalid()) + FirstOpen = SourceLocation(); + S.Diag(UO->getOperatorLoc(), diag::note_logical_not_fix) + << IsBitwiseOp + << FixItHint::CreateInsertion(FirstOpen, "(") + << FixItHint::CreateInsertion(FirstClose, ")"); + + // Second note suggests (!x) < y + SourceLocation SecondOpen = LHS.get()->getBeginLoc(); + SourceLocation SecondClose = LHS.get()->getEndLoc(); + SecondClose = S.getLocForEndOfToken(SecondClose); + if (SecondClose.isInvalid()) + SecondOpen = SourceLocation(); + S.Diag(UO->getOperatorLoc(), diag::note_logical_not_silence_with_parens) + << FixItHint::CreateInsertion(SecondOpen, "(") + << FixItHint::CreateInsertion(SecondClose, ")"); +} + +// Returns true if E refers to a non-weak array. +static bool checkForArray(const Expr *E) { + const ValueDecl *D = nullptr; + if (const DeclRefExpr *DR = dyn_cast(E)) { + D = DR->getDecl(); + } else if (const MemberExpr *Mem = dyn_cast(E)) { + if (Mem->isImplicitAccess()) + D = Mem->getMemberDecl(); } + if (!D) + return false; + return D->getType()->isArrayType() && !D->isWeak(); } -/// getPrimaryDecl - Helper function for CheckAddressOfOperand(). -/// This routine allows us to typecheck complex/recursive expressions -/// where the declaration is needed for type checking. We only need to -/// handle cases when the expression references a function designator -/// or is an lvalue. Here are some examples: -/// - &(x) => x -/// - &*****f => f for f a function designator. -/// - &s.xx => s -/// - &s.zz[1].yy -> s, if zz is an array -/// - *(x + 1) -> x, if x is an array -/// - &"123"[2] -> 0 -/// - & __real__ x -> x -/// -/// FIXME: We don't recurse to the RHS of a comma, nor handle pointers to -/// members. -static ValueDecl *getPrimaryDecl(Expr *E) { - switch (E->getStmtClass()) { - case Stmt::DeclRefExprClass: - return cast(E)->getDecl(); - case Stmt::MemberExprClass: - // If this is an arrow operator, the address is an offset from - // the base's value, so the object the base refers to is - // irrelevant. - if (cast(E)->isArrow()) - return nullptr; - // Otherwise, the expression refers to a part of the base - return getPrimaryDecl(cast(E)->getBase()); - case Stmt::ArraySubscriptExprClass: { - // FIXME: This code shouldn't be necessary! We should catch the implicit - // promotion of register arrays earlier. - Expr* Base = cast(E)->getBase(); - if (ImplicitCastExpr* ICE = dyn_cast(Base)) { - if (ICE->getSubExpr()->getType()->isArrayType()) - return getPrimaryDecl(ICE->getSubExpr()); - } - return nullptr; - } - case Stmt::UnaryOperatorClass: { - UnaryOperator *UO = cast(E); +/// Detect patterns ptr + size >= ptr and ptr + size < ptr, where ptr is a +/// pointer and size is an unsigned integer. Return whether the result is +/// always true/false. +static std::optional isTautologicalBoundsCheck(Sema &S, const Expr *LHS, + const Expr *RHS, + BinaryOperatorKind Opc) { + if (!LHS->getType()->isPointerType() || + S.getLangOpts().PointerOverflowDefined) + return std::nullopt; - switch(UO->getOpcode()) { - case UO_Real: - case UO_Imag: - case UO_Extension: - return getPrimaryDecl(UO->getSubExpr()); - default: - return nullptr; - } - } - case Stmt::ParenExprClass: - return getPrimaryDecl(cast(E)->getSubExpr()); - case Stmt::ImplicitCastExprClass: - // If the result of an implicit cast is an l-value, we care about - // the sub-expression; otherwise, the result here doesn't matter. - return getPrimaryDecl(cast(E)->getSubExpr()); - case Stmt::CXXUuidofExprClass: - return cast(E)->getGuidDecl(); + // Canonicalize to >= or < predicate. + switch (Opc) { + case BO_GE: + case BO_LT: + break; + case BO_GT: + std::swap(LHS, RHS); + Opc = BO_LT; + break; + case BO_LE: + std::swap(LHS, RHS); + Opc = BO_GE; + break; default: - return nullptr; + return std::nullopt; } -} - -namespace { -enum { - AO_Bit_Field = 0, - AO_Vector_Element = 1, - AO_Property_Expansion = 2, - AO_Register_Variable = 3, - AO_Matrix_Element = 4, - AO_No_Error = 5 -}; -} -/// Diagnose invalid operand for address of operations. -/// -/// \param Type The type of operand which cannot have its address taken. -static void diagnoseAddressOfInvalidType(Sema &S, SourceLocation Loc, - Expr *E, unsigned Type) { - S.Diag(Loc, diag::err_typecheck_address_of) << Type << E->getSourceRange(); -} - -bool Sema::CheckUseOfCXXMethodAsAddressOfOperand(SourceLocation OpLoc, - const Expr *Op, - const CXXMethodDecl *MD) { - const auto *DRE = cast(Op->IgnoreParens()); - if (Op != DRE) - return Diag(OpLoc, diag::err_parens_pointer_member_function) - << Op->getSourceRange(); - - // Taking the address of a dtor is illegal per C++ [class.dtor]p2. - if (isa(MD)) - return Diag(OpLoc, diag::err_typecheck_addrof_dtor) - << DRE->getSourceRange(); + auto *BO = dyn_cast(LHS); + if (!BO || BO->getOpcode() != BO_Add) + return std::nullopt; - if (DRE->getQualifier()) - return false; + Expr *Other; + if (Expr::isSameComparisonOperand(BO->getLHS(), RHS)) + Other = BO->getRHS(); + else if (Expr::isSameComparisonOperand(BO->getRHS(), RHS)) + Other = BO->getLHS(); + else + return std::nullopt; - if (MD->getParent()->getName().empty()) - return Diag(OpLoc, diag::err_unqualified_pointer_member_function) - << DRE->getSourceRange(); + if (!Other->getType()->isUnsignedIntegerType()) + return std::nullopt; - SmallString<32> Str; - StringRef Qual = (MD->getParent()->getName() + "::").toStringRef(Str); - return Diag(OpLoc, diag::err_unqualified_pointer_member_function) - << DRE->getSourceRange() - << FixItHint::CreateInsertion(DRE->getSourceRange().getBegin(), Qual); + return Opc == BO_GE; } -QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { - if (const BuiltinType *PTy = OrigOp.get()->getType()->getAsPlaceholderType()){ - if (PTy->getKind() == BuiltinType::Overload) { - Expr *E = OrigOp.get()->IgnoreParens(); - if (!isa(E)) { - assert(cast(E)->getOpcode() == UO_AddrOf); - Diag(OpLoc, diag::err_typecheck_invalid_lvalue_addrof_addrof_function) - << OrigOp.get()->getSourceRange(); - return QualType(); - } - - OverloadExpr *Ovl = cast(E); - if (isa(Ovl)) - if (!ResolveSingleFunctionTemplateSpecialization(Ovl)) { - Diag(OpLoc, diag::err_invalid_form_pointer_member_function) - << OrigOp.get()->getSourceRange(); - return QualType(); - } - - return Context.OverloadTy; - } - - if (PTy->getKind() == BuiltinType::UnknownAny) - return Context.UnknownAnyTy; - - if (PTy->getKind() == BuiltinType::BoundMember) { - Diag(OpLoc, diag::err_invalid_form_pointer_member_function) - << OrigOp.get()->getSourceRange(); - return QualType(); - } +/// Diagnose some forms of syntactically-obvious tautological comparison. +static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc, + Expr *LHS, Expr *RHS, + BinaryOperatorKind Opc) { + Expr *LHSStripped = LHS->IgnoreParenImpCasts(); + Expr *RHSStripped = RHS->IgnoreParenImpCasts(); - OrigOp = CheckPlaceholderExpr(OrigOp.get()); - if (OrigOp.isInvalid()) return QualType(); - } + QualType LHSType = LHS->getType(); + QualType RHSType = RHS->getType(); + if (LHSType->hasFloatingRepresentation() || + (LHSType->isBlockPointerType() && !BinaryOperator::isEqualityOp(Opc)) || + S.inTemplateInstantiation()) + return; - if (OrigOp.get()->isTypeDependent()) - return Context.DependentTy; + // WebAssembly Tables cannot be compared, therefore shouldn't emit + // Tautological diagnostics. + if (LHSType->isWebAssemblyTableType() || RHSType->isWebAssemblyTableType()) + return; - assert(!OrigOp.get()->hasPlaceholderType()); + // Comparisons between two array types are ill-formed for operator<=>, so + // we shouldn't emit any additional warnings about it. + if (Opc == BO_Cmp && LHSType->isArrayType() && RHSType->isArrayType()) + return; - // Make sure to ignore parentheses in subsequent checks - Expr *op = OrigOp.get()->IgnoreParens(); + // For non-floating point types, check for self-comparisons of the form + // x == x, x != x, x < x, etc. These always evaluate to a constant, and + // often indicate logic errors in the program. + // + // NOTE: Don't warn about comparison expressions resulting from macro + // expansion. Also don't warn about comparisons which are only self + // comparisons within a template instantiation. The warnings should catch + // obvious cases in the definition of the template anyways. The idea is to + // warn when the typed comparison operator will always evaluate to the same + // result. - // In OpenCL captures for blocks called as lambda functions - // are located in the private address space. Blocks used in - // enqueue_kernel can be located in a different address space - // depending on a vendor implementation. Thus preventing - // taking an address of the capture to avoid invalid AS casts. - if (LangOpts.OpenCL) { - auto* VarRef = dyn_cast(op); - if (VarRef && VarRef->refersToEnclosingVariableOrCapture()) { - Diag(op->getExprLoc(), diag::err_opencl_taking_address_capture); - return QualType(); - } - } + // Used for indexing into %select in warn_comparison_always + enum { + AlwaysConstant, + AlwaysTrue, + AlwaysFalse, + AlwaysEqual, // std::strong_ordering::equal from operator<=> + }; - if (getLangOpts().C99) { - // Implement C99-only parts of addressof rules. - if (UnaryOperator* uOp = dyn_cast(op)) { - if (uOp->getOpcode() == UO_Deref) - // Per C99 6.5.3.2, the address of a deref always returns a valid result - // (assuming the deref expression is valid). - return uOp->getSubExpr()->getType(); - } - // Technically, there should be a check for array subscript - // expressions here, but the result of one is always an lvalue anyway. + // C++1a [array.comp]: + // Equality and relational comparisons ([expr.eq], [expr.rel]) between two + // operands of array type. + // C++2a [depr.array.comp]: + // Equality and relational comparisons ([expr.eq], [expr.rel]) between two + // operands of array type are deprecated. + if (S.getLangOpts().CPlusPlus && LHSStripped->getType()->isArrayType() && + RHSStripped->getType()->isArrayType()) { + auto IsDeprArrayComparionIgnored = + S.getDiagnostics().isIgnored(diag::warn_depr_array_comparison, Loc); + auto DiagID = S.getLangOpts().CPlusPlus26 + ? diag::warn_array_comparison_cxx26 + : !S.getLangOpts().CPlusPlus20 || IsDeprArrayComparionIgnored + ? diag::warn_array_comparison + : diag::warn_depr_array_comparison; + S.Diag(Loc, DiagID) << LHS->getSourceRange() << RHS->getSourceRange() + << LHSStripped->getType() << RHSStripped->getType(); + // Carry on to produce the tautological comparison warning, if this + // expression is potentially-evaluated, we can resolve the array to a + // non-weak declaration, and so on. } - ValueDecl *dcl = getPrimaryDecl(op); - if (auto *FD = dyn_cast_or_null(dcl)) - if (!checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true, - op->getBeginLoc())) + if (!LHS->getBeginLoc().isMacroID() && !RHS->getBeginLoc().isMacroID()) { + if (Expr::isSameComparisonOperand(LHS, RHS)) { + unsigned Result; + switch (Opc) { + case BO_EQ: + case BO_LE: + case BO_GE: + Result = AlwaysTrue; + break; + case BO_NE: + case BO_LT: + case BO_GT: + Result = AlwaysFalse; + break; + case BO_Cmp: + Result = AlwaysEqual; + break; + default: + Result = AlwaysConstant; + break; + } + S.DiagRuntimeBehavior(Loc, nullptr, + S.PDiag(diag::warn_comparison_always) + << 0 /*self-comparison*/ + << Result); + } else if (checkForArray(LHSStripped) && checkForArray(RHSStripped)) { + // What is it always going to evaluate to? + unsigned Result; + switch (Opc) { + case BO_EQ: // e.g. array1 == array2 + Result = AlwaysFalse; + break; + case BO_NE: // e.g. array1 != array2 + Result = AlwaysTrue; + break; + default: // e.g. array1 <= array2 + // The best we can say is 'a constant' + Result = AlwaysConstant; + break; + } + S.DiagRuntimeBehavior(Loc, nullptr, + S.PDiag(diag::warn_comparison_always) + << 1 /*array comparison*/ + << Result); + } else if (std::optional Res = + isTautologicalBoundsCheck(S, LHS, RHS, Opc)) { + S.DiagRuntimeBehavior(Loc, nullptr, + S.PDiag(diag::warn_comparison_always) + << 2 /*pointer comparison*/ + << (*Res ? AlwaysTrue : AlwaysFalse)); + } + } + + if (isa(LHSStripped)) + LHSStripped = LHSStripped->IgnoreParenCasts(); + if (isa(RHSStripped)) + RHSStripped = RHSStripped->IgnoreParenCasts(); + + // Warn about comparisons against a string constant (unless the other + // operand is null); the user probably wants string comparison function. + Expr *LiteralString = nullptr; + Expr *LiteralStringStripped = nullptr; + if ((isa(LHSStripped) || isa(LHSStripped)) && + !RHSStripped->isNullPointerConstant(S.Context, + Expr::NPC_ValueDependentIsNull)) { + LiteralString = LHS; + LiteralStringStripped = LHSStripped; + } else if ((isa(RHSStripped) || + isa(RHSStripped)) && + !LHSStripped->isNullPointerConstant(S.Context, + Expr::NPC_ValueDependentIsNull)) { + LiteralString = RHS; + LiteralStringStripped = RHSStripped; + } + + if (LiteralString) { + S.DiagRuntimeBehavior(Loc, nullptr, + S.PDiag(diag::warn_stringcompare) + << isa(LiteralStringStripped) + << LiteralString->getSourceRange()); + } +} + +static ImplicitConversionKind castKindToImplicitConversionKind(CastKind CK) { + switch (CK) { + default: { +#ifndef NDEBUG + llvm::errs() << "unhandled cast kind: " << CastExpr::getCastKindName(CK) + << "\n"; +#endif + llvm_unreachable("unhandled cast kind"); + } + case CK_UserDefinedConversion: + return ICK_Identity; + case CK_LValueToRValue: + return ICK_Lvalue_To_Rvalue; + case CK_ArrayToPointerDecay: + return ICK_Array_To_Pointer; + case CK_FunctionToPointerDecay: + return ICK_Function_To_Pointer; + case CK_IntegralCast: + return ICK_Integral_Conversion; + case CK_FloatingCast: + return ICK_Floating_Conversion; + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + return ICK_Floating_Integral; + case CK_IntegralComplexCast: + case CK_FloatingComplexCast: + case CK_FloatingComplexToIntegralComplex: + case CK_IntegralComplexToFloatingComplex: + return ICK_Complex_Conversion; + case CK_FloatingComplexToReal: + case CK_FloatingRealToComplex: + case CK_IntegralComplexToReal: + case CK_IntegralRealToComplex: + return ICK_Complex_Real; + case CK_HLSLArrayRValue: + return ICK_HLSL_Array_RValue; + } +} + +static bool checkThreeWayNarrowingConversion(Sema &S, QualType ToType, Expr *E, + QualType FromType, + SourceLocation Loc) { + // Check for a narrowing implicit conversion. + StandardConversionSequence SCS; + SCS.setAsIdentityConversion(); + SCS.setToType(0, FromType); + SCS.setToType(1, ToType); + if (const auto *ICE = dyn_cast(E)) + SCS.Second = castKindToImplicitConversionKind(ICE->getCastKind()); + + APValue PreNarrowingValue; + QualType PreNarrowingType; + switch (SCS.getNarrowingKind(S.Context, E, PreNarrowingValue, + PreNarrowingType, + /*IgnoreFloatToIntegralConversion*/ true)) { + case NK_Dependent_Narrowing: + // Implicit conversion to a narrower type, but the expression is + // value-dependent so we can't tell whether it's actually narrowing. + case NK_Not_Narrowing: + return false; + + case NK_Constant_Narrowing: + // Implicit conversion to a narrower type, and the value is not a constant + // expression. + S.Diag(E->getBeginLoc(), diag::err_spaceship_argument_narrowing) + << /*Constant*/ 1 + << PreNarrowingValue.getAsString(S.Context, PreNarrowingType) << ToType; + return true; + + case NK_Variable_Narrowing: + // Implicit conversion to a narrower type, and the value is not a constant + // expression. + case NK_Type_Narrowing: + S.Diag(E->getBeginLoc(), diag::err_spaceship_argument_narrowing) + << /*Constant*/ 0 << FromType << ToType; + // TODO: It's not a constant expression, but what if the user intended it + // to be? Can we produce notes to help them figure out why it isn't? + return true; + } + llvm_unreachable("unhandled case in switch"); +} + +static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S, + ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc) { + QualType LHSType = LHS.get()->getType(); + QualType RHSType = RHS.get()->getType(); + // Dig out the original argument type and expression before implicit casts + // were applied. These are the types/expressions we need to check the + // [expr.spaceship] requirements against. + ExprResult LHSStripped = LHS.get()->IgnoreParenImpCasts(); + ExprResult RHSStripped = RHS.get()->IgnoreParenImpCasts(); + QualType LHSStrippedType = LHSStripped.get()->getType(); + QualType RHSStrippedType = RHSStripped.get()->getType(); + + // C++2a [expr.spaceship]p3: If one of the operands is of type bool and the + // other is not, the program is ill-formed. + if (LHSStrippedType->isBooleanType() != RHSStrippedType->isBooleanType()) { + S.InvalidOperands(Loc, LHSStripped, RHSStripped); + return QualType(); + } + + // FIXME: Consider combining this with checkEnumArithmeticConversions. + int NumEnumArgs = (int)LHSStrippedType->isEnumeralType() + + RHSStrippedType->isEnumeralType(); + if (NumEnumArgs == 1) { + bool LHSIsEnum = LHSStrippedType->isEnumeralType(); + QualType OtherTy = LHSIsEnum ? RHSStrippedType : LHSStrippedType; + if (OtherTy->hasFloatingRepresentation()) { + S.InvalidOperands(Loc, LHSStripped, RHSStripped); + return QualType(); + } + } + if (NumEnumArgs == 2) { + // C++2a [expr.spaceship]p5: If both operands have the same enumeration + // type E, the operator yields the result of converting the operands + // to the underlying type of E and applying <=> to the converted operands. + if (!S.Context.hasSameUnqualifiedType(LHSStrippedType, RHSStrippedType)) { + S.InvalidOperands(Loc, LHS, RHS); return QualType(); + } + QualType IntType = + LHSStrippedType->castAs()->getDecl()->getIntegerType(); + assert(IntType->isArithmeticType()); + + // We can't use `CK_IntegralCast` when the underlying type is 'bool', so we + // promote the boolean type, and all other promotable integer types, to + // avoid this. + if (S.Context.isPromotableIntegerType(IntType)) + IntType = S.Context.getPromotedIntegerType(IntType); + + LHS = S.ImpCastExprToType(LHS.get(), IntType, CK_IntegralCast); + RHS = S.ImpCastExprToType(RHS.get(), IntType, CK_IntegralCast); + LHSType = RHSType = IntType; + } + + // C++2a [expr.spaceship]p4: If both operands have arithmetic types, the + // usual arithmetic conversions are applied to the operands. + QualType Type = + S.UsualArithmeticConversions(LHS, RHS, Loc, Sema::ACK_Comparison); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); + if (Type.isNull()) + return S.InvalidOperands(Loc, LHS, RHS); + + std::optional CCT = + getComparisonCategoryForBuiltinCmp(Type); + if (!CCT) + return S.InvalidOperands(Loc, LHS, RHS); + + bool HasNarrowing = checkThreeWayNarrowingConversion( + S, Type, LHS.get(), LHSType, LHS.get()->getBeginLoc()); + HasNarrowing |= checkThreeWayNarrowingConversion(S, Type, RHS.get(), RHSType, + RHS.get()->getBeginLoc()); + if (HasNarrowing) + return QualType(); + + assert(!Type.isNull() && "composite type for <=> has not been set"); + + return S.CheckComparisonCategoryType( + *CCT, Loc, Sema::ComparisonCategoryUsage::OperatorInExpression); +} + +static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc, + BinaryOperatorKind Opc) { + if (Opc == BO_Cmp) + return checkArithmeticOrEnumeralThreeWayCompare(S, LHS, RHS, Loc); + + // C99 6.5.8p3 / C99 6.5.9p4 + QualType Type = + S.UsualArithmeticConversions(LHS, RHS, Loc, Sema::ACK_Comparison); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); + if (Type.isNull()) + return S.InvalidOperands(Loc, LHS, RHS); + assert(Type->isArithmeticType() || Type->isEnumeralType()); + + if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc)) + return S.InvalidOperands(Loc, LHS, RHS); + + // Check for comparisons of floating point operands using != and ==. + if (Type->hasFloatingRepresentation()) + S.CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); + + // The result of comparisons is 'bool' in C++, 'int' in C. + return S.Context.getLogicalOperationType(); +} + +void Sema::CheckPtrComparisonWithNullChar(ExprResult &E, ExprResult &NullE) { + if (!NullE.get()->getType()->isAnyPointerType()) + return; + int NullValue = PP.isMacroDefined("NULL") ? 0 : 1; + if (!E.get()->getType()->isAnyPointerType() && + E.get()->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNotNull) == + Expr::NPCK_ZeroExpression) { + if (const auto *CL = dyn_cast(E.get())) { + if (CL->getValue() == 0) + Diag(E.get()->getExprLoc(), diag::warn_pointer_compare) + << NullValue + << FixItHint::CreateReplacement(E.get()->getExprLoc(), + NullValue ? "NULL" : "(void *)0"); + } else if (const auto *CE = dyn_cast(E.get())) { + TypeSourceInfo *TI = CE->getTypeInfoAsWritten(); + QualType T = Context.getCanonicalType(TI->getType()).getUnqualifiedType(); + if (T == Context.CharTy) + Diag(E.get()->getExprLoc(), diag::warn_pointer_compare) + << NullValue + << FixItHint::CreateReplacement(E.get()->getExprLoc(), + NullValue ? "NULL" : "(void *)0"); + } + } +} + +// C99 6.5.8, C++ [expr.rel] +QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + BinaryOperatorKind Opc) { + bool IsRelational = BinaryOperator::isRelationalOp(Opc); + bool IsThreeWay = Opc == BO_Cmp; + bool IsOrdered = IsRelational || IsThreeWay; + auto IsAnyPointerType = [](ExprResult E) { + QualType Ty = E.get()->getType(); + return Ty->isPointerType() || Ty->isMemberPointerType(); + }; + + // C++2a [expr.spaceship]p6: If at least one of the operands is of pointer + // type, array-to-pointer, ..., conversions are performed on both operands to + // bring them to their composite type. + // Otherwise, all comparisons expect an rvalue, so convert to rvalue before + // any type-related checks. + if (!IsThreeWay || IsAnyPointerType(LHS) || IsAnyPointerType(RHS)) { + LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + } else { + LHS = DefaultLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + RHS = DefaultLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + } + + auto ConvertWideToRawPointer = [&](ExprResult E) -> ExprResult { + QualType Ty = E.get()->getType(); + auto *PT = Ty->getAs(); + if (!PT || PT->hasRawPointerLayout()) + return E; + return ImpCastExprToType(E.get(), + Context.getPointerType(PT->getPointeeType()), + CK_BoundsSafetyPointerCast); + }; + + LHS = ConvertWideToRawPointer(LHS); + RHS = ConvertWideToRawPointer(RHS); + + checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/true); + if (!getLangOpts().CPlusPlus && BinaryOperator::isEqualityOp(Opc)) { + CheckPtrComparisonWithNullChar(LHS, RHS); + CheckPtrComparisonWithNullChar(RHS, LHS); + } + + // Handle vector comparisons separately. + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) + return CheckVectorCompareOperands(LHS, RHS, Loc, Opc); + + if (LHS.get()->getType()->isSveVLSBuiltinType() || + RHS.get()->getType()->isSveVLSBuiltinType()) + return CheckSizelessVectorCompareOperands(LHS, RHS, Loc, Opc); + + diagnoseLogicalNotOnLHSofCheck(*this, LHS, RHS, Loc, Opc); + diagnoseTautologicalComparison(*this, Loc, LHS.get(), RHS.get(), Opc); + + QualType LHSType = LHS.get()->getType(); + QualType RHSType = RHS.get()->getType(); + if ((LHSType->isArithmeticType() || LHSType->isEnumeralType()) && + (RHSType->isArithmeticType() || RHSType->isEnumeralType())) + return checkArithmeticOrEnumeralCompare(*this, LHS, RHS, Loc, Opc); + + if ((LHSType->isPointerType() && + LHSType->getPointeeType().isWebAssemblyReferenceType()) || + (RHSType->isPointerType() && + RHSType->getPointeeType().isWebAssemblyReferenceType())) + return InvalidOperands(Loc, LHS, RHS); + + const Expr::NullPointerConstantKind LHSNullKind = + LHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); + const Expr::NullPointerConstantKind RHSNullKind = + RHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); + bool LHSIsNull = LHSNullKind != Expr::NPCK_NotNull; + bool RHSIsNull = RHSNullKind != Expr::NPCK_NotNull; + + auto computeResultTy = [&]() { + if (Opc != BO_Cmp) + return Context.getLogicalOperationType(); + assert(getLangOpts().CPlusPlus); + assert(Context.hasSameType(LHS.get()->getType(), RHS.get()->getType())); + + QualType CompositeTy = LHS.get()->getType(); + assert(!CompositeTy->isReferenceType()); + + std::optional CCT = + getComparisonCategoryForBuiltinCmp(CompositeTy); + if (!CCT) + return InvalidOperands(Loc, LHS, RHS); + + if (CompositeTy->isPointerType() && LHSIsNull != RHSIsNull) { + // P0946R0: Comparisons between a null pointer constant and an object + // pointer result in std::strong_equality, which is ill-formed under + // P1959R0. + Diag(Loc, diag::err_typecheck_three_way_comparison_of_pointer_and_zero) + << (LHSIsNull ? LHS.get()->getSourceRange() + : RHS.get()->getSourceRange()); + return QualType(); + } + + return CheckComparisonCategoryType( + *CCT, Loc, ComparisonCategoryUsage::OperatorInExpression); + }; + + if (!IsOrdered && LHSIsNull != RHSIsNull) { + bool IsEquality = Opc == BO_EQ; + if (RHSIsNull) + DiagnoseAlwaysNonNullPointer(LHS.get(), RHSNullKind, IsEquality, + RHS.get()->getSourceRange()); + else + DiagnoseAlwaysNonNullPointer(RHS.get(), LHSNullKind, IsEquality, + LHS.get()->getSourceRange()); + } + + if (IsOrdered && LHSType->isFunctionPointerType() && + RHSType->isFunctionPointerType()) { + // Valid unless a relational comparison of function pointers + bool IsError = Opc == BO_Cmp; + auto DiagID = + IsError ? diag::err_typecheck_ordered_comparison_of_function_pointers + : getLangOpts().CPlusPlus + ? diag::warn_typecheck_ordered_comparison_of_function_pointers + : diag::ext_typecheck_ordered_comparison_of_function_pointers; + Diag(Loc, DiagID) << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + if (IsError) + return QualType(); + } + + if ((LHSType->isIntegerType() && !LHSIsNull) || + (RHSType->isIntegerType() && !RHSIsNull)) { + // Skip normal pointer conversion checks in this case; we have better + // diagnostics for this below. + } else if (getLangOpts().CPlusPlus) { + // Equality comparison of a function pointer to a void pointer is invalid, + // but we allow it as an extension. + // FIXME: If we really want to allow this, should it be part of composite + // pointer type computation so it works in conditionals too? + if (!IsOrdered && + ((LHSType->isFunctionPointerType() && RHSType->isVoidPointerType()) || + (RHSType->isFunctionPointerType() && LHSType->isVoidPointerType()))) { + // This is a gcc extension compatibility comparison. + // In a SFINAE context, we treat this as a hard error to maintain + // conformance with the C++ standard. + diagnoseFunctionPointerToVoidComparison( + *this, Loc, LHS, RHS, /*isError*/ (bool)isSFINAEContext()); + + if (isSFINAEContext()) + return QualType(); + + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); + return computeResultTy(); + } + + // C++ [expr.eq]p2: + // If at least one operand is a pointer [...] bring them to their + // composite pointer type. + // C++ [expr.spaceship]p6 + // If at least one of the operands is of pointer type, [...] bring them + // to their composite pointer type. + // C++ [expr.rel]p2: + // If both operands are pointers, [...] bring them to their composite + // pointer type. + // For <=>, the only valid non-pointer types are arrays and functions, and + // we already decayed those, so this is really the same as the relational + // comparison rule. + if ((int)LHSType->isPointerType() + (int)RHSType->isPointerType() >= + (IsOrdered ? 2 : 1) && + (!LangOpts.ObjCAutoRefCount || !(LHSType->isObjCObjectPointerType() || + RHSType->isObjCObjectPointerType()))) { + if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) + return QualType(); + return computeResultTy(); + } + } else if (LHSType->isPointerType() && + RHSType->isPointerType()) { // C99 6.5.8p2 + // All of the following pointer-related warnings are GCC extensions, except + // when handling null pointer constants. + QualType LCanPointeeTy = + LHSType->castAs()->getPointeeType().getCanonicalType(); + QualType RCanPointeeTy = + RHSType->castAs()->getPointeeType().getCanonicalType(); + + // C99 6.5.9p2 and C99 6.5.8p2 + if (Context.typesAreCompatible(LCanPointeeTy.getUnqualifiedType(), + RCanPointeeTy.getUnqualifiedType())) { + if (IsRelational) { + // Pointers both need to point to complete or incomplete types + if ((LCanPointeeTy->isIncompleteType() != + RCanPointeeTy->isIncompleteType()) && + !getLangOpts().C11) { + Diag(Loc, diag::ext_typecheck_compare_complete_incomplete_pointers) + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange() + << LHSType << RHSType << LCanPointeeTy->isIncompleteType() + << RCanPointeeTy->isIncompleteType(); + } + } + } else if (!IsRelational && + (LCanPointeeTy->isVoidType() || RCanPointeeTy->isVoidType())) { + // Valid unless comparison between non-null pointer and function pointer + if ((LCanPointeeTy->isFunctionType() || RCanPointeeTy->isFunctionType()) + && !LHSIsNull && !RHSIsNull) + diagnoseFunctionPointerToVoidComparison(*this, Loc, LHS, RHS, + /*isError*/false); + } else { + // Invalid + diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, /*isError*/false); + } + if (LCanPointeeTy != RCanPointeeTy) { + // Treat NULL constant as a special case in OpenCL. + if (getLangOpts().OpenCL && !LHSIsNull && !RHSIsNull) { + if (!LCanPointeeTy.isAddressSpaceOverlapping(RCanPointeeTy, + getASTContext())) { + Diag(Loc, + diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) + << LHSType << RHSType << 0 /* comparison */ + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + } + } + LangAS AddrSpaceL = LCanPointeeTy.getAddressSpace(); + LangAS AddrSpaceR = RCanPointeeTy.getAddressSpace(); + CastKind Kind = AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion + : CK_BitCast; + if (LHSIsNull && !RHSIsNull) + LHS = ImpCastExprToType(LHS.get(), RHSType, Kind); + else + RHS = ImpCastExprToType(RHS.get(), LHSType, Kind); + } + return computeResultTy(); + } + + + // C++ [expr.eq]p4: + // Two operands of type std::nullptr_t or one operand of type + // std::nullptr_t and the other a null pointer constant compare + // equal. + // C23 6.5.9p5: + // If both operands have type nullptr_t or one operand has type nullptr_t + // and the other is a null pointer constant, they compare equal if the + // former is a null pointer. + if (!IsOrdered && LHSIsNull && RHSIsNull) { + if (LHSType->isNullPtrType()) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return computeResultTy(); + } + if (RHSType->isNullPtrType()) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); + } + } + + if (!getLangOpts().CPlusPlus && !IsOrdered && (LHSIsNull || RHSIsNull)) { + // C23 6.5.9p6: + // Otherwise, at least one operand is a pointer. If one is a pointer and + // the other is a null pointer constant or has type nullptr_t, they + // compare equal + if (LHSIsNull && RHSType->isPointerType()) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); + } + if (RHSIsNull && LHSType->isPointerType()) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return computeResultTy(); + } + } + + // Comparison of Objective-C pointers and block pointers against nullptr_t. + // These aren't covered by the composite pointer type rules. + if (!IsOrdered && RHSType->isNullPtrType() && + (LHSType->isObjCObjectPointerType() || LHSType->isBlockPointerType())) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return computeResultTy(); + } + if (!IsOrdered && LHSType->isNullPtrType() && + (RHSType->isObjCObjectPointerType() || RHSType->isBlockPointerType())) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); + } + + if (getLangOpts().CPlusPlus) { + if (IsRelational && + ((LHSType->isNullPtrType() && RHSType->isPointerType()) || + (RHSType->isNullPtrType() && LHSType->isPointerType()))) { + // HACK: Relational comparison of nullptr_t against a pointer type is + // invalid per DR583, but we allow it within std::less<> and friends, + // since otherwise common uses of it break. + // FIXME: Consider removing this hack once LWG fixes std::less<> and + // friends to have std::nullptr_t overload candidates. + DeclContext *DC = CurContext; + if (isa(DC)) + DC = DC->getParent(); + if (auto *CTSD = dyn_cast(DC)) { + if (CTSD->isInStdNamespace() && + llvm::StringSwitch(CTSD->getName()) + .Cases("less", "less_equal", "greater", "greater_equal", true) + .Default(false)) { + if (RHSType->isNullPtrType()) + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + else + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); + } + } + } + + // C++ [expr.eq]p2: + // If at least one operand is a pointer to member, [...] bring them to + // their composite pointer type. + if (!IsOrdered && + (LHSType->isMemberPointerType() || RHSType->isMemberPointerType())) { + if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) + return QualType(); + else + return computeResultTy(); + } + } + + // Handle block pointer types. + if (!IsOrdered && LHSType->isBlockPointerType() && + RHSType->isBlockPointerType()) { + QualType lpointee = LHSType->castAs()->getPointeeType(); + QualType rpointee = RHSType->castAs()->getPointeeType(); + + if (!LHSIsNull && !RHSIsNull && + !Context.typesAreCompatible(lpointee, rpointee)) { + Diag(Loc, diag::err_typecheck_comparison_of_distinct_blocks) + << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + } + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); + return computeResultTy(); + } + + // Allow block pointers to be compared with null pointer constants. + if (!IsOrdered + && ((LHSType->isBlockPointerType() && RHSType->isPointerType()) + || (LHSType->isPointerType() && RHSType->isBlockPointerType()))) { + if (!LHSIsNull && !RHSIsNull) { + if (!((RHSType->isPointerType() && RHSType->castAs() + ->getPointeeType()->isVoidType()) + || (LHSType->isPointerType() && LHSType->castAs() + ->getPointeeType()->isVoidType()))) + Diag(Loc, diag::err_typecheck_comparison_of_distinct_blocks) + << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + } + if (LHSIsNull && !RHSIsNull) + LHS = ImpCastExprToType(LHS.get(), RHSType, + RHSType->isPointerType() ? CK_BitCast + : CK_AnyPointerToBlockPointerCast); + else + RHS = ImpCastExprToType(RHS.get(), LHSType, + LHSType->isPointerType() ? CK_BitCast + : CK_AnyPointerToBlockPointerCast); + return computeResultTy(); + } + + if (LHSType->isObjCObjectPointerType() || + RHSType->isObjCObjectPointerType()) { + const PointerType *LPT = LHSType->getAs(); + const PointerType *RPT = RHSType->getAs(); + if (LPT || RPT) { + bool LPtrToVoid = LPT ? LPT->getPointeeType()->isVoidType() : false; + bool RPtrToVoid = RPT ? RPT->getPointeeType()->isVoidType() : false; + + if (!LPtrToVoid && !RPtrToVoid && + !Context.typesAreCompatible(LHSType, RHSType)) { + diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, + /*isError*/false); + } + // FIXME: If LPtrToVoid, we should presumably convert the LHS rather than + // the RHS, but we have test coverage for this behavior. + // FIXME: Consider using convertPointersToCompositeType in C++. + if (LHSIsNull && !RHSIsNull) { + Expr *E = LHS.get(); + if (getLangOpts().ObjCAutoRefCount) + ObjC().CheckObjCConversion(SourceRange(), RHSType, E, + CheckedConversionKind::Implicit); + LHS = ImpCastExprToType(E, RHSType, + RPT ? CK_BitCast :CK_CPointerToObjCPointerCast); + } + else { + Expr *E = RHS.get(); + if (getLangOpts().ObjCAutoRefCount) + ObjC().CheckObjCConversion(SourceRange(), LHSType, E, + CheckedConversionKind::Implicit, + /*Diagnose=*/true, + /*DiagnoseCFAudited=*/false, Opc); + RHS = ImpCastExprToType(E, LHSType, + LPT ? CK_BitCast :CK_CPointerToObjCPointerCast); + } + return computeResultTy(); + } + if (LHSType->isObjCObjectPointerType() && + RHSType->isObjCObjectPointerType()) { + if (!Context.areComparableObjCPointerTypes(LHSType, RHSType)) + diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, + /*isError*/false); + if (isObjCObjectLiteral(LHS) || isObjCObjectLiteral(RHS)) + diagnoseObjCLiteralComparison(*this, Loc, LHS, RHS, Opc); + + if (LHSIsNull && !RHSIsNull) + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); + else + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); + return computeResultTy(); + } + + if (!IsOrdered && LHSType->isBlockPointerType() && + RHSType->isBlockCompatibleObjCPointerType(Context)) { + LHS = ImpCastExprToType(LHS.get(), RHSType, + CK_BlockPointerToObjCPointerCast); + return computeResultTy(); + } else if (!IsOrdered && + LHSType->isBlockCompatibleObjCPointerType(Context) && + RHSType->isBlockPointerType()) { + RHS = ImpCastExprToType(RHS.get(), LHSType, + CK_BlockPointerToObjCPointerCast); + return computeResultTy(); + } + } + if ((LHSType->isAnyPointerType() && RHSType->isIntegerType()) || + (LHSType->isIntegerType() && RHSType->isAnyPointerType())) { + unsigned DiagID = 0; + bool isError = false; + if (LangOpts.DebuggerSupport) { + // Under a debugger, allow the comparison of pointers to integers, + // since users tend to want to compare addresses. + } else if ((LHSIsNull && LHSType->isIntegerType()) || + (RHSIsNull && RHSType->isIntegerType())) { + if (IsOrdered) { + isError = getLangOpts().CPlusPlus; + DiagID = + isError ? diag::err_typecheck_ordered_comparison_of_pointer_and_zero + : diag::ext_typecheck_ordered_comparison_of_pointer_and_zero; + } + } else if (getLangOpts().CPlusPlus) { + DiagID = diag::err_typecheck_comparison_of_pointer_integer; + isError = true; + } else if (IsOrdered) + DiagID = diag::ext_typecheck_ordered_comparison_of_pointer_integer; + else + DiagID = diag::ext_typecheck_comparison_of_pointer_integer; + + if (DiagID) { + Diag(Loc, DiagID) + << LHSType << RHSType << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange(); + if (isError) + return QualType(); + } + + if (LHSType->isIntegerType()) + LHS = ImpCastExprToType(LHS.get(), RHSType, + LHSIsNull ? CK_NullToPointer : CK_IntegralToPointer); + else + RHS = ImpCastExprToType(RHS.get(), LHSType, + RHSIsNull ? CK_NullToPointer : CK_IntegralToPointer); + return computeResultTy(); + } + + // Handle block pointers. + if (!IsOrdered && RHSIsNull + && LHSType->isBlockPointerType() && RHSType->isIntegerType()) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return computeResultTy(); + } + if (!IsOrdered && LHSIsNull + && LHSType->isIntegerType() && RHSType->isBlockPointerType()) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); + } + + if (getLangOpts().getOpenCLCompatibleVersion() >= 200) { + if (LHSType->isClkEventT() && RHSType->isClkEventT()) { + return computeResultTy(); + } + + if (LHSType->isQueueT() && RHSType->isQueueT()) { + return computeResultTy(); + } + + if (LHSIsNull && RHSType->isQueueT()) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return computeResultTy(); + } + + if (LHSType->isQueueT() && RHSIsNull) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return computeResultTy(); + } + } + + return InvalidOperands(Loc, LHS, RHS); +} + +QualType Sema::GetSignedVectorType(QualType V) { + const VectorType *VTy = V->castAs(); + unsigned TypeSize = Context.getTypeSize(VTy->getElementType()); + + if (isa(VTy)) { + if (VTy->isExtVectorBoolType()) + return Context.getExtVectorType(Context.BoolTy, VTy->getNumElements()); + if (TypeSize == Context.getTypeSize(Context.CharTy)) + return Context.getExtVectorType(Context.CharTy, VTy->getNumElements()); + if (TypeSize == Context.getTypeSize(Context.ShortTy)) + return Context.getExtVectorType(Context.ShortTy, VTy->getNumElements()); + if (TypeSize == Context.getTypeSize(Context.IntTy)) + return Context.getExtVectorType(Context.IntTy, VTy->getNumElements()); + if (TypeSize == Context.getTypeSize(Context.Int128Ty)) + return Context.getExtVectorType(Context.Int128Ty, VTy->getNumElements()); + if (TypeSize == Context.getTypeSize(Context.LongTy)) + return Context.getExtVectorType(Context.LongTy, VTy->getNumElements()); + assert(TypeSize == Context.getTypeSize(Context.LongLongTy) && + "Unhandled vector element size in vector compare"); + return Context.getExtVectorType(Context.LongLongTy, VTy->getNumElements()); + } + + if (TypeSize == Context.getTypeSize(Context.Int128Ty)) + return Context.getVectorType(Context.Int128Ty, VTy->getNumElements(), + VectorKind::Generic); + if (TypeSize == Context.getTypeSize(Context.LongLongTy)) + return Context.getVectorType(Context.LongLongTy, VTy->getNumElements(), + VectorKind::Generic); + if (TypeSize == Context.getTypeSize(Context.LongTy)) + return Context.getVectorType(Context.LongTy, VTy->getNumElements(), + VectorKind::Generic); + if (TypeSize == Context.getTypeSize(Context.IntTy)) + return Context.getVectorType(Context.IntTy, VTy->getNumElements(), + VectorKind::Generic); + if (TypeSize == Context.getTypeSize(Context.ShortTy)) + return Context.getVectorType(Context.ShortTy, VTy->getNumElements(), + VectorKind::Generic); + assert(TypeSize == Context.getTypeSize(Context.CharTy) && + "Unhandled vector element size in vector compare"); + return Context.getVectorType(Context.CharTy, VTy->getNumElements(), + VectorKind::Generic); +} + +QualType Sema::GetSignedSizelessVectorType(QualType V) { + const BuiltinType *VTy = V->castAs(); + assert(VTy->isSizelessBuiltinType() && "expected sizeless type"); + + const QualType ETy = V->getSveEltType(Context); + const auto TypeSize = Context.getTypeSize(ETy); + + const QualType IntTy = Context.getIntTypeForBitwidth(TypeSize, true); + const llvm::ElementCount VecSize = Context.getBuiltinVectorTypeInfo(VTy).EC; + return Context.getScalableVectorType(IntTy, VecSize.getKnownMinValue()); +} + +QualType Sema::CheckVectorCompareOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + BinaryOperatorKind Opc) { + if (Opc == BO_Cmp) { + Diag(Loc, diag::err_three_way_vector_comparison); + return QualType(); + } + + // Check to make sure we're operating on vectors of the same type and width, + // Allowing one side to be a scalar of element type. + QualType vType = + CheckVectorOperands(LHS, RHS, Loc, /*isCompAssign*/ false, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ getLangOpts().ZVector, + /*AllowBooleanOperation*/ true, + /*ReportInvalid*/ true); + if (vType.isNull()) + return vType; + + QualType LHSType = LHS.get()->getType(); + + // Determine the return type of a vector compare. By default clang will return + // a scalar for all vector compares except vector bool and vector pixel. + // With the gcc compiler we will always return a vector type and with the xl + // compiler we will always return a scalar type. This switch allows choosing + // which behavior is prefered. + if (getLangOpts().AltiVec) { + switch (getLangOpts().getAltivecSrcCompat()) { + case LangOptions::AltivecSrcCompatKind::Mixed: + // If AltiVec, the comparison results in a numeric type, i.e. + // bool for C++, int for C + if (vType->castAs()->getVectorKind() == + VectorKind::AltiVecVector) + return Context.getLogicalOperationType(); + else + Diag(Loc, diag::warn_deprecated_altivec_src_compat); + break; + case LangOptions::AltivecSrcCompatKind::GCC: + // For GCC we always return the vector type. + break; + case LangOptions::AltivecSrcCompatKind::XL: + return Context.getLogicalOperationType(); + break; + } + } + + // For non-floating point types, check for self-comparisons of the form + // x == x, x != x, x < x, etc. These always evaluate to a constant, and + // often indicate logic errors in the program. + diagnoseTautologicalComparison(*this, Loc, LHS.get(), RHS.get(), Opc); + + // Check for comparisons of floating point operands using != and ==. + if (LHSType->hasFloatingRepresentation()) { + assert(RHS.get()->getType()->hasFloatingRepresentation()); + CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); + } + + // Return a signed type for the vector. + return GetSignedVectorType(vType); +} + +QualType Sema::CheckSizelessVectorCompareOperands(ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc, + BinaryOperatorKind Opc) { + if (Opc == BO_Cmp) { + Diag(Loc, diag::err_three_way_vector_comparison); + return QualType(); + } + + // Check to make sure we're operating on vectors of the same type and width, + // Allowing one side to be a scalar of element type. + QualType vType = CheckSizelessVectorOperands( + LHS, RHS, Loc, /*isCompAssign*/ false, ACK_Comparison); + + if (vType.isNull()) + return vType; + + QualType LHSType = LHS.get()->getType(); + + // For non-floating point types, check for self-comparisons of the form + // x == x, x != x, x < x, etc. These always evaluate to a constant, and + // often indicate logic errors in the program. + diagnoseTautologicalComparison(*this, Loc, LHS.get(), RHS.get(), Opc); + + // Check for comparisons of floating point operands using != and ==. + if (LHSType->hasFloatingRepresentation()) { + assert(RHS.get()->getType()->hasFloatingRepresentation()); + CheckFloatComparison(Loc, LHS.get(), RHS.get(), Opc); + } + + const BuiltinType *LHSBuiltinTy = LHSType->getAs(); + const BuiltinType *RHSBuiltinTy = RHS.get()->getType()->getAs(); + + if (LHSBuiltinTy && RHSBuiltinTy && LHSBuiltinTy->isSVEBool() && + RHSBuiltinTy->isSVEBool()) + return LHSType; + + // Return a signed type for the vector. + return GetSignedSizelessVectorType(vType); +} + +static void diagnoseXorMisusedAsPow(Sema &S, const ExprResult &XorLHS, + const ExprResult &XorRHS, + const SourceLocation Loc) { + // Do not diagnose macros. + if (Loc.isMacroID()) + return; + + // Do not diagnose if both LHS and RHS are macros. + if (XorLHS.get()->getExprLoc().isMacroID() && + XorRHS.get()->getExprLoc().isMacroID()) + return; + + bool Negative = false; + bool ExplicitPlus = false; + const auto *LHSInt = dyn_cast(XorLHS.get()); + const auto *RHSInt = dyn_cast(XorRHS.get()); + + if (!LHSInt) + return; + if (!RHSInt) { + // Check negative literals. + if (const auto *UO = dyn_cast(XorRHS.get())) { + UnaryOperatorKind Opc = UO->getOpcode(); + if (Opc != UO_Minus && Opc != UO_Plus) + return; + RHSInt = dyn_cast(UO->getSubExpr()); + if (!RHSInt) + return; + Negative = (Opc == UO_Minus); + ExplicitPlus = !Negative; + } else { + return; + } + } + + const llvm::APInt &LeftSideValue = LHSInt->getValue(); + llvm::APInt RightSideValue = RHSInt->getValue(); + if (LeftSideValue != 2 && LeftSideValue != 10) + return; + + if (LeftSideValue.getBitWidth() != RightSideValue.getBitWidth()) + return; + + CharSourceRange ExprRange = CharSourceRange::getCharRange( + LHSInt->getBeginLoc(), S.getLocForEndOfToken(RHSInt->getLocation())); + llvm::StringRef ExprStr = + Lexer::getSourceText(ExprRange, S.getSourceManager(), S.getLangOpts()); + + CharSourceRange XorRange = + CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); + llvm::StringRef XorStr = + Lexer::getSourceText(XorRange, S.getSourceManager(), S.getLangOpts()); + // Do not diagnose if xor keyword/macro is used. + if (XorStr == "xor") + return; + + std::string LHSStr = std::string(Lexer::getSourceText( + CharSourceRange::getTokenRange(LHSInt->getSourceRange()), + S.getSourceManager(), S.getLangOpts())); + std::string RHSStr = std::string(Lexer::getSourceText( + CharSourceRange::getTokenRange(RHSInt->getSourceRange()), + S.getSourceManager(), S.getLangOpts())); + + if (Negative) { + RightSideValue = -RightSideValue; + RHSStr = "-" + RHSStr; + } else if (ExplicitPlus) { + RHSStr = "+" + RHSStr; + } + + StringRef LHSStrRef = LHSStr; + StringRef RHSStrRef = RHSStr; + // Do not diagnose literals with digit separators, binary, hexadecimal, octal + // literals. + if (LHSStrRef.starts_with("0b") || LHSStrRef.starts_with("0B") || + RHSStrRef.starts_with("0b") || RHSStrRef.starts_with("0B") || + LHSStrRef.starts_with("0x") || LHSStrRef.starts_with("0X") || + RHSStrRef.starts_with("0x") || RHSStrRef.starts_with("0X") || + (LHSStrRef.size() > 1 && LHSStrRef.starts_with("0")) || + (RHSStrRef.size() > 1 && RHSStrRef.starts_with("0")) || + LHSStrRef.contains('\'') || RHSStrRef.contains('\'')) + return; + + bool SuggestXor = + S.getLangOpts().CPlusPlus || S.getPreprocessor().isMacroDefined("xor"); + const llvm::APInt XorValue = LeftSideValue ^ RightSideValue; + int64_t RightSideIntValue = RightSideValue.getSExtValue(); + if (LeftSideValue == 2 && RightSideIntValue >= 0) { + std::string SuggestedExpr = "1 << " + RHSStr; + bool Overflow = false; + llvm::APInt One = (LeftSideValue - 1); + llvm::APInt PowValue = One.sshl_ov(RightSideValue, Overflow); + if (Overflow) { + if (RightSideIntValue < 64) + S.Diag(Loc, diag::warn_xor_used_as_pow_base) + << ExprStr << toString(XorValue, 10, true) << ("1LL << " + RHSStr) + << FixItHint::CreateReplacement(ExprRange, "1LL << " + RHSStr); + else if (RightSideIntValue == 64) + S.Diag(Loc, diag::warn_xor_used_as_pow) + << ExprStr << toString(XorValue, 10, true); + else + return; + } else { + S.Diag(Loc, diag::warn_xor_used_as_pow_base_extra) + << ExprStr << toString(XorValue, 10, true) << SuggestedExpr + << toString(PowValue, 10, true) + << FixItHint::CreateReplacement( + ExprRange, (RightSideIntValue == 0) ? "1" : SuggestedExpr); + } + + S.Diag(Loc, diag::note_xor_used_as_pow_silence) + << ("0x2 ^ " + RHSStr) << SuggestXor; + } else if (LeftSideValue == 10) { + std::string SuggestedValue = "1e" + std::to_string(RightSideIntValue); + S.Diag(Loc, diag::warn_xor_used_as_pow_base) + << ExprStr << toString(XorValue, 10, true) << SuggestedValue + << FixItHint::CreateReplacement(ExprRange, SuggestedValue); + S.Diag(Loc, diag::note_xor_used_as_pow_silence) + << ("0xA ^ " + RHSStr) << SuggestXor; + } +} + +QualType Sema::CheckVectorLogicalOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + BinaryOperatorKind Opc) { + // Ensure that either both operands are of the same vector type, or + // one operand is of a vector type and the other is of its element type. + QualType vType = CheckVectorOperands(LHS, RHS, Loc, false, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ false); + if (vType.isNull()) + return InvalidOperands(Loc, LHS, RHS); + if (getLangOpts().OpenCL && + getLangOpts().getOpenCLCompatibleVersion() < 120 && + vType->hasFloatingRepresentation()) + return InvalidOperands(Loc, LHS, RHS); + // FIXME: The check for C++ here is for GCC compatibility. GCC rejects the + // usage of the logical operators && and || with vectors in C. This + // check could be notionally dropped. + if (!getLangOpts().CPlusPlus && + !(isa(vType->getAs()))) + return InvalidLogicalVectorOperands(Loc, LHS, RHS); + // Beginning with HLSL 2021, HLSL disallows logical operators on vector + // operands and instead requires the use of the `and`, `or`, `any`, `all`, and + // `select` functions. + if (getLangOpts().HLSL && + getLangOpts().getHLSLVersion() >= LangOptionsBase::HLSL_2021) { + (void)InvalidOperands(Loc, LHS, RHS); + HLSL().emitLogicalOperatorFixIt(LHS.get(), RHS.get(), Opc); + return QualType(); + } + + return GetSignedVectorType(LHS.get()->getType()); +} + +QualType Sema::CheckMatrixElementwiseOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + bool IsCompAssign) { + if (!IsCompAssign) { + LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + } + RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + + // For conversion purposes, we ignore any qualifiers. + // For example, "const float" and "float" are equivalent. + QualType LHSType = LHS.get()->getType().getUnqualifiedType(); + QualType RHSType = RHS.get()->getType().getUnqualifiedType(); + + const MatrixType *LHSMatType = LHSType->getAs(); + const MatrixType *RHSMatType = RHSType->getAs(); + assert((LHSMatType || RHSMatType) && "At least one operand must be a matrix"); + + if (Context.hasSameType(LHSType, RHSType)) + return Context.getCommonSugaredType(LHSType, RHSType); + + // Type conversion may change LHS/RHS. Keep copies to the original results, in + // case we have to return InvalidOperands. + ExprResult OriginalLHS = LHS; + ExprResult OriginalRHS = RHS; + if (LHSMatType && !RHSMatType) { + RHS = tryConvertExprToType(RHS.get(), LHSMatType->getElementType()); + if (!RHS.isInvalid()) + return LHSType; + + return InvalidOperands(Loc, OriginalLHS, OriginalRHS); + } + + if (!LHSMatType && RHSMatType) { + LHS = tryConvertExprToType(LHS.get(), RHSMatType->getElementType()); + if (!LHS.isInvalid()) + return RHSType; + return InvalidOperands(Loc, OriginalLHS, OriginalRHS); + } + + return InvalidOperands(Loc, LHS, RHS); +} + +QualType Sema::CheckMatrixMultiplyOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + bool IsCompAssign) { + if (!IsCompAssign) { + LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + } + RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + + auto *LHSMatType = LHS.get()->getType()->getAs(); + auto *RHSMatType = RHS.get()->getType()->getAs(); + assert((LHSMatType || RHSMatType) && "At least one operand must be a matrix"); + + if (LHSMatType && RHSMatType) { + if (LHSMatType->getNumColumns() != RHSMatType->getNumRows()) + return InvalidOperands(Loc, LHS, RHS); + + if (Context.hasSameType(LHSMatType, RHSMatType)) + return Context.getCommonSugaredType( + LHS.get()->getType().getUnqualifiedType(), + RHS.get()->getType().getUnqualifiedType()); + + QualType LHSELTy = LHSMatType->getElementType(), + RHSELTy = RHSMatType->getElementType(); + if (!Context.hasSameType(LHSELTy, RHSELTy)) + return InvalidOperands(Loc, LHS, RHS); + + return Context.getConstantMatrixType( + Context.getCommonSugaredType(LHSELTy, RHSELTy), + LHSMatType->getNumRows(), RHSMatType->getNumColumns()); + } + return CheckMatrixElementwiseOperands(LHS, RHS, Loc, IsCompAssign); +} + +static bool isLegalBoolVectorBinaryOp(BinaryOperatorKind Opc) { + switch (Opc) { + default: + return false; + case BO_And: + case BO_AndAssign: + case BO_Or: + case BO_OrAssign: + case BO_Xor: + case BO_XorAssign: + return true; + } +} + +inline QualType Sema::CheckBitwiseOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + BinaryOperatorKind Opc) { + checkArithmeticNull(*this, LHS, RHS, Loc, /*IsCompare=*/false); + + bool IsCompAssign = + Opc == BO_AndAssign || Opc == BO_OrAssign || Opc == BO_XorAssign; + + bool LegalBoolVecOperator = isLegalBoolVectorBinaryOp(Opc); + + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) { + if (LHS.get()->getType()->hasIntegerRepresentation() && + RHS.get()->getType()->hasIntegerRepresentation()) + return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ getLangOpts().ZVector, + /*AllowBooleanOperation*/ LegalBoolVecOperator, + /*ReportInvalid*/ true); + return InvalidOperands(Loc, LHS, RHS); + } + + if (LHS.get()->getType()->isSveVLSBuiltinType() || + RHS.get()->getType()->isSveVLSBuiltinType()) { + if (LHS.get()->getType()->hasIntegerRepresentation() && + RHS.get()->getType()->hasIntegerRepresentation()) + return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, + ACK_BitwiseOp); + return InvalidOperands(Loc, LHS, RHS); + } + + if (LHS.get()->getType()->isSveVLSBuiltinType() || + RHS.get()->getType()->isSveVLSBuiltinType()) { + if (LHS.get()->getType()->hasIntegerRepresentation() && + RHS.get()->getType()->hasIntegerRepresentation()) + return CheckSizelessVectorOperands(LHS, RHS, Loc, IsCompAssign, + ACK_BitwiseOp); + return InvalidOperands(Loc, LHS, RHS); + } + + if (Opc == BO_And) + diagnoseLogicalNotOnLHSofCheck(*this, LHS, RHS, Loc, Opc); + + if (LHS.get()->getType()->hasFloatingRepresentation() || + RHS.get()->getType()->hasFloatingRepresentation()) + return InvalidOperands(Loc, LHS, RHS); + + ExprResult LHSResult = LHS, RHSResult = RHS; + QualType compType = UsualArithmeticConversions( + LHSResult, RHSResult, Loc, IsCompAssign ? ACK_CompAssign : ACK_BitwiseOp); + if (LHSResult.isInvalid() || RHSResult.isInvalid()) + return QualType(); + LHS = LHSResult.get(); + RHS = RHSResult.get(); + + if (Opc == BO_Xor) + diagnoseXorMisusedAsPow(*this, LHS, RHS, Loc); + + if (!compType.isNull() && compType->isIntegralOrUnscopedEnumerationType()) + return compType; + return InvalidOperands(Loc, LHS, RHS); +} + +// C99 6.5.[13,14] +inline QualType Sema::CheckLogicalOperands(ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc, + BinaryOperatorKind Opc) { + // Check vector operands differently. + if (LHS.get()->getType()->isVectorType() || + RHS.get()->getType()->isVectorType()) + return CheckVectorLogicalOperands(LHS, RHS, Loc, Opc); + + bool EnumConstantInBoolContext = false; + for (const ExprResult &HS : {LHS, RHS}) { + if (const auto *DREHS = dyn_cast(HS.get())) { + const auto *ECDHS = dyn_cast(DREHS->getDecl()); + if (ECDHS && ECDHS->getInitVal() != 0 && ECDHS->getInitVal() != 1) + EnumConstantInBoolContext = true; + } + } + + if (EnumConstantInBoolContext) + Diag(Loc, diag::warn_enum_constant_in_bool_context); + + // WebAssembly tables can't be used with logical operators. + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); + const auto *LHSATy = dyn_cast(LHSTy); + const auto *RHSATy = dyn_cast(RHSTy); + if ((LHSATy && LHSATy->getElementType().isWebAssemblyReferenceType()) || + (RHSATy && RHSATy->getElementType().isWebAssemblyReferenceType())) { + return InvalidOperands(Loc, LHS, RHS); + } + + // Diagnose cases where the user write a logical and/or but probably meant a + // bitwise one. We do this when the LHS is a non-bool integer and the RHS + // is a constant. + if (!EnumConstantInBoolContext && LHS.get()->getType()->isIntegerType() && + !LHS.get()->getType()->isBooleanType() && + RHS.get()->getType()->isIntegerType() && !RHS.get()->isValueDependent() && + // Don't warn in macros or template instantiations. + !Loc.isMacroID() && !inTemplateInstantiation()) { + // If the RHS can be constant folded, and if it constant folds to something + // that isn't 0 or 1 (which indicate a potential logical operation that + // happened to fold to true/false) then warn. + // Parens on the RHS are ignored. + Expr::EvalResult EVResult; + if (RHS.get()->EvaluateAsInt(EVResult, Context)) { + llvm::APSInt Result = EVResult.Val.getInt(); + if ((getLangOpts().CPlusPlus && !RHS.get()->getType()->isBooleanType() && + !RHS.get()->getExprLoc().isMacroID()) || + (Result != 0 && Result != 1)) { + Diag(Loc, diag::warn_logical_instead_of_bitwise) + << RHS.get()->getSourceRange() << (Opc == BO_LAnd ? "&&" : "||"); + // Suggest replacing the logical operator with the bitwise version + Diag(Loc, diag::note_logical_instead_of_bitwise_change_operator) + << (Opc == BO_LAnd ? "&" : "|") + << FixItHint::CreateReplacement( + SourceRange(Loc, getLocForEndOfToken(Loc)), + Opc == BO_LAnd ? "&" : "|"); + if (Opc == BO_LAnd) + // Suggest replacing "Foo() && kNonZero" with "Foo()" + Diag(Loc, diag::note_logical_instead_of_bitwise_remove_constant) + << FixItHint::CreateRemoval( + SourceRange(getLocForEndOfToken(LHS.get()->getEndLoc()), + RHS.get()->getEndLoc())); + } + } + } + + if (!Context.getLangOpts().CPlusPlus) { + // OpenCL v1.1 s6.3.g: The logical operators and (&&), or (||) do + // not operate on the built-in scalar and vector float types. + if (Context.getLangOpts().OpenCL && + Context.getLangOpts().OpenCLVersion < 120) { + if (LHS.get()->getType()->isFloatingType() || + RHS.get()->getType()->isFloatingType()) + return InvalidOperands(Loc, LHS, RHS); + } + + LHS = UsualUnaryConversions(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + + RHS = UsualUnaryConversions(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + + if (!LHS.get()->getType()->isScalarType() || + !RHS.get()->getType()->isScalarType()) + return InvalidOperands(Loc, LHS, RHS); + + return Context.IntTy; + } + + // The following is safe because we only use this method for + // non-overloadable operands. + + // C++ [expr.log.and]p1 + // C++ [expr.log.or]p1 + // The operands are both contextually converted to type bool. + ExprResult LHSRes = PerformContextuallyConvertToBool(LHS.get()); + if (LHSRes.isInvalid()) + return InvalidOperands(Loc, LHS, RHS); + LHS = LHSRes; + + ExprResult RHSRes = PerformContextuallyConvertToBool(RHS.get()); + if (RHSRes.isInvalid()) + return InvalidOperands(Loc, LHS, RHS); + RHS = RHSRes; + + // C++ [expr.log.and]p2 + // C++ [expr.log.or]p2 + // The result is a bool. + return Context.BoolTy; +} + +static bool IsReadonlyMessage(Expr *E, Sema &S) { + const MemberExpr *ME = dyn_cast(E); + if (!ME) return false; + if (!isa(ME->getMemberDecl())) return false; + ObjCMessageExpr *Base = dyn_cast( + ME->getBase()->IgnoreImplicit()->IgnoreParenImpCasts()); + if (!Base) return false; + return Base->getMethodDecl() != nullptr; +} + +/// Is the given expression (which must be 'const') a reference to a +/// variable which was originally non-const, but which has become +/// 'const' due to being captured within a block? +enum NonConstCaptureKind { NCCK_None, NCCK_Block, NCCK_Lambda }; +static NonConstCaptureKind isReferenceToNonConstCapture(Sema &S, Expr *E) { + assert(E->isLValue() && E->getType().isConstQualified()); + E = E->IgnoreParens(); + + // Must be a reference to a declaration from an enclosing scope. + DeclRefExpr *DRE = dyn_cast(E); + if (!DRE) return NCCK_None; + if (!DRE->refersToEnclosingVariableOrCapture()) return NCCK_None; + + ValueDecl *Value = dyn_cast(DRE->getDecl()); + + // The declaration must be a value which is not declared 'const'. + if (!Value || Value->getType().isConstQualified()) + return NCCK_None; + + BindingDecl *Binding = dyn_cast(Value); + if (Binding) { + assert(S.getLangOpts().CPlusPlus && "BindingDecl outside of C++?"); + assert(!isa(Binding->getDeclContext())); + return NCCK_Lambda; + } + + VarDecl *Var = dyn_cast(Value); + if (!Var) + return NCCK_None; + + assert(Var->hasLocalStorage() && "capture added 'const' to non-local?"); + + // Decide whether the first capture was for a block or a lambda. + DeclContext *DC = S.CurContext, *Prev = nullptr; + // Decide whether the first capture was for a block or a lambda. + while (DC) { + // For init-capture, it is possible that the variable belongs to the + // template pattern of the current context. + if (auto *FD = dyn_cast(DC)) + if (Var->isInitCapture() && + FD->getTemplateInstantiationPattern() == Var->getDeclContext()) + break; + if (DC == Var->getDeclContext()) + break; + Prev = DC; + DC = DC->getParent(); + } + // Unless we have an init-capture, we've gone one step too far. + if (!Var->isInitCapture()) + DC = Prev; + return (isa(DC) ? NCCK_Block : NCCK_Lambda); +} + +static bool IsTypeModifiable(QualType Ty, bool IsDereference) { + Ty = Ty.getNonReferenceType(); + if (IsDereference && Ty->isPointerType()) + Ty = Ty->getPointeeType(); + return !Ty.isConstQualified(); +} + +// Update err_typecheck_assign_const and note_typecheck_assign_const +// when this enum is changed. +enum { + ConstFunction, + ConstVariable, + ConstMember, + ConstMethod, + NestedConstMember, + ConstUnknown, // Keep as last element +}; + +/// Emit the "read-only variable not assignable" error and print notes to give +/// more information about why the variable is not assignable, such as pointing +/// to the declaration of a const variable, showing that a method is const, or +/// that the function is returning a const reference. +static void DiagnoseConstAssignment(Sema &S, const Expr *E, + SourceLocation Loc) { + SourceRange ExprRange = E->getSourceRange(); + + // Only emit one error on the first const found. All other consts will emit + // a note to the error. + bool DiagnosticEmitted = false; + + // Track if the current expression is the result of a dereference, and if the + // next checked expression is the result of a dereference. + bool IsDereference = false; + bool NextIsDereference = false; + + // Loop to process MemberExpr chains. + while (true) { + IsDereference = NextIsDereference; + + E = E->IgnoreImplicit()->IgnoreParenImpCasts(); + if (const MemberExpr *ME = dyn_cast(E)) { + NextIsDereference = ME->isArrow(); + const ValueDecl *VD = ME->getMemberDecl(); + if (const FieldDecl *Field = dyn_cast(VD)) { + // Mutable fields can be modified even if the class is const. + if (Field->isMutable()) { + assert(DiagnosticEmitted && "Expected diagnostic not emitted."); + break; + } + + if (!IsTypeModifiable(Field->getType(), IsDereference)) { + if (!DiagnosticEmitted) { + S.Diag(Loc, diag::err_typecheck_assign_const) + << ExprRange << ConstMember << false /*static*/ << Field + << Field->getType(); + DiagnosticEmitted = true; + } + S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) + << ConstMember << false /*static*/ << Field << Field->getType() + << Field->getSourceRange(); + } + E = ME->getBase(); + continue; + } else if (const VarDecl *VDecl = dyn_cast(VD)) { + if (VDecl->getType().isConstQualified()) { + if (!DiagnosticEmitted) { + S.Diag(Loc, diag::err_typecheck_assign_const) + << ExprRange << ConstMember << true /*static*/ << VDecl + << VDecl->getType(); + DiagnosticEmitted = true; + } + S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) + << ConstMember << true /*static*/ << VDecl << VDecl->getType() + << VDecl->getSourceRange(); + } + // Static fields do not inherit constness from parents. + break; + } + break; // End MemberExpr + } else if (const ArraySubscriptExpr *ASE = + dyn_cast(E)) { + E = ASE->getBase()->IgnoreParenImpCasts(); + continue; + } else if (const ExtVectorElementExpr *EVE = + dyn_cast(E)) { + E = EVE->getBase()->IgnoreParenImpCasts(); + continue; + } + break; + } + + if (const CallExpr *CE = dyn_cast(E)) { + // Function calls + const FunctionDecl *FD = CE->getDirectCallee(); + if (FD && !IsTypeModifiable(FD->getReturnType(), IsDereference)) { + if (!DiagnosticEmitted) { + S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange + << ConstFunction << FD; + DiagnosticEmitted = true; + } + S.Diag(FD->getReturnTypeSourceRange().getBegin(), + diag::note_typecheck_assign_const) + << ConstFunction << FD << FD->getReturnType() + << FD->getReturnTypeSourceRange(); + } + } else if (const DeclRefExpr *DRE = dyn_cast(E)) { + // Point to variable declaration. + if (const ValueDecl *VD = DRE->getDecl()) { + if (!IsTypeModifiable(VD->getType(), IsDereference)) { + if (!DiagnosticEmitted) { + S.Diag(Loc, diag::err_typecheck_assign_const) + << ExprRange << ConstVariable << VD << VD->getType(); + DiagnosticEmitted = true; + } + S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) + << ConstVariable << VD << VD->getType() << VD->getSourceRange(); + } + } + } else if (isa(E)) { + if (const DeclContext *DC = S.getFunctionLevelDeclContext()) { + if (const CXXMethodDecl *MD = dyn_cast(DC)) { + if (MD->isConst()) { + if (!DiagnosticEmitted) { + S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange + << ConstMethod << MD; + DiagnosticEmitted = true; + } + S.Diag(MD->getLocation(), diag::note_typecheck_assign_const) + << ConstMethod << MD << MD->getSourceRange(); + } + } + } + } + + if (DiagnosticEmitted) + return; + + // Can't determine a more specific message, so display the generic error. + S.Diag(Loc, diag::err_typecheck_assign_const) << ExprRange << ConstUnknown; +} + +enum OriginalExprKind { + OEK_Variable, + OEK_Member, + OEK_LValue +}; + +static void DiagnoseRecursiveConstFields(Sema &S, const ValueDecl *VD, + const RecordType *Ty, + SourceLocation Loc, SourceRange Range, + OriginalExprKind OEK, + bool &DiagnosticEmitted) { + std::vector RecordTypeList; + RecordTypeList.push_back(Ty); + unsigned NextToCheckIndex = 0; + // We walk the record hierarchy breadth-first to ensure that we print + // diagnostics in field nesting order. + while (RecordTypeList.size() > NextToCheckIndex) { + bool IsNested = NextToCheckIndex > 0; + for (const FieldDecl *Field : + RecordTypeList[NextToCheckIndex]->getDecl()->fields()) { + // First, check every field for constness. + QualType FieldTy = Field->getType(); + if (FieldTy.isConstQualified()) { + if (!DiagnosticEmitted) { + S.Diag(Loc, diag::err_typecheck_assign_const) + << Range << NestedConstMember << OEK << VD + << IsNested << Field; + DiagnosticEmitted = true; + } + S.Diag(Field->getLocation(), diag::note_typecheck_assign_const) + << NestedConstMember << IsNested << Field + << FieldTy << Field->getSourceRange(); + } + + // Then we append it to the list to check next in order. + FieldTy = FieldTy.getCanonicalType(); + if (const auto *FieldRecTy = FieldTy->getAs()) { + if (!llvm::is_contained(RecordTypeList, FieldRecTy)) + RecordTypeList.push_back(FieldRecTy); + } + } + ++NextToCheckIndex; + } +} + +/// Emit an error for the case where a record we are trying to assign to has a +/// const-qualified field somewhere in its hierarchy. +static void DiagnoseRecursiveConstFields(Sema &S, const Expr *E, + SourceLocation Loc) { + QualType Ty = E->getType(); + assert(Ty->isRecordType() && "lvalue was not record?"); + SourceRange Range = E->getSourceRange(); + const RecordType *RTy = Ty.getCanonicalType()->getAs(); + bool DiagEmitted = false; + + if (const MemberExpr *ME = dyn_cast(E)) + DiagnoseRecursiveConstFields(S, ME->getMemberDecl(), RTy, Loc, + Range, OEK_Member, DiagEmitted); + else if (const DeclRefExpr *DRE = dyn_cast(E)) + DiagnoseRecursiveConstFields(S, DRE->getDecl(), RTy, Loc, + Range, OEK_Variable, DiagEmitted); + else + DiagnoseRecursiveConstFields(S, nullptr, RTy, Loc, + Range, OEK_LValue, DiagEmitted); + if (!DiagEmitted) + DiagnoseConstAssignment(S, E, Loc); +} + +/// CheckForModifiableLvalue - Verify that E is a modifiable lvalue. If not, +/// emit an error and return true. If so, return false. +static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { + assert(!E->hasPlaceholderType(BuiltinType::PseudoObject)); + + S.CheckShadowingDeclModification(E, Loc); + + SourceLocation OrigLoc = Loc; + Expr::isModifiableLvalueResult IsLV = E->isModifiableLvalue(S.Context, + &Loc); + if (IsLV == Expr::MLV_ClassTemporary && IsReadonlyMessage(E, S)) + IsLV = Expr::MLV_InvalidMessageExpression; + if (IsLV == Expr::MLV_Valid) + return false; + + unsigned DiagID = 0; + bool NeedType = false; + switch (IsLV) { // C99 6.5.16p2 + case Expr::MLV_ConstQualified: + // Use a specialized diagnostic when we're assigning to an object + // from an enclosing function or block. + if (NonConstCaptureKind NCCK = isReferenceToNonConstCapture(S, E)) { + if (NCCK == NCCK_Block) + DiagID = diag::err_block_decl_ref_not_modifiable_lvalue; + else + DiagID = diag::err_lambda_decl_ref_not_modifiable_lvalue; + break; + } + + // In ARC, use some specialized diagnostics for occasions where we + // infer 'const'. These are always pseudo-strong variables. + if (S.getLangOpts().ObjCAutoRefCount) { + DeclRefExpr *declRef = dyn_cast(E->IgnoreParenCasts()); + if (declRef && isa(declRef->getDecl())) { + VarDecl *var = cast(declRef->getDecl()); + + // Use the normal diagnostic if it's pseudo-__strong but the + // user actually wrote 'const'. + if (var->isARCPseudoStrong() && + (!var->getTypeSourceInfo() || + !var->getTypeSourceInfo()->getType().isConstQualified())) { + // There are three pseudo-strong cases: + // - self + ObjCMethodDecl *method = S.getCurMethodDecl(); + if (method && var == method->getSelfDecl()) { + DiagID = method->isClassMethod() + ? diag::err_typecheck_arc_assign_self_class_method + : diag::err_typecheck_arc_assign_self; + + // - Objective-C externally_retained attribute. + } else if (var->hasAttr() || + isa(var)) { + DiagID = diag::err_typecheck_arc_assign_externally_retained; + + // - fast enumeration variables + } else { + DiagID = diag::err_typecheck_arr_assign_enumeration; + } + + SourceRange Assign; + if (Loc != OrigLoc) + Assign = SourceRange(OrigLoc, OrigLoc); + S.Diag(Loc, DiagID) << E->getSourceRange() << Assign; + // We need to preserve the AST regardless, so migration tool + // can do its job. + return false; + } + } + } + + // If none of the special cases above are triggered, then this is a + // simple const assignment. + if (DiagID == 0) { + DiagnoseConstAssignment(S, E, Loc); + return true; + } + + break; + case Expr::MLV_ConstAddrSpace: + DiagnoseConstAssignment(S, E, Loc); + return true; + case Expr::MLV_ConstQualifiedField: + DiagnoseRecursiveConstFields(S, E, Loc); + return true; + case Expr::MLV_ArrayType: + case Expr::MLV_ArrayTemporary: + DiagID = diag::err_typecheck_array_not_modifiable_lvalue; + NeedType = true; + break; + case Expr::MLV_NotObjectType: + DiagID = diag::err_typecheck_non_object_not_modifiable_lvalue; + NeedType = true; + break; + case Expr::MLV_LValueCast: + DiagID = diag::err_typecheck_lvalue_casts_not_supported; + break; + case Expr::MLV_Valid: + llvm_unreachable("did not take early return for MLV_Valid"); + case Expr::MLV_InvalidExpression: + case Expr::MLV_MemberFunction: + case Expr::MLV_ClassTemporary: + DiagID = diag::err_typecheck_expression_not_modifiable_lvalue; + break; + case Expr::MLV_IncompleteType: + case Expr::MLV_IncompleteVoidType: + return S.RequireCompleteType(Loc, E->getType(), + diag::err_typecheck_incomplete_type_not_modifiable_lvalue, E); + case Expr::MLV_DuplicateVectorComponents: + DiagID = diag::err_typecheck_duplicate_vector_components_not_mlvalue; + break; + case Expr::MLV_NoSetterProperty: + llvm_unreachable("readonly properties should be processed differently"); + case Expr::MLV_InvalidMessageExpression: + DiagID = diag::err_readonly_message_assignment; + break; + case Expr::MLV_SubObjCPropertySetting: + DiagID = diag::err_no_subobject_property_setting; + break; + } + + SourceRange Assign; + if (Loc != OrigLoc) + Assign = SourceRange(OrigLoc, OrigLoc); + if (NeedType) + S.Diag(Loc, DiagID) << E->getType() << E->getSourceRange() << Assign; + else + S.Diag(Loc, DiagID) << E->getSourceRange() << Assign; + return true; +} + +static void CheckIdentityFieldAssignment(Expr *LHSExpr, Expr *RHSExpr, + SourceLocation Loc, + Sema &Sema) { + if (Sema.inTemplateInstantiation()) + return; + if (Sema.isUnevaluatedContext()) + return; + if (Loc.isInvalid() || Loc.isMacroID()) + return; + if (LHSExpr->getExprLoc().isMacroID() || RHSExpr->getExprLoc().isMacroID()) + return; + + // C / C++ fields + MemberExpr *ML = dyn_cast(LHSExpr); + MemberExpr *MR = dyn_cast(RHSExpr); + if (ML && MR) { + if (!(isa(ML->getBase()) && isa(MR->getBase()))) + return; + const ValueDecl *LHSDecl = + cast(ML->getMemberDecl()->getCanonicalDecl()); + const ValueDecl *RHSDecl = + cast(MR->getMemberDecl()->getCanonicalDecl()); + if (LHSDecl != RHSDecl) + return; + if (LHSDecl->getType().isVolatileQualified()) + return; + if (const ReferenceType *RefTy = LHSDecl->getType()->getAs()) + if (RefTy->getPointeeType().isVolatileQualified()) + return; + + Sema.Diag(Loc, diag::warn_identity_field_assign) << 0; + } + + // Objective-C instance variables + ObjCIvarRefExpr *OL = dyn_cast(LHSExpr); + ObjCIvarRefExpr *OR = dyn_cast(RHSExpr); + if (OL && OR && OL->getDecl() == OR->getDecl()) { + DeclRefExpr *RL = dyn_cast(OL->getBase()->IgnoreImpCasts()); + DeclRefExpr *RR = dyn_cast(OR->getBase()->IgnoreImpCasts()); + if (RL && RR && RL->getDecl() == RR->getDecl()) + Sema.Diag(Loc, diag::warn_identity_field_assign) << 1; + } +} + +// C99 6.5.16.1 +QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS, + SourceLocation Loc, + QualType CompoundType, + BinaryOperatorKind Opc) { + assert(!LHSExpr->hasPlaceholderType(BuiltinType::PseudoObject)); + + // Verify that LHS is a modifiable lvalue, and emit error if not. + if (CheckForModifiableLvalue(LHSExpr, Loc, *this)) + return QualType(); + + QualType LHSType = LHSExpr->getType(); + QualType RHSType = CompoundType.isNull() ? RHS.get()->getType() : + CompoundType; + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + auto *RecordTy = RHSType->getAs(); + if (RecordTy && RecordTy->getDecl()->hasFlexibleArrayMember()) { + // BoundsSafety prevents passing flexible array members by copy. + QualType DisplayType = CompoundType.isNull() + ? RHS.get()->IgnoreParenImpCasts()->getType() : CompoundType; + Diag(Loc, diag::err_flexible_array_member_passed_by_copy) + << DisplayType; + // recovery by continuing as if this worked + } + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + + if (RHS.isUsable()) { + // Even if this check fails don't return early to allow the best + // possible error recovery and to allow any subsequent diagnostics to + // work. + const ValueDecl *Assignee = nullptr; + bool ShowFullyQualifiedAssigneeName = false; + // In simple cases describe what is being assigned to + if (auto *DR = dyn_cast(LHSExpr->IgnoreParenCasts())) { + Assignee = DR->getDecl(); + } else if (auto *ME = dyn_cast(LHSExpr->IgnoreParenCasts())) { + Assignee = ME->getMemberDecl(); + ShowFullyQualifiedAssigneeName = true; + } + + BoundsSafetyCheckAssignmentToCountAttrPtr( + LHSType, RHS.get(), AssignmentAction::Assigning, Loc, Assignee, + ShowFullyQualifiedAssigneeName); + } + + // OpenCL v1.2 s6.1.1.1 p2: + // The half data type can only be used to declare a pointer to a buffer that + // contains half values + if (getLangOpts().OpenCL && + !getOpenCLOptions().isAvailableOption("cl_khr_fp16", getLangOpts()) && + LHSType->isHalfType()) { + Diag(Loc, diag::err_opencl_half_load_store) << 1 + << LHSType.getUnqualifiedType(); + return QualType(); + } + + // WebAssembly tables can't be used on RHS of an assignment expression. + if (RHSType->isWebAssemblyTableType()) { + Diag(Loc, diag::err_wasm_table_art) << 0; + return QualType(); + } + + AssignConvertType ConvTy; + if (CompoundType.isNull()) { + Expr *RHSCheck = RHS.get(); + + CheckIdentityFieldAssignment(LHSExpr, RHSCheck, Loc, *this); + + QualType LHSTy(LHSType); + ConvTy = CheckSingleAssignmentConstraints(LHSTy, RHS); + if (RHS.isInvalid()) + return QualType(); + // Special case of NSObject attributes on c-style pointer types. + if (ConvTy == IncompatiblePointer && + ((Context.isObjCNSObjectType(LHSType) && + RHSType->isObjCObjectPointerType()) || + (Context.isObjCNSObjectType(RHSType) && + LHSType->isObjCObjectPointerType()))) + ConvTy = Compatible; + + if (ConvTy == Compatible && + LHSType->isObjCObjectType()) + Diag(Loc, diag::err_objc_object_assignment) + << LHSType; + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + if (isCompatibleBoundsUnsafeAssignment(ConvTy) && + allowBoundsUnsafePointerAssignment(LHSExpr->getType(), RHS.get(), + Loc)) + ConvTy = Compatible; + if (!CheckDynamicBoundVariableEscape(LHSTy, RHS.get()) && + !allowBoundsUnsafePointerAssignment(LHSExpr->getType(), RHS.get(), + Loc)) + return QualType(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + // If the RHS is a unary plus or minus, check to see if they = and + are + // right next to each other. If so, the user may have typo'd "x =+ 4" + // instead of "x += 4". + if (ImplicitCastExpr *ICE = dyn_cast(RHSCheck)) + RHSCheck = ICE->getSubExpr(); + if (UnaryOperator *UO = dyn_cast(RHSCheck)) { + if ((UO->getOpcode() == UO_Plus || UO->getOpcode() == UO_Minus) && + Loc.isFileID() && UO->getOperatorLoc().isFileID() && + // Only if the two operators are exactly adjacent. + Loc.getLocWithOffset(1) == UO->getOperatorLoc() && + // And there is a space or other character before the subexpr of the + // unary +/-. We don't want to warn on "x=-1". + Loc.getLocWithOffset(2) != UO->getSubExpr()->getBeginLoc() && + UO->getSubExpr()->getBeginLoc().isFileID()) { + Diag(Loc, diag::warn_not_compound_assign) + << (UO->getOpcode() == UO_Plus ? "+" : "-") + << SourceRange(UO->getOperatorLoc(), UO->getOperatorLoc()); + } + } + + if (ConvTy == Compatible) { + if (LHSType.getObjCLifetime() == Qualifiers::OCL_Strong) { + // Warn about retain cycles where a block captures the LHS, but + // not if the LHS is a simple variable into which the block is + // being stored...unless that variable can be captured by reference! + const Expr *InnerLHS = LHSExpr->IgnoreParenCasts(); + const DeclRefExpr *DRE = dyn_cast(InnerLHS); + if (!DRE || DRE->getDecl()->hasAttr()) + ObjC().checkRetainCycles(LHSExpr, RHS.get()); + } + + if (LHSType.getObjCLifetime() == Qualifiers::OCL_Strong || + LHSType.isNonWeakInMRRWithObjCWeak(Context)) { + // It is safe to assign a weak reference into a strong variable. + // Although this code can still have problems: + // id x = self.weakProp; + // id y = self.weakProp; + // we do not warn to warn spuriously when 'x' and 'y' are on separate + // paths through the function. This should be revisited if + // -Wrepeated-use-of-weak is made flow-sensitive. + // For ObjCWeak only, we do not warn if the assign is to a non-weak + // variable, which will be valid for the current autorelease scope. + if (!Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, + RHS.get()->getBeginLoc())) + getCurFunction()->markSafeWeakUse(RHS.get()); + + } else if (getLangOpts().ObjCAutoRefCount || getLangOpts().ObjCWeak) { + checkUnsafeExprAssigns(Loc, LHSExpr, RHS.get()); + } + } + } else { + // Compound assignment "x += y" + ConvTy = CheckAssignmentConstraints(Loc, LHSType, RHSType); + } + + const ValueDecl *Assignee = nullptr; + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Currently the Assignee is only needed for bounds-safety diagnostics + // so guard passing this information along in this mode. + if (LangOpts.BoundsSafety) { + // LHS is allowed to have parentheses so drop them before casting. + if (auto *DRE = dyn_cast(LHSExpr->IgnoreParens())) { + Assignee = DRE->getDecl(); + } else if (auto *ME = dyn_cast(LHSExpr->IgnoreParens())) { + Assignee = ME->getMemberDecl(); + } + // TODO(dliew): Support ArraySubscriptExpr here (rdar://115201001) + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + /*TO_UPSTREAM(BoundsSafety) ON*/ + // Upstream doesn't have `Assignee`. + if (DiagnoseAssignmentResult(ConvTy, Loc, LHSType, RHSType, RHS.get(), + AssignmentAction::Assigning, nullptr, Assignee)) + /*TO_UPSTREAM(BoundsSafety) OFF*/ + return QualType(); + + CheckForNullPointerDereference(*this, LHSExpr); + + AssignedEntity AE{LHSExpr}; + checkAssignmentLifetime(*this, AE, RHS.get()); + + if (getLangOpts().CPlusPlus20 && LHSType.isVolatileQualified()) { + if (CompoundType.isNull()) { + // C++2a [expr.ass]p5: + // A simple-assignment whose left operand is of a volatile-qualified + // type is deprecated unless the assignment is either a discarded-value + // expression or an unevaluated operand + ExprEvalContexts.back().VolatileAssignmentLHSs.push_back(LHSExpr); + } + } + + // C11 6.5.16p3: The type of an assignment expression is the type of the + // left operand would have after lvalue conversion. + // C11 6.3.2.1p2: ...this is called lvalue conversion. If the lvalue has + // qualified type, the value has the unqualified version of the type of the + // lvalue; additionally, if the lvalue has atomic type, the value has the + // non-atomic version of the type of the lvalue. + // C++ 5.17p1: the type of the assignment expression is that of its left + // operand. + return getLangOpts().CPlusPlus ? LHSType : LHSType.getAtomicUnqualifiedType(); +} + +// Scenarios to ignore if expression E is: +// 1. an explicit cast expression into void +// 2. a function call expression that returns void +static bool IgnoreCommaOperand(const Expr *E, const ASTContext &Context) { + E = E->IgnoreParens(); + + if (const CastExpr *CE = dyn_cast(E)) { + if (CE->getCastKind() == CK_ToVoid) { + return true; + } + + // static_cast on a dependent type will not show up as CK_ToVoid. + if (CE->getCastKind() == CK_Dependent && E->getType()->isVoidType() && + CE->getSubExpr()->getType()->isDependentType()) { + return true; + } + } + + if (const auto *CE = dyn_cast(E)) + return CE->getCallReturnType(Context)->isVoidType(); + return false; +} + +void Sema::DiagnoseCommaOperator(const Expr *LHS, SourceLocation Loc) { + // No warnings in macros + if (Loc.isMacroID()) + return; + + // Don't warn in template instantiations. + if (inTemplateInstantiation()) + return; + + // Scope isn't fine-grained enough to explicitly list the specific cases, so + // instead, skip more than needed, then call back into here with the + // CommaVisitor in SemaStmt.cpp. + // The listed locations are the initialization and increment portions + // of a for loop. The additional checks are on the condition of + // if statements, do/while loops, and for loops. + // Differences in scope flags for C89 mode requires the extra logic. + const unsigned ForIncrementFlags = + getLangOpts().C99 || getLangOpts().CPlusPlus + ? Scope::ControlScope | Scope::ContinueScope | Scope::BreakScope + : Scope::ContinueScope | Scope::BreakScope; + const unsigned ForInitFlags = Scope::ControlScope | Scope::DeclScope; + const unsigned ScopeFlags = getCurScope()->getFlags(); + if ((ScopeFlags & ForIncrementFlags) == ForIncrementFlags || + (ScopeFlags & ForInitFlags) == ForInitFlags) + return; + + // If there are multiple comma operators used together, get the RHS of the + // of the comma operator as the LHS. + while (const BinaryOperator *BO = dyn_cast(LHS)) { + if (BO->getOpcode() != BO_Comma) + break; + LHS = BO->getRHS(); + } + + // Only allow some expressions on LHS to not warn. + if (IgnoreCommaOperand(LHS, Context)) + return; + + Diag(Loc, diag::warn_comma_operator); + Diag(LHS->getBeginLoc(), diag::note_cast_to_void) + << LHS->getSourceRange() + << FixItHint::CreateInsertion(LHS->getBeginLoc(), + LangOpts.CPlusPlus ? "static_cast(" + : "(void)(") + << FixItHint::CreateInsertion(PP.getLocForEndOfToken(LHS->getEndLoc()), + ")"); +} + +// C99 6.5.17 +static QualType CheckCommaOperands(Sema &S, ExprResult &LHS, ExprResult &RHS, + SourceLocation Loc) { + LHS = S.CheckPlaceholderExpr(LHS.get()); + RHS = S.CheckPlaceholderExpr(RHS.get()); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); + + // C's comma performs lvalue conversion (C99 6.3.2.1) on both its + // operands, but not unary promotions. + // C++'s comma does not do any conversions at all (C++ [expr.comma]p1). + + // So we treat the LHS as a ignored value, and in C++ we allow the + // containing site to determine what should be done with the RHS. + LHS = S.IgnoredValueConversions(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + + S.DiagnoseUnusedExprResult(LHS.get(), diag::warn_unused_comma_left_operand); + + if (!S.getLangOpts().CPlusPlus) { + RHS = S.DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + if (!RHS.get()->getType()->isVoidType()) + S.RequireCompleteType(Loc, RHS.get()->getType(), + diag::err_incomplete_type); + } + + if (!S.getDiagnostics().isIgnored(diag::warn_comma_operator, Loc)) + S.DiagnoseCommaOperator(LHS.get(), Loc); + + return RHS.get()->getType(); +} + +/// CheckIncrementDecrementOperand - unlike most "Check" methods, this routine +/// doesn't need to call UsualUnaryConversions or UsualArithmeticConversions. +static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op, + ExprValueKind &VK, + ExprObjectKind &OK, + SourceLocation OpLoc, bool IsInc, + bool IsPrefix) { + QualType ResType = Op->getType(); + // Atomic types can be used for increment / decrement where the non-atomic + // versions can, so ignore the _Atomic() specifier for the purpose of + // checking. + if (const AtomicType *ResAtomicType = ResType->getAs()) + ResType = ResAtomicType->getValueType(); + + assert(!ResType.isNull() && "no type for increment/decrement expression"); + + if (S.getLangOpts().CPlusPlus && ResType->isBooleanType()) { + // Decrement of bool is not allowed. + if (!IsInc) { + S.Diag(OpLoc, diag::err_decrement_bool) << Op->getSourceRange(); + return QualType(); + } + // Increment of bool sets it to true, but is deprecated. + S.Diag(OpLoc, S.getLangOpts().CPlusPlus17 ? diag::ext_increment_bool + : diag::warn_increment_bool) + << Op->getSourceRange(); + } else if (S.getLangOpts().CPlusPlus && ResType->isEnumeralType()) { + // Error on enum increments and decrements in C++ mode + S.Diag(OpLoc, diag::err_increment_decrement_enum) << IsInc << ResType; + return QualType(); + } else if (ResType->isRealType()) { + // OK! + } else if (ResType->isPointerType()) { + // C99 6.5.2.4p2, 6.5.6p2 + if (!checkArithmeticOpPointerOperand(S, OpLoc, Op)) + return QualType(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (S.getLangOpts().BoundsSafety && + !checkArithmeticUnaryOpBoundsSafetyPointer(S, Op, IsInc)) + return QualType(); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + } else if (ResType->isObjCObjectPointerType()) { + // On modern runtimes, ObjC pointer arithmetic is forbidden. + // Otherwise, we just need a complete type. + if (checkArithmeticIncompletePointerType(S, OpLoc, Op) || + checkArithmeticOnObjCPointer(S, OpLoc, Op)) + return QualType(); + } else if (ResType->isAnyComplexType()) { + // C99 does not support ++/-- on complex types, we allow as an extension. + S.Diag(OpLoc, S.getLangOpts().C2y ? diag::warn_c2y_compat_increment_complex + : diag::ext_c2y_increment_complex) + << IsInc << Op->getSourceRange(); + } else if (ResType->isPlaceholderType()) { + ExprResult PR = S.CheckPlaceholderExpr(Op); + if (PR.isInvalid()) return QualType(); + return CheckIncrementDecrementOperand(S, PR.get(), VK, OK, OpLoc, + IsInc, IsPrefix); + } else if (S.getLangOpts().AltiVec && ResType->isVectorType()) { + // OK! ( C/C++ Language Extensions for CBEA(Version 2.6) 10.3 ) + } else if (S.getLangOpts().ZVector && ResType->isVectorType() && + (ResType->castAs()->getVectorKind() != + VectorKind::AltiVecBool)) { + // The z vector extensions allow ++ and -- for non-bool vectors. + } else if (S.getLangOpts().OpenCL && ResType->isVectorType() && + ResType->castAs()->getElementType()->isIntegerType()) { + // OpenCL V1.2 6.3 says dec/inc ops operate on integer vector types. + } else { + S.Diag(OpLoc, diag::err_typecheck_illegal_increment_decrement) + << ResType << int(IsInc) << Op->getSourceRange(); + return QualType(); + } + // At this point, we know we have a real, complex or pointer type. + // Now make sure the operand is a modifiable lvalue. + if (CheckForModifiableLvalue(Op, OpLoc, S)) + return QualType(); + if (S.getLangOpts().CPlusPlus20 && ResType.isVolatileQualified()) { + // C++2a [expr.pre.inc]p1, [expr.post.inc]p1: + // An operand with volatile-qualified type is deprecated + S.Diag(OpLoc, diag::warn_deprecated_increment_decrement_volatile) + << IsInc << ResType; + } + // In C++, a prefix increment is the same type as the operand. Otherwise + // (in C or with postfix), the increment is the unqualified type of the + // operand. + if (IsPrefix && S.getLangOpts().CPlusPlus) { + VK = VK_LValue; + OK = Op->getObjectKind(); + return ResType; + } else { + VK = VK_PRValue; + return ResType.getUnqualifiedType(); + } +} + +/// getPrimaryDecl - Helper function for CheckAddressOfOperand(). +/// This routine allows us to typecheck complex/recursive expressions +/// where the declaration is needed for type checking. We only need to +/// handle cases when the expression references a function designator +/// or is an lvalue. Here are some examples: +/// - &(x) => x +/// - &*****f => f for f a function designator. +/// - &s.xx => s +/// - &s.zz[1].yy -> s, if zz is an array +/// - *(x + 1) -> x, if x is an array +/// - &"123"[2] -> 0 +/// - & __real__ x -> x +/// +/// FIXME: We don't recurse to the RHS of a comma, nor handle pointers to +/// members. +static ValueDecl *getPrimaryDecl(Expr *E) { + switch (E->getStmtClass()) { + case Stmt::DeclRefExprClass: + return cast(E)->getDecl(); + case Stmt::MemberExprClass: + // If this is an arrow operator, the address is an offset from + // the base's value, so the object the base refers to is + // irrelevant. + if (cast(E)->isArrow()) + return nullptr; + // Otherwise, the expression refers to a part of the base + return getPrimaryDecl(cast(E)->getBase()); + case Stmt::ArraySubscriptExprClass: { + // FIXME: This code shouldn't be necessary! We should catch the implicit + // promotion of register arrays earlier. + Expr* Base = cast(E)->getBase(); + if (ImplicitCastExpr* ICE = dyn_cast(Base)) { + if (ICE->getSubExpr()->getType()->isArrayType()) + return getPrimaryDecl(ICE->getSubExpr()); + } + return nullptr; + } + case Stmt::UnaryOperatorClass: { + UnaryOperator *UO = cast(E); + + switch(UO->getOpcode()) { + case UO_Real: + case UO_Imag: + case UO_Extension: + return getPrimaryDecl(UO->getSubExpr()); + default: + return nullptr; + } + } + case Stmt::ParenExprClass: + return getPrimaryDecl(cast(E)->getSubExpr()); + case Stmt::ImplicitCastExprClass: + // If the result of an implicit cast is an l-value, we care about + // the sub-expression; otherwise, the result here doesn't matter. + return getPrimaryDecl(cast(E)->getSubExpr()); + case Stmt::CXXUuidofExprClass: + return cast(E)->getGuidDecl(); + default: + return nullptr; + } +} + +namespace { +enum { + AO_Bit_Field = 0, + AO_Vector_Element = 1, + AO_Property_Expansion = 2, + AO_Register_Variable = 3, + AO_Matrix_Element = 4, + AO_No_Error = 5 +}; +} +/// Diagnose invalid operand for address of operations. +/// +/// \param Type The type of operand which cannot have its address taken. +static void diagnoseAddressOfInvalidType(Sema &S, SourceLocation Loc, + Expr *E, unsigned Type) { + S.Diag(Loc, diag::err_typecheck_address_of) << Type << E->getSourceRange(); +} + +bool Sema::CheckUseOfCXXMethodAsAddressOfOperand(SourceLocation OpLoc, + const Expr *Op, + const CXXMethodDecl *MD) { + const auto *DRE = cast(Op->IgnoreParens()); + + if (Op != DRE) + return Diag(OpLoc, diag::err_parens_pointer_member_function) + << Op->getSourceRange(); + + // Taking the address of a dtor is illegal per C++ [class.dtor]p2. + if (isa(MD)) + return Diag(OpLoc, diag::err_typecheck_addrof_dtor) + << DRE->getSourceRange(); + + if (DRE->getQualifier()) + return false; + + if (MD->getParent()->getName().empty()) + return Diag(OpLoc, diag::err_unqualified_pointer_member_function) + << DRE->getSourceRange(); + + SmallString<32> Str; + StringRef Qual = (MD->getParent()->getName() + "::").toStringRef(Str); + return Diag(OpLoc, diag::err_unqualified_pointer_member_function) + << DRE->getSourceRange() + << FixItHint::CreateInsertion(DRE->getSourceRange().getBegin(), Qual); +} + +QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) { + if (const BuiltinType *PTy = OrigOp.get()->getType()->getAsPlaceholderType()){ + if (PTy->getKind() == BuiltinType::Overload) { + Expr *E = OrigOp.get()->IgnoreParens(); + if (!isa(E)) { + assert(cast(E)->getOpcode() == UO_AddrOf); + Diag(OpLoc, diag::err_typecheck_invalid_lvalue_addrof_addrof_function) + << OrigOp.get()->getSourceRange(); + return QualType(); + } + + OverloadExpr *Ovl = cast(E); + if (isa(Ovl)) + if (!ResolveSingleFunctionTemplateSpecialization(Ovl)) { + Diag(OpLoc, diag::err_invalid_form_pointer_member_function) + << OrigOp.get()->getSourceRange(); + return QualType(); + } + + return Context.OverloadTy; + } + + if (PTy->getKind() == BuiltinType::UnknownAny) + return Context.UnknownAnyTy; + + if (PTy->getKind() == BuiltinType::BoundMember) { + Diag(OpLoc, diag::err_invalid_form_pointer_member_function) + << OrigOp.get()->getSourceRange(); + return QualType(); + } + + OrigOp = CheckPlaceholderExpr(OrigOp.get()); + if (OrigOp.isInvalid()) return QualType(); + } + + if (OrigOp.get()->isTypeDependent()) + return Context.DependentTy; + + assert(!OrigOp.get()->hasPlaceholderType()); + + // Make sure to ignore parentheses in subsequent checks + Expr *op = OrigOp.get()->IgnoreParens(); + + // In OpenCL captures for blocks called as lambda functions + // are located in the private address space. Blocks used in + // enqueue_kernel can be located in a different address space + // depending on a vendor implementation. Thus preventing + // taking an address of the capture to avoid invalid AS casts. + if (LangOpts.OpenCL) { + auto* VarRef = dyn_cast(op); + if (VarRef && VarRef->refersToEnclosingVariableOrCapture()) { + Diag(op->getExprLoc(), diag::err_opencl_taking_address_capture); + return QualType(); + } + } + + if (getLangOpts().C99) { + // Implement C99-only parts of addressof rules. + if (UnaryOperator* uOp = dyn_cast(op)) { + if (uOp->getOpcode() == UO_Deref) + // Per C99 6.5.3.2, the address of a deref always returns a valid result + // (assuming the deref expression is valid). + return uOp->getSubExpr()->getType(); + } + // Technically, there should be a check for array subscript + // expressions here, but the result of one is always an lvalue anyway. + } + ValueDecl *dcl = getPrimaryDecl(op); + + if (auto *FD = dyn_cast_or_null(dcl)) + if (!checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true, + op->getBeginLoc())) + return QualType(); + + Expr::LValueClassification lval = op->ClassifyLValue(Context); + unsigned AddressOfError = AO_No_Error; + + if (lval == Expr::LV_ClassTemporary || lval == Expr::LV_ArrayTemporary) { + bool sfinae = (bool)isSFINAEContext(); + Diag(OpLoc, isSFINAEContext() ? diag::err_typecheck_addrof_temporary + : diag::ext_typecheck_addrof_temporary) + << op->getType() << op->getSourceRange(); + if (sfinae) + return QualType(); + // Materialize the temporary as an lvalue so that we can take its address. + OrigOp = op = + CreateMaterializeTemporaryExpr(op->getType(), OrigOp.get(), true); + } else if (isa(op)) { + return Context.getPointerType(op->getType()); + } else if (lval == Expr::LV_MemberFunction) { + // If it's an instance method, make a member pointer. + // The expression must have exactly the form &A::foo. + + // If the underlying expression isn't a decl ref, give up. + if (!isa(op)) { + Diag(OpLoc, diag::err_invalid_form_pointer_member_function) + << OrigOp.get()->getSourceRange(); + return QualType(); + } + DeclRefExpr *DRE = cast(op); + CXXMethodDecl *MD = cast(DRE->getDecl()); + + CheckUseOfCXXMethodAsAddressOfOperand(OpLoc, OrigOp.get(), MD); + QualType MPTy = Context.getMemberPointerType( + op->getType(), DRE->getQualifier(), MD->getParent()); + + if (getLangOpts().PointerAuthCalls && MD->isVirtual() && + !isUnevaluatedContext() && !MPTy->isDependentType()) { + // When pointer authentication is enabled, argument and return types of + // vitual member functions must be complete. This is because vitrual + // member function pointers are implemented using virtual dispatch + // thunks and the thunks cannot be emitted if the argument or return + // types are incomplete. + auto ReturnOrParamTypeIsIncomplete = [&](QualType T, + SourceLocation DeclRefLoc, + SourceLocation RetArgTypeLoc) { + if (RequireCompleteType(DeclRefLoc, T, diag::err_incomplete_type)) { + Diag(DeclRefLoc, + diag::note_ptrauth_virtual_function_pointer_incomplete_arg_ret); + Diag(RetArgTypeLoc, + diag::note_ptrauth_virtual_function_incomplete_arg_ret_type) + << T; + return true; + } + return false; + }; + QualType RetTy = MD->getReturnType(); + bool IsIncomplete = + !RetTy->isVoidType() && + ReturnOrParamTypeIsIncomplete( + RetTy, OpLoc, MD->getReturnTypeSourceRange().getBegin()); + for (auto *PVD : MD->parameters()) + IsIncomplete |= ReturnOrParamTypeIsIncomplete(PVD->getType(), OpLoc, + PVD->getBeginLoc()); + if (IsIncomplete) + return QualType(); + } + + // Under the MS ABI, lock down the inheritance model now. + if (Context.getTargetInfo().getCXXABI().isMicrosoft()) + (void)isCompleteType(OpLoc, MPTy); + return MPTy; + } else if (lval != Expr::LV_Valid && lval != Expr::LV_IncompleteVoidType) { + // C99 6.5.3.2p1 + // The operand must be either an l-value or a function designator + if (!op->getType()->isFunctionType()) { + // Use a special diagnostic for loads from property references. + if (isa(op)) { + AddressOfError = AO_Property_Expansion; + } else { + Diag(OpLoc, diag::err_typecheck_invalid_lvalue_addrof) + << op->getType() << op->getSourceRange(); + return QualType(); + } + } else if (const auto *DRE = dyn_cast(op)) { + if (const auto *MD = dyn_cast_or_null(DRE->getDecl())) + CheckUseOfCXXMethodAsAddressOfOperand(OpLoc, OrigOp.get(), MD); + } + + } else if (op->getObjectKind() == OK_BitField) { // C99 6.5.3.2p1 + // The operand cannot be a bit-field + AddressOfError = AO_Bit_Field; + } else if (op->getObjectKind() == OK_VectorComponent) { + // The operand cannot be an element of a vector + AddressOfError = AO_Vector_Element; + } else if (op->getObjectKind() == OK_MatrixComponent) { + // The operand cannot be an element of a matrix. + AddressOfError = AO_Matrix_Element; + } else if (dcl) { // C99 6.5.3.2p1 + // We have an lvalue with a decl. Make sure the decl is not declared + // with the register storage-class specifier. + if (const VarDecl *vd = dyn_cast(dcl)) { + // in C++ it is not error to take address of a register + // variable (c++03 7.1.1P3) + if (vd->getStorageClass() == SC_Register && + !getLangOpts().CPlusPlus) { + AddressOfError = AO_Register_Variable; + } + } else if (isa(dcl)) { + AddressOfError = AO_Property_Expansion; + } else if (isa(dcl)) { + return Context.OverloadTy; + } else if (isa(dcl) || isa(dcl)) { + // Okay: we can take the address of a field. + // Could be a pointer to member, though, if there is an explicit + // scope qualifier for the class. + + // [C++26] [expr.prim.id.general] + // If an id-expression E denotes a non-static non-type member + // of some class C [...] and if E is a qualified-id, E is + // not the un-parenthesized operand of the unary & operator [...] + // the id-expression is transformed into a class member access expression. + if (auto *DRE = dyn_cast(op); + DRE && DRE->getQualifier() && !isa(OrigOp.get())) { + DeclContext *Ctx = dcl->getDeclContext(); + if (Ctx && Ctx->isRecord()) { + if (dcl->getType()->isReferenceType()) { + Diag(OpLoc, + diag::err_cannot_form_pointer_to_member_of_reference_type) + << dcl->getDeclName() << dcl->getType(); + return QualType(); + } + + while (cast(Ctx)->isAnonymousStructOrUnion()) + Ctx = Ctx->getParent(); + + QualType MPTy = Context.getMemberPointerType( + op->getType(), DRE->getQualifier(), cast(Ctx)); + // Under the MS ABI, lock down the inheritance model now. + if (Context.getTargetInfo().getCXXABI().isMicrosoft()) + (void)isCompleteType(OpLoc, MPTy); + return MPTy; + } + } + } else if (!isa(dcl)) + llvm_unreachable("Unknown/unexpected decl type"); + } + + if (AddressOfError != AO_No_Error) { + diagnoseAddressOfInvalidType(*this, OpLoc, op, AddressOfError); + return QualType(); + } + + if (lval == Expr::LV_IncompleteVoidType) { + // Taking the address of a void variable is technically illegal, but we + // allow it in cases which are otherwise valid. + // Example: "extern void x; void* y = &x;". + Diag(OpLoc, diag::ext_typecheck_addrof_void) << op->getSourceRange(); + } + + // If the operand has type "type", the result has type "pointer to type". + if (op->getType()->isObjCObjectType()) + return Context.getObjCObjectPointerType(op->getType()); + + // Cannot take the address of WebAssembly references or tables. + if (Context.getTargetInfo().getTriple().isWasm()) { + QualType OpTy = op->getType(); + if (OpTy.isWebAssemblyReferenceType()) { + Diag(OpLoc, diag::err_wasm_ca_reference) + << 1 << OrigOp.get()->getSourceRange(); + return QualType(); + } + if (OpTy->isWebAssemblyTableType()) { + Diag(OpLoc, diag::err_wasm_table_pr) + << 1 << OrigOp.get()->getSourceRange(); + return QualType(); + } + } + + CheckAddressOfPackedMember(op); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + if (auto ME = dyn_cast(op)) { + auto FD = ME->getMemberDecl(); + if (auto *Att = FD->getAttr()) { + assert(Att->dependerDecls_size() > 0); + const RecordDecl *FlexBase = + DynamicCountPointerAssignmentAnalysis::computeFlexBaseKey( + op, /*OS*/ nullptr); + if (FlexBase) { + for (auto DepDecl : Att->dependerDecls()) { + // Only emit error if this field is being accessed from a struct + // with a flexible array member. If this is a nested struct field, + // it could be used separately in a context without FAMs, and in + // that case we should allow taking its address. + if (!FlexBase->isParentStructOf(DepDecl)) + continue; + ValueDecl *FAMDecl = cast(DepDecl); + if (auto DCPTy = FAMDecl->getType()->getAs()) { + assert(DCPTy->isIncompleteArrayType() && + "expected flexible array member"); + Diag( + OpLoc, + diag:: + err_bounds_safety_unsupported_address_of_incomplete_array_count) + << op->getSourceRange(); + auto CountExpr = DCPTy->getCountExpr(); + Diag(CountExpr->getExprLoc(), + diag::note_bounds_safety_count_param_loc) + << CountExpr->getSourceRange(); + // This results in RecoveryExpr, preventing double errors when the + // expression is reconstructed + return QualType(); + } + } + } + } + } + } + + // BoundsSafety: taking an address returns a bound pointer to allow bounds to be + // propagated in case the target has bounds, this can be the case with or + // without auto-typing of variables. + auto GetPointerBoundAttributes = [&](Expr *E) -> BoundsSafetyPointerAttributes { + BoundsSafetyPointerAttributes FA; + if (auto ASE = dyn_cast(E)) { + if (auto PT = ASE->getBase()->getType()->getAs()) { + FA = PT->getPointerAttributes(); + } + } else if (getLangOpts().BoundsSafety) { + // Taking the address of an object with a meaningless size results in a + // __single pointer. + if (E->getType()->isSizeMeaningless()) { + FA.setSingle(); + } else { + FA.setBidiIndexable(); + } + } + if (E->getType()->isCountAttributedType()) { + if (const auto *AT = Context.getAsIncompleteArrayType(E->getType())) { + Diag(OpLoc, diag::err_bounds_safety_unsupported_address_of_incomplete_array) + << op->getSourceRange(); + Diag(OpLoc, diag::note_bounds_safety_remove_address_of_operator) + << Context.getPointerType(AT->getElementType()) + << Context.getPointerType(OrigOp.get()->getType()) + << FixItHint::CreateRemoval(op->getSourceRange()); + // recover by using __bidi_indexable bounds, which are less likely + // to cause spurious warnings + FA.setBidiIndexable(); + } + } + return FA; + }; + BoundsSafetyPointerAttributes FA = GetPointerBoundAttributes(op); + QualType PT = Context.getPointerType(op->getType(), FA); + if (const auto *ASE = dyn_cast(op)) { + if (const auto *VTT = + ASE->getBase()->getType()->getAs()) { + return Context.getValueTerminatedType(PT, VTT->getTerminatorExpr()); + } + } else if (getLangOpts().BoundsSafety) { + PT = Context.getAttributedType(attr::PtrAutoAttr, PT, PT); + } + return PT; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + return Context.getPointerType(op->getType()); +} + +static void RecordModifiableNonNullParam(Sema &S, const Expr *Exp) { + const DeclRefExpr *DRE = dyn_cast(Exp); + if (!DRE) + return; + const Decl *D = DRE->getDecl(); + if (!D) + return; + const ParmVarDecl *Param = dyn_cast(D); + if (!Param) + return; + if (const FunctionDecl* FD = dyn_cast(Param->getDeclContext())) + if (!FD->hasAttr() && !Param->hasAttr()) + return; + if (FunctionScopeInfo *FD = S.getCurFunction()) + FD->ModifiedNonNullParams.insert(Param); +} + +/// CheckIndirectionOperand - Type check unary indirection (prefix '*'). +static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK, + SourceLocation OpLoc, + bool IsAfterAmp = false) { + ExprResult ConvResult = S.UsualUnaryConversions(Op); + if (ConvResult.isInvalid()) + return QualType(); + Op = ConvResult.get(); + QualType OpTy = Op->getType(); + QualType Result; + + if (isa(Op)) { + QualType OpOrigType = Op->IgnoreParenCasts()->getType(); + S.CheckCompatibleReinterpretCast(OpOrigType, OpTy, /*IsDereference*/true, + Op->getSourceRange()); + } + + if (const PointerType *PT = OpTy->getAs()) + { + Result = PT->getPointeeType(); + } + else if (const ObjCObjectPointerType *OPT = + OpTy->getAs()) + Result = OPT->getPointeeType(); + else { + ExprResult PR = S.CheckPlaceholderExpr(Op); + if (PR.isInvalid()) return QualType(); + if (PR.get() != Op) + return CheckIndirectionOperand(S, PR.get(), VK, OpLoc); + } + + if (Result.isNull()) { + S.Diag(OpLoc, diag::err_typecheck_indirection_requires_pointer) + << OpTy << Op->getSourceRange(); + return QualType(); + } + + if (Result->isVoidType()) { + // C++ [expr.unary.op]p1: + // [...] the expression to which [the unary * operator] is applied shall + // be a pointer to an object type, or a pointer to a function type + LangOptions LO = S.getLangOpts(); + if (LO.CPlusPlus) + S.Diag(OpLoc, diag::err_typecheck_indirection_through_void_pointer_cpp) + << OpTy << Op->getSourceRange(); + else if (!(LO.C99 && IsAfterAmp) && !S.isUnevaluatedContext()) + S.Diag(OpLoc, diag::ext_typecheck_indirection_through_void_pointer) + << OpTy << Op->getSourceRange(); + } + + // Dereferences are usually l-values... + VK = VK_LValue; + + // ...except that certain expressions are never l-values in C. + if (!S.getLangOpts().CPlusPlus && Result.isCForbiddenLValueType()) + VK = VK_PRValue; + + return Result; +} + +BinaryOperatorKind Sema::ConvertTokenKindToBinaryOpcode(tok::TokenKind Kind) { + BinaryOperatorKind Opc; + switch (Kind) { + default: llvm_unreachable("Unknown binop!"); + case tok::periodstar: Opc = BO_PtrMemD; break; + case tok::arrowstar: Opc = BO_PtrMemI; break; + case tok::star: Opc = BO_Mul; break; + case tok::slash: Opc = BO_Div; break; + case tok::percent: Opc = BO_Rem; break; + case tok::plus: Opc = BO_Add; break; + case tok::minus: Opc = BO_Sub; break; + case tok::lessless: Opc = BO_Shl; break; + case tok::greatergreater: Opc = BO_Shr; break; + case tok::lessequal: Opc = BO_LE; break; + case tok::less: Opc = BO_LT; break; + case tok::greaterequal: Opc = BO_GE; break; + case tok::greater: Opc = BO_GT; break; + case tok::exclaimequal: Opc = BO_NE; break; + case tok::equalequal: Opc = BO_EQ; break; + case tok::spaceship: Opc = BO_Cmp; break; + case tok::amp: Opc = BO_And; break; + case tok::caret: Opc = BO_Xor; break; + case tok::pipe: Opc = BO_Or; break; + case tok::ampamp: Opc = BO_LAnd; break; + case tok::pipepipe: Opc = BO_LOr; break; + case tok::equal: Opc = BO_Assign; break; + case tok::starequal: Opc = BO_MulAssign; break; + case tok::slashequal: Opc = BO_DivAssign; break; + case tok::percentequal: Opc = BO_RemAssign; break; + case tok::plusequal: Opc = BO_AddAssign; break; + case tok::minusequal: Opc = BO_SubAssign; break; + case tok::lesslessequal: Opc = BO_ShlAssign; break; + case tok::greatergreaterequal: Opc = BO_ShrAssign; break; + case tok::ampequal: Opc = BO_AndAssign; break; + case tok::caretequal: Opc = BO_XorAssign; break; + case tok::pipeequal: Opc = BO_OrAssign; break; + case tok::comma: Opc = BO_Comma; break; + } + return Opc; +} + +static inline UnaryOperatorKind ConvertTokenKindToUnaryOpcode( + tok::TokenKind Kind) { + UnaryOperatorKind Opc; + switch (Kind) { + default: llvm_unreachable("Unknown unary op!"); + case tok::plusplus: Opc = UO_PreInc; break; + case tok::minusminus: Opc = UO_PreDec; break; + case tok::amp: Opc = UO_AddrOf; break; + case tok::star: Opc = UO_Deref; break; + case tok::plus: Opc = UO_Plus; break; + case tok::minus: Opc = UO_Minus; break; + case tok::tilde: Opc = UO_Not; break; + case tok::exclaim: Opc = UO_LNot; break; + case tok::kw___real: Opc = UO_Real; break; + case tok::kw___imag: Opc = UO_Imag; break; + case tok::kw___extension__: Opc = UO_Extension; break; + } + return Opc; +} + +const FieldDecl * +Sema::getSelfAssignmentClassMemberCandidate(const ValueDecl *SelfAssigned) { + // Explore the case for adding 'this->' to the LHS of a self assignment, very + // common for setters. + // struct A { + // int X; + // -void setX(int X) { X = X; } + // +void setX(int X) { this->X = X; } + // }; + + // Only consider parameters for self assignment fixes. + if (!isa(SelfAssigned)) + return nullptr; + const auto *Method = + dyn_cast_or_null(getCurFunctionDecl(true)); + if (!Method) + return nullptr; + + const CXXRecordDecl *Parent = Method->getParent(); + // In theory this is fixable if the lambda explicitly captures this, but + // that's added complexity that's rarely going to be used. + if (Parent->isLambda()) + return nullptr; + + // FIXME: Use an actual Lookup operation instead of just traversing fields + // in order to get base class fields. + auto Field = + llvm::find_if(Parent->fields(), + [Name(SelfAssigned->getDeclName())](const FieldDecl *F) { + return F->getDeclName() == Name; + }); + return (Field != Parent->field_end()) ? *Field : nullptr; +} + +/// DiagnoseSelfAssignment - Emits a warning if a value is assigned to itself. +/// This warning suppressed in the event of macro expansions. +static void DiagnoseSelfAssignment(Sema &S, Expr *LHSExpr, Expr *RHSExpr, + SourceLocation OpLoc, bool IsBuiltin) { + if (S.inTemplateInstantiation()) + return; + if (S.isUnevaluatedContext()) + return; + if (OpLoc.isInvalid() || OpLoc.isMacroID()) + return; + LHSExpr = LHSExpr->IgnoreParenImpCasts(); + RHSExpr = RHSExpr->IgnoreParenImpCasts(); + const DeclRefExpr *LHSDeclRef = dyn_cast(LHSExpr); + const DeclRefExpr *RHSDeclRef = dyn_cast(RHSExpr); + if (!LHSDeclRef || !RHSDeclRef || + LHSDeclRef->getLocation().isMacroID() || + RHSDeclRef->getLocation().isMacroID()) + return; + const ValueDecl *LHSDecl = + cast(LHSDeclRef->getDecl()->getCanonicalDecl()); + const ValueDecl *RHSDecl = + cast(RHSDeclRef->getDecl()->getCanonicalDecl()); + if (LHSDecl != RHSDecl) + return; + if (LHSDecl->getType().isVolatileQualified()) + return; + if (const ReferenceType *RefTy = LHSDecl->getType()->getAs()) + if (RefTy->getPointeeType().isVolatileQualified()) + return; + + // Do not warn about self-assignments for dynamic-range pointer types, or + // for declarations that they depend on. + if (LHSDecl->getType()->isBoundsAttributedType()) + return; + if (auto *Attr = LHSDecl->getAttr()) + if (!Attr->getIsDeref()) + return; + + auto Diag = S.Diag(OpLoc, IsBuiltin ? diag::warn_self_assignment_builtin + : diag::warn_self_assignment_overloaded) + << LHSDeclRef->getType() << LHSExpr->getSourceRange() + << RHSExpr->getSourceRange(); + if (const FieldDecl *SelfAssignField = + S.getSelfAssignmentClassMemberCandidate(RHSDecl)) + Diag << 1 << SelfAssignField + << FixItHint::CreateInsertion(LHSDeclRef->getBeginLoc(), "this->"); + else + Diag << 0; +} + +/// Check if a bitwise-& is performed on an Objective-C pointer. This +/// is usually indicative of introspection within the Objective-C pointer. +static void checkObjCPointerIntrospection(Sema &S, ExprResult &L, ExprResult &R, + SourceLocation OpLoc) { + if (!S.getLangOpts().ObjC) + return; + + const Expr *ObjCPointerExpr = nullptr, *OtherExpr = nullptr; + const Expr *LHS = L.get(); + const Expr *RHS = R.get(); + + if (LHS->IgnoreParenCasts()->getType()->isObjCObjectPointerType()) { + ObjCPointerExpr = LHS; + OtherExpr = RHS; + } + else if (RHS->IgnoreParenCasts()->getType()->isObjCObjectPointerType()) { + ObjCPointerExpr = RHS; + OtherExpr = LHS; + } + + // This warning is deliberately made very specific to reduce false + // positives with logic that uses '&' for hashing. This logic mainly + // looks for code trying to introspect into tagged pointers, which + // code should generally never do. + if (ObjCPointerExpr && isa(OtherExpr->IgnoreParenCasts())) { + unsigned Diag = diag::warn_objc_pointer_masking; + // Determine if we are introspecting the result of performSelectorXXX. + const Expr *Ex = ObjCPointerExpr->IgnoreParenCasts(); + // Special case messages to -performSelector and friends, which + // can return non-pointer values boxed in a pointer value. + // Some clients may wish to silence warnings in this subcase. + if (const ObjCMessageExpr *ME = dyn_cast(Ex)) { + Selector S = ME->getSelector(); + StringRef SelArg0 = S.getNameForSlot(0); + if (SelArg0.starts_with("performSelector")) + Diag = diag::warn_objc_pointer_masking_performSelector; + } + + S.Diag(OpLoc, Diag) + << ObjCPointerExpr->getSourceRange(); + } +} + +static NamedDecl *getDeclFromExpr(Expr *E) { + if (!E) + return nullptr; + if (auto *DRE = dyn_cast(E)) + return DRE->getDecl(); + if (auto *ME = dyn_cast(E)) + return ME->getMemberDecl(); + if (auto *IRE = dyn_cast(E)) + return IRE->getDecl(); + return nullptr; +} + +// This helper function promotes a binary operator's operands (which are of a +// half vector type) to a vector of floats and then truncates the result to +// a vector of either half or short. +static ExprResult convertHalfVecBinOp(Sema &S, ExprResult LHS, ExprResult RHS, + BinaryOperatorKind Opc, QualType ResultTy, + ExprValueKind VK, ExprObjectKind OK, + bool IsCompAssign, SourceLocation OpLoc, + FPOptionsOverride FPFeatures) { + auto &Context = S.getASTContext(); + assert((isVector(ResultTy, Context.HalfTy) || + isVector(ResultTy, Context.ShortTy)) && + "Result must be a vector of half or short"); + assert(isVector(LHS.get()->getType(), Context.HalfTy) && + isVector(RHS.get()->getType(), Context.HalfTy) && + "both operands expected to be a half vector"); + + RHS = convertVector(RHS.get(), Context.FloatTy, S); + QualType BinOpResTy = RHS.get()->getType(); + + // If Opc is a comparison, ResultType is a vector of shorts. In that case, + // change BinOpResTy to a vector of ints. + if (isVector(ResultTy, Context.ShortTy)) + BinOpResTy = S.GetSignedVectorType(BinOpResTy); + + if (IsCompAssign) + return CompoundAssignOperator::Create(Context, LHS.get(), RHS.get(), Opc, + ResultTy, VK, OK, OpLoc, FPFeatures, + BinOpResTy, BinOpResTy); + + LHS = convertVector(LHS.get(), Context.FloatTy, S); + auto *BO = BinaryOperator::Create(Context, LHS.get(), RHS.get(), Opc, + BinOpResTy, VK, OK, OpLoc, FPFeatures); + return convertVector(BO, ResultTy->castAs()->getElementType(), S); +} + +static std::pair +CorrectDelayedTyposInBinOp(Sema &S, BinaryOperatorKind Opc, Expr *LHSExpr, + Expr *RHSExpr) { + ExprResult LHS = LHSExpr, RHS = RHSExpr; + if (!S.Context.isDependenceAllowed()) { + // C cannot handle TypoExpr nodes on either side of a binop because it + // doesn't handle dependent types properly, so make sure any TypoExprs have + // been dealt with before checking the operands. + LHS = S.CorrectDelayedTyposInExpr(LHS); + RHS = S.CorrectDelayedTyposInExpr( + RHS, /*InitDecl=*/nullptr, /*RecoverUncorrectedTypos=*/false, + [Opc, LHS](Expr *E) { + if (Opc != BO_Assign) + return ExprResult(E); + // Avoid correcting the RHS to the same Expr as the LHS. + Decl *D = getDeclFromExpr(E); + return (D && D == getDeclFromExpr(LHS.get())) ? ExprError() : E; + }); + } + return std::make_pair(LHS, RHS); +} + +/// Returns true if conversion between vectors of halfs and vectors of floats +/// is needed. +static bool needsConversionOfHalfVec(bool OpRequiresConversion, ASTContext &Ctx, + Expr *E0, Expr *E1 = nullptr) { + if (!OpRequiresConversion || Ctx.getLangOpts().NativeHalfType || + Ctx.getTargetInfo().useFP16ConversionIntrinsics()) + return false; + + auto HasVectorOfHalfType = [&Ctx](Expr *E) { + QualType Ty = E->IgnoreImplicit()->getType(); + + // Don't promote half precision neon vectors like float16x4_t in arm_neon.h + // to vectors of floats. Although the element type of the vectors is __fp16, + // the vectors shouldn't be treated as storage-only types. See the + // discussion here: https://reviews.llvm.org/rG825235c140e7 + if (const VectorType *VT = Ty->getAs()) { + if (VT->getVectorKind() == VectorKind::Neon) + return false; + return VT->getElementType().getCanonicalType() == Ctx.HalfTy; + } + return false; + }; + + return HasVectorOfHalfType(E0) && (!E1 || HasVectorOfHalfType(E1)); +} + +ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, + BinaryOperatorKind Opc, + Expr *LHSExpr, Expr *RHSExpr) { + if (getLangOpts().CPlusPlus11 && isa(RHSExpr)) { + // The syntax only allows initializer lists on the RHS of assignment, + // so we don't need to worry about accepting invalid code for + // non-assignment operators. + // C++11 5.17p9: + // The meaning of x = {v} [...] is that of x = T(v) [...]. The meaning + // of x = {} is x = T(). + InitializationKind Kind = InitializationKind::CreateDirectList( + RHSExpr->getBeginLoc(), RHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); + InitializedEntity Entity = + InitializedEntity::InitializeTemporary(LHSExpr->getType()); + InitializationSequence InitSeq(*this, Entity, Kind, RHSExpr); + ExprResult Init = InitSeq.Perform(*this, Entity, Kind, RHSExpr); + if (Init.isInvalid()) + return Init; + RHSExpr = Init.get(); + } + + ExprResult LHS = LHSExpr, RHS = RHSExpr; + QualType ResultTy; // Result type of the binary operator. + // The following two variables are used for compound assignment operators + QualType CompLHSTy; // Type of LHS after promotions for computation + QualType CompResultTy; // Type of computation result + ExprValueKind VK = VK_PRValue; + ExprObjectKind OK = OK_Ordinary; + bool ConvertHalfVec = false; + + std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr); + if (!LHS.isUsable() || !RHS.isUsable()) + return ExprError(); + + if (getLangOpts().OpenCL) { + QualType LHSTy = LHSExpr->getType(); + QualType RHSTy = RHSExpr->getType(); + // OpenCLC v2.0 s6.13.11.1 allows atomic variables to be initialized by + // the ATOMIC_VAR_INIT macro. + if (LHSTy->isAtomicType() || RHSTy->isAtomicType()) { + SourceRange SR(LHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); + if (BO_Assign == Opc) + Diag(OpLoc, diag::err_opencl_atomic_init) << 0 << SR; + else + ResultTy = InvalidOperands(OpLoc, LHS, RHS); + return ExprError(); + } + + // OpenCL special types - image, sampler, pipe, and blocks are to be used + // only with a builtin functions and therefore should be disallowed here. + if (LHSTy->isImageType() || RHSTy->isImageType() || + LHSTy->isSamplerT() || RHSTy->isSamplerT() || + LHSTy->isPipeType() || RHSTy->isPipeType() || + LHSTy->isBlockPointerType() || RHSTy->isBlockPointerType()) { + ResultTy = InvalidOperands(OpLoc, LHS, RHS); + return ExprError(); + } + } + + checkTypeSupport(LHSExpr->getType(), OpLoc, /*ValueDecl*/ nullptr); + checkTypeSupport(RHSExpr->getType(), OpLoc, /*ValueDecl*/ nullptr); + + switch (Opc) { + case BO_Assign: + ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, QualType(), Opc); + if (getLangOpts().CPlusPlus && + LHS.get()->getObjectKind() != OK_ObjCProperty) { + VK = LHS.get()->getValueKind(); + OK = LHS.get()->getObjectKind(); + } + if (!ResultTy.isNull()) { + DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc, true); + DiagnoseSelfMove(LHS.get(), RHS.get(), OpLoc); + + // Avoid copying a block to the heap if the block is assigned to a local + // auto variable that is declared in the same scope as the block. This + // optimization is unsafe if the local variable is declared in an outer + // scope. For example: + // + // BlockTy b; + // { + // b = ^{...}; + // } + // // It is unsafe to invoke the block here if it wasn't copied to the + // // heap. + // b(); + + if (auto *BE = dyn_cast(RHS.get()->IgnoreParens())) + if (auto *DRE = dyn_cast(LHS.get()->IgnoreParens())) + if (auto *VD = dyn_cast(DRE->getDecl())) + if (VD->hasLocalStorage() && getCurScope()->isDeclScope(VD)) + BE->getBlockDecl()->setCanAvoidCopyToHeap(); + + if (LHS.get()->getType().hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnion(LHS.get()->getType(), LHS.get()->getExprLoc(), + NTCUC_Assignment, NTCUK_Copy); + } + RecordModifiableNonNullParam(*this, LHS.get()); + break; + case BO_PtrMemD: + case BO_PtrMemI: + ResultTy = CheckPointerToMemberOperands(LHS, RHS, VK, OpLoc, + Opc == BO_PtrMemI); + break; + case BO_Mul: + case BO_Div: + ConvertHalfVec = true; + ResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, false, + Opc == BO_Div); + break; + case BO_Rem: + ResultTy = CheckRemainderOperands(LHS, RHS, OpLoc); + break; + case BO_Add: + ConvertHalfVec = true; + ResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_Sub: + ConvertHalfVec = true; + /* TO_UPSTREAM(BoundsSafety) ON*/ + ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, Opc); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + break; + case BO_Shl: + case BO_Shr: + ResultTy = CheckShiftOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_LE: + case BO_LT: + case BO_GE: + case BO_GT: + ConvertHalfVec = true; + ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); + + if (const auto *BI = dyn_cast(LHSExpr); + BI && BI->isComparisonOp()) + Diag(OpLoc, diag::warn_consecutive_comparison) + << BI->getOpcodeStr() << BinaryOperator::getOpcodeStr(Opc); + + break; + case BO_EQ: + case BO_NE: + ConvertHalfVec = true; + ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_Cmp: + ConvertHalfVec = true; + ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); + assert(ResultTy.isNull() || ResultTy->getAsCXXRecordDecl()); + break; + case BO_And: + checkObjCPointerIntrospection(*this, LHS, RHS, OpLoc); + [[fallthrough]]; + case BO_Xor: + case BO_Or: + ResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_LAnd: + case BO_LOr: + ConvertHalfVec = true; + ResultTy = CheckLogicalOperands(LHS, RHS, OpLoc, Opc); + break; + case BO_MulAssign: + case BO_DivAssign: + ConvertHalfVec = true; + CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, true, + Opc == BO_DivAssign); + CompLHSTy = CompResultTy; + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_RemAssign: + CompResultTy = CheckRemainderOperands(LHS, RHS, OpLoc, true); + CompLHSTy = CompResultTy; + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_AddAssign: + ConvertHalfVec = true; + CompResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy); + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_SubAssign: + ConvertHalfVec = true; + /* TO_UPSTREAM(BoundsSafety) ON*/ + CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy); + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_ShlAssign: + case BO_ShrAssign: + CompResultTy = CheckShiftOperands(LHS, RHS, OpLoc, Opc, true); + CompLHSTy = CompResultTy; + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_AndAssign: + case BO_OrAssign: // fallthrough + DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc, true); + [[fallthrough]]; + case BO_XorAssign: + CompResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); + CompLHSTy = CompResultTy; + if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) + ResultTy = + CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); + break; + case BO_Comma: + ResultTy = CheckCommaOperands(*this, LHS, RHS, OpLoc); + if (getLangOpts().CPlusPlus && !RHS.isInvalid()) { + VK = RHS.get()->getValueKind(); + OK = RHS.get()->getObjectKind(); + } + break; + } + if (ResultTy.isNull() || LHS.isInvalid() || RHS.isInvalid()) + return ExprError(); + + // Some of the binary operations require promoting operands of half vector to + // float vectors and truncating the result back to half vector. For now, we do + // this only when HalfArgsAndReturn is set (that is, when the target is arm or + // arm64). + assert( + (Opc == BO_Comma || isVector(RHS.get()->getType(), Context.HalfTy) == + isVector(LHS.get()->getType(), Context.HalfTy)) && + "both sides are half vectors or neither sides are"); + ConvertHalfVec = + needsConversionOfHalfVec(ConvertHalfVec, Context, LHS.get(), RHS.get()); + + // Check for array bounds violations for both sides of the BinaryOperator + CheckArrayAccess(LHS.get()); + CheckArrayAccess(RHS.get()); + + if (const ObjCIsaExpr *OISA = dyn_cast(LHS.get()->IgnoreParenCasts())) { + NamedDecl *ObjectSetClass = LookupSingleName(TUScope, + &Context.Idents.get("object_setClass"), + SourceLocation(), LookupOrdinaryName); + if (ObjectSetClass && isa(LHS.get())) { + SourceLocation RHSLocEnd = getLocForEndOfToken(RHS.get()->getEndLoc()); + Diag(LHS.get()->getExprLoc(), diag::warn_objc_isa_assign) + << FixItHint::CreateInsertion(LHS.get()->getBeginLoc(), + "object_setClass(") + << FixItHint::CreateReplacement(SourceRange(OISA->getOpLoc(), OpLoc), + ",") + << FixItHint::CreateInsertion(RHSLocEnd, ")"); + } + else + Diag(LHS.get()->getExprLoc(), diag::warn_objc_isa_assign); + } + else if (const ObjCIvarRefExpr *OIRE = + dyn_cast(LHS.get()->IgnoreParenCasts())) + DiagnoseDirectIsaAccess(*this, OIRE, OpLoc, RHS.get()); + + // Opc is not a compound assignment if CompResultTy is null. + if (CompResultTy.isNull()) { + if (ConvertHalfVec) + return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, false, + OpLoc, CurFPFeatureOverrides()); + return BinaryOperator::Create(Context, LHS.get(), RHS.get(), Opc, ResultTy, + VK, OK, OpLoc, CurFPFeatureOverrides()); + } + + // Handle compound assignments. + if (getLangOpts().CPlusPlus && LHS.get()->getObjectKind() != + OK_ObjCProperty) { + VK = VK_LValue; + OK = LHS.get()->getObjectKind(); + } + + // The LHS is not converted to the result type for fixed-point compound + // assignment as the common type is computed on demand. Reset the CompLHSTy + // to the LHS type we would have gotten after unary conversions. + if (CompResultTy->isFixedPointType()) + CompLHSTy = UsualUnaryConversions(LHS.get()).get()->getType(); + + if (ConvertHalfVec) + return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, true, + OpLoc, CurFPFeatureOverrides()); + + return CompoundAssignOperator::Create( + Context, LHS.get(), RHS.get(), Opc, ResultTy, VK, OK, OpLoc, + CurFPFeatureOverrides(), CompLHSTy, CompResultTy); +} + +/// DiagnoseBitwisePrecedence - Emit a warning when bitwise and comparison +/// operators are mixed in a way that suggests that the programmer forgot that +/// comparison operators have higher precedence. The most typical example of +/// such code is "flags & 0x0020 != 0", which is equivalent to "flags & 1". +static void DiagnoseBitwisePrecedence(Sema &Self, BinaryOperatorKind Opc, + SourceLocation OpLoc, Expr *LHSExpr, + Expr *RHSExpr) { + BinaryOperator *LHSBO = dyn_cast(LHSExpr); + BinaryOperator *RHSBO = dyn_cast(RHSExpr); + + // Check that one of the sides is a comparison operator and the other isn't. + bool isLeftComp = LHSBO && LHSBO->isComparisonOp(); + bool isRightComp = RHSBO && RHSBO->isComparisonOp(); + if (isLeftComp == isRightComp) + return; + + // Bitwise operations are sometimes used as eager logical ops. + // Don't diagnose this. + bool isLeftBitwise = LHSBO && LHSBO->isBitwiseOp(); + bool isRightBitwise = RHSBO && RHSBO->isBitwiseOp(); + if (isLeftBitwise || isRightBitwise) + return; + + SourceRange DiagRange = isLeftComp + ? SourceRange(LHSExpr->getBeginLoc(), OpLoc) + : SourceRange(OpLoc, RHSExpr->getEndLoc()); + StringRef OpStr = isLeftComp ? LHSBO->getOpcodeStr() : RHSBO->getOpcodeStr(); + SourceRange ParensRange = + isLeftComp + ? SourceRange(LHSBO->getRHS()->getBeginLoc(), RHSExpr->getEndLoc()) + : SourceRange(LHSExpr->getBeginLoc(), RHSBO->getLHS()->getEndLoc()); + + Self.Diag(OpLoc, diag::warn_precedence_bitwise_rel) + << DiagRange << BinaryOperator::getOpcodeStr(Opc) << OpStr; + SuggestParentheses(Self, OpLoc, + Self.PDiag(diag::note_precedence_silence) << OpStr, + (isLeftComp ? LHSExpr : RHSExpr)->getSourceRange()); + SuggestParentheses(Self, OpLoc, + Self.PDiag(diag::note_precedence_bitwise_first) + << BinaryOperator::getOpcodeStr(Opc), + ParensRange); +} + +/// It accepts a '&&' expr that is inside a '||' one. +/// Emit a diagnostic together with a fixit hint that wraps the '&&' expression +/// in parentheses. +static void +EmitDiagnosticForLogicalAndInLogicalOr(Sema &Self, SourceLocation OpLoc, + BinaryOperator *Bop) { + assert(Bop->getOpcode() == BO_LAnd); + Self.Diag(Bop->getOperatorLoc(), diag::warn_logical_and_in_logical_or) + << Bop->getSourceRange() << OpLoc; + SuggestParentheses(Self, Bop->getOperatorLoc(), + Self.PDiag(diag::note_precedence_silence) + << Bop->getOpcodeStr(), + Bop->getSourceRange()); +} + +/// Look for '&&' in the left hand of a '||' expr. +static void DiagnoseLogicalAndInLogicalOrLHS(Sema &S, SourceLocation OpLoc, + Expr *LHSExpr, Expr *RHSExpr) { + if (BinaryOperator *Bop = dyn_cast(LHSExpr)) { + if (Bop->getOpcode() == BO_LAnd) { + // If it's "string_literal && a || b" don't warn since the precedence + // doesn't matter. + if (!isa(Bop->getLHS()->IgnoreParenImpCasts())) + return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, Bop); + } else if (Bop->getOpcode() == BO_LOr) { + if (BinaryOperator *RBop = dyn_cast(Bop->getRHS())) { + // If it's "a || b && string_literal || c" we didn't warn earlier for + // "a || b && string_literal", but warn now. + if (RBop->getOpcode() == BO_LAnd && + isa(RBop->getRHS()->IgnoreParenImpCasts())) + return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, RBop); + } + } + } +} + +/// Look for '&&' in the right hand of a '||' expr. +static void DiagnoseLogicalAndInLogicalOrRHS(Sema &S, SourceLocation OpLoc, + Expr *LHSExpr, Expr *RHSExpr) { + if (BinaryOperator *Bop = dyn_cast(RHSExpr)) { + if (Bop->getOpcode() == BO_LAnd) { + // If it's "a || b && string_literal" don't warn since the precedence + // doesn't matter. + if (!isa(Bop->getRHS()->IgnoreParenImpCasts())) + return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, Bop); + } + } +} + +/// Look for bitwise op in the left or right hand of a bitwise op with +/// lower precedence and emit a diagnostic together with a fixit hint that wraps +/// the '&' expression in parentheses. +static void DiagnoseBitwiseOpInBitwiseOp(Sema &S, BinaryOperatorKind Opc, + SourceLocation OpLoc, Expr *SubExpr) { + if (BinaryOperator *Bop = dyn_cast(SubExpr)) { + if (Bop->isBitwiseOp() && Bop->getOpcode() < Opc) { + S.Diag(Bop->getOperatorLoc(), diag::warn_bitwise_op_in_bitwise_op) + << Bop->getOpcodeStr() << BinaryOperator::getOpcodeStr(Opc) + << Bop->getSourceRange() << OpLoc; + SuggestParentheses(S, Bop->getOperatorLoc(), + S.PDiag(diag::note_precedence_silence) + << Bop->getOpcodeStr(), + Bop->getSourceRange()); + } + } +} + +static void DiagnoseAdditionInShift(Sema &S, SourceLocation OpLoc, + Expr *SubExpr, StringRef Shift) { + if (BinaryOperator *Bop = dyn_cast(SubExpr)) { + if (Bop->getOpcode() == BO_Add || Bop->getOpcode() == BO_Sub) { + StringRef Op = Bop->getOpcodeStr(); + S.Diag(Bop->getOperatorLoc(), diag::warn_addition_in_bitshift) + << Bop->getSourceRange() << OpLoc << Shift << Op; + SuggestParentheses(S, Bop->getOperatorLoc(), + S.PDiag(diag::note_precedence_silence) << Op, + Bop->getSourceRange()); + } + } +} + +static void DiagnoseShiftCompare(Sema &S, SourceLocation OpLoc, + Expr *LHSExpr, Expr *RHSExpr) { + CXXOperatorCallExpr *OCE = dyn_cast(LHSExpr); + if (!OCE) + return; + + FunctionDecl *FD = OCE->getDirectCallee(); + if (!FD || !FD->isOverloadedOperator()) + return; + + OverloadedOperatorKind Kind = FD->getOverloadedOperator(); + if (Kind != OO_LessLess && Kind != OO_GreaterGreater) + return; + + S.Diag(OpLoc, diag::warn_overloaded_shift_in_comparison) + << LHSExpr->getSourceRange() << RHSExpr->getSourceRange() + << (Kind == OO_LessLess); + SuggestParentheses(S, OCE->getOperatorLoc(), + S.PDiag(diag::note_precedence_silence) + << (Kind == OO_LessLess ? "<<" : ">>"), + OCE->getSourceRange()); + SuggestParentheses( + S, OpLoc, S.PDiag(diag::note_evaluate_comparison_first), + SourceRange(OCE->getArg(1)->getBeginLoc(), RHSExpr->getEndLoc())); +} + +/// DiagnoseBinOpPrecedence - Emit warnings for expressions with tricky +/// precedence. +static void DiagnoseBinOpPrecedence(Sema &Self, BinaryOperatorKind Opc, + SourceLocation OpLoc, Expr *LHSExpr, + Expr *RHSExpr){ + // Diagnose "arg1 'bitwise' arg2 'eq' arg3". + if (BinaryOperator::isBitwiseOp(Opc)) + DiagnoseBitwisePrecedence(Self, Opc, OpLoc, LHSExpr, RHSExpr); + + // Diagnose "arg1 & arg2 | arg3" + if ((Opc == BO_Or || Opc == BO_Xor) && + !OpLoc.isMacroID()/* Don't warn in macros. */) { + DiagnoseBitwiseOpInBitwiseOp(Self, Opc, OpLoc, LHSExpr); + DiagnoseBitwiseOpInBitwiseOp(Self, Opc, OpLoc, RHSExpr); + } + + // Warn about arg1 || arg2 && arg3, as GCC 4.3+ does. + // We don't warn for 'assert(a || b && "bad")' since this is safe. + if (Opc == BO_LOr && !OpLoc.isMacroID()/* Don't warn in macros. */) { + DiagnoseLogicalAndInLogicalOrLHS(Self, OpLoc, LHSExpr, RHSExpr); + DiagnoseLogicalAndInLogicalOrRHS(Self, OpLoc, LHSExpr, RHSExpr); + } + + if ((Opc == BO_Shl && LHSExpr->getType()->isIntegralType(Self.getASTContext())) + || Opc == BO_Shr) { + StringRef Shift = BinaryOperator::getOpcodeStr(Opc); + DiagnoseAdditionInShift(Self, OpLoc, LHSExpr, Shift); + DiagnoseAdditionInShift(Self, OpLoc, RHSExpr, Shift); + } + + // Warn on overloaded shift operators and comparisons, such as: + // cout << 5 == 4; + if (BinaryOperator::isComparisonOp(Opc)) + DiagnoseShiftCompare(Self, OpLoc, LHSExpr, RHSExpr); +} + +ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, + tok::TokenKind Kind, + Expr *LHSExpr, Expr *RHSExpr) { + BinaryOperatorKind Opc = ConvertTokenKindToBinaryOpcode(Kind); + assert(LHSExpr && "ActOnBinOp(): missing left expression"); + assert(RHSExpr && "ActOnBinOp(): missing right expression"); + + // Emit warnings for tricky precedence issues, e.g. "bitfield & 0x4 == 0" + DiagnoseBinOpPrecedence(*this, Opc, TokLoc, LHSExpr, RHSExpr); + + BuiltinCountedByRefKind K = + BinaryOperator::isAssignmentOp(Opc) ? AssignmentKind : BinaryExprKind; + + CheckInvalidBuiltinCountedByRef(LHSExpr, K); + CheckInvalidBuiltinCountedByRef(RHSExpr, K); + + return BuildBinOp(S, TokLoc, Opc, LHSExpr, RHSExpr); +} + +void Sema::LookupBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc, + UnresolvedSetImpl &Functions) { + OverloadedOperatorKind OverOp = BinaryOperator::getOverloadedOperator(Opc); + if (OverOp != OO_None && OverOp != OO_Equal) + LookupOverloadedOperatorName(OverOp, S, Functions); + + // In C++20 onwards, we may have a second operator to look up. + if (getLangOpts().CPlusPlus20) { + if (OverloadedOperatorKind ExtraOp = getRewrittenOverloadedOperator(OverOp)) + LookupOverloadedOperatorName(ExtraOp, S, Functions); + } +} + +/// Build an overloaded binary operator expression in the given scope. +static ExprResult BuildOverloadedBinOp(Sema &S, Scope *Sc, SourceLocation OpLoc, + BinaryOperatorKind Opc, + Expr *LHS, Expr *RHS) { + switch (Opc) { + case BO_Assign: + // In the non-overloaded case, we warn about self-assignment (x = x) for + // both simple assignment and certain compound assignments where algebra + // tells us the operation yields a constant result. When the operator is + // overloaded, we can't do the latter because we don't want to assume that + // those algebraic identities still apply; for example, a path-building + // library might use operator/= to append paths. But it's still reasonable + // to assume that simple assignment is just moving/copying values around + // and so self-assignment is likely a bug. + DiagnoseSelfAssignment(S, LHS, RHS, OpLoc, false); + [[fallthrough]]; + case BO_DivAssign: + case BO_RemAssign: + case BO_SubAssign: + case BO_AndAssign: + case BO_OrAssign: + case BO_XorAssign: + CheckIdentityFieldAssignment(LHS, RHS, OpLoc, S); + break; + default: + break; + } + + // Find all of the overloaded operators visible from this point. + UnresolvedSet<16> Functions; + S.LookupBinOp(Sc, OpLoc, Opc, Functions); + + // Build the (potentially-overloaded, potentially-dependent) + // binary operation. + return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS); +} + +ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, + BinaryOperatorKind Opc, + Expr *LHSExpr, Expr *RHSExpr) { + ExprResult LHS, RHS; + std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr); + if (!LHS.isUsable() || !RHS.isUsable()) + return ExprError(); + LHSExpr = LHS.get(); + RHSExpr = RHS.get(); + + // We want to end up calling one of SemaPseudoObject::checkAssignment + // (if the LHS is a pseudo-object), BuildOverloadedBinOp (if + // both expressions are overloadable or either is type-dependent), + // or CreateBuiltinBinOp (in any other case). We also want to get + // any placeholder types out of the way. + + // Handle pseudo-objects in the LHS. + if (const BuiltinType *pty = LHSExpr->getType()->getAsPlaceholderType()) { + // Assignments with a pseudo-object l-value need special analysis. + if (pty->getKind() == BuiltinType::PseudoObject && + BinaryOperator::isAssignmentOp(Opc)) + return PseudoObject().checkAssignment(S, OpLoc, Opc, LHSExpr, RHSExpr); + + // Don't resolve overloads if the other type is overloadable. + if (getLangOpts().CPlusPlus && pty->getKind() == BuiltinType::Overload) { + // We can't actually test that if we still have a placeholder, + // though. Fortunately, none of the exceptions we see in that + // code below are valid when the LHS is an overload set. Note + // that an overload set can be dependently-typed, but it never + // instantiates to having an overloadable type. + ExprResult resolvedRHS = CheckPlaceholderExpr(RHSExpr); + if (resolvedRHS.isInvalid()) return ExprError(); + RHSExpr = resolvedRHS.get(); + + if (RHSExpr->isTypeDependent() || + RHSExpr->getType()->isOverloadableType()) + return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + } + + // If we're instantiating "a.x < b" or "A::x < b" and 'x' names a function + // template, diagnose the missing 'template' keyword instead of diagnosing + // an invalid use of a bound member function. + // + // Note that "A::x < b" might be valid if 'b' has an overloadable type due + // to C++1z [over.over]/1.4, but we already checked for that case above. + if (Opc == BO_LT && inTemplateInstantiation() && + (pty->getKind() == BuiltinType::BoundMember || + pty->getKind() == BuiltinType::Overload)) { + auto *OE = dyn_cast(LHSExpr); + if (OE && !OE->hasTemplateKeyword() && !OE->hasExplicitTemplateArgs() && + llvm::any_of(OE->decls(), [](NamedDecl *ND) { + return isa(ND); + })) { + Diag(OE->getQualifier() ? OE->getQualifierLoc().getBeginLoc() + : OE->getNameLoc(), + diag::err_template_kw_missing) + << OE->getName().getAsIdentifierInfo(); + return ExprError(); + } + } + + ExprResult LHS = CheckPlaceholderExpr(LHSExpr); + if (LHS.isInvalid()) return ExprError(); + LHSExpr = LHS.get(); + } + + // Handle pseudo-objects in the RHS. + if (const BuiltinType *pty = RHSExpr->getType()->getAsPlaceholderType()) { + // An overload in the RHS can potentially be resolved by the type + // being assigned to. + if (Opc == BO_Assign && pty->getKind() == BuiltinType::Overload) { + if (getLangOpts().CPlusPlus && + (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent() || + LHSExpr->getType()->isOverloadableType())) + return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + + return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr); + } + + // Don't resolve overloads if the other type is overloadable. + if (getLangOpts().CPlusPlus && pty->getKind() == BuiltinType::Overload && + LHSExpr->getType()->isOverloadableType()) + return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + + ExprResult resolvedRHS = CheckPlaceholderExpr(RHSExpr); + if (!resolvedRHS.isUsable()) return ExprError(); + RHSExpr = resolvedRHS.get(); + } + + if (getLangOpts().CPlusPlus) { + // Otherwise, build an overloaded op if either expression is type-dependent + // or has an overloadable type. + if (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent() || + LHSExpr->getType()->isOverloadableType() || + RHSExpr->getType()->isOverloadableType()) + return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + } + + if (getLangOpts().RecoveryAST && + (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent())) { + assert(!getLangOpts().CPlusPlus); + assert((LHSExpr->containsErrors() || RHSExpr->containsErrors()) && + "Should only occur in error-recovery path."); + if (BinaryOperator::isCompoundAssignmentOp(Opc)) + // C [6.15.16] p3: + // An assignment expression has the value of the left operand after the + // assignment, but is not an lvalue. + return CompoundAssignOperator::Create( + Context, LHSExpr, RHSExpr, Opc, + LHSExpr->getType().getUnqualifiedType(), VK_PRValue, OK_Ordinary, + OpLoc, CurFPFeatureOverrides()); + QualType ResultType; + switch (Opc) { + case BO_Assign: + ResultType = LHSExpr->getType().getUnqualifiedType(); + break; + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: + case BO_EQ: + case BO_NE: + case BO_LAnd: + case BO_LOr: + // These operators have a fixed result type regardless of operands. + ResultType = Context.IntTy; + break; + case BO_Comma: + ResultType = RHSExpr->getType(); + break; + default: + ResultType = Context.DependentTy; + break; + } + return BinaryOperator::Create(Context, LHSExpr, RHSExpr, Opc, ResultType, + VK_PRValue, OK_Ordinary, OpLoc, + CurFPFeatureOverrides()); + } + + // Build a built-in binary operation. + return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr); +} + +static bool isOverflowingIntegerType(ASTContext &Ctx, QualType T) { + if (T.isNull() || T->isDependentType()) + return false; + + if (!Ctx.isPromotableIntegerType(T)) + return true; + + return Ctx.getIntWidth(T) >= Ctx.getIntWidth(Ctx.IntTy); +} + +ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, + UnaryOperatorKind Opc, Expr *InputExpr, + bool IsAfterAmp) { + ExprResult Input = InputExpr; + ExprValueKind VK = VK_PRValue; + ExprObjectKind OK = OK_Ordinary; + QualType resultType; + bool CanOverflow = false; + + bool ConvertHalfVec = false; + if (getLangOpts().OpenCL) { + QualType Ty = InputExpr->getType(); + // The only legal unary operation for atomics is '&'. + if ((Opc != UO_AddrOf && Ty->isAtomicType()) || + // OpenCL special types - image, sampler, pipe, and blocks are to be used + // only with a builtin functions and therefore should be disallowed here. + (Ty->isImageType() || Ty->isSamplerT() || Ty->isPipeType() + || Ty->isBlockPointerType())) { + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << InputExpr->getType() + << Input.get()->getSourceRange()); + } + } + + if (getLangOpts().HLSL && OpLoc.isValid()) { + if (Opc == UO_AddrOf) + return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 0); + if (Opc == UO_Deref) + return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 1); + } + + if (InputExpr->isTypeDependent() && + InputExpr->getType()->isSpecificBuiltinType(BuiltinType::Dependent)) { + resultType = Context.DependentTy; + } else { + switch (Opc) { + case UO_PreInc: + case UO_PreDec: + case UO_PostInc: + case UO_PostDec: + resultType = + CheckIncrementDecrementOperand(*this, Input.get(), VK, OK, OpLoc, + Opc == UO_PreInc || Opc == UO_PostInc, + Opc == UO_PreInc || Opc == UO_PreDec); + CanOverflow = isOverflowingIntegerType(Context, resultType); + break; + case UO_AddrOf: + resultType = CheckAddressOfOperand(Input, OpLoc); + CheckAddressOfNoDeref(InputExpr); + RecordModifiableNonNullParam(*this, InputExpr); + break; + case UO_Deref: { + Input = DefaultFunctionArrayLvalueConversion(Input.get()); + if (Input.isInvalid()) + return ExprError(); + resultType = + CheckIndirectionOperand(*this, Input.get(), VK, OpLoc, IsAfterAmp); + break; + } + case UO_Plus: + case UO_Minus: + CanOverflow = Opc == UO_Minus && + isOverflowingIntegerType(Context, Input.get()->getType()); + Input = UsualUnaryConversions(Input.get()); + if (Input.isInvalid()) + return ExprError(); + // Unary plus and minus require promoting an operand of half vector to a + // float vector and truncating the result back to a half vector. For now, + // we do this only when HalfArgsAndReturns is set (that is, when the + // target is arm or arm64). + ConvertHalfVec = needsConversionOfHalfVec(true, Context, Input.get()); - Expr::LValueClassification lval = op->ClassifyLValue(Context); - unsigned AddressOfError = AO_No_Error; + // If the operand is a half vector, promote it to a float vector. + if (ConvertHalfVec) + Input = convertVector(Input.get(), Context.FloatTy, *this); + resultType = Input.get()->getType(); + if (resultType->isArithmeticType()) // C99 6.5.3.3p1 + break; + else if (resultType->isVectorType() && + // The z vector extensions don't allow + or - with bool vectors. + (!Context.getLangOpts().ZVector || + resultType->castAs()->getVectorKind() != + VectorKind::AltiVecBool)) + break; + else if (resultType->isSveVLSBuiltinType()) // SVE vectors allow + and - + break; + else if (getLangOpts().CPlusPlus && // C++ [expr.unary.op]p6 + Opc == UO_Plus && resultType->isPointerType()) + break; - if (lval == Expr::LV_ClassTemporary || lval == Expr::LV_ArrayTemporary) { - bool sfinae = (bool)isSFINAEContext(); - Diag(OpLoc, isSFINAEContext() ? diag::err_typecheck_addrof_temporary - : diag::ext_typecheck_addrof_temporary) - << op->getType() << op->getSourceRange(); - if (sfinae) - return QualType(); - // Materialize the temporary as an lvalue so that we can take its address. - OrigOp = op = - CreateMaterializeTemporaryExpr(op->getType(), OrigOp.get(), true); - } else if (isa(op)) { - return Context.getPointerType(op->getType()); - } else if (lval == Expr::LV_MemberFunction) { - // If it's an instance method, make a member pointer. - // The expression must have exactly the form &A::foo. + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); - // If the underlying expression isn't a decl ref, give up. - if (!isa(op)) { - Diag(OpLoc, diag::err_invalid_form_pointer_member_function) - << OrigOp.get()->getSourceRange(); - return QualType(); + case UO_Not: // bitwise complement + Input = UsualUnaryConversions(Input.get()); + if (Input.isInvalid()) + return ExprError(); + resultType = Input.get()->getType(); + // C99 6.5.3.3p1. We allow complex int and float as a GCC extension. + if (resultType->isComplexType() || resultType->isComplexIntegerType()) + // C99 does not support '~' for complex conjugation. + Diag(OpLoc, diag::ext_integer_complement_complex) + << resultType << Input.get()->getSourceRange(); + else if (resultType->hasIntegerRepresentation()) + break; + else if (resultType->isExtVectorType() && Context.getLangOpts().OpenCL) { + // OpenCL v1.1 s6.3.f: The bitwise operator not (~) does not operate + // on vector float types. + QualType T = resultType->castAs()->getElementType(); + if (!T->isIntegerType()) + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } else { + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + break; + + case UO_LNot: // logical negation + // Unlike +/-/~, integer promotions aren't done here (C99 6.5.3.3p5). + Input = DefaultFunctionArrayLvalueConversion(Input.get()); + if (Input.isInvalid()) + return ExprError(); + resultType = Input.get()->getType(); + + // Though we still have to promote half FP to float... + if (resultType->isHalfType() && !Context.getLangOpts().NativeHalfType) { + Input = ImpCastExprToType(Input.get(), Context.FloatTy, CK_FloatingCast) + .get(); + resultType = Context.FloatTy; + } + + // WebAsembly tables can't be used in unary expressions. + if (resultType->isPointerType() && + resultType->getPointeeType().isWebAssemblyReferenceType()) { + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + + if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) { + // C99 6.5.3.3p1: ok, fallthrough; + if (Context.getLangOpts().CPlusPlus) { + // C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9: + // operand contextually converted to bool. + Input = ImpCastExprToType(Input.get(), Context.BoolTy, + ScalarTypeToBooleanCastKind(resultType)); + } else if (Context.getLangOpts().OpenCL && + Context.getLangOpts().OpenCLVersion < 120) { + // OpenCL v1.1 6.3.h: The logical operator not (!) does not + // operate on scalar float types. + if (!resultType->isIntegerType() && !resultType->isPointerType()) + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + } else if (resultType->isExtVectorType()) { + if (Context.getLangOpts().OpenCL && + Context.getLangOpts().getOpenCLCompatibleVersion() < 120) { + // OpenCL v1.1 6.3.h: The logical operator not (!) does not + // operate on vector float types. + QualType T = resultType->castAs()->getElementType(); + if (!T->isIntegerType()) + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + // Vector logical not returns the signed variant of the operand type. + resultType = GetSignedVectorType(resultType); + break; + } else if (Context.getLangOpts().CPlusPlus && + resultType->isVectorType()) { + const VectorType *VTy = resultType->castAs(); + if (VTy->getVectorKind() != VectorKind::Generic) + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + + // Vector logical not returns the signed variant of the operand type. + resultType = GetSignedVectorType(resultType); + break; + } else { + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + + // LNot always has type int. C99 6.5.3.3p5. + // In C++, it's bool. C++ 5.3.1p8 + resultType = Context.getLogicalOperationType(); + break; + case UO_Real: + case UO_Imag: + resultType = CheckRealImagOperand(*this, Input, OpLoc, Opc == UO_Real); + // _Real maps ordinary l-values into ordinary l-values. _Imag maps + // ordinary complex l-values to ordinary l-values and all other values to + // r-values. + if (Input.isInvalid()) + return ExprError(); + if (Opc == UO_Real || Input.get()->getType()->isAnyComplexType()) { + if (Input.get()->isGLValue() && + Input.get()->getObjectKind() == OK_Ordinary) + VK = Input.get()->getValueKind(); + } else if (!getLangOpts().CPlusPlus) { + // In C, a volatile scalar is read by __imag. In C++, it is not. + Input = DefaultLvalueConversion(Input.get()); + } + break; + case UO_Extension: + resultType = Input.get()->getType(); + VK = Input.get()->getValueKind(); + OK = Input.get()->getObjectKind(); + break; + case UO_Coawait: + // It's unnecessary to represent the pass-through operator co_await in the + // AST; just return the input expression instead. + assert(!Input.get()->getType()->isDependentType() && + "the co_await expression must be non-dependant before " + "building operator co_await"); + return Input; } - DeclRefExpr *DRE = cast(op); - CXXMethodDecl *MD = cast(DRE->getDecl()); + } + if (resultType.isNull() || Input.isInvalid()) + return ExprError(); - CheckUseOfCXXMethodAsAddressOfOperand(OpLoc, OrigOp.get(), MD); - QualType MPTy = Context.getMemberPointerType( - op->getType(), DRE->getQualifier(), MD->getParent()); + // Check for array bounds violations in the operand of the UnaryOperator, + // except for the '*' and '&' operators that have to be handled specially + // by CheckArrayAccess (as there are special cases like &array[arraysize] + // that are explicitly defined as valid by the standard). + if (Opc != UO_AddrOf && Opc != UO_Deref) + CheckArrayAccess(Input.get()); - if (getLangOpts().PointerAuthCalls && MD->isVirtual() && - !isUnevaluatedContext() && !MPTy->isDependentType()) { - // When pointer authentication is enabled, argument and return types of - // vitual member functions must be complete. This is because vitrual - // member function pointers are implemented using virtual dispatch - // thunks and the thunks cannot be emitted if the argument or return - // types are incomplete. - auto ReturnOrParamTypeIsIncomplete = [&](QualType T, - SourceLocation DeclRefLoc, - SourceLocation RetArgTypeLoc) { - if (RequireCompleteType(DeclRefLoc, T, diag::err_incomplete_type)) { - Diag(DeclRefLoc, - diag::note_ptrauth_virtual_function_pointer_incomplete_arg_ret); - Diag(RetArgTypeLoc, - diag::note_ptrauth_virtual_function_incomplete_arg_ret_type) - << T; - return true; + auto *UO = + UnaryOperator::Create(Context, Input.get(), Opc, resultType, VK, OK, + OpLoc, CanOverflow, CurFPFeatureOverrides()); + + if (Opc == UO_Deref && UO->getType()->hasAttr(attr::NoDeref) && + !isa(UO->getType().getDesugaredType(Context)) && + !isUnevaluatedContext()) + ExprEvalContexts.back().PossibleDerefs.insert(UO); + + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + FlexibleArrayMemberUtils FlexUtils(*this); + if (Opc == UO_AddrOf) { + // when taking the address of an lvalue with a flexible array member, + // return a promoted pointer to it + if (auto *RD = FlexUtils.GetFlexibleRecord(Input.get()->getType())) { + // Skipping the null check for the struct base because 'address of' is + // never null. + return PromoteBoundsSafetyPointerToFlexibleArrayMember( + *this, RD, UO, /*NullCheck*/ false); + } + } else if (Opc == UO_Deref) { + // when dereferencing a flexible array member struct, do a bounds check + // to verify that the number of elements fits the size of the pointer + auto PtrTy = Input.get()->getType()->getAs(); + if (PtrTy && PtrTy->getPointerAttributes().hasUpperBound()) { + if (auto *RD = FlexUtils.GetFlexibleRecord(PtrTy->getPointeeType())) { + // Check if this has been promoted from a __single pointer to flexible + // array member struct. If so, we skip the check since being __single + // should mean the count value is already valid. + bool SkipCheck = false; + auto InnerTy = Input.get()->IgnoreImpCasts()->getType(); + if (InnerTy->isSinglePointerType() && + !InnerTy->isBoundsAttributedType()) { + auto *InnerRD = + FlexUtils.GetFlexibleRecord(InnerTy->getPointeeType()); + if (InnerRD == RD) + SkipCheck = true; + } + + if (!SkipCheck) { + ExprResult Ckd = BoundsCheckBuilder::CheckFlexibleArrayMemberSize( + *this, UO->getBeginLoc(), + BoundsCheckKind::FlexibleArrayCountDeref, Input.get()); + if (!Ckd.get()) + return ExprError(); + UO->setSubExpr(Ckd.get()); + } } - return false; - }; - QualType RetTy = MD->getReturnType(); - bool IsIncomplete = - !RetTy->isVoidType() && - ReturnOrParamTypeIsIncomplete( - RetTy, OpLoc, MD->getReturnTypeSourceRange().getBegin()); - for (auto *PVD : MD->parameters()) - IsIncomplete |= ReturnOrParamTypeIsIncomplete(PVD->getType(), OpLoc, - PVD->getBeginLoc()); - if (IsIncomplete) - return QualType(); + } } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + + // Convert the result back to a half vector. + if (ConvertHalfVec) + return convertVector(UO, Context.HalfTy, *this); + return UO; +} + +bool Sema::isQualifiedMemberAccess(Expr *E) { + if (DeclRefExpr *DRE = dyn_cast(E)) { + if (!DRE->getQualifier()) + return false; + + ValueDecl *VD = DRE->getDecl(); + if (!VD->isCXXClassMember()) + return false; + + if (isa(VD) || isa(VD)) + return true; + if (CXXMethodDecl *Method = dyn_cast(VD)) + return Method->isImplicitObjectMemberFunction(); + + return false; + } - // Under the MS ABI, lock down the inheritance model now. - if (Context.getTargetInfo().getCXXABI().isMicrosoft()) - (void)isCompleteType(OpLoc, MPTy); - return MPTy; - } else if (lval != Expr::LV_Valid && lval != Expr::LV_IncompleteVoidType) { - // C99 6.5.3.2p1 - // The operand must be either an l-value or a function designator - if (!op->getType()->isFunctionType()) { - // Use a special diagnostic for loads from property references. - if (isa(op)) { - AddressOfError = AO_Property_Expansion; + if (UnresolvedLookupExpr *ULE = dyn_cast(E)) { + if (!ULE->getQualifier()) + return false; + + for (NamedDecl *D : ULE->decls()) { + if (CXXMethodDecl *Method = dyn_cast(D)) { + if (Method->isImplicitObjectMemberFunction()) + return true; } else { - Diag(OpLoc, diag::err_typecheck_invalid_lvalue_addrof) - << op->getType() << op->getSourceRange(); - return QualType(); + // Overload set does not contain methods. + break; } - } else if (const auto *DRE = dyn_cast(op)) { - if (const auto *MD = dyn_cast_or_null(DRE->getDecl())) - CheckUseOfCXXMethodAsAddressOfOperand(OpLoc, OrigOp.get(), MD); } - } else if (op->getObjectKind() == OK_BitField) { // C99 6.5.3.2p1 - // The operand cannot be a bit-field - AddressOfError = AO_Bit_Field; - } else if (op->getObjectKind() == OK_VectorComponent) { - // The operand cannot be an element of a vector - AddressOfError = AO_Vector_Element; - } else if (op->getObjectKind() == OK_MatrixComponent) { - // The operand cannot be an element of a matrix. - AddressOfError = AO_Matrix_Element; - } else if (dcl) { // C99 6.5.3.2p1 - // We have an lvalue with a decl. Make sure the decl is not declared - // with the register storage-class specifier. - if (const VarDecl *vd = dyn_cast(dcl)) { - // in C++ it is not error to take address of a register - // variable (c++03 7.1.1P3) - if (vd->getStorageClass() == SC_Register && - !getLangOpts().CPlusPlus) { - AddressOfError = AO_Register_Variable; - } - } else if (isa(dcl)) { - AddressOfError = AO_Property_Expansion; - } else if (isa(dcl)) { - return Context.OverloadTy; - } else if (isa(dcl) || isa(dcl)) { - // Okay: we can take the address of a field. - // Could be a pointer to member, though, if there is an explicit - // scope qualifier for the class. + return false; + } - // [C++26] [expr.prim.id.general] - // If an id-expression E denotes a non-static non-type member - // of some class C [...] and if E is a qualified-id, E is - // not the un-parenthesized operand of the unary & operator [...] - // the id-expression is transformed into a class member access expression. - if (auto *DRE = dyn_cast(op); - DRE && DRE->getQualifier() && !isa(OrigOp.get())) { - DeclContext *Ctx = dcl->getDeclContext(); - if (Ctx && Ctx->isRecord()) { - if (dcl->getType()->isReferenceType()) { - Diag(OpLoc, - diag::err_cannot_form_pointer_to_member_of_reference_type) - << dcl->getDeclName() << dcl->getType(); - return QualType(); - } + return false; +} - while (cast(Ctx)->isAnonymousStructOrUnion()) - Ctx = Ctx->getParent(); +ExprResult Sema::BuildUnaryOp(Scope *S, SourceLocation OpLoc, + UnaryOperatorKind Opc, Expr *Input, + bool IsAfterAmp) { + // First things first: handle placeholders so that the + // overloaded-operator check considers the right type. + if (const BuiltinType *pty = Input->getType()->getAsPlaceholderType()) { + // Increment and decrement of pseudo-object references. + if (pty->getKind() == BuiltinType::PseudoObject && + UnaryOperator::isIncrementDecrementOp(Opc)) + return PseudoObject().checkIncDec(S, OpLoc, Opc, Input); - QualType MPTy = Context.getMemberPointerType( - op->getType(), DRE->getQualifier(), cast(Ctx)); - // Under the MS ABI, lock down the inheritance model now. - if (Context.getTargetInfo().getCXXABI().isMicrosoft()) - (void)isCompleteType(OpLoc, MPTy); - return MPTy; - } - } - } else if (!isa(dcl)) - llvm_unreachable("Unknown/unexpected decl type"); - } + // extension is always a builtin operator. + if (Opc == UO_Extension) + return CreateBuiltinUnaryOp(OpLoc, Opc, Input); - if (AddressOfError != AO_No_Error) { - diagnoseAddressOfInvalidType(*this, OpLoc, op, AddressOfError); - return QualType(); - } + // & gets special logic for several kinds of placeholder. + // The builtin code knows what to do. + if (Opc == UO_AddrOf && + (pty->getKind() == BuiltinType::Overload || + pty->getKind() == BuiltinType::UnknownAny || + pty->getKind() == BuiltinType::BoundMember)) + return CreateBuiltinUnaryOp(OpLoc, Opc, Input); - if (lval == Expr::LV_IncompleteVoidType) { - // Taking the address of a void variable is technically illegal, but we - // allow it in cases which are otherwise valid. - // Example: "extern void x; void* y = &x;". - Diag(OpLoc, diag::ext_typecheck_addrof_void) << op->getSourceRange(); + // Anything else needs to be handled now. + ExprResult Result = CheckPlaceholderExpr(Input); + if (Result.isInvalid()) return ExprError(); + Input = Result.get(); } - // If the operand has type "type", the result has type "pointer to type". - if (op->getType()->isObjCObjectType()) - return Context.getObjCObjectPointerType(op->getType()); + if (getLangOpts().CPlusPlus && Input->getType()->isOverloadableType() && + UnaryOperator::getOverloadedOperator(Opc) != OO_None && + !(Opc == UO_AddrOf && isQualifiedMemberAccess(Input))) { + // Find all of the overloaded operators visible from this point. + UnresolvedSet<16> Functions; + OverloadedOperatorKind OverOp = UnaryOperator::getOverloadedOperator(Opc); + if (S && OverOp != OO_None) + LookupOverloadedOperatorName(OverOp, S, Functions); - // Cannot take the address of WebAssembly references or tables. - if (Context.getTargetInfo().getTriple().isWasm()) { - QualType OpTy = op->getType(); - if (OpTy.isWebAssemblyReferenceType()) { - Diag(OpLoc, diag::err_wasm_ca_reference) - << 1 << OrigOp.get()->getSourceRange(); - return QualType(); - } - if (OpTy->isWebAssemblyTableType()) { - Diag(OpLoc, diag::err_wasm_table_pr) - << 1 << OrigOp.get()->getSourceRange(); - return QualType(); - } + return CreateOverloadedUnaryOp(OpLoc, Opc, Functions, Input); } - CheckAddressOfPackedMember(op); + return CreateBuiltinUnaryOp(OpLoc, Opc, Input, IsAfterAmp); +} - return Context.getPointerType(op->getType()); +ExprResult Sema::ActOnUnaryOp(Scope *S, SourceLocation OpLoc, tok::TokenKind Op, + Expr *Input, bool IsAfterAmp) { + return BuildUnaryOp(S, OpLoc, ConvertTokenKindToUnaryOpcode(Op), Input, + IsAfterAmp); } -static void RecordModifiableNonNullParam(Sema &S, const Expr *Exp) { - const DeclRefExpr *DRE = dyn_cast(Exp); - if (!DRE) - return; - const Decl *D = DRE->getDecl(); - if (!D) - return; - const ParmVarDecl *Param = dyn_cast(D); - if (!Param) - return; - if (const FunctionDecl* FD = dyn_cast(Param->getDeclContext())) - if (!FD->hasAttr() && !Param->hasAttr()) - return; - if (FunctionScopeInfo *FD = S.getCurFunction()) - FD->ModifiedNonNullParams.insert(Param); +ExprResult Sema::ActOnAddrLabel(SourceLocation OpLoc, SourceLocation LabLoc, + LabelDecl *TheDecl) { + TheDecl->markUsed(Context); + // Create the AST node. The address of a label always has type 'void*'. + auto *Res = new (Context) AddrLabelExpr( + OpLoc, LabLoc, TheDecl, Context.getPointerType(Context.VoidTy)); + + if (getCurFunction()) + getCurFunction()->AddrLabels.push_back(Res); + + return Res; } -/// CheckIndirectionOperand - Type check unary indirection (prefix '*'). -static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK, - SourceLocation OpLoc, - bool IsAfterAmp = false) { - ExprResult ConvResult = S.UsualUnaryConversions(Op); - if (ConvResult.isInvalid()) - return QualType(); - Op = ConvResult.get(); - QualType OpTy = Op->getType(); - QualType Result; +void Sema::ActOnStartStmtExpr() { + PushExpressionEvaluationContext(ExprEvalContexts.back().Context); + // Make sure we diagnose jumping into a statement expression. + setFunctionHasBranchProtectedScope(); +} + +void Sema::ActOnStmtExprError() { + // Note that function is also called by TreeTransform when leaving a + // StmtExpr scope without rebuilding anything. + + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); +} + +ExprResult Sema::ActOnStmtExpr(Scope *S, SourceLocation LPLoc, Stmt *SubStmt, + SourceLocation RPLoc) { + return BuildStmtExpr(LPLoc, SubStmt, RPLoc, getTemplateDepth(S)); +} + +ExprResult Sema::BuildStmtExpr(SourceLocation LPLoc, Stmt *SubStmt, + SourceLocation RPLoc, unsigned TemplateDepth) { + assert(SubStmt && isa(SubStmt) && "Invalid action invocation!"); + CompoundStmt *Compound = cast(SubStmt); + + if (hasAnyUnrecoverableErrorsInThisFunction()) + DiscardCleanupsInEvaluationContext(); + assert(!Cleanup.exprNeedsCleanups() && + "cleanups within StmtExpr not correctly bound!"); + PopExpressionEvaluationContext(); + + // FIXME: there are a variety of strange constraints to enforce here, for + // example, it is not possible to goto into a stmt expression apparently. + // More semantic analysis is needed. + + // If there are sub-stmts in the compound stmt, take the type of the last one + // as the type of the stmtexpr. + QualType Ty = Context.VoidTy; + bool StmtExprMayBindToTemp = false; + if (!Compound->body_empty()) { + // For GCC compatibility we get the last Stmt excluding trailing NullStmts. + if (const auto *LastStmt = + dyn_cast(Compound->getStmtExprResult())) { + if (const Expr *Value = LastStmt->getExprStmt()) { + StmtExprMayBindToTemp = true; + Ty = Value->getType(); + } + } + } + + // FIXME: Check that expression type is complete/non-abstract; statement + // expressions are not lvalues. + Expr *ResStmtExpr = + new (Context) StmtExpr(Compound, Ty, LPLoc, RPLoc, TemplateDepth); + if (StmtExprMayBindToTemp) + return MaybeBindToTemporary(ResStmtExpr); + return ResStmtExpr; +} + +ExprResult Sema::ActOnStmtExprResult(ExprResult ER) { + if (ER.isInvalid()) + return ExprError(); + + // Do function/array conversion on the last expression, but not + // lvalue-to-rvalue. However, initialize an unqualified type. + ER = DefaultFunctionArrayConversion(ER.get()); + if (ER.isInvalid()) + return ExprError(); + Expr *E = ER.get(); - if (isa(Op)) { - QualType OpOrigType = Op->IgnoreParenCasts()->getType(); - S.CheckCompatibleReinterpretCast(OpOrigType, OpTy, /*IsDereference*/true, - Op->getSourceRange()); - } + if (E->isTypeDependent()) + return E; - if (const PointerType *PT = OpTy->getAs()) - { - Result = PT->getPointeeType(); - } - else if (const ObjCObjectPointerType *OPT = - OpTy->getAs()) - Result = OPT->getPointeeType(); - else { - ExprResult PR = S.CheckPlaceholderExpr(Op); - if (PR.isInvalid()) return QualType(); - if (PR.get() != Op) - return CheckIndirectionOperand(S, PR.get(), VK, OpLoc); - } + // In ARC, if the final expression ends in a consume, splice + // the consume out and bind it later. In the alternate case + // (when dealing with a retainable type), the result + // initialization will create a produce. In both cases the + // result will be +1, and we'll need to balance that out with + // a bind. + auto *Cast = dyn_cast(E); + if (Cast && Cast->getCastKind() == CK_ARCConsumeObject) + return Cast->getSubExpr(); - if (Result.isNull()) { - S.Diag(OpLoc, diag::err_typecheck_indirection_requires_pointer) - << OpTy << Op->getSourceRange(); - return QualType(); - } + // FIXME: Provide a better location for the initialization. + return PerformCopyInitialization( + InitializedEntity::InitializeStmtExprResult( + E->getBeginLoc(), E->getType().getAtomicUnqualifiedType()), + SourceLocation(), E); +} - if (Result->isVoidType()) { - // C++ [expr.unary.op]p1: - // [...] the expression to which [the unary * operator] is applied shall - // be a pointer to an object type, or a pointer to a function type - LangOptions LO = S.getLangOpts(); - if (LO.CPlusPlus) - S.Diag(OpLoc, diag::err_typecheck_indirection_through_void_pointer_cpp) - << OpTy << Op->getSourceRange(); - else if (!(LO.C99 && IsAfterAmp) && !S.isUnevaluatedContext()) - S.Diag(OpLoc, diag::ext_typecheck_indirection_through_void_pointer) - << OpTy << Op->getSourceRange(); - } +ExprResult Sema::BuildBuiltinOffsetOf(SourceLocation BuiltinLoc, + TypeSourceInfo *TInfo, + ArrayRef Components, + SourceLocation RParenLoc) { + QualType ArgTy = TInfo->getType(); + bool Dependent = ArgTy->isDependentType(); + SourceRange TypeRange = TInfo->getTypeLoc().getLocalSourceRange(); - // Dereferences are usually l-values... - VK = VK_LValue; + // We must have at least one component that refers to the type, and the first + // one is known to be a field designator. Verify that the ArgTy represents + // a struct/union/class. + if (!Dependent && !ArgTy->isRecordType()) + return ExprError(Diag(BuiltinLoc, diag::err_offsetof_record_type) + << ArgTy << TypeRange); - // ...except that certain expressions are never l-values in C. - if (!S.getLangOpts().CPlusPlus && Result.isCForbiddenLValueType()) - VK = VK_PRValue; + // Type must be complete per C99 7.17p3 because a declaring a variable + // with an incomplete type would be ill-formed. + if (!Dependent + && RequireCompleteType(BuiltinLoc, ArgTy, + diag::err_offsetof_incomplete_type, TypeRange)) + return ExprError(); - return Result; -} + bool DidWarnAboutNonPOD = false; + QualType CurrentType = ArgTy; + SmallVector Comps; + SmallVector Exprs; + for (const OffsetOfComponent &OC : Components) { + if (OC.isBrackets) { + // Offset of an array sub-field. TODO: Should we allow vector elements? + if (!CurrentType->isDependentType()) { + const ArrayType *AT = Context.getAsArrayType(CurrentType); + if(!AT) + return ExprError(Diag(OC.LocEnd, diag::err_offsetof_array_type) + << CurrentType); + CurrentType = AT->getElementType(); + } else + CurrentType = Context.DependentTy; -BinaryOperatorKind Sema::ConvertTokenKindToBinaryOpcode(tok::TokenKind Kind) { - BinaryOperatorKind Opc; - switch (Kind) { - default: llvm_unreachable("Unknown binop!"); - case tok::periodstar: Opc = BO_PtrMemD; break; - case tok::arrowstar: Opc = BO_PtrMemI; break; - case tok::star: Opc = BO_Mul; break; - case tok::slash: Opc = BO_Div; break; - case tok::percent: Opc = BO_Rem; break; - case tok::plus: Opc = BO_Add; break; - case tok::minus: Opc = BO_Sub; break; - case tok::lessless: Opc = BO_Shl; break; - case tok::greatergreater: Opc = BO_Shr; break; - case tok::lessequal: Opc = BO_LE; break; - case tok::less: Opc = BO_LT; break; - case tok::greaterequal: Opc = BO_GE; break; - case tok::greater: Opc = BO_GT; break; - case tok::exclaimequal: Opc = BO_NE; break; - case tok::equalequal: Opc = BO_EQ; break; - case tok::spaceship: Opc = BO_Cmp; break; - case tok::amp: Opc = BO_And; break; - case tok::caret: Opc = BO_Xor; break; - case tok::pipe: Opc = BO_Or; break; - case tok::ampamp: Opc = BO_LAnd; break; - case tok::pipepipe: Opc = BO_LOr; break; - case tok::equal: Opc = BO_Assign; break; - case tok::starequal: Opc = BO_MulAssign; break; - case tok::slashequal: Opc = BO_DivAssign; break; - case tok::percentequal: Opc = BO_RemAssign; break; - case tok::plusequal: Opc = BO_AddAssign; break; - case tok::minusequal: Opc = BO_SubAssign; break; - case tok::lesslessequal: Opc = BO_ShlAssign; break; - case tok::greatergreaterequal: Opc = BO_ShrAssign; break; - case tok::ampequal: Opc = BO_AndAssign; break; - case tok::caretequal: Opc = BO_XorAssign; break; - case tok::pipeequal: Opc = BO_OrAssign; break; - case tok::comma: Opc = BO_Comma; break; - } - return Opc; -} + ExprResult IdxRval = DefaultLvalueConversion(static_cast(OC.U.E)); + if (IdxRval.isInvalid()) + return ExprError(); + Expr *Idx = IdxRval.get(); -static inline UnaryOperatorKind ConvertTokenKindToUnaryOpcode( - tok::TokenKind Kind) { - UnaryOperatorKind Opc; - switch (Kind) { - default: llvm_unreachable("Unknown unary op!"); - case tok::plusplus: Opc = UO_PreInc; break; - case tok::minusminus: Opc = UO_PreDec; break; - case tok::amp: Opc = UO_AddrOf; break; - case tok::star: Opc = UO_Deref; break; - case tok::plus: Opc = UO_Plus; break; - case tok::minus: Opc = UO_Minus; break; - case tok::tilde: Opc = UO_Not; break; - case tok::exclaim: Opc = UO_LNot; break; - case tok::kw___real: Opc = UO_Real; break; - case tok::kw___imag: Opc = UO_Imag; break; - case tok::kw___extension__: Opc = UO_Extension; break; - } - return Opc; -} + // The expression must be an integral expression. + // FIXME: An integral constant expression? + if (!Idx->isTypeDependent() && !Idx->isValueDependent() && + !Idx->getType()->isIntegerType()) + return ExprError( + Diag(Idx->getBeginLoc(), diag::err_typecheck_subscript_not_integer) + << Idx->getSourceRange()); -const FieldDecl * -Sema::getSelfAssignmentClassMemberCandidate(const ValueDecl *SelfAssigned) { - // Explore the case for adding 'this->' to the LHS of a self assignment, very - // common for setters. - // struct A { - // int X; - // -void setX(int X) { X = X; } - // +void setX(int X) { this->X = X; } - // }; + // Record this array index. + Comps.push_back(OffsetOfNode(OC.LocStart, Exprs.size(), OC.LocEnd)); + Exprs.push_back(Idx); + continue; + } - // Only consider parameters for self assignment fixes. - if (!isa(SelfAssigned)) - return nullptr; - const auto *Method = - dyn_cast_or_null(getCurFunctionDecl(true)); - if (!Method) - return nullptr; + // Offset of a field. + if (CurrentType->isDependentType()) { + // We have the offset of a field, but we can't look into the dependent + // type. Just record the identifier of the field. + Comps.push_back(OffsetOfNode(OC.LocStart, OC.U.IdentInfo, OC.LocEnd)); + CurrentType = Context.DependentTy; + continue; + } - const CXXRecordDecl *Parent = Method->getParent(); - // In theory this is fixable if the lambda explicitly captures this, but - // that's added complexity that's rarely going to be used. - if (Parent->isLambda()) - return nullptr; + // We need to have a complete type to look into. + if (RequireCompleteType(OC.LocStart, CurrentType, + diag::err_offsetof_incomplete_type)) + return ExprError(); - // FIXME: Use an actual Lookup operation instead of just traversing fields - // in order to get base class fields. - auto Field = - llvm::find_if(Parent->fields(), - [Name(SelfAssigned->getDeclName())](const FieldDecl *F) { - return F->getDeclName() == Name; - }); - return (Field != Parent->field_end()) ? *Field : nullptr; -} + // Look for the designated field. + const RecordType *RC = CurrentType->getAs(); + if (!RC) + return ExprError(Diag(OC.LocEnd, diag::err_offsetof_record_type) + << CurrentType); + RecordDecl *RD = RC->getDecl(); -/// DiagnoseSelfAssignment - Emits a warning if a value is assigned to itself. -/// This warning suppressed in the event of macro expansions. -static void DiagnoseSelfAssignment(Sema &S, Expr *LHSExpr, Expr *RHSExpr, - SourceLocation OpLoc, bool IsBuiltin) { - if (S.inTemplateInstantiation()) - return; - if (S.isUnevaluatedContext()) - return; - if (OpLoc.isInvalid() || OpLoc.isMacroID()) - return; - LHSExpr = LHSExpr->IgnoreParenImpCasts(); - RHSExpr = RHSExpr->IgnoreParenImpCasts(); - const DeclRefExpr *LHSDeclRef = dyn_cast(LHSExpr); - const DeclRefExpr *RHSDeclRef = dyn_cast(RHSExpr); - if (!LHSDeclRef || !RHSDeclRef || - LHSDeclRef->getLocation().isMacroID() || - RHSDeclRef->getLocation().isMacroID()) - return; - const ValueDecl *LHSDecl = - cast(LHSDeclRef->getDecl()->getCanonicalDecl()); - const ValueDecl *RHSDecl = - cast(RHSDeclRef->getDecl()->getCanonicalDecl()); - if (LHSDecl != RHSDecl) - return; - if (LHSDecl->getType().isVolatileQualified()) - return; - if (const ReferenceType *RefTy = LHSDecl->getType()->getAs()) - if (RefTy->getPointeeType().isVolatileQualified()) - return; + // C++ [lib.support.types]p5: + // The macro offsetof accepts a restricted set of type arguments in this + // International Standard. type shall be a POD structure or a POD union + // (clause 9). + // C++11 [support.types]p4: + // If type is not a standard-layout class (Clause 9), the results are + // undefined. + if (CXXRecordDecl *CRD = dyn_cast(RD)) { + bool IsSafe = LangOpts.CPlusPlus11? CRD->isStandardLayout() : CRD->isPOD(); + unsigned DiagID = + LangOpts.CPlusPlus11? diag::ext_offsetof_non_standardlayout_type + : diag::ext_offsetof_non_pod_type; - auto Diag = S.Diag(OpLoc, IsBuiltin ? diag::warn_self_assignment_builtin - : diag::warn_self_assignment_overloaded) - << LHSDeclRef->getType() << LHSExpr->getSourceRange() - << RHSExpr->getSourceRange(); - if (const FieldDecl *SelfAssignField = - S.getSelfAssignmentClassMemberCandidate(RHSDecl)) - Diag << 1 << SelfAssignField - << FixItHint::CreateInsertion(LHSDeclRef->getBeginLoc(), "this->"); - else - Diag << 0; -} + if (!IsSafe && !DidWarnAboutNonPOD && !isUnevaluatedContext()) { + Diag(BuiltinLoc, DiagID) + << SourceRange(Components[0].LocStart, OC.LocEnd) << CurrentType; + DidWarnAboutNonPOD = true; + } + } -/// Check if a bitwise-& is performed on an Objective-C pointer. This -/// is usually indicative of introspection within the Objective-C pointer. -static void checkObjCPointerIntrospection(Sema &S, ExprResult &L, ExprResult &R, - SourceLocation OpLoc) { - if (!S.getLangOpts().ObjC) - return; + // Look for the field. + LookupResult R(*this, OC.U.IdentInfo, OC.LocStart, LookupMemberName); + LookupQualifiedName(R, RD); + FieldDecl *MemberDecl = R.getAsSingle(); + IndirectFieldDecl *IndirectMemberDecl = nullptr; + if (!MemberDecl) { + if ((IndirectMemberDecl = R.getAsSingle())) + MemberDecl = IndirectMemberDecl->getAnonField(); + } - const Expr *ObjCPointerExpr = nullptr, *OtherExpr = nullptr; - const Expr *LHS = L.get(); - const Expr *RHS = R.get(); + if (!MemberDecl) { + // Lookup could be ambiguous when looking up a placeholder variable + // __builtin_offsetof(S, _). + // In that case we would already have emitted a diagnostic + if (!R.isAmbiguous()) + Diag(BuiltinLoc, diag::err_no_member) + << OC.U.IdentInfo << RD << SourceRange(OC.LocStart, OC.LocEnd); + return ExprError(); + } - if (LHS->IgnoreParenCasts()->getType()->isObjCObjectPointerType()) { - ObjCPointerExpr = LHS; - OtherExpr = RHS; - } - else if (RHS->IgnoreParenCasts()->getType()->isObjCObjectPointerType()) { - ObjCPointerExpr = RHS; - OtherExpr = LHS; - } + // C99 7.17p3: + // (If the specified member is a bit-field, the behavior is undefined.) + // + // We diagnose this as an error. + if (MemberDecl->isBitField()) { + Diag(OC.LocEnd, diag::err_offsetof_bitfield) + << MemberDecl->getDeclName() + << SourceRange(BuiltinLoc, RParenLoc); + Diag(MemberDecl->getLocation(), diag::note_bitfield_decl); + return ExprError(); + } - // This warning is deliberately made very specific to reduce false - // positives with logic that uses '&' for hashing. This logic mainly - // looks for code trying to introspect into tagged pointers, which - // code should generally never do. - if (ObjCPointerExpr && isa(OtherExpr->IgnoreParenCasts())) { - unsigned Diag = diag::warn_objc_pointer_masking; - // Determine if we are introspecting the result of performSelectorXXX. - const Expr *Ex = ObjCPointerExpr->IgnoreParenCasts(); - // Special case messages to -performSelector and friends, which - // can return non-pointer values boxed in a pointer value. - // Some clients may wish to silence warnings in this subcase. - if (const ObjCMessageExpr *ME = dyn_cast(Ex)) { - Selector S = ME->getSelector(); - StringRef SelArg0 = S.getNameForSlot(0); - if (SelArg0.starts_with("performSelector")) - Diag = diag::warn_objc_pointer_masking_performSelector; + RecordDecl *Parent = MemberDecl->getParent(); + if (IndirectMemberDecl) + Parent = cast(IndirectMemberDecl->getDeclContext()); + + // If the member was found in a base class, introduce OffsetOfNodes for + // the base class indirections. + CXXBasePaths Paths; + if (IsDerivedFrom(OC.LocStart, CurrentType, Context.getTypeDeclType(Parent), + Paths)) { + if (Paths.getDetectedVirtual()) { + Diag(OC.LocEnd, diag::err_offsetof_field_of_virtual_base) + << MemberDecl->getDeclName() + << SourceRange(BuiltinLoc, RParenLoc); + return ExprError(); + } + + CXXBasePath &Path = Paths.front(); + for (const CXXBasePathElement &B : Path) + Comps.push_back(OffsetOfNode(B.Base)); } - S.Diag(OpLoc, Diag) - << ObjCPointerExpr->getSourceRange(); + if (IndirectMemberDecl) { + for (auto *FI : IndirectMemberDecl->chain()) { + assert(isa(FI)); + Comps.push_back(OffsetOfNode(OC.LocStart, + cast(FI), OC.LocEnd)); + } + } else + Comps.push_back(OffsetOfNode(OC.LocStart, MemberDecl, OC.LocEnd)); + + CurrentType = MemberDecl->getType().getNonReferenceType(); } -} -static NamedDecl *getDeclFromExpr(Expr *E) { - if (!E) - return nullptr; - if (auto *DRE = dyn_cast(E)) - return DRE->getDecl(); - if (auto *ME = dyn_cast(E)) - return ME->getMemberDecl(); - if (auto *IRE = dyn_cast(E)) - return IRE->getDecl(); - return nullptr; + return OffsetOfExpr::Create(Context, Context.getSizeType(), BuiltinLoc, TInfo, + Comps, Exprs, RParenLoc); } -// This helper function promotes a binary operator's operands (which are of a -// half vector type) to a vector of floats and then truncates the result to -// a vector of either half or short. -static ExprResult convertHalfVecBinOp(Sema &S, ExprResult LHS, ExprResult RHS, - BinaryOperatorKind Opc, QualType ResultTy, - ExprValueKind VK, ExprObjectKind OK, - bool IsCompAssign, SourceLocation OpLoc, - FPOptionsOverride FPFeatures) { - auto &Context = S.getASTContext(); - assert((isVector(ResultTy, Context.HalfTy) || - isVector(ResultTy, Context.ShortTy)) && - "Result must be a vector of half or short"); - assert(isVector(LHS.get()->getType(), Context.HalfTy) && - isVector(RHS.get()->getType(), Context.HalfTy) && - "both operands expected to be a half vector"); - - RHS = convertVector(RHS.get(), Context.FloatTy, S); - QualType BinOpResTy = RHS.get()->getType(); +ExprResult Sema::ActOnBuiltinOffsetOf(Scope *S, + SourceLocation BuiltinLoc, + SourceLocation TypeLoc, + ParsedType ParsedArgTy, + ArrayRef Components, + SourceLocation RParenLoc) { - // If Opc is a comparison, ResultType is a vector of shorts. In that case, - // change BinOpResTy to a vector of ints. - if (isVector(ResultTy, Context.ShortTy)) - BinOpResTy = S.GetSignedVectorType(BinOpResTy); + TypeSourceInfo *ArgTInfo; + QualType ArgTy = GetTypeFromParser(ParsedArgTy, &ArgTInfo); + if (ArgTy.isNull()) + return ExprError(); - if (IsCompAssign) - return CompoundAssignOperator::Create(Context, LHS.get(), RHS.get(), Opc, - ResultTy, VK, OK, OpLoc, FPFeatures, - BinOpResTy, BinOpResTy); + if (!ArgTInfo) + ArgTInfo = Context.getTrivialTypeSourceInfo(ArgTy, TypeLoc); - LHS = convertVector(LHS.get(), Context.FloatTy, S); - auto *BO = BinaryOperator::Create(Context, LHS.get(), RHS.get(), Opc, - BinOpResTy, VK, OK, OpLoc, FPFeatures); - return convertVector(BO, ResultTy->castAs()->getElementType(), S); + return BuildBuiltinOffsetOf(BuiltinLoc, ArgTInfo, Components, RParenLoc); } -static std::pair -CorrectDelayedTyposInBinOp(Sema &S, BinaryOperatorKind Opc, Expr *LHSExpr, - Expr *RHSExpr) { - ExprResult LHS = LHSExpr, RHS = RHSExpr; - if (!S.Context.isDependenceAllowed()) { - // C cannot handle TypoExpr nodes on either side of a binop because it - // doesn't handle dependent types properly, so make sure any TypoExprs have - // been dealt with before checking the operands. - LHS = S.CorrectDelayedTyposInExpr(LHS); - RHS = S.CorrectDelayedTyposInExpr( - RHS, /*InitDecl=*/nullptr, /*RecoverUncorrectedTypos=*/false, - [Opc, LHS](Expr *E) { - if (Opc != BO_Assign) - return ExprResult(E); - // Avoid correcting the RHS to the same Expr as the LHS. - Decl *D = getDeclFromExpr(E); - return (D && D == getDeclFromExpr(LHS.get())) ? ExprError() : E; - }); + +ExprResult Sema::ActOnChooseExpr(SourceLocation BuiltinLoc, + Expr *CondExpr, + Expr *LHSExpr, Expr *RHSExpr, + SourceLocation RPLoc) { + assert((CondExpr && LHSExpr && RHSExpr) && "Missing type argument(s)"); + + ExprValueKind VK = VK_PRValue; + ExprObjectKind OK = OK_Ordinary; + QualType resType; + bool CondIsTrue = false; + if (CondExpr->isTypeDependent() || CondExpr->isValueDependent()) { + resType = Context.DependentTy; + } else { + // The conditional expression is required to be a constant expression. + llvm::APSInt condEval(32); + ExprResult CondICE = VerifyIntegerConstantExpression( + CondExpr, &condEval, diag::err_typecheck_choose_expr_requires_constant); + if (CondICE.isInvalid()) + return ExprError(); + CondExpr = CondICE.get(); + CondIsTrue = condEval.getZExtValue(); + + // If the condition is > zero, then the AST type is the same as the LHSExpr. + Expr *ActiveExpr = CondIsTrue ? LHSExpr : RHSExpr; + + resType = ActiveExpr->getType(); + VK = ActiveExpr->getValueKind(); + OK = ActiveExpr->getObjectKind(); } - return std::make_pair(LHS, RHS); + + return new (Context) ChooseExpr(BuiltinLoc, CondExpr, LHSExpr, RHSExpr, + resType, VK, OK, RPLoc, CondIsTrue); } -/// Returns true if conversion between vectors of halfs and vectors of floats -/// is needed. -static bool needsConversionOfHalfVec(bool OpRequiresConversion, ASTContext &Ctx, - Expr *E0, Expr *E1 = nullptr) { - if (!OpRequiresConversion || Ctx.getLangOpts().NativeHalfType || - Ctx.getTargetInfo().useFP16ConversionIntrinsics()) - return false; +//===----------------------------------------------------------------------===// +// Clang Extensions. +//===----------------------------------------------------------------------===// - auto HasVectorOfHalfType = [&Ctx](Expr *E) { - QualType Ty = E->IgnoreImplicit()->getType(); +void Sema::ActOnBlockStart(SourceLocation CaretLoc, Scope *CurScope) { + BlockDecl *Block = BlockDecl::Create(Context, CurContext, CaretLoc); - // Don't promote half precision neon vectors like float16x4_t in arm_neon.h - // to vectors of floats. Although the element type of the vectors is __fp16, - // the vectors shouldn't be treated as storage-only types. See the - // discussion here: https://reviews.llvm.org/rG825235c140e7 - if (const VectorType *VT = Ty->getAs()) { - if (VT->getVectorKind() == VectorKind::Neon) - return false; - return VT->getElementType().getCanonicalType() == Ctx.HalfTy; + if (LangOpts.CPlusPlus) { + MangleNumberingContext *MCtx; + Decl *ManglingContextDecl; + std::tie(MCtx, ManglingContextDecl) = + getCurrentMangleNumberContext(Block->getDeclContext()); + if (MCtx) { + unsigned ManglingNumber = MCtx->getManglingNumber(Block); + Block->setBlockMangling(ManglingNumber, ManglingContextDecl); } - return false; - }; + } + + PushBlockScope(CurScope, Block); + CurContext->addDecl(Block); + if (CurScope) + PushDeclContext(CurScope, Block); + else + CurContext = Block; - return HasVectorOfHalfType(E0) && (!E1 || HasVectorOfHalfType(E1)); + getCurBlock()->HasImplicitReturnType = true; + + // Enter a new evaluation context to insulate the block from any + // cleanups from the enclosing full-expression. + PushExpressionEvaluationContext( + ExpressionEvaluationContext::PotentiallyEvaluated); } -ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, - BinaryOperatorKind Opc, - Expr *LHSExpr, Expr *RHSExpr) { - if (getLangOpts().CPlusPlus11 && isa(RHSExpr)) { - // The syntax only allows initializer lists on the RHS of assignment, - // so we don't need to worry about accepting invalid code for - // non-assignment operators. - // C++11 5.17p9: - // The meaning of x = {v} [...] is that of x = T(v) [...]. The meaning - // of x = {} is x = T(). - InitializationKind Kind = InitializationKind::CreateDirectList( - RHSExpr->getBeginLoc(), RHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); - InitializedEntity Entity = - InitializedEntity::InitializeTemporary(LHSExpr->getType()); - InitializationSequence InitSeq(*this, Entity, Kind, RHSExpr); - ExprResult Init = InitSeq.Perform(*this, Entity, Kind, RHSExpr); - if (Init.isInvalid()) - return Init; - RHSExpr = Init.get(); - } +void Sema::ActOnBlockArguments(SourceLocation CaretLoc, Declarator &ParamInfo, + Scope *CurScope) { + assert(ParamInfo.getIdentifier() == nullptr && + "block-id should have no identifier!"); + assert(ParamInfo.getContext() == DeclaratorContext::BlockLiteral); + BlockScopeInfo *CurBlock = getCurBlock(); - ExprResult LHS = LHSExpr, RHS = RHSExpr; - QualType ResultTy; // Result type of the binary operator. - // The following two variables are used for compound assignment operators - QualType CompLHSTy; // Type of LHS after promotions for computation - QualType CompResultTy; // Type of computation result - ExprValueKind VK = VK_PRValue; - ExprObjectKind OK = OK_Ordinary; - bool ConvertHalfVec = false; + TypeSourceInfo *Sig = GetTypeForDeclarator(ParamInfo); + QualType T = Sig->getType(); + DiagnoseUnexpandedParameterPack(CaretLoc, Sig, UPPC_Block); - std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr); - if (!LHS.isUsable() || !RHS.isUsable()) - return ExprError(); + // GetTypeForDeclarator always produces a function type for a block + // literal signature. Furthermore, it is always a FunctionProtoType + // unless the function was written with a typedef. + assert(T->isFunctionType() && + "GetTypeForDeclarator made a non-function block signature"); - if (getLangOpts().OpenCL) { - QualType LHSTy = LHSExpr->getType(); - QualType RHSTy = RHSExpr->getType(); - // OpenCLC v2.0 s6.13.11.1 allows atomic variables to be initialized by - // the ATOMIC_VAR_INIT macro. - if (LHSTy->isAtomicType() || RHSTy->isAtomicType()) { - SourceRange SR(LHSExpr->getBeginLoc(), RHSExpr->getEndLoc()); - if (BO_Assign == Opc) - Diag(OpLoc, diag::err_opencl_atomic_init) << 0 << SR; - else - ResultTy = InvalidOperands(OpLoc, LHS, RHS); - return ExprError(); - } + // Look for an explicit signature in that function type. + FunctionProtoTypeLoc ExplicitSignature; - // OpenCL special types - image, sampler, pipe, and blocks are to be used - // only with a builtin functions and therefore should be disallowed here. - if (LHSTy->isImageType() || RHSTy->isImageType() || - LHSTy->isSamplerT() || RHSTy->isSamplerT() || - LHSTy->isPipeType() || RHSTy->isPipeType() || - LHSTy->isBlockPointerType() || RHSTy->isBlockPointerType()) { - ResultTy = InvalidOperands(OpLoc, LHS, RHS); - return ExprError(); - } - } + if ((ExplicitSignature = Sig->getTypeLoc() + .getAsAdjusted())) { - checkTypeSupport(LHSExpr->getType(), OpLoc, /*ValueDecl*/ nullptr); - checkTypeSupport(RHSExpr->getType(), OpLoc, /*ValueDecl*/ nullptr); + // Check whether that explicit signature was synthesized by + // GetTypeForDeclarator. If so, don't save that as part of the + // written signature. + if (ExplicitSignature.getLocalRangeBegin() == + ExplicitSignature.getLocalRangeEnd()) { + // This would be much cheaper if we stored TypeLocs instead of + // TypeSourceInfos. + TypeLoc Result = ExplicitSignature.getReturnLoc(); + unsigned Size = Result.getFullDataSize(); + Sig = Context.CreateTypeSourceInfo(Result.getType(), Size); + Sig->getTypeLoc().initializeFullCopy(Result, Size); - switch (Opc) { - case BO_Assign: - ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, QualType(), Opc); - if (getLangOpts().CPlusPlus && - LHS.get()->getObjectKind() != OK_ObjCProperty) { - VK = LHS.get()->getValueKind(); - OK = LHS.get()->getObjectKind(); + ExplicitSignature = FunctionProtoTypeLoc(); } - if (!ResultTy.isNull()) { - DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc, true); - DiagnoseSelfMove(LHS.get(), RHS.get(), OpLoc); - - // Avoid copying a block to the heap if the block is assigned to a local - // auto variable that is declared in the same scope as the block. This - // optimization is unsafe if the local variable is declared in an outer - // scope. For example: - // - // BlockTy b; - // { - // b = ^{...}; - // } - // // It is unsafe to invoke the block here if it wasn't copied to the - // // heap. - // b(); + } - if (auto *BE = dyn_cast(RHS.get()->IgnoreParens())) - if (auto *DRE = dyn_cast(LHS.get()->IgnoreParens())) - if (auto *VD = dyn_cast(DRE->getDecl())) - if (VD->hasLocalStorage() && getCurScope()->isDeclScope(VD)) - BE->getBlockDecl()->setCanAvoidCopyToHeap(); + CurBlock->TheDecl->setSignatureAsWritten(Sig); + CurBlock->FunctionType = T; - if (LHS.get()->getType().hasNonTrivialToPrimitiveCopyCUnion()) - checkNonTrivialCUnion(LHS.get()->getType(), LHS.get()->getExprLoc(), - NTCUC_Assignment, NTCUK_Copy); - } - RecordModifiableNonNullParam(*this, LHS.get()); - break; - case BO_PtrMemD: - case BO_PtrMemI: - ResultTy = CheckPointerToMemberOperands(LHS, RHS, VK, OpLoc, - Opc == BO_PtrMemI); - break; - case BO_Mul: - case BO_Div: - ConvertHalfVec = true; - ResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, false, - Opc == BO_Div); - break; - case BO_Rem: - ResultTy = CheckRemainderOperands(LHS, RHS, OpLoc); - break; - case BO_Add: - ConvertHalfVec = true; - ResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_Sub: - ConvertHalfVec = true; - ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc); - break; - case BO_Shl: - case BO_Shr: - ResultTy = CheckShiftOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_LE: - case BO_LT: - case BO_GE: - case BO_GT: - ConvertHalfVec = true; - ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); + const auto *Fn = T->castAs(); + QualType RetTy = Fn->getReturnType(); + bool isVariadic = + (isa(Fn) && cast(Fn)->isVariadic()); - if (const auto *BI = dyn_cast(LHSExpr); - BI && BI->isComparisonOp()) - Diag(OpLoc, diag::warn_consecutive_comparison) - << BI->getOpcodeStr() << BinaryOperator::getOpcodeStr(Opc); + CurBlock->TheDecl->setIsVariadic(isVariadic); - break; - case BO_EQ: - case BO_NE: - ConvertHalfVec = true; - ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_Cmp: - ConvertHalfVec = true; - ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc); - assert(ResultTy.isNull() || ResultTy->getAsCXXRecordDecl()); - break; - case BO_And: - checkObjCPointerIntrospection(*this, LHS, RHS, OpLoc); - [[fallthrough]]; - case BO_Xor: - case BO_Or: - ResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_LAnd: - case BO_LOr: - ConvertHalfVec = true; - ResultTy = CheckLogicalOperands(LHS, RHS, OpLoc, Opc); - break; - case BO_MulAssign: - case BO_DivAssign: - ConvertHalfVec = true; - CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, true, - Opc == BO_DivAssign); - CompLHSTy = CompResultTy; - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_RemAssign: - CompResultTy = CheckRemainderOperands(LHS, RHS, OpLoc, true); - CompLHSTy = CompResultTy; - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_AddAssign: - ConvertHalfVec = true; - CompResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy); - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_SubAssign: - ConvertHalfVec = true; - CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, &CompLHSTy); - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_ShlAssign: - case BO_ShrAssign: - CompResultTy = CheckShiftOperands(LHS, RHS, OpLoc, Opc, true); - CompLHSTy = CompResultTy; - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_AndAssign: - case BO_OrAssign: // fallthrough - DiagnoseSelfAssignment(*this, LHS.get(), RHS.get(), OpLoc, true); - [[fallthrough]]; - case BO_XorAssign: - CompResultTy = CheckBitwiseOperands(LHS, RHS, OpLoc, Opc); - CompLHSTy = CompResultTy; - if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid()) - ResultTy = - CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy, Opc); - break; - case BO_Comma: - ResultTy = CheckCommaOperands(*this, LHS, RHS, OpLoc); - if (getLangOpts().CPlusPlus && !RHS.isInvalid()) { - VK = RHS.get()->getValueKind(); - OK = RHS.get()->getObjectKind(); - } - break; + // Context.DependentTy is used as a placeholder for a missing block + // return type. TODO: what should we do with declarators like: + // ^ * { ... } + // If the answer is "apply template argument deduction".... + if (RetTy != Context.DependentTy) { + CurBlock->ReturnType = RetTy; + CurBlock->TheDecl->setBlockMissingReturnType(false); + CurBlock->HasImplicitReturnType = false; } - if (ResultTy.isNull() || LHS.isInvalid() || RHS.isInvalid()) - return ExprError(); - - // Some of the binary operations require promoting operands of half vector to - // float vectors and truncating the result back to half vector. For now, we do - // this only when HalfArgsAndReturn is set (that is, when the target is arm or - // arm64). - assert( - (Opc == BO_Comma || isVector(RHS.get()->getType(), Context.HalfTy) == - isVector(LHS.get()->getType(), Context.HalfTy)) && - "both sides are half vectors or neither sides are"); - ConvertHalfVec = - needsConversionOfHalfVec(ConvertHalfVec, Context, LHS.get(), RHS.get()); - // Check for array bounds violations for both sides of the BinaryOperator - CheckArrayAccess(LHS.get()); - CheckArrayAccess(RHS.get()); + // Push block parameters from the declarator if we had them. + SmallVector Params; + if (ExplicitSignature) { + for (unsigned I = 0, E = ExplicitSignature.getNumParams(); I != E; ++I) { + ParmVarDecl *Param = ExplicitSignature.getParam(I); + if (Param->getIdentifier() == nullptr && !Param->isImplicit() && + !Param->isInvalidDecl() && !getLangOpts().CPlusPlus) { + // Diagnose this as an extension in C17 and earlier. + if (!getLangOpts().C23) + Diag(Param->getLocation(), diag::ext_parameter_name_omitted_c23); + } + Params.push_back(Param); + } - if (const ObjCIsaExpr *OISA = dyn_cast(LHS.get()->IgnoreParenCasts())) { - NamedDecl *ObjectSetClass = LookupSingleName(TUScope, - &Context.Idents.get("object_setClass"), - SourceLocation(), LookupOrdinaryName); - if (ObjectSetClass && isa(LHS.get())) { - SourceLocation RHSLocEnd = getLocForEndOfToken(RHS.get()->getEndLoc()); - Diag(LHS.get()->getExprLoc(), diag::warn_objc_isa_assign) - << FixItHint::CreateInsertion(LHS.get()->getBeginLoc(), - "object_setClass(") - << FixItHint::CreateReplacement(SourceRange(OISA->getOpLoc(), OpLoc), - ",") - << FixItHint::CreateInsertion(RHSLocEnd, ")"); + // Fake up parameter variables if we have a typedef, like + // ^ fntype { ... } + } else if (const FunctionProtoType *Fn = T->getAs()) { + for (const auto &I : Fn->param_types()) { + ParmVarDecl *Param = BuildParmVarDeclForTypedef( + CurBlock->TheDecl, ParamInfo.getBeginLoc(), I); + Params.push_back(Param); } - else - Diag(LHS.get()->getExprLoc(), diag::warn_objc_isa_assign); } - else if (const ObjCIvarRefExpr *OIRE = - dyn_cast(LHS.get()->IgnoreParenCasts())) - DiagnoseDirectIsaAccess(*this, OIRE, OpLoc, RHS.get()); - // Opc is not a compound assignment if CompResultTy is null. - if (CompResultTy.isNull()) { - if (ConvertHalfVec) - return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, false, - OpLoc, CurFPFeatureOverrides()); - return BinaryOperator::Create(Context, LHS.get(), RHS.get(), Opc, ResultTy, - VK, OK, OpLoc, CurFPFeatureOverrides()); + // Set the parameters on the block decl. + if (!Params.empty()) { + CurBlock->TheDecl->setParams(Params); + CheckParmsForFunctionDef(CurBlock->TheDecl->parameters(), + /*CheckParameterNames=*/false); } - // Handle compound assignments. - if (getLangOpts().CPlusPlus && LHS.get()->getObjectKind() != - OK_ObjCProperty) { - VK = VK_LValue; - OK = LHS.get()->getObjectKind(); - } + // Finally we can process decl attributes. + ProcessDeclAttributes(CurScope, CurBlock->TheDecl, ParamInfo); - // The LHS is not converted to the result type for fixed-point compound - // assignment as the common type is computed on demand. Reset the CompLHSTy - // to the LHS type we would have gotten after unary conversions. - if (CompResultTy->isFixedPointType()) - CompLHSTy = UsualUnaryConversions(LHS.get()).get()->getType(); + // Put the parameter variables in scope. + for (auto *AI : CurBlock->TheDecl->parameters()) { + AI->setOwningFunction(CurBlock->TheDecl); - if (ConvertHalfVec) - return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, true, - OpLoc, CurFPFeatureOverrides()); + // If this has an identifier, add it to the scope stack. + if (AI->getIdentifier()) { + CheckShadow(CurBlock->TheScope, AI); - return CompoundAssignOperator::Create( - Context, LHS.get(), RHS.get(), Opc, ResultTy, VK, OK, OpLoc, - CurFPFeatureOverrides(), CompLHSTy, CompResultTy); + PushOnScopeChains(AI, CurBlock->TheScope); + } + + if (AI->isInvalidDecl()) + CurBlock->TheDecl->setInvalidDecl(); + } } -/// DiagnoseBitwisePrecedence - Emit a warning when bitwise and comparison -/// operators are mixed in a way that suggests that the programmer forgot that -/// comparison operators have higher precedence. The most typical example of -/// such code is "flags & 0x0020 != 0", which is equivalent to "flags & 1". -static void DiagnoseBitwisePrecedence(Sema &Self, BinaryOperatorKind Opc, - SourceLocation OpLoc, Expr *LHSExpr, - Expr *RHSExpr) { - BinaryOperator *LHSBO = dyn_cast(LHSExpr); - BinaryOperator *RHSBO = dyn_cast(RHSExpr); +void Sema::ActOnBlockError(SourceLocation CaretLoc, Scope *CurScope) { + // Leave the expression-evaluation context. + DiscardCleanupsInEvaluationContext(); + PopExpressionEvaluationContext(); - // Check that one of the sides is a comparison operator and the other isn't. - bool isLeftComp = LHSBO && LHSBO->isComparisonOp(); - bool isRightComp = RHSBO && RHSBO->isComparisonOp(); - if (isLeftComp == isRightComp) - return; + // Pop off CurBlock, handle nested blocks. + PopDeclContext(); + PopFunctionScopeInfo(); +} - // Bitwise operations are sometimes used as eager logical ops. - // Don't diagnose this. - bool isLeftBitwise = LHSBO && LHSBO->isBitwiseOp(); - bool isRightBitwise = RHSBO && RHSBO->isBitwiseOp(); - if (isLeftBitwise || isRightBitwise) - return; +ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, + Stmt *Body, Scope *CurScope) { + // If blocks are disabled, emit an error. + if (!LangOpts.Blocks) + Diag(CaretLoc, diag::err_blocks_disable) << LangOpts.OpenCL; - SourceRange DiagRange = isLeftComp - ? SourceRange(LHSExpr->getBeginLoc(), OpLoc) - : SourceRange(OpLoc, RHSExpr->getEndLoc()); - StringRef OpStr = isLeftComp ? LHSBO->getOpcodeStr() : RHSBO->getOpcodeStr(); - SourceRange ParensRange = - isLeftComp - ? SourceRange(LHSBO->getRHS()->getBeginLoc(), RHSExpr->getEndLoc()) - : SourceRange(LHSExpr->getBeginLoc(), RHSBO->getLHS()->getEndLoc()); + // Leave the expression-evaluation context. + if (hasAnyUnrecoverableErrorsInThisFunction()) + DiscardCleanupsInEvaluationContext(); + assert(!Cleanup.exprNeedsCleanups() && + "cleanups within block not correctly bound!"); + PopExpressionEvaluationContext(); - Self.Diag(OpLoc, diag::warn_precedence_bitwise_rel) - << DiagRange << BinaryOperator::getOpcodeStr(Opc) << OpStr; - SuggestParentheses(Self, OpLoc, - Self.PDiag(diag::note_precedence_silence) << OpStr, - (isLeftComp ? LHSExpr : RHSExpr)->getSourceRange()); - SuggestParentheses(Self, OpLoc, - Self.PDiag(diag::note_precedence_bitwise_first) - << BinaryOperator::getOpcodeStr(Opc), - ParensRange); -} + BlockScopeInfo *BSI = cast(FunctionScopes.back()); + BlockDecl *BD = BSI->TheDecl; -/// It accepts a '&&' expr that is inside a '||' one. -/// Emit a diagnostic together with a fixit hint that wraps the '&&' expression -/// in parentheses. -static void -EmitDiagnosticForLogicalAndInLogicalOr(Sema &Self, SourceLocation OpLoc, - BinaryOperator *Bop) { - assert(Bop->getOpcode() == BO_LAnd); - Self.Diag(Bop->getOperatorLoc(), diag::warn_logical_and_in_logical_or) - << Bop->getSourceRange() << OpLoc; - SuggestParentheses(Self, Bop->getOperatorLoc(), - Self.PDiag(diag::note_precedence_silence) - << Bop->getOpcodeStr(), - Bop->getSourceRange()); -} + maybeAddDeclWithEffects(BD); -/// Look for '&&' in the left hand of a '||' expr. -static void DiagnoseLogicalAndInLogicalOrLHS(Sema &S, SourceLocation OpLoc, - Expr *LHSExpr, Expr *RHSExpr) { - if (BinaryOperator *Bop = dyn_cast(LHSExpr)) { - if (Bop->getOpcode() == BO_LAnd) { - // If it's "string_literal && a || b" don't warn since the precedence - // doesn't matter. - if (!isa(Bop->getLHS()->IgnoreParenImpCasts())) - return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, Bop); - } else if (Bop->getOpcode() == BO_LOr) { - if (BinaryOperator *RBop = dyn_cast(Bop->getRHS())) { - // If it's "a || b && string_literal || c" we didn't warn earlier for - // "a || b && string_literal", but warn now. - if (RBop->getOpcode() == BO_LAnd && - isa(RBop->getRHS()->IgnoreParenImpCasts())) - return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, RBop); - } - } - } -} + if (BSI->HasImplicitReturnType) + deduceClosureReturnType(*BSI); -/// Look for '&&' in the right hand of a '||' expr. -static void DiagnoseLogicalAndInLogicalOrRHS(Sema &S, SourceLocation OpLoc, - Expr *LHSExpr, Expr *RHSExpr) { - if (BinaryOperator *Bop = dyn_cast(RHSExpr)) { - if (Bop->getOpcode() == BO_LAnd) { - // If it's "a || b && string_literal" don't warn since the precedence - // doesn't matter. - if (!isa(Bop->getRHS()->IgnoreParenImpCasts())) - return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, Bop); - } - } -} + QualType RetTy = Context.VoidTy; + if (!BSI->ReturnType.isNull()) + RetTy = BSI->ReturnType; -/// Look for bitwise op in the left or right hand of a bitwise op with -/// lower precedence and emit a diagnostic together with a fixit hint that wraps -/// the '&' expression in parentheses. -static void DiagnoseBitwiseOpInBitwiseOp(Sema &S, BinaryOperatorKind Opc, - SourceLocation OpLoc, Expr *SubExpr) { - if (BinaryOperator *Bop = dyn_cast(SubExpr)) { - if (Bop->isBitwiseOp() && Bop->getOpcode() < Opc) { - S.Diag(Bop->getOperatorLoc(), diag::warn_bitwise_op_in_bitwise_op) - << Bop->getOpcodeStr() << BinaryOperator::getOpcodeStr(Opc) - << Bop->getSourceRange() << OpLoc; - SuggestParentheses(S, Bop->getOperatorLoc(), - S.PDiag(diag::note_precedence_silence) - << Bop->getOpcodeStr(), - Bop->getSourceRange()); - } - } -} + bool NoReturn = BD->hasAttr(); + QualType BlockTy; -static void DiagnoseAdditionInShift(Sema &S, SourceLocation OpLoc, - Expr *SubExpr, StringRef Shift) { - if (BinaryOperator *Bop = dyn_cast(SubExpr)) { - if (Bop->getOpcode() == BO_Add || Bop->getOpcode() == BO_Sub) { - StringRef Op = Bop->getOpcodeStr(); - S.Diag(Bop->getOperatorLoc(), diag::warn_addition_in_bitshift) - << Bop->getSourceRange() << OpLoc << Shift << Op; - SuggestParentheses(S, Bop->getOperatorLoc(), - S.PDiag(diag::note_precedence_silence) << Op, - Bop->getSourceRange()); + // If the user wrote a function type in some form, try to use that. + if (!BSI->FunctionType.isNull()) { + const FunctionType *FTy = BSI->FunctionType->castAs(); + + FunctionType::ExtInfo Ext = FTy->getExtInfo(); + if (NoReturn && !Ext.getNoReturn()) Ext = Ext.withNoReturn(true); + + // Turn protoless block types into nullary block types. + if (isa(FTy)) { + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExtInfo = Ext; + BlockTy = Context.getFunctionType(RetTy, {}, EPI); + + // Otherwise, if we don't need to change anything about the function type, + // preserve its sugar structure. + } else if (FTy->getReturnType() == RetTy && + (!NoReturn || FTy->getNoReturnAttr())) { + BlockTy = BSI->FunctionType; + + // Otherwise, make the minimal modifications to the function type. + } else { + const FunctionProtoType *FPT = cast(FTy); + FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); + EPI.TypeQuals = Qualifiers(); + EPI.ExtInfo = Ext; + BlockTy = Context.getFunctionType(RetTy, FPT->getParamTypes(), EPI); } + + // If we don't have a function type, just build one from nothing. + } else { + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExtInfo = FunctionType::ExtInfo().withNoReturn(NoReturn); + BlockTy = Context.getFunctionType(RetTy, {}, EPI); } -} -static void DiagnoseShiftCompare(Sema &S, SourceLocation OpLoc, - Expr *LHSExpr, Expr *RHSExpr) { - CXXOperatorCallExpr *OCE = dyn_cast(LHSExpr); - if (!OCE) - return; + DiagnoseUnusedParameters(BD->parameters()); + BlockTy = Context.getBlockPointerType(BlockTy); - FunctionDecl *FD = OCE->getDirectCallee(); - if (!FD || !FD->isOverloadedOperator()) - return; + // If needed, diagnose invalid gotos and switches in the block. + if (getCurFunction()->NeedsScopeChecking() && + !PP.isCodeCompletionEnabled()) + DiagnoseInvalidJumps(cast(Body)); - OverloadedOperatorKind Kind = FD->getOverloadedOperator(); - if (Kind != OO_LessLess && Kind != OO_GreaterGreater) - return; + BD->setBody(cast(Body)); - S.Diag(OpLoc, diag::warn_overloaded_shift_in_comparison) - << LHSExpr->getSourceRange() << RHSExpr->getSourceRange() - << (Kind == OO_LessLess); - SuggestParentheses(S, OCE->getOperatorLoc(), - S.PDiag(diag::note_precedence_silence) - << (Kind == OO_LessLess ? "<<" : ">>"), - OCE->getSourceRange()); - SuggestParentheses( - S, OpLoc, S.PDiag(diag::note_evaluate_comparison_first), - SourceRange(OCE->getArg(1)->getBeginLoc(), RHSExpr->getEndLoc())); -} + if (Body && getCurFunction()->HasPotentialAvailabilityViolations) + DiagnoseUnguardedAvailabilityViolations(BD); -/// DiagnoseBinOpPrecedence - Emit warnings for expressions with tricky -/// precedence. -static void DiagnoseBinOpPrecedence(Sema &Self, BinaryOperatorKind Opc, - SourceLocation OpLoc, Expr *LHSExpr, - Expr *RHSExpr){ - // Diagnose "arg1 'bitwise' arg2 'eq' arg3". - if (BinaryOperator::isBitwiseOp(Opc)) - DiagnoseBitwisePrecedence(Self, Opc, OpLoc, LHSExpr, RHSExpr); + if (Body && getCurFunction()->HasPotentialFeatureAvailabilityViolations) + DiagnoseUnguardedFeatureAvailabilityViolations(BD); - // Diagnose "arg1 & arg2 | arg3" - if ((Opc == BO_Or || Opc == BO_Xor) && - !OpLoc.isMacroID()/* Don't warn in macros. */) { - DiagnoseBitwiseOpInBitwiseOp(Self, Opc, OpLoc, LHSExpr); - DiagnoseBitwiseOpInBitwiseOp(Self, Opc, OpLoc, RHSExpr); - } + // Try to apply the named return value optimization. We have to check again + // if we can do this, though, because blocks keep return statements around + // to deduce an implicit return type. + if (getLangOpts().CPlusPlus && RetTy->isRecordType() && + !BD->isDependentContext()) + computeNRVO(Body, BSI); - // Warn about arg1 || arg2 && arg3, as GCC 4.3+ does. - // We don't warn for 'assert(a || b && "bad")' since this is safe. - if (Opc == BO_LOr && !OpLoc.isMacroID()/* Don't warn in macros. */) { - DiagnoseLogicalAndInLogicalOrLHS(Self, OpLoc, LHSExpr, RHSExpr); - DiagnoseLogicalAndInLogicalOrRHS(Self, OpLoc, LHSExpr, RHSExpr); - } + if (RetTy.hasNonTrivialToPrimitiveDestructCUnion() || + RetTy.hasNonTrivialToPrimitiveCopyCUnion()) + checkNonTrivialCUnion(RetTy, BD->getCaretLocation(), NTCUC_FunctionReturn, + NTCUK_Destruct|NTCUK_Copy); - if ((Opc == BO_Shl && LHSExpr->getType()->isIntegralType(Self.getASTContext())) - || Opc == BO_Shr) { - StringRef Shift = BinaryOperator::getOpcodeStr(Opc); - DiagnoseAdditionInShift(Self, OpLoc, LHSExpr, Shift); - DiagnoseAdditionInShift(Self, OpLoc, RHSExpr, Shift); - } + PopDeclContext(); - // Warn on overloaded shift operators and comparisons, such as: - // cout << 5 == 4; - if (BinaryOperator::isComparisonOp(Opc)) - DiagnoseShiftCompare(Self, OpLoc, LHSExpr, RHSExpr); -} + // Set the captured variables on the block. + SmallVector Captures; + for (Capture &Cap : BSI->Captures) { + if (Cap.isInvalid() || Cap.isThisCapture()) + continue; + // Cap.getVariable() is always a VarDecl because + // blocks cannot capture structured bindings or other ValueDecl kinds. + auto *Var = cast(Cap.getVariable()); + Expr *CopyExpr = nullptr; + if (getLangOpts().CPlusPlus && Cap.isCopyCapture()) { + if (const RecordType *Record = + Cap.getCaptureType()->getAs()) { + // The capture logic needs the destructor, so make sure we mark it. + // Usually this is unnecessary because most local variables have + // their destructors marked at declaration time, but parameters are + // an exception because it's technically only the call site that + // actually requires the destructor. + if (isa(Var)) + FinalizeVarWithDestructor(Var, Record); -ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc, - tok::TokenKind Kind, - Expr *LHSExpr, Expr *RHSExpr) { - BinaryOperatorKind Opc = ConvertTokenKindToBinaryOpcode(Kind); - assert(LHSExpr && "ActOnBinOp(): missing left expression"); - assert(RHSExpr && "ActOnBinOp(): missing right expression"); + // Enter a separate potentially-evaluated context while building block + // initializers to isolate their cleanups from those of the block + // itself. + // FIXME: Is this appropriate even when the block itself occurs in an + // unevaluated operand? + EnterExpressionEvaluationContext EvalContext( + *this, ExpressionEvaluationContext::PotentiallyEvaluated); - // Emit warnings for tricky precedence issues, e.g. "bitfield & 0x4 == 0" - DiagnoseBinOpPrecedence(*this, Opc, TokLoc, LHSExpr, RHSExpr); + SourceLocation Loc = Cap.getLocation(); - BuiltinCountedByRefKind K = - BinaryOperator::isAssignmentOp(Opc) ? AssignmentKind : BinaryExprKind; + ExprResult Result = BuildDeclarationNameExpr( + CXXScopeSpec(), DeclarationNameInfo(Var->getDeclName(), Loc), Var); - CheckInvalidBuiltinCountedByRef(LHSExpr, K); - CheckInvalidBuiltinCountedByRef(RHSExpr, K); + // According to the blocks spec, the capture of a variable from + // the stack requires a const copy constructor. This is not true + // of the copy/move done to move a __block variable to the heap. + if (!Result.isInvalid() && + !Result.get()->getType().isConstQualified()) { + Result = ImpCastExprToType(Result.get(), + Result.get()->getType().withConst(), + CK_NoOp, VK_LValue); + } - return BuildBinOp(S, TokLoc, Opc, LHSExpr, RHSExpr); -} + if (!Result.isInvalid()) { + Result = PerformCopyInitialization( + InitializedEntity::InitializeBlock(Var->getLocation(), + Cap.getCaptureType()), + Loc, Result.get()); + } -void Sema::LookupBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc, - UnresolvedSetImpl &Functions) { - OverloadedOperatorKind OverOp = BinaryOperator::getOverloadedOperator(Opc); - if (OverOp != OO_None && OverOp != OO_Equal) - LookupOverloadedOperatorName(OverOp, S, Functions); + // Build a full-expression copy expression if initialization + // succeeded and used a non-trivial constructor. Recover from + // errors by pretending that the copy isn't necessary. + if (!Result.isInvalid() && + !cast(Result.get())->getConstructor() + ->isTrivial()) { + Result = MaybeCreateExprWithCleanups(Result); + CopyExpr = Result.get(); + } + } + } - // In C++20 onwards, we may have a second operator to look up. - if (getLangOpts().CPlusPlus20) { - if (OverloadedOperatorKind ExtraOp = getRewrittenOverloadedOperator(OverOp)) - LookupOverloadedOperatorName(ExtraOp, S, Functions); + BlockDecl::Capture NewCap(Var, Cap.isBlockCapture(), Cap.isNested(), + CopyExpr); + Captures.push_back(NewCap); } -} + BD->setCaptures(Context, Captures, BSI->CXXThisCaptureIndex != 0); -/// Build an overloaded binary operator expression in the given scope. -static ExprResult BuildOverloadedBinOp(Sema &S, Scope *Sc, SourceLocation OpLoc, - BinaryOperatorKind Opc, - Expr *LHS, Expr *RHS) { - switch (Opc) { - case BO_Assign: - // In the non-overloaded case, we warn about self-assignment (x = x) for - // both simple assignment and certain compound assignments where algebra - // tells us the operation yields a constant result. When the operator is - // overloaded, we can't do the latter because we don't want to assume that - // those algebraic identities still apply; for example, a path-building - // library might use operator/= to append paths. But it's still reasonable - // to assume that simple assignment is just moving/copying values around - // and so self-assignment is likely a bug. - DiagnoseSelfAssignment(S, LHS, RHS, OpLoc, false); - [[fallthrough]]; - case BO_DivAssign: - case BO_RemAssign: - case BO_SubAssign: - case BO_AndAssign: - case BO_OrAssign: - case BO_XorAssign: - CheckIdentityFieldAssignment(LHS, RHS, OpLoc, S); - break; - default: - break; - } + // Pop the block scope now but keep it alive to the end of this function. + AnalysisBasedWarnings::Policy WP = + AnalysisWarnings.getPolicyInEffectAt(Body->getEndLoc()); + PoppedFunctionScopePtr ScopeRAII = PopFunctionScopeInfo(&WP, BD, BlockTy); - // Find all of the overloaded operators visible from this point. - UnresolvedSet<16> Functions; - S.LookupBinOp(Sc, OpLoc, Opc, Functions); + BlockExpr *Result = new (Context) + BlockExpr(BD, BlockTy, BSI->ContainsUnexpandedParameterPack); - // Build the (potentially-overloaded, potentially-dependent) - // binary operation. - return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS); -} + // If the block isn't obviously global, i.e. it captures anything at + // all, then we need to do a few things in the surrounding context: + if (Result->getBlockDecl()->hasCaptures()) { + // First, this expression has a new cleanup object. + ExprCleanupObjects.push_back(Result->getBlockDecl()); + Cleanup.setExprNeedsCleanups(true); -ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, - BinaryOperatorKind Opc, - Expr *LHSExpr, Expr *RHSExpr) { - ExprResult LHS, RHS; - std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr); - if (!LHS.isUsable() || !RHS.isUsable()) - return ExprError(); - LHSExpr = LHS.get(); - RHSExpr = RHS.get(); + // It also gets a branch-protected scope if any of the captured + // variables needs destruction. + for (const auto &CI : Result->getBlockDecl()->captures()) { + const VarDecl *var = CI.getVariable(); + if (var->getType().isDestructedType() != QualType::DK_none) { + setFunctionHasBranchProtectedScope(); + break; + } + } + } - // We want to end up calling one of SemaPseudoObject::checkAssignment - // (if the LHS is a pseudo-object), BuildOverloadedBinOp (if - // both expressions are overloadable or either is type-dependent), - // or CreateBuiltinBinOp (in any other case). We also want to get - // any placeholder types out of the way. + if (getCurFunction()) + getCurFunction()->addBlock(BD); - // Handle pseudo-objects in the LHS. - if (const BuiltinType *pty = LHSExpr->getType()->getAsPlaceholderType()) { - // Assignments with a pseudo-object l-value need special analysis. - if (pty->getKind() == BuiltinType::PseudoObject && - BinaryOperator::isAssignmentOp(Opc)) - return PseudoObject().checkAssignment(S, OpLoc, Opc, LHSExpr, RHSExpr); + // This can happen if the block's return type is deduced, but + // the return expression is invalid. + if (BD->isInvalidDecl()) + return CreateRecoveryExpr(Result->getBeginLoc(), Result->getEndLoc(), + {Result}, Result->getType()); + return Result; +} - // Don't resolve overloads if the other type is overloadable. - if (getLangOpts().CPlusPlus && pty->getKind() == BuiltinType::Overload) { - // We can't actually test that if we still have a placeholder, - // though. Fortunately, none of the exceptions we see in that - // code below are valid when the LHS is an overload set. Note - // that an overload set can be dependently-typed, but it never - // instantiates to having an overloadable type. - ExprResult resolvedRHS = CheckPlaceholderExpr(RHSExpr); - if (resolvedRHS.isInvalid()) return ExprError(); - RHSExpr = resolvedRHS.get(); +ExprResult Sema::ActOnVAArg(SourceLocation BuiltinLoc, Expr *E, ParsedType Ty, + SourceLocation RPLoc) { + TypeSourceInfo *TInfo; + GetTypeFromParser(Ty, &TInfo); + return BuildVAArgExpr(BuiltinLoc, E, TInfo, RPLoc); +} - if (RHSExpr->isTypeDependent() || - RHSExpr->getType()->isOverloadableType()) - return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); +ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, + Expr *E, TypeSourceInfo *TInfo, + SourceLocation RPLoc) { + Expr *OrigExpr = E; + bool IsMS = false; + + // CUDA device code does not support varargs. + if (getLangOpts().CUDA && getLangOpts().CUDAIsDevice) { + if (const FunctionDecl *F = dyn_cast(CurContext)) { + CUDAFunctionTarget T = CUDA().IdentifyTarget(F); + if (T == CUDAFunctionTarget::Global || T == CUDAFunctionTarget::Device || + T == CUDAFunctionTarget::HostDevice) + return ExprError(Diag(E->getBeginLoc(), diag::err_va_arg_in_device)); } + } - // If we're instantiating "a.x < b" or "A::x < b" and 'x' names a function - // template, diagnose the missing 'template' keyword instead of diagnosing - // an invalid use of a bound member function. - // - // Note that "A::x < b" might be valid if 'b' has an overloadable type due - // to C++1z [over.over]/1.4, but we already checked for that case above. - if (Opc == BO_LT && inTemplateInstantiation() && - (pty->getKind() == BuiltinType::BoundMember || - pty->getKind() == BuiltinType::Overload)) { - auto *OE = dyn_cast(LHSExpr); - if (OE && !OE->hasTemplateKeyword() && !OE->hasExplicitTemplateArgs() && - llvm::any_of(OE->decls(), [](NamedDecl *ND) { - return isa(ND); - })) { - Diag(OE->getQualifier() ? OE->getQualifierLoc().getBeginLoc() - : OE->getNameLoc(), - diag::err_template_kw_missing) - << OE->getName().getAsIdentifierInfo(); + // NVPTX does not support va_arg expression. + if (getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice && + Context.getTargetInfo().getTriple().isNVPTX()) + targetDiag(E->getBeginLoc(), diag::err_va_arg_in_device); + + // It might be a __builtin_ms_va_list. (But don't ever mark a va_arg() + // as Microsoft ABI on an actual Microsoft platform, where + // __builtin_ms_va_list and __builtin_va_list are the same.) + if (!E->isTypeDependent() && Context.getTargetInfo().hasBuiltinMSVaList() && + Context.getTargetInfo().getBuiltinVaListKind() != TargetInfo::CharPtrBuiltinVaList) { + QualType MSVaListType = Context.getBuiltinMSVaListType(); + if (Context.hasSameType(MSVaListType, E->getType())) { + if (CheckForModifiableLvalue(E, BuiltinLoc, *this)) return ExprError(); - } + IsMS = true; } + } - ExprResult LHS = CheckPlaceholderExpr(LHSExpr); - if (LHS.isInvalid()) return ExprError(); - LHSExpr = LHS.get(); + // Get the va_list type + QualType VaListType = Context.getBuiltinVaListType(); + if (!IsMS) { + if (VaListType->isArrayType()) { + // Deal with implicit array decay; for example, on x86-64, + // va_list is an array, but it's supposed to decay to + // a pointer for va_arg. + VaListType = Context.getArrayDecayedType(VaListType); + // Make sure the input expression also decays appropriately. + ExprResult Result = UsualUnaryConversions(E); + if (Result.isInvalid()) + return ExprError(); + E = Result.get(); + } else if (VaListType->isRecordType() && getLangOpts().CPlusPlus) { + // If va_list is a record type and we are compiling in C++ mode, + // check the argument using reference binding. + InitializedEntity Entity = InitializedEntity::InitializeParameter( + Context, Context.getLValueReferenceType(VaListType), false); + ExprResult Init = PerformCopyInitialization(Entity, SourceLocation(), E); + if (Init.isInvalid()) + return ExprError(); + E = Init.getAs(); + } else { + // Otherwise, the va_list argument must be an l-value because + // it is modified by va_arg. + if (!E->isTypeDependent() && + CheckForModifiableLvalue(E, BuiltinLoc, *this)) + return ExprError(); + } } - // Handle pseudo-objects in the RHS. - if (const BuiltinType *pty = RHSExpr->getType()->getAsPlaceholderType()) { - // An overload in the RHS can potentially be resolved by the type - // being assigned to. - if (Opc == BO_Assign && pty->getKind() == BuiltinType::Overload) { - if (getLangOpts().CPlusPlus && - (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent() || - LHSExpr->getType()->isOverloadableType())) - return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + if (!IsMS && !E->isTypeDependent() && + !Context.hasSameType(VaListType, E->getType())) + return ExprError( + Diag(E->getBeginLoc(), + diag::err_first_argument_to_va_arg_not_of_type_va_list) + << OrigExpr->getType() << E->getSourceRange()); - return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr); - } + if (!TInfo->getType()->isDependentType()) { + if (RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), TInfo->getType(), + diag::err_second_parameter_to_va_arg_incomplete, + TInfo->getTypeLoc())) + return ExprError(); - // Don't resolve overloads if the other type is overloadable. - if (getLangOpts().CPlusPlus && pty->getKind() == BuiltinType::Overload && - LHSExpr->getType()->isOverloadableType()) - return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); + if (RequireNonAbstractType(TInfo->getTypeLoc().getBeginLoc(), + TInfo->getType(), + diag::err_second_parameter_to_va_arg_abstract, + TInfo->getTypeLoc())) + return ExprError(); - ExprResult resolvedRHS = CheckPlaceholderExpr(RHSExpr); - if (!resolvedRHS.isUsable()) return ExprError(); - RHSExpr = resolvedRHS.get(); - } + if (!TInfo->getType().isPODType(Context)) { + Diag(TInfo->getTypeLoc().getBeginLoc(), + TInfo->getType()->isObjCLifetimeType() + ? diag::warn_second_parameter_to_va_arg_ownership_qualified + : diag::warn_second_parameter_to_va_arg_not_pod) + << TInfo->getType() + << TInfo->getTypeLoc().getSourceRange(); + } - if (getLangOpts().CPlusPlus) { - // Otherwise, build an overloaded op if either expression is type-dependent - // or has an overloadable type. - if (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent() || - LHSExpr->getType()->isOverloadableType() || - RHSExpr->getType()->isOverloadableType()) - return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); - } + if (TInfo->getType()->isArrayType()) { + DiagRuntimeBehavior(TInfo->getTypeLoc().getBeginLoc(), E, + PDiag(diag::warn_second_parameter_to_va_arg_array) + << TInfo->getType() + << TInfo->getTypeLoc().getSourceRange()); + } - if (getLangOpts().RecoveryAST && - (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent())) { - assert(!getLangOpts().CPlusPlus); - assert((LHSExpr->containsErrors() || RHSExpr->containsErrors()) && - "Should only occur in error-recovery path."); - if (BinaryOperator::isCompoundAssignmentOp(Opc)) - // C [6.15.16] p3: - // An assignment expression has the value of the left operand after the - // assignment, but is not an lvalue. - return CompoundAssignOperator::Create( - Context, LHSExpr, RHSExpr, Opc, - LHSExpr->getType().getUnqualifiedType(), VK_PRValue, OK_Ordinary, - OpLoc, CurFPFeatureOverrides()); - QualType ResultType; - switch (Opc) { - case BO_Assign: - ResultType = LHSExpr->getType().getUnqualifiedType(); - break; - case BO_LT: - case BO_GT: - case BO_LE: - case BO_GE: - case BO_EQ: - case BO_NE: - case BO_LAnd: - case BO_LOr: - // These operators have a fixed result type regardless of operands. - ResultType = Context.IntTy; - break; - case BO_Comma: - ResultType = RHSExpr->getType(); - break; - default: - ResultType = Context.DependentTy; - break; + // Check for va_arg where arguments of the given type will be promoted + // (i.e. this va_arg is guaranteed to have undefined behavior). + QualType PromoteType; + if (Context.isPromotableIntegerType(TInfo->getType())) { + PromoteType = Context.getPromotedIntegerType(TInfo->getType()); + // [cstdarg.syn]p1 defers the C++ behavior to what the C standard says, + // and C23 7.16.1.1p2 says, in part: + // If type is not compatible with the type of the actual next argument + // (as promoted according to the default argument promotions), the + // behavior is undefined, except for the following cases: + // - both types are pointers to qualified or unqualified versions of + // compatible types; + // - one type is compatible with a signed integer type, the other + // type is compatible with the corresponding unsigned integer type, + // and the value is representable in both types; + // - one type is pointer to qualified or unqualified void and the + // other is a pointer to a qualified or unqualified character type; + // - or, the type of the next argument is nullptr_t and type is a + // pointer type that has the same representation and alignment + // requirements as a pointer to a character type. + // Given that type compatibility is the primary requirement (ignoring + // qualifications), you would think we could call typesAreCompatible() + // directly to test this. However, in C++, that checks for *same type*, + // which causes false positives when passing an enumeration type to + // va_arg. Instead, get the underlying type of the enumeration and pass + // that. + QualType UnderlyingType = TInfo->getType(); + if (const auto *ET = UnderlyingType->getAs()) + UnderlyingType = ET->getDecl()->getIntegerType(); + if (Context.typesAreCompatible(PromoteType, UnderlyingType, + /*CompareUnqualified*/ true)) + PromoteType = QualType(); + + // If the types are still not compatible, we need to test whether the + // promoted type and the underlying type are the same except for + // signedness. Ask the AST for the correctly corresponding type and see + // if that's compatible. + if (!PromoteType.isNull() && !UnderlyingType->isBooleanType() && + PromoteType->isUnsignedIntegerType() != + UnderlyingType->isUnsignedIntegerType()) { + UnderlyingType = + UnderlyingType->isUnsignedIntegerType() + ? Context.getCorrespondingSignedType(UnderlyingType) + : Context.getCorrespondingUnsignedType(UnderlyingType); + if (Context.typesAreCompatible(PromoteType, UnderlyingType, + /*CompareUnqualified*/ true)) + PromoteType = QualType(); + } } - return BinaryOperator::Create(Context, LHSExpr, RHSExpr, Opc, ResultType, - VK_PRValue, OK_Ordinary, OpLoc, - CurFPFeatureOverrides()); + if (TInfo->getType()->isSpecificBuiltinType(BuiltinType::Float)) + PromoteType = Context.DoubleTy; + if (!PromoteType.isNull()) + DiagRuntimeBehavior(TInfo->getTypeLoc().getBeginLoc(), E, + PDiag(diag::warn_second_parameter_to_va_arg_never_compatible) + << TInfo->getType() + << PromoteType + << TInfo->getTypeLoc().getSourceRange()); } - // Build a built-in binary operation. - return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr); + QualType T = TInfo->getType().getNonLValueExprType(Context); + return new (Context) VAArgExpr(BuiltinLoc, E, TInfo, RPLoc, T, IsMS); } -static bool isOverflowingIntegerType(ASTContext &Ctx, QualType T) { - if (T.isNull() || T->isDependentType()) - return false; - - if (!Ctx.isPromotableIntegerType(T)) - return true; +ExprResult Sema::ActOnGNUNullExpr(SourceLocation TokenLoc) { + // The type of __null will be int or long, depending on the size of + // pointers on the target. + QualType Ty; + unsigned pw = Context.getTargetInfo().getPointerWidth(LangAS::Default); + if (pw == Context.getTargetInfo().getIntWidth()) + Ty = Context.IntTy; + else if (pw == Context.getTargetInfo().getLongWidth()) + Ty = Context.LongTy; + else if (pw == Context.getTargetInfo().getLongLongWidth()) + Ty = Context.LongLongTy; + else { + llvm_unreachable("I don't know size of pointer!"); + } - return Ctx.getIntWidth(T) >= Ctx.getIntWidth(Ctx.IntTy); + return new (Context) GNUNullExpr(Ty, TokenLoc); } -ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, - UnaryOperatorKind Opc, Expr *InputExpr, - bool IsAfterAmp) { - ExprResult Input = InputExpr; - ExprValueKind VK = VK_PRValue; - ExprObjectKind OK = OK_Ordinary; - QualType resultType; - bool CanOverflow = false; +static CXXRecordDecl *LookupStdSourceLocationImpl(Sema &S, SourceLocation Loc) { + CXXRecordDecl *ImplDecl = nullptr; - bool ConvertHalfVec = false; - if (getLangOpts().OpenCL) { - QualType Ty = InputExpr->getType(); - // The only legal unary operation for atomics is '&'. - if ((Opc != UO_AddrOf && Ty->isAtomicType()) || - // OpenCL special types - image, sampler, pipe, and blocks are to be used - // only with a builtin functions and therefore should be disallowed here. - (Ty->isImageType() || Ty->isSamplerT() || Ty->isPipeType() - || Ty->isBlockPointerType())) { - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << InputExpr->getType() - << Input.get()->getSourceRange()); + // Fetch the std::source_location::__impl decl. + if (NamespaceDecl *Std = S.getStdNamespace()) { + LookupResult ResultSL(S, &S.PP.getIdentifierTable().get("source_location"), + Loc, Sema::LookupOrdinaryName); + if (S.LookupQualifiedName(ResultSL, Std)) { + if (auto *SLDecl = ResultSL.getAsSingle()) { + LookupResult ResultImpl(S, &S.PP.getIdentifierTable().get("__impl"), + Loc, Sema::LookupOrdinaryName); + if ((SLDecl->isCompleteDefinition() || SLDecl->isBeingDefined()) && + S.LookupQualifiedName(ResultImpl, SLDecl)) { + ImplDecl = ResultImpl.getAsSingle(); + } + } } } - if (getLangOpts().HLSL && OpLoc.isValid()) { - if (Opc == UO_AddrOf) - return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 0); - if (Opc == UO_Deref) - return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 1); + if (!ImplDecl || !ImplDecl->isCompleteDefinition()) { + S.Diag(Loc, diag::err_std_source_location_impl_not_found); + return nullptr; } - if (InputExpr->isTypeDependent() && - InputExpr->getType()->isSpecificBuiltinType(BuiltinType::Dependent)) { - resultType = Context.DependentTy; - } else { - switch (Opc) { - case UO_PreInc: - case UO_PreDec: - case UO_PostInc: - case UO_PostDec: - resultType = - CheckIncrementDecrementOperand(*this, Input.get(), VK, OK, OpLoc, - Opc == UO_PreInc || Opc == UO_PostInc, - Opc == UO_PreInc || Opc == UO_PreDec); - CanOverflow = isOverflowingIntegerType(Context, resultType); - break; - case UO_AddrOf: - resultType = CheckAddressOfOperand(Input, OpLoc); - CheckAddressOfNoDeref(InputExpr); - RecordModifiableNonNullParam(*this, InputExpr); - break; - case UO_Deref: { - Input = DefaultFunctionArrayLvalueConversion(Input.get()); - if (Input.isInvalid()) - return ExprError(); - resultType = - CheckIndirectionOperand(*this, Input.get(), VK, OpLoc, IsAfterAmp); - break; - } - case UO_Plus: - case UO_Minus: - CanOverflow = Opc == UO_Minus && - isOverflowingIntegerType(Context, Input.get()->getType()); - Input = UsualUnaryConversions(Input.get()); - if (Input.isInvalid()) - return ExprError(); - // Unary plus and minus require promoting an operand of half vector to a - // float vector and truncating the result back to a half vector. For now, - // we do this only when HalfArgsAndReturns is set (that is, when the - // target is arm or arm64). - ConvertHalfVec = needsConversionOfHalfVec(true, Context, Input.get()); - - // If the operand is a half vector, promote it to a float vector. - if (ConvertHalfVec) - Input = convertVector(Input.get(), Context.FloatTy, *this); - resultType = Input.get()->getType(); - if (resultType->isArithmeticType()) // C99 6.5.3.3p1 - break; - else if (resultType->isVectorType() && - // The z vector extensions don't allow + or - with bool vectors. - (!Context.getLangOpts().ZVector || - resultType->castAs()->getVectorKind() != - VectorKind::AltiVecBool)) - break; - else if (resultType->isSveVLSBuiltinType()) // SVE vectors allow + and - - break; - else if (getLangOpts().CPlusPlus && // C++ [expr.unary.op]p6 - Opc == UO_Plus && resultType->isPointerType()) - break; + // Verify that __impl is a trivial struct type, with no base classes, and with + // only the four expected fields. + if (ImplDecl->isUnion() || !ImplDecl->isStandardLayout() || + ImplDecl->getNumBases() != 0) { + S.Diag(Loc, diag::err_std_source_location_impl_malformed); + return nullptr; + } - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); + unsigned Count = 0; + for (FieldDecl *F : ImplDecl->fields()) { + StringRef Name = F->getName(); - case UO_Not: // bitwise complement - Input = UsualUnaryConversions(Input.get()); - if (Input.isInvalid()) - return ExprError(); - resultType = Input.get()->getType(); - // C99 6.5.3.3p1. We allow complex int and float as a GCC extension. - if (resultType->isComplexType() || resultType->isComplexIntegerType()) - // C99 does not support '~' for complex conjugation. - Diag(OpLoc, diag::ext_integer_complement_complex) - << resultType << Input.get()->getSourceRange(); - else if (resultType->hasIntegerRepresentation()) + if (Name == "_M_file_name") { + if (F->getType() != + S.Context.getPointerType(S.Context.CharTy.withConst())) break; - else if (resultType->isExtVectorType() && Context.getLangOpts().OpenCL) { - // OpenCL v1.1 s6.3.f: The bitwise operator not (~) does not operate - // on vector float types. - QualType T = resultType->castAs()->getElementType(); - if (!T->isIntegerType()) - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } else { - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } - break; - - case UO_LNot: // logical negation - // Unlike +/-/~, integer promotions aren't done here (C99 6.5.3.3p5). - Input = DefaultFunctionArrayLvalueConversion(Input.get()); - if (Input.isInvalid()) - return ExprError(); - resultType = Input.get()->getType(); - - // Though we still have to promote half FP to float... - if (resultType->isHalfType() && !Context.getLangOpts().NativeHalfType) { - Input = ImpCastExprToType(Input.get(), Context.FloatTy, CK_FloatingCast) - .get(); - resultType = Context.FloatTy; - } - - // WebAsembly tables can't be used in unary expressions. - if (resultType->isPointerType() && - resultType->getPointeeType().isWebAssemblyReferenceType()) { - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } - - if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) { - // C99 6.5.3.3p1: ok, fallthrough; - if (Context.getLangOpts().CPlusPlus) { - // C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9: - // operand contextually converted to bool. - Input = ImpCastExprToType(Input.get(), Context.BoolTy, - ScalarTypeToBooleanCastKind(resultType)); - } else if (Context.getLangOpts().OpenCL && - Context.getLangOpts().OpenCLVersion < 120) { - // OpenCL v1.1 6.3.h: The logical operator not (!) does not - // operate on scalar float types. - if (!resultType->isIntegerType() && !resultType->isPointerType()) - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } - } else if (resultType->isExtVectorType()) { - if (Context.getLangOpts().OpenCL && - Context.getLangOpts().getOpenCLCompatibleVersion() < 120) { - // OpenCL v1.1 6.3.h: The logical operator not (!) does not - // operate on vector float types. - QualType T = resultType->castAs()->getElementType(); - if (!T->isIntegerType()) - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } - // Vector logical not returns the signed variant of the operand type. - resultType = GetSignedVectorType(resultType); + Count++; + } else if (Name == "_M_function_name") { + if (F->getType() != + S.Context.getPointerType(S.Context.CharTy.withConst())) break; - } else if (Context.getLangOpts().CPlusPlus && - resultType->isVectorType()) { - const VectorType *VTy = resultType->castAs(); - if (VTy->getVectorKind() != VectorKind::Generic) - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - - // Vector logical not returns the signed variant of the operand type. - resultType = GetSignedVectorType(resultType); + Count++; + } else if (Name == "_M_line") { + if (!F->getType()->isIntegerType()) break; - } else { - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } - - // LNot always has type int. C99 6.5.3.3p5. - // In C++, it's bool. C++ 5.3.1p8 - resultType = Context.getLogicalOperationType(); - break; - case UO_Real: - case UO_Imag: - resultType = CheckRealImagOperand(*this, Input, OpLoc, Opc == UO_Real); - // _Real maps ordinary l-values into ordinary l-values. _Imag maps - // ordinary complex l-values to ordinary l-values and all other values to - // r-values. - if (Input.isInvalid()) - return ExprError(); - if (Opc == UO_Real || Input.get()->getType()->isAnyComplexType()) { - if (Input.get()->isGLValue() && - Input.get()->getObjectKind() == OK_Ordinary) - VK = Input.get()->getValueKind(); - } else if (!getLangOpts().CPlusPlus) { - // In C, a volatile scalar is read by __imag. In C++, it is not. - Input = DefaultLvalueConversion(Input.get()); - } - break; - case UO_Extension: - resultType = Input.get()->getType(); - VK = Input.get()->getValueKind(); - OK = Input.get()->getObjectKind(); + Count++; + } else if (Name == "_M_column") { + if (!F->getType()->isIntegerType()) + break; + Count++; + } else { + Count = 100; // invalid break; - case UO_Coawait: - // It's unnecessary to represent the pass-through operator co_await in the - // AST; just return the input expression instead. - assert(!Input.get()->getType()->isDependentType() && - "the co_await expression must be non-dependant before " - "building operator co_await"); - return Input; } } - if (resultType.isNull() || Input.isInvalid()) - return ExprError(); - - // Check for array bounds violations in the operand of the UnaryOperator, - // except for the '*' and '&' operators that have to be handled specially - // by CheckArrayAccess (as there are special cases like &array[arraysize] - // that are explicitly defined as valid by the standard). - if (Opc != UO_AddrOf && Opc != UO_Deref) - CheckArrayAccess(Input.get()); + if (Count != 4) { + S.Diag(Loc, diag::err_std_source_location_impl_malformed); + return nullptr; + } - auto *UO = - UnaryOperator::Create(Context, Input.get(), Opc, resultType, VK, OK, - OpLoc, CanOverflow, CurFPFeatureOverrides()); + return ImplDecl; +} - if (Opc == UO_Deref && UO->getType()->hasAttr(attr::NoDeref) && - !isa(UO->getType().getDesugaredType(Context)) && - !isUnevaluatedContext()) - ExprEvalContexts.back().PossibleDerefs.insert(UO); +ExprResult Sema::ActOnSourceLocExpr(SourceLocIdentKind Kind, + SourceLocation BuiltinLoc, + SourceLocation RPLoc) { + QualType ResultTy; + switch (Kind) { + case SourceLocIdentKind::File: + case SourceLocIdentKind::FileName: + case SourceLocIdentKind::Function: + case SourceLocIdentKind::FuncSig: { + QualType ArrTy = Context.getStringLiteralArrayType(Context.CharTy, 0); + ResultTy = + Context.getPointerType(ArrTy->getAsArrayTypeUnsafe()->getElementType()); + break; + } + case SourceLocIdentKind::Line: + case SourceLocIdentKind::Column: + ResultTy = Context.UnsignedIntTy; + break; + case SourceLocIdentKind::SourceLocStruct: + if (!StdSourceLocationImplDecl) { + StdSourceLocationImplDecl = + LookupStdSourceLocationImpl(*this, BuiltinLoc); + if (!StdSourceLocationImplDecl) + return ExprError(); + } + ResultTy = Context.getPointerType( + Context.getRecordType(StdSourceLocationImplDecl).withConst()); + break; + } - // Convert the result back to a half vector. - if (ConvertHalfVec) - return convertVector(UO, Context.HalfTy, *this); - return UO; + return BuildSourceLocExpr(Kind, ResultTy, BuiltinLoc, RPLoc, CurContext); } -bool Sema::isQualifiedMemberAccess(Expr *E) { - if (DeclRefExpr *DRE = dyn_cast(E)) { - if (!DRE->getQualifier()) - return false; +ExprResult Sema::BuildSourceLocExpr(SourceLocIdentKind Kind, QualType ResultTy, + SourceLocation BuiltinLoc, + SourceLocation RPLoc, + DeclContext *ParentContext) { + return new (Context) + SourceLocExpr(Context, Kind, ResultTy, BuiltinLoc, RPLoc, ParentContext); +} - ValueDecl *VD = DRE->getDecl(); - if (!VD->isCXXClassMember()) - return false; +ExprResult Sema::ActOnEmbedExpr(SourceLocation EmbedKeywordLoc, + StringLiteral *BinaryData, StringRef FileName) { + EmbedDataStorage *Data = new (Context) EmbedDataStorage; + Data->BinaryData = BinaryData; + Data->FileName = FileName; + return new (Context) + EmbedExpr(Context, EmbedKeywordLoc, Data, /*NumOfElements=*/0, + Data->getDataElementCount()); +} - if (isa(VD) || isa(VD)) - return true; - if (CXXMethodDecl *Method = dyn_cast(VD)) - return Method->isImplicitObjectMemberFunction(); +// Emits the +// `diag::warn_bounds_safety_implicit_conv_single_to_explicit_indexable` diagnostic. +// +// If the `__single` expression is a `DeclRefExpr` then a pointer to the +// corresponding Decl is returned, otherwise a nullptr is returned. +static ValueDecl * +DiagnoseBoundsSafetyImplicitConversionFromSingleToExplicitIndexable( + QualType DstType, QualType SrcType, Expr *SrcExpr, + AssignmentAction ActionForDiag, QualType FirstType, + QualType SecondType, PartialDiagnostic &FDiag) { + // We emit that a type is `__indexable` or + // `__bidi_indexable` only when necesary. + // 0 - emit `__indexable` + // 1 - emit `__bidi_indexable` + // 2 - Don't emit (DiagDstTypeMaybeEmpty only) + int DiagDstType = 0; + int DiagDstTypeMaybeEmpty = 0; + + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *DstTypePtr = DstType->getUnqualifiedDesugaredType(); + const auto *DstPointerType = dyn_cast(DstTypePtr); + if (!DstPointerType) + llvm_unreachable("DstTypePtr should be a PointerType"); + + if (DstPointerType->isIndexable()) + DiagDstType = 0; + else if (DstPointerType->isBidiIndexable()) + DiagDstType = 1; + else + llvm_unreachable("DstPointerType has unexpected type"); + auto IsNestedPtrType = [](const Type *Ty) -> bool { + if (const auto PtrType = + dyn_cast(Ty->getUnqualifiedDesugaredType())) { + if (isa( + PtrType->getPointeeType()->getUnqualifiedDesugaredType())) + return true; + } return false; + }; + + if (IsNestedPtrType(DstPointerType)) { + // Nested Pointer. In this case explicitly state that it's + // an indexable type in the diagnostic to make this more obvious + // to the developer. + DiagDstTypeMaybeEmpty = DiagDstType; + } else { + // Don't emphasise the pointer type in the diagnostic to make + // it shorter. + DiagDstTypeMaybeEmpty = 2; + } + + // 0 - emit `__single` + // 1 - Don't emit + // Decide whether or not to emphaise that the pointer type is `__single`. + int DiagSrcTypeMaybeEmpty = IsNestedPtrType(SrcType.getTypePtr()) ? 0 : 1; + + FDiag << FirstType << SecondType << ActionForDiag << DiagDstTypeMaybeEmpty + << DiagSrcTypeMaybeEmpty << DiagDstType << SrcExpr->getSourceRange(); + + // Look at the __single pointer and see if it's a DeclRefExpr. In that case + // we can suggest to the developer to use `__counted_by`. + // TODO(dliew): We should also look for function calls that return `__single`. + // In that case we could suggest adding `__counted_by` to return type + // (rdar://91928583). + ValueDecl *SrcDecl = nullptr; + std::tie(SrcDecl, std::ignore) = shouldSuggestBoundsSafetyCountedBy(SrcExpr); + + // Decide whether or not to suggest using __counted_by. + if (SrcDecl) { + FDiag << 1 << SrcDecl->getQualifiedNameAsString(); + } else { + FDiag << 0; } + return SrcDecl; +} - if (UnresolvedLookupExpr *ULE = dyn_cast(E)) { - if (!ULE->getQualifier()) - return false; +static bool maybeDiagnoseAssignmentToFunction(Sema &S, QualType DstType, + const Expr *SrcExpr) { + if (!DstType->isFunctionPointerType() || + !SrcExpr->getType()->isFunctionType()) + return false; - for (NamedDecl *D : ULE->decls()) { - if (CXXMethodDecl *Method = dyn_cast(D)) { - if (Method->isImplicitObjectMemberFunction()) - return true; - } else { - // Overload set does not contain methods. - break; - } - } + auto *DRE = dyn_cast(SrcExpr->IgnoreParenImpCasts()); + if (!DRE) + return false; + auto *FD = dyn_cast(DRE->getDecl()); + if (!FD) return false; - } - return false; + return !S.checkAddressOfFunctionIsAvailable(FD, + /*Complain=*/true, + SrcExpr->getBeginLoc()); } -ExprResult Sema::BuildUnaryOp(Scope *S, SourceLocation OpLoc, - UnaryOperatorKind Opc, Expr *Input, - bool IsAfterAmp) { - // First things first: handle placeholders so that the - // overloaded-operator check considers the right type. - if (const BuiltinType *pty = Input->getType()->getAsPlaceholderType()) { - // Increment and decrement of pseudo-object references. - if (pty->getKind() == BuiltinType::PseudoObject && - UnaryOperator::isIncrementDecrementOp(Opc)) - return PseudoObject().checkIncDec(S, OpLoc, Opc, Input); +/* TO_UPSTREAM(BoundsSafety) ON*/ +static void TryFixAssigningSingleOpaquePtrToImplicitIndexablePtr( + const ValueDecl *Assignee, QualType DstType, QualType SrcType, Sema &S, + llvm::SmallVectorImpl> + &FixIts) { + // If a local bidi_indexable or global __bidi_indexable/__indexable pointer is + // being initialized from an opaque pointer suggest making it __single. This + // is the only sensible attribute because opaque types cannot be indexed. - // extension is always a builtin operator. - if (Opc == UO_Extension) - return CreateBuiltinUnaryOp(OpLoc, Opc, Input); + // Is the pointer attribute on the destination implicit? + bool IsAutoBoundPtr = DstType->hasAttr(attr::PtrAutoAttr); - // & gets special logic for several kinds of placeholder. - // The builtin code knows what to do. - if (Opc == UO_AddrOf && - (pty->getKind() == BuiltinType::Overload || - pty->getKind() == BuiltinType::UnknownAny || - pty->getKind() == BuiltinType::BoundMember)) - return CreateBuiltinUnaryOp(OpLoc, Opc, Input); + // TODO: rdar://114478465 + if (!IsAutoBoundPtr) + return; - // Anything else needs to be handled now. - ExprResult Result = CheckPlaceholderExpr(Input); - if (Result.isInvalid()) return ExprError(); - Input = Result.get(); - } + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *DstPointerType = DstType->getAs(); + assert(DstPointerType->isIndexable() || DstPointerType->isBidiIndexable()); + assert(SrcType->isSinglePointerType()); - if (getLangOpts().CPlusPlus && Input->getType()->isOverloadableType() && - UnaryOperator::getOverloadedOperator(Opc) != OO_None && - !(Opc == UO_AddrOf && isQualifiedMemberAccess(Input))) { - // Find all of the overloaded operators visible from this point. - UnresolvedSet<16> Functions; - OverloadedOperatorKind OverOp = UnaryOperator::getOverloadedOperator(Opc); - if (S && OverOp != OO_None) - LookupOverloadedOperatorName(OverOp, S, Functions); + // Check if source is a pointer to an opaque type. + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *SrcPointerType = SrcType->getAs(); + if (!SrcPointerType->getPointeeType()->isIncompleteType()) + return; - return CreateOverloadedUnaryOp(OpLoc, Opc, Functions, Input); + if (auto *AssignedVar = dyn_cast(Assignee)) { + // There may be multiple variable decls in the case of globals so we try + // to annotate all of them. + BoundsSafetyFixItUtils::CreateAnnotateAllPointerDeclsFixIts( + AssignedVar, "__single", S, FixIts); + } else if (auto *AssignedFieldDecl = dyn_cast(Assignee)) { + auto FixIt = BoundsSafetyFixItUtils::CreateAnnotatePointerDeclFixIt( + AssignedFieldDecl, "__single", S); + if (!FixIt.isNull()) + FixIts.emplace_back(std::make_tuple(FixIt, AssignedFieldDecl)); + } else { + llvm_unreachable("Unexpected ValueDecl sub type"); } - - return CreateBuiltinUnaryOp(OpLoc, Opc, Input, IsAfterAmp); } -ExprResult Sema::ActOnUnaryOp(Scope *S, SourceLocation OpLoc, tok::TokenKind Op, - Expr *Input, bool IsAfterAmp) { - return BuildUnaryOp(S, OpLoc, ConvertTokenKindToUnaryOpcode(Op), Input, - IsAfterAmp); -} +void Sema::TryFixAssigningNullTerminatedToImplicitBidiIndexablePtr( + const ValueDecl *Assignee, Expr *SrcExpr, QualType DstType, AssignmentAction Action) { + // If a bidi_indexable pointer is being assigned from a null + // terminated pointer suggest making the local __bidi_indexable + // __null_terminated. -ExprResult Sema::ActOnAddrLabel(SourceLocation OpLoc, SourceLocation LabLoc, - LabelDecl *TheDecl) { - TheDecl->markUsed(Context); - // Create the AST node. The address of a label always has type 'void*'. - auto *Res = new (Context) AddrLabelExpr( - OpLoc, LabLoc, TheDecl, Context.getPointerType(Context.VoidTy)); + // Is the pointer attribute on the destination implicit? + bool IsAutoBoundPtr = DstType->hasAttr(attr::PtrAutoAttr); + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *DstPointerType = DstType->getAs(); - if (getCurFunction()) - getCurFunction()->AddrLabels.push_back(Res); + if (!IsAutoBoundPtr || !DstPointerType->isBidiIndexable()) + return; - return Res; -} + const auto *SrcPointerType = + SrcExpr->IgnoreParenImpCasts()->getType()->getAs(); + if (!SrcPointerType->isValueTerminatedType()) + return; + if (!SrcPointerType->getTerminatorValue(getASTContext()).isZero()) + return; -void Sema::ActOnStartStmtExpr() { - PushExpressionEvaluationContext(ExprEvalContexts.back().Context); - // Make sure we diagnose jumping into a statement expression. - setFunctionHasBranchProtectedScope(); -} + // If the function return type is an implicit __bidi_indexable and the + // returned pointer is __null_terminated, suggest adding the attribute + // __null_terminated to the function return type. + if (Action == AssignmentAction::Returning) { + auto *Caller = getCurFunctionDecl(); + if (!Caller) + return; + auto FnLoc = Caller->getReturnTypeSourceRange(); + if (FnLoc.isInvalid()) + return; + for (auto *I : Caller->redecls()) { + if (!I->getReturnType()->hasAttr(attr::PtrAutoAttr)) + return; + } -void Sema::ActOnStmtExprError() { - // Note that function is also called by TreeTransform when leaving a - // StmtExpr scope without rebuilding anything. + auto RetLoc = FnLoc.getEnd().getLocWithOffset(1); + auto FixitDiag = Diag(RetLoc, diag::note_bounds_safety_consider_adding_to_return); + // FIXME: rdar://125936876 + for (auto *I : Caller->redecls()) { + auto IFnLoc = I->getNameInfo().getSourceRange(); + auto IRetLoc = IFnLoc.getBegin(); + auto FixIt = FixItHint::CreateInsertion(IRetLoc, "__null_terminated "); + FixitDiag << I->getDeclaredReturnType(); + FixitDiag << "__null_terminated"; + FixitDiag << I->getName().str(); + FixitDiag << FixIt; + } + return; + } - DiscardCleanupsInEvaluationContext(); - PopExpressionEvaluationContext(); -} -ExprResult Sema::ActOnStmtExpr(Scope *S, SourceLocation LPLoc, Stmt *SubStmt, - SourceLocation RPLoc) { - return BuildStmtExpr(LPLoc, SubStmt, RPLoc, getTemplateDepth(S)); -} + auto *AssignedVar = dyn_cast_or_null(Assignee); + if (!AssignedVar) + return; + auto FixIt = BoundsSafetyFixItUtils::CreateAnnotatePointerDeclFixIt( + AssignedVar, "__null_terminated", *this); + if (FixIt.isNull()) + return; -ExprResult Sema::BuildStmtExpr(SourceLocation LPLoc, Stmt *SubStmt, - SourceLocation RPLoc, unsigned TemplateDepth) { - assert(SubStmt && isa(SubStmt) && "Invalid action invocation!"); - CompoundStmt *Compound = cast(SubStmt); + // Emit a note about where the variable that a fix is suggested for is + // declared. + auto FixitDiag = Diag(AssignedVar->getInnerLocStart(), + diag::note_bounds_safety_consider_adding_to); + FixitDiag << AssignedVar->getName(); + FixitDiag << "__null_terminated"; + FixitDiag << FixIt; - if (hasAnyUnrecoverableErrorsInThisFunction()) - DiscardCleanupsInEvaluationContext(); - assert(!Cleanup.exprNeedsCleanups() && - "cleanups within StmtExpr not correctly bound!"); - PopExpressionEvaluationContext(); + // FIXME: rdar://122434039 +} - // FIXME: there are a variety of strange constraints to enforce here, for - // example, it is not possible to goto into a stmt expression apparently. - // More semantic analysis is needed. +void Sema::TryFixAssigningImplicitBidiIndexableToNullTerminatedPtr( + Expr *SrcExpr, QualType DstType) { - // If there are sub-stmts in the compound stmt, take the type of the last one - // as the type of the stmtexpr. - QualType Ty = Context.VoidTy; - bool StmtExprMayBindToTemp = false; - if (!Compound->body_empty()) { - // For GCC compatibility we get the last Stmt excluding trailing NullStmts. - if (const auto *LastStmt = - dyn_cast(Compound->getStmtExprResult())) { - if (const Expr *Value = LastStmt->getExprStmt()) { - StmtExprMayBindToTemp = true; - Ty = Value->getType(); - } - } - } + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *DstPointerType = DstType->getAs(); - // FIXME: Check that expression type is complete/non-abstract; statement - // expressions are not lvalues. - Expr *ResStmtExpr = - new (Context) StmtExpr(Compound, Ty, LPLoc, RPLoc, TemplateDepth); - if (StmtExprMayBindToTemp) - return MaybeBindToTemporary(ResStmtExpr); - return ResStmtExpr; -} + if (!DstPointerType || + !DstPointerType->getTerminatorValue(getASTContext()).isZero()) + return; + + auto IsLocalBidi = [](VarDecl *VD) { + if (!VD || !VD->isLocalVarDecl()) + return false; + auto SrcVarType = VD->getType(); + auto SrcPtr = SrcVarType->getAs(); + // We only support implicit __bidi_indexable to: + // 1. Make writing the fixit easier (it's purely additive). + // If there ways already an attribute in source code we'd need to + // remove it. + // 2. If the __bidi_indexable attribute is explicit someone probably + // added deliberately and that's a hint that we should respect their choice. + if (!SrcPtr || !SrcVarType->hasAttr(attr::PtrAutoAttr)) + return false; + if(!SrcPtr->isBidiIndexable()) + return false; + return true; + }; -ExprResult Sema::ActOnStmtExprResult(ExprResult ER) { - if (ER.isInvalid()) - return ExprError(); + auto EmitFixitDiag = [&](FixItHint FixIt, DeclaratorDecl *SrcVar) { + if (FixIt.isNull()) + return; + auto FixitDiag = Diag(SrcVar->getInnerLocStart(), + diag::note_bounds_safety_consider_adding_to); + FixitDiag << SrcVar->getName(); + FixitDiag << "__null_terminated"; + FixitDiag << FixIt; + }; - // Do function/array conversion on the last expression, but not - // lvalue-to-rvalue. However, initialize an unqualified type. - ER = DefaultFunctionArrayConversion(ER.get()); - if (ER.isInvalid()) - return ExprError(); - Expr *E = ER.get(); + // If a local bidi_indexable pointer is being assigned to a null + // terminated pointer suggest changing the local __bidi_indexable + // to be __null_terminated. + if (auto *SrcDRE = dyn_cast(SrcExpr->IgnoreParens())) { + auto *SrcVD = SrcDRE->getDecl(); + auto *SrcVar = dyn_cast(SrcVD); + if (!IsLocalBidi(SrcVar)) + return; + auto FixIt = BoundsSafetyFixItUtils::CreateAnnotatePointerDeclFixIt( + SrcVar, "__null_terminated", *this); + EmitFixitDiag(FixIt, SrcVar); + return; + } - if (E->isTypeDependent()) - return E; + // If a bidi_indexable const array in struct is being assigned to a null + // terminated pointer suggest changing the __bidi_indexable to be + // __null_terminated. + if (auto *SrcME = dyn_cast(SrcExpr->IgnoreParens())) { + auto *SrcVar = dyn_cast(SrcME->getMemberDecl()); + if (!SrcVar || !SrcVar->getType()->isConstantArrayType()) + return; + auto SrcArr = SrcVar->getTypeSourceInfo()->getTypeLoc() + .getAs(); + if (!SrcArr) + return; + auto FixIt = FixItHint::CreateInsertion(SrcArr.getLBracketLoc() + .getLocWithOffset(1), "__null_terminated "); + EmitFixitDiag(FixIt, SrcVar); + return; + } - // In ARC, if the final expression ends in a consume, splice - // the consume out and bind it later. In the alternate case - // (when dealing with a retainable type), the result - // initialization will create a produce. In both cases the - // result will be +1, and we'll need to balance that out with - // a bind. - auto *Cast = dyn_cast(E); - if (Cast && Cast->getCastKind() == CK_ARCConsumeObject) - return Cast->getSubExpr(); + if (auto *SrcCastExpr = dyn_cast(SrcExpr->IgnoreParens())) { + if (SrcCastExpr->getCastKind() != clang::CK_BitCast + && SrcCastExpr->getCastKind() != clang::CK_NoOp) + return; + auto *SrcSE = SrcCastExpr->getSubExpr(); + if (!SrcSE) + return; + auto *SrcD = dyn_cast(SrcSE->IgnoreCasts()); + if (!SrcD) + return; + auto *SrcVar = dyn_cast(SrcD->getDecl()); + if (!IsLocalBidi(SrcVar)) + return; + auto FixIt = BoundsSafetyFixItUtils::CreateAnnotatePointerDeclFixIt( + SrcVar, "__null_terminated", *this); + EmitFixitDiag(FixIt, SrcVar); + } - // FIXME: Provide a better location for the initialization. - return PerformCopyInitialization( - InitializedEntity::InitializeStmtExprResult( - E->getBeginLoc(), E->getType().getAtomicUnqualifiedType()), - SourceLocation(), E); + // FIXME: rdar://122434039 } -ExprResult Sema::BuildBuiltinOffsetOf(SourceLocation BuiltinLoc, - TypeSourceInfo *TInfo, - ArrayRef Components, - SourceLocation RParenLoc) { - QualType ArgTy = TInfo->getType(); - bool Dependent = ArgTy->isDependentType(); - SourceRange TypeRange = TInfo->getTypeLoc().getLocalSourceRange(); +void Sema::TryFixAssigningBidiIndexableExprToNullTerminated(Expr *SrcExpr, + QualType DstType) { + // If a source expression with __bidi_indexable pointer type + // is being assigned to something with a __null_terminated pointer + // type suggest the __unsafe_null_terminated_from_indexable + // builtin to allow the conversion. - // We must have at least one component that refers to the type, and the first - // one is known to be a field designator. Verify that the ArgTy represents - // a struct/union/class. - if (!Dependent && !ArgTy->isRecordType()) - return ExprError(Diag(BuiltinLoc, diag::err_offsetof_record_type) - << ArgTy << TypeRange); + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *DstPointerType = DstType->getAs(); - // Type must be complete per C99 7.17p3 because a declaring a variable - // with an incomplete type would be ill-formed. - if (!Dependent - && RequireCompleteType(BuiltinLoc, ArgTy, - diag::err_offsetof_incomplete_type, TypeRange)) - return ExprError(); + if (!DstPointerType || + !DstPointerType->getTerminatorValue(getASTContext()).isZero()) + return; - bool DidWarnAboutNonPOD = false; - QualType CurrentType = ArgTy; - SmallVector Comps; - SmallVector Exprs; - for (const OffsetOfComponent &OC : Components) { - if (OC.isBrackets) { - // Offset of an array sub-field. TODO: Should we allow vector elements? - if (!CurrentType->isDependentType()) { - const ArrayType *AT = Context.getAsArrayType(CurrentType); - if(!AT) - return ExprError(Diag(OC.LocEnd, diag::err_offsetof_array_type) - << CurrentType); - CurrentType = AT->getElementType(); - } else - CurrentType = Context.DependentTy; + QualType SrcType = SrcExpr->IgnoreParenImpCasts()->getType(); + if (!SrcType->isPointerTypeWithBounds() && !SrcType->isConstantArrayType()) + return; - ExprResult IdxRval = DefaultLvalueConversion(static_cast(OC.U.E)); - if (IdxRval.isInvalid()) - return ExprError(); - Expr *Idx = IdxRval.get(); + auto FixIt = FixItHint::CreateInsertion(SrcExpr->getBeginLoc(), "__unsafe_null_terminated_from_indexable("); + if (FixIt.isNull()) + return; - // The expression must be an integral expression. - // FIXME: An integral constant expression? - if (!Idx->isTypeDependent() && !Idx->isValueDependent() && - !Idx->getType()->isIntegerType()) - return ExprError( - Diag(Idx->getBeginLoc(), diag::err_typecheck_subscript_not_integer) - << Idx->getSourceRange()); + unsigned TkLen = Lexer::MeasureTokenLength(SrcExpr->getEndLoc(), + getSourceManager(), LangOpts); + { + auto FixitDiag = + Diag(SrcExpr->getBeginLoc(), + diag::note_fixit_unsafe_null_terminated_from_indexable); + FixitDiag << FixIt; - // Record this array index. - Comps.push_back(OffsetOfNode(OC.LocStart, Exprs.size(), OC.LocEnd)); - Exprs.push_back(Idx); - continue; - } + FixitDiag << FixItHint::CreateInsertion(SrcExpr->getEndLoc() + .getLocWithOffset(TkLen), ")"); + } - // Offset of a field. - if (CurrentType->isDependentType()) { - // We have the offset of a field, but we can't look into the dependent - // type. Just record the identifier of the field. - Comps.push_back(OffsetOfNode(OC.LocStart, OC.U.IdentInfo, OC.LocEnd)); - CurrentType = Context.DependentTy; - continue; - } + { + auto FixitDiag = + Diag(SrcExpr->getBeginLoc(), + diag::note_fixit_unsafe_null_terminated_from_indexable_ptr); + FixitDiag << FixIt; + FixitDiag << FixItHint::CreateInsertion(SrcExpr->getEndLoc().getLocWithOffset(TkLen), + ", <# pointer to null terminator #>)"); + } - // We need to have a complete type to look into. - if (RequireCompleteType(OC.LocStart, CurrentType, - diag::err_offsetof_incomplete_type)) - return ExprError(); + // FIXME: rdar://122434039 +} - // Look for the designated field. - const RecordType *RC = CurrentType->getAs(); - if (!RC) - return ExprError(Diag(OC.LocEnd, diag::err_offsetof_record_type) - << CurrentType); - RecordDecl *RD = RC->getDecl(); +void Sema::TryFixAssigningNullTerminatedToBidiIndexableExpr(Expr *SrcExpr, + QualType DstType) { - // C++ [lib.support.types]p5: - // The macro offsetof accepts a restricted set of type arguments in this - // International Standard. type shall be a POD structure or a POD union - // (clause 9). - // C++11 [support.types]p4: - // If type is not a standard-layout class (Clause 9), the results are - // undefined. - if (CXXRecordDecl *CRD = dyn_cast(RD)) { - bool IsSafe = LangOpts.CPlusPlus11? CRD->isStandardLayout() : CRD->isPOD(); - unsigned DiagID = - LangOpts.CPlusPlus11? diag::ext_offsetof_non_standardlayout_type - : diag::ext_offsetof_non_pod_type; + // Walk through `AttributedType` to get to the underlying `PointerType`. + const auto *DstPointerType = DstType->getAs(); + if(!DstPointerType->isPointerTypeWithBounds()) + return; - if (!IsSafe && !DidWarnAboutNonPOD && !isUnevaluatedContext()) { - Diag(BuiltinLoc, DiagID) - << SourceRange(Components[0].LocStart, OC.LocEnd) << CurrentType; - DidWarnAboutNonPOD = true; - } - } + const auto *SrcPointerType = + SrcExpr->IgnoreParenImpCasts()->getType()->getAs(); + if (!SrcPointerType || !SrcPointerType->isValueTerminatedType()) + return; - // Look for the field. - LookupResult R(*this, OC.U.IdentInfo, OC.LocStart, LookupMemberName); - LookupQualifiedName(R, RD); - FieldDecl *MemberDecl = R.getAsSingle(); - IndirectFieldDecl *IndirectMemberDecl = nullptr; - if (!MemberDecl) { - if ((IndirectMemberDecl = R.getAsSingle())) - MemberDecl = IndirectMemberDecl->getAnonField(); - } + if (!SrcPointerType->getTerminatorValue(getASTContext()).isZero()) + return; + auto EmitFixitDiag = [&](StringRef Code, unsigned DiagID, + int SelectFixitNote) { + assert(Code.ends_with("(")); + auto FixitDiag = Diag(SrcExpr->getBeginLoc(), DiagID) << SelectFixitNote; + auto FixIt = FixItHint::CreateInsertion(SrcExpr->getBeginLoc(), Code); + if (FixIt.isNull()) + return; + FixitDiag << FixIt; + unsigned TkLen = Lexer::MeasureTokenLength(SrcExpr->getEndLoc(), + getSourceManager(), LangOpts); + FixitDiag << FixItHint::CreateInsertion( + SrcExpr->getEndLoc().getLocWithOffset(TkLen), ")"); + }; + EmitFixitDiag("__null_terminated_to_indexable(", + diag::note_fixit_null_terminated_to_indexable, 0); + EmitFixitDiag("__unsafe_null_terminated_to_indexable(", + diag::note_fixit_null_terminated_to_indexable, 1); + + return; + // FIXME: rdar://122434039 +} + +static std::tuple +TryFixNestedBoundsSafetyPointerAttributeMismatch(Expr *SrcExpr, + QualType DstType, + ASTContext &Context, Sema &S) { + //  If taking the address of a local variable, suggest adding pointer + // attributes to match the destination. Only suggest if the source's + // pointer attributes were automatically set and the destination has + // internal bounds. + auto LocalPointerThatHasAddressTaken = [](Expr *E) -> VarDecl * { + auto *AddrOf = dyn_cast(E->IgnoreParenImpCasts()); + if (!AddrOf || AddrOf->getOpcode() != UO_AddrOf) + return nullptr; - if (!MemberDecl) { - // Lookup could be ambiguous when looking up a placeholder variable - // __builtin_offsetof(S, _). - // In that case we would already have emitted a diagnostic - if (!R.isAmbiguous()) - Diag(BuiltinLoc, diag::err_no_member) - << OC.U.IdentInfo << RD << SourceRange(OC.LocStart, OC.LocEnd); - return ExprError(); - } + auto *AddrOfOperand = + dyn_cast(AddrOf->getSubExpr()->IgnoreParenImpCasts()); + if (!AddrOfOperand) + return nullptr; - // C99 7.17p3: - // (If the specified member is a bit-field, the behavior is undefined.) - // - // We diagnose this as an error. - if (MemberDecl->isBitField()) { - Diag(OC.LocEnd, diag::err_offsetof_bitfield) - << MemberDecl->getDeclName() - << SourceRange(BuiltinLoc, RParenLoc); - Diag(MemberDecl->getLocation(), diag::note_bitfield_decl); - return ExprError(); - } + auto *VD = dyn_cast(AddrOfOperand->getDecl()); + if (!VD || !VD->hasLocalStorage()) + return nullptr; - RecordDecl *Parent = MemberDecl->getParent(); - if (IndirectMemberDecl) - Parent = cast(IndirectMemberDecl->getDeclContext()); + if (!VD->getType()->hasAttr(attr::PtrAutoAttr)) + return nullptr; - // If the member was found in a base class, introduce OffsetOfNodes for - // the base class indirections. - CXXBasePaths Paths; - if (IsDerivedFrom(OC.LocStart, CurrentType, Context.getTypeDeclType(Parent), - Paths)) { - if (Paths.getDetectedVirtual()) { - Diag(OC.LocEnd, diag::err_offsetof_field_of_virtual_base) - << MemberDecl->getDeclName() - << SourceRange(BuiltinLoc, RParenLoc); - return ExprError(); - } + return VD; + }; - CXXBasePath &Path = Paths.front(); - for (const CXXBasePathElement &B : Path) - Comps.push_back(OffsetOfNode(B.Base)); - } + auto NestedPointer = [](QualType QT) { + auto PPTy = QT->getAs(); + if (!PPTy) + return QualType(); - if (IndirectMemberDecl) { - for (auto *FI : IndirectMemberDecl->chain()) { - assert(isa(FI)); - Comps.push_back(OffsetOfNode(OC.LocStart, - cast(FI), OC.LocEnd)); - } - } else - Comps.push_back(OffsetOfNode(OC.LocStart, MemberDecl, OC.LocEnd)); + auto PQTy = PPTy->getPointeeType(); + if (PQTy->getAs()) + return QualType(); - CurrentType = MemberDecl->getType().getNonReferenceType(); - } + if (!PQTy->getAs()) + return QualType(); - return OffsetOfExpr::Create(Context, Context.getSizeType(), BuiltinLoc, TInfo, - Comps, Exprs, RParenLoc); + return PQTy; + }; + + auto *VD = LocalPointerThatHasAddressTaken(SrcExpr); + + if (!VD) + return std::make_tuple(FixItHint(), nullptr); + auto InnerTy = NestedPointer(DstType); + if (InnerTy.isNull()) + return std::make_tuple(FixItHint(), nullptr); + + auto InnerAttrs = InnerTy->getAs()->getPointerAttributes(); + auto Corrected = Context.getBoundsSafetyPointerType(VD->getType(), InnerAttrs); + auto PtrToCorrected = Context.getPointerType( + Corrected, DstType->getAs()->getPointerAttributes()); + auto AssignType = + S.CheckAssignmentConstraints(SourceLocation(), DstType, PtrToCorrected); + + if (AssignType != Sema::Compatible) + return std::make_tuple(FixItHint(), nullptr); + + bool NeedsSpaceAfterKeyword; + SourceLocation FixItLoc; + std::tie(FixItLoc, NeedsSpaceAfterKeyword) = + BoundsSafetyFixItUtils::FindPointerAttrInsertPoint( + VD->getTypeSourceInfo()->getTypeLoc(), S); + + if (FixItLoc.isInvalid()) + return std::make_tuple(FixItHint(), nullptr); + + StringRef Keyword; + if (InnerAttrs.isSingle()) + Keyword = "__single "; + else if (InnerAttrs.isIndexable()) + Keyword = "__indexable "; + else if (InnerAttrs.isBidiIndexable()) + Keyword = "__bidi_indexable "; + else if (InnerAttrs.isUnsafeIndexable()) + Keyword = "__unsafe_indexable "; + + if (Keyword.empty()) + return std::make_tuple(FixItHint(), nullptr); + + StringRef NoSpace = Keyword.drop_back(); + if (!NeedsSpaceAfterKeyword) + Keyword = NoSpace; + if (!S.PP.isMacroDefined(NoSpace)) + return std::make_tuple(FixItHint(), nullptr); + + return std::make_tuple(FixItHint::CreateInsertion(FixItLoc, Keyword), VD); +} + +static const ValueDecl *ReferencedValueDecl(const Expr *E) { + const DeclRefExpr *DRef = nullptr; + + if (auto *DRE = dyn_cast(E->IgnoreParenImpCasts())) { + DRef = DRE; + } else if (const auto *ME = dyn_cast(E)) { + return dyn_cast(ME->getMemberDecl()); + } else if (const auto *AE = + dyn_cast(E->IgnoreParenImpCasts())) { + const auto *BaseExpr = AE->getBase()->IgnoreParenImpCasts(); + if (const auto *ME = dyn_cast(BaseExpr)) + return dyn_cast(ME->getMemberDecl()); + DRef = dyn_cast(BaseExpr); + } + + if (!DRef) + return nullptr; + + return dyn_cast(DRef->getDecl()); } -ExprResult Sema::ActOnBuiltinOffsetOf(Scope *S, - SourceLocation BuiltinLoc, - SourceLocation TypeLoc, - ParsedType ParsedArgTy, - ArrayRef Components, - SourceLocation RParenLoc) { +void Sema::TryFixAssigningConstArrayToNullTerminated(const Expr *SrcExpr, + QualType SrcType, + QualType DstType) { + const auto *DstPointerType = DstType->getAs(); - TypeSourceInfo *ArgTInfo; - QualType ArgTy = GetTypeFromParser(ParsedArgTy, &ArgTInfo); - if (ArgTy.isNull()) - return ExprError(); + if (!DstPointerType || + !DstPointerType->getTerminatorValue(getASTContext()).isZero()) + return; - if (!ArgTInfo) - ArgTInfo = Context.getTrivialTypeSourceInfo(ArgTy, TypeLoc); + // Check if this is a constant sized array. + if (!SrcType->isConstantArrayType()) + return; - return BuildBuiltinOffsetOf(BuiltinLoc, ArgTInfo, Components, RParenLoc); -} + const auto *Decl = ReferencedValueDecl(SrcExpr); + if (!Decl) + return; -ExprResult Sema::ActOnChooseExpr(SourceLocation BuiltinLoc, - Expr *CondExpr, - Expr *LHSExpr, Expr *RHSExpr, - SourceLocation RPLoc) { - assert((CondExpr && LHSExpr && RHSExpr) && "Missing type argument(s)"); + const VarDecl *VD = dyn_cast(Decl); - ExprValueKind VK = VK_PRValue; - ExprObjectKind OK = OK_Ordinary; - QualType resType; - bool CondIsTrue = false; - if (CondExpr->isTypeDependent() || CondExpr->isValueDependent()) { - resType = Context.DependentTy; - } else { - // The conditional expression is required to be a constant expression. - llvm::APSInt condEval(32); - ExprResult CondICE = VerifyIntegerConstantExpression( - CondExpr, &condEval, diag::err_typecheck_choose_expr_requires_constant); - if (CondICE.isInvalid()) - return ExprError(); - CondExpr = CondICE.get(); - CondIsTrue = condEval.getZExtValue(); + if (!VD) + return; + + bool InitializerTerminatedWithNull = false; + + if (VD->hasInit()) { + const Expr *e = VD->getInit()->IgnoreParenImpCasts(); + if (const auto InitExpr = dyn_cast(e)) { + // Check if the intializer end with a null pointer constant. If yes, + // suggest adding appropriate attribute to the decl. + bool isInitExprEmpty = InitExpr->inits().empty(); + if (!isInitExprEmpty && + !InitExpr->inits().back()->isNullPointerConstant( + getASTContext(), Expr::NPC_ValueDependentIsNull)) + return; + InitializerTerminatedWithNull = true; + } else if (const auto StrLiteral = dyn_cast(e)) { + const auto *SrcArrayType = Context.getAsConstantArrayType(SrcType); + // The initializations via StringLiterals are always terminated by if the + // intializer length is smaller than the size of the array being + // initialized + if (SrcArrayType->getSize().ule(StrLiteral->getLength())) + return; + InitializerTerminatedWithNull = true; + } else + return; + } - // If the condition is > zero, then the AST type is the same as the LHSExpr. - Expr *ActiveExpr = CondIsTrue ? LHSExpr : RHSExpr; + SourceLocation FixItLoc = VD->getLocation(); + FixItHint NonTerminatedByToTerminatedByFixIt; + + // Suggest adding const qualifier to local arrays that are initialized with + // a null terminator. Adding const to such arrays allows them to be + // implicitly converted to a __null_terminated pointer. In all other cases + // implicit conversion is forbidden. This suggestion is only made for local + // arrays because suggesting it other locations (e.g. function parameters + // and global variables) has a higher chance of breaking compilation, + // especially for non bounds-safety code where adding const changes semantics + // but __null_terminated doesn't change semantics of non fbounds-safety code. + if (InitializerTerminatedWithNull && VD->hasLocalStorage() && + DstType->getPointeeType().isConstQualified()) { + FixItLoc = VD->getBeginLoc(); + NonTerminatedByToTerminatedByFixIt = + FixItHint::CreateInsertion(FixItLoc, "const "); + } else { + std::string Token = "__null_terminated"; + TypeLoc TL = VD->getTypeSourceInfo()->getTypeLoc(); + if (ArrayTypeLoc ATL = TL.getAs()) { + FixItLoc = ATL.getLBracketLoc().getLocWithOffset(1); + // Add space if there is a size expression + if (ATL.getSizeExpr()) + Token += " "; + } else if(SrcType->isTypedefNameType()) { + Token += " "; + } + NonTerminatedByToTerminatedByFixIt = + FixItHint::CreateInsertion(FixItLoc, Token); + } - resType = ActiveExpr->getType(); - VK = ActiveExpr->getValueKind(); - OK = ActiveExpr->getObjectKind(); - } + if (NonTerminatedByToTerminatedByFixIt.isNull()) + return; - return new (Context) ChooseExpr(BuiltinLoc, CondExpr, LHSExpr, RHSExpr, - resType, VK, OK, RPLoc, CondIsTrue); + auto FixitDiag = + Diag(VD->getBeginLoc(), diag::note_bounds_safety_consider_adding_to); + auto AddedAttr = + llvm::StringRef(NonTerminatedByToTerminatedByFixIt.CodeToInsert).trim(); + FixitDiag << VD->getName() << AddedAttr; + FixitDiag << NonTerminatedByToTerminatedByFixIt; } -//===----------------------------------------------------------------------===// -// Clang Extensions. -//===----------------------------------------------------------------------===// +void Sema::TryFixAssigningSinglePtrToNullTerminated(Expr *SrcExpr, + QualType SrcType, + QualType DstType) { + auto *VD = ReferencedValueDecl(SrcExpr); -void Sema::ActOnBlockStart(SourceLocation CaretLoc, Scope *CurScope) { - BlockDecl *Block = BlockDecl::Create(Context, CurContext, CaretLoc); + if (!VD) + return; - if (LangOpts.CPlusPlus) { - MangleNumberingContext *MCtx; - Decl *ManglingContextDecl; - std::tie(MCtx, ManglingContextDecl) = - getCurrentMangleNumberContext(Block->getDeclContext()); - if (MCtx) { - unsigned ManglingNumber = MCtx->getManglingNumber(Block); - Block->setBlockMangling(ManglingNumber, ManglingContextDecl); - } - } + auto IsReallySinglePointer = [](const QualType Ty) -> bool { + if (!Ty->isSinglePointerType()) + return false; - PushBlockScope(CurScope, Block); - CurContext->addDecl(Block); - if (CurScope) - PushDeclContext(CurScope, Block); - else - CurContext = Block; + if (Ty->isBoundsAttributedType() || Ty->isValueTerminatedType()) + return false; - getCurBlock()->HasImplicitReturnType = true; + return true; + }; - // Enter a new evaluation context to insulate the block from any - // cleanups from the enclosing full-expression. - PushExpressionEvaluationContext( - ExpressionEvaluationContext::PotentiallyEvaluated); + if (!IsReallySinglePointer(SrcType)) + return; + + // If the `__single` is explicit don't suggest a fix-it because + // the user manually added this attribute which is a hint that + // they know what they are doing. + if (!SrcType->hasAttr(attr::PtrAutoAttr)) + return; + + // TODO: This should be handled by + // BoundsSafetyFixItUtils::FindPointerAttrInsertPoint rdar://123659361 + SourceLocation FixItLoc; + + FixItHint NonTerminatedByToTerminatedByFixIt; + + if (auto *VTT = dyn_cast(DstType)) + if (!VTT->getPointeeType()->isAnyCharacterType() || + !VTT->getTerminatorValue(Context).isZero()) + return; + + // If the destination type is a pointer to a const qualified type, then + // suggest making the type pointed by SrcType const qualified. Otherwise, + // suggest qualifying SrcType with __null_terminated. + if (DstType->getPointeeType().isConstQualified()) { + FixItLoc = VD->getBeginLoc(); + NonTerminatedByToTerminatedByFixIt = + FixItHint::CreateInsertion(FixItLoc, "const "); + } else { + FixItLoc = VD->getLocation(); + NonTerminatedByToTerminatedByFixIt = + FixItHint::CreateInsertion(FixItLoc, "__null_terminated "); + } + + if (VD && !NonTerminatedByToTerminatedByFixIt.isNull()) { + auto FixitDiag = + Diag(VD->getBeginLoc(), diag::note_bounds_safety_consider_adding_to); + auto AddedAttr = + llvm::StringRef(NonTerminatedByToTerminatedByFixIt.CodeToInsert).trim(); + FixitDiag << VD->getName() << AddedAttr; + FixitDiag << NonTerminatedByToTerminatedByFixIt; + } } +/* TO_UPSTREAM(BoundsSafety) OFF*/ -void Sema::ActOnBlockArguments(SourceLocation CaretLoc, Declarator &ParamInfo, - Scope *CurScope) { - assert(ParamInfo.getIdentifier() == nullptr && - "block-id should have no identifier!"); - assert(ParamInfo.getContext() == DeclaratorContext::BlockLiteral); - BlockScopeInfo *CurBlock = getCurBlock(); +bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, + SourceLocation Loc, + QualType DstType, QualType SrcType, + Expr *SrcExpr, AssignmentAction Action, + bool *Complained, + // TO_UPSTREAM(BoundsSafety) + const ValueDecl *Assignee) { + if (Complained) + *Complained = false; - TypeSourceInfo *Sig = GetTypeForDeclarator(ParamInfo); - QualType T = Sig->getType(); - DiagnoseUnexpandedParameterPack(CaretLoc, Sig, UPPC_Block); + // Decode the result (notice that AST's are still created for extensions). + bool CheckInferredResultType = false; + bool isInvalid = false; + unsigned DiagKind = 0; + ConversionFixItGenerator ConvHints; + bool MayHaveConvFixit = false; + bool MayHaveFunctionDiff = false; + bool IsSrcNullTerm = false; + bool IsDstNullTerm = false; + const ObjCInterfaceDecl *IFace = nullptr; + const ObjCProtocolDecl *PDecl = nullptr; + /*TO_UPSTREAM(BoundsSafety) ON*/ + llvm::SmallVector, 1> + AssignedVarFixIts; + VarDecl *BoundsSafetyIncompatNestedPtrLocalVarToFix = nullptr; + FixItHint BoundsSafetyIncompatNestedPtrFixIt; + /*TO_UPSTREAM(BoundsSafety) OFF*/ - // GetTypeForDeclarator always produces a function type for a block - // literal signature. Furthermore, it is always a FunctionProtoType - // unless the function was written with a typedef. - assert(T->isFunctionType() && - "GetTypeForDeclarator made a non-function block signature"); - // Look for an explicit signature in that function type. - FunctionProtoTypeLoc ExplicitSignature; + switch (ConvTy) { + case Compatible: + DiagnoseAssignmentEnum(DstType, SrcType, SrcExpr); + return false; + case CompatibleVoidPtrToNonVoidPtr: + // Still a valid conversion, but we may want to diagnose for C++ + // compatibility reasons. + DiagKind = diag::warn_compatible_implicit_pointer_conv; + break; + case PointerToInt: + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_pointer_int; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_pointer_int; + } + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = true; + break; + /* TO_UPSTREAM(BoundsSafety) ON*/ + case IncompatibleIntToSafePointer: + DiagKind = diag::err_bounds_safety_non_to_pointer; + isInvalid = true; + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = true; + break; + case IncompatibleUnsafeToIndexablePointer: { + DiagKind = diag::err_bounds_safety_unsafe_to_safe; + isInvalid = true; + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = false; + // Fix up SrcType to show __unsafe_indexable + auto SrcPTy = SrcType->getAs(); + if (SrcPTy && !SrcPTy->isUnsafeIndexable()) { + SrcType = Context.getPointerType( + SrcPTy->getPointeeType(), + BoundsSafetyPointerAttributes::unsafeIndexable()); + } + break; + } + case IncompatibleUnsafeToSafePointer: { + DiagKind = diag::err_bounds_safety_unsafe_to_safe; + isInvalid = true; + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = false; + break; + } + case IncompleteSingleToIndexablePointer: { + DiagKind = diag::err_bounds_safety_incomplete_single_to_indexable; + isInvalid = true; + // Can these FixIts ever fire? + bool ConvFixitEmitted = + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = false; - if ((ExplicitSignature = Sig->getTypeLoc() - .getAsAdjusted())) { + // Guard with `!ConvFixitEmitted` because we probably don't want these + // FixIts interacting. + if (!ConvFixitEmitted && Assignee) { + TryFixAssigningSingleOpaquePtrToImplicitIndexablePtr( + Assignee, DstType, SrcType, *this, AssignedVarFixIts); + } + break; + } + case CompatibleSingleToExplicitIndexablePointer: { + DiagKind = diag::warn_bounds_safety_implicit_conv_single_to_explicit_indexable; + isInvalid = false; + MayHaveConvFixit = false; + break; + } + case IncompatibleStringLiteralToValueTerminatedPointer: + DiagKind = diag::err_bounds_safety_incompatible_string_literal_to_terminated_by; + isInvalid = true; + break; + case IncompatibleValueTerminatedTerminators: + DiagKind = diag::err_bounds_safety_incompatible_terminated_by_terminators; + isInvalid = true; + break; + case IncompatibleValueTerminatedToNonValueTerminatedPointer: { + const auto *SrcPointerType = SrcType->getAs(); + IsSrcNullTerm = + SrcPointerType->getTerminatorValue(getASTContext()).isZero(); + DiagKind = + diag::err_bounds_safety_incompatible_terminated_by_to_non_terminated_by; + isInvalid = true; + break; + } + case IncompatibleNonValueTerminatedToValueTerminatedPointer: { + const auto *DstPointerType = DstType->getAs(); + IsDstNullTerm = + DstPointerType->getTerminatorValue(getASTContext()).isZero(); - // Check whether that explicit signature was synthesized by - // GetTypeForDeclarator. If so, don't save that as part of the - // written signature. - if (ExplicitSignature.getLocalRangeBegin() == - ExplicitSignature.getLocalRangeEnd()) { - // This would be much cheaper if we stored TypeLocs instead of - // TypeSourceInfos. - TypeLoc Result = ExplicitSignature.getReturnLoc(); - unsigned Size = Result.getFullDataSize(); - Sig = Context.CreateTypeSourceInfo(Result.getType(), Size); - Sig->getTypeLoc().initializeFullCopy(Result, Size); + if (getLangOpts().BoundsSafetyRelaxedSystemHeaders || + isCXXSafeBuffersBoundsSafetyInteropEnabledAt(Loc)) { + DiagKind = diag:: + warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by; + isInvalid = false; + } else { + DiagKind = + diag::err_bounds_safety_incompatible_non_terminated_by_to_terminated_by; + isInvalid = true; + } + break; + } + case IncompatibleNestedValueTerminatedToNonValueTerminatedPointer: + DiagKind = diag:: + err_bounds_safety_incompatible_terminated_by_to_non_terminated_by_mismatch; + isInvalid = true; + break; + case IncompatibleNestedNonValueTerminatedToValueTerminatedPointer: + if (getLangOpts().BoundsSafetyRelaxedSystemHeaders) { + DiagKind = diag:: + warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch; + isInvalid = false; + } else { + DiagKind = diag:: + err_bounds_safety_incompatible_non_terminated_by_to_terminated_by_mismatch; + isInvalid = true; + } + break; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + case IntToPointer: + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_int_pointer; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_int_pointer; + } + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = true; + break; + case IncompatibleFunctionPointerStrict: + DiagKind = + diag::warn_typecheck_convert_incompatible_function_pointer_strict; + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = true; + break; + case IncompatibleFunctionPointer: + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_incompatible_function_pointer; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_incompatible_function_pointer; + } + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = true; + break; + case IncompatiblePointer: + if (Action == AssignmentAction::Passing_CFAudited) { + DiagKind = diag::err_arc_typecheck_convert_incompatible_pointer; + } else if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_incompatible_pointer; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_incompatible_pointer; + } + CheckInferredResultType = DstType->isObjCObjectPointerType() && + SrcType->isObjCObjectPointerType(); + if (CheckInferredResultType) { + SrcType = SrcType.getUnqualifiedType(); + DstType = DstType.getUnqualifiedType(); + } else { + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + } + MayHaveConvFixit = true; + break; + case IncompatiblePointerSign: + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_incompatible_pointer_sign; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_incompatible_pointer_sign; + } + break; + case FunctionVoidPointer: + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_pointer_void_func; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_pointer_void_func; + } + break; + case IncompatiblePointerDiscardsQualifiers: { + // Perform array-to-pointer decay if necessary. + if (SrcType->isArrayType()) SrcType = Context.getArrayDecayedType(SrcType); - ExplicitSignature = FunctionProtoTypeLoc(); + isInvalid = true; + + Qualifiers lhq = SrcType->getPointeeType().getQualifiers(); + Qualifiers rhq = DstType->getPointeeType().getQualifiers(); + if (lhq.getAddressSpace() != rhq.getAddressSpace()) { + DiagKind = diag::err_typecheck_incompatible_address_space; + break; + } else if (lhq.getPointerAuth() != rhq.getPointerAuth()) { + DiagKind = diag::err_typecheck_incompatible_ptrauth; + break; + } else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) { + DiagKind = diag::err_typecheck_incompatible_ownership; + break; + } else if (!lhq.getPointerAuth().isEquivalent(rhq.getPointerAuth())) { + DiagKind = diag::err_typecheck_incompatible_ptrauth; + break; + } + + llvm_unreachable("unknown error case for discarding qualifiers!"); + // fallthrough + } + case CompatiblePointerDiscardsQualifiers: + // If the qualifiers lost were because we were applying the + // (deprecated) C++ conversion from a string literal to a char* + // (or wchar_t*), then there was no error (C++ 4.2p2). FIXME: + // Ideally, this check would be performed in + // checkPointerTypesForAssignment. However, that would require a + // bit of refactoring (so that the second argument is an + // expression, rather than a type), which should be done as part + // of a larger effort to fix checkPointerTypesForAssignment for + // C++ semantics. + if (getLangOpts().CPlusPlus && + IsStringLiteralToNonConstPointerConversion(SrcExpr, DstType)) + return false; + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_typecheck_convert_discards_qualifiers; + isInvalid = true; + } else { + DiagKind = diag::ext_typecheck_convert_discards_qualifiers; } - } - - CurBlock->TheDecl->setSignatureAsWritten(Sig); - CurBlock->FunctionType = T; - const auto *Fn = T->castAs(); - QualType RetTy = Fn->getReturnType(); - bool isVariadic = - (isa(Fn) && cast(Fn)->isVariadic()); - - CurBlock->TheDecl->setIsVariadic(isVariadic); + break; + case IncompatibleNestedPointerQualifiers: + if (getLangOpts().CPlusPlus) { + isInvalid = true; + DiagKind = diag::err_nested_pointer_qualifier_mismatch; + } else { + DiagKind = diag::ext_nested_pointer_qualifier_mismatch; + } + break; + /* TO_UPSTREAM(BoundsSafety) ON*/ + case IncompatibleNestedBoundsSafetyPointerAttributes: { + DiagKind = diag::err_nested_bounds_safety_pointer_attribute_mismatch; + isInvalid = true; - // Context.DependentTy is used as a placeholder for a missing block - // return type. TODO: what should we do with declarators like: - // ^ * { ... } - // If the answer is "apply template argument deduction".... - if (RetTy != Context.DependentTy) { - CurBlock->ReturnType = RetTy; - CurBlock->TheDecl->setBlockMissingReturnType(false); - CurBlock->HasImplicitReturnType = false; + std::tie(BoundsSafetyIncompatNestedPtrFixIt, + BoundsSafetyIncompatNestedPtrLocalVarToFix) = + TryFixNestedBoundsSafetyPointerAttributeMismatch(SrcExpr, DstType, + Context, *this); + break; } - - // Push block parameters from the declarator if we had them. - SmallVector Params; - if (ExplicitSignature) { - for (unsigned I = 0, E = ExplicitSignature.getNumParams(); I != E; ++I) { - ParmVarDecl *Param = ExplicitSignature.getParam(I); - if (Param->getIdentifier() == nullptr && !Param->isImplicit() && - !Param->isInvalidDecl() && !getLangOpts().CPlusPlus) { - // Diagnose this as an extension in C17 and earlier. - if (!getLangOpts().C23) - Diag(Param->getLocation(), diag::ext_parameter_name_omitted_c23); + case IncompatibleBoundsSafetyFunctionPointer: + DiagKind = diag::err_incompatible_bounds_safety_function_pointer; + isInvalid = true; + break; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + case IncompatibleNestedPointerAddressSpaceMismatch: + DiagKind = diag::err_typecheck_incompatible_nested_address_space; + isInvalid = true; + break; + case IntToBlockPointer: + DiagKind = diag::err_int_to_block_pointer; + isInvalid = true; + break; + case IncompatibleBlockPointer: + DiagKind = diag::err_typecheck_convert_incompatible_block_pointer; + isInvalid = true; + break; + case IncompatibleObjCQualifiedId: { + if (SrcType->isObjCQualifiedIdType()) { + const ObjCObjectPointerType *srcOPT = + SrcType->castAs(); + for (auto *srcProto : srcOPT->quals()) { + PDecl = srcProto; + break; } - Params.push_back(Param); + if (const ObjCInterfaceType *IFaceT = + DstType->castAs()->getInterfaceType()) + IFace = IFaceT->getDecl(); } - - // Fake up parameter variables if we have a typedef, like - // ^ fntype { ... } - } else if (const FunctionProtoType *Fn = T->getAs()) { - for (const auto &I : Fn->param_types()) { - ParmVarDecl *Param = BuildParmVarDeclForTypedef( - CurBlock->TheDecl, ParamInfo.getBeginLoc(), I); - Params.push_back(Param); + else if (DstType->isObjCQualifiedIdType()) { + const ObjCObjectPointerType *dstOPT = + DstType->castAs(); + for (auto *dstProto : dstOPT->quals()) { + PDecl = dstProto; + break; + } + if (const ObjCInterfaceType *IFaceT = + SrcType->castAs()->getInterfaceType()) + IFace = IFaceT->getDecl(); } - } - - // Set the parameters on the block decl. - if (!Params.empty()) { - CurBlock->TheDecl->setParams(Params); - CheckParmsForFunctionDef(CurBlock->TheDecl->parameters(), - /*CheckParameterNames=*/false); - } - - // Finally we can process decl attributes. - ProcessDeclAttributes(CurScope, CurBlock->TheDecl, ParamInfo); - - // Put the parameter variables in scope. - for (auto *AI : CurBlock->TheDecl->parameters()) { - AI->setOwningFunction(CurBlock->TheDecl); - - // If this has an identifier, add it to the scope stack. - if (AI->getIdentifier()) { - CheckShadow(CurBlock->TheScope, AI); - - PushOnScopeChains(AI, CurBlock->TheScope); + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_incompatible_qualified_id; + isInvalid = true; + } else { + DiagKind = diag::warn_incompatible_qualified_id; } - - if (AI->isInvalidDecl()) - CurBlock->TheDecl->setInvalidDecl(); + break; } -} - -void Sema::ActOnBlockError(SourceLocation CaretLoc, Scope *CurScope) { - // Leave the expression-evaluation context. - DiscardCleanupsInEvaluationContext(); - PopExpressionEvaluationContext(); - - // Pop off CurBlock, handle nested blocks. - PopDeclContext(); - PopFunctionScopeInfo(); -} - -ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, - Stmt *Body, Scope *CurScope) { - // If blocks are disabled, emit an error. - if (!LangOpts.Blocks) - Diag(CaretLoc, diag::err_blocks_disable) << LangOpts.OpenCL; - - // Leave the expression-evaluation context. - if (hasAnyUnrecoverableErrorsInThisFunction()) - DiscardCleanupsInEvaluationContext(); - assert(!Cleanup.exprNeedsCleanups() && - "cleanups within block not correctly bound!"); - PopExpressionEvaluationContext(); - - BlockScopeInfo *BSI = cast(FunctionScopes.back()); - BlockDecl *BD = BSI->TheDecl; - - maybeAddDeclWithEffects(BD); - - if (BSI->HasImplicitReturnType) - deduceClosureReturnType(*BSI); - - QualType RetTy = Context.VoidTy; - if (!BSI->ReturnType.isNull()) - RetTy = BSI->ReturnType; - - bool NoReturn = BD->hasAttr(); - QualType BlockTy; - - // If the user wrote a function type in some form, try to use that. - if (!BSI->FunctionType.isNull()) { - const FunctionType *FTy = BSI->FunctionType->castAs(); - - FunctionType::ExtInfo Ext = FTy->getExtInfo(); - if (NoReturn && !Ext.getNoReturn()) Ext = Ext.withNoReturn(true); - - // Turn protoless block types into nullary block types. - if (isa(FTy)) { - FunctionProtoType::ExtProtoInfo EPI; - EPI.ExtInfo = Ext; - BlockTy = Context.getFunctionType(RetTy, {}, EPI); - - // Otherwise, if we don't need to change anything about the function type, - // preserve its sugar structure. - } else if (FTy->getReturnType() == RetTy && - (!NoReturn || FTy->getNoReturnAttr())) { - BlockTy = BSI->FunctionType; - - // Otherwise, make the minimal modifications to the function type. + case IncompatibleVectors: + if (getLangOpts().CPlusPlus) { + DiagKind = diag::err_incompatible_vectors; + isInvalid = true; } else { - const FunctionProtoType *FPT = cast(FTy); - FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); - EPI.TypeQuals = Qualifiers(); - EPI.ExtInfo = Ext; - BlockTy = Context.getFunctionType(RetTy, FPT->getParamTypes(), EPI); + DiagKind = diag::warn_incompatible_vectors; + } + break; + case IncompatibleObjCWeakRef: + DiagKind = diag::err_arc_weak_unavailable_assign; + isInvalid = true; + break; + case Incompatible: + if (maybeDiagnoseAssignmentToFunction(*this, DstType, SrcExpr)) { + if (Complained) + *Complained = true; + return true; } - // If we don't have a function type, just build one from nothing. - } else { - FunctionProtoType::ExtProtoInfo EPI; - EPI.ExtInfo = FunctionType::ExtInfo().withNoReturn(NoReturn); - BlockTy = Context.getFunctionType(RetTy, {}, EPI); + DiagKind = diag::err_typecheck_convert_incompatible; + ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); + MayHaveConvFixit = true; + isInvalid = true; + MayHaveFunctionDiff = true; + break; } - DiagnoseUnusedParameters(BD->parameters()); - BlockTy = Context.getBlockPointerType(BlockTy); + QualType FirstType, SecondType; + switch (Action) { + case AssignmentAction::Assigning: + case AssignmentAction::Initializing: + // The destination type comes first. + FirstType = DstType; + SecondType = SrcType; + break; - // If needed, diagnose invalid gotos and switches in the block. - if (getCurFunction()->NeedsScopeChecking() && - !PP.isCodeCompletionEnabled()) - DiagnoseInvalidJumps(cast(Body)); + case AssignmentAction::Returning: + case AssignmentAction::Passing: + case AssignmentAction::Passing_CFAudited: + case AssignmentAction::Converting: + case AssignmentAction::Sending: + case AssignmentAction::Casting: + // The source type comes first. + FirstType = SrcType; + SecondType = DstType; + break; + } - BD->setBody(cast(Body)); + /* TO_UPSTREAM(BoundsSafety) ON*/ + PartialDiagnostic FDiag = PDiag(DiagKind); + AssignmentAction ActionForDiag = Action; + if (Action == AssignmentAction::Passing_CFAudited) + ActionForDiag = AssignmentAction::Passing; - if (Body && getCurFunction()->HasPotentialAvailabilityViolations) - DiagnoseUnguardedAvailabilityViolations(BD); + ValueDecl *SrcDecl = nullptr; + if (DiagKind == diag::err_bounds_safety_incomplete_single_to_indexable) { + std::string AssigneeName(""); + if (Assignee) + AssigneeName = Assignee->getQualifiedNameAsString(); + FDiag << FirstType << SecondType << ActionForDiag << AssigneeName + << /* NonEmptyName */ (AssigneeName.size() > 0) + << SrcExpr->getSourceRange(); + } else if (DiagKind == + diag::warn_bounds_safety_implicit_conv_single_to_explicit_indexable) { + SrcDecl = DiagnoseBoundsSafetyImplicitConversionFromSingleToExplicitIndexable( + DstType, SrcType, SrcExpr, ActionForDiag, FirstType, SecondType, FDiag); + } else if ( + DiagKind == + diag::err_bounds_safety_incompatible_terminated_by_to_non_terminated_by) { + FDiag << FirstType << SecondType << ActionForDiag + << SrcExpr->getSourceRange() + << (IsSrcNullTerm ? /*null_terminated*/ 1 : /*terminated_by*/ 0); + } else if ( + DiagKind == + diag::err_bounds_safety_incompatible_non_terminated_by_to_terminated_by || + DiagKind == + diag:: + warn_bounds_safety_incompatible_non_terminated_by_to_terminated_by) { + FDiag << FirstType << SecondType << ActionForDiag + << SrcExpr->getSourceRange() + << (!isCXXSafeBuffersBoundsSafetyInteropEnabledAt(Loc) + ? (IsDstNullTerm ? /*null_terminated*/ 1 + : /*terminated_by*/ 0) + : 2 /* cut message irrelevant to that mode*/); + } else { + FDiag << FirstType << SecondType << ActionForDiag + << SrcExpr->getSourceRange(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - // Try to apply the named return value optimization. We have to check again - // if we can do this, though, because blocks keep return statements around - // to deduce an implicit return type. - if (getLangOpts().CPlusPlus && RetTy->isRecordType() && - !BD->isDependentContext()) - computeNRVO(Body, BSI); + if (DiagKind == diag::ext_typecheck_convert_incompatible_pointer_sign || + DiagKind == diag::err_typecheck_convert_incompatible_pointer_sign) { + auto isPlainChar = [](const clang::Type *Type) { + return Type->isSpecificBuiltinType(BuiltinType::Char_S) || + Type->isSpecificBuiltinType(BuiltinType::Char_U); + }; + FDiag << (isPlainChar(FirstType->getPointeeOrArrayElementType()) || + isPlainChar(SecondType->getPointeeOrArrayElementType())); + } - if (RetTy.hasNonTrivialToPrimitiveDestructCUnion() || - RetTy.hasNonTrivialToPrimitiveCopyCUnion()) - checkNonTrivialCUnion(RetTy, BD->getCaretLocation(), NTCUC_FunctionReturn, - NTCUK_Destruct|NTCUK_Copy); + // If we can fix the conversion, suggest the FixIts. + if (!ConvHints.isNull()) { + for (FixItHint &H : ConvHints.Hints) + FDiag << H; + } - PopDeclContext(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + // Add FixIts to the error diagnostic if FixIts for this VarDecl haven't been + // emitted before. + bool AssignedVarFixItGoesOnError = false; + if (Assignee) { + AssignedVarFixItGoesOnError = + !AssignedVarFixIts.empty() && + !BoundsSafetyFixItWasEmittedFor(cast(Assignee)); + if (AssignedVarFixItGoesOnError) { + bool AssignedVarFixItsContainedAssignee = false; + for (auto &HintPair : AssignedVarFixIts) { + auto *VarToFix = std::get(HintPair); + // The FixIts we receive might actually fix multiple variables + // so we have to check for each one if a FixIt was already emitted + if (!BoundsSafetyFixItWasEmittedFor(VarToFix)) { + FDiag << std::get(HintPair); + // Record that a FixIt was emitted so that if another diagnostic is + // emitted we avoid trying to fix it again. Applying the same FixIt + // multiple times (i.e. with `-Xclang -fixit`) will result in invalid + // code. + BoundsSafetyFixItWasEmittedFor(VarToFix, + /*Set=*/true); + } + if (VarToFix == Assignee) + AssignedVarFixItsContainedAssignee = true; + } + assert(AssignedVarFixItsContainedAssignee); + (void)AssignedVarFixItsContainedAssignee; + } + } - // Set the captured variables on the block. - SmallVector Captures; - for (Capture &Cap : BSI->Captures) { - if (Cap.isInvalid() || Cap.isThisCapture()) - continue; - // Cap.getVariable() is always a VarDecl because - // blocks cannot capture structured bindings or other ValueDecl kinds. - auto *Var = cast(Cap.getVariable()); - Expr *CopyExpr = nullptr; - if (getLangOpts().CPlusPlus && Cap.isCopyCapture()) { - if (const RecordType *Record = - Cap.getCaptureType()->getAs()) { - // The capture logic needs the destructor, so make sure we mark it. - // Usually this is unnecessary because most local variables have - // their destructors marked at declaration time, but parameters are - // an exception because it's technically only the call site that - // actually requires the destructor. - if (isa(Var)) - FinalizeVarWithDestructor(Var, Record); + // Add Fixits to the error diagnostic for a local var that can be fixed. + bool BoundsSafetyIncompatNestedPtrFixItGoesOnError = false; + if (BoundsSafetyIncompatNestedPtrLocalVarToFix) { + BoundsSafetyIncompatNestedPtrFixItGoesOnError = + !BoundsSafetyIncompatNestedPtrFixIt.isNull() && + !BoundsSafetyFixItWasEmittedFor( + BoundsSafetyIncompatNestedPtrLocalVarToFix); + if (BoundsSafetyIncompatNestedPtrFixItGoesOnError) { + FDiag << BoundsSafetyIncompatNestedPtrFixIt; + // Record that a FixIt was emitted so that if another diagnostic is + // emitted we avoid trying to fix it again. Applying the same FixIt + // multiple times (i.e. with `-Xclang -fixit`) will result in invalid + // code. + BoundsSafetyFixItWasEmittedFor(BoundsSafetyIncompatNestedPtrLocalVarToFix, + /*Set=*/true); + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ - // Enter a separate potentially-evaluated context while building block - // initializers to isolate their cleanups from those of the block - // itself. - // FIXME: Is this appropriate even when the block itself occurs in an - // unevaluated operand? - EnterExpressionEvaluationContext EvalContext( - *this, ExpressionEvaluationContext::PotentiallyEvaluated); + if (MayHaveConvFixit) { FDiag << (unsigned) (ConvHints.Kind); } - SourceLocation Loc = Cap.getLocation(); + if (MayHaveFunctionDiff) + HandleFunctionTypeMismatch(FDiag, SecondType, FirstType); - ExprResult Result = BuildDeclarationNameExpr( - CXXScopeSpec(), DeclarationNameInfo(Var->getDeclName(), Loc), Var); + Diag(Loc, FDiag); + if ((DiagKind == diag::warn_incompatible_qualified_id || + DiagKind == diag::err_incompatible_qualified_id) && + PDecl && IFace && !IFace->hasDefinition()) + Diag(IFace->getLocation(), diag::note_incomplete_class_and_qualified_id) + << IFace << PDecl; - // According to the blocks spec, the capture of a variable from - // the stack requires a const copy constructor. This is not true - // of the copy/move done to move a __block variable to the heap. - if (!Result.isInvalid() && - !Result.get()->getType().isConstQualified()) { - Result = ImpCastExprToType(Result.get(), - Result.get()->getType().withConst(), - CK_NoOp, VK_LValue); - } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (DiagKind == + diag::warn_bounds_safety_implicit_conv_single_to_explicit_indexable && + SrcDecl) { + // Note where declaration of __single pointer is declared. + Diag(SrcDecl->getBeginLoc(), diag::note_pointer_declared_here_quoted) + << SrcDecl->getQualifiedNameAsString() << SrcDecl->getSourceRange(); + + // TODO(dliew): We should note when the __single pointer is implicitly + // __single (rdar://91928812). + } + + if (DiagKind == diag::err_bounds_safety_incomplete_single_to_indexable && + Assignee) { + // Emit a note telling the user where the assigned pointer is declared. + const NamedDecl *AssignedVar = cast(Assignee); + std::string AssigneeName = AssignedVar->getQualifiedNameAsString(); + // We try to omit the note when the VarDecl being assigned to is + // a function parameter because the `diag::note_parameter_named_here` + // is already emitted for it. An exception is made if there are FixIts + // available that need to go on a note. + if (Action != AssignmentAction::Passing || + (Action == AssignmentAction::Passing && AssignedVarFixIts.size() > 0 && + !AssignedVarFixItGoesOnError)) { + auto NoteDiag = Diag(AssignedVar->getBeginLoc(), + AssigneeName.size() > 0 + ? diag::note_pointer_declared_here_quoted + : diag::note_unnamed_pointer_declared_here); + if (AssigneeName.size() > 0) + NoteDiag << AssigneeName; + + NoteDiag << AssignedVar->getSourceRange(); + + if (!AssignedVarFixItGoesOnError) { + // Emit the FixIts on a note instead because a diagnostic with a FixIt + // for the this VarDecl was already emitted on an error diagnostic. + // + // The are several reasons for emitting the FixIts on a note: + // + // 1. Using `-Xclang -fixit` will only apply the fix to VarDecl once + // because only the first error diagnostic has the FixIt attached + // directly to it. Applying it multiple times would have resulted in + // broken code. + // 2. In interactive use cases (e.g. in an IDE) if the user is not + // looking at the first error diagnostic then they can still apply the + // FixIt by applying the FixIt attached to the note that is attached to + // the error diagnostic. + for (auto &HintPair : AssignedVarFixIts) + NoteDiag << std::get(HintPair); + } + } + } - if (!Result.isInvalid()) { - Result = PerformCopyInitialization( - InitializedEntity::InitializeBlock(Var->getLocation(), - Cap.getCaptureType()), - Loc, Result.get()); - } + if (DiagKind == diag::err_nested_bounds_safety_pointer_attribute_mismatch) { + if (!BoundsSafetyIncompatNestedPtrFixIt.isNull()) { + // Emit a note about where the variable that a fix is suggested for is + // declared. + assert(BoundsSafetyIncompatNestedPtrLocalVarToFix); + auto FixitDiag = + Diag(BoundsSafetyIncompatNestedPtrLocalVarToFix->getInnerLocStart(), + diag::note_entity_declared_at); + FixitDiag << BoundsSafetyIncompatNestedPtrLocalVarToFix->getDeclName(); - // Build a full-expression copy expression if initialization - // succeeded and used a non-trivial constructor. Recover from - // errors by pretending that the copy isn't necessary. - if (!Result.isInvalid() && - !cast(Result.get())->getConstructor() - ->isTrivial()) { - Result = MaybeCreateExprWithCleanups(Result); - CopyExpr = Result.get(); - } + if (!BoundsSafetyIncompatNestedPtrFixItGoesOnError) { + // Emit the FixIts on a note instead because a diagnostic with a FixIt + // for the this VarDecl was already emitted on an error diagnostic. + FixitDiag << BoundsSafetyIncompatNestedPtrFixIt; } } + } - BlockDecl::Capture NewCap(Var, Cap.isBlockCapture(), Cap.isNested(), - CopyExpr); - Captures.push_back(NewCap); + if (IsSrcNullTerm) { + TryFixAssigningNullTerminatedToImplicitBidiIndexablePtr(Assignee, SrcExpr, + DstType, Action); + + TryFixAssigningNullTerminatedToBidiIndexableExpr(SrcExpr, DstType); } - BD->setCaptures(Context, Captures, BSI->CXXThisCaptureIndex != 0); - // Pop the block scope now but keep it alive to the end of this function. - AnalysisBasedWarnings::Policy WP = - AnalysisWarnings.getPolicyInEffectAt(Body->getEndLoc()); - PoppedFunctionScopePtr ScopeRAII = PopFunctionScopeInfo(&WP, BD, BlockTy); + if (IsDstNullTerm) { + TryFixAssigningImplicitBidiIndexableToNullTerminatedPtr(SrcExpr, DstType); - BlockExpr *Result = new (Context) - BlockExpr(BD, BlockTy, BSI->ContainsUnexpandedParameterPack); + TryFixAssigningBidiIndexableExprToNullTerminated(SrcExpr, DstType); - // If the block isn't obviously global, i.e. it captures anything at - // all, then we need to do a few things in the surrounding context: - if (Result->getBlockDecl()->hasCaptures()) { - // First, this expression has a new cleanup object. - ExprCleanupObjects.push_back(Result->getBlockDecl()); - Cleanup.setExprNeedsCleanups(true); + TryFixAssigningSinglePtrToNullTerminated(SrcExpr, SrcType, DstType); - // It also gets a branch-protected scope if any of the captured - // variables needs destruction. - for (const auto &CI : Result->getBlockDecl()->captures()) { - const VarDecl *var = CI.getVariable(); - if (var->getType().isDestructedType() != QualType::DK_none) { - setFunctionHasBranchProtectedScope(); - break; - } - } + TryFixAssigningConstArrayToNullTerminated(SrcExpr, SrcType, DstType); } + /* TO_UPSTREAM(BoundsSafety) OFF*/ - if (getCurFunction()) - getCurFunction()->addBlock(BD); + if (SecondType == Context.OverloadTy) + NoteAllOverloadCandidates(OverloadExpr::find(SrcExpr).Expression, + FirstType, /*TakingAddress=*/true); - // This can happen if the block's return type is deduced, but - // the return expression is invalid. - if (BD->isInvalidDecl()) - return CreateRecoveryExpr(Result->getBeginLoc(), Result->getEndLoc(), - {Result}, Result->getType()); - return Result; + if (CheckInferredResultType) + ObjC().EmitRelatedResultTypeNote(SrcExpr); + + if (Action == AssignmentAction::Returning && ConvTy == IncompatiblePointer) + ObjC().EmitRelatedResultTypeNoteForReturn(DstType); + + if (Complained) + *Complained = true; + return isInvalid; } -ExprResult Sema::ActOnVAArg(SourceLocation BuiltinLoc, Expr *E, ParsedType Ty, - SourceLocation RPLoc) { - TypeSourceInfo *TInfo; - GetTypeFromParser(Ty, &TInfo); - return BuildVAArgExpr(BuiltinLoc, E, TInfo, RPLoc); +ExprResult Sema::VerifyIntegerConstantExpression(Expr *E, + llvm::APSInt *Result, + AllowFoldKind CanFold) { + class SimpleICEDiagnoser : public VerifyICEDiagnoser { + public: + SemaDiagnosticBuilder diagnoseNotICEType(Sema &S, SourceLocation Loc, + QualType T) override { + return S.Diag(Loc, diag::err_ice_not_integral) + << T << S.LangOpts.CPlusPlus; + } + SemaDiagnosticBuilder diagnoseNotICE(Sema &S, SourceLocation Loc) override { + return S.Diag(Loc, diag::err_expr_not_ice) << S.LangOpts.CPlusPlus; + } + } Diagnoser; + + return VerifyIntegerConstantExpression(E, Result, Diagnoser, CanFold); } -ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, - Expr *E, TypeSourceInfo *TInfo, - SourceLocation RPLoc) { - Expr *OrigExpr = E; - bool IsMS = false; +ExprResult Sema::VerifyIntegerConstantExpression(Expr *E, + llvm::APSInt *Result, + unsigned DiagID, + AllowFoldKind CanFold) { + class IDDiagnoser : public VerifyICEDiagnoser { + unsigned DiagID; - // CUDA device code does not support varargs. - if (getLangOpts().CUDA && getLangOpts().CUDAIsDevice) { - if (const FunctionDecl *F = dyn_cast(CurContext)) { - CUDAFunctionTarget T = CUDA().IdentifyTarget(F); - if (T == CUDAFunctionTarget::Global || T == CUDAFunctionTarget::Device || - T == CUDAFunctionTarget::HostDevice) - return ExprError(Diag(E->getBeginLoc(), diag::err_va_arg_in_device)); + public: + IDDiagnoser(unsigned DiagID) + : VerifyICEDiagnoser(DiagID == 0), DiagID(DiagID) { } + + SemaDiagnosticBuilder diagnoseNotICE(Sema &S, SourceLocation Loc) override { + return S.Diag(Loc, DiagID); } - } + } Diagnoser(DiagID); - // NVPTX does not support va_arg expression. - if (getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice && - Context.getTargetInfo().getTriple().isNVPTX()) - targetDiag(E->getBeginLoc(), diag::err_va_arg_in_device); + return VerifyIntegerConstantExpression(E, Result, Diagnoser, CanFold); +} - // It might be a __builtin_ms_va_list. (But don't ever mark a va_arg() - // as Microsoft ABI on an actual Microsoft platform, where - // __builtin_ms_va_list and __builtin_va_list are the same.) - if (!E->isTypeDependent() && Context.getTargetInfo().hasBuiltinMSVaList() && - Context.getTargetInfo().getBuiltinVaListKind() != TargetInfo::CharPtrBuiltinVaList) { - QualType MSVaListType = Context.getBuiltinMSVaListType(); - if (Context.hasSameType(MSVaListType, E->getType())) { - if (CheckForModifiableLvalue(E, BuiltinLoc, *this)) - return ExprError(); - IsMS = true; - } - } +Sema::SemaDiagnosticBuilder +Sema::VerifyICEDiagnoser::diagnoseNotICEType(Sema &S, SourceLocation Loc, + QualType T) { + return diagnoseNotICE(S, Loc); +} - // Get the va_list type - QualType VaListType = Context.getBuiltinVaListType(); - if (!IsMS) { - if (VaListType->isArrayType()) { - // Deal with implicit array decay; for example, on x86-64, - // va_list is an array, but it's supposed to decay to - // a pointer for va_arg. - VaListType = Context.getArrayDecayedType(VaListType); - // Make sure the input expression also decays appropriately. - ExprResult Result = UsualUnaryConversions(E); - if (Result.isInvalid()) - return ExprError(); - E = Result.get(); - } else if (VaListType->isRecordType() && getLangOpts().CPlusPlus) { - // If va_list is a record type and we are compiling in C++ mode, - // check the argument using reference binding. - InitializedEntity Entity = InitializedEntity::InitializeParameter( - Context, Context.getLValueReferenceType(VaListType), false); - ExprResult Init = PerformCopyInitialization(Entity, SourceLocation(), E); - if (Init.isInvalid()) - return ExprError(); - E = Init.getAs(); - } else { - // Otherwise, the va_list argument must be an l-value because - // it is modified by va_arg. - if (!E->isTypeDependent() && - CheckForModifiableLvalue(E, BuiltinLoc, *this)) - return ExprError(); - } - } +Sema::SemaDiagnosticBuilder +Sema::VerifyICEDiagnoser::diagnoseFold(Sema &S, SourceLocation Loc) { + return S.Diag(Loc, diag::ext_expr_not_ice) << S.LangOpts.CPlusPlus; +} + +ExprResult +Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result, + VerifyICEDiagnoser &Diagnoser, + AllowFoldKind CanFold) { + SourceLocation DiagLoc = E->getBeginLoc(); + + if (getLangOpts().CPlusPlus11) { + // C++11 [expr.const]p5: + // If an expression of literal class type is used in a context where an + // integral constant expression is required, then that class type shall + // have a single non-explicit conversion function to an integral or + // unscoped enumeration type + ExprResult Converted; + class CXX11ConvertDiagnoser : public ICEConvertDiagnoser { + VerifyICEDiagnoser &BaseDiagnoser; + public: + CXX11ConvertDiagnoser(VerifyICEDiagnoser &BaseDiagnoser) + : ICEConvertDiagnoser(/*AllowScopedEnumerations*/ false, + BaseDiagnoser.Suppress, true), + BaseDiagnoser(BaseDiagnoser) {} - if (!IsMS && !E->isTypeDependent() && - !Context.hasSameType(VaListType, E->getType())) - return ExprError( - Diag(E->getBeginLoc(), - diag::err_first_argument_to_va_arg_not_of_type_va_list) - << OrigExpr->getType() << E->getSourceRange()); + SemaDiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc, + QualType T) override { + return BaseDiagnoser.diagnoseNotICEType(S, Loc, T); + } - if (!TInfo->getType()->isDependentType()) { - if (RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), TInfo->getType(), - diag::err_second_parameter_to_va_arg_incomplete, - TInfo->getTypeLoc())) - return ExprError(); + SemaDiagnosticBuilder diagnoseIncomplete( + Sema &S, SourceLocation Loc, QualType T) override { + return S.Diag(Loc, diag::err_ice_incomplete_type) << T; + } - if (RequireNonAbstractType(TInfo->getTypeLoc().getBeginLoc(), - TInfo->getType(), - diag::err_second_parameter_to_va_arg_abstract, - TInfo->getTypeLoc())) - return ExprError(); + SemaDiagnosticBuilder diagnoseExplicitConv( + Sema &S, SourceLocation Loc, QualType T, QualType ConvTy) override { + return S.Diag(Loc, diag::err_ice_explicit_conversion) << T << ConvTy; + } - if (!TInfo->getType().isPODType(Context)) { - Diag(TInfo->getTypeLoc().getBeginLoc(), - TInfo->getType()->isObjCLifetimeType() - ? diag::warn_second_parameter_to_va_arg_ownership_qualified - : diag::warn_second_parameter_to_va_arg_not_pod) - << TInfo->getType() - << TInfo->getTypeLoc().getSourceRange(); - } + SemaDiagnosticBuilder noteExplicitConv( + Sema &S, CXXConversionDecl *Conv, QualType ConvTy) override { + return S.Diag(Conv->getLocation(), diag::note_ice_conversion_here) + << ConvTy->isEnumeralType() << ConvTy; + } - if (TInfo->getType()->isArrayType()) { - DiagRuntimeBehavior(TInfo->getTypeLoc().getBeginLoc(), E, - PDiag(diag::warn_second_parameter_to_va_arg_array) - << TInfo->getType() - << TInfo->getTypeLoc().getSourceRange()); - } + SemaDiagnosticBuilder diagnoseAmbiguous( + Sema &S, SourceLocation Loc, QualType T) override { + return S.Diag(Loc, diag::err_ice_ambiguous_conversion) << T; + } - // Check for va_arg where arguments of the given type will be promoted - // (i.e. this va_arg is guaranteed to have undefined behavior). - QualType PromoteType; - if (Context.isPromotableIntegerType(TInfo->getType())) { - PromoteType = Context.getPromotedIntegerType(TInfo->getType()); - // [cstdarg.syn]p1 defers the C++ behavior to what the C standard says, - // and C23 7.16.1.1p2 says, in part: - // If type is not compatible with the type of the actual next argument - // (as promoted according to the default argument promotions), the - // behavior is undefined, except for the following cases: - // - both types are pointers to qualified or unqualified versions of - // compatible types; - // - one type is compatible with a signed integer type, the other - // type is compatible with the corresponding unsigned integer type, - // and the value is representable in both types; - // - one type is pointer to qualified or unqualified void and the - // other is a pointer to a qualified or unqualified character type; - // - or, the type of the next argument is nullptr_t and type is a - // pointer type that has the same representation and alignment - // requirements as a pointer to a character type. - // Given that type compatibility is the primary requirement (ignoring - // qualifications), you would think we could call typesAreCompatible() - // directly to test this. However, in C++, that checks for *same type*, - // which causes false positives when passing an enumeration type to - // va_arg. Instead, get the underlying type of the enumeration and pass - // that. - QualType UnderlyingType = TInfo->getType(); - if (const auto *ET = UnderlyingType->getAs()) - UnderlyingType = ET->getDecl()->getIntegerType(); - if (Context.typesAreCompatible(PromoteType, UnderlyingType, - /*CompareUnqualified*/ true)) - PromoteType = QualType(); + SemaDiagnosticBuilder noteAmbiguous( + Sema &S, CXXConversionDecl *Conv, QualType ConvTy) override { + return S.Diag(Conv->getLocation(), diag::note_ice_conversion_here) + << ConvTy->isEnumeralType() << ConvTy; + } - // If the types are still not compatible, we need to test whether the - // promoted type and the underlying type are the same except for - // signedness. Ask the AST for the correctly corresponding type and see - // if that's compatible. - if (!PromoteType.isNull() && !UnderlyingType->isBooleanType() && - PromoteType->isUnsignedIntegerType() != - UnderlyingType->isUnsignedIntegerType()) { - UnderlyingType = - UnderlyingType->isUnsignedIntegerType() - ? Context.getCorrespondingSignedType(UnderlyingType) - : Context.getCorrespondingUnsignedType(UnderlyingType); - if (Context.typesAreCompatible(PromoteType, UnderlyingType, - /*CompareUnqualified*/ true)) - PromoteType = QualType(); + SemaDiagnosticBuilder diagnoseConversion( + Sema &S, SourceLocation Loc, QualType T, QualType ConvTy) override { + llvm_unreachable("conversion functions are permitted"); } - } - if (TInfo->getType()->isSpecificBuiltinType(BuiltinType::Float)) - PromoteType = Context.DoubleTy; - if (!PromoteType.isNull()) - DiagRuntimeBehavior(TInfo->getTypeLoc().getBeginLoc(), E, - PDiag(diag::warn_second_parameter_to_va_arg_never_compatible) - << TInfo->getType() - << PromoteType - << TInfo->getTypeLoc().getSourceRange()); + } ConvertDiagnoser(Diagnoser); + + Converted = PerformContextualImplicitConversion(DiagLoc, E, + ConvertDiagnoser); + if (Converted.isInvalid()) + return Converted; + E = Converted.get(); + // The 'explicit' case causes us to get a RecoveryExpr. Give up here so we + // don't try to evaluate it later. We also don't want to return the + // RecoveryExpr here, as it results in this call succeeding, thus callers of + // this function will attempt to use 'Value'. + if (isa(E)) + return ExprError(); + if (!E->getType()->isIntegralOrUnscopedEnumerationType()) + return ExprError(); + } else if (!E->getType()->isIntegralOrUnscopedEnumerationType()) { + // An ICE must be of integral or unscoped enumeration type. + if (!Diagnoser.Suppress) + Diagnoser.diagnoseNotICEType(*this, DiagLoc, E->getType()) + << E->getSourceRange(); + return ExprError(); } - QualType T = TInfo->getType().getNonLValueExprType(Context); - return new (Context) VAArgExpr(BuiltinLoc, E, TInfo, RPLoc, T, IsMS); -} + ExprResult RValueExpr = DefaultLvalueConversion(E); + if (RValueExpr.isInvalid()) + return ExprError(); -ExprResult Sema::ActOnGNUNullExpr(SourceLocation TokenLoc) { - // The type of __null will be int or long, depending on the size of - // pointers on the target. - QualType Ty; - unsigned pw = Context.getTargetInfo().getPointerWidth(LangAS::Default); - if (pw == Context.getTargetInfo().getIntWidth()) - Ty = Context.IntTy; - else if (pw == Context.getTargetInfo().getLongWidth()) - Ty = Context.LongTy; - else if (pw == Context.getTargetInfo().getLongLongWidth()) - Ty = Context.LongLongTy; - else { - llvm_unreachable("I don't know size of pointer!"); - } + E = RValueExpr.get(); - return new (Context) GNUNullExpr(Ty, TokenLoc); -} + // Circumvent ICE checking in C++11 to avoid evaluating the expression twice + // in the non-ICE case. + if (!getLangOpts().CPlusPlus11 && E->isIntegerConstantExpr(Context)) { + SmallVector Notes; + if (Result) + *Result = E->EvaluateKnownConstIntCheckOverflow(Context, &Notes); + if (!isa(E)) + E = Result ? ConstantExpr::Create(Context, E, APValue(*Result)) + : ConstantExpr::Create(Context, E); -static CXXRecordDecl *LookupStdSourceLocationImpl(Sema &S, SourceLocation Loc) { - CXXRecordDecl *ImplDecl = nullptr; + if (Notes.empty()) + return E; - // Fetch the std::source_location::__impl decl. - if (NamespaceDecl *Std = S.getStdNamespace()) { - LookupResult ResultSL(S, &S.PP.getIdentifierTable().get("source_location"), - Loc, Sema::LookupOrdinaryName); - if (S.LookupQualifiedName(ResultSL, Std)) { - if (auto *SLDecl = ResultSL.getAsSingle()) { - LookupResult ResultImpl(S, &S.PP.getIdentifierTable().get("__impl"), - Loc, Sema::LookupOrdinaryName); - if ((SLDecl->isCompleteDefinition() || SLDecl->isBeingDefined()) && - S.LookupQualifiedName(ResultImpl, SLDecl)) { - ImplDecl = ResultImpl.getAsSingle(); - } + // If our only note is the usual "invalid subexpression" note, just point + // the caret at its location rather than producing an essentially + // redundant note. + if (Notes.size() == 1 && Notes[0].second.getDiagID() == + diag::note_invalid_subexpr_in_const_expr) { + DiagLoc = Notes[0].first; + Notes.clear(); + } + + if (getLangOpts().CPlusPlus) { + if (!Diagnoser.Suppress) { + Diagnoser.diagnoseNotICE(*this, DiagLoc) << E->getSourceRange(); + for (const PartialDiagnosticAt &Note : Notes) + Diag(Note.first, Note.second); } + return ExprError(); } - } - if (!ImplDecl || !ImplDecl->isCompleteDefinition()) { - S.Diag(Loc, diag::err_std_source_location_impl_not_found); - return nullptr; - } + Diagnoser.diagnoseFold(*this, DiagLoc) << E->getSourceRange(); + for (const PartialDiagnosticAt &Note : Notes) + Diag(Note.first, Note.second); - // Verify that __impl is a trivial struct type, with no base classes, and with - // only the four expected fields. - if (ImplDecl->isUnion() || !ImplDecl->isStandardLayout() || - ImplDecl->getNumBases() != 0) { - S.Diag(Loc, diag::err_std_source_location_impl_malformed); - return nullptr; + return E; } - unsigned Count = 0; - for (FieldDecl *F : ImplDecl->fields()) { - StringRef Name = F->getName(); + Expr::EvalResult EvalResult; + SmallVector Notes; + EvalResult.Diag = &Notes; - if (Name == "_M_file_name") { - if (F->getType() != - S.Context.getPointerType(S.Context.CharTy.withConst())) - break; - Count++; - } else if (Name == "_M_function_name") { - if (F->getType() != - S.Context.getPointerType(S.Context.CharTy.withConst())) - break; - Count++; - } else if (Name == "_M_line") { - if (!F->getType()->isIntegerType()) - break; - Count++; - } else if (Name == "_M_column") { - if (!F->getType()->isIntegerType()) - break; - Count++; - } else { - Count = 100; // invalid - break; - } - } - if (Count != 4) { - S.Diag(Loc, diag::err_std_source_location_impl_malformed); - return nullptr; - } + // Try to evaluate the expression, and produce diagnostics explaining why it's + // not a constant expression as a side-effect. + bool Folded = + E->EvaluateAsRValue(EvalResult, Context, /*isConstantContext*/ true) && + EvalResult.Val.isInt() && !EvalResult.HasSideEffects && + (!getLangOpts().CPlusPlus || !EvalResult.HasUndefinedBehavior); + + if (!isa(E)) + E = ConstantExpr::Create(Context, E, EvalResult.Val); - return ImplDecl; -} + // In C++11, we can rely on diagnostics being produced for any expression + // which is not a constant expression. If no diagnostics were produced, then + // this is a constant expression. + if (Folded && getLangOpts().CPlusPlus11 && Notes.empty()) { + if (Result) + *Result = EvalResult.Val.getInt(); + return E; + } -ExprResult Sema::ActOnSourceLocExpr(SourceLocIdentKind Kind, - SourceLocation BuiltinLoc, - SourceLocation RPLoc) { - QualType ResultTy; - switch (Kind) { - case SourceLocIdentKind::File: - case SourceLocIdentKind::FileName: - case SourceLocIdentKind::Function: - case SourceLocIdentKind::FuncSig: { - QualType ArrTy = Context.getStringLiteralArrayType(Context.CharTy, 0); - ResultTy = - Context.getPointerType(ArrTy->getAsArrayTypeUnsafe()->getElementType()); - break; + // If our only note is the usual "invalid subexpression" note, just point + // the caret at its location rather than producing an essentially + // redundant note. + if (Notes.size() == 1 && Notes[0].second.getDiagID() == + diag::note_invalid_subexpr_in_const_expr) { + DiagLoc = Notes[0].first; + Notes.clear(); } - case SourceLocIdentKind::Line: - case SourceLocIdentKind::Column: - ResultTy = Context.UnsignedIntTy; - break; - case SourceLocIdentKind::SourceLocStruct: - if (!StdSourceLocationImplDecl) { - StdSourceLocationImplDecl = - LookupStdSourceLocationImpl(*this, BuiltinLoc); - if (!StdSourceLocationImplDecl) - return ExprError(); + + if (!Folded || !CanFold) { + if (!Diagnoser.Suppress) { + Diagnoser.diagnoseNotICE(*this, DiagLoc) << E->getSourceRange(); + for (const PartialDiagnosticAt &Note : Notes) + Diag(Note.first, Note.second); } - ResultTy = Context.getPointerType( - Context.getRecordType(StdSourceLocationImplDecl).withConst()); - break; - } - return BuildSourceLocExpr(Kind, ResultTy, BuiltinLoc, RPLoc, CurContext); -} + return ExprError(); + } -ExprResult Sema::BuildSourceLocExpr(SourceLocIdentKind Kind, QualType ResultTy, - SourceLocation BuiltinLoc, - SourceLocation RPLoc, - DeclContext *ParentContext) { - return new (Context) - SourceLocExpr(Context, Kind, ResultTy, BuiltinLoc, RPLoc, ParentContext); -} + Diagnoser.diagnoseFold(*this, DiagLoc) << E->getSourceRange(); + for (const PartialDiagnosticAt &Note : Notes) + Diag(Note.first, Note.second); -ExprResult Sema::ActOnEmbedExpr(SourceLocation EmbedKeywordLoc, - StringLiteral *BinaryData, StringRef FileName) { - EmbedDataStorage *Data = new (Context) EmbedDataStorage; - Data->BinaryData = BinaryData; - Data->FileName = FileName; - return new (Context) - EmbedExpr(Context, EmbedKeywordLoc, Data, /*NumOfElements=*/0, - Data->getDataElementCount()); + if (Result) + *Result = EvalResult.Val.getInt(); + return E; } -static bool maybeDiagnoseAssignmentToFunction(Sema &S, QualType DstType, - const Expr *SrcExpr) { - if (!DstType->isFunctionPointerType() || - !SrcExpr->getType()->isFunctionType()) - return false; +namespace { + // Handle the case where we conclude a expression which we speculatively + // considered to be unevaluated is actually evaluated. + class TransformToPE : public TreeTransform { + typedef TreeTransform BaseTransform; - auto *DRE = dyn_cast(SrcExpr->IgnoreParenImpCasts()); - if (!DRE) - return false; + public: + TransformToPE(Sema &SemaRef) : BaseTransform(SemaRef) { } - auto *FD = dyn_cast(DRE->getDecl()); - if (!FD) - return false; + // Make sure we redo semantic analysis + bool AlwaysRebuild() { return true; } + bool ReplacingOriginal() { return true; } - return !S.checkAddressOfFunctionIsAvailable(FD, - /*Complain=*/true, - SrcExpr->getBeginLoc()); -} + // We need to special-case DeclRefExprs referring to FieldDecls which + // are not part of a member pointer formation; normal TreeTransforming + // doesn't catch this case because of the way we represent them in the AST. + // FIXME: This is a bit ugly; is it really the best way to handle this + // case? + // + // Error on DeclRefExprs referring to FieldDecls. + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + if (isa(E->getDecl()) && + !SemaRef.isUnevaluatedContext()) + return SemaRef.Diag(E->getLocation(), + diag::err_invalid_non_static_member_use) + << E->getDecl() << E->getSourceRange(); -bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, - SourceLocation Loc, - QualType DstType, QualType SrcType, - Expr *SrcExpr, AssignmentAction Action, - bool *Complained) { - if (Complained) - *Complained = false; + return BaseTransform::TransformDeclRefExpr(E); + } - // Decode the result (notice that AST's are still created for extensions). - bool CheckInferredResultType = false; - bool isInvalid = false; - unsigned DiagKind = 0; - ConversionFixItGenerator ConvHints; - bool MayHaveConvFixit = false; - bool MayHaveFunctionDiff = false; - const ObjCInterfaceDecl *IFace = nullptr; - const ObjCProtocolDecl *PDecl = nullptr; + // Exception: filter out member pointer formation + ExprResult TransformUnaryOperator(UnaryOperator *E) { + if (E->getOpcode() == UO_AddrOf && E->getType()->isMemberPointerType()) + return E; - switch (ConvTy) { - case Compatible: - DiagnoseAssignmentEnum(DstType, SrcType, SrcExpr); - return false; - case CompatibleVoidPtrToNonVoidPtr: - // Still a valid conversion, but we may want to diagnose for C++ - // compatibility reasons. - DiagKind = diag::warn_compatible_implicit_pointer_conv; - break; - case PointerToInt: - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_pointer_int; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_pointer_int; - } - ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); - MayHaveConvFixit = true; - break; - case IntToPointer: - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_int_pointer; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_int_pointer; - } - ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); - MayHaveConvFixit = true; - break; - case IncompatibleFunctionPointerStrict: - DiagKind = - diag::warn_typecheck_convert_incompatible_function_pointer_strict; - ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); - MayHaveConvFixit = true; - break; - case IncompatibleFunctionPointer: - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_incompatible_function_pointer; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_incompatible_function_pointer; - } - ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); - MayHaveConvFixit = true; - break; - case IncompatiblePointer: - if (Action == AssignmentAction::Passing_CFAudited) { - DiagKind = diag::err_arc_typecheck_convert_incompatible_pointer; - } else if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_incompatible_pointer; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_incompatible_pointer; - } - CheckInferredResultType = DstType->isObjCObjectPointerType() && - SrcType->isObjCObjectPointerType(); - if (CheckInferredResultType) { - SrcType = SrcType.getUnqualifiedType(); - DstType = DstType.getUnqualifiedType(); - } else { - ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); - } - MayHaveConvFixit = true; - break; - case IncompatiblePointerSign: - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_incompatible_pointer_sign; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_incompatible_pointer_sign; + return BaseTransform::TransformUnaryOperator(E); } - break; - case FunctionVoidPointer: - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_pointer_void_func; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_pointer_void_func; + + // The body of a lambda-expression is in a separate expression evaluation + // context so never needs to be transformed. + // FIXME: Ideally we wouldn't transform the closure type either, and would + // just recreate the capture expressions and lambda expression. + StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body) { + return SkipLambdaBody(E, Body); } - break; - case IncompatiblePointerDiscardsQualifiers: { - // Perform array-to-pointer decay if necessary. - if (SrcType->isArrayType()) SrcType = Context.getArrayDecayedType(SrcType); + }; +} + +ExprResult Sema::TransformToPotentiallyEvaluated(Expr *E) { + assert(isUnevaluatedContext() && + "Should only transform unevaluated expressions"); + ExprEvalContexts.back().Context = + ExprEvalContexts[ExprEvalContexts.size()-2].Context; + if (isUnevaluatedContext()) + return E; + return TransformToPE(*this).TransformExpr(E); +} + +TypeSourceInfo *Sema::TransformToPotentiallyEvaluated(TypeSourceInfo *TInfo) { + assert(isUnevaluatedContext() && + "Should only transform unevaluated expressions"); + ExprEvalContexts.back().Context = parentEvaluationContext().Context; + if (isUnevaluatedContext()) + return TInfo; + return TransformToPE(*this).TransformType(TInfo); +} + +void +Sema::PushExpressionEvaluationContext( + ExpressionEvaluationContext NewContext, Decl *LambdaContextDecl, + ExpressionEvaluationContextRecord::ExpressionKind ExprContext) { + ExprEvalContexts.emplace_back(NewContext, ExprCleanupObjects.size(), Cleanup, + LambdaContextDecl, ExprContext); + + // Discarded statements and immediate contexts nested in other + // discarded statements or immediate context are themselves + // a discarded statement or an immediate context, respectively. + ExprEvalContexts.back().InDiscardedStatement = + parentEvaluationContext().isDiscardedStatementContext(); - isInvalid = true; + // C++23 [expr.const]/p15 + // An expression or conversion is in an immediate function context if [...] + // it is a subexpression of a manifestly constant-evaluated expression or + // conversion. + const auto &Prev = parentEvaluationContext(); + ExprEvalContexts.back().InImmediateFunctionContext = + Prev.isImmediateFunctionContext() || Prev.isConstantEvaluated(); - Qualifiers lhq = SrcType->getPointeeType().getQualifiers(); - Qualifiers rhq = DstType->getPointeeType().getQualifiers(); - if (lhq.getAddressSpace() != rhq.getAddressSpace()) { - DiagKind = diag::err_typecheck_incompatible_address_space; - break; - } else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) { - DiagKind = diag::err_typecheck_incompatible_ownership; - break; - } else if (!lhq.getPointerAuth().isEquivalent(rhq.getPointerAuth())) { - DiagKind = diag::err_typecheck_incompatible_ptrauth; - break; - } + ExprEvalContexts.back().InImmediateEscalatingFunctionContext = + Prev.InImmediateEscalatingFunctionContext; - llvm_unreachable("unknown error case for discarding qualifiers!"); - // fallthrough + Cleanup.reset(); + if (!MaybeODRUseExprs.empty()) + std::swap(MaybeODRUseExprs, ExprEvalContexts.back().SavedMaybeODRUseExprs); +} + +void +Sema::PushExpressionEvaluationContext( + ExpressionEvaluationContext NewContext, ReuseLambdaContextDecl_t, + ExpressionEvaluationContextRecord::ExpressionKind ExprContext) { + Decl *ClosureContextDecl = ExprEvalContexts.back().ManglingContextDecl; + PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext); +} + +namespace { + +const DeclRefExpr *CheckPossibleDeref(Sema &S, const Expr *PossibleDeref) { + PossibleDeref = PossibleDeref->IgnoreParenImpCasts(); + if (const auto *E = dyn_cast(PossibleDeref)) { + if (E->getOpcode() == UO_Deref) + return CheckPossibleDeref(S, E->getSubExpr()); + } else if (const auto *E = dyn_cast(PossibleDeref)) { + return CheckPossibleDeref(S, E->getBase()); + } else if (const auto *E = dyn_cast(PossibleDeref)) { + return CheckPossibleDeref(S, E->getBase()); + } else if (const auto E = dyn_cast(PossibleDeref)) { + QualType Inner; + QualType Ty = E->getType(); + if (const auto *Ptr = Ty->getAs()) + Inner = Ptr->getPointeeType(); + else if (const auto *Arr = S.Context.getAsArrayType(Ty)) + Inner = Arr->getElementType(); + else + return nullptr; + + if (Inner->hasAttr(attr::NoDeref)) + return E; } - case CompatiblePointerDiscardsQualifiers: - // If the qualifiers lost were because we were applying the - // (deprecated) C++ conversion from a string literal to a char* - // (or wchar_t*), then there was no error (C++ 4.2p2). FIXME: - // Ideally, this check would be performed in - // checkPointerTypesForAssignment. However, that would require a - // bit of refactoring (so that the second argument is an - // expression, rather than a type), which should be done as part - // of a larger effort to fix checkPointerTypesForAssignment for - // C++ semantics. - if (getLangOpts().CPlusPlus && - IsStringLiteralToNonConstPointerConversion(SrcExpr, DstType)) - return false; - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_typecheck_convert_discards_qualifiers; - isInvalid = true; - } else { - DiagKind = diag::ext_typecheck_convert_discards_qualifiers; - } + return nullptr; +} - break; - case IncompatibleNestedPointerQualifiers: - if (getLangOpts().CPlusPlus) { - isInvalid = true; - DiagKind = diag::err_nested_pointer_qualifier_mismatch; - } else { - DiagKind = diag::ext_nested_pointer_qualifier_mismatch; - } - break; - case IncompatibleNestedPointerAddressSpaceMismatch: - DiagKind = diag::err_typecheck_incompatible_nested_address_space; - isInvalid = true; - break; - case IntToBlockPointer: - DiagKind = diag::err_int_to_block_pointer; - isInvalid = true; - break; - case IncompatibleBlockPointer: - DiagKind = diag::err_typecheck_convert_incompatible_block_pointer; - isInvalid = true; - break; - case IncompatibleObjCQualifiedId: { - if (SrcType->isObjCQualifiedIdType()) { - const ObjCObjectPointerType *srcOPT = - SrcType->castAs(); - for (auto *srcProto : srcOPT->quals()) { - PDecl = srcProto; - break; - } - if (const ObjCInterfaceType *IFaceT = - DstType->castAs()->getInterfaceType()) - IFace = IFaceT->getDecl(); - } - else if (DstType->isObjCQualifiedIdType()) { - const ObjCObjectPointerType *dstOPT = - DstType->castAs(); - for (auto *dstProto : dstOPT->quals()) { - PDecl = dstProto; - break; - } - if (const ObjCInterfaceType *IFaceT = - SrcType->castAs()->getInterfaceType()) - IFace = IFaceT->getDecl(); - } - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_incompatible_qualified_id; - isInvalid = true; +} // namespace + +void Sema::WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec) { + for (const Expr *E : Rec.PossibleDerefs) { + const DeclRefExpr *DeclRef = CheckPossibleDeref(*this, E); + if (DeclRef) { + const ValueDecl *Decl = DeclRef->getDecl(); + Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type) + << Decl->getName() << E->getSourceRange(); + Diag(Decl->getLocation(), diag::note_previous_decl) << Decl->getName(); } else { - DiagKind = diag::warn_incompatible_qualified_id; + Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type_no_decl) + << E->getSourceRange(); } - break; } - case IncompatibleVectors: - if (getLangOpts().CPlusPlus) { - DiagKind = diag::err_incompatible_vectors; - isInvalid = true; - } else { - DiagKind = diag::warn_incompatible_vectors; - } - break; - case IncompatibleObjCWeakRef: - DiagKind = diag::err_arc_weak_unavailable_assign; - isInvalid = true; - break; - case Incompatible: - if (maybeDiagnoseAssignmentToFunction(*this, DstType, SrcExpr)) { - if (Complained) - *Complained = true; - return true; - } + Rec.PossibleDerefs.clear(); +} - DiagKind = diag::err_typecheck_convert_incompatible; - ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); - MayHaveConvFixit = true; - isInvalid = true; - MayHaveFunctionDiff = true; - break; - } +void Sema::CheckUnusedVolatileAssignment(Expr *E) { + if (!E->getType().isVolatileQualified() || !getLangOpts().CPlusPlus20) + return; - QualType FirstType, SecondType; - switch (Action) { - case AssignmentAction::Assigning: - case AssignmentAction::Initializing: - // The destination type comes first. - FirstType = DstType; - SecondType = SrcType; - break; + // Note: ignoring parens here is not justified by the standard rules, but + // ignoring parentheses seems like a more reasonable approach, and this only + // drives a deprecation warning so doesn't affect conformance. + if (auto *BO = dyn_cast(E->IgnoreParenImpCasts())) { + if (BO->getOpcode() == BO_Assign) { + auto &LHSs = ExprEvalContexts.back().VolatileAssignmentLHSs; + llvm::erase(LHSs, BO->getLHS()); + } + } +} - case AssignmentAction::Returning: - case AssignmentAction::Passing: - case AssignmentAction::Passing_CFAudited: - case AssignmentAction::Converting: - case AssignmentAction::Sending: - case AssignmentAction::Casting: - // The source type comes first. - FirstType = SrcType; - SecondType = DstType; - break; +void Sema::MarkExpressionAsImmediateEscalating(Expr *E) { + assert(getLangOpts().CPlusPlus20 && + ExprEvalContexts.back().InImmediateEscalatingFunctionContext && + "Cannot mark an immediate escalating expression outside of an " + "immediate escalating context"); + if (auto *Call = dyn_cast(E->IgnoreImplicit()); + Call && Call->getCallee()) { + if (auto *DeclRef = + dyn_cast(Call->getCallee()->IgnoreImplicit())) + DeclRef->setIsImmediateEscalating(true); + } else if (auto *Ctr = dyn_cast(E->IgnoreImplicit())) { + Ctr->setIsImmediateEscalating(true); + } else if (auto *DeclRef = dyn_cast(E->IgnoreImplicit())) { + DeclRef->setIsImmediateEscalating(true); + } else { + assert(false && "expected an immediately escalating expression"); } + if (FunctionScopeInfo *FI = getCurFunction()) + FI->FoundImmediateEscalatingExpression = true; +} - PartialDiagnostic FDiag = PDiag(DiagKind); - AssignmentAction ActionForDiag = Action; - if (Action == AssignmentAction::Passing_CFAudited) - ActionForDiag = AssignmentAction::Passing; +ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) { + if (isUnevaluatedContext() || !E.isUsable() || !Decl || + !Decl->isImmediateFunction() || isAlwaysConstantEvaluatedContext() || + isCheckingDefaultArgumentOrInitializer() || + RebuildingImmediateInvocation || isImmediateFunctionContext()) + return E; - FDiag << FirstType << SecondType << ActionForDiag - << SrcExpr->getSourceRange(); + /// Opportunistically remove the callee from ReferencesToConsteval if we can. + /// It's OK if this fails; we'll also remove this in + /// HandleImmediateInvocations, but catching it here allows us to avoid + /// walking the AST looking for it in simple cases. + if (auto *Call = dyn_cast(E.get()->IgnoreImplicit())) + if (auto *DeclRef = + dyn_cast(Call->getCallee()->IgnoreImplicit())) + ExprEvalContexts.back().ReferenceToConsteval.erase(DeclRef); - if (DiagKind == diag::ext_typecheck_convert_incompatible_pointer_sign || - DiagKind == diag::err_typecheck_convert_incompatible_pointer_sign) { - auto isPlainChar = [](const clang::Type *Type) { - return Type->isSpecificBuiltinType(BuiltinType::Char_S) || - Type->isSpecificBuiltinType(BuiltinType::Char_U); - }; - FDiag << (isPlainChar(FirstType->getPointeeOrArrayElementType()) || - isPlainChar(SecondType->getPointeeOrArrayElementType())); - } + // C++23 [expr.const]/p16 + // An expression or conversion is immediate-escalating if it is not initially + // in an immediate function context and it is [...] an immediate invocation + // that is not a constant expression and is not a subexpression of an + // immediate invocation. + APValue Cached; + auto CheckConstantExpressionAndKeepResult = [&]() { + llvm::SmallVector Notes; + Expr::EvalResult Eval; + Eval.Diag = &Notes; + bool Res = E.get()->EvaluateAsConstantExpr( + Eval, getASTContext(), ConstantExprKind::ImmediateInvocation); + if (Res && Notes.empty()) { + Cached = std::move(Eval.Val); + return true; + } + return false; + }; - // If we can fix the conversion, suggest the FixIts. - if (!ConvHints.isNull()) { - for (FixItHint &H : ConvHints.Hints) - FDiag << H; + if (!E.get()->isValueDependent() && + ExprEvalContexts.back().InImmediateEscalatingFunctionContext && + !CheckConstantExpressionAndKeepResult()) { + MarkExpressionAsImmediateEscalating(E.get()); + return E; } - if (MayHaveConvFixit) { FDiag << (unsigned) (ConvHints.Kind); } - - if (MayHaveFunctionDiff) - HandleFunctionTypeMismatch(FDiag, SecondType, FirstType); - - Diag(Loc, FDiag); - if ((DiagKind == diag::warn_incompatible_qualified_id || - DiagKind == diag::err_incompatible_qualified_id) && - PDecl && IFace && !IFace->hasDefinition()) - Diag(IFace->getLocation(), diag::note_incomplete_class_and_qualified_id) - << IFace << PDecl; + if (Cleanup.exprNeedsCleanups()) { + // Since an immediate invocation is a full expression itself - it requires + // an additional ExprWithCleanups node, but it can participate to a bigger + // full expression which actually requires cleanups to be run after so + // create ExprWithCleanups without using MaybeCreateExprWithCleanups as it + // may discard cleanups for outer expression too early. - if (SecondType == Context.OverloadTy) - NoteAllOverloadCandidates(OverloadExpr::find(SrcExpr).Expression, - FirstType, /*TakingAddress=*/true); + // Note that ExprWithCleanups created here must always have empty cleanup + // objects: + // - compound literals do not create cleanup objects in C++ and immediate + // invocations are C++-only. + // - blocks are not allowed inside constant expressions and compiler will + // issue an error if they appear there. + // + // Hence, in correct code any cleanup objects created inside current + // evaluation context must be outside the immediate invocation. + E = ExprWithCleanups::Create(getASTContext(), E.get(), + Cleanup.cleanupsHaveSideEffects(), {}); + } - if (CheckInferredResultType) - ObjC().EmitRelatedResultTypeNote(SrcExpr); + ConstantExpr *Res = ConstantExpr::Create( + getASTContext(), E.get(), + ConstantExpr::getStorageKind(Decl->getReturnType().getTypePtr(), + getASTContext()), + /*IsImmediateInvocation*/ true); + if (Cached.hasValue()) + Res->MoveIntoResult(Cached, getASTContext()); + /// Value-dependent constant expressions should not be immediately + /// evaluated until they are instantiated. + if (!Res->isValueDependent()) + ExprEvalContexts.back().ImmediateInvocationCandidates.emplace_back(Res, 0); + return Res; +} - if (Action == AssignmentAction::Returning && ConvTy == IncompatiblePointer) - ObjC().EmitRelatedResultTypeNoteForReturn(DstType); +static void EvaluateAndDiagnoseImmediateInvocation( + Sema &SemaRef, Sema::ImmediateInvocationCandidate Candidate) { + llvm::SmallVector Notes; + Expr::EvalResult Eval; + Eval.Diag = &Notes; + ConstantExpr *CE = Candidate.getPointer(); + bool Result = CE->EvaluateAsConstantExpr( + Eval, SemaRef.getASTContext(), ConstantExprKind::ImmediateInvocation); + if (!Result || !Notes.empty()) { + SemaRef.FailedImmediateInvocations.insert(CE); + Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit(); + if (auto *FunctionalCast = dyn_cast(InnerExpr)) + InnerExpr = FunctionalCast->getSubExpr()->IgnoreImplicit(); + FunctionDecl *FD = nullptr; + if (auto *Call = dyn_cast(InnerExpr)) + FD = cast(Call->getCalleeDecl()); + else if (auto *Call = dyn_cast(InnerExpr)) + FD = Call->getConstructor(); + else if (auto *Cast = dyn_cast(InnerExpr)) + FD = dyn_cast_or_null(Cast->getConversionFunction()); - if (Complained) - *Complained = true; - return isInvalid; + assert(FD && FD->isImmediateFunction() && + "could not find an immediate function in this expression"); + if (FD->isInvalidDecl()) + return; + SemaRef.Diag(CE->getBeginLoc(), diag::err_invalid_consteval_call) + << FD << FD->isConsteval(); + if (auto Context = + SemaRef.InnermostDeclarationWithDelayedImmediateInvocations()) { + SemaRef.Diag(Context->Loc, diag::note_invalid_consteval_initializer) + << Context->Decl; + SemaRef.Diag(Context->Decl->getBeginLoc(), diag::note_declared_at); + } + if (!FD->isConsteval()) + SemaRef.DiagnoseImmediateEscalatingReason(FD); + for (auto &Note : Notes) + SemaRef.Diag(Note.first, Note.second); + return; + } + CE->MoveIntoResult(Eval.Val, SemaRef.getASTContext()); } -ExprResult Sema::VerifyIntegerConstantExpression(Expr *E, - llvm::APSInt *Result, - AllowFoldKind CanFold) { - class SimpleICEDiagnoser : public VerifyICEDiagnoser { - public: - SemaDiagnosticBuilder diagnoseNotICEType(Sema &S, SourceLocation Loc, - QualType T) override { - return S.Diag(Loc, diag::err_ice_not_integral) - << T << S.LangOpts.CPlusPlus; +static void RemoveNestedImmediateInvocation( + Sema &SemaRef, Sema::ExpressionEvaluationContextRecord &Rec, + SmallVector::reverse_iterator It) { + struct ComplexRemove : TreeTransform { + using Base = TreeTransform; + llvm::SmallPtrSetImpl &DRSet; + SmallVector &IISet; + SmallVector::reverse_iterator + CurrentII; + ComplexRemove(Sema &SemaRef, llvm::SmallPtrSetImpl &DR, + SmallVector &II, + SmallVector::reverse_iterator Current) + : Base(SemaRef), DRSet(DR), IISet(II), CurrentII(Current) {} + void RemoveImmediateInvocation(ConstantExpr* E) { + auto It = std::find_if(CurrentII, IISet.rend(), + [E](Sema::ImmediateInvocationCandidate Elem) { + return Elem.getPointer() == E; + }); + // It is possible that some subexpression of the current immediate + // invocation was handled from another expression evaluation context. Do + // not handle the current immediate invocation if some of its + // subexpressions failed before. + if (It == IISet.rend()) { + if (SemaRef.FailedImmediateInvocations.contains(E)) + CurrentII->setInt(1); + } else { + It->setInt(1); // Mark as deleted + } } - SemaDiagnosticBuilder diagnoseNotICE(Sema &S, SourceLocation Loc) override { - return S.Diag(Loc, diag::err_expr_not_ice) << S.LangOpts.CPlusPlus; + ExprResult TransformConstantExpr(ConstantExpr *E) { + if (!E->isImmediateInvocation()) + return Base::TransformConstantExpr(E); + RemoveImmediateInvocation(E); + return Base::TransformExpr(E->getSubExpr()); } - } Diagnoser; - - return VerifyIntegerConstantExpression(E, Result, Diagnoser, CanFold); -} - -ExprResult Sema::VerifyIntegerConstantExpression(Expr *E, - llvm::APSInt *Result, - unsigned DiagID, - AllowFoldKind CanFold) { - class IDDiagnoser : public VerifyICEDiagnoser { - unsigned DiagID; - - public: - IDDiagnoser(unsigned DiagID) - : VerifyICEDiagnoser(DiagID == 0), DiagID(DiagID) { } + /// Base::TransfromCXXOperatorCallExpr doesn't traverse the callee so + /// we need to remove its DeclRefExpr from the DRSet. + ExprResult TransformCXXOperatorCallExpr(CXXOperatorCallExpr *E) { + DRSet.erase(cast(E->getCallee()->IgnoreImplicit())); + return Base::TransformCXXOperatorCallExpr(E); + } + /// Base::TransformUserDefinedLiteral doesn't preserve the + /// UserDefinedLiteral node. + ExprResult TransformUserDefinedLiteral(UserDefinedLiteral *E) { return E; } + /// Base::TransformInitializer skips ConstantExpr so we need to visit them + /// here. + ExprResult TransformInitializer(Expr *Init, bool NotCopyInit) { + if (!Init) + return Init; - SemaDiagnosticBuilder diagnoseNotICE(Sema &S, SourceLocation Loc) override { - return S.Diag(Loc, DiagID); + // We cannot use IgnoreImpCasts because we need to preserve + // full expressions. + while (true) { + if (auto *ICE = dyn_cast(Init)) + Init = ICE->getSubExpr(); + else if (auto *ICE = dyn_cast(Init)) + Init = ICE->getSubExpr(); + else + break; + } + /// ConstantExprs are the first layer of implicit node to be removed so if + /// Init isn't a ConstantExpr, no ConstantExpr will be skipped. + if (auto *CE = dyn_cast(Init); + CE && CE->isImmediateInvocation()) + RemoveImmediateInvocation(CE); + return Base::TransformInitializer(Init, NotCopyInit); } - } Diagnoser(DiagID); + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + DRSet.erase(E); + return E; + } + ExprResult TransformLambdaExpr(LambdaExpr *E) { + // Do not rebuild lambdas to avoid creating a new type. + // Lambdas have already been processed inside their eval contexts. + return E; + } + bool AlwaysRebuild() { return false; } + bool ReplacingOriginal() { return true; } + bool AllowSkippingCXXConstructExpr() { + bool Res = AllowSkippingFirstCXXConstructExpr; + AllowSkippingFirstCXXConstructExpr = true; + return Res; + } + bool AllowSkippingFirstCXXConstructExpr = true; + } Transformer(SemaRef, Rec.ReferenceToConsteval, + Rec.ImmediateInvocationCandidates, It); - return VerifyIntegerConstantExpression(E, Result, Diagnoser, CanFold); -} + /// CXXConstructExpr with a single argument are getting skipped by + /// TreeTransform in some situtation because they could be implicit. This + /// can only occur for the top-level CXXConstructExpr because it is used + /// nowhere in the expression being transformed therefore will not be rebuilt. + /// Setting AllowSkippingFirstCXXConstructExpr to false will prevent from + /// skipping the first CXXConstructExpr. + if (isa(It->getPointer()->IgnoreImplicit())) + Transformer.AllowSkippingFirstCXXConstructExpr = false; -Sema::SemaDiagnosticBuilder -Sema::VerifyICEDiagnoser::diagnoseNotICEType(Sema &S, SourceLocation Loc, - QualType T) { - return diagnoseNotICE(S, Loc); + ExprResult Res = Transformer.TransformExpr(It->getPointer()->getSubExpr()); + // The result may not be usable in case of previous compilation errors. + // In this case evaluation of the expression may result in crash so just + // don't do anything further with the result. + if (Res.isUsable()) { + Res = SemaRef.MaybeCreateExprWithCleanups(Res); + It->getPointer()->setSubExpr(Res.get()); + } } -Sema::SemaDiagnosticBuilder -Sema::VerifyICEDiagnoser::diagnoseFold(Sema &S, SourceLocation Loc) { - return S.Diag(Loc, diag::ext_expr_not_ice) << S.LangOpts.CPlusPlus; -} +static void +HandleImmediateInvocations(Sema &SemaRef, + Sema::ExpressionEvaluationContextRecord &Rec) { + if ((Rec.ImmediateInvocationCandidates.size() == 0 && + Rec.ReferenceToConsteval.size() == 0) || + Rec.isImmediateFunctionContext() || SemaRef.RebuildingImmediateInvocation) + return; + + /// When we have more than 1 ImmediateInvocationCandidates or previously + /// failed immediate invocations, we need to check for nested + /// ImmediateInvocationCandidates in order to avoid duplicate diagnostics. + /// Otherwise we only need to remove ReferenceToConsteval in the immediate + /// invocation. + if (Rec.ImmediateInvocationCandidates.size() > 1 || + !SemaRef.FailedImmediateInvocations.empty()) { -ExprResult -Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result, - VerifyICEDiagnoser &Diagnoser, - AllowFoldKind CanFold) { - SourceLocation DiagLoc = E->getBeginLoc(); + /// Prevent sema calls during the tree transform from adding pointers that + /// are already in the sets. + llvm::SaveAndRestore DisableIITracking( + SemaRef.RebuildingImmediateInvocation, true); - if (getLangOpts().CPlusPlus11) { - // C++11 [expr.const]p5: - // If an expression of literal class type is used in a context where an - // integral constant expression is required, then that class type shall - // have a single non-explicit conversion function to an integral or - // unscoped enumeration type - ExprResult Converted; - class CXX11ConvertDiagnoser : public ICEConvertDiagnoser { - VerifyICEDiagnoser &BaseDiagnoser; - public: - CXX11ConvertDiagnoser(VerifyICEDiagnoser &BaseDiagnoser) - : ICEConvertDiagnoser(/*AllowScopedEnumerations*/ false, - BaseDiagnoser.Suppress, true), - BaseDiagnoser(BaseDiagnoser) {} + /// Prevent diagnostic during tree transfrom as they are duplicates + Sema::TentativeAnalysisScope DisableDiag(SemaRef); - SemaDiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc, - QualType T) override { - return BaseDiagnoser.diagnoseNotICEType(S, Loc, T); + for (auto It = Rec.ImmediateInvocationCandidates.rbegin(); + It != Rec.ImmediateInvocationCandidates.rend(); It++) + if (!It->getInt()) + RemoveNestedImmediateInvocation(SemaRef, Rec, It); + } else if (Rec.ImmediateInvocationCandidates.size() == 1 && + Rec.ReferenceToConsteval.size()) { + struct SimpleRemove : DynamicRecursiveASTVisitor { + llvm::SmallPtrSetImpl &DRSet; + SimpleRemove(llvm::SmallPtrSetImpl &S) : DRSet(S) {} + bool VisitDeclRefExpr(DeclRefExpr *E) override { + DRSet.erase(E); + return DRSet.size(); } + } Visitor(Rec.ReferenceToConsteval); + Visitor.TraverseStmt( + Rec.ImmediateInvocationCandidates.front().getPointer()->getSubExpr()); + } + for (auto CE : Rec.ImmediateInvocationCandidates) + if (!CE.getInt()) + EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE); + for (auto *DR : Rec.ReferenceToConsteval) { + // If the expression is immediate escalating, it is not an error; + // The outer context itself becomes immediate and further errors, + // if any, will be handled by DiagnoseImmediateEscalatingReason. + if (DR->isImmediateEscalating()) + continue; + auto *FD = cast(DR->getDecl()); + const NamedDecl *ND = FD; + if (const auto *MD = dyn_cast(ND); + MD && (MD->isLambdaStaticInvoker() || isLambdaCallOperator(MD))) + ND = MD->getParent(); - SemaDiagnosticBuilder diagnoseIncomplete( - Sema &S, SourceLocation Loc, QualType T) override { - return S.Diag(Loc, diag::err_ice_incomplete_type) << T; - } + // C++23 [expr.const]/p16 + // An expression or conversion is immediate-escalating if it is not + // initially in an immediate function context and it is [...] a + // potentially-evaluated id-expression that denotes an immediate function + // that is not a subexpression of an immediate invocation. + bool ImmediateEscalating = false; + bool IsPotentiallyEvaluated = + Rec.Context == + Sema::ExpressionEvaluationContext::PotentiallyEvaluated || + Rec.Context == + Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed; + if (SemaRef.inTemplateInstantiation() && IsPotentiallyEvaluated) + ImmediateEscalating = Rec.InImmediateEscalatingFunctionContext; - SemaDiagnosticBuilder diagnoseExplicitConv( - Sema &S, SourceLocation Loc, QualType T, QualType ConvTy) override { - return S.Diag(Loc, diag::err_ice_explicit_conversion) << T << ConvTy; + if (!Rec.InImmediateEscalatingFunctionContext || + (SemaRef.inTemplateInstantiation() && !ImmediateEscalating)) { + SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address) + << ND << isa(ND) << FD->isConsteval(); + if (!FD->getBuiltinID()) + SemaRef.Diag(ND->getLocation(), diag::note_declared_at); + if (auto Context = + SemaRef.InnermostDeclarationWithDelayedImmediateInvocations()) { + SemaRef.Diag(Context->Loc, diag::note_invalid_consteval_initializer) + << Context->Decl; + SemaRef.Diag(Context->Decl->getBeginLoc(), diag::note_declared_at); } + if (FD->isImmediateEscalating() && !FD->isConsteval()) + SemaRef.DiagnoseImmediateEscalatingReason(FD); - SemaDiagnosticBuilder noteExplicitConv( - Sema &S, CXXConversionDecl *Conv, QualType ConvTy) override { - return S.Diag(Conv->getLocation(), diag::note_ice_conversion_here) - << ConvTy->isEnumeralType() << ConvTy; - } + } else { + SemaRef.MarkExpressionAsImmediateEscalating(DR); + } + } +} - SemaDiagnosticBuilder diagnoseAmbiguous( - Sema &S, SourceLocation Loc, QualType T) override { - return S.Diag(Loc, diag::err_ice_ambiguous_conversion) << T; - } +void Sema::PopExpressionEvaluationContext() { + ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back(); + unsigned NumTypos = Rec.NumTypos; - SemaDiagnosticBuilder noteAmbiguous( - Sema &S, CXXConversionDecl *Conv, QualType ConvTy) override { - return S.Diag(Conv->getLocation(), diag::note_ice_conversion_here) - << ConvTy->isEnumeralType() << ConvTy; - } + if (!Rec.Lambdas.empty()) { + using ExpressionKind = ExpressionEvaluationContextRecord::ExpressionKind; + if (!getLangOpts().CPlusPlus20 && + (Rec.ExprContext == ExpressionKind::EK_TemplateArgument || + Rec.isUnevaluated() || + (Rec.isConstantEvaluated() && !getLangOpts().CPlusPlus17))) { + unsigned D; + if (Rec.isUnevaluated()) { + // C++11 [expr.prim.lambda]p2: + // A lambda-expression shall not appear in an unevaluated operand + // (Clause 5). + D = diag::err_lambda_unevaluated_operand; + } else if (Rec.isConstantEvaluated() && !getLangOpts().CPlusPlus17) { + // C++1y [expr.const]p2: + // A conditional-expression e is a core constant expression unless the + // evaluation of e, following the rules of the abstract machine, would + // evaluate [...] a lambda-expression. + D = diag::err_lambda_in_constant_expression; + } else if (Rec.ExprContext == ExpressionKind::EK_TemplateArgument) { + // C++17 [expr.prim.lamda]p2: + // A lambda-expression shall not appear [...] in a template-argument. + D = diag::err_lambda_in_invalid_context; + } else + llvm_unreachable("Couldn't infer lambda error message."); - SemaDiagnosticBuilder diagnoseConversion( - Sema &S, SourceLocation Loc, QualType T, QualType ConvTy) override { - llvm_unreachable("conversion functions are permitted"); - } - } ConvertDiagnoser(Diagnoser); + for (const auto *L : Rec.Lambdas) + Diag(L->getBeginLoc(), D); + } + } - Converted = PerformContextualImplicitConversion(DiagLoc, E, - ConvertDiagnoser); - if (Converted.isInvalid()) - return Converted; - E = Converted.get(); - // The 'explicit' case causes us to get a RecoveryExpr. Give up here so we - // don't try to evaluate it later. We also don't want to return the - // RecoveryExpr here, as it results in this call succeeding, thus callers of - // this function will attempt to use 'Value'. - if (isa(E)) - return ExprError(); - if (!E->getType()->isIntegralOrUnscopedEnumerationType()) - return ExprError(); - } else if (!E->getType()->isIntegralOrUnscopedEnumerationType()) { - // An ICE must be of integral or unscoped enumeration type. - if (!Diagnoser.Suppress) - Diagnoser.diagnoseNotICEType(*this, DiagLoc, E->getType()) - << E->getSourceRange(); - return ExprError(); + // Append the collected materialized temporaries into previous context before + // exit if the previous also is a lifetime extending context. + if (getLangOpts().CPlusPlus23 && Rec.InLifetimeExtendingContext && + parentEvaluationContext().InLifetimeExtendingContext && + !Rec.ForRangeLifetimeExtendTemps.empty()) { + parentEvaluationContext().ForRangeLifetimeExtendTemps.append( + Rec.ForRangeLifetimeExtendTemps); } - ExprResult RValueExpr = DefaultLvalueConversion(E); - if (RValueExpr.isInvalid()) - return ExprError(); + WarnOnPendingNoDerefs(Rec); + HandleImmediateInvocations(*this, Rec); - E = RValueExpr.get(); + // Warn on any volatile-qualified simple-assignments that are not discarded- + // value expressions nor unevaluated operands (those cases get removed from + // this list by CheckUnusedVolatileAssignment). + for (auto *BO : Rec.VolatileAssignmentLHSs) + Diag(BO->getBeginLoc(), diag::warn_deprecated_simple_assign_volatile) + << BO->getType(); - // Circumvent ICE checking in C++11 to avoid evaluating the expression twice - // in the non-ICE case. - if (!getLangOpts().CPlusPlus11 && E->isIntegerConstantExpr(Context)) { - SmallVector Notes; - if (Result) - *Result = E->EvaluateKnownConstIntCheckOverflow(Context, &Notes); - if (!isa(E)) - E = Result ? ConstantExpr::Create(Context, E, APValue(*Result)) - : ConstantExpr::Create(Context, E); + // When are coming out of an unevaluated context, clear out any + // temporaries that we may have created as part of the evaluation of + // the expression in that context: they aren't relevant because they + // will never be constructed. + if (Rec.isUnevaluated() || Rec.isConstantEvaluated()) { + ExprCleanupObjects.erase(ExprCleanupObjects.begin() + Rec.NumCleanupObjects, + ExprCleanupObjects.end()); + Cleanup = Rec.ParentCleanup; + CleanupVarDeclMarking(); + std::swap(MaybeODRUseExprs, Rec.SavedMaybeODRUseExprs); + // Otherwise, merge the contexts together. + } else { + Cleanup.mergeFrom(Rec.ParentCleanup); + MaybeODRUseExprs.insert_range(Rec.SavedMaybeODRUseExprs); + } - if (Notes.empty()) - return E; + // Pop the current expression evaluation context off the stack. + ExprEvalContexts.pop_back(); - // If our only note is the usual "invalid subexpression" note, just point - // the caret at its location rather than producing an essentially - // redundant note. - if (Notes.size() == 1 && Notes[0].second.getDiagID() == - diag::note_invalid_subexpr_in_const_expr) { - DiagLoc = Notes[0].first; - Notes.clear(); - } + // The global expression evaluation context record is never popped. + ExprEvalContexts.back().NumTypos += NumTypos; +} + +void Sema::DiscardCleanupsInEvaluationContext() { + ExprCleanupObjects.erase( + ExprCleanupObjects.begin() + ExprEvalContexts.back().NumCleanupObjects, + ExprCleanupObjects.end()); + Cleanup.reset(); + MaybeODRUseExprs.clear(); +} + +ExprResult Sema::HandleExprEvaluationContextForTypeof(Expr *E) { + ExprResult Result = CheckPlaceholderExpr(E); + if (Result.isInvalid()) + return ExprError(); + E = Result.get(); + if (!E->getType()->isVariablyModifiedType()) + return E; + return TransformToPotentiallyEvaluated(E); +} + +/// Are we in a context that is potentially constant evaluated per C++20 +/// [expr.const]p12? +static bool isPotentiallyConstantEvaluatedContext(Sema &SemaRef) { + /// C++2a [expr.const]p12: + // An expression or conversion is potentially constant evaluated if it is + switch (SemaRef.ExprEvalContexts.back().Context) { + case Sema::ExpressionEvaluationContext::ConstantEvaluated: + case Sema::ExpressionEvaluationContext::ImmediateFunctionContext: - if (getLangOpts().CPlusPlus) { - if (!Diagnoser.Suppress) { - Diagnoser.diagnoseNotICE(*this, DiagLoc) << E->getSourceRange(); - for (const PartialDiagnosticAt &Note : Notes) - Diag(Note.first, Note.second); - } - return ExprError(); - } + // -- a manifestly constant-evaluated expression, + case Sema::ExpressionEvaluationContext::PotentiallyEvaluated: + case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: + case Sema::ExpressionEvaluationContext::DiscardedStatement: + // -- a potentially-evaluated expression, + case Sema::ExpressionEvaluationContext::UnevaluatedList: + // -- an immediate subexpression of a braced-init-list, - Diagnoser.diagnoseFold(*this, DiagLoc) << E->getSourceRange(); - for (const PartialDiagnosticAt &Note : Notes) - Diag(Note.first, Note.second); + // -- [FIXME] an expression of the form & cast-expression that occurs + // within a templated entity + // -- a subexpression of one of the above that is not a subexpression of + // a nested unevaluated operand. + return true; - return E; + case Sema::ExpressionEvaluationContext::Unevaluated: + case Sema::ExpressionEvaluationContext::UnevaluatedAbstract: + // Expressions in this context are never evaluated. + return false; } + llvm_unreachable("Invalid context"); +} - Expr::EvalResult EvalResult; - SmallVector Notes; - EvalResult.Diag = &Notes; - - // Try to evaluate the expression, and produce diagnostics explaining why it's - // not a constant expression as a side-effect. - bool Folded = - E->EvaluateAsRValue(EvalResult, Context, /*isConstantContext*/ true) && - EvalResult.Val.isInt() && !EvalResult.HasSideEffects && - (!getLangOpts().CPlusPlus || !EvalResult.HasUndefinedBehavior); +/// Return true if this function has a calling convention that requires mangling +/// in the size of the parameter pack. +static bool funcHasParameterSizeMangling(Sema &S, FunctionDecl *FD) { + // These manglings are only applicable for targets whcih use Microsoft + // mangling scheme for C. + if (!S.Context.getTargetInfo().shouldUseMicrosoftCCforMangling()) + return false; - if (!isa(E)) - E = ConstantExpr::Create(Context, E, EvalResult.Val); + // If this is C++ and this isn't an extern "C" function, parameters do not + // need to be complete. In this case, C++ mangling will apply, which doesn't + // use the size of the parameters. + if (S.getLangOpts().CPlusPlus && !FD->isExternC()) + return false; - // In C++11, we can rely on diagnostics being produced for any expression - // which is not a constant expression. If no diagnostics were produced, then - // this is a constant expression. - if (Folded && getLangOpts().CPlusPlus11 && Notes.empty()) { - if (Result) - *Result = EvalResult.Val.getInt(); - return E; + // Stdcall, fastcall, and vectorcall need this special treatment. + CallingConv CC = FD->getType()->castAs()->getCallConv(); + switch (CC) { + case CC_X86StdCall: + case CC_X86FastCall: + case CC_X86VectorCall: + return true; + default: + break; } + return false; +} - // If our only note is the usual "invalid subexpression" note, just point - // the caret at its location rather than producing an essentially - // redundant note. - if (Notes.size() == 1 && Notes[0].second.getDiagID() == - diag::note_invalid_subexpr_in_const_expr) { - DiagLoc = Notes[0].first; - Notes.clear(); - } +/// Require that all of the parameter types of function be complete. Normally, +/// parameter types are only required to be complete when a function is called +/// or defined, but to mangle functions with certain calling conventions, the +/// mangler needs to know the size of the parameter list. In this situation, +/// MSVC doesn't emit an error or instantiate templates. Instead, MSVC mangles +/// the function as _foo@0, i.e. zero bytes of parameters, which will usually +/// result in a linker error. Clang doesn't implement this behavior, and instead +/// attempts to error at compile time. +static void CheckCompleteParameterTypesForMangler(Sema &S, FunctionDecl *FD, + SourceLocation Loc) { + class ParamIncompleteTypeDiagnoser : public Sema::TypeDiagnoser { + FunctionDecl *FD; + ParmVarDecl *Param; - if (!Folded || !CanFold) { - if (!Diagnoser.Suppress) { - Diagnoser.diagnoseNotICE(*this, DiagLoc) << E->getSourceRange(); - for (const PartialDiagnosticAt &Note : Notes) - Diag(Note.first, Note.second); - } + public: + ParamIncompleteTypeDiagnoser(FunctionDecl *FD, ParmVarDecl *Param) + : FD(FD), Param(Param) {} - return ExprError(); - } + void diagnose(Sema &S, SourceLocation Loc, QualType T) override { + CallingConv CC = FD->getType()->castAs()->getCallConv(); + StringRef CCName; + switch (CC) { + case CC_X86StdCall: + CCName = "stdcall"; + break; + case CC_X86FastCall: + CCName = "fastcall"; + break; + case CC_X86VectorCall: + CCName = "vectorcall"; + break; + default: + llvm_unreachable("CC does not need mangling"); + } - Diagnoser.diagnoseFold(*this, DiagLoc) << E->getSourceRange(); - for (const PartialDiagnosticAt &Note : Notes) - Diag(Note.first, Note.second); + S.Diag(Loc, diag::err_cconv_incomplete_param_type) + << Param->getDeclName() << FD->getDeclName() << CCName; + } + }; - if (Result) - *Result = EvalResult.Val.getInt(); - return E; + for (ParmVarDecl *Param : FD->parameters()) { + ParamIncompleteTypeDiagnoser Diagnoser(FD, Param); + S.RequireCompleteType(Loc, Param->getType(), Diagnoser); + } } namespace { - // Handle the case where we conclude a expression which we speculatively - // considered to be unevaluated is actually evaluated. - class TransformToPE : public TreeTransform { - typedef TreeTransform BaseTransform; +enum class OdrUseContext { + /// Declarations in this context are not odr-used. + None, + /// Declarations in this context are formally odr-used, but this is a + /// dependent context. + Dependent, + /// Declarations in this context are odr-used but not actually used (yet). + FormallyOdrUsed, + /// Declarations in this context are used. + Used +}; +} - public: - TransformToPE(Sema &SemaRef) : BaseTransform(SemaRef) { } +/// Are we within a context in which references to resolved functions or to +/// variables result in odr-use? +static OdrUseContext isOdrUseContext(Sema &SemaRef) { + OdrUseContext Result; - // Make sure we redo semantic analysis - bool AlwaysRebuild() { return true; } - bool ReplacingOriginal() { return true; } + switch (SemaRef.ExprEvalContexts.back().Context) { + case Sema::ExpressionEvaluationContext::Unevaluated: + case Sema::ExpressionEvaluationContext::UnevaluatedList: + case Sema::ExpressionEvaluationContext::UnevaluatedAbstract: + return OdrUseContext::None; - // We need to special-case DeclRefExprs referring to FieldDecls which - // are not part of a member pointer formation; normal TreeTransforming - // doesn't catch this case because of the way we represent them in the AST. - // FIXME: This is a bit ugly; is it really the best way to handle this - // case? - // - // Error on DeclRefExprs referring to FieldDecls. - ExprResult TransformDeclRefExpr(DeclRefExpr *E) { - if (isa(E->getDecl()) && - !SemaRef.isUnevaluatedContext()) - return SemaRef.Diag(E->getLocation(), - diag::err_invalid_non_static_member_use) - << E->getDecl() << E->getSourceRange(); + case Sema::ExpressionEvaluationContext::ConstantEvaluated: + case Sema::ExpressionEvaluationContext::ImmediateFunctionContext: + case Sema::ExpressionEvaluationContext::PotentiallyEvaluated: + Result = OdrUseContext::Used; + break; - return BaseTransform::TransformDeclRefExpr(E); - } + case Sema::ExpressionEvaluationContext::DiscardedStatement: + Result = OdrUseContext::FormallyOdrUsed; + break; - // Exception: filter out member pointer formation - ExprResult TransformUnaryOperator(UnaryOperator *E) { - if (E->getOpcode() == UO_AddrOf && E->getType()->isMemberPointerType()) - return E; + case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: + // A default argument formally results in odr-use, but doesn't actually + // result in a use in any real sense until it itself is used. + Result = OdrUseContext::FormallyOdrUsed; + break; + } - return BaseTransform::TransformUnaryOperator(E); - } + if (SemaRef.CurContext->isDependentContext()) + return OdrUseContext::Dependent; - // The body of a lambda-expression is in a separate expression evaluation - // context so never needs to be transformed. - // FIXME: Ideally we wouldn't transform the closure type either, and would - // just recreate the capture expressions and lambda expression. - StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body) { - return SkipLambdaBody(E, Body); - } - }; + return Result; } -ExprResult Sema::TransformToPotentiallyEvaluated(Expr *E) { - assert(isUnevaluatedContext() && - "Should only transform unevaluated expressions"); - ExprEvalContexts.back().Context = - ExprEvalContexts[ExprEvalContexts.size()-2].Context; - if (isUnevaluatedContext()) - return E; - return TransformToPE(*this).TransformExpr(E); -} +static bool isImplicitlyDefinableConstexprFunction(FunctionDecl *Func) { + if (!Func->isConstexpr()) + return false; -TypeSourceInfo *Sema::TransformToPotentiallyEvaluated(TypeSourceInfo *TInfo) { - assert(isUnevaluatedContext() && - "Should only transform unevaluated expressions"); - ExprEvalContexts.back().Context = parentEvaluationContext().Context; - if (isUnevaluatedContext()) - return TInfo; - return TransformToPE(*this).TransformType(TInfo); + if (Func->isImplicitlyInstantiable() || !Func->isUserProvided()) + return true; + auto *CCD = dyn_cast(Func); + return CCD && CCD->getInheritedConstructor(); } -void -Sema::PushExpressionEvaluationContext( - ExpressionEvaluationContext NewContext, Decl *LambdaContextDecl, - ExpressionEvaluationContextRecord::ExpressionKind ExprContext) { - ExprEvalContexts.emplace_back(NewContext, ExprCleanupObjects.size(), Cleanup, - LambdaContextDecl, ExprContext); +void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, + bool MightBeOdrUse) { + assert(Func && "No function?"); - // Discarded statements and immediate contexts nested in other - // discarded statements or immediate context are themselves - // a discarded statement or an immediate context, respectively. - ExprEvalContexts.back().InDiscardedStatement = - parentEvaluationContext().isDiscardedStatementContext(); + Func->setReferenced(); + + // Recursive functions aren't really used until they're used from some other + // context. + bool IsRecursiveCall = CurContext == Func; + + // C++11 [basic.def.odr]p3: + // A function whose name appears as a potentially-evaluated expression is + // odr-used if it is the unique lookup result or the selected member of a + // set of overloaded functions [...]. + // + // We (incorrectly) mark overload resolution as an unevaluated context, so we + // can just check that here. + OdrUseContext OdrUse = + MightBeOdrUse ? isOdrUseContext(*this) : OdrUseContext::None; + if (IsRecursiveCall && OdrUse == OdrUseContext::Used) + OdrUse = OdrUseContext::FormallyOdrUsed; - // C++23 [expr.const]/p15 - // An expression or conversion is in an immediate function context if [...] - // it is a subexpression of a manifestly constant-evaluated expression or - // conversion. - const auto &Prev = parentEvaluationContext(); - ExprEvalContexts.back().InImmediateFunctionContext = - Prev.isImmediateFunctionContext() || Prev.isConstantEvaluated(); + // Trivial default constructors and destructors are never actually used. + // FIXME: What about other special members? + if (Func->isTrivial() && !Func->hasAttr() && + OdrUse == OdrUseContext::Used) { + if (auto *Constructor = dyn_cast(Func)) + if (Constructor->isDefaultConstructor()) + OdrUse = OdrUseContext::FormallyOdrUsed; + if (isa(Func)) + OdrUse = OdrUseContext::FormallyOdrUsed; + } - ExprEvalContexts.back().InImmediateEscalatingFunctionContext = - Prev.InImmediateEscalatingFunctionContext; + // C++20 [expr.const]p12: + // A function [...] is needed for constant evaluation if it is [...] a + // constexpr function that is named by an expression that is potentially + // constant evaluated + bool NeededForConstantEvaluation = + isPotentiallyConstantEvaluatedContext(*this) && + isImplicitlyDefinableConstexprFunction(Func); - Cleanup.reset(); - if (!MaybeODRUseExprs.empty()) - std::swap(MaybeODRUseExprs, ExprEvalContexts.back().SavedMaybeODRUseExprs); -} + // Determine whether we require a function definition to exist, per + // C++11 [temp.inst]p3: + // Unless a function template specialization has been explicitly + // instantiated or explicitly specialized, the function template + // specialization is implicitly instantiated when the specialization is + // referenced in a context that requires a function definition to exist. + // C++20 [temp.inst]p7: + // The existence of a definition of a [...] function is considered to + // affect the semantics of the program if the [...] function is needed for + // constant evaluation by an expression + // C++20 [basic.def.odr]p10: + // Every program shall contain exactly one definition of every non-inline + // function or variable that is odr-used in that program outside of a + // discarded statement + // C++20 [special]p1: + // The implementation will implicitly define [defaulted special members] + // if they are odr-used or needed for constant evaluation. + // + // Note that we skip the implicit instantiation of templates that are only + // used in unused default arguments or by recursive calls to themselves. + // This is formally non-conforming, but seems reasonable in practice. + bool NeedDefinition = + !IsRecursiveCall && + (OdrUse == OdrUseContext::Used || + (NeededForConstantEvaluation && !Func->isPureVirtual())); -void -Sema::PushExpressionEvaluationContext( - ExpressionEvaluationContext NewContext, ReuseLambdaContextDecl_t, - ExpressionEvaluationContextRecord::ExpressionKind ExprContext) { - Decl *ClosureContextDecl = ExprEvalContexts.back().ManglingContextDecl; - PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext); -} + // C++14 [temp.expl.spec]p6: + // If a template [...] is explicitly specialized then that specialization + // shall be declared before the first use of that specialization that would + // cause an implicit instantiation to take place, in every translation unit + // in which such a use occurs + if (NeedDefinition && + (Func->getTemplateSpecializationKind() != TSK_Undeclared || + Func->getMemberSpecializationInfo())) + checkSpecializationReachability(Loc, Func); -namespace { + if (getLangOpts().CUDA) + CUDA().CheckCall(Loc, Func); -const DeclRefExpr *CheckPossibleDeref(Sema &S, const Expr *PossibleDeref) { - PossibleDeref = PossibleDeref->IgnoreParenImpCasts(); - if (const auto *E = dyn_cast(PossibleDeref)) { - if (E->getOpcode() == UO_Deref) - return CheckPossibleDeref(S, E->getSubExpr()); - } else if (const auto *E = dyn_cast(PossibleDeref)) { - return CheckPossibleDeref(S, E->getBase()); - } else if (const auto *E = dyn_cast(PossibleDeref)) { - return CheckPossibleDeref(S, E->getBase()); - } else if (const auto E = dyn_cast(PossibleDeref)) { - QualType Inner; - QualType Ty = E->getType(); - if (const auto *Ptr = Ty->getAs()) - Inner = Ptr->getPointeeType(); - else if (const auto *Arr = S.Context.getAsArrayType(Ty)) - Inner = Arr->getElementType(); - else - return nullptr; + // If we need a definition, try to create one. + if (NeedDefinition && !Func->getBody()) { + runWithSufficientStackSpace(Loc, [&] { + if (CXXConstructorDecl *Constructor = + dyn_cast(Func)) { + Constructor = cast(Constructor->getFirstDecl()); + if (Constructor->isDefaulted() && !Constructor->isDeleted()) { + if (Constructor->isDefaultConstructor()) { + if (Constructor->isTrivial() && + !Constructor->hasAttr()) + return; + DefineImplicitDefaultConstructor(Loc, Constructor); + } else if (Constructor->isCopyConstructor()) { + DefineImplicitCopyConstructor(Loc, Constructor); + } else if (Constructor->isMoveConstructor()) { + DefineImplicitMoveConstructor(Loc, Constructor); + } + } else if (Constructor->getInheritedConstructor()) { + DefineInheritingConstructor(Loc, Constructor); + } + } else if (CXXDestructorDecl *Destructor = + dyn_cast(Func)) { + Destructor = cast(Destructor->getFirstDecl()); + if (Destructor->isDefaulted() && !Destructor->isDeleted()) { + if (Destructor->isTrivial() && !Destructor->hasAttr()) + return; + DefineImplicitDestructor(Loc, Destructor); + } + if (Destructor->isVirtual() && getLangOpts().AppleKext) + MarkVTableUsed(Loc, Destructor->getParent()); + } else if (CXXMethodDecl *MethodDecl = dyn_cast(Func)) { + if (MethodDecl->isOverloadedOperator() && + MethodDecl->getOverloadedOperator() == OO_Equal) { + MethodDecl = cast(MethodDecl->getFirstDecl()); + if (MethodDecl->isDefaulted() && !MethodDecl->isDeleted()) { + if (MethodDecl->isCopyAssignmentOperator()) + DefineImplicitCopyAssignment(Loc, MethodDecl); + else if (MethodDecl->isMoveAssignmentOperator()) + DefineImplicitMoveAssignment(Loc, MethodDecl); + } + } else if (isa(MethodDecl) && + MethodDecl->getParent()->isLambda()) { + CXXConversionDecl *Conversion = + cast(MethodDecl->getFirstDecl()); + if (Conversion->isLambdaToBlockPointerConversion()) + DefineImplicitLambdaToBlockPointerConversion(Loc, Conversion); + else + DefineImplicitLambdaToFunctionPointerConversion(Loc, Conversion); + } else if (MethodDecl->isVirtual() && getLangOpts().AppleKext) + MarkVTableUsed(Loc, MethodDecl->getParent()); + } - if (Inner->hasAttr(attr::NoDeref)) - return E; - } - return nullptr; -} + if (Func->isDefaulted() && !Func->isDeleted()) { + DefaultedComparisonKind DCK = getDefaultedComparisonKind(Func); + if (DCK != DefaultedComparisonKind::None) + DefineDefaultedComparison(Loc, Func, DCK); + } -} // namespace + // Implicit instantiation of function templates and member functions of + // class templates. + if (Func->isImplicitlyInstantiable()) { + TemplateSpecializationKind TSK = + Func->getTemplateSpecializationKindForInstantiation(); + SourceLocation PointOfInstantiation = Func->getPointOfInstantiation(); + bool FirstInstantiation = PointOfInstantiation.isInvalid(); + if (FirstInstantiation) { + PointOfInstantiation = Loc; + if (auto *MSI = Func->getMemberSpecializationInfo()) + MSI->setPointOfInstantiation(Loc); + // FIXME: Notify listener. + else + Func->setTemplateSpecializationKind(TSK, PointOfInstantiation); + } else if (TSK != TSK_ImplicitInstantiation) { + // Use the point of use as the point of instantiation, instead of the + // point of explicit instantiation (which we track as the actual point + // of instantiation). This gives better backtraces in diagnostics. + PointOfInstantiation = Loc; + } -void Sema::WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec) { - for (const Expr *E : Rec.PossibleDerefs) { - const DeclRefExpr *DeclRef = CheckPossibleDeref(*this, E); - if (DeclRef) { - const ValueDecl *Decl = DeclRef->getDecl(); - Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type) - << Decl->getName() << E->getSourceRange(); - Diag(Decl->getLocation(), diag::note_previous_decl) << Decl->getName(); - } else { - Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type_no_decl) - << E->getSourceRange(); - } + if (FirstInstantiation || TSK != TSK_ImplicitInstantiation || + Func->isConstexpr()) { + if (isa(Func->getDeclContext()) && + cast(Func->getDeclContext())->isLocalClass() && + CodeSynthesisContexts.size()) + PendingLocalImplicitInstantiations.push_back( + std::make_pair(Func, PointOfInstantiation)); + else if (Func->isConstexpr()) + // Do not defer instantiations of constexpr functions, to avoid the + // expression evaluator needing to call back into Sema if it sees a + // call to such a function. + InstantiateFunctionDefinition(PointOfInstantiation, Func); + else { + Func->setInstantiationIsPending(true); + PendingInstantiations.push_back( + std::make_pair(Func, PointOfInstantiation)); + if (llvm::isTimeTraceVerbose()) { + llvm::timeTraceAddInstantEvent("DeferInstantiation", [&] { + std::string Name; + llvm::raw_string_ostream OS(Name); + Func->getNameForDiagnostic(OS, getPrintingPolicy(), + /*Qualified=*/true); + return Name; + }); + } + // Notify the consumer that a function was implicitly instantiated. + Consumer.HandleCXXImplicitFunctionInstantiation(Func); + } + } + } else { + // Walk redefinitions, as some of them may be instantiable. + for (auto *i : Func->redecls()) { + if (!i->isUsed(false) && i->isImplicitlyInstantiable()) + MarkFunctionReferenced(Loc, i, MightBeOdrUse); + } + } + }); } - Rec.PossibleDerefs.clear(); -} - -void Sema::CheckUnusedVolatileAssignment(Expr *E) { - if (!E->getType().isVolatileQualified() || !getLangOpts().CPlusPlus20) - return; - // Note: ignoring parens here is not justified by the standard rules, but - // ignoring parentheses seems like a more reasonable approach, and this only - // drives a deprecation warning so doesn't affect conformance. - if (auto *BO = dyn_cast(E->IgnoreParenImpCasts())) { - if (BO->getOpcode() == BO_Assign) { - auto &LHSs = ExprEvalContexts.back().VolatileAssignmentLHSs; - llvm::erase(LHSs, BO->getLHS()); + // If a constructor was defined in the context of a default parameter + // or of another default member initializer (ie a PotentiallyEvaluatedIfUsed + // context), its initializers may not be referenced yet. + if (CXXConstructorDecl *Constructor = dyn_cast(Func)) { + EnterExpressionEvaluationContext EvalContext( + *this, + Constructor->isImmediateFunction() + ? ExpressionEvaluationContext::ImmediateFunctionContext + : ExpressionEvaluationContext::PotentiallyEvaluated, + Constructor); + for (CXXCtorInitializer *Init : Constructor->inits()) { + if (Init->isInClassMemberInitializer()) + runWithSufficientStackSpace(Init->getSourceLocation(), [&]() { + MarkDeclarationsReferencedInExpr(Init->getInit()); + }); } } -} -void Sema::MarkExpressionAsImmediateEscalating(Expr *E) { - assert(getLangOpts().CPlusPlus20 && - ExprEvalContexts.back().InImmediateEscalatingFunctionContext && - "Cannot mark an immediate escalating expression outside of an " - "immediate escalating context"); - if (auto *Call = dyn_cast(E->IgnoreImplicit()); - Call && Call->getCallee()) { - if (auto *DeclRef = - dyn_cast(Call->getCallee()->IgnoreImplicit())) - DeclRef->setIsImmediateEscalating(true); - } else if (auto *Ctr = dyn_cast(E->IgnoreImplicit())) { - Ctr->setIsImmediateEscalating(true); - } else if (auto *DeclRef = dyn_cast(E->IgnoreImplicit())) { - DeclRef->setIsImmediateEscalating(true); - } else { - assert(false && "expected an immediately escalating expression"); - } - if (FunctionScopeInfo *FI = getCurFunction()) - FI->FoundImmediateEscalatingExpression = true; -} + // C++14 [except.spec]p17: + // An exception-specification is considered to be needed when: + // - the function is odr-used or, if it appears in an unevaluated operand, + // would be odr-used if the expression were potentially-evaluated; + // + // Note, we do this even if MightBeOdrUse is false. That indicates that the + // function is a pure virtual function we're calling, and in that case the + // function was selected by overload resolution and we need to resolve its + // exception specification for a different reason. + const FunctionProtoType *FPT = Func->getType()->getAs(); + if (FPT && isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) + ResolveExceptionSpec(Loc, FPT); -ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) { - if (isUnevaluatedContext() || !E.isUsable() || !Decl || - !Decl->isImmediateFunction() || isAlwaysConstantEvaluatedContext() || - isCheckingDefaultArgumentOrInitializer() || - RebuildingImmediateInvocation || isImmediateFunctionContext()) - return E; + // A callee could be called by a host function then by a device function. + // If we only try recording once, we will miss recording the use on device + // side. Therefore keep trying until it is recorded. + if (LangOpts.OffloadImplicitHostDeviceTemplates && LangOpts.CUDAIsDevice && + !getASTContext().CUDAImplicitHostDeviceFunUsedByDevice.count(Func)) + CUDA().RecordImplicitHostDeviceFuncUsedByDevice(Func); - /// Opportunistically remove the callee from ReferencesToConsteval if we can. - /// It's OK if this fails; we'll also remove this in - /// HandleImmediateInvocations, but catching it here allows us to avoid - /// walking the AST looking for it in simple cases. - if (auto *Call = dyn_cast(E.get()->IgnoreImplicit())) - if (auto *DeclRef = - dyn_cast(Call->getCallee()->IgnoreImplicit())) - ExprEvalContexts.back().ReferenceToConsteval.erase(DeclRef); + // If this is the first "real" use, act on that. + if (OdrUse == OdrUseContext::Used && !Func->isUsed(/*CheckUsedAttr=*/false)) { + // Keep track of used but undefined functions. + if (!Func->isDefined() && !Func->isInAnotherModuleUnit()) { + if (mightHaveNonExternalLinkage(Func)) + UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); + else if (Func->getMostRecentDecl()->isInlined() && + !LangOpts.GNUInline && + !Func->getMostRecentDecl()->hasAttr()) + UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); + else if (isExternalWithNoLinkageType(Func)) + UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); + } - // C++23 [expr.const]/p16 - // An expression or conversion is immediate-escalating if it is not initially - // in an immediate function context and it is [...] an immediate invocation - // that is not a constant expression and is not a subexpression of an - // immediate invocation. - APValue Cached; - auto CheckConstantExpressionAndKeepResult = [&]() { - llvm::SmallVector Notes; - Expr::EvalResult Eval; - Eval.Diag = &Notes; - bool Res = E.get()->EvaluateAsConstantExpr( - Eval, getASTContext(), ConstantExprKind::ImmediateInvocation); - if (Res && Notes.empty()) { - Cached = std::move(Eval.Val); - return true; + // Some x86 Windows calling conventions mangle the size of the parameter + // pack into the name. Computing the size of the parameters requires the + // parameter types to be complete. Check that now. + if (funcHasParameterSizeMangling(*this, Func)) + CheckCompleteParameterTypesForMangler(*this, Func, Loc); + + // In the MS C++ ABI, the compiler emits destructor variants where they are + // used. If the destructor is used here but defined elsewhere, mark the + // virtual base destructors referenced. If those virtual base destructors + // are inline, this will ensure they are defined when emitting the complete + // destructor variant. This checking may be redundant if the destructor is + // provided later in this TU. + if (Context.getTargetInfo().getCXXABI().isMicrosoft()) { + if (auto *Dtor = dyn_cast(Func)) { + CXXRecordDecl *Parent = Dtor->getParent(); + if (Parent->getNumVBases() > 0 && !Dtor->getBody()) + CheckCompleteDestructorVariant(Loc, Dtor); + } } - return false; - }; - if (!E.get()->isValueDependent() && - ExprEvalContexts.back().InImmediateEscalatingFunctionContext && - !CheckConstantExpressionAndKeepResult()) { - MarkExpressionAsImmediateEscalating(E.get()); - return E; + Func->markUsed(Context); } +} - if (Cleanup.exprNeedsCleanups()) { - // Since an immediate invocation is a full expression itself - it requires - // an additional ExprWithCleanups node, but it can participate to a bigger - // full expression which actually requires cleanups to be run after so - // create ExprWithCleanups without using MaybeCreateExprWithCleanups as it - // may discard cleanups for outer expression too early. +/// Directly mark a variable odr-used. Given a choice, prefer to use +/// MarkVariableReferenced since it does additional checks and then +/// calls MarkVarDeclODRUsed. +/// If the variable must be captured: +/// - if FunctionScopeIndexToStopAt is null, capture it in the CurContext +/// - else capture it in the DeclContext that maps to the +/// *FunctionScopeIndexToStopAt on the FunctionScopeInfo stack. +static void +MarkVarDeclODRUsed(ValueDecl *V, SourceLocation Loc, Sema &SemaRef, + const unsigned *const FunctionScopeIndexToStopAt = nullptr) { + // Keep track of used but undefined variables. + // FIXME: We shouldn't suppress this warning for static data members. + VarDecl *Var = V->getPotentiallyDecomposedVarDecl(); + assert(Var && "expected a capturable variable"); - // Note that ExprWithCleanups created here must always have empty cleanup - // objects: - // - compound literals do not create cleanup objects in C++ and immediate - // invocations are C++-only. - // - blocks are not allowed inside constant expressions and compiler will - // issue an error if they appear there. - // - // Hence, in correct code any cleanup objects created inside current - // evaluation context must be outside the immediate invocation. - E = ExprWithCleanups::Create(getASTContext(), E.get(), - Cleanup.cleanupsHaveSideEffects(), {}); + if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly && + (!Var->isExternallyVisible() || Var->isInline() || + SemaRef.isExternalWithNoLinkageType(Var)) && + !(Var->isStaticDataMember() && Var->hasInit())) { + SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()]; + if (old.isInvalid()) + old = Loc; } + QualType CaptureType, DeclRefType; + if (SemaRef.LangOpts.OpenMP) + SemaRef.OpenMP().tryCaptureOpenMPLambdas(V); + SemaRef.tryCaptureVariable(V, Loc, Sema::TryCapture_Implicit, + /*EllipsisLoc*/ SourceLocation(), + /*BuildAndDiagnose*/ true, CaptureType, + DeclRefType, FunctionScopeIndexToStopAt); - ConstantExpr *Res = ConstantExpr::Create( - getASTContext(), E.get(), - ConstantExpr::getStorageKind(Decl->getReturnType().getTypePtr(), - getASTContext()), - /*IsImmediateInvocation*/ true); - if (Cached.hasValue()) - Res->MoveIntoResult(Cached, getASTContext()); - /// Value-dependent constant expressions should not be immediately - /// evaluated until they are instantiated. - if (!Res->isValueDependent()) - ExprEvalContexts.back().ImmediateInvocationCandidates.emplace_back(Res, 0); - return Res; + if (SemaRef.LangOpts.CUDA && Var->hasGlobalStorage()) { + auto *FD = dyn_cast_or_null(SemaRef.CurContext); + auto VarTarget = SemaRef.CUDA().IdentifyTarget(Var); + auto UserTarget = SemaRef.CUDA().IdentifyTarget(FD); + if (VarTarget == SemaCUDA::CVT_Host && + (UserTarget == CUDAFunctionTarget::Device || + UserTarget == CUDAFunctionTarget::HostDevice || + UserTarget == CUDAFunctionTarget::Global)) { + // Diagnose ODR-use of host global variables in device functions. + // Reference of device global variables in host functions is allowed + // through shadow variables therefore it is not diagnosed. + if (SemaRef.LangOpts.CUDAIsDevice && !SemaRef.LangOpts.HIPStdPar) { + SemaRef.targetDiag(Loc, diag::err_ref_bad_target) + << /*host*/ 2 << /*variable*/ 1 << Var + << llvm::to_underlying(UserTarget); + SemaRef.targetDiag(Var->getLocation(), + Var->getType().isConstQualified() + ? diag::note_cuda_const_var_unpromoted + : diag::note_cuda_host_var); + } + } else if (VarTarget == SemaCUDA::CVT_Device && + !Var->hasAttr() && + (UserTarget == CUDAFunctionTarget::Host || + UserTarget == CUDAFunctionTarget::HostDevice)) { + // Record a CUDA/HIP device side variable if it is ODR-used + // by host code. This is done conservatively, when the variable is + // referenced in any of the following contexts: + // - a non-function context + // - a host function + // - a host device function + // This makes the ODR-use of the device side variable by host code to + // be visible in the device compilation for the compiler to be able to + // emit template variables instantiated by host code only and to + // externalize the static device side variable ODR-used by host code. + if (!Var->hasExternalStorage()) + SemaRef.getASTContext().CUDADeviceVarODRUsedByHost.insert(Var); + else if (SemaRef.LangOpts.GPURelocatableDeviceCode && + (!FD || (!FD->getDescribedFunctionTemplate() && + SemaRef.getASTContext().GetGVALinkageForFunction(FD) == + GVA_StrongExternal))) + SemaRef.getASTContext().CUDAExternalDeviceDeclODRUsedByHost.insert(Var); + } + } + + V->markUsed(SemaRef.Context); } -static void EvaluateAndDiagnoseImmediateInvocation( - Sema &SemaRef, Sema::ImmediateInvocationCandidate Candidate) { - llvm::SmallVector Notes; - Expr::EvalResult Eval; - Eval.Diag = &Notes; - ConstantExpr *CE = Candidate.getPointer(); - bool Result = CE->EvaluateAsConstantExpr( - Eval, SemaRef.getASTContext(), ConstantExprKind::ImmediateInvocation); - if (!Result || !Notes.empty()) { - SemaRef.FailedImmediateInvocations.insert(CE); - Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit(); - if (auto *FunctionalCast = dyn_cast(InnerExpr)) - InnerExpr = FunctionalCast->getSubExpr()->IgnoreImplicit(); - FunctionDecl *FD = nullptr; - if (auto *Call = dyn_cast(InnerExpr)) - FD = cast(Call->getCalleeDecl()); - else if (auto *Call = dyn_cast(InnerExpr)) - FD = Call->getConstructor(); - else if (auto *Cast = dyn_cast(InnerExpr)) - FD = dyn_cast_or_null(Cast->getConversionFunction()); +void Sema::MarkCaptureUsedInEnclosingContext(ValueDecl *Capture, + SourceLocation Loc, + unsigned CapturingScopeIndex) { + MarkVarDeclODRUsed(Capture, Loc, *this, &CapturingScopeIndex); +} - assert(FD && FD->isImmediateFunction() && - "could not find an immediate function in this expression"); - if (FD->isInvalidDecl()) - return; - SemaRef.Diag(CE->getBeginLoc(), diag::err_invalid_consteval_call) - << FD << FD->isConsteval(); - if (auto Context = - SemaRef.InnermostDeclarationWithDelayedImmediateInvocations()) { - SemaRef.Diag(Context->Loc, diag::note_invalid_consteval_initializer) - << Context->Decl; - SemaRef.Diag(Context->Decl->getBeginLoc(), diag::note_declared_at); - } - if (!FD->isConsteval()) - SemaRef.DiagnoseImmediateEscalatingReason(FD); - for (auto &Note : Notes) - SemaRef.Diag(Note.first, Note.second); +void diagnoseUncapturableValueReferenceOrBinding(Sema &S, SourceLocation loc, + ValueDecl *var) { + DeclContext *VarDC = var->getDeclContext(); + + // If the parameter still belongs to the translation unit, then + // we're actually just using one parameter in the declaration of + // the next. + if (isa(var) && + isa(VarDC)) + return; + + // For C code, don't diagnose about capture if we're not actually in code + // right now; it's impossible to write a non-constant expression outside of + // function context, so we'll get other (more useful) diagnostics later. + // + // For C++, things get a bit more nasty... it would be nice to suppress this + // diagnostic for certain cases like using a local variable in an array bound + // for a member of a local class, but the correct predicate is not obvious. + if (!S.getLangOpts().CPlusPlus && !S.CurContext->isFunctionOrMethod()) return; + + unsigned ValueKind = isa(var) ? 1 : 0; + unsigned ContextKind = 3; // unknown + if (isa(VarDC) && + cast(VarDC->getParent())->isLambda()) { + ContextKind = 2; + } else if (isa(VarDC)) { + ContextKind = 0; + } else if (isa(VarDC)) { + ContextKind = 1; } - CE->MoveIntoResult(Eval.Val, SemaRef.getASTContext()); + + S.Diag(loc, diag::err_reference_to_local_in_enclosing_context) + << var << ValueKind << ContextKind << VarDC; + S.Diag(var->getLocation(), diag::note_entity_declared_at) + << var; + + // FIXME: Add additional diagnostic info about class etc. which prevents + // capture. } -static void RemoveNestedImmediateInvocation( - Sema &SemaRef, Sema::ExpressionEvaluationContextRecord &Rec, - SmallVector::reverse_iterator It) { - struct ComplexRemove : TreeTransform { - using Base = TreeTransform; - llvm::SmallPtrSetImpl &DRSet; - SmallVector &IISet; - SmallVector::reverse_iterator - CurrentII; - ComplexRemove(Sema &SemaRef, llvm::SmallPtrSetImpl &DR, - SmallVector &II, - SmallVector::reverse_iterator Current) - : Base(SemaRef), DRSet(DR), IISet(II), CurrentII(Current) {} - void RemoveImmediateInvocation(ConstantExpr* E) { - auto It = std::find_if(CurrentII, IISet.rend(), - [E](Sema::ImmediateInvocationCandidate Elem) { - return Elem.getPointer() == E; - }); - // It is possible that some subexpression of the current immediate - // invocation was handled from another expression evaluation context. Do - // not handle the current immediate invocation if some of its - // subexpressions failed before. - if (It == IISet.rend()) { - if (SemaRef.FailedImmediateInvocations.contains(E)) - CurrentII->setInt(1); - } else { - It->setInt(1); // Mark as deleted - } - } - ExprResult TransformConstantExpr(ConstantExpr *E) { - if (!E->isImmediateInvocation()) - return Base::TransformConstantExpr(E); - RemoveImmediateInvocation(E); - return Base::TransformExpr(E->getSubExpr()); - } - /// Base::TransfromCXXOperatorCallExpr doesn't traverse the callee so - /// we need to remove its DeclRefExpr from the DRSet. - ExprResult TransformCXXOperatorCallExpr(CXXOperatorCallExpr *E) { - DRSet.erase(cast(E->getCallee()->IgnoreImplicit())); - return Base::TransformCXXOperatorCallExpr(E); - } - /// Base::TransformUserDefinedLiteral doesn't preserve the - /// UserDefinedLiteral node. - ExprResult TransformUserDefinedLiteral(UserDefinedLiteral *E) { return E; } - /// Base::TransformInitializer skips ConstantExpr so we need to visit them - /// here. - ExprResult TransformInitializer(Expr *Init, bool NotCopyInit) { - if (!Init) - return Init; +static bool isVariableAlreadyCapturedInScopeInfo(CapturingScopeInfo *CSI, + ValueDecl *Var, + bool &SubCapturesAreNested, + QualType &CaptureType, + QualType &DeclRefType) { + // Check whether we've already captured it. + if (CSI->CaptureMap.count(Var)) { + // If we found a capture, any subcaptures are nested. + SubCapturesAreNested = true; - // We cannot use IgnoreImpCasts because we need to preserve - // full expressions. - while (true) { - if (auto *ICE = dyn_cast(Init)) - Init = ICE->getSubExpr(); - else if (auto *ICE = dyn_cast(Init)) - Init = ICE->getSubExpr(); - else - break; - } - /// ConstantExprs are the first layer of implicit node to be removed so if - /// Init isn't a ConstantExpr, no ConstantExpr will be skipped. - if (auto *CE = dyn_cast(Init); - CE && CE->isImmediateInvocation()) - RemoveImmediateInvocation(CE); - return Base::TransformInitializer(Init, NotCopyInit); - } - ExprResult TransformDeclRefExpr(DeclRefExpr *E) { - DRSet.erase(E); - return E; - } - ExprResult TransformLambdaExpr(LambdaExpr *E) { - // Do not rebuild lambdas to avoid creating a new type. - // Lambdas have already been processed inside their eval contexts. - return E; - } - bool AlwaysRebuild() { return false; } - bool ReplacingOriginal() { return true; } - bool AllowSkippingCXXConstructExpr() { - bool Res = AllowSkippingFirstCXXConstructExpr; - AllowSkippingFirstCXXConstructExpr = true; - return Res; - } - bool AllowSkippingFirstCXXConstructExpr = true; - } Transformer(SemaRef, Rec.ReferenceToConsteval, - Rec.ImmediateInvocationCandidates, It); + // Retrieve the capture type for this variable. + CaptureType = CSI->getCapture(Var).getCaptureType(); - /// CXXConstructExpr with a single argument are getting skipped by - /// TreeTransform in some situtation because they could be implicit. This - /// can only occur for the top-level CXXConstructExpr because it is used - /// nowhere in the expression being transformed therefore will not be rebuilt. - /// Setting AllowSkippingFirstCXXConstructExpr to false will prevent from - /// skipping the first CXXConstructExpr. - if (isa(It->getPointer()->IgnoreImplicit())) - Transformer.AllowSkippingFirstCXXConstructExpr = false; + // Compute the type of an expression that refers to this variable. + DeclRefType = CaptureType.getNonReferenceType(); - ExprResult Res = Transformer.TransformExpr(It->getPointer()->getSubExpr()); - // The result may not be usable in case of previous compilation errors. - // In this case evaluation of the expression may result in crash so just - // don't do anything further with the result. - if (Res.isUsable()) { - Res = SemaRef.MaybeCreateExprWithCleanups(Res); - It->getPointer()->setSubExpr(Res.get()); + // Similarly to mutable captures in lambda, all the OpenMP captures by copy + // are mutable in the sense that user can change their value - they are + // private instances of the captured declarations. + const Capture &Cap = CSI->getCapture(Var); + // C++ [expr.prim.lambda]p10: + // The type of such a data member is [...] an lvalue reference to the + // referenced function type if the entity is a reference to a function. + // [...] + if (Cap.isCopyCapture() && !DeclRefType->isFunctionType() && + !(isa(CSI) && + !cast(CSI)->lambdaCaptureShouldBeConst()) && + !(isa(CSI) && + cast(CSI)->CapRegionKind == CR_OpenMP)) + DeclRefType.addConst(); + return true; } + return false; } -static void -HandleImmediateInvocations(Sema &SemaRef, - Sema::ExpressionEvaluationContextRecord &Rec) { - if ((Rec.ImmediateInvocationCandidates.size() == 0 && - Rec.ReferenceToConsteval.size() == 0) || - Rec.isImmediateFunctionContext() || SemaRef.RebuildingImmediateInvocation) - return; - - /// When we have more than 1 ImmediateInvocationCandidates or previously - /// failed immediate invocations, we need to check for nested - /// ImmediateInvocationCandidates in order to avoid duplicate diagnostics. - /// Otherwise we only need to remove ReferenceToConsteval in the immediate - /// invocation. - if (Rec.ImmediateInvocationCandidates.size() > 1 || - !SemaRef.FailedImmediateInvocations.empty()) { +// Only block literals, captured statements, and lambda expressions can +// capture; other scopes don't work. +static DeclContext *getParentOfCapturingContextOrNull(DeclContext *DC, + ValueDecl *Var, + SourceLocation Loc, + const bool Diagnose, + Sema &S) { + if (isa(DC) || isa(DC) || isLambdaCallOperator(DC)) + return getLambdaAwareParentOfDeclContext(DC); - /// Prevent sema calls during the tree transform from adding pointers that - /// are already in the sets. - llvm::SaveAndRestore DisableIITracking( - SemaRef.RebuildingImmediateInvocation, true); + VarDecl *Underlying = Var->getPotentiallyDecomposedVarDecl(); + if (Underlying) { + if (Underlying->hasLocalStorage() && Diagnose) + diagnoseUncapturableValueReferenceOrBinding(S, Loc, Var); + } + return nullptr; +} - /// Prevent diagnostic during tree transfrom as they are duplicates - Sema::TentativeAnalysisScope DisableDiag(SemaRef); +// Certain capturing entities (lambdas, blocks etc.) are not allowed to capture +// certain types of variables (unnamed, variably modified types etc.) +// so check for eligibility. +static bool isVariableCapturable(CapturingScopeInfo *CSI, ValueDecl *Var, + SourceLocation Loc, const bool Diagnose, + Sema &S) { - for (auto It = Rec.ImmediateInvocationCandidates.rbegin(); - It != Rec.ImmediateInvocationCandidates.rend(); It++) - if (!It->getInt()) - RemoveNestedImmediateInvocation(SemaRef, Rec, It); - } else if (Rec.ImmediateInvocationCandidates.size() == 1 && - Rec.ReferenceToConsteval.size()) { - struct SimpleRemove : DynamicRecursiveASTVisitor { - llvm::SmallPtrSetImpl &DRSet; - SimpleRemove(llvm::SmallPtrSetImpl &S) : DRSet(S) {} - bool VisitDeclRefExpr(DeclRefExpr *E) override { - DRSet.erase(E); - return DRSet.size(); - } - } Visitor(Rec.ReferenceToConsteval); - Visitor.TraverseStmt( - Rec.ImmediateInvocationCandidates.front().getPointer()->getSubExpr()); - } - for (auto CE : Rec.ImmediateInvocationCandidates) - if (!CE.getInt()) - EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE); - for (auto *DR : Rec.ReferenceToConsteval) { - // If the expression is immediate escalating, it is not an error; - // The outer context itself becomes immediate and further errors, - // if any, will be handled by DiagnoseImmediateEscalatingReason. - if (DR->isImmediateEscalating()) - continue; - auto *FD = cast(DR->getDecl()); - const NamedDecl *ND = FD; - if (const auto *MD = dyn_cast(ND); - MD && (MD->isLambdaStaticInvoker() || isLambdaCallOperator(MD))) - ND = MD->getParent(); + assert((isa(Var)) && + "Only variables and structured bindings can be captured"); - // C++23 [expr.const]/p16 - // An expression or conversion is immediate-escalating if it is not - // initially in an immediate function context and it is [...] a - // potentially-evaluated id-expression that denotes an immediate function - // that is not a subexpression of an immediate invocation. - bool ImmediateEscalating = false; - bool IsPotentiallyEvaluated = - Rec.Context == - Sema::ExpressionEvaluationContext::PotentiallyEvaluated || - Rec.Context == - Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed; - if (SemaRef.inTemplateInstantiation() && IsPotentiallyEvaluated) - ImmediateEscalating = Rec.InImmediateEscalatingFunctionContext; + bool IsBlock = isa(CSI); + bool IsLambda = isa(CSI); - if (!Rec.InImmediateEscalatingFunctionContext || - (SemaRef.inTemplateInstantiation() && !ImmediateEscalating)) { - SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address) - << ND << isa(ND) << FD->isConsteval(); - if (!FD->getBuiltinID()) - SemaRef.Diag(ND->getLocation(), diag::note_declared_at); - if (auto Context = - SemaRef.InnermostDeclarationWithDelayedImmediateInvocations()) { - SemaRef.Diag(Context->Loc, diag::note_invalid_consteval_initializer) - << Context->Decl; - SemaRef.Diag(Context->Decl->getBeginLoc(), diag::note_declared_at); + // Lambdas are not allowed to capture unnamed variables + // (e.g. anonymous unions). + // FIXME: The C++11 rule don't actually state this explicitly, but I'm + // assuming that's the intent. + if (IsLambda && !Var->getDeclName()) { + if (Diagnose) { + S.Diag(Loc, diag::err_lambda_capture_anonymous_var); + S.Diag(Var->getLocation(), diag::note_declared_at); + } + return false; + } + + // Prohibit variably-modified types in blocks; they're difficult to deal with. + if (Var->getType()->isVariablyModifiedType() && IsBlock) { + if (Diagnose) { + S.Diag(Loc, diag::err_ref_vm_type); + S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; + } + return false; + } + // Prohibit structs with flexible array members too. + // We cannot capture what is in the tail end of the struct. + if (const RecordType *VTTy = Var->getType()->getAs()) { + if (VTTy->getDecl()->hasFlexibleArrayMember()) { + if (Diagnose) { + if (IsBlock) + S.Diag(Loc, diag::err_ref_flexarray_type); + else + S.Diag(Loc, diag::err_lambda_capture_flexarray_type) << Var; + S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; } - if (FD->isImmediateEscalating() && !FD->isConsteval()) - SemaRef.DiagnoseImmediateEscalatingReason(FD); + return false; + } + } + const bool HasBlocksAttr = Var->hasAttr(); + // Lambdas and captured statements are not allowed to capture __block + // variables; they don't support the expected semantics. + if (HasBlocksAttr && (IsLambda || isa(CSI))) { + if (Diagnose) { + S.Diag(Loc, diag::err_capture_block_variable) << Var << !IsLambda; + S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; + } + return false; + } + // OpenCL v2.0 s6.12.5: Blocks cannot reference/capture other blocks + if (S.getLangOpts().OpenCL && IsBlock && + Var->getType()->isBlockPointerType()) { + if (Diagnose) + S.Diag(Loc, diag::err_opencl_block_ref_block); + return false; + } - } else { - SemaRef.MarkExpressionAsImmediateEscalating(DR); + if (isa(Var)) { + if (!IsLambda || !S.getLangOpts().CPlusPlus) { + if (Diagnose) + diagnoseUncapturableValueReferenceOrBinding(S, Loc, Var); + return false; + } else if (Diagnose && S.getLangOpts().CPlusPlus) { + S.Diag(Loc, S.LangOpts.CPlusPlus20 + ? diag::warn_cxx17_compat_capture_binding + : diag::ext_capture_binding) + << Var; + S.Diag(Var->getLocation(), diag::note_entity_declared_at) << Var; } } -} -void Sema::PopExpressionEvaluationContext() { - ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back(); - unsigned NumTypos = Rec.NumTypos; + return true; +} - if (!Rec.Lambdas.empty()) { - using ExpressionKind = ExpressionEvaluationContextRecord::ExpressionKind; - if (!getLangOpts().CPlusPlus20 && - (Rec.ExprContext == ExpressionKind::EK_TemplateArgument || - Rec.isUnevaluated() || - (Rec.isConstantEvaluated() && !getLangOpts().CPlusPlus17))) { - unsigned D; - if (Rec.isUnevaluated()) { - // C++11 [expr.prim.lambda]p2: - // A lambda-expression shall not appear in an unevaluated operand - // (Clause 5). - D = diag::err_lambda_unevaluated_operand; - } else if (Rec.isConstantEvaluated() && !getLangOpts().CPlusPlus17) { - // C++1y [expr.const]p2: - // A conditional-expression e is a core constant expression unless the - // evaluation of e, following the rules of the abstract machine, would - // evaluate [...] a lambda-expression. - D = diag::err_lambda_in_constant_expression; - } else if (Rec.ExprContext == ExpressionKind::EK_TemplateArgument) { - // C++17 [expr.prim.lamda]p2: - // A lambda-expression shall not appear [...] in a template-argument. - D = diag::err_lambda_in_invalid_context; - } else - llvm_unreachable("Couldn't infer lambda error message."); +// Returns true if the capture by block was successful. +static bool captureInBlock(BlockScopeInfo *BSI, ValueDecl *Var, + SourceLocation Loc, const bool BuildAndDiagnose, + QualType &CaptureType, QualType &DeclRefType, + const bool Nested, Sema &S, bool Invalid) { + bool ByRef = false; - for (const auto *L : Rec.Lambdas) - Diag(L->getBeginLoc(), D); + // Blocks are not allowed to capture arrays, excepting OpenCL. + // OpenCL v2.0 s1.12.5 (revision 40): arrays are captured by reference + // (decayed to pointers). + if (!Invalid && !S.getLangOpts().OpenCL && CaptureType->isArrayType()) { + if (BuildAndDiagnose) { + S.Diag(Loc, diag::err_ref_array_type); + S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; + Invalid = true; + } else { + return false; } } - // Append the collected materialized temporaries into previous context before - // exit if the previous also is a lifetime extending context. - if (getLangOpts().CPlusPlus23 && Rec.InLifetimeExtendingContext && - parentEvaluationContext().InLifetimeExtendingContext && - !Rec.ForRangeLifetimeExtendTemps.empty()) { - parentEvaluationContext().ForRangeLifetimeExtendTemps.append( - Rec.ForRangeLifetimeExtendTemps); + // Forbid the block-capture of autoreleasing variables. + if (!Invalid && + CaptureType.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) { + if (BuildAndDiagnose) { + S.Diag(Loc, diag::err_arc_autoreleasing_capture) + << /*block*/ 0; + S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; + Invalid = true; + } else { + return false; + } } - WarnOnPendingNoDerefs(Rec); - HandleImmediateInvocations(*this, Rec); + // Warn about implicitly autoreleasing indirect parameters captured by blocks. + if (const auto *PT = CaptureType->getAs()) { + QualType PointeeTy = PT->getPointeeType(); - // Warn on any volatile-qualified simple-assignments that are not discarded- - // value expressions nor unevaluated operands (those cases get removed from - // this list by CheckUnusedVolatileAssignment). - for (auto *BO : Rec.VolatileAssignmentLHSs) - Diag(BO->getBeginLoc(), diag::warn_deprecated_simple_assign_volatile) - << BO->getType(); + if (!Invalid && PointeeTy->getAs() && + PointeeTy.getObjCLifetime() == Qualifiers::OCL_Autoreleasing && + !S.Context.hasDirectOwnershipQualifier(PointeeTy)) { + if (BuildAndDiagnose) { + SourceLocation VarLoc = Var->getLocation(); + S.Diag(Loc, diag::warn_block_capture_autoreleasing); + S.Diag(VarLoc, diag::note_declare_parameter_strong); + } + } + } - // When are coming out of an unevaluated context, clear out any - // temporaries that we may have created as part of the evaluation of - // the expression in that context: they aren't relevant because they - // will never be constructed. - if (Rec.isUnevaluated() || Rec.isConstantEvaluated()) { - ExprCleanupObjects.erase(ExprCleanupObjects.begin() + Rec.NumCleanupObjects, - ExprCleanupObjects.end()); - Cleanup = Rec.ParentCleanup; - CleanupVarDeclMarking(); - std::swap(MaybeODRUseExprs, Rec.SavedMaybeODRUseExprs); - // Otherwise, merge the contexts together. + const bool HasBlocksAttr = Var->hasAttr(); + if (HasBlocksAttr || CaptureType->isReferenceType() || + (S.getLangOpts().OpenMP && S.OpenMP().isOpenMPCapturedDecl(Var))) { + // Block capture by reference does not change the capture or + // declaration reference types. + ByRef = true; } else { - Cleanup.mergeFrom(Rec.ParentCleanup); - MaybeODRUseExprs.insert_range(Rec.SavedMaybeODRUseExprs); + // Block capture by copy introduces 'const'. + CaptureType = CaptureType.getNonReferenceType().withConst(); + DeclRefType = CaptureType; } - // Pop the current expression evaluation context off the stack. - ExprEvalContexts.pop_back(); - - // The global expression evaluation context record is never popped. - ExprEvalContexts.back().NumTypos += NumTypos; -} - -void Sema::DiscardCleanupsInEvaluationContext() { - ExprCleanupObjects.erase( - ExprCleanupObjects.begin() + ExprEvalContexts.back().NumCleanupObjects, - ExprCleanupObjects.end()); - Cleanup.reset(); - MaybeODRUseExprs.clear(); -} + // Actually capture the variable. + if (BuildAndDiagnose) + BSI->addCapture(Var, HasBlocksAttr, ByRef, Nested, Loc, SourceLocation(), + CaptureType, Invalid); -ExprResult Sema::HandleExprEvaluationContextForTypeof(Expr *E) { - ExprResult Result = CheckPlaceholderExpr(E); - if (Result.isInvalid()) - return ExprError(); - E = Result.get(); - if (!E->getType()->isVariablyModifiedType()) - return E; - return TransformToPotentiallyEvaluated(E); + return !Invalid; } -/// Are we in a context that is potentially constant evaluated per C++20 -/// [expr.const]p12? -static bool isPotentiallyConstantEvaluatedContext(Sema &SemaRef) { - /// C++2a [expr.const]p12: - // An expression or conversion is potentially constant evaluated if it is - switch (SemaRef.ExprEvalContexts.back().Context) { - case Sema::ExpressionEvaluationContext::ConstantEvaluated: - case Sema::ExpressionEvaluationContext::ImmediateFunctionContext: - - // -- a manifestly constant-evaluated expression, - case Sema::ExpressionEvaluationContext::PotentiallyEvaluated: - case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: - case Sema::ExpressionEvaluationContext::DiscardedStatement: - // -- a potentially-evaluated expression, - case Sema::ExpressionEvaluationContext::UnevaluatedList: - // -- an immediate subexpression of a braced-init-list, - - // -- [FIXME] an expression of the form & cast-expression that occurs - // within a templated entity - // -- a subexpression of one of the above that is not a subexpression of - // a nested unevaluated operand. +/// Capture the given variable in the captured region. +static bool captureInCapturedRegion( + CapturedRegionScopeInfo *RSI, ValueDecl *Var, SourceLocation Loc, + const bool BuildAndDiagnose, QualType &CaptureType, QualType &DeclRefType, + const bool RefersToCapturedVariable, Sema::TryCaptureKind Kind, + bool IsTopScope, Sema &S, bool Invalid) { + // By default, capture variables by reference. + bool ByRef = true; + if (IsTopScope && Kind != Sema::TryCapture_Implicit) { + ByRef = (Kind == Sema::TryCapture_ExplicitByRef); + } else if (S.getLangOpts().OpenMP && RSI->CapRegionKind == CR_OpenMP) { + // Using an LValue reference type is consistent with Lambdas (see below). + if (S.OpenMP().isOpenMPCapturedDecl(Var)) { + bool HasConst = DeclRefType.isConstQualified(); + DeclRefType = DeclRefType.getUnqualifiedType(); + // Don't lose diagnostics about assignments to const. + if (HasConst) + DeclRefType.addConst(); + } + // Do not capture firstprivates in tasks. + if (S.OpenMP().isOpenMPPrivateDecl(Var, RSI->OpenMPLevel, + RSI->OpenMPCaptureLevel) != OMPC_unknown) return true; - - case Sema::ExpressionEvaluationContext::Unevaluated: - case Sema::ExpressionEvaluationContext::UnevaluatedAbstract: - // Expressions in this context are never evaluated. - return false; + ByRef = S.OpenMP().isOpenMPCapturedByRef(Var, RSI->OpenMPLevel, + RSI->OpenMPCaptureLevel); } - llvm_unreachable("Invalid context"); -} -/// Return true if this function has a calling convention that requires mangling -/// in the size of the parameter pack. -static bool funcHasParameterSizeMangling(Sema &S, FunctionDecl *FD) { - // These manglings are only applicable for targets whcih use Microsoft - // mangling scheme for C. - if (!S.Context.getTargetInfo().shouldUseMicrosoftCCforMangling()) - return false; + if (ByRef) + CaptureType = S.Context.getLValueReferenceType(DeclRefType); + else + CaptureType = DeclRefType; - // If this is C++ and this isn't an extern "C" function, parameters do not - // need to be complete. In this case, C++ mangling will apply, which doesn't - // use the size of the parameters. - if (S.getLangOpts().CPlusPlus && !FD->isExternC()) - return false; + // Actually capture the variable. + if (BuildAndDiagnose) + RSI->addCapture(Var, /*isBlock*/ false, ByRef, RefersToCapturedVariable, + Loc, SourceLocation(), CaptureType, Invalid); - // Stdcall, fastcall, and vectorcall need this special treatment. - CallingConv CC = FD->getType()->castAs()->getCallConv(); - switch (CC) { - case CC_X86StdCall: - case CC_X86FastCall: - case CC_X86VectorCall: - return true; - default: - break; - } - return false; + return !Invalid; } -/// Require that all of the parameter types of function be complete. Normally, -/// parameter types are only required to be complete when a function is called -/// or defined, but to mangle functions with certain calling conventions, the -/// mangler needs to know the size of the parameter list. In this situation, -/// MSVC doesn't emit an error or instantiate templates. Instead, MSVC mangles -/// the function as _foo@0, i.e. zero bytes of parameters, which will usually -/// result in a linker error. Clang doesn't implement this behavior, and instead -/// attempts to error at compile time. -static void CheckCompleteParameterTypesForMangler(Sema &S, FunctionDecl *FD, - SourceLocation Loc) { - class ParamIncompleteTypeDiagnoser : public Sema::TypeDiagnoser { - FunctionDecl *FD; - ParmVarDecl *Param; +/// Capture the given variable in the lambda. +static bool captureInLambda(LambdaScopeInfo *LSI, ValueDecl *Var, + SourceLocation Loc, const bool BuildAndDiagnose, + QualType &CaptureType, QualType &DeclRefType, + const bool RefersToCapturedVariable, + const Sema::TryCaptureKind Kind, + SourceLocation EllipsisLoc, const bool IsTopScope, + Sema &S, bool Invalid) { + // Determine whether we are capturing by reference or by value. + bool ByRef = false; + if (IsTopScope && Kind != Sema::TryCapture_Implicit) { + ByRef = (Kind == Sema::TryCapture_ExplicitByRef); + } else { + ByRef = (LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByref); + } - public: - ParamIncompleteTypeDiagnoser(FunctionDecl *FD, ParmVarDecl *Param) - : FD(FD), Param(Param) {} + if (BuildAndDiagnose && S.Context.getTargetInfo().getTriple().isWasm() && + CaptureType.getNonReferenceType().isWebAssemblyReferenceType()) { + S.Diag(Loc, diag::err_wasm_ca_reference) << 0; + Invalid = true; + } - void diagnose(Sema &S, SourceLocation Loc, QualType T) override { - CallingConv CC = FD->getType()->castAs()->getCallConv(); - StringRef CCName; - switch (CC) { - case CC_X86StdCall: - CCName = "stdcall"; - break; - case CC_X86FastCall: - CCName = "fastcall"; - break; - case CC_X86VectorCall: - CCName = "vectorcall"; - break; - default: - llvm_unreachable("CC does not need mangling"); + // Compute the type of the field that will capture this variable. + if (ByRef) { + // C++11 [expr.prim.lambda]p15: + // An entity is captured by reference if it is implicitly or + // explicitly captured but not captured by copy. It is + // unspecified whether additional unnamed non-static data + // members are declared in the closure type for entities + // captured by reference. + // + // FIXME: It is not clear whether we want to build an lvalue reference + // to the DeclRefType or to CaptureType.getNonReferenceType(). GCC appears + // to do the former, while EDG does the latter. Core issue 1249 will + // clarify, but for now we follow GCC because it's a more permissive and + // easily defensible position. + CaptureType = S.Context.getLValueReferenceType(DeclRefType); + } else { + // C++11 [expr.prim.lambda]p14: + // For each entity captured by copy, an unnamed non-static + // data member is declared in the closure type. The + // declaration order of these members is unspecified. The type + // of such a data member is the type of the corresponding + // captured entity if the entity is not a reference to an + // object, or the referenced type otherwise. [Note: If the + // captured entity is a reference to a function, the + // corresponding data member is also a reference to a + // function. - end note ] + if (const ReferenceType *RefType = CaptureType->getAs()){ + if (!RefType->getPointeeType()->isFunctionType()) + CaptureType = RefType->getPointeeType(); + } + + // Forbid the lambda copy-capture of autoreleasing variables. + if (!Invalid && + CaptureType.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) { + if (BuildAndDiagnose) { + S.Diag(Loc, diag::err_arc_autoreleasing_capture) << /*lambda*/ 1; + S.Diag(Var->getLocation(), diag::note_previous_decl) + << Var->getDeclName(); + Invalid = true; + } else { + return false; } + } - S.Diag(Loc, diag::err_cconv_incomplete_param_type) - << Param->getDeclName() << FD->getDeclName() << CCName; + // Make sure that by-copy captures are of a complete and non-abstract type. + if (!Invalid && BuildAndDiagnose) { + if (!CaptureType->isDependentType() && + S.RequireCompleteSizedType( + Loc, CaptureType, + diag::err_capture_of_incomplete_or_sizeless_type, + Var->getDeclName())) + Invalid = true; + else if (S.RequireNonAbstractType(Loc, CaptureType, + diag::err_capture_of_abstract_type)) + Invalid = true; } - }; + } - for (ParmVarDecl *Param : FD->parameters()) { - ParamIncompleteTypeDiagnoser Diagnoser(FD, Param); - S.RequireCompleteType(Loc, Param->getType(), Diagnoser); + // Compute the type of a reference to this captured variable. + if (ByRef) + DeclRefType = CaptureType.getNonReferenceType(); + else { + // C++ [expr.prim.lambda]p5: + // The closure type for a lambda-expression has a public inline + // function call operator [...]. This function call operator is + // declared const (9.3.1) if and only if the lambda-expression's + // parameter-declaration-clause is not followed by mutable. + DeclRefType = CaptureType.getNonReferenceType(); + bool Const = LSI->lambdaCaptureShouldBeConst(); + // C++ [expr.prim.lambda]p10: + // The type of such a data member is [...] an lvalue reference to the + // referenced function type if the entity is a reference to a function. + // [...] + if (Const && !CaptureType->isReferenceType() && + !DeclRefType->isFunctionType()) + DeclRefType.addConst(); } -} -namespace { -enum class OdrUseContext { - /// Declarations in this context are not odr-used. - None, - /// Declarations in this context are formally odr-used, but this is a - /// dependent context. - Dependent, - /// Declarations in this context are odr-used but not actually used (yet). - FormallyOdrUsed, - /// Declarations in this context are used. - Used -}; -} + // Add the capture. + if (BuildAndDiagnose) + LSI->addCapture(Var, /*isBlock=*/false, ByRef, RefersToCapturedVariable, + Loc, EllipsisLoc, CaptureType, Invalid); -/// Are we within a context in which references to resolved functions or to -/// variables result in odr-use? -static OdrUseContext isOdrUseContext(Sema &SemaRef) { - OdrUseContext Result; + return !Invalid; +} - switch (SemaRef.ExprEvalContexts.back().Context) { - case Sema::ExpressionEvaluationContext::Unevaluated: - case Sema::ExpressionEvaluationContext::UnevaluatedList: - case Sema::ExpressionEvaluationContext::UnevaluatedAbstract: - return OdrUseContext::None; +static bool canCaptureVariableByCopy(ValueDecl *Var, + const ASTContext &Context) { + // Offer a Copy fix even if the type is dependent. + if (Var->getType()->isDependentType()) + return true; + QualType T = Var->getType().getNonReferenceType(); + if (T.isTriviallyCopyableType(Context)) + return true; + if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) { - case Sema::ExpressionEvaluationContext::ConstantEvaluated: - case Sema::ExpressionEvaluationContext::ImmediateFunctionContext: - case Sema::ExpressionEvaluationContext::PotentiallyEvaluated: - Result = OdrUseContext::Used; - break; + if (!(RD = RD->getDefinition())) + return false; + if (RD->hasSimpleCopyConstructor()) + return true; + if (RD->hasUserDeclaredCopyConstructor()) + for (CXXConstructorDecl *Ctor : RD->ctors()) + if (Ctor->isCopyConstructor()) + return !Ctor->isDeleted(); + } + return false; +} - case Sema::ExpressionEvaluationContext::DiscardedStatement: - Result = OdrUseContext::FormallyOdrUsed; - break; +/// Create up to 4 fix-its for explicit reference and value capture of \p Var or +/// default capture. Fixes may be omitted if they aren't allowed by the +/// standard, for example we can't emit a default copy capture fix-it if we +/// already explicitly copy capture capture another variable. +static void buildLambdaCaptureFixit(Sema &Sema, LambdaScopeInfo *LSI, + ValueDecl *Var) { + assert(LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None); + // Don't offer Capture by copy of default capture by copy fixes if Var is + // known not to be copy constructible. + bool ShouldOfferCopyFix = canCaptureVariableByCopy(Var, Sema.getASTContext()); - case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: - // A default argument formally results in odr-use, but doesn't actually - // result in a use in any real sense until it itself is used. - Result = OdrUseContext::FormallyOdrUsed; - break; + SmallString<32> FixBuffer; + StringRef Separator = LSI->NumExplicitCaptures > 0 ? ", " : ""; + if (Var->getDeclName().isIdentifier() && !Var->getName().empty()) { + SourceLocation VarInsertLoc = LSI->IntroducerRange.getEnd(); + if (ShouldOfferCopyFix) { + // Offer fixes to insert an explicit capture for the variable. + // [] -> [VarName] + // [OtherCapture] -> [OtherCapture, VarName] + FixBuffer.assign({Separator, Var->getName()}); + Sema.Diag(VarInsertLoc, diag::note_lambda_variable_capture_fixit) + << Var << /*value*/ 0 + << FixItHint::CreateInsertion(VarInsertLoc, FixBuffer); + } + // As above but capture by reference. + FixBuffer.assign({Separator, "&", Var->getName()}); + Sema.Diag(VarInsertLoc, diag::note_lambda_variable_capture_fixit) + << Var << /*reference*/ 1 + << FixItHint::CreateInsertion(VarInsertLoc, FixBuffer); } - if (SemaRef.CurContext->isDependentContext()) - return OdrUseContext::Dependent; + // Only try to offer default capture if there are no captures excluding this + // and init captures. + // [this]: OK. + // [X = Y]: OK. + // [&A, &B]: Don't offer. + // [A, B]: Don't offer. + if (llvm::any_of(LSI->Captures, [](Capture &C) { + return !C.isThisCapture() && !C.isInitCapture(); + })) + return; - return Result; -} + // The default capture specifiers, '=' or '&', must appear first in the + // capture body. + SourceLocation DefaultInsertLoc = + LSI->IntroducerRange.getBegin().getLocWithOffset(1); -static bool isImplicitlyDefinableConstexprFunction(FunctionDecl *Func) { - if (!Func->isConstexpr()) - return false; + if (ShouldOfferCopyFix) { + bool CanDefaultCopyCapture = true; + // [=, *this] OK since c++17 + // [=, this] OK since c++20 + if (LSI->isCXXThisCaptured() && !Sema.getLangOpts().CPlusPlus20) + CanDefaultCopyCapture = Sema.getLangOpts().CPlusPlus17 + ? LSI->getCXXThisCapture().isCopyCapture() + : false; + // We can't use default capture by copy if any captures already specified + // capture by copy. + if (CanDefaultCopyCapture && llvm::none_of(LSI->Captures, [](Capture &C) { + return !C.isThisCapture() && !C.isInitCapture() && C.isCopyCapture(); + })) { + FixBuffer.assign({"=", Separator}); + Sema.Diag(DefaultInsertLoc, diag::note_lambda_default_capture_fixit) + << /*value*/ 0 + << FixItHint::CreateInsertion(DefaultInsertLoc, FixBuffer); + } + } - if (Func->isImplicitlyInstantiable() || !Func->isUserProvided()) - return true; - auto *CCD = dyn_cast(Func); - return CCD && CCD->getInheritedConstructor(); + // We can't use default capture by reference if any captures already specified + // capture by reference. + if (llvm::none_of(LSI->Captures, [](Capture &C) { + return !C.isInitCapture() && C.isReferenceCapture() && + !C.isThisCapture(); + })) { + FixBuffer.assign({"&", Separator}); + Sema.Diag(DefaultInsertLoc, diag::note_lambda_default_capture_fixit) + << /*reference*/ 1 + << FixItHint::CreateInsertion(DefaultInsertLoc, FixBuffer); + } } -void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func, - bool MightBeOdrUse) { - assert(Func && "No function?"); +bool Sema::tryCaptureVariable( + ValueDecl *Var, SourceLocation ExprLoc, TryCaptureKind Kind, + SourceLocation EllipsisLoc, bool BuildAndDiagnose, QualType &CaptureType, + QualType &DeclRefType, const unsigned *const FunctionScopeIndexToStopAt) { + // An init-capture is notionally from the context surrounding its + // declaration, but its parent DC is the lambda class. + DeclContext *VarDC = Var->getDeclContext(); + DeclContext *DC = CurContext; - Func->setReferenced(); + // Skip past RequiresExprBodys because they don't constitute function scopes. + while (DC->isRequiresExprBody()) + DC = DC->getParent(); - // Recursive functions aren't really used until they're used from some other - // context. - bool IsRecursiveCall = CurContext == Func; + // tryCaptureVariable is called every time a DeclRef is formed, + // it can therefore have non-negigible impact on performances. + // For local variables and when there is no capturing scope, + // we can bailout early. + if (CapturingFunctionScopes == 0 && (!BuildAndDiagnose || VarDC == DC)) + return true; - // C++11 [basic.def.odr]p3: - // A function whose name appears as a potentially-evaluated expression is - // odr-used if it is the unique lookup result or the selected member of a - // set of overloaded functions [...]. - // - // We (incorrectly) mark overload resolution as an unevaluated context, so we - // can just check that here. - OdrUseContext OdrUse = - MightBeOdrUse ? isOdrUseContext(*this) : OdrUseContext::None; - if (IsRecursiveCall && OdrUse == OdrUseContext::Used) - OdrUse = OdrUseContext::FormallyOdrUsed; + // Exception: Function parameters are not tied to the function's DeclContext + // until we enter the function definition. Capturing them anyway would result + // in an out-of-bounds error while traversing DC and its parents. + if (isa(Var) && !VarDC->isFunctionOrMethod()) + return true; - // Trivial default constructors and destructors are never actually used. - // FIXME: What about other special members? - if (Func->isTrivial() && !Func->hasAttr() && - OdrUse == OdrUseContext::Used) { - if (auto *Constructor = dyn_cast(Func)) - if (Constructor->isDefaultConstructor()) - OdrUse = OdrUseContext::FormallyOdrUsed; - if (isa(Func)) - OdrUse = OdrUseContext::FormallyOdrUsed; + const auto *VD = dyn_cast(Var); + if (VD) { + if (VD->isInitCapture()) + VarDC = VarDC->getParent(); + } else { + VD = Var->getPotentiallyDecomposedVarDecl(); } + assert(VD && "Cannot capture a null variable"); - // C++20 [expr.const]p12: - // A function [...] is needed for constant evaluation if it is [...] a - // constexpr function that is named by an expression that is potentially - // constant evaluated - bool NeededForConstantEvaluation = - isPotentiallyConstantEvaluatedContext(*this) && - isImplicitlyDefinableConstexprFunction(Func); + const unsigned MaxFunctionScopesIndex = FunctionScopeIndexToStopAt + ? *FunctionScopeIndexToStopAt : FunctionScopes.size() - 1; + // We need to sync up the Declaration Context with the + // FunctionScopeIndexToStopAt + if (FunctionScopeIndexToStopAt) { + assert(!FunctionScopes.empty() && "No function scopes to stop at?"); + unsigned FSIndex = FunctionScopes.size() - 1; + // When we're parsing the lambda parameter list, the current DeclContext is + // NOT the lambda but its parent. So move away the current LSI before + // aligning DC and FunctionScopeIndexToStopAt. + if (auto *LSI = dyn_cast(FunctionScopes[FSIndex]); + FSIndex && LSI && !LSI->AfterParameterList) + --FSIndex; + assert(MaxFunctionScopesIndex <= FSIndex && + "FunctionScopeIndexToStopAt should be no greater than FSIndex into " + "FunctionScopes."); + while (FSIndex != MaxFunctionScopesIndex) { + DC = getLambdaAwareParentOfDeclContext(DC); + --FSIndex; + } + } - // Determine whether we require a function definition to exist, per - // C++11 [temp.inst]p3: - // Unless a function template specialization has been explicitly - // instantiated or explicitly specialized, the function template - // specialization is implicitly instantiated when the specialization is - // referenced in a context that requires a function definition to exist. - // C++20 [temp.inst]p7: - // The existence of a definition of a [...] function is considered to - // affect the semantics of the program if the [...] function is needed for - // constant evaluation by an expression - // C++20 [basic.def.odr]p10: - // Every program shall contain exactly one definition of every non-inline - // function or variable that is odr-used in that program outside of a - // discarded statement - // C++20 [special]p1: - // The implementation will implicitly define [defaulted special members] - // if they are odr-used or needed for constant evaluation. - // - // Note that we skip the implicit instantiation of templates that are only - // used in unused default arguments or by recursive calls to themselves. - // This is formally non-conforming, but seems reasonable in practice. - bool NeedDefinition = - !IsRecursiveCall && - (OdrUse == OdrUseContext::Used || - (NeededForConstantEvaluation && !Func->isPureVirtual())); + // Capture global variables if it is required to use private copy of this + // variable. + bool IsGlobal = !VD->hasLocalStorage(); + if (IsGlobal && !(LangOpts.OpenMP && + OpenMP().isOpenMPCapturedDecl(Var, /*CheckScopeInfo=*/true, + MaxFunctionScopesIndex))) + return true; - // C++14 [temp.expl.spec]p6: - // If a template [...] is explicitly specialized then that specialization - // shall be declared before the first use of that specialization that would - // cause an implicit instantiation to take place, in every translation unit - // in which such a use occurs - if (NeedDefinition && - (Func->getTemplateSpecializationKind() != TSK_Undeclared || - Func->getMemberSpecializationInfo())) - checkSpecializationReachability(Loc, Func); + if (isa(Var)) + Var = cast(Var->getCanonicalDecl()); - if (getLangOpts().CUDA) - CUDA().CheckCall(Loc, Func); + // Walk up the stack to determine whether we can capture the variable, + // performing the "simple" checks that don't depend on type. We stop when + // we've either hit the declared scope of the variable or find an existing + // capture of that variable. We start from the innermost capturing-entity + // (the DC) and ensure that all intervening capturing-entities + // (blocks/lambdas etc.) between the innermost capturer and the variable`s + // declcontext can either capture the variable or have already captured + // the variable. + CaptureType = Var->getType(); + DeclRefType = CaptureType.getNonReferenceType(); + bool Nested = false; + bool Explicit = (Kind != TryCapture_Implicit); + unsigned FunctionScopesIndex = MaxFunctionScopesIndex; + do { - // If we need a definition, try to create one. - if (NeedDefinition && !Func->getBody()) { - runWithSufficientStackSpace(Loc, [&] { - if (CXXConstructorDecl *Constructor = - dyn_cast(Func)) { - Constructor = cast(Constructor->getFirstDecl()); - if (Constructor->isDefaulted() && !Constructor->isDeleted()) { - if (Constructor->isDefaultConstructor()) { - if (Constructor->isTrivial() && - !Constructor->hasAttr()) - return; - DefineImplicitDefaultConstructor(Loc, Constructor); - } else if (Constructor->isCopyConstructor()) { - DefineImplicitCopyConstructor(Loc, Constructor); - } else if (Constructor->isMoveConstructor()) { - DefineImplicitMoveConstructor(Loc, Constructor); - } - } else if (Constructor->getInheritedConstructor()) { - DefineInheritingConstructor(Loc, Constructor); - } - } else if (CXXDestructorDecl *Destructor = - dyn_cast(Func)) { - Destructor = cast(Destructor->getFirstDecl()); - if (Destructor->isDefaulted() && !Destructor->isDeleted()) { - if (Destructor->isTrivial() && !Destructor->hasAttr()) - return; - DefineImplicitDestructor(Loc, Destructor); - } - if (Destructor->isVirtual() && getLangOpts().AppleKext) - MarkVTableUsed(Loc, Destructor->getParent()); - } else if (CXXMethodDecl *MethodDecl = dyn_cast(Func)) { - if (MethodDecl->isOverloadedOperator() && - MethodDecl->getOverloadedOperator() == OO_Equal) { - MethodDecl = cast(MethodDecl->getFirstDecl()); - if (MethodDecl->isDefaulted() && !MethodDecl->isDeleted()) { - if (MethodDecl->isCopyAssignmentOperator()) - DefineImplicitCopyAssignment(Loc, MethodDecl); - else if (MethodDecl->isMoveAssignmentOperator()) - DefineImplicitMoveAssignment(Loc, MethodDecl); - } - } else if (isa(MethodDecl) && - MethodDecl->getParent()->isLambda()) { - CXXConversionDecl *Conversion = - cast(MethodDecl->getFirstDecl()); - if (Conversion->isLambdaToBlockPointerConversion()) - DefineImplicitLambdaToBlockPointerConversion(Loc, Conversion); - else - DefineImplicitLambdaToFunctionPointerConversion(Loc, Conversion); - } else if (MethodDecl->isVirtual() && getLangOpts().AppleKext) - MarkVTableUsed(Loc, MethodDecl->getParent()); - } + LambdaScopeInfo *LSI = nullptr; + if (!FunctionScopes.empty()) + LSI = dyn_cast_or_null( + FunctionScopes[FunctionScopesIndex]); - if (Func->isDefaulted() && !Func->isDeleted()) { - DefaultedComparisonKind DCK = getDefaultedComparisonKind(Func); - if (DCK != DefaultedComparisonKind::None) - DefineDefaultedComparison(Loc, Func, DCK); - } + bool IsInScopeDeclarationContext = + !LSI || LSI->AfterParameterList || CurContext == LSI->CallOperator; - // Implicit instantiation of function templates and member functions of - // class templates. - if (Func->isImplicitlyInstantiable()) { - TemplateSpecializationKind TSK = - Func->getTemplateSpecializationKindForInstantiation(); - SourceLocation PointOfInstantiation = Func->getPointOfInstantiation(); - bool FirstInstantiation = PointOfInstantiation.isInvalid(); - if (FirstInstantiation) { - PointOfInstantiation = Loc; - if (auto *MSI = Func->getMemberSpecializationInfo()) - MSI->setPointOfInstantiation(Loc); - // FIXME: Notify listener. - else - Func->setTemplateSpecializationKind(TSK, PointOfInstantiation); - } else if (TSK != TSK_ImplicitInstantiation) { - // Use the point of use as the point of instantiation, instead of the - // point of explicit instantiation (which we track as the actual point - // of instantiation). This gives better backtraces in diagnostics. - PointOfInstantiation = Loc; - } + if (LSI && !LSI->AfterParameterList) { + // This allows capturing parameters from a default value which does not + // seems correct + if (isa(Var) && !Var->getDeclContext()->isFunctionOrMethod()) + return true; + } + // If the variable is declared in the current context, there is no need to + // capture it. + if (IsInScopeDeclarationContext && + FunctionScopesIndex == MaxFunctionScopesIndex && VarDC == DC) + return true; - if (FirstInstantiation || TSK != TSK_ImplicitInstantiation || - Func->isConstexpr()) { - if (isa(Func->getDeclContext()) && - cast(Func->getDeclContext())->isLocalClass() && - CodeSynthesisContexts.size()) - PendingLocalImplicitInstantiations.push_back( - std::make_pair(Func, PointOfInstantiation)); - else if (Func->isConstexpr()) - // Do not defer instantiations of constexpr functions, to avoid the - // expression evaluator needing to call back into Sema if it sees a - // call to such a function. - InstantiateFunctionDefinition(PointOfInstantiation, Func); - else { - Func->setInstantiationIsPending(true); - PendingInstantiations.push_back( - std::make_pair(Func, PointOfInstantiation)); - if (llvm::isTimeTraceVerbose()) { - llvm::timeTraceAddInstantEvent("DeferInstantiation", [&] { - std::string Name; - llvm::raw_string_ostream OS(Name); - Func->getNameForDiagnostic(OS, getPrintingPolicy(), - /*Qualified=*/true); - return Name; - }); - } - // Notify the consumer that a function was implicitly instantiated. - Consumer.HandleCXXImplicitFunctionInstantiation(Func); - } - } - } else { - // Walk redefinitions, as some of them may be instantiable. - for (auto *i : Func->redecls()) { - if (!i->isUsed(false) && i->isImplicitlyInstantiable()) - MarkFunctionReferenced(Loc, i, MightBeOdrUse); - } + // Only block literals, captured statements, and lambda expressions can + // capture; other scopes don't work. + DeclContext *ParentDC = + !IsInScopeDeclarationContext + ? DC->getParent() + : getParentOfCapturingContextOrNull(DC, Var, ExprLoc, + BuildAndDiagnose, *this); + // We need to check for the parent *first* because, if we *have* + // private-captured a global variable, we need to recursively capture it in + // intermediate blocks, lambdas, etc. + if (!ParentDC) { + if (IsGlobal) { + FunctionScopesIndex = MaxFunctionScopesIndex - 1; + break; } - }); - } + return true; + } - // If a constructor was defined in the context of a default parameter - // or of another default member initializer (ie a PotentiallyEvaluatedIfUsed - // context), its initializers may not be referenced yet. - if (CXXConstructorDecl *Constructor = dyn_cast(Func)) { - EnterExpressionEvaluationContext EvalContext( - *this, - Constructor->isImmediateFunction() - ? ExpressionEvaluationContext::ImmediateFunctionContext - : ExpressionEvaluationContext::PotentiallyEvaluated, - Constructor); - for (CXXCtorInitializer *Init : Constructor->inits()) { - if (Init->isInClassMemberInitializer()) - runWithSufficientStackSpace(Init->getSourceLocation(), [&]() { - MarkDeclarationsReferencedInExpr(Init->getInit()); - }); + FunctionScopeInfo *FSI = FunctionScopes[FunctionScopesIndex]; + CapturingScopeInfo *CSI = cast(FSI); + + // Check whether we've already captured it. + if (isVariableAlreadyCapturedInScopeInfo(CSI, Var, Nested, CaptureType, + DeclRefType)) { + CSI->getCapture(Var).markUsed(BuildAndDiagnose); + break; } - } - // C++14 [except.spec]p17: - // An exception-specification is considered to be needed when: - // - the function is odr-used or, if it appears in an unevaluated operand, - // would be odr-used if the expression were potentially-evaluated; - // - // Note, we do this even if MightBeOdrUse is false. That indicates that the - // function is a pure virtual function we're calling, and in that case the - // function was selected by overload resolution and we need to resolve its - // exception specification for a different reason. - const FunctionProtoType *FPT = Func->getType()->getAs(); - if (FPT && isUnresolvedExceptionSpec(FPT->getExceptionSpecType())) - ResolveExceptionSpec(Loc, FPT); + // When evaluating some attributes (like enable_if) we might refer to a + // function parameter appertaining to the same declaration as that + // attribute. + if (const auto *Parm = dyn_cast(Var); + Parm && Parm->getDeclContext() == DC) + return true; - // A callee could be called by a host function then by a device function. - // If we only try recording once, we will miss recording the use on device - // side. Therefore keep trying until it is recorded. - if (LangOpts.OffloadImplicitHostDeviceTemplates && LangOpts.CUDAIsDevice && - !getASTContext().CUDAImplicitHostDeviceFunUsedByDevice.count(Func)) - CUDA().RecordImplicitHostDeviceFuncUsedByDevice(Func); + // If we are instantiating a generic lambda call operator body, + // we do not want to capture new variables. What was captured + // during either a lambdas transformation or initial parsing + // should be used. + if (isGenericLambdaCallOperatorSpecialization(DC)) { + if (BuildAndDiagnose) { + LambdaScopeInfo *LSI = cast(CSI); + if (LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None) { + Diag(ExprLoc, diag::err_lambda_impcap) << Var; + Diag(Var->getLocation(), diag::note_previous_decl) << Var; + Diag(LSI->Lambda->getBeginLoc(), diag::note_lambda_decl); + buildLambdaCaptureFixit(*this, LSI, Var); + } else + diagnoseUncapturableValueReferenceOrBinding(*this, ExprLoc, Var); + } + return true; + } - // If this is the first "real" use, act on that. - if (OdrUse == OdrUseContext::Used && !Func->isUsed(/*CheckUsedAttr=*/false)) { - // Keep track of used but undefined functions. - if (!Func->isDefined() && !Func->isInAnotherModuleUnit()) { - if (mightHaveNonExternalLinkage(Func)) - UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); - else if (Func->getMostRecentDecl()->isInlined() && - !LangOpts.GNUInline && - !Func->getMostRecentDecl()->hasAttr()) - UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); - else if (isExternalWithNoLinkageType(Func)) - UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); + // Try to capture variable-length arrays types. + if (Var->getType()->isVariablyModifiedType()) { + // We're going to walk down into the type and look for VLA + // expressions. + QualType QTy = Var->getType(); + if (ParmVarDecl *PVD = dyn_cast_or_null(Var)) + QTy = PVD->getOriginalType(); + captureVariablyModifiedType(Context, QTy, CSI); } - // Some x86 Windows calling conventions mangle the size of the parameter - // pack into the name. Computing the size of the parameters requires the - // parameter types to be complete. Check that now. - if (funcHasParameterSizeMangling(*this, Func)) - CheckCompleteParameterTypesForMangler(*this, Func, Loc); + if (getLangOpts().OpenMP) { + if (auto *RSI = dyn_cast(CSI)) { + // OpenMP private variables should not be captured in outer scope, so + // just break here. Similarly, global variables that are captured in a + // target region should not be captured outside the scope of the region. + if (RSI->CapRegionKind == CR_OpenMP) { + // FIXME: We should support capturing structured bindings in OpenMP. + if (isa(Var)) { + if (BuildAndDiagnose) { + Diag(ExprLoc, diag::err_capture_binding_openmp) << Var; + Diag(Var->getLocation(), diag::note_entity_declared_at) << Var; + } + return true; + } + OpenMPClauseKind IsOpenMPPrivateDecl = OpenMP().isOpenMPPrivateDecl( + Var, RSI->OpenMPLevel, RSI->OpenMPCaptureLevel); + // If the variable is private (i.e. not captured) and has variably + // modified type, we still need to capture the type for correct + // codegen in all regions, associated with the construct. Currently, + // it is captured in the innermost captured region only. + if (IsOpenMPPrivateDecl != OMPC_unknown && + Var->getType()->isVariablyModifiedType()) { + QualType QTy = Var->getType(); + if (ParmVarDecl *PVD = dyn_cast_or_null(Var)) + QTy = PVD->getOriginalType(); + for (int I = 1, + E = OpenMP().getNumberOfConstructScopes(RSI->OpenMPLevel); + I < E; ++I) { + auto *OuterRSI = cast( + FunctionScopes[FunctionScopesIndex - I]); + assert(RSI->OpenMPLevel == OuterRSI->OpenMPLevel && + "Wrong number of captured regions associated with the " + "OpenMP construct."); + captureVariablyModifiedType(Context, QTy, OuterRSI); + } + } + bool IsTargetCap = + IsOpenMPPrivateDecl != OMPC_private && + OpenMP().isOpenMPTargetCapturedDecl(Var, RSI->OpenMPLevel, + RSI->OpenMPCaptureLevel); + // Do not capture global if it is not privatized in outer regions. + bool IsGlobalCap = + IsGlobal && OpenMP().isOpenMPGlobalCapturedDecl( + Var, RSI->OpenMPLevel, RSI->OpenMPCaptureLevel); - // In the MS C++ ABI, the compiler emits destructor variants where they are - // used. If the destructor is used here but defined elsewhere, mark the - // virtual base destructors referenced. If those virtual base destructors - // are inline, this will ensure they are defined when emitting the complete - // destructor variant. This checking may be redundant if the destructor is - // provided later in this TU. - if (Context.getTargetInfo().getCXXABI().isMicrosoft()) { - if (auto *Dtor = dyn_cast(Func)) { - CXXRecordDecl *Parent = Dtor->getParent(); - if (Parent->getNumVBases() > 0 && !Dtor->getBody()) - CheckCompleteDestructorVariant(Loc, Dtor); + // When we detect target captures we are looking from inside the + // target region, therefore we need to propagate the capture from the + // enclosing region. Therefore, the capture is not initially nested. + if (IsTargetCap) + OpenMP().adjustOpenMPTargetScopeIndex(FunctionScopesIndex, + RSI->OpenMPLevel); + + if (IsTargetCap || IsOpenMPPrivateDecl == OMPC_private || + (IsGlobal && !IsGlobalCap)) { + Nested = !IsTargetCap; + bool HasConst = DeclRefType.isConstQualified(); + DeclRefType = DeclRefType.getUnqualifiedType(); + // Don't lose diagnostics about assignments to const. + if (HasConst) + DeclRefType.addConst(); + CaptureType = Context.getLValueReferenceType(DeclRefType); + break; + } + } } } + if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None && !Explicit) { + // No capture-default, and this is not an explicit capture + // so cannot capture this variable. + if (BuildAndDiagnose) { + Diag(ExprLoc, diag::err_lambda_impcap) << Var; + Diag(Var->getLocation(), diag::note_previous_decl) << Var; + auto *LSI = cast(CSI); + if (LSI->Lambda) { + Diag(LSI->Lambda->getBeginLoc(), diag::note_lambda_decl); + buildLambdaCaptureFixit(*this, LSI, Var); + } + // FIXME: If we error out because an outer lambda can not implicitly + // capture a variable that an inner lambda explicitly captures, we + // should have the inner lambda do the explicit capture - because + // it makes for cleaner diagnostics later. This would purely be done + // so that the diagnostic does not misleadingly claim that a variable + // can not be captured by a lambda implicitly even though it is captured + // explicitly. Suggestion: + // - create const bool VariableCaptureWasInitiallyExplicit = Explicit + // at the function head + // - cache the StartingDeclContext - this must be a lambda + // - captureInLambda in the innermost lambda the variable. + } + return true; + } + Explicit = false; + FunctionScopesIndex--; + if (IsInScopeDeclarationContext) + DC = ParentDC; + } while (!VarDC->Equals(DC)); - Func->markUsed(Context); - } -} + // Walk back down the scope stack, (e.g. from outer lambda to inner lambda) + // computing the type of the capture at each step, checking type-specific + // requirements, and adding captures if requested. + // If the variable had already been captured previously, we start capturing + // at the lambda nested within that one. + bool Invalid = false; + for (unsigned I = ++FunctionScopesIndex, N = MaxFunctionScopesIndex + 1; I != N; + ++I) { + CapturingScopeInfo *CSI = cast(FunctionScopes[I]); -/// Directly mark a variable odr-used. Given a choice, prefer to use -/// MarkVariableReferenced since it does additional checks and then -/// calls MarkVarDeclODRUsed. -/// If the variable must be captured: -/// - if FunctionScopeIndexToStopAt is null, capture it in the CurContext -/// - else capture it in the DeclContext that maps to the -/// *FunctionScopeIndexToStopAt on the FunctionScopeInfo stack. -static void -MarkVarDeclODRUsed(ValueDecl *V, SourceLocation Loc, Sema &SemaRef, - const unsigned *const FunctionScopeIndexToStopAt = nullptr) { - // Keep track of used but undefined variables. - // FIXME: We shouldn't suppress this warning for static data members. - VarDecl *Var = V->getPotentiallyDecomposedVarDecl(); - assert(Var && "expected a capturable variable"); + // Certain capturing entities (lambdas, blocks etc.) are not allowed to capture + // certain types of variables (unnamed, variably modified types etc.) + // so check for eligibility. + if (!Invalid) + Invalid = + !isVariableCapturable(CSI, Var, ExprLoc, BuildAndDiagnose, *this); - if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly && - (!Var->isExternallyVisible() || Var->isInline() || - SemaRef.isExternalWithNoLinkageType(Var)) && - !(Var->isStaticDataMember() && Var->hasInit())) { - SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()]; - if (old.isInvalid()) - old = Loc; - } - QualType CaptureType, DeclRefType; - if (SemaRef.LangOpts.OpenMP) - SemaRef.OpenMP().tryCaptureOpenMPLambdas(V); - SemaRef.tryCaptureVariable(V, Loc, Sema::TryCapture_Implicit, - /*EllipsisLoc*/ SourceLocation(), - /*BuildAndDiagnose*/ true, CaptureType, - DeclRefType, FunctionScopeIndexToStopAt); + // After encountering an error, if we're actually supposed to capture, keep + // capturing in nested contexts to suppress any follow-on diagnostics. + if (Invalid && !BuildAndDiagnose) + return true; - if (SemaRef.LangOpts.CUDA && Var->hasGlobalStorage()) { - auto *FD = dyn_cast_or_null(SemaRef.CurContext); - auto VarTarget = SemaRef.CUDA().IdentifyTarget(Var); - auto UserTarget = SemaRef.CUDA().IdentifyTarget(FD); - if (VarTarget == SemaCUDA::CVT_Host && - (UserTarget == CUDAFunctionTarget::Device || - UserTarget == CUDAFunctionTarget::HostDevice || - UserTarget == CUDAFunctionTarget::Global)) { - // Diagnose ODR-use of host global variables in device functions. - // Reference of device global variables in host functions is allowed - // through shadow variables therefore it is not diagnosed. - if (SemaRef.LangOpts.CUDAIsDevice && !SemaRef.LangOpts.HIPStdPar) { - SemaRef.targetDiag(Loc, diag::err_ref_bad_target) - << /*host*/ 2 << /*variable*/ 1 << Var - << llvm::to_underlying(UserTarget); - SemaRef.targetDiag(Var->getLocation(), - Var->getType().isConstQualified() - ? diag::note_cuda_const_var_unpromoted - : diag::note_cuda_host_var); - } - } else if (VarTarget == SemaCUDA::CVT_Device && - !Var->hasAttr() && - (UserTarget == CUDAFunctionTarget::Host || - UserTarget == CUDAFunctionTarget::HostDevice)) { - // Record a CUDA/HIP device side variable if it is ODR-used - // by host code. This is done conservatively, when the variable is - // referenced in any of the following contexts: - // - a non-function context - // - a host function - // - a host device function - // This makes the ODR-use of the device side variable by host code to - // be visible in the device compilation for the compiler to be able to - // emit template variables instantiated by host code only and to - // externalize the static device side variable ODR-used by host code. - if (!Var->hasExternalStorage()) - SemaRef.getASTContext().CUDADeviceVarODRUsedByHost.insert(Var); - else if (SemaRef.LangOpts.GPURelocatableDeviceCode && - (!FD || (!FD->getDescribedFunctionTemplate() && - SemaRef.getASTContext().GetGVALinkageForFunction(FD) == - GVA_StrongExternal))) - SemaRef.getASTContext().CUDAExternalDeviceDeclODRUsedByHost.insert(Var); + if (BlockScopeInfo *BSI = dyn_cast(CSI)) { + Invalid = !captureInBlock(BSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, + DeclRefType, Nested, *this, Invalid); + Nested = true; + } else if (CapturedRegionScopeInfo *RSI = dyn_cast(CSI)) { + Invalid = !captureInCapturedRegion( + RSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, DeclRefType, Nested, + Kind, /*IsTopScope*/ I == N - 1, *this, Invalid); + Nested = true; + } else { + LambdaScopeInfo *LSI = cast(CSI); + Invalid = + !captureInLambda(LSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, + DeclRefType, Nested, Kind, EllipsisLoc, + /*IsTopScope*/ I == N - 1, *this, Invalid); + Nested = true; } - } - V->markUsed(SemaRef.Context); + if (Invalid && !BuildAndDiagnose) + return true; + } + return Invalid; } -void Sema::MarkCaptureUsedInEnclosingContext(ValueDecl *Capture, - SourceLocation Loc, - unsigned CapturingScopeIndex) { - MarkVarDeclODRUsed(Capture, Loc, *this, &CapturingScopeIndex); +bool Sema::tryCaptureVariable(ValueDecl *Var, SourceLocation Loc, + TryCaptureKind Kind, SourceLocation EllipsisLoc) { + QualType CaptureType; + QualType DeclRefType; + return tryCaptureVariable(Var, Loc, Kind, EllipsisLoc, + /*BuildAndDiagnose=*/true, CaptureType, + DeclRefType, nullptr); } -void diagnoseUncapturableValueReferenceOrBinding(Sema &S, SourceLocation loc, - ValueDecl *var) { - DeclContext *VarDC = var->getDeclContext(); - - // If the parameter still belongs to the translation unit, then - // we're actually just using one parameter in the declaration of - // the next. - if (isa(var) && - isa(VarDC)) - return; +bool Sema::NeedToCaptureVariable(ValueDecl *Var, SourceLocation Loc) { + QualType CaptureType; + QualType DeclRefType; + return !tryCaptureVariable(Var, Loc, TryCapture_Implicit, SourceLocation(), + /*BuildAndDiagnose=*/false, CaptureType, + DeclRefType, nullptr); +} - // For C code, don't diagnose about capture if we're not actually in code - // right now; it's impossible to write a non-constant expression outside of - // function context, so we'll get other (more useful) diagnostics later. - // - // For C++, things get a bit more nasty... it would be nice to suppress this - // diagnostic for certain cases like using a local variable in an array bound - // for a member of a local class, but the correct predicate is not obvious. - if (!S.getLangOpts().CPlusPlus && !S.CurContext->isFunctionOrMethod()) - return; +QualType Sema::getCapturedDeclRefType(ValueDecl *Var, SourceLocation Loc) { + assert(Var && "Null value cannot be captured"); - unsigned ValueKind = isa(var) ? 1 : 0; - unsigned ContextKind = 3; // unknown - if (isa(VarDC) && - cast(VarDC->getParent())->isLambda()) { - ContextKind = 2; - } else if (isa(VarDC)) { - ContextKind = 0; - } else if (isa(VarDC)) { - ContextKind = 1; - } + QualType CaptureType; + QualType DeclRefType; - S.Diag(loc, diag::err_reference_to_local_in_enclosing_context) - << var << ValueKind << ContextKind << VarDC; - S.Diag(var->getLocation(), diag::note_entity_declared_at) - << var; + // Determine whether we can capture this variable. + if (tryCaptureVariable(Var, Loc, TryCapture_Implicit, SourceLocation(), + /*BuildAndDiagnose=*/false, CaptureType, + DeclRefType, nullptr)) + return QualType(); - // FIXME: Add additional diagnostic info about class etc. which prevents - // capture. + return DeclRefType; } -static bool isVariableAlreadyCapturedInScopeInfo(CapturingScopeInfo *CSI, - ValueDecl *Var, - bool &SubCapturesAreNested, - QualType &CaptureType, - QualType &DeclRefType) { - // Check whether we've already captured it. - if (CSI->CaptureMap.count(Var)) { - // If we found a capture, any subcaptures are nested. - SubCapturesAreNested = true; - - // Retrieve the capture type for this variable. - CaptureType = CSI->getCapture(Var).getCaptureType(); - - // Compute the type of an expression that refers to this variable. - DeclRefType = CaptureType.getNonReferenceType(); - - // Similarly to mutable captures in lambda, all the OpenMP captures by copy - // are mutable in the sense that user can change their value - they are - // private instances of the captured declarations. - const Capture &Cap = CSI->getCapture(Var); - // C++ [expr.prim.lambda]p10: - // The type of such a data member is [...] an lvalue reference to the - // referenced function type if the entity is a reference to a function. - // [...] - if (Cap.isCopyCapture() && !DeclRefType->isFunctionType() && - !(isa(CSI) && - !cast(CSI)->lambdaCaptureShouldBeConst()) && - !(isa(CSI) && - cast(CSI)->CapRegionKind == CR_OpenMP)) - DeclRefType.addConst(); - return true; +namespace { +// Helper to copy the template arguments from a DeclRefExpr or MemberExpr. +// The produced TemplateArgumentListInfo* points to data stored within this +// object, so should only be used in contexts where the pointer will not be +// used after the CopiedTemplateArgs object is destroyed. +class CopiedTemplateArgs { + bool HasArgs; + TemplateArgumentListInfo TemplateArgStorage; +public: + template + CopiedTemplateArgs(RefExpr *E) : HasArgs(E->hasExplicitTemplateArgs()) { + if (HasArgs) + E->copyTemplateArgumentsInto(TemplateArgStorage); + } + operator TemplateArgumentListInfo*() +#ifdef __has_cpp_attribute +#if __has_cpp_attribute(clang::lifetimebound) + [[clang::lifetimebound]] +#endif +#endif + { + return HasArgs ? &TemplateArgStorage : nullptr; } - return false; +}; } -// Only block literals, captured statements, and lambda expressions can -// capture; other scopes don't work. -static DeclContext *getParentOfCapturingContextOrNull(DeclContext *DC, - ValueDecl *Var, - SourceLocation Loc, - const bool Diagnose, - Sema &S) { - if (isa(DC) || isa(DC) || isLambdaCallOperator(DC)) - return getLambdaAwareParentOfDeclContext(DC); +/// Walk the set of potential results of an expression and mark them all as +/// non-odr-uses if they satisfy the side-conditions of the NonOdrUseReason. +/// +/// \return A new expression if we found any potential results, ExprEmpty() if +/// not, and ExprError() if we diagnosed an error. +static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E, + NonOdrUseReason NOUR) { + // Per C++11 [basic.def.odr], a variable is odr-used "unless it is + // an object that satisfies the requirements for appearing in a + // constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) + // is immediately applied." This function handles the lvalue-to-rvalue + // conversion part. + // + // If we encounter a node that claims to be an odr-use but shouldn't be, we + // transform it into the relevant kind of non-odr-use node and rebuild the + // tree of nodes leading to it. + // + // This is a mini-TreeTransform that only transforms a restricted subset of + // nodes (and only certain operands of them). - VarDecl *Underlying = Var->getPotentiallyDecomposedVarDecl(); - if (Underlying) { - if (Underlying->hasLocalStorage() && Diagnose) - diagnoseUncapturableValueReferenceOrBinding(S, Loc, Var); - } - return nullptr; -} + // Rebuild a subexpression. + auto Rebuild = [&](Expr *Sub) { + return rebuildPotentialResultsAsNonOdrUsed(S, Sub, NOUR); + }; -// Certain capturing entities (lambdas, blocks etc.) are not allowed to capture -// certain types of variables (unnamed, variably modified types etc.) -// so check for eligibility. -static bool isVariableCapturable(CapturingScopeInfo *CSI, ValueDecl *Var, - SourceLocation Loc, const bool Diagnose, - Sema &S) { + // Check whether a potential result satisfies the requirements of NOUR. + auto IsPotentialResultOdrUsed = [&](NamedDecl *D) { + // Any entity other than a VarDecl is always odr-used whenever it's named + // in a potentially-evaluated expression. + auto *VD = dyn_cast(D); + if (!VD) + return true; - assert((isa(Var)) && - "Only variables and structured bindings can be captured"); + // C++2a [basic.def.odr]p4: + // A variable x whose name appears as a potentially-evalauted expression + // e is odr-used by e unless + // -- x is a reference that is usable in constant expressions, or + // -- x is a variable of non-reference type that is usable in constant + // expressions and has no mutable subobjects, and e is an element of + // the set of potential results of an expression of + // non-volatile-qualified non-class type to which the lvalue-to-rvalue + // conversion is applied, or + // -- x is a variable of non-reference type, and e is an element of the + // set of potential results of a discarded-value expression to which + // the lvalue-to-rvalue conversion is not applied + // + // We check the first bullet and the "potentially-evaluated" condition in + // BuildDeclRefExpr. We check the type requirements in the second bullet + // in CheckLValueToRValueConversionOperand below. + switch (NOUR) { + case NOUR_None: + case NOUR_Unevaluated: + llvm_unreachable("unexpected non-odr-use-reason"); - bool IsBlock = isa(CSI); - bool IsLambda = isa(CSI); + case NOUR_Constant: + // Constant references were handled when they were built. + if (VD->getType()->isReferenceType()) + return true; + if (auto *RD = VD->getType()->getAsCXXRecordDecl()) + if (RD->hasDefinition() && RD->hasMutableFields()) + return true; + if (!VD->isUsableInConstantExpressions(S.Context)) + return true; + break; - // Lambdas are not allowed to capture unnamed variables - // (e.g. anonymous unions). - // FIXME: The C++11 rule don't actually state this explicitly, but I'm - // assuming that's the intent. - if (IsLambda && !Var->getDeclName()) { - if (Diagnose) { - S.Diag(Loc, diag::err_lambda_capture_anonymous_var); - S.Diag(Var->getLocation(), diag::note_declared_at); + case NOUR_Discarded: + if (VD->getType()->isReferenceType()) + return true; + break; } return false; - } + }; - // Prohibit variably-modified types in blocks; they're difficult to deal with. - if (Var->getType()->isVariablyModifiedType() && IsBlock) { - if (Diagnose) { - S.Diag(Loc, diag::err_ref_vm_type); - S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; - } - return false; - } - // Prohibit structs with flexible array members too. - // We cannot capture what is in the tail end of the struct. - if (const RecordType *VTTy = Var->getType()->getAs()) { - if (VTTy->getDecl()->hasFlexibleArrayMember()) { - if (Diagnose) { - if (IsBlock) - S.Diag(Loc, diag::err_ref_flexarray_type); - else - S.Diag(Loc, diag::err_lambda_capture_flexarray_type) << Var; - S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; - } + // Check whether this expression may be odr-used in CUDA/HIP. + auto MaybeCUDAODRUsed = [&]() -> bool { + if (!S.LangOpts.CUDA) return false; - } - } - const bool HasBlocksAttr = Var->hasAttr(); - // Lambdas and captured statements are not allowed to capture __block - // variables; they don't support the expected semantics. - if (HasBlocksAttr && (IsLambda || isa(CSI))) { - if (Diagnose) { - S.Diag(Loc, diag::err_capture_block_variable) << Var << !IsLambda; - S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; - } - return false; - } - // OpenCL v2.0 s6.12.5: Blocks cannot reference/capture other blocks - if (S.getLangOpts().OpenCL && IsBlock && - Var->getType()->isBlockPointerType()) { - if (Diagnose) - S.Diag(Loc, diag::err_opencl_block_ref_block); - return false; - } - - if (isa(Var)) { - if (!IsLambda || !S.getLangOpts().CPlusPlus) { - if (Diagnose) - diagnoseUncapturableValueReferenceOrBinding(S, Loc, Var); + LambdaScopeInfo *LSI = S.getCurLambda(); + if (!LSI) return false; - } else if (Diagnose && S.getLangOpts().CPlusPlus) { - S.Diag(Loc, S.LangOpts.CPlusPlus20 - ? diag::warn_cxx17_compat_capture_binding - : diag::ext_capture_binding) - << Var; - S.Diag(Var->getLocation(), diag::note_entity_declared_at) << Var; - } - } - - return true; -} - -// Returns true if the capture by block was successful. -static bool captureInBlock(BlockScopeInfo *BSI, ValueDecl *Var, - SourceLocation Loc, const bool BuildAndDiagnose, - QualType &CaptureType, QualType &DeclRefType, - const bool Nested, Sema &S, bool Invalid) { - bool ByRef = false; - - // Blocks are not allowed to capture arrays, excepting OpenCL. - // OpenCL v2.0 s1.12.5 (revision 40): arrays are captured by reference - // (decayed to pointers). - if (!Invalid && !S.getLangOpts().OpenCL && CaptureType->isArrayType()) { - if (BuildAndDiagnose) { - S.Diag(Loc, diag::err_ref_array_type); - S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; - Invalid = true; - } else { + auto *DRE = dyn_cast(E); + if (!DRE) return false; - } - } - - // Forbid the block-capture of autoreleasing variables. - if (!Invalid && - CaptureType.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) { - if (BuildAndDiagnose) { - S.Diag(Loc, diag::err_arc_autoreleasing_capture) - << /*block*/ 0; - S.Diag(Var->getLocation(), diag::note_previous_decl) << Var; - Invalid = true; - } else { + auto *VD = dyn_cast(DRE->getDecl()); + if (!VD) return false; - } - } - - // Warn about implicitly autoreleasing indirect parameters captured by blocks. - if (const auto *PT = CaptureType->getAs()) { - QualType PointeeTy = PT->getPointeeType(); - - if (!Invalid && PointeeTy->getAs() && - PointeeTy.getObjCLifetime() == Qualifiers::OCL_Autoreleasing && - !S.Context.hasDirectOwnershipQualifier(PointeeTy)) { - if (BuildAndDiagnose) { - SourceLocation VarLoc = Var->getLocation(); - S.Diag(Loc, diag::warn_block_capture_autoreleasing); - S.Diag(VarLoc, diag::note_declare_parameter_strong); - } - } - } - - const bool HasBlocksAttr = Var->hasAttr(); - if (HasBlocksAttr || CaptureType->isReferenceType() || - (S.getLangOpts().OpenMP && S.OpenMP().isOpenMPCapturedDecl(Var))) { - // Block capture by reference does not change the capture or - // declaration reference types. - ByRef = true; - } else { - // Block capture by copy introduces 'const'. - CaptureType = CaptureType.getNonReferenceType().withConst(); - DeclRefType = CaptureType; - } - - // Actually capture the variable. - if (BuildAndDiagnose) - BSI->addCapture(Var, HasBlocksAttr, ByRef, Nested, Loc, SourceLocation(), - CaptureType, Invalid); - - return !Invalid; -} + return LSI->CUDAPotentialODRUsedVars.count(VD); + }; -/// Capture the given variable in the captured region. -static bool captureInCapturedRegion( - CapturedRegionScopeInfo *RSI, ValueDecl *Var, SourceLocation Loc, - const bool BuildAndDiagnose, QualType &CaptureType, QualType &DeclRefType, - const bool RefersToCapturedVariable, Sema::TryCaptureKind Kind, - bool IsTopScope, Sema &S, bool Invalid) { - // By default, capture variables by reference. - bool ByRef = true; - if (IsTopScope && Kind != Sema::TryCapture_Implicit) { - ByRef = (Kind == Sema::TryCapture_ExplicitByRef); - } else if (S.getLangOpts().OpenMP && RSI->CapRegionKind == CR_OpenMP) { - // Using an LValue reference type is consistent with Lambdas (see below). - if (S.OpenMP().isOpenMPCapturedDecl(Var)) { - bool HasConst = DeclRefType.isConstQualified(); - DeclRefType = DeclRefType.getUnqualifiedType(); - // Don't lose diagnostics about assignments to const. - if (HasConst) - DeclRefType.addConst(); + // Mark that this expression does not constitute an odr-use. + auto MarkNotOdrUsed = [&] { + if (!MaybeCUDAODRUsed()) { + S.MaybeODRUseExprs.remove(E); + if (LambdaScopeInfo *LSI = S.getCurLambda()) + LSI->markVariableExprAsNonODRUsed(E); } - // Do not capture firstprivates in tasks. - if (S.OpenMP().isOpenMPPrivateDecl(Var, RSI->OpenMPLevel, - RSI->OpenMPCaptureLevel) != OMPC_unknown) - return true; - ByRef = S.OpenMP().isOpenMPCapturedByRef(Var, RSI->OpenMPLevel, - RSI->OpenMPCaptureLevel); - } + }; - if (ByRef) - CaptureType = S.Context.getLValueReferenceType(DeclRefType); - else - CaptureType = DeclRefType; + // C++2a [basic.def.odr]p2: + // The set of potential results of an expression e is defined as follows: + switch (E->getStmtClass()) { + // -- If e is an id-expression, ... + case Expr::DeclRefExprClass: { + auto *DRE = cast(E); + if (DRE->isNonOdrUse() || IsPotentialResultOdrUsed(DRE->getDecl())) + break; - // Actually capture the variable. - if (BuildAndDiagnose) - RSI->addCapture(Var, /*isBlock*/ false, ByRef, RefersToCapturedVariable, - Loc, SourceLocation(), CaptureType, Invalid); + // Rebuild as a non-odr-use DeclRefExpr. + MarkNotOdrUsed(); + return DeclRefExpr::Create( + S.Context, DRE->getQualifierLoc(), DRE->getTemplateKeywordLoc(), + DRE->getDecl(), DRE->refersToEnclosingVariableOrCapture(), + DRE->getNameInfo(), DRE->getType(), DRE->getValueKind(), + DRE->getFoundDecl(), CopiedTemplateArgs(DRE), NOUR); + } - return !Invalid; -} + case Expr::FunctionParmPackExprClass: { + auto *FPPE = cast(E); + // If any of the declarations in the pack is odr-used, then the expression + // as a whole constitutes an odr-use. + for (ValueDecl *D : *FPPE) + if (IsPotentialResultOdrUsed(D)) + return ExprEmpty(); -/// Capture the given variable in the lambda. -static bool captureInLambda(LambdaScopeInfo *LSI, ValueDecl *Var, - SourceLocation Loc, const bool BuildAndDiagnose, - QualType &CaptureType, QualType &DeclRefType, - const bool RefersToCapturedVariable, - const Sema::TryCaptureKind Kind, - SourceLocation EllipsisLoc, const bool IsTopScope, - Sema &S, bool Invalid) { - // Determine whether we are capturing by reference or by value. - bool ByRef = false; - if (IsTopScope && Kind != Sema::TryCapture_Implicit) { - ByRef = (Kind == Sema::TryCapture_ExplicitByRef); - } else { - ByRef = (LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByref); + // FIXME: Rebuild as a non-odr-use FunctionParmPackExpr? In practice, + // nothing cares about whether we marked this as an odr-use, but it might + // be useful for non-compiler tools. + MarkNotOdrUsed(); + break; } - if (BuildAndDiagnose && S.Context.getTargetInfo().getTriple().isWasm() && - CaptureType.getNonReferenceType().isWebAssemblyReferenceType()) { - S.Diag(Loc, diag::err_wasm_ca_reference) << 0; - Invalid = true; + // -- If e is a subscripting operation with an array operand... + case Expr::ArraySubscriptExprClass: { + auto *ASE = cast(E); + Expr *OldBase = ASE->getBase()->IgnoreImplicit(); + if (!OldBase->getType()->isArrayType()) + break; + ExprResult Base = Rebuild(OldBase); + if (!Base.isUsable()) + return Base; + Expr *LHS = ASE->getBase() == ASE->getLHS() ? Base.get() : ASE->getLHS(); + Expr *RHS = ASE->getBase() == ASE->getRHS() ? Base.get() : ASE->getRHS(); + SourceLocation LBracketLoc = ASE->getBeginLoc(); // FIXME: Not stored. + return S.ActOnArraySubscriptExpr(nullptr, LHS, LBracketLoc, RHS, + ASE->getRBracketLoc()); } - // Compute the type of the field that will capture this variable. - if (ByRef) { - // C++11 [expr.prim.lambda]p15: - // An entity is captured by reference if it is implicitly or - // explicitly captured but not captured by copy. It is - // unspecified whether additional unnamed non-static data - // members are declared in the closure type for entities - // captured by reference. - // - // FIXME: It is not clear whether we want to build an lvalue reference - // to the DeclRefType or to CaptureType.getNonReferenceType(). GCC appears - // to do the former, while EDG does the latter. Core issue 1249 will - // clarify, but for now we follow GCC because it's a more permissive and - // easily defensible position. - CaptureType = S.Context.getLValueReferenceType(DeclRefType); - } else { - // C++11 [expr.prim.lambda]p14: - // For each entity captured by copy, an unnamed non-static - // data member is declared in the closure type. The - // declaration order of these members is unspecified. The type - // of such a data member is the type of the corresponding - // captured entity if the entity is not a reference to an - // object, or the referenced type otherwise. [Note: If the - // captured entity is a reference to a function, the - // corresponding data member is also a reference to a - // function. - end note ] - if (const ReferenceType *RefType = CaptureType->getAs()){ - if (!RefType->getPointeeType()->isFunctionType()) - CaptureType = RefType->getPointeeType(); + case Expr::MemberExprClass: { + auto *ME = cast(E); + // -- If e is a class member access expression [...] naming a non-static + // data member... + if (isa(ME->getMemberDecl())) { + ExprResult Base = Rebuild(ME->getBase()); + if (!Base.isUsable()) + return Base; + return MemberExpr::Create( + S.Context, Base.get(), ME->isArrow(), ME->getOperatorLoc(), + ME->getQualifierLoc(), ME->getTemplateKeywordLoc(), + ME->getMemberDecl(), ME->getFoundDecl(), ME->getMemberNameInfo(), + CopiedTemplateArgs(ME), ME->getType(), ME->getValueKind(), + ME->getObjectKind(), ME->isNonOdrUse()); } - // Forbid the lambda copy-capture of autoreleasing variables. - if (!Invalid && - CaptureType.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) { - if (BuildAndDiagnose) { - S.Diag(Loc, diag::err_arc_autoreleasing_capture) << /*lambda*/ 1; - S.Diag(Var->getLocation(), diag::note_previous_decl) - << Var->getDeclName(); - Invalid = true; - } else { - return false; - } - } + if (ME->getMemberDecl()->isCXXInstanceMember()) + break; - // Make sure that by-copy captures are of a complete and non-abstract type. - if (!Invalid && BuildAndDiagnose) { - if (!CaptureType->isDependentType() && - S.RequireCompleteSizedType( - Loc, CaptureType, - diag::err_capture_of_incomplete_or_sizeless_type, - Var->getDeclName())) - Invalid = true; - else if (S.RequireNonAbstractType(Loc, CaptureType, - diag::err_capture_of_abstract_type)) - Invalid = true; + // -- If e is a class member access expression naming a static data member, + // ... + if (ME->isNonOdrUse() || IsPotentialResultOdrUsed(ME->getMemberDecl())) + break; + + // Rebuild as a non-odr-use MemberExpr. + MarkNotOdrUsed(); + return MemberExpr::Create( + S.Context, ME->getBase(), ME->isArrow(), ME->getOperatorLoc(), + ME->getQualifierLoc(), ME->getTemplateKeywordLoc(), ME->getMemberDecl(), + ME->getFoundDecl(), ME->getMemberNameInfo(), CopiedTemplateArgs(ME), + ME->getType(), ME->getValueKind(), ME->getObjectKind(), NOUR); + } + + case Expr::BinaryOperatorClass: { + auto *BO = cast(E); + Expr *LHS = BO->getLHS(); + Expr *RHS = BO->getRHS(); + // -- If e is a pointer-to-member expression of the form e1 .* e2 ... + if (BO->getOpcode() == BO_PtrMemD) { + ExprResult Sub = Rebuild(LHS); + if (!Sub.isUsable()) + return Sub; + BO->setLHS(Sub.get()); + // -- If e is a comma expression, ... + } else if (BO->getOpcode() == BO_Comma) { + ExprResult Sub = Rebuild(RHS); + if (!Sub.isUsable()) + return Sub; + BO->setRHS(Sub.get()); + } else { + break; } + return ExprResult(BO); } - // Compute the type of a reference to this captured variable. - if (ByRef) - DeclRefType = CaptureType.getNonReferenceType(); - else { - // C++ [expr.prim.lambda]p5: - // The closure type for a lambda-expression has a public inline - // function call operator [...]. This function call operator is - // declared const (9.3.1) if and only if the lambda-expression's - // parameter-declaration-clause is not followed by mutable. - DeclRefType = CaptureType.getNonReferenceType(); - bool Const = LSI->lambdaCaptureShouldBeConst(); - // C++ [expr.prim.lambda]p10: - // The type of such a data member is [...] an lvalue reference to the - // referenced function type if the entity is a reference to a function. - // [...] - if (Const && !CaptureType->isReferenceType() && - !DeclRefType->isFunctionType()) - DeclRefType.addConst(); + // -- If e has the form (e1)... + case Expr::ParenExprClass: { + auto *PE = cast(E); + ExprResult Sub = Rebuild(PE->getSubExpr()); + if (!Sub.isUsable()) + return Sub; + return S.ActOnParenExpr(PE->getLParen(), PE->getRParen(), Sub.get()); + } + + // -- If e is a glvalue conditional expression, ... + // We don't apply this to a binary conditional operator. FIXME: Should we? + case Expr::ConditionalOperatorClass: { + auto *CO = cast(E); + ExprResult LHS = Rebuild(CO->getLHS()); + if (LHS.isInvalid()) + return ExprError(); + ExprResult RHS = Rebuild(CO->getRHS()); + if (RHS.isInvalid()) + return ExprError(); + if (!LHS.isUsable() && !RHS.isUsable()) + return ExprEmpty(); + if (!LHS.isUsable()) + LHS = CO->getLHS(); + if (!RHS.isUsable()) + RHS = CO->getRHS(); + return S.ActOnConditionalOp(CO->getQuestionLoc(), CO->getColonLoc(), + CO->getCond(), LHS.get(), RHS.get()); + } + + // [Clang extension] + // -- If e has the form __extension__ e1... + case Expr::UnaryOperatorClass: { + auto *UO = cast(E); + if (UO->getOpcode() != UO_Extension) + break; + ExprResult Sub = Rebuild(UO->getSubExpr()); + if (!Sub.isUsable()) + return Sub; + return S.BuildUnaryOp(nullptr, UO->getOperatorLoc(), UO_Extension, + Sub.get()); } - // Add the capture. - if (BuildAndDiagnose) - LSI->addCapture(Var, /*isBlock=*/false, ByRef, RefersToCapturedVariable, - Loc, EllipsisLoc, CaptureType, Invalid); + // [Clang extension] + // -- If e has the form _Generic(...), the set of potential results is the + // union of the sets of potential results of the associated expressions. + case Expr::GenericSelectionExprClass: { + auto *GSE = cast(E); + + SmallVector AssocExprs; + bool AnyChanged = false; + for (Expr *OrigAssocExpr : GSE->getAssocExprs()) { + ExprResult AssocExpr = Rebuild(OrigAssocExpr); + if (AssocExpr.isInvalid()) + return ExprError(); + if (AssocExpr.isUsable()) { + AssocExprs.push_back(AssocExpr.get()); + AnyChanged = true; + } else { + AssocExprs.push_back(OrigAssocExpr); + } + } + + void *ExOrTy = nullptr; + bool IsExpr = GSE->isExprPredicate(); + if (IsExpr) + ExOrTy = GSE->getControllingExpr(); + else + ExOrTy = GSE->getControllingType(); + return AnyChanged ? S.CreateGenericSelectionExpr( + GSE->getGenericLoc(), GSE->getDefaultLoc(), + GSE->getRParenLoc(), IsExpr, ExOrTy, + GSE->getAssocTypeSourceInfos(), AssocExprs) + : ExprEmpty(); + } - return !Invalid; -} + // [Clang extension] + // -- If e has the form __builtin_choose_expr(...), the set of potential + // results is the union of the sets of potential results of the + // second and third subexpressions. + case Expr::ChooseExprClass: { + auto *CE = cast(E); -static bool canCaptureVariableByCopy(ValueDecl *Var, - const ASTContext &Context) { - // Offer a Copy fix even if the type is dependent. - if (Var->getType()->isDependentType()) - return true; - QualType T = Var->getType().getNonReferenceType(); - if (T.isTriviallyCopyableType(Context)) - return true; - if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) { + ExprResult LHS = Rebuild(CE->getLHS()); + if (LHS.isInvalid()) + return ExprError(); - if (!(RD = RD->getDefinition())) - return false; - if (RD->hasSimpleCopyConstructor()) - return true; - if (RD->hasUserDeclaredCopyConstructor()) - for (CXXConstructorDecl *Ctor : RD->ctors()) - if (Ctor->isCopyConstructor()) - return !Ctor->isDeleted(); - } - return false; -} + ExprResult RHS = Rebuild(CE->getLHS()); + if (RHS.isInvalid()) + return ExprError(); -/// Create up to 4 fix-its for explicit reference and value capture of \p Var or -/// default capture. Fixes may be omitted if they aren't allowed by the -/// standard, for example we can't emit a default copy capture fix-it if we -/// already explicitly copy capture capture another variable. -static void buildLambdaCaptureFixit(Sema &Sema, LambdaScopeInfo *LSI, - ValueDecl *Var) { - assert(LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None); - // Don't offer Capture by copy of default capture by copy fixes if Var is - // known not to be copy constructible. - bool ShouldOfferCopyFix = canCaptureVariableByCopy(Var, Sema.getASTContext()); + if (!LHS.get() && !RHS.get()) + return ExprEmpty(); + if (!LHS.isUsable()) + LHS = CE->getLHS(); + if (!RHS.isUsable()) + RHS = CE->getRHS(); - SmallString<32> FixBuffer; - StringRef Separator = LSI->NumExplicitCaptures > 0 ? ", " : ""; - if (Var->getDeclName().isIdentifier() && !Var->getName().empty()) { - SourceLocation VarInsertLoc = LSI->IntroducerRange.getEnd(); - if (ShouldOfferCopyFix) { - // Offer fixes to insert an explicit capture for the variable. - // [] -> [VarName] - // [OtherCapture] -> [OtherCapture, VarName] - FixBuffer.assign({Separator, Var->getName()}); - Sema.Diag(VarInsertLoc, diag::note_lambda_variable_capture_fixit) - << Var << /*value*/ 0 - << FixItHint::CreateInsertion(VarInsertLoc, FixBuffer); - } - // As above but capture by reference. - FixBuffer.assign({Separator, "&", Var->getName()}); - Sema.Diag(VarInsertLoc, diag::note_lambda_variable_capture_fixit) - << Var << /*reference*/ 1 - << FixItHint::CreateInsertion(VarInsertLoc, FixBuffer); + return S.ActOnChooseExpr(CE->getBuiltinLoc(), CE->getCond(), LHS.get(), + RHS.get(), CE->getRParenLoc()); } - // Only try to offer default capture if there are no captures excluding this - // and init captures. - // [this]: OK. - // [X = Y]: OK. - // [&A, &B]: Don't offer. - // [A, B]: Don't offer. - if (llvm::any_of(LSI->Captures, [](Capture &C) { - return !C.isThisCapture() && !C.isInitCapture(); - })) - return; + // Step through non-syntactic nodes. + case Expr::ConstantExprClass: { + auto *CE = cast(E); + ExprResult Sub = Rebuild(CE->getSubExpr()); + if (!Sub.isUsable()) + return Sub; + return ConstantExpr::Create(S.Context, Sub.get()); + } - // The default capture specifiers, '=' or '&', must appear first in the - // capture body. - SourceLocation DefaultInsertLoc = - LSI->IntroducerRange.getBegin().getLocWithOffset(1); + // We could mostly rely on the recursive rebuilding to rebuild implicit + // casts, but not at the top level, so rebuild them here. + case Expr::ImplicitCastExprClass: { + auto *ICE = cast(E); + // Only step through the narrow set of cast kinds we expect to encounter. + // Anything else suggests we've left the region in which potential results + // can be found. + switch (ICE->getCastKind()) { + case CK_NoOp: + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: { + ExprResult Sub = Rebuild(ICE->getSubExpr()); + if (!Sub.isUsable()) + return Sub; + CXXCastPath Path(ICE->path()); + return S.ImpCastExprToType(Sub.get(), ICE->getType(), ICE->getCastKind(), + ICE->getValueKind(), &Path); + } - if (ShouldOfferCopyFix) { - bool CanDefaultCopyCapture = true; - // [=, *this] OK since c++17 - // [=, this] OK since c++20 - if (LSI->isCXXThisCaptured() && !Sema.getLangOpts().CPlusPlus20) - CanDefaultCopyCapture = Sema.getLangOpts().CPlusPlus17 - ? LSI->getCXXThisCapture().isCopyCapture() - : false; - // We can't use default capture by copy if any captures already specified - // capture by copy. - if (CanDefaultCopyCapture && llvm::none_of(LSI->Captures, [](Capture &C) { - return !C.isThisCapture() && !C.isInitCapture() && C.isCopyCapture(); - })) { - FixBuffer.assign({"=", Separator}); - Sema.Diag(DefaultInsertLoc, diag::note_lambda_default_capture_fixit) - << /*value*/ 0 - << FixItHint::CreateInsertion(DefaultInsertLoc, FixBuffer); + default: + break; } + break; } - // We can't use default capture by reference if any captures already specified - // capture by reference. - if (llvm::none_of(LSI->Captures, [](Capture &C) { - return !C.isInitCapture() && C.isReferenceCapture() && - !C.isThisCapture(); - })) { - FixBuffer.assign({"&", Separator}); - Sema.Diag(DefaultInsertLoc, diag::note_lambda_default_capture_fixit) - << /*reference*/ 1 - << FixItHint::CreateInsertion(DefaultInsertLoc, FixBuffer); + default: + break; } -} -bool Sema::tryCaptureVariable( - ValueDecl *Var, SourceLocation ExprLoc, TryCaptureKind Kind, - SourceLocation EllipsisLoc, bool BuildAndDiagnose, QualType &CaptureType, - QualType &DeclRefType, const unsigned *const FunctionScopeIndexToStopAt) { - // An init-capture is notionally from the context surrounding its - // declaration, but its parent DC is the lambda class. - DeclContext *VarDC = Var->getDeclContext(); - DeclContext *DC = CurContext; + // Can't traverse through this node. Nothing to do. + return ExprEmpty(); +} - // Skip past RequiresExprBodys because they don't constitute function scopes. - while (DC->isRequiresExprBody()) - DC = DC->getParent(); +ExprResult Sema::CheckLValueToRValueConversionOperand(Expr *E) { + // Check whether the operand is or contains an object of non-trivial C union + // type. + if (E->getType().isVolatileQualified() && + (E->getType().hasNonTrivialToPrimitiveDestructCUnion() || + E->getType().hasNonTrivialToPrimitiveCopyCUnion())) + checkNonTrivialCUnion(E->getType(), E->getExprLoc(), + Sema::NTCUC_LValueToRValueVolatile, + NTCUK_Destruct|NTCUK_Copy); - // tryCaptureVariable is called every time a DeclRef is formed, - // it can therefore have non-negigible impact on performances. - // For local variables and when there is no capturing scope, - // we can bailout early. - if (CapturingFunctionScopes == 0 && (!BuildAndDiagnose || VarDC == DC)) - return true; + // C++2a [basic.def.odr]p4: + // [...] an expression of non-volatile-qualified non-class type to which + // the lvalue-to-rvalue conversion is applied [...] + if (E->getType().isVolatileQualified() || E->getType()->getAs()) + return E; - // Exception: Function parameters are not tied to the function's DeclContext - // until we enter the function definition. Capturing them anyway would result - // in an out-of-bounds error while traversing DC and its parents. - if (isa(Var) && !VarDC->isFunctionOrMethod()) - return true; + ExprResult Result = + rebuildPotentialResultsAsNonOdrUsed(*this, E, NOUR_Constant); + if (Result.isInvalid()) + return ExprError(); + return Result.get() ? Result : E; +} - const auto *VD = dyn_cast(Var); - if (VD) { - if (VD->isInitCapture()) - VarDC = VarDC->getParent(); - } else { - VD = Var->getPotentiallyDecomposedVarDecl(); - } - assert(VD && "Cannot capture a null variable"); +ExprResult Sema::ActOnConstantExpression(ExprResult Res) { + Res = CorrectDelayedTyposInExpr(Res); - const unsigned MaxFunctionScopesIndex = FunctionScopeIndexToStopAt - ? *FunctionScopeIndexToStopAt : FunctionScopes.size() - 1; - // We need to sync up the Declaration Context with the - // FunctionScopeIndexToStopAt - if (FunctionScopeIndexToStopAt) { - assert(!FunctionScopes.empty() && "No function scopes to stop at?"); - unsigned FSIndex = FunctionScopes.size() - 1; - // When we're parsing the lambda parameter list, the current DeclContext is - // NOT the lambda but its parent. So move away the current LSI before - // aligning DC and FunctionScopeIndexToStopAt. - if (auto *LSI = dyn_cast(FunctionScopes[FSIndex]); - FSIndex && LSI && !LSI->AfterParameterList) - --FSIndex; - assert(MaxFunctionScopesIndex <= FSIndex && - "FunctionScopeIndexToStopAt should be no greater than FSIndex into " - "FunctionScopes."); - while (FSIndex != MaxFunctionScopesIndex) { - DC = getLambdaAwareParentOfDeclContext(DC); - --FSIndex; - } - } + if (!Res.isUsable()) + return Res; - // Capture global variables if it is required to use private copy of this - // variable. - bool IsGlobal = !VD->hasLocalStorage(); - if (IsGlobal && !(LangOpts.OpenMP && - OpenMP().isOpenMPCapturedDecl(Var, /*CheckScopeInfo=*/true, - MaxFunctionScopesIndex))) - return true; + // If a constant-expression is a reference to a variable where we delay + // deciding whether it is an odr-use, just assume we will apply the + // lvalue-to-rvalue conversion. In the one case where this doesn't happen + // (a non-type template argument), we have special handling anyway. + return CheckLValueToRValueConversionOperand(Res.get()); +} - if (isa(Var)) - Var = cast(Var->getCanonicalDecl()); +void Sema::CleanupVarDeclMarking() { + // Iterate through a local copy in case MarkVarDeclODRUsed makes a recursive + // call. + MaybeODRUseExprSet LocalMaybeODRUseExprs; + std::swap(LocalMaybeODRUseExprs, MaybeODRUseExprs); - // Walk up the stack to determine whether we can capture the variable, - // performing the "simple" checks that don't depend on type. We stop when - // we've either hit the declared scope of the variable or find an existing - // capture of that variable. We start from the innermost capturing-entity - // (the DC) and ensure that all intervening capturing-entities - // (blocks/lambdas etc.) between the innermost capturer and the variable`s - // declcontext can either capture the variable or have already captured - // the variable. - CaptureType = Var->getType(); - DeclRefType = CaptureType.getNonReferenceType(); - bool Nested = false; - bool Explicit = (Kind != TryCapture_Implicit); - unsigned FunctionScopesIndex = MaxFunctionScopesIndex; - do { + for (Expr *E : LocalMaybeODRUseExprs) { + if (auto *DRE = dyn_cast(E)) { + MarkVarDeclODRUsed(cast(DRE->getDecl()), + DRE->getLocation(), *this); + } else if (auto *ME = dyn_cast(E)) { + MarkVarDeclODRUsed(cast(ME->getMemberDecl()), ME->getMemberLoc(), + *this); + } else if (auto *FP = dyn_cast(E)) { + for (ValueDecl *VD : *FP) + MarkVarDeclODRUsed(VD, FP->getParameterPackLocation(), *this); + } else { + llvm_unreachable("Unexpected expression"); + } + } - LambdaScopeInfo *LSI = nullptr; - if (!FunctionScopes.empty()) - LSI = dyn_cast_or_null( - FunctionScopes[FunctionScopesIndex]); + assert(MaybeODRUseExprs.empty() && + "MarkVarDeclODRUsed failed to cleanup MaybeODRUseExprs?"); +} - bool IsInScopeDeclarationContext = - !LSI || LSI->AfterParameterList || CurContext == LSI->CallOperator; +static void DoMarkPotentialCapture(Sema &SemaRef, SourceLocation Loc, + ValueDecl *Var, Expr *E) { + VarDecl *VD = Var->getPotentiallyDecomposedVarDecl(); + if (!VD) + return; - if (LSI && !LSI->AfterParameterList) { - // This allows capturing parameters from a default value which does not - // seems correct - if (isa(Var) && !Var->getDeclContext()->isFunctionOrMethod()) - return true; + const bool RefersToEnclosingScope = + (SemaRef.CurContext != VD->getDeclContext() && + VD->getDeclContext()->isFunctionOrMethod() && VD->hasLocalStorage()); + if (RefersToEnclosingScope) { + LambdaScopeInfo *const LSI = + SemaRef.getCurLambda(/*IgnoreNonLambdaCapturingScope=*/true); + if (LSI && (!LSI->CallOperator || + !LSI->CallOperator->Encloses(Var->getDeclContext()))) { + // If a variable could potentially be odr-used, defer marking it so + // until we finish analyzing the full expression for any + // lvalue-to-rvalue + // or discarded value conversions that would obviate odr-use. + // Add it to the list of potential captures that will be analyzed + // later (ActOnFinishFullExpr) for eventual capture and odr-use marking + // unless the variable is a reference that was initialized by a constant + // expression (this will never need to be captured or odr-used). + // + // FIXME: We can simplify this a lot after implementing P0588R1. + assert(E && "Capture variable should be used in an expression."); + if (!Var->getType()->isReferenceType() || + !VD->isUsableInConstantExpressions(SemaRef.Context)) + LSI->addPotentialCapture(E->IgnoreParens()); } - // If the variable is declared in the current context, there is no need to - // capture it. - if (IsInScopeDeclarationContext && - FunctionScopesIndex == MaxFunctionScopesIndex && VarDC == DC) - return true; + } +} - // Only block literals, captured statements, and lambda expressions can - // capture; other scopes don't work. - DeclContext *ParentDC = - !IsInScopeDeclarationContext - ? DC->getParent() - : getParentOfCapturingContextOrNull(DC, Var, ExprLoc, - BuildAndDiagnose, *this); - // We need to check for the parent *first* because, if we *have* - // private-captured a global variable, we need to recursively capture it in - // intermediate blocks, lambdas, etc. - if (!ParentDC) { - if (IsGlobal) { - FunctionScopesIndex = MaxFunctionScopesIndex - 1; - break; - } - return true; - } +static void DoMarkVarDeclReferenced( + Sema &SemaRef, SourceLocation Loc, VarDecl *Var, Expr *E, + llvm::DenseMap &RefsMinusAssignments) { + assert((!E || isa(E) || isa(E) || + isa(E)) && + "Invalid Expr argument to DoMarkVarDeclReferenced"); + Var->setReferenced(); - FunctionScopeInfo *FSI = FunctionScopes[FunctionScopesIndex]; - CapturingScopeInfo *CSI = cast(FSI); + if (Var->isInvalidDecl()) + return; - // Check whether we've already captured it. - if (isVariableAlreadyCapturedInScopeInfo(CSI, Var, Nested, CaptureType, - DeclRefType)) { - CSI->getCapture(Var).markUsed(BuildAndDiagnose); - break; - } + auto *MSI = Var->getMemberSpecializationInfo(); + TemplateSpecializationKind TSK = MSI ? MSI->getTemplateSpecializationKind() + : Var->getTemplateSpecializationKind(); - // When evaluating some attributes (like enable_if) we might refer to a - // function parameter appertaining to the same declaration as that - // attribute. - if (const auto *Parm = dyn_cast(Var); - Parm && Parm->getDeclContext() == DC) - return true; + OdrUseContext OdrUse = isOdrUseContext(SemaRef); + bool UsableInConstantExpr = + Var->mightBeUsableInConstantExpressions(SemaRef.Context); - // If we are instantiating a generic lambda call operator body, - // we do not want to capture new variables. What was captured - // during either a lambdas transformation or initial parsing - // should be used. - if (isGenericLambdaCallOperatorSpecialization(DC)) { - if (BuildAndDiagnose) { - LambdaScopeInfo *LSI = cast(CSI); - if (LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None) { - Diag(ExprLoc, diag::err_lambda_impcap) << Var; - Diag(Var->getLocation(), diag::note_previous_decl) << Var; - Diag(LSI->Lambda->getBeginLoc(), diag::note_lambda_decl); - buildLambdaCaptureFixit(*this, LSI, Var); - } else - diagnoseUncapturableValueReferenceOrBinding(*this, ExprLoc, Var); - } - return true; - } + if (Var->isLocalVarDeclOrParm() && !Var->hasExternalStorage()) { + RefsMinusAssignments.insert({Var, 0}).first->getSecond()++; + } - // Try to capture variable-length arrays types. - if (Var->getType()->isVariablyModifiedType()) { - // We're going to walk down into the type and look for VLA - // expressions. - QualType QTy = Var->getType(); - if (ParmVarDecl *PVD = dyn_cast_or_null(Var)) - QTy = PVD->getOriginalType(); - captureVariablyModifiedType(Context, QTy, CSI); - } + // C++20 [expr.const]p12: + // A variable [...] is needed for constant evaluation if it is [...] a + // variable whose name appears as a potentially constant evaluated + // expression that is either a contexpr variable or is of non-volatile + // const-qualified integral type or of reference type + bool NeededForConstantEvaluation = + isPotentiallyConstantEvaluatedContext(SemaRef) && UsableInConstantExpr; - if (getLangOpts().OpenMP) { - if (auto *RSI = dyn_cast(CSI)) { - // OpenMP private variables should not be captured in outer scope, so - // just break here. Similarly, global variables that are captured in a - // target region should not be captured outside the scope of the region. - if (RSI->CapRegionKind == CR_OpenMP) { - // FIXME: We should support capturing structured bindings in OpenMP. - if (isa(Var)) { - if (BuildAndDiagnose) { - Diag(ExprLoc, diag::err_capture_binding_openmp) << Var; - Diag(Var->getLocation(), diag::note_entity_declared_at) << Var; - } - return true; - } - OpenMPClauseKind IsOpenMPPrivateDecl = OpenMP().isOpenMPPrivateDecl( - Var, RSI->OpenMPLevel, RSI->OpenMPCaptureLevel); - // If the variable is private (i.e. not captured) and has variably - // modified type, we still need to capture the type for correct - // codegen in all regions, associated with the construct. Currently, - // it is captured in the innermost captured region only. - if (IsOpenMPPrivateDecl != OMPC_unknown && - Var->getType()->isVariablyModifiedType()) { - QualType QTy = Var->getType(); - if (ParmVarDecl *PVD = dyn_cast_or_null(Var)) - QTy = PVD->getOriginalType(); - for (int I = 1, - E = OpenMP().getNumberOfConstructScopes(RSI->OpenMPLevel); - I < E; ++I) { - auto *OuterRSI = cast( - FunctionScopes[FunctionScopesIndex - I]); - assert(RSI->OpenMPLevel == OuterRSI->OpenMPLevel && - "Wrong number of captured regions associated with the " - "OpenMP construct."); - captureVariablyModifiedType(Context, QTy, OuterRSI); - } - } - bool IsTargetCap = - IsOpenMPPrivateDecl != OMPC_private && - OpenMP().isOpenMPTargetCapturedDecl(Var, RSI->OpenMPLevel, - RSI->OpenMPCaptureLevel); - // Do not capture global if it is not privatized in outer regions. - bool IsGlobalCap = - IsGlobal && OpenMP().isOpenMPGlobalCapturedDecl( - Var, RSI->OpenMPLevel, RSI->OpenMPCaptureLevel); + bool NeedDefinition = + OdrUse == OdrUseContext::Used || NeededForConstantEvaluation; - // When we detect target captures we are looking from inside the - // target region, therefore we need to propagate the capture from the - // enclosing region. Therefore, the capture is not initially nested. - if (IsTargetCap) - OpenMP().adjustOpenMPTargetScopeIndex(FunctionScopesIndex, - RSI->OpenMPLevel); + assert(!isa(Var) && + "Can't instantiate a partial template specialization."); - if (IsTargetCap || IsOpenMPPrivateDecl == OMPC_private || - (IsGlobal && !IsGlobalCap)) { - Nested = !IsTargetCap; - bool HasConst = DeclRefType.isConstQualified(); - DeclRefType = DeclRefType.getUnqualifiedType(); - // Don't lose diagnostics about assignments to const. - if (HasConst) - DeclRefType.addConst(); - CaptureType = Context.getLValueReferenceType(DeclRefType); - break; - } - } - } - } - if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None && !Explicit) { - // No capture-default, and this is not an explicit capture - // so cannot capture this variable. - if (BuildAndDiagnose) { - Diag(ExprLoc, diag::err_lambda_impcap) << Var; - Diag(Var->getLocation(), diag::note_previous_decl) << Var; - auto *LSI = cast(CSI); - if (LSI->Lambda) { - Diag(LSI->Lambda->getBeginLoc(), diag::note_lambda_decl); - buildLambdaCaptureFixit(*this, LSI, Var); - } - // FIXME: If we error out because an outer lambda can not implicitly - // capture a variable that an inner lambda explicitly captures, we - // should have the inner lambda do the explicit capture - because - // it makes for cleaner diagnostics later. This would purely be done - // so that the diagnostic does not misleadingly claim that a variable - // can not be captured by a lambda implicitly even though it is captured - // explicitly. Suggestion: - // - create const bool VariableCaptureWasInitiallyExplicit = Explicit - // at the function head - // - cache the StartingDeclContext - this must be a lambda - // - captureInLambda in the innermost lambda the variable. + // If this might be a member specialization of a static data member, check + // the specialization is visible. We already did the checks for variable + // template specializations when we created them. + if (NeedDefinition && TSK != TSK_Undeclared && + !isa(Var)) + SemaRef.checkSpecializationVisibility(Loc, Var); + + // Perform implicit instantiation of static data members, static data member + // templates of class templates, and variable template specializations. Delay + // instantiations of variable templates, except for those that could be used + // in a constant expression. + if (NeedDefinition && isTemplateInstantiation(TSK)) { + // Per C++17 [temp.explicit]p10, we may instantiate despite an explicit + // instantiation declaration if a variable is usable in a constant + // expression (among other cases). + bool TryInstantiating = + TSK == TSK_ImplicitInstantiation || + (TSK == TSK_ExplicitInstantiationDeclaration && UsableInConstantExpr); + + if (TryInstantiating) { + SourceLocation PointOfInstantiation = + MSI ? MSI->getPointOfInstantiation() : Var->getPointOfInstantiation(); + bool FirstInstantiation = PointOfInstantiation.isInvalid(); + if (FirstInstantiation) { + PointOfInstantiation = Loc; + if (MSI) + MSI->setPointOfInstantiation(PointOfInstantiation); + // FIXME: Notify listener. + else + Var->setTemplateSpecializationKind(TSK, PointOfInstantiation); } - return true; - } - Explicit = false; - FunctionScopesIndex--; - if (IsInScopeDeclarationContext) - DC = ParentDC; - } while (!VarDC->Equals(DC)); - // Walk back down the scope stack, (e.g. from outer lambda to inner lambda) - // computing the type of the capture at each step, checking type-specific - // requirements, and adding captures if requested. - // If the variable had already been captured previously, we start capturing - // at the lambda nested within that one. - bool Invalid = false; - for (unsigned I = ++FunctionScopesIndex, N = MaxFunctionScopesIndex + 1; I != N; - ++I) { - CapturingScopeInfo *CSI = cast(FunctionScopes[I]); + if (UsableInConstantExpr) { + // Do not defer instantiations of variables that could be used in a + // constant expression. + SemaRef.runWithSufficientStackSpace(PointOfInstantiation, [&] { + SemaRef.InstantiateVariableDefinition(PointOfInstantiation, Var); + }); - // Certain capturing entities (lambdas, blocks etc.) are not allowed to capture - // certain types of variables (unnamed, variably modified types etc.) - // so check for eligibility. - if (!Invalid) - Invalid = - !isVariableCapturable(CSI, Var, ExprLoc, BuildAndDiagnose, *this); + // The size of an incomplete array type can be updated by + // instantiating the initializer. The DeclRefExpr's type should be + // updated accordingly too, or users of it would be confused! + if (E) + SemaRef.getCompletedType(E); - // After encountering an error, if we're actually supposed to capture, keep - // capturing in nested contexts to suppress any follow-on diagnostics. - if (Invalid && !BuildAndDiagnose) - return true; + // Re-set the member to trigger a recomputation of the dependence bits + // for the expression. + if (auto *DRE = dyn_cast_or_null(E)) + DRE->setDecl(DRE->getDecl()); + else if (auto *ME = dyn_cast_or_null(E)) + ME->setMemberDecl(ME->getMemberDecl()); + } else if (FirstInstantiation) { + SemaRef.PendingInstantiations + .push_back(std::make_pair(Var, PointOfInstantiation)); + } else { + bool Inserted = false; + for (auto &I : SemaRef.SavedPendingInstantiations) { + auto Iter = llvm::find_if( + I, [Var](const Sema::PendingImplicitInstantiation &P) { + return P.first == Var; + }); + if (Iter != I.end()) { + SemaRef.PendingInstantiations.push_back(*Iter); + I.erase(Iter); + Inserted = true; + break; + } + } - if (BlockScopeInfo *BSI = dyn_cast(CSI)) { - Invalid = !captureInBlock(BSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, - DeclRefType, Nested, *this, Invalid); - Nested = true; - } else if (CapturedRegionScopeInfo *RSI = dyn_cast(CSI)) { - Invalid = !captureInCapturedRegion( - RSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, DeclRefType, Nested, - Kind, /*IsTopScope*/ I == N - 1, *this, Invalid); - Nested = true; - } else { - LambdaScopeInfo *LSI = cast(CSI); - Invalid = - !captureInLambda(LSI, Var, ExprLoc, BuildAndDiagnose, CaptureType, - DeclRefType, Nested, Kind, EllipsisLoc, - /*IsTopScope*/ I == N - 1, *this, Invalid); - Nested = true; + // FIXME: For a specialization of a variable template, we don't + // distinguish between "declaration and type implicitly instantiated" + // and "implicit instantiation of definition requested", so we have + // no direct way to avoid enqueueing the pending instantiation + // multiple times. + if (isa(Var) && !Inserted) + SemaRef.PendingInstantiations + .push_back(std::make_pair(Var, PointOfInstantiation)); + } } - - if (Invalid && !BuildAndDiagnose) - return true; } - return Invalid; -} - -bool Sema::tryCaptureVariable(ValueDecl *Var, SourceLocation Loc, - TryCaptureKind Kind, SourceLocation EllipsisLoc) { - QualType CaptureType; - QualType DeclRefType; - return tryCaptureVariable(Var, Loc, Kind, EllipsisLoc, - /*BuildAndDiagnose=*/true, CaptureType, - DeclRefType, nullptr); -} -bool Sema::NeedToCaptureVariable(ValueDecl *Var, SourceLocation Loc) { - QualType CaptureType; - QualType DeclRefType; - return !tryCaptureVariable(Var, Loc, TryCapture_Implicit, SourceLocation(), - /*BuildAndDiagnose=*/false, CaptureType, - DeclRefType, nullptr); -} + // C++2a [basic.def.odr]p4: + // A variable x whose name appears as a potentially-evaluated expression e + // is odr-used by e unless + // -- x is a reference that is usable in constant expressions + // -- x is a variable of non-reference type that is usable in constant + // expressions and has no mutable subobjects [FIXME], and e is an + // element of the set of potential results of an expression of + // non-volatile-qualified non-class type to which the lvalue-to-rvalue + // conversion is applied + // -- x is a variable of non-reference type, and e is an element of the set + // of potential results of a discarded-value expression to which the + // lvalue-to-rvalue conversion is not applied [FIXME] + // + // We check the first part of the second bullet here, and + // Sema::CheckLValueToRValueConversionOperand deals with the second part. + // FIXME: To get the third bullet right, we need to delay this even for + // variables that are not usable in constant expressions. -QualType Sema::getCapturedDeclRefType(ValueDecl *Var, SourceLocation Loc) { - assert(Var && "Null value cannot be captured"); + // If we already know this isn't an odr-use, there's nothing more to do. + if (DeclRefExpr *DRE = dyn_cast_or_null(E)) + if (DRE->isNonOdrUse()) + return; + if (MemberExpr *ME = dyn_cast_or_null(E)) + if (ME->isNonOdrUse()) + return; - QualType CaptureType; - QualType DeclRefType; + switch (OdrUse) { + case OdrUseContext::None: + // In some cases, a variable may not have been marked unevaluated, if it + // appears in a defaukt initializer. + assert((!E || isa(E) || + SemaRef.isUnevaluatedContext()) && + "missing non-odr-use marking for unevaluated decl ref"); + break; - // Determine whether we can capture this variable. - if (tryCaptureVariable(Var, Loc, TryCapture_Implicit, SourceLocation(), - /*BuildAndDiagnose=*/false, CaptureType, - DeclRefType, nullptr)) - return QualType(); + case OdrUseContext::FormallyOdrUsed: + // FIXME: Ignoring formal odr-uses results in incorrect lambda capture + // behavior. + break; - return DeclRefType; -} + case OdrUseContext::Used: + // If we might later find that this expression isn't actually an odr-use, + // delay the marking. + if (E && Var->isUsableInConstantExpressions(SemaRef.Context)) + SemaRef.MaybeODRUseExprs.insert(E); + else + MarkVarDeclODRUsed(Var, Loc, SemaRef); + break; -namespace { -// Helper to copy the template arguments from a DeclRefExpr or MemberExpr. -// The produced TemplateArgumentListInfo* points to data stored within this -// object, so should only be used in contexts where the pointer will not be -// used after the CopiedTemplateArgs object is destroyed. -class CopiedTemplateArgs { - bool HasArgs; - TemplateArgumentListInfo TemplateArgStorage; -public: - template - CopiedTemplateArgs(RefExpr *E) : HasArgs(E->hasExplicitTemplateArgs()) { - if (HasArgs) - E->copyTemplateArgumentsInto(TemplateArgStorage); - } - operator TemplateArgumentListInfo*() -#ifdef __has_cpp_attribute -#if __has_cpp_attribute(clang::lifetimebound) - [[clang::lifetimebound]] -#endif -#endif - { - return HasArgs ? &TemplateArgStorage : nullptr; + case OdrUseContext::Dependent: + // If this is a dependent context, we don't need to mark variables as + // odr-used, but we may still need to track them for lambda capture. + // FIXME: Do we also need to do this inside dependent typeid expressions + // (which are modeled as unevaluated at this point)? + DoMarkPotentialCapture(SemaRef, Loc, Var, E); + break; } -}; } -/// Walk the set of potential results of an expression and mark them all as -/// non-odr-uses if they satisfy the side-conditions of the NonOdrUseReason. -/// -/// \return A new expression if we found any potential results, ExprEmpty() if -/// not, and ExprError() if we diagnosed an error. -static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E, - NonOdrUseReason NOUR) { - // Per C++11 [basic.def.odr], a variable is odr-used "unless it is - // an object that satisfies the requirements for appearing in a - // constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) - // is immediately applied." This function handles the lvalue-to-rvalue - // conversion part. - // - // If we encounter a node that claims to be an odr-use but shouldn't be, we - // transform it into the relevant kind of non-odr-use node and rebuild the - // tree of nodes leading to it. - // - // This is a mini-TreeTransform that only transforms a restricted subset of - // nodes (and only certain operands of them). +static void DoMarkBindingDeclReferenced(Sema &SemaRef, SourceLocation Loc, + BindingDecl *BD, Expr *E) { + BD->setReferenced(); + + if (BD->isInvalidDecl()) + return; + + OdrUseContext OdrUse = isOdrUseContext(SemaRef); + if (OdrUse == OdrUseContext::Used) { + QualType CaptureType, DeclRefType; + SemaRef.tryCaptureVariable(BD, Loc, Sema::TryCapture_Implicit, + /*EllipsisLoc*/ SourceLocation(), + /*BuildAndDiagnose*/ true, CaptureType, + DeclRefType, + /*FunctionScopeIndexToStopAt*/ nullptr); + } else if (OdrUse == OdrUseContext::Dependent) { + DoMarkPotentialCapture(SemaRef, Loc, BD, E); + } +} - // Rebuild a subexpression. - auto Rebuild = [&](Expr *Sub) { - return rebuildPotentialResultsAsNonOdrUsed(S, Sub, NOUR); - }; +void Sema::MarkVariableReferenced(SourceLocation Loc, VarDecl *Var) { + DoMarkVarDeclReferenced(*this, Loc, Var, nullptr, RefsMinusAssignments); +} - // Check whether a potential result satisfies the requirements of NOUR. - auto IsPotentialResultOdrUsed = [&](NamedDecl *D) { - // Any entity other than a VarDecl is always odr-used whenever it's named - // in a potentially-evaluated expression. - auto *VD = dyn_cast(D); - if (!VD) - return true; +// C++ [temp.dep.expr]p3: +// An id-expression is type-dependent if it contains: +// - an identifier associated by name lookup with an entity captured by copy +// in a lambda-expression that has an explicit object parameter whose type +// is dependent ([dcl.fct]), +static void FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter( + Sema &SemaRef, ValueDecl *D, Expr *E) { + auto *ID = dyn_cast(E); + if (!ID || ID->isTypeDependent() || !ID->refersToEnclosingVariableOrCapture()) + return; - // C++2a [basic.def.odr]p4: - // A variable x whose name appears as a potentially-evalauted expression - // e is odr-used by e unless - // -- x is a reference that is usable in constant expressions, or - // -- x is a variable of non-reference type that is usable in constant - // expressions and has no mutable subobjects, and e is an element of - // the set of potential results of an expression of - // non-volatile-qualified non-class type to which the lvalue-to-rvalue - // conversion is applied, or - // -- x is a variable of non-reference type, and e is an element of the - // set of potential results of a discarded-value expression to which - // the lvalue-to-rvalue conversion is not applied - // - // We check the first bullet and the "potentially-evaluated" condition in - // BuildDeclRefExpr. We check the type requirements in the second bullet - // in CheckLValueToRValueConversionOperand below. - switch (NOUR) { - case NOUR_None: - case NOUR_Unevaluated: - llvm_unreachable("unexpected non-odr-use-reason"); + // If any enclosing lambda with a dependent explicit object parameter either + // explicitly captures the variable by value, or has a capture default of '=' + // and does not capture the variable by reference, then the type of the DRE + // is dependent on the type of that lambda's explicit object parameter. + auto IsDependent = [&]() { + for (auto *Scope : llvm::reverse(SemaRef.FunctionScopes)) { + auto *LSI = dyn_cast(Scope); + if (!LSI) + continue; - case NOUR_Constant: - // Constant references were handled when they were built. - if (VD->getType()->isReferenceType()) - return true; - if (auto *RD = VD->getType()->getAsCXXRecordDecl()) - if (RD->hasDefinition() && RD->hasMutableFields()) + if (LSI->Lambda && !LSI->Lambda->Encloses(SemaRef.CurContext) && + LSI->AfterParameterList) + return false; + + const auto *MD = LSI->CallOperator; + if (MD->getType().isNull()) + continue; + + const auto *Ty = MD->getType()->getAs(); + if (!Ty || !MD->isExplicitObjectMemberFunction() || + !Ty->getParamType(0)->isDependentType()) + continue; + + if (auto *C = LSI->CaptureMap.count(D) ? &LSI->getCapture(D) : nullptr) { + if (C->isCopyCapture()) return true; - if (!VD->isUsableInConstantExpressions(S.Context)) - return true; - break; + continue; + } - case NOUR_Discarded: - if (VD->getType()->isReferenceType()) + if (LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByval) return true; - break; } return false; - }; + }(); - // Check whether this expression may be odr-used in CUDA/HIP. - auto MaybeCUDAODRUsed = [&]() -> bool { - if (!S.LangOpts.CUDA) - return false; - LambdaScopeInfo *LSI = S.getCurLambda(); - if (!LSI) - return false; - auto *DRE = dyn_cast(E); - if (!DRE) - return false; - auto *VD = dyn_cast(DRE->getDecl()); - if (!VD) - return false; - return LSI->CUDAPotentialODRUsedVars.count(VD); - }; + ID->setCapturedByCopyInLambdaWithExplicitObjectParameter( + IsDependent, SemaRef.getASTContext()); +} - // Mark that this expression does not constitute an odr-use. - auto MarkNotOdrUsed = [&] { - if (!MaybeCUDAODRUsed()) { - S.MaybeODRUseExprs.remove(E); - if (LambdaScopeInfo *LSI = S.getCurLambda()) - LSI->markVariableExprAsNonODRUsed(E); - } - }; +static void +MarkExprReferenced(Sema &SemaRef, SourceLocation Loc, Decl *D, Expr *E, + bool MightBeOdrUse, + llvm::DenseMap &RefsMinusAssignments) { + if (SemaRef.OpenMP().isInOpenMPDeclareTargetContext()) + SemaRef.OpenMP().checkDeclIsAllowedInOpenMPTarget(E, D); - // C++2a [basic.def.odr]p2: - // The set of potential results of an expression e is defined as follows: - switch (E->getStmtClass()) { - // -- If e is an id-expression, ... - case Expr::DeclRefExprClass: { - auto *DRE = cast(E); - if (DRE->isNonOdrUse() || IsPotentialResultOdrUsed(DRE->getDecl())) - break; + if (VarDecl *Var = dyn_cast(D)) { + DoMarkVarDeclReferenced(SemaRef, Loc, Var, E, RefsMinusAssignments); + if (SemaRef.getLangOpts().CPlusPlus) + FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(SemaRef, + Var, E); + return; + } - // Rebuild as a non-odr-use DeclRefExpr. - MarkNotOdrUsed(); - return DeclRefExpr::Create( - S.Context, DRE->getQualifierLoc(), DRE->getTemplateKeywordLoc(), - DRE->getDecl(), DRE->refersToEnclosingVariableOrCapture(), - DRE->getNameInfo(), DRE->getType(), DRE->getValueKind(), - DRE->getFoundDecl(), CopiedTemplateArgs(DRE), NOUR); + if (BindingDecl *Decl = dyn_cast(D)) { + DoMarkBindingDeclReferenced(SemaRef, Loc, Decl, E); + if (SemaRef.getLangOpts().CPlusPlus) + FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(SemaRef, + Decl, E); + return; } + SemaRef.MarkAnyDeclReferenced(Loc, D, MightBeOdrUse); - case Expr::FunctionParmPackExprClass: { - auto *FPPE = cast(E); - // If any of the declarations in the pack is odr-used, then the expression - // as a whole constitutes an odr-use. - for (ValueDecl *D : *FPPE) - if (IsPotentialResultOdrUsed(D)) - return ExprEmpty(); + // If this is a call to a method via a cast, also mark the method in the + // derived class used in case codegen can devirtualize the call. + const MemberExpr *ME = dyn_cast(E); + if (!ME) + return; + CXXMethodDecl *MD = dyn_cast(ME->getMemberDecl()); + if (!MD) + return; + // Only attempt to devirtualize if this is truly a virtual call. + bool IsVirtualCall = MD->isVirtual() && + ME->performsVirtualDispatch(SemaRef.getLangOpts()); + if (!IsVirtualCall) + return; + + // If it's possible to devirtualize the call, mark the called function + // referenced. + CXXMethodDecl *DM = MD->getDevirtualizedMethod( + ME->getBase(), SemaRef.getLangOpts().AppleKext); + if (DM) + SemaRef.MarkAnyDeclReferenced(Loc, DM, MightBeOdrUse); +} + +void Sema::MarkDeclRefReferenced(DeclRefExpr *E, const Expr *Base) { + // TODO: update this with DR# once a defect report is filed. + // C++11 defect. The address of a pure member should not be an ODR use, even + // if it's a qualified reference. + bool OdrUse = true; + if (const CXXMethodDecl *Method = dyn_cast(E->getDecl())) + if (Method->isVirtual() && + !Method->getDevirtualizedMethod(Base, getLangOpts().AppleKext)) + OdrUse = false; + + if (auto *FD = dyn_cast(E->getDecl())) { + if (!isUnevaluatedContext() && !isConstantEvaluatedContext() && + !isImmediateFunctionContext() && + !isCheckingDefaultArgumentOrInitializer() && + FD->isImmediateFunction() && !RebuildingImmediateInvocation && + !FD->isDependentContext()) + ExprEvalContexts.back().ReferenceToConsteval.insert(E); + } + MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse, + RefsMinusAssignments); +} + +void Sema::MarkMemberReferenced(MemberExpr *E) { + // C++11 [basic.def.odr]p2: + // A non-overloaded function whose name appears as a potentially-evaluated + // expression or a member of a set of candidate functions, if selected by + // overload resolution when referred to from a potentially-evaluated + // expression, is odr-used, unless it is a pure virtual function and its + // name is not explicitly qualified. + bool MightBeOdrUse = true; + if (E->performsVirtualDispatch(getLangOpts())) { + if (CXXMethodDecl *Method = dyn_cast(E->getMemberDecl())) + if (Method->isPureVirtual()) + MightBeOdrUse = false; + } + SourceLocation Loc = + E->getMemberLoc().isValid() ? E->getMemberLoc() : E->getBeginLoc(); + MarkExprReferenced(*this, Loc, E->getMemberDecl(), E, MightBeOdrUse, + RefsMinusAssignments); +} + +void Sema::MarkFunctionParmPackReferenced(FunctionParmPackExpr *E) { + for (ValueDecl *VD : *E) + MarkExprReferenced(*this, E->getParameterPackLocation(), VD, E, true, + RefsMinusAssignments); +} + +/// Perform marking for a reference to an arbitrary declaration. It +/// marks the declaration referenced, and performs odr-use checking for +/// functions and variables. This method should not be used when building a +/// normal expression which refers to a variable. +void Sema::MarkAnyDeclReferenced(SourceLocation Loc, Decl *D, + bool MightBeOdrUse) { + if (MightBeOdrUse) { + if (auto *VD = dyn_cast(D)) { + MarkVariableReferenced(Loc, VD); + return; + } + } + if (auto *FD = dyn_cast(D)) { + MarkFunctionReferenced(Loc, FD, MightBeOdrUse); + return; + } + D->setReferenced(); +} + +namespace { + // Mark all of the declarations used by a type as referenced. + // FIXME: Not fully implemented yet! We need to have a better understanding + // of when we're entering a context we should not recurse into. + // FIXME: This is and EvaluatedExprMarker are more-or-less equivalent to + // TreeTransforms rebuilding the type in a new context. Rather than + // duplicating the TreeTransform logic, we should consider reusing it here. + // Currently that causes problems when rebuilding LambdaExprs. +class MarkReferencedDecls : public DynamicRecursiveASTVisitor { + Sema &S; + SourceLocation Loc; + +public: + MarkReferencedDecls(Sema &S, SourceLocation Loc) : S(S), Loc(Loc) {} - // FIXME: Rebuild as a non-odr-use FunctionParmPackExpr? In practice, - // nothing cares about whether we marked this as an odr-use, but it might - // be useful for non-compiler tools. - MarkNotOdrUsed(); - break; - } + bool TraverseTemplateArgument(const TemplateArgument &Arg) override; +}; +} - // -- If e is a subscripting operation with an array operand... - case Expr::ArraySubscriptExprClass: { - auto *ASE = cast(E); - Expr *OldBase = ASE->getBase()->IgnoreImplicit(); - if (!OldBase->getType()->isArrayType()) - break; - ExprResult Base = Rebuild(OldBase); - if (!Base.isUsable()) - return Base; - Expr *LHS = ASE->getBase() == ASE->getLHS() ? Base.get() : ASE->getLHS(); - Expr *RHS = ASE->getBase() == ASE->getRHS() ? Base.get() : ASE->getRHS(); - SourceLocation LBracketLoc = ASE->getBeginLoc(); // FIXME: Not stored. - return S.ActOnArraySubscriptExpr(nullptr, LHS, LBracketLoc, RHS, - ASE->getRBracketLoc()); +bool MarkReferencedDecls::TraverseTemplateArgument( + const TemplateArgument &Arg) { + { + // A non-type template argument is a constant-evaluated context. + EnterExpressionEvaluationContext Evaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + if (Arg.getKind() == TemplateArgument::Declaration) { + if (Decl *D = Arg.getAsDecl()) + S.MarkAnyDeclReferenced(Loc, D, true); + } else if (Arg.getKind() == TemplateArgument::Expression) { + S.MarkDeclarationsReferencedInExpr(Arg.getAsExpr(), false); + } } - case Expr::MemberExprClass: { - auto *ME = cast(E); - // -- If e is a class member access expression [...] naming a non-static - // data member... - if (isa(ME->getMemberDecl())) { - ExprResult Base = Rebuild(ME->getBase()); - if (!Base.isUsable()) - return Base; - return MemberExpr::Create( - S.Context, Base.get(), ME->isArrow(), ME->getOperatorLoc(), - ME->getQualifierLoc(), ME->getTemplateKeywordLoc(), - ME->getMemberDecl(), ME->getFoundDecl(), ME->getMemberNameInfo(), - CopiedTemplateArgs(ME), ME->getType(), ME->getValueKind(), - ME->getObjectKind(), ME->isNonOdrUse()); - } + return DynamicRecursiveASTVisitor::TraverseTemplateArgument(Arg); +} - if (ME->getMemberDecl()->isCXXInstanceMember()) - break; +void Sema::MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T) { + MarkReferencedDecls Marker(*this, Loc); + Marker.TraverseType(T); +} - // -- If e is a class member access expression naming a static data member, - // ... - if (ME->isNonOdrUse() || IsPotentialResultOdrUsed(ME->getMemberDecl())) - break; +namespace { +/// Helper class that marks all of the declarations referenced by +/// potentially-evaluated subexpressions as "referenced". +class EvaluatedExprMarker : public UsedDeclVisitor { +public: + typedef UsedDeclVisitor Inherited; + bool SkipLocalVariables; + ArrayRef StopAt; - // Rebuild as a non-odr-use MemberExpr. - MarkNotOdrUsed(); - return MemberExpr::Create( - S.Context, ME->getBase(), ME->isArrow(), ME->getOperatorLoc(), - ME->getQualifierLoc(), ME->getTemplateKeywordLoc(), ME->getMemberDecl(), - ME->getFoundDecl(), ME->getMemberNameInfo(), CopiedTemplateArgs(ME), - ME->getType(), ME->getValueKind(), ME->getObjectKind(), NOUR); + EvaluatedExprMarker(Sema &S, bool SkipLocalVariables, + ArrayRef StopAt) + : Inherited(S), SkipLocalVariables(SkipLocalVariables), StopAt(StopAt) {} + + void visitUsedDecl(SourceLocation Loc, Decl *D) { + S.MarkFunctionReferenced(Loc, cast(D)); } - case Expr::BinaryOperatorClass: { - auto *BO = cast(E); - Expr *LHS = BO->getLHS(); - Expr *RHS = BO->getRHS(); - // -- If e is a pointer-to-member expression of the form e1 .* e2 ... - if (BO->getOpcode() == BO_PtrMemD) { - ExprResult Sub = Rebuild(LHS); - if (!Sub.isUsable()) - return Sub; - BO->setLHS(Sub.get()); - // -- If e is a comma expression, ... - } else if (BO->getOpcode() == BO_Comma) { - ExprResult Sub = Rebuild(RHS); - if (!Sub.isUsable()) - return Sub; - BO->setRHS(Sub.get()); - } else { - break; - } - return ExprResult(BO); + void Visit(Expr *E) { + if (llvm::is_contained(StopAt, E)) + return; + Inherited::Visit(E); } - // -- If e has the form (e1)... - case Expr::ParenExprClass: { - auto *PE = cast(E); - ExprResult Sub = Rebuild(PE->getSubExpr()); - if (!Sub.isUsable()) - return Sub; - return S.ActOnParenExpr(PE->getLParen(), PE->getRParen(), Sub.get()); + void VisitConstantExpr(ConstantExpr *E) { + // Don't mark declarations within a ConstantExpression, as this expression + // will be evaluated and folded to a value. } - // -- If e is a glvalue conditional expression, ... - // We don't apply this to a binary conditional operator. FIXME: Should we? - case Expr::ConditionalOperatorClass: { - auto *CO = cast(E); - ExprResult LHS = Rebuild(CO->getLHS()); - if (LHS.isInvalid()) - return ExprError(); - ExprResult RHS = Rebuild(CO->getRHS()); - if (RHS.isInvalid()) - return ExprError(); - if (!LHS.isUsable() && !RHS.isUsable()) - return ExprEmpty(); - if (!LHS.isUsable()) - LHS = CO->getLHS(); - if (!RHS.isUsable()) - RHS = CO->getRHS(); - return S.ActOnConditionalOp(CO->getQuestionLoc(), CO->getColonLoc(), - CO->getCond(), LHS.get(), RHS.get()); + void VisitDeclRefExpr(DeclRefExpr *E) { + // If we were asked not to visit local variables, don't. + if (SkipLocalVariables) { + if (VarDecl *VD = dyn_cast(E->getDecl())) + if (VD->hasLocalStorage()) + return; + } + + // FIXME: This can trigger the instantiation of the initializer of a + // variable, which can cause the expression to become value-dependent + // or error-dependent. Do we need to propagate the new dependence bits? + S.MarkDeclRefReferenced(E); } - // [Clang extension] - // -- If e has the form __extension__ e1... - case Expr::UnaryOperatorClass: { - auto *UO = cast(E); - if (UO->getOpcode() != UO_Extension) - break; - ExprResult Sub = Rebuild(UO->getSubExpr()); - if (!Sub.isUsable()) - return Sub; - return S.BuildUnaryOp(nullptr, UO->getOperatorLoc(), UO_Extension, - Sub.get()); + void VisitMemberExpr(MemberExpr *E) { + S.MarkMemberReferenced(E); + Visit(E->getBase()); } +}; +} // namespace - // [Clang extension] - // -- If e has the form _Generic(...), the set of potential results is the - // union of the sets of potential results of the associated expressions. - case Expr::GenericSelectionExprClass: { - auto *GSE = cast(E); +void Sema::MarkDeclarationsReferencedInExpr(Expr *E, + bool SkipLocalVariables, + ArrayRef StopAt) { + EvaluatedExprMarker(*this, SkipLocalVariables, StopAt).Visit(E); +} - SmallVector AssocExprs; - bool AnyChanged = false; - for (Expr *OrigAssocExpr : GSE->getAssocExprs()) { - ExprResult AssocExpr = Rebuild(OrigAssocExpr); - if (AssocExpr.isInvalid()) - return ExprError(); - if (AssocExpr.isUsable()) { - AssocExprs.push_back(AssocExpr.get()); - AnyChanged = true; - } else { - AssocExprs.push_back(OrigAssocExpr); - } - } +/// Emit a diagnostic when statements are reachable. +/// FIXME: check for reachability even in expressions for which we don't build a +/// CFG (eg, in the initializer of a global or in a constant expression). +/// For example, +/// namespace { auto *p = new double[3][false ? (1, 2) : 3]; } +bool Sema::DiagIfReachable(SourceLocation Loc, ArrayRef Stmts, + const PartialDiagnostic &PD) { + if (!Stmts.empty() && getCurFunctionOrMethodDecl()) { + if (!FunctionScopes.empty()) + FunctionScopes.back()->PossiblyUnreachableDiags.push_back( + sema::PossiblyUnreachableDiag(PD, Loc, Stmts)); + return true; + } - void *ExOrTy = nullptr; - bool IsExpr = GSE->isExprPredicate(); - if (IsExpr) - ExOrTy = GSE->getControllingExpr(); - else - ExOrTy = GSE->getControllingType(); - return AnyChanged ? S.CreateGenericSelectionExpr( - GSE->getGenericLoc(), GSE->getDefaultLoc(), - GSE->getRParenLoc(), IsExpr, ExOrTy, - GSE->getAssocTypeSourceInfos(), AssocExprs) - : ExprEmpty(); + // The initializer of a constexpr variable or of the first declaration of a + // static data member is not syntactically a constant evaluated constant, + // but nonetheless is always required to be a constant expression, so we + // can skip diagnosing. + // FIXME: Using the mangling context here is a hack. + if (auto *VD = dyn_cast_or_null( + ExprEvalContexts.back().ManglingContextDecl)) { + if (VD->isConstexpr() || + (VD->isStaticDataMember() && VD->isFirstDecl() && !VD->isInline())) + return false; + // FIXME: For any other kind of variable, we should build a CFG for its + // initializer and check whether the context in question is reachable. } - // [Clang extension] - // -- If e has the form __builtin_choose_expr(...), the set of potential - // results is the union of the sets of potential results of the - // second and third subexpressions. - case Expr::ChooseExprClass: { - auto *CE = cast(E); + Diag(Loc, PD); + return true; +} - ExprResult LHS = Rebuild(CE->getLHS()); - if (LHS.isInvalid()) - return ExprError(); +/// Emit a diagnostic that describes an effect on the run-time behavior +/// of the program being compiled. +/// +/// This routine emits the given diagnostic when the code currently being +/// type-checked is "potentially evaluated", meaning that there is a +/// possibility that the code will actually be executable. Code in sizeof() +/// expressions, code used only during overload resolution, etc., are not +/// potentially evaluated. This routine will suppress such diagnostics or, +/// in the absolutely nutty case of potentially potentially evaluated +/// expressions (C++ typeid), queue the diagnostic to potentially emit it +/// later. +/// +/// This routine should be used for all diagnostics that describe the run-time +/// behavior of a program, such as passing a non-POD value through an ellipsis. +/// Failure to do so will likely result in spurious diagnostics or failures +/// during overload resolution or within sizeof/alignof/typeof/typeid. +bool Sema::DiagRuntimeBehavior(SourceLocation Loc, ArrayRef Stmts, + const PartialDiagnostic &PD) { + + if (ExprEvalContexts.back().isDiscardedStatementContext()) + return false; - ExprResult RHS = Rebuild(CE->getLHS()); - if (RHS.isInvalid()) - return ExprError(); + switch (ExprEvalContexts.back().Context) { + case ExpressionEvaluationContext::Unevaluated: + case ExpressionEvaluationContext::UnevaluatedList: + case ExpressionEvaluationContext::UnevaluatedAbstract: + case ExpressionEvaluationContext::DiscardedStatement: + // The argument will never be evaluated, so don't complain. + break; - if (!LHS.get() && !RHS.get()) - return ExprEmpty(); - if (!LHS.isUsable()) - LHS = CE->getLHS(); - if (!RHS.isUsable()) - RHS = CE->getRHS(); + case ExpressionEvaluationContext::ConstantEvaluated: + case ExpressionEvaluationContext::ImmediateFunctionContext: + // Relevant diagnostics should be produced by constant evaluation. + break; - return S.ActOnChooseExpr(CE->getBuiltinLoc(), CE->getCond(), LHS.get(), - RHS.get(), CE->getRParenLoc()); + case ExpressionEvaluationContext::PotentiallyEvaluated: + case ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: + return DiagIfReachable(Loc, Stmts, PD); } - // Step through non-syntactic nodes. - case Expr::ConstantExprClass: { - auto *CE = cast(E); - ExprResult Sub = Rebuild(CE->getSubExpr()); - if (!Sub.isUsable()) - return Sub; - return ConstantExpr::Create(S.Context, Sub.get()); - } + return false; +} - // We could mostly rely on the recursive rebuilding to rebuild implicit - // casts, but not at the top level, so rebuild them here. - case Expr::ImplicitCastExprClass: { - auto *ICE = cast(E); - // Only step through the narrow set of cast kinds we expect to encounter. - // Anything else suggests we've left the region in which potential results - // can be found. - switch (ICE->getCastKind()) { - case CK_NoOp: - case CK_DerivedToBase: - case CK_UncheckedDerivedToBase: { - ExprResult Sub = Rebuild(ICE->getSubExpr()); - if (!Sub.isUsable()) - return Sub; - CXXCastPath Path(ICE->path()); - return S.ImpCastExprToType(Sub.get(), ICE->getType(), ICE->getCastKind(), - ICE->getValueKind(), &Path); - } +bool Sema::DiagRuntimeBehavior(SourceLocation Loc, const Stmt *Statement, + const PartialDiagnostic &PD) { + return DiagRuntimeBehavior( + Loc, Statement ? llvm::ArrayRef(Statement) : llvm::ArrayRef(), + PD); +} - default: - break; - } - break; - } +bool Sema::CheckCallReturnType(QualType ReturnType, SourceLocation Loc, + CallExpr *CE, FunctionDecl *FD) { + if (ReturnType->isVoidType() || !ReturnType->isIncompleteType()) + return false; - default: - break; + // If we're inside a decltype's expression, don't check for a valid return + // type or construct temporaries until we know whether this is the last call. + if (ExprEvalContexts.back().ExprContext == + ExpressionEvaluationContextRecord::EK_Decltype) { + ExprEvalContexts.back().DelayedDecltypeCalls.push_back(CE); + return false; } - // Can't traverse through this node. Nothing to do. - return ExprEmpty(); -} - -ExprResult Sema::CheckLValueToRValueConversionOperand(Expr *E) { - // Check whether the operand is or contains an object of non-trivial C union - // type. - if (E->getType().isVolatileQualified() && - (E->getType().hasNonTrivialToPrimitiveDestructCUnion() || - E->getType().hasNonTrivialToPrimitiveCopyCUnion())) - checkNonTrivialCUnion(E->getType(), E->getExprLoc(), - Sema::NTCUC_LValueToRValueVolatile, - NTCUK_Destruct|NTCUK_Copy); + class CallReturnIncompleteDiagnoser : public TypeDiagnoser { + FunctionDecl *FD; + CallExpr *CE; - // C++2a [basic.def.odr]p4: - // [...] an expression of non-volatile-qualified non-class type to which - // the lvalue-to-rvalue conversion is applied [...] - if (E->getType().isVolatileQualified() || E->getType()->getAs()) - return E; + public: + CallReturnIncompleteDiagnoser(FunctionDecl *FD, CallExpr *CE) + : FD(FD), CE(CE) { } - ExprResult Result = - rebuildPotentialResultsAsNonOdrUsed(*this, E, NOUR_Constant); - if (Result.isInvalid()) - return ExprError(); - return Result.get() ? Result : E; -} + void diagnose(Sema &S, SourceLocation Loc, QualType T) override { + if (!FD) { + S.Diag(Loc, diag::err_call_incomplete_return) + << T << CE->getSourceRange(); + return; + } -ExprResult Sema::ActOnConstantExpression(ExprResult Res) { - Res = CorrectDelayedTyposInExpr(Res); + S.Diag(Loc, diag::err_call_function_incomplete_return) + << CE->getSourceRange() << FD << T; + S.Diag(FD->getLocation(), diag::note_entity_declared_at) + << FD->getDeclName(); + } + } Diagnoser(FD, CE); - if (!Res.isUsable()) - return Res; + if (RequireCompleteType(Loc, ReturnType, Diagnoser)) + return true; - // If a constant-expression is a reference to a variable where we delay - // deciding whether it is an odr-use, just assume we will apply the - // lvalue-to-rvalue conversion. In the one case where this doesn't happen - // (a non-type template argument), we have special handling anyway. - return CheckLValueToRValueConversionOperand(Res.get()); + return false; } -void Sema::CleanupVarDeclMarking() { - // Iterate through a local copy in case MarkVarDeclODRUsed makes a recursive - // call. - MaybeODRUseExprSet LocalMaybeODRUseExprs; - std::swap(LocalMaybeODRUseExprs, MaybeODRUseExprs); +// Diagnose the s/=/==/ and s/\|=/!=/ typos. Note that adding parentheses +// will prevent this condition from triggering, which is what we want. +void Sema::DiagnoseAssignmentAsCondition(Expr *E) { + SourceLocation Loc; - for (Expr *E : LocalMaybeODRUseExprs) { - if (auto *DRE = dyn_cast(E)) { - MarkVarDeclODRUsed(cast(DRE->getDecl()), - DRE->getLocation(), *this); - } else if (auto *ME = dyn_cast(E)) { - MarkVarDeclODRUsed(cast(ME->getMemberDecl()), ME->getMemberLoc(), - *this); - } else if (auto *FP = dyn_cast(E)) { - for (ValueDecl *VD : *FP) - MarkVarDeclODRUsed(VD, FP->getParameterPackLocation(), *this); - } else { - llvm_unreachable("Unexpected expression"); - } - } + unsigned diagnostic = diag::warn_condition_is_assignment; + bool IsOrAssign = false; - assert(MaybeODRUseExprs.empty() && - "MarkVarDeclODRUsed failed to cleanup MaybeODRUseExprs?"); -} + if (BinaryOperator *Op = dyn_cast(E)) { + if (Op->getOpcode() != BO_Assign && Op->getOpcode() != BO_OrAssign) + return; -static void DoMarkPotentialCapture(Sema &SemaRef, SourceLocation Loc, - ValueDecl *Var, Expr *E) { - VarDecl *VD = Var->getPotentiallyDecomposedVarDecl(); - if (!VD) - return; + IsOrAssign = Op->getOpcode() == BO_OrAssign; - const bool RefersToEnclosingScope = - (SemaRef.CurContext != VD->getDeclContext() && - VD->getDeclContext()->isFunctionOrMethod() && VD->hasLocalStorage()); - if (RefersToEnclosingScope) { - LambdaScopeInfo *const LSI = - SemaRef.getCurLambda(/*IgnoreNonLambdaCapturingScope=*/true); - if (LSI && (!LSI->CallOperator || - !LSI->CallOperator->Encloses(Var->getDeclContext()))) { - // If a variable could potentially be odr-used, defer marking it so - // until we finish analyzing the full expression for any - // lvalue-to-rvalue - // or discarded value conversions that would obviate odr-use. - // Add it to the list of potential captures that will be analyzed - // later (ActOnFinishFullExpr) for eventual capture and odr-use marking - // unless the variable is a reference that was initialized by a constant - // expression (this will never need to be captured or odr-used). - // - // FIXME: We can simplify this a lot after implementing P0588R1. - assert(E && "Capture variable should be used in an expression."); - if (!Var->getType()->isReferenceType() || - !VD->isUsableInConstantExpressions(SemaRef.Context)) - LSI->addPotentialCapture(E->IgnoreParens()); + // Greylist some idioms by putting them into a warning subcategory. + if (ObjCMessageExpr *ME + = dyn_cast(Op->getRHS()->IgnoreParenCasts())) { + Selector Sel = ME->getSelector(); + + // self = [ init...] + if (ObjC().isSelfExpr(Op->getLHS()) && ME->getMethodFamily() == OMF_init) + diagnostic = diag::warn_condition_is_idiomatic_assignment; + + // = [ nextObject] + else if (Sel.isUnarySelector() && Sel.getNameForSlot(0) == "nextObject") + diagnostic = diag::warn_condition_is_idiomatic_assignment; } - } -} -static void DoMarkVarDeclReferenced( - Sema &SemaRef, SourceLocation Loc, VarDecl *Var, Expr *E, - llvm::DenseMap &RefsMinusAssignments) { - assert((!E || isa(E) || isa(E) || - isa(E)) && - "Invalid Expr argument to DoMarkVarDeclReferenced"); - Var->setReferenced(); + Loc = Op->getOperatorLoc(); + } else if (CXXOperatorCallExpr *Op = dyn_cast(E)) { + if (Op->getOperator() != OO_Equal && Op->getOperator() != OO_PipeEqual) + return; - if (Var->isInvalidDecl()) + IsOrAssign = Op->getOperator() == OO_PipeEqual; + Loc = Op->getOperatorLoc(); + } else if (PseudoObjectExpr *POE = dyn_cast(E)) + return DiagnoseAssignmentAsCondition(POE->getSyntacticForm()); + else { + // Not an assignment. return; + } - auto *MSI = Var->getMemberSpecializationInfo(); - TemplateSpecializationKind TSK = MSI ? MSI->getTemplateSpecializationKind() - : Var->getTemplateSpecializationKind(); - - OdrUseContext OdrUse = isOdrUseContext(SemaRef); - bool UsableInConstantExpr = - Var->mightBeUsableInConstantExpressions(SemaRef.Context); + Diag(Loc, diagnostic) << E->getSourceRange(); - if (Var->isLocalVarDeclOrParm() && !Var->hasExternalStorage()) { - RefsMinusAssignments.insert({Var, 0}).first->getSecond()++; - } + SourceLocation Open = E->getBeginLoc(); + SourceLocation Close = getLocForEndOfToken(E->getSourceRange().getEnd()); + Diag(Loc, diag::note_condition_assign_silence) + << FixItHint::CreateInsertion(Open, "(") + << FixItHint::CreateInsertion(Close, ")"); - // C++20 [expr.const]p12: - // A variable [...] is needed for constant evaluation if it is [...] a - // variable whose name appears as a potentially constant evaluated - // expression that is either a contexpr variable or is of non-volatile - // const-qualified integral type or of reference type - bool NeededForConstantEvaluation = - isPotentiallyConstantEvaluatedContext(SemaRef) && UsableInConstantExpr; + if (IsOrAssign) + Diag(Loc, diag::note_condition_or_assign_to_comparison) + << FixItHint::CreateReplacement(Loc, "!="); + else + Diag(Loc, diag::note_condition_assign_to_comparison) + << FixItHint::CreateReplacement(Loc, "=="); +} - bool NeedDefinition = - OdrUse == OdrUseContext::Used || NeededForConstantEvaluation; +void Sema::DiagnoseEqualityWithExtraParens(ParenExpr *ParenE) { + // Don't warn if the parens came from a macro. + SourceLocation parenLoc = ParenE->getBeginLoc(); + if (parenLoc.isInvalid() || parenLoc.isMacroID()) + return; + // Don't warn for dependent expressions. + if (ParenE->isTypeDependent()) + return; - assert(!isa(Var) && - "Can't instantiate a partial template specialization."); + Expr *E = ParenE->IgnoreParens(); + if (ParenE->isProducedByFoldExpansion() && ParenE->getSubExpr() == E) + return; - // If this might be a member specialization of a static data member, check - // the specialization is visible. We already did the checks for variable - // template specializations when we created them. - if (NeedDefinition && TSK != TSK_Undeclared && - !isa(Var)) - SemaRef.checkSpecializationVisibility(Loc, Var); + if (BinaryOperator *opE = dyn_cast(E)) + if (opE->getOpcode() == BO_EQ && + opE->getLHS()->IgnoreParenImpCasts()->isModifiableLvalue(Context) + == Expr::MLV_Valid) { + SourceLocation Loc = opE->getOperatorLoc(); - // Perform implicit instantiation of static data members, static data member - // templates of class templates, and variable template specializations. Delay - // instantiations of variable templates, except for those that could be used - // in a constant expression. - if (NeedDefinition && isTemplateInstantiation(TSK)) { - // Per C++17 [temp.explicit]p10, we may instantiate despite an explicit - // instantiation declaration if a variable is usable in a constant - // expression (among other cases). - bool TryInstantiating = - TSK == TSK_ImplicitInstantiation || - (TSK == TSK_ExplicitInstantiationDeclaration && UsableInConstantExpr); + Diag(Loc, diag::warn_equality_with_extra_parens) << E->getSourceRange(); + SourceRange ParenERange = ParenE->getSourceRange(); + Diag(Loc, diag::note_equality_comparison_silence) + << FixItHint::CreateRemoval(ParenERange.getBegin()) + << FixItHint::CreateRemoval(ParenERange.getEnd()); + Diag(Loc, diag::note_equality_comparison_to_assign) + << FixItHint::CreateReplacement(Loc, "="); + } +} - if (TryInstantiating) { - SourceLocation PointOfInstantiation = - MSI ? MSI->getPointOfInstantiation() : Var->getPointOfInstantiation(); - bool FirstInstantiation = PointOfInstantiation.isInvalid(); - if (FirstInstantiation) { - PointOfInstantiation = Loc; - if (MSI) - MSI->setPointOfInstantiation(PointOfInstantiation); - // FIXME: Notify listener. - else - Var->setTemplateSpecializationKind(TSK, PointOfInstantiation); - } +ExprResult Sema::CheckBooleanCondition(SourceLocation Loc, Expr *E, + bool IsConstexpr) { + DiagnoseAssignmentAsCondition(E); + if (ParenExpr *parenE = dyn_cast(E)) + DiagnoseEqualityWithExtraParens(parenE); - if (UsableInConstantExpr) { - // Do not defer instantiations of variables that could be used in a - // constant expression. - SemaRef.runWithSufficientStackSpace(PointOfInstantiation, [&] { - SemaRef.InstantiateVariableDefinition(PointOfInstantiation, Var); - }); + ExprResult result = CheckPlaceholderExpr(E); + if (result.isInvalid()) return ExprError(); + E = result.get(); - // The size of an incomplete array type can be updated by - // instantiating the initializer. The DeclRefExpr's type should be - // updated accordingly too, or users of it would be confused! - if (E) - SemaRef.getCompletedType(E); + if (!E->isTypeDependent()) { + if (getLangOpts().CPlusPlus) + return CheckCXXBooleanCondition(E, IsConstexpr); // C++ 6.4p4 - // Re-set the member to trigger a recomputation of the dependence bits - // for the expression. - if (auto *DRE = dyn_cast_or_null(E)) - DRE->setDecl(DRE->getDecl()); - else if (auto *ME = dyn_cast_or_null(E)) - ME->setMemberDecl(ME->getMemberDecl()); - } else if (FirstInstantiation) { - SemaRef.PendingInstantiations - .push_back(std::make_pair(Var, PointOfInstantiation)); - } else { - bool Inserted = false; - for (auto &I : SemaRef.SavedPendingInstantiations) { - auto Iter = llvm::find_if( - I, [Var](const Sema::PendingImplicitInstantiation &P) { - return P.first == Var; - }); - if (Iter != I.end()) { - SemaRef.PendingInstantiations.push_back(*Iter); - I.erase(Iter); - Inserted = true; - break; - } - } + ExprResult ERes = DefaultFunctionArrayLvalueConversion(E); + if (ERes.isInvalid()) + return ExprError(); + E = ERes.get(); - // FIXME: For a specialization of a variable template, we don't - // distinguish between "declaration and type implicitly instantiated" - // and "implicit instantiation of definition requested", so we have - // no direct way to avoid enqueueing the pending instantiation - // multiple times. - if (isa(Var) && !Inserted) - SemaRef.PendingInstantiations - .push_back(std::make_pair(Var, PointOfInstantiation)); - } + QualType T = E->getType(); + if (!T->isScalarType()) { // C99 6.8.4.1p1 + Diag(Loc, diag::err_typecheck_statement_requires_scalar) + << T << E->getSourceRange(); + return ExprError(); } + CheckBoolLikeConversion(E, Loc); } - // C++2a [basic.def.odr]p4: - // A variable x whose name appears as a potentially-evaluated expression e - // is odr-used by e unless - // -- x is a reference that is usable in constant expressions - // -- x is a variable of non-reference type that is usable in constant - // expressions and has no mutable subobjects [FIXME], and e is an - // element of the set of potential results of an expression of - // non-volatile-qualified non-class type to which the lvalue-to-rvalue - // conversion is applied - // -- x is a variable of non-reference type, and e is an element of the set - // of potential results of a discarded-value expression to which the - // lvalue-to-rvalue conversion is not applied [FIXME] - // - // We check the first part of the second bullet here, and - // Sema::CheckLValueToRValueConversionOperand deals with the second part. - // FIXME: To get the third bullet right, we need to delay this even for - // variables that are not usable in constant expressions. - - // If we already know this isn't an odr-use, there's nothing more to do. - if (DeclRefExpr *DRE = dyn_cast_or_null(E)) - if (DRE->isNonOdrUse()) - return; - if (MemberExpr *ME = dyn_cast_or_null(E)) - if (ME->isNonOdrUse()) - return; + return E; +} - switch (OdrUse) { - case OdrUseContext::None: - // In some cases, a variable may not have been marked unevaluated, if it - // appears in a defaukt initializer. - assert((!E || isa(E) || - SemaRef.isUnevaluatedContext()) && - "missing non-odr-use marking for unevaluated decl ref"); - break; +Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc, + Expr *SubExpr, ConditionKind CK, + bool MissingOK) { + // MissingOK indicates whether having no condition expression is valid + // (for loop) or invalid (e.g. while loop). + if (!SubExpr) + return MissingOK ? ConditionResult() : ConditionError(); - case OdrUseContext::FormallyOdrUsed: - // FIXME: Ignoring formal odr-uses results in incorrect lambda capture - // behavior. + ExprResult Cond; + switch (CK) { + case ConditionKind::Boolean: + Cond = CheckBooleanCondition(Loc, SubExpr); break; - case OdrUseContext::Used: - // If we might later find that this expression isn't actually an odr-use, - // delay the marking. - if (E && Var->isUsableInConstantExpressions(SemaRef.Context)) - SemaRef.MaybeODRUseExprs.insert(E); - else - MarkVarDeclODRUsed(Var, Loc, SemaRef); + case ConditionKind::ConstexprIf: + Cond = CheckBooleanCondition(Loc, SubExpr, true); break; - case OdrUseContext::Dependent: - // If this is a dependent context, we don't need to mark variables as - // odr-used, but we may still need to track them for lambda capture. - // FIXME: Do we also need to do this inside dependent typeid expressions - // (which are modeled as unevaluated at this point)? - DoMarkPotentialCapture(SemaRef, Loc, Var, E); + case ConditionKind::Switch: + Cond = CheckSwitchCondition(Loc, SubExpr); break; } + if (Cond.isInvalid()) { + Cond = CreateRecoveryExpr(SubExpr->getBeginLoc(), SubExpr->getEndLoc(), + {SubExpr}, PreferredConditionType(CK)); + if (!Cond.get()) + return ConditionError(); + } + // FIXME: FullExprArg doesn't have an invalid bit, so check nullness instead. + FullExprArg FullExpr = MakeFullExpr(Cond.get(), Loc); + if (!FullExpr.get()) + return ConditionError(); + + return ConditionResult(*this, nullptr, FullExpr, + CK == ConditionKind::ConstexprIf); } -static void DoMarkBindingDeclReferenced(Sema &SemaRef, SourceLocation Loc, - BindingDecl *BD, Expr *E) { - BD->setReferenced(); +namespace { + /// A visitor for rebuilding a call to an __unknown_any expression + /// to have an appropriate type. + struct RebuildUnknownAnyFunction + : StmtVisitor { - if (BD->isInvalidDecl()) - return; + Sema &S; - OdrUseContext OdrUse = isOdrUseContext(SemaRef); - if (OdrUse == OdrUseContext::Used) { - QualType CaptureType, DeclRefType; - SemaRef.tryCaptureVariable(BD, Loc, Sema::TryCapture_Implicit, - /*EllipsisLoc*/ SourceLocation(), - /*BuildAndDiagnose*/ true, CaptureType, - DeclRefType, - /*FunctionScopeIndexToStopAt*/ nullptr); - } else if (OdrUse == OdrUseContext::Dependent) { - DoMarkPotentialCapture(SemaRef, Loc, BD, E); - } -} + RebuildUnknownAnyFunction(Sema &S) : S(S) {} -void Sema::MarkVariableReferenced(SourceLocation Loc, VarDecl *Var) { - DoMarkVarDeclReferenced(*this, Loc, Var, nullptr, RefsMinusAssignments); -} + ExprResult VisitStmt(Stmt *S) { + llvm_unreachable("unexpected statement!"); + } -// C++ [temp.dep.expr]p3: -// An id-expression is type-dependent if it contains: -// - an identifier associated by name lookup with an entity captured by copy -// in a lambda-expression that has an explicit object parameter whose type -// is dependent ([dcl.fct]), -static void FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter( - Sema &SemaRef, ValueDecl *D, Expr *E) { - auto *ID = dyn_cast(E); - if (!ID || ID->isTypeDependent() || !ID->refersToEnclosingVariableOrCapture()) - return; + ExprResult VisitExpr(Expr *E) { + S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_call) + << E->getSourceRange(); + return ExprError(); + } - // If any enclosing lambda with a dependent explicit object parameter either - // explicitly captures the variable by value, or has a capture default of '=' - // and does not capture the variable by reference, then the type of the DRE - // is dependent on the type of that lambda's explicit object parameter. - auto IsDependent = [&]() { - for (auto *Scope : llvm::reverse(SemaRef.FunctionScopes)) { - auto *LSI = dyn_cast(Scope); - if (!LSI) - continue; + /// Rebuild an expression which simply semantically wraps another + /// expression which it shares the type and value kind of. + template ExprResult rebuildSugarExpr(T *E) { + ExprResult SubResult = Visit(E->getSubExpr()); + if (SubResult.isInvalid()) return ExprError(); - if (LSI->Lambda && !LSI->Lambda->Encloses(SemaRef.CurContext) && - LSI->AfterParameterList) - return false; + Expr *SubExpr = SubResult.get(); + E->setSubExpr(SubExpr); + E->setType(SubExpr->getType()); + E->setValueKind(SubExpr->getValueKind()); + assert(E->getObjectKind() == OK_Ordinary); + return E; + } - const auto *MD = LSI->CallOperator; - if (MD->getType().isNull()) - continue; + ExprResult VisitParenExpr(ParenExpr *E) { + return rebuildSugarExpr(E); + } - const auto *Ty = MD->getType()->getAs(); - if (!Ty || !MD->isExplicitObjectMemberFunction() || - !Ty->getParamType(0)->isDependentType()) - continue; + ExprResult VisitUnaryExtension(UnaryOperator *E) { + return rebuildSugarExpr(E); + } - if (auto *C = LSI->CaptureMap.count(D) ? &LSI->getCapture(D) : nullptr) { - if (C->isCopyCapture()) - return true; - continue; - } + ExprResult VisitUnaryAddrOf(UnaryOperator *E) { + ExprResult SubResult = Visit(E->getSubExpr()); + if (SubResult.isInvalid()) return ExprError(); - if (LSI->ImpCaptureStyle == LambdaScopeInfo::ImpCap_LambdaByval) - return true; + Expr *SubExpr = SubResult.get(); + E->setSubExpr(SubExpr); + E->setType(S.Context.getPointerType(SubExpr->getType())); + assert(E->isPRValue()); + assert(E->getObjectKind() == OK_Ordinary); + return E; } - return false; - }(); - ID->setCapturedByCopyInLambdaWithExplicitObjectParameter( - IsDependent, SemaRef.getASTContext()); -} + ExprResult resolveDecl(Expr *E, ValueDecl *VD) { + if (!isa(VD)) return VisitExpr(E); -static void -MarkExprReferenced(Sema &SemaRef, SourceLocation Loc, Decl *D, Expr *E, - bool MightBeOdrUse, - llvm::DenseMap &RefsMinusAssignments) { - if (SemaRef.OpenMP().isInOpenMPDeclareTargetContext()) - SemaRef.OpenMP().checkDeclIsAllowedInOpenMPTarget(E, D); + E->setType(VD->getType()); - if (VarDecl *Var = dyn_cast(D)) { - DoMarkVarDeclReferenced(SemaRef, Loc, Var, E, RefsMinusAssignments); - if (SemaRef.getLangOpts().CPlusPlus) - FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(SemaRef, - Var, E); - return; - } + assert(E->isPRValue()); + if (S.getLangOpts().CPlusPlus && + !(isa(VD) && + cast(VD)->isInstance())) + E->setValueKind(VK_LValue); - if (BindingDecl *Decl = dyn_cast(D)) { - DoMarkBindingDeclReferenced(SemaRef, Loc, Decl, E); - if (SemaRef.getLangOpts().CPlusPlus) - FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(SemaRef, - Decl, E); - return; - } - SemaRef.MarkAnyDeclReferenced(Loc, D, MightBeOdrUse); + return E; + } - // If this is a call to a method via a cast, also mark the method in the - // derived class used in case codegen can devirtualize the call. - const MemberExpr *ME = dyn_cast(E); - if (!ME) - return; - CXXMethodDecl *MD = dyn_cast(ME->getMemberDecl()); - if (!MD) - return; - // Only attempt to devirtualize if this is truly a virtual call. - bool IsVirtualCall = MD->isVirtual() && - ME->performsVirtualDispatch(SemaRef.getLangOpts()); - if (!IsVirtualCall) - return; + ExprResult VisitMemberExpr(MemberExpr *E) { + return resolveDecl(E, E->getMemberDecl()); + } - // If it's possible to devirtualize the call, mark the called function - // referenced. - CXXMethodDecl *DM = MD->getDevirtualizedMethod( - ME->getBase(), SemaRef.getLangOpts().AppleKext); - if (DM) - SemaRef.MarkAnyDeclReferenced(Loc, DM, MightBeOdrUse); + ExprResult VisitDeclRefExpr(DeclRefExpr *E) { + return resolveDecl(E, E->getDecl()); + } + }; } -void Sema::MarkDeclRefReferenced(DeclRefExpr *E, const Expr *Base) { - // TODO: update this with DR# once a defect report is filed. - // C++11 defect. The address of a pure member should not be an ODR use, even - // if it's a qualified reference. - bool OdrUse = true; - if (const CXXMethodDecl *Method = dyn_cast(E->getDecl())) - if (Method->isVirtual() && - !Method->getDevirtualizedMethod(Base, getLangOpts().AppleKext)) - OdrUse = false; - - if (auto *FD = dyn_cast(E->getDecl())) { - if (!isUnevaluatedContext() && !isConstantEvaluatedContext() && - !isImmediateFunctionContext() && - !isCheckingDefaultArgumentOrInitializer() && - FD->isImmediateFunction() && !RebuildingImmediateInvocation && - !FD->isDependentContext()) - ExprEvalContexts.back().ReferenceToConsteval.insert(E); - } - MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse, - RefsMinusAssignments); +/// Given a function expression of unknown-any type, try to rebuild it +/// to have a function type. +static ExprResult rebuildUnknownAnyFunction(Sema &S, Expr *FunctionExpr) { + ExprResult Result = RebuildUnknownAnyFunction(S).Visit(FunctionExpr); + if (Result.isInvalid()) return ExprError(); + return S.DefaultFunctionArrayConversion(Result.get()); } -void Sema::MarkMemberReferenced(MemberExpr *E) { - // C++11 [basic.def.odr]p2: - // A non-overloaded function whose name appears as a potentially-evaluated - // expression or a member of a set of candidate functions, if selected by - // overload resolution when referred to from a potentially-evaluated - // expression, is odr-used, unless it is a pure virtual function and its - // name is not explicitly qualified. - bool MightBeOdrUse = true; - if (E->performsVirtualDispatch(getLangOpts())) { - if (CXXMethodDecl *Method = dyn_cast(E->getMemberDecl())) - if (Method->isPureVirtual()) - MightBeOdrUse = false; - } - SourceLocation Loc = - E->getMemberLoc().isValid() ? E->getMemberLoc() : E->getBeginLoc(); - MarkExprReferenced(*this, Loc, E->getMemberDecl(), E, MightBeOdrUse, - RefsMinusAssignments); -} +namespace { + /// A visitor for rebuilding an expression of type __unknown_anytype + /// into one which resolves the type directly on the referring + /// expression. Strict preservation of the original source + /// structure is not a goal. + struct RebuildUnknownAnyExpr + : StmtVisitor { -void Sema::MarkFunctionParmPackReferenced(FunctionParmPackExpr *E) { - for (ValueDecl *VD : *E) - MarkExprReferenced(*this, E->getParameterPackLocation(), VD, E, true, - RefsMinusAssignments); -} + Sema &S; -/// Perform marking for a reference to an arbitrary declaration. It -/// marks the declaration referenced, and performs odr-use checking for -/// functions and variables. This method should not be used when building a -/// normal expression which refers to a variable. -void Sema::MarkAnyDeclReferenced(SourceLocation Loc, Decl *D, - bool MightBeOdrUse) { - if (MightBeOdrUse) { - if (auto *VD = dyn_cast(D)) { - MarkVariableReferenced(Loc, VD); - return; + /// The current destination type. + QualType DestType; + + RebuildUnknownAnyExpr(Sema &S, QualType CastType) + : S(S), DestType(CastType) {} + + ExprResult VisitStmt(Stmt *S) { + llvm_unreachable("unexpected statement!"); } - } - if (auto *FD = dyn_cast(D)) { - MarkFunctionReferenced(Loc, FD, MightBeOdrUse); - return; - } - D->setReferenced(); -} -namespace { - // Mark all of the declarations used by a type as referenced. - // FIXME: Not fully implemented yet! We need to have a better understanding - // of when we're entering a context we should not recurse into. - // FIXME: This is and EvaluatedExprMarker are more-or-less equivalent to - // TreeTransforms rebuilding the type in a new context. Rather than - // duplicating the TreeTransform logic, we should consider reusing it here. - // Currently that causes problems when rebuilding LambdaExprs. -class MarkReferencedDecls : public DynamicRecursiveASTVisitor { - Sema &S; - SourceLocation Loc; + ExprResult VisitExpr(Expr *E) { + S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_expr) + << E->getSourceRange(); + return ExprError(); + } -public: - MarkReferencedDecls(Sema &S, SourceLocation Loc) : S(S), Loc(Loc) {} + ExprResult VisitCallExpr(CallExpr *E); + ExprResult VisitObjCMessageExpr(ObjCMessageExpr *E); - bool TraverseTemplateArgument(const TemplateArgument &Arg) override; -}; -} + /// Rebuild an expression which simply semantically wraps another + /// expression which it shares the type and value kind of. + template ExprResult rebuildSugarExpr(T *E) { + ExprResult SubResult = Visit(E->getSubExpr()); + if (SubResult.isInvalid()) return ExprError(); + Expr *SubExpr = SubResult.get(); + E->setSubExpr(SubExpr); + E->setType(SubExpr->getType()); + E->setValueKind(SubExpr->getValueKind()); + assert(E->getObjectKind() == OK_Ordinary); + return E; + } -bool MarkReferencedDecls::TraverseTemplateArgument( - const TemplateArgument &Arg) { - { - // A non-type template argument is a constant-evaluated context. - EnterExpressionEvaluationContext Evaluated( - S, Sema::ExpressionEvaluationContext::ConstantEvaluated); - if (Arg.getKind() == TemplateArgument::Declaration) { - if (Decl *D = Arg.getAsDecl()) - S.MarkAnyDeclReferenced(Loc, D, true); - } else if (Arg.getKind() == TemplateArgument::Expression) { - S.MarkDeclarationsReferencedInExpr(Arg.getAsExpr(), false); + ExprResult VisitParenExpr(ParenExpr *E) { + return rebuildSugarExpr(E); } - } - return DynamicRecursiveASTVisitor::TraverseTemplateArgument(Arg); -} + ExprResult VisitUnaryExtension(UnaryOperator *E) { + return rebuildSugarExpr(E); + } -void Sema::MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T) { - MarkReferencedDecls Marker(*this, Loc); - Marker.TraverseType(T); -} + ExprResult VisitUnaryAddrOf(UnaryOperator *E) { + const PointerType *Ptr = DestType->getAs(); + if (!Ptr) { + S.Diag(E->getOperatorLoc(), diag::err_unknown_any_addrof) + << E->getSourceRange(); + return ExprError(); + } -namespace { -/// Helper class that marks all of the declarations referenced by -/// potentially-evaluated subexpressions as "referenced". -class EvaluatedExprMarker : public UsedDeclVisitor { -public: - typedef UsedDeclVisitor Inherited; - bool SkipLocalVariables; - ArrayRef StopAt; + if (isa(E->getSubExpr())) { + S.Diag(E->getOperatorLoc(), diag::err_unknown_any_addrof_call) + << E->getSourceRange(); + return ExprError(); + } - EvaluatedExprMarker(Sema &S, bool SkipLocalVariables, - ArrayRef StopAt) - : Inherited(S), SkipLocalVariables(SkipLocalVariables), StopAt(StopAt) {} + assert(E->isPRValue()); + assert(E->getObjectKind() == OK_Ordinary); + E->setType(DestType); - void visitUsedDecl(SourceLocation Loc, Decl *D) { - S.MarkFunctionReferenced(Loc, cast(D)); - } + // Build the sub-expression as if it were an object of the pointee type. + DestType = Ptr->getPointeeType(); + ExprResult SubResult = Visit(E->getSubExpr()); + if (SubResult.isInvalid()) return ExprError(); + E->setSubExpr(SubResult.get()); + return E; + } - void Visit(Expr *E) { - if (llvm::is_contained(StopAt, E)) - return; - Inherited::Visit(E); - } + ExprResult VisitImplicitCastExpr(ImplicitCastExpr *E); - void VisitConstantExpr(ConstantExpr *E) { - // Don't mark declarations within a ConstantExpression, as this expression - // will be evaluated and folded to a value. - } + ExprResult resolveDecl(Expr *E, ValueDecl *VD); - void VisitDeclRefExpr(DeclRefExpr *E) { - // If we were asked not to visit local variables, don't. - if (SkipLocalVariables) { - if (VarDecl *VD = dyn_cast(E->getDecl())) - if (VD->hasLocalStorage()) - return; + ExprResult VisitMemberExpr(MemberExpr *E) { + return resolveDecl(E, E->getMemberDecl()); } - // FIXME: This can trigger the instantiation of the initializer of a - // variable, which can cause the expression to become value-dependent - // or error-dependent. Do we need to propagate the new dependence bits? - S.MarkDeclRefReferenced(E); - } + ExprResult VisitDeclRefExpr(DeclRefExpr *E) { + return resolveDecl(E, E->getDecl()); + } + }; +} - void VisitMemberExpr(MemberExpr *E) { - S.MarkMemberReferenced(E); - Visit(E->getBase()); - } -}; -} // namespace +/// Rebuilds a call expression which yielded __unknown_anytype. +ExprResult RebuildUnknownAnyExpr::VisitCallExpr(CallExpr *E) { + Expr *CalleeExpr = E->getCallee(); -void Sema::MarkDeclarationsReferencedInExpr(Expr *E, - bool SkipLocalVariables, - ArrayRef StopAt) { - EvaluatedExprMarker(*this, SkipLocalVariables, StopAt).Visit(E); -} + enum FnKind { + FK_MemberFunction, + FK_FunctionPointer, + FK_BlockPointer + }; -/// Emit a diagnostic when statements are reachable. -/// FIXME: check for reachability even in expressions for which we don't build a -/// CFG (eg, in the initializer of a global or in a constant expression). -/// For example, -/// namespace { auto *p = new double[3][false ? (1, 2) : 3]; } -bool Sema::DiagIfReachable(SourceLocation Loc, ArrayRef Stmts, - const PartialDiagnostic &PD) { - if (!Stmts.empty() && getCurFunctionOrMethodDecl()) { - if (!FunctionScopes.empty()) - FunctionScopes.back()->PossiblyUnreachableDiags.push_back( - sema::PossiblyUnreachableDiag(PD, Loc, Stmts)); - return true; + FnKind Kind; + QualType CalleeType = CalleeExpr->getType(); + if (CalleeType == S.Context.BoundMemberTy) { + assert(isa(E) || isa(E)); + Kind = FK_MemberFunction; + CalleeType = Expr::findBoundMemberType(CalleeExpr); + } else if (const PointerType *Ptr = CalleeType->getAs()) { + CalleeType = Ptr->getPointeeType(); + Kind = FK_FunctionPointer; + } else { + CalleeType = CalleeType->castAs()->getPointeeType(); + Kind = FK_BlockPointer; } + const FunctionType *FnType = CalleeType->castAs(); - // The initializer of a constexpr variable or of the first declaration of a - // static data member is not syntactically a constant evaluated constant, - // but nonetheless is always required to be a constant expression, so we - // can skip diagnosing. - // FIXME: Using the mangling context here is a hack. - if (auto *VD = dyn_cast_or_null( - ExprEvalContexts.back().ManglingContextDecl)) { - if (VD->isConstexpr() || - (VD->isStaticDataMember() && VD->isFirstDecl() && !VD->isInline())) - return false; - // FIXME: For any other kind of variable, we should build a CFG for its - // initializer and check whether the context in question is reachable. + // Verify that this is a legal result type of a function. + if ((DestType->isArrayType() && !S.getLangOpts().allowArrayReturnTypes()) || + DestType->isFunctionType()) { + unsigned diagID = diag::err_func_returning_array_function; + if (Kind == FK_BlockPointer) + diagID = diag::err_block_returning_array_function; + + S.Diag(E->getExprLoc(), diagID) + << DestType->isFunctionType() << DestType; + return ExprError(); } - Diag(Loc, PD); - return true; -} + // Otherwise, go ahead and set DestType as the call's result. + E->setType(DestType.getNonLValueExprType(S.Context)); + E->setValueKind(Expr::getValueKindForType(DestType)); + assert(E->getObjectKind() == OK_Ordinary); -/// Emit a diagnostic that describes an effect on the run-time behavior -/// of the program being compiled. -/// -/// This routine emits the given diagnostic when the code currently being -/// type-checked is "potentially evaluated", meaning that there is a -/// possibility that the code will actually be executable. Code in sizeof() -/// expressions, code used only during overload resolution, etc., are not -/// potentially evaluated. This routine will suppress such diagnostics or, -/// in the absolutely nutty case of potentially potentially evaluated -/// expressions (C++ typeid), queue the diagnostic to potentially emit it -/// later. -/// -/// This routine should be used for all diagnostics that describe the run-time -/// behavior of a program, such as passing a non-POD value through an ellipsis. -/// Failure to do so will likely result in spurious diagnostics or failures -/// during overload resolution or within sizeof/alignof/typeof/typeid. -bool Sema::DiagRuntimeBehavior(SourceLocation Loc, ArrayRef Stmts, - const PartialDiagnostic &PD) { + // Rebuild the function type, replacing the result type with DestType. + const FunctionProtoType *Proto = dyn_cast(FnType); + if (Proto) { + // __unknown_anytype(...) is a special case used by the debugger when + // it has no idea what a function's signature is. + // + // We want to build this call essentially under the K&R + // unprototyped rules, but making a FunctionNoProtoType in C++ + // would foul up all sorts of assumptions. However, we cannot + // simply pass all arguments as variadic arguments, nor can we + // portably just call the function under a non-variadic type; see + // the comment on IR-gen's TargetInfo::isNoProtoCallVariadic. + // However, it turns out that in practice it is generally safe to + // call a function declared as "A foo(B,C,D);" under the prototype + // "A foo(B,C,D,...);". The only known exception is with the + // Windows ABI, where any variadic function is implicitly cdecl + // regardless of its normal CC. Therefore we change the parameter + // types to match the types of the arguments. + // + // This is a hack, but it is far superior to moving the + // corresponding target-specific code from IR-gen to Sema/AST. - if (ExprEvalContexts.back().isDiscardedStatementContext()) - return false; + ArrayRef ParamTypes = Proto->getParamTypes(); + SmallVector ArgTypes; + if (ParamTypes.empty() && Proto->isVariadic()) { // the special case + ArgTypes.reserve(E->getNumArgs()); + for (unsigned i = 0, e = E->getNumArgs(); i != e; ++i) { + ArgTypes.push_back(S.Context.getReferenceQualifiedType(E->getArg(i))); + } + ParamTypes = ArgTypes; + } + DestType = S.Context.getFunctionType(DestType, ParamTypes, + Proto->getExtProtoInfo()); + } else { + DestType = S.Context.getFunctionNoProtoType(DestType, + FnType->getExtInfo()); + } - switch (ExprEvalContexts.back().Context) { - case ExpressionEvaluationContext::Unevaluated: - case ExpressionEvaluationContext::UnevaluatedList: - case ExpressionEvaluationContext::UnevaluatedAbstract: - case ExpressionEvaluationContext::DiscardedStatement: - // The argument will never be evaluated, so don't complain. + // Rebuild the appropriate pointer-to-function type. + switch (Kind) { + case FK_MemberFunction: + // Nothing to do. break; - case ExpressionEvaluationContext::ConstantEvaluated: - case ExpressionEvaluationContext::ImmediateFunctionContext: - // Relevant diagnostics should be produced by constant evaluation. + case FK_FunctionPointer: + DestType = S.Context.getPointerType(DestType); break; - case ExpressionEvaluationContext::PotentiallyEvaluated: - case ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: - return DiagIfReachable(Loc, Stmts, PD); + case FK_BlockPointer: + DestType = S.Context.getBlockPointerType(DestType); + break; } - return false; -} + // Finally, we can recurse. + ExprResult CalleeResult = Visit(CalleeExpr); + if (!CalleeResult.isUsable()) return ExprError(); + E->setCallee(CalleeResult.get()); -bool Sema::DiagRuntimeBehavior(SourceLocation Loc, const Stmt *Statement, - const PartialDiagnostic &PD) { - return DiagRuntimeBehavior( - Loc, Statement ? llvm::ArrayRef(Statement) : llvm::ArrayRef(), - PD); + // Bind a temporary if necessary. + return S.MaybeBindToTemporary(E); } -bool Sema::CheckCallReturnType(QualType ReturnType, SourceLocation Loc, - CallExpr *CE, FunctionDecl *FD) { - if (ReturnType->isVoidType() || !ReturnType->isIncompleteType()) - return false; +ExprResult RebuildUnknownAnyExpr::VisitObjCMessageExpr(ObjCMessageExpr *E) { + // Verify that this is a legal result type of a call. + if (DestType->isArrayType() || DestType->isFunctionType()) { + S.Diag(E->getExprLoc(), diag::err_func_returning_array_function) + << DestType->isFunctionType() << DestType; + return ExprError(); + } - // If we're inside a decltype's expression, don't check for a valid return - // type or construct temporaries until we know whether this is the last call. - if (ExprEvalContexts.back().ExprContext == - ExpressionEvaluationContextRecord::EK_Decltype) { - ExprEvalContexts.back().DelayedDecltypeCalls.push_back(CE); - return false; + // Rewrite the method result type if available. + if (ObjCMethodDecl *Method = E->getMethodDecl()) { + assert(Method->getReturnType() == S.Context.UnknownAnyTy); + Method->setReturnType(DestType); } - class CallReturnIncompleteDiagnoser : public TypeDiagnoser { - FunctionDecl *FD; - CallExpr *CE; + // Change the type of the message. + E->setType(DestType.getNonReferenceType()); + E->setValueKind(Expr::getValueKindForType(DestType)); + + return S.MaybeBindToTemporary(E); +} + +ExprResult RebuildUnknownAnyExpr::VisitImplicitCastExpr(ImplicitCastExpr *E) { + // The only case we should ever see here is a function-to-pointer decay. + if (E->getCastKind() == CK_FunctionToPointerDecay) { + assert(E->isPRValue()); + assert(E->getObjectKind() == OK_Ordinary); + + E->setType(DestType); + + // Rebuild the sub-expression as the pointee (function) type. + DestType = DestType->castAs()->getPointeeType(); + + ExprResult Result = Visit(E->getSubExpr()); + if (!Result.isUsable()) return ExprError(); - public: - CallReturnIncompleteDiagnoser(FunctionDecl *FD, CallExpr *CE) - : FD(FD), CE(CE) { } + E->setSubExpr(Result.get()); + return E; + } else if (E->getCastKind() == CK_LValueToRValue) { + assert(E->isPRValue()); + assert(E->getObjectKind() == OK_Ordinary); - void diagnose(Sema &S, SourceLocation Loc, QualType T) override { - if (!FD) { - S.Diag(Loc, diag::err_call_incomplete_return) - << T << CE->getSourceRange(); - return; - } + assert(isa(E->getType())); - S.Diag(Loc, diag::err_call_function_incomplete_return) - << CE->getSourceRange() << FD << T; - S.Diag(FD->getLocation(), diag::note_entity_declared_at) - << FD->getDeclName(); - } - } Diagnoser(FD, CE); + E->setType(DestType); - if (RequireCompleteType(Loc, ReturnType, Diagnoser)) - return true; + // The sub-expression has to be a lvalue reference, so rebuild it as such. + DestType = S.Context.getLValueReferenceType(DestType); - return false; -} + ExprResult Result = Visit(E->getSubExpr()); + if (!Result.isUsable()) return ExprError(); -// Diagnose the s/=/==/ and s/\|=/!=/ typos. Note that adding parentheses -// will prevent this condition from triggering, which is what we want. -void Sema::DiagnoseAssignmentAsCondition(Expr *E) { - SourceLocation Loc; + E->setSubExpr(Result.get()); + return E; + } else { + llvm_unreachable("Unhandled cast type!"); + } +} - unsigned diagnostic = diag::warn_condition_is_assignment; - bool IsOrAssign = false; +ExprResult RebuildUnknownAnyExpr::resolveDecl(Expr *E, ValueDecl *VD) { + ExprValueKind ValueKind = VK_LValue; + QualType Type = DestType; - if (BinaryOperator *Op = dyn_cast(E)) { - if (Op->getOpcode() != BO_Assign && Op->getOpcode() != BO_OrAssign) - return; + // We know how to make this work for certain kinds of decls: - IsOrAssign = Op->getOpcode() == BO_OrAssign; + // - functions + if (FunctionDecl *FD = dyn_cast(VD)) { + if (const PointerType *Ptr = Type->getAs()) { + DestType = Ptr->getPointeeType(); + ExprResult Result = resolveDecl(E, VD); + if (Result.isInvalid()) return ExprError(); + return S.ImpCastExprToType(Result.get(), Type, CK_FunctionToPointerDecay, + VK_PRValue); + } - // Greylist some idioms by putting them into a warning subcategory. - if (ObjCMessageExpr *ME - = dyn_cast(Op->getRHS()->IgnoreParenCasts())) { - Selector Sel = ME->getSelector(); + if (!Type->isFunctionType()) { + S.Diag(E->getExprLoc(), diag::err_unknown_any_function) + << VD << E->getSourceRange(); + return ExprError(); + } + if (const FunctionProtoType *FT = Type->getAs()) { + // We must match the FunctionDecl's type to the hack introduced in + // RebuildUnknownAnyExpr::VisitCallExpr to vararg functions of unknown + // type. See the lengthy commentary in that routine. + QualType FDT = FD->getType(); + const FunctionType *FnType = FDT->castAs(); + const FunctionProtoType *Proto = dyn_cast_or_null(FnType); + DeclRefExpr *DRE = dyn_cast(E); + if (DRE && Proto && Proto->getParamTypes().empty() && Proto->isVariadic()) { + SourceLocation Loc = FD->getLocation(); + FunctionDecl *NewFD = FunctionDecl::Create( + S.Context, FD->getDeclContext(), Loc, Loc, + FD->getNameInfo().getName(), DestType, FD->getTypeSourceInfo(), + SC_None, S.getCurFPFeatures().isFPConstrained(), + false /*isInlineSpecified*/, FD->hasPrototype(), + /*ConstexprKind*/ ConstexprSpecKind::Unspecified); - // self = [ init...] - if (ObjC().isSelfExpr(Op->getLHS()) && ME->getMethodFamily() == OMF_init) - diagnostic = diag::warn_condition_is_idiomatic_assignment; + if (FD->getQualifier()) + NewFD->setQualifierInfo(FD->getQualifierLoc()); - // = [ nextObject] - else if (Sel.isUnarySelector() && Sel.getNameForSlot(0) == "nextObject") - diagnostic = diag::warn_condition_is_idiomatic_assignment; + SmallVector Params; + for (const auto &AI : FT->param_types()) { + ParmVarDecl *Param = + S.BuildParmVarDeclForTypedef(FD, Loc, AI); + Param->setScopeInfo(0, Params.size()); + Params.push_back(Param); + } + NewFD->setParams(Params); + DRE->setDecl(NewFD); + VD = DRE->getDecl(); + } } - Loc = Op->getOperatorLoc(); - } else if (CXXOperatorCallExpr *Op = dyn_cast(E)) { - if (Op->getOperator() != OO_Equal && Op->getOperator() != OO_PipeEqual) - return; + if (CXXMethodDecl *MD = dyn_cast(FD)) + if (MD->isInstance()) { + ValueKind = VK_PRValue; + Type = S.Context.BoundMemberTy; + } - IsOrAssign = Op->getOperator() == OO_PipeEqual; - Loc = Op->getOperatorLoc(); - } else if (PseudoObjectExpr *POE = dyn_cast(E)) - return DiagnoseAssignmentAsCondition(POE->getSyntacticForm()); - else { - // Not an assignment. - return; - } + // Function references aren't l-values in C. + if (!S.getLangOpts().CPlusPlus) + ValueKind = VK_PRValue; - Diag(Loc, diagnostic) << E->getSourceRange(); + // - variables + } else if (isa(VD)) { + if (const ReferenceType *RefTy = Type->getAs()) { + Type = RefTy->getPointeeType(); + } else if (Type->isFunctionType()) { + S.Diag(E->getExprLoc(), diag::err_unknown_any_var_function_type) + << VD << E->getSourceRange(); + return ExprError(); + } - SourceLocation Open = E->getBeginLoc(); - SourceLocation Close = getLocForEndOfToken(E->getSourceRange().getEnd()); - Diag(Loc, diag::note_condition_assign_silence) - << FixItHint::CreateInsertion(Open, "(") - << FixItHint::CreateInsertion(Close, ")"); + // - nothing else + } else { + S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_decl) + << VD << E->getSourceRange(); + return ExprError(); + } - if (IsOrAssign) - Diag(Loc, diag::note_condition_or_assign_to_comparison) - << FixItHint::CreateReplacement(Loc, "!="); - else - Diag(Loc, diag::note_condition_assign_to_comparison) - << FixItHint::CreateReplacement(Loc, "=="); + // Modifying the declaration like this is friendly to IR-gen but + // also really dangerous. + VD->setType(DestType); + E->setType(Type); + E->setValueKind(ValueKind); + return E; } -void Sema::DiagnoseEqualityWithExtraParens(ParenExpr *ParenE) { - // Don't warn if the parens came from a macro. - SourceLocation parenLoc = ParenE->getBeginLoc(); - if (parenLoc.isInvalid() || parenLoc.isMacroID()) - return; - // Don't warn for dependent expressions. - if (ParenE->isTypeDependent()) - return; +ExprResult Sema::checkUnknownAnyCast(SourceRange TypeRange, QualType CastType, + Expr *CastExpr, CastKind &CastKind, + ExprValueKind &VK, CXXCastPath &Path) { + // The type we're casting to must be either void or complete. + if (!CastType->isVoidType() && + RequireCompleteType(TypeRange.getBegin(), CastType, + diag::err_typecheck_cast_to_incomplete)) + return ExprError(); - Expr *E = ParenE->IgnoreParens(); - if (ParenE->isProducedByFoldExpansion() && ParenE->getSubExpr() == E) - return; + // Rewrite the casted expression from scratch. + ExprResult result = RebuildUnknownAnyExpr(*this, CastType).Visit(CastExpr); + if (!result.isUsable()) return ExprError(); - if (BinaryOperator *opE = dyn_cast(E)) - if (opE->getOpcode() == BO_EQ && - opE->getLHS()->IgnoreParenImpCasts()->isModifiableLvalue(Context) - == Expr::MLV_Valid) { - SourceLocation Loc = opE->getOperatorLoc(); + CastExpr = result.get(); + VK = CastExpr->getValueKind(); + CastKind = CK_NoOp; - Diag(Loc, diag::warn_equality_with_extra_parens) << E->getSourceRange(); - SourceRange ParenERange = ParenE->getSourceRange(); - Diag(Loc, diag::note_equality_comparison_silence) - << FixItHint::CreateRemoval(ParenERange.getBegin()) - << FixItHint::CreateRemoval(ParenERange.getEnd()); - Diag(Loc, diag::note_equality_comparison_to_assign) - << FixItHint::CreateReplacement(Loc, "="); - } + return CastExpr; } -ExprResult Sema::CheckBooleanCondition(SourceLocation Loc, Expr *E, - bool IsConstexpr) { - DiagnoseAssignmentAsCondition(E); - if (ParenExpr *parenE = dyn_cast(E)) - DiagnoseEqualityWithExtraParens(parenE); +ExprResult Sema::forceUnknownAnyToType(Expr *E, QualType ToType) { + return RebuildUnknownAnyExpr(*this, ToType).Visit(E); +} - ExprResult result = CheckPlaceholderExpr(E); - if (result.isInvalid()) return ExprError(); - E = result.get(); +ExprResult Sema::checkUnknownAnyArg(SourceLocation callLoc, + Expr *arg, QualType ¶mType) { + // If the syntactic form of the argument is not an explicit cast of + // any sort, just do default argument promotion. + ExplicitCastExpr *castArg = dyn_cast(arg->IgnoreParens()); + if (!castArg) { + ExprResult result = DefaultArgumentPromotion(arg); + if (result.isInvalid()) return ExprError(); + paramType = result.get()->getType(); + return result; + } - if (!E->isTypeDependent()) { - if (getLangOpts().CPlusPlus) - return CheckCXXBooleanCondition(E, IsConstexpr); // C++ 6.4p4 + // Otherwise, use the type that was written in the explicit cast. + assert(!arg->hasPlaceholderType()); + paramType = castArg->getTypeAsWritten(); - ExprResult ERes = DefaultFunctionArrayLvalueConversion(E); - if (ERes.isInvalid()) - return ExprError(); - E = ERes.get(); + // Copy-initialize a parameter of that type. + InitializedEntity entity = + InitializedEntity::InitializeParameter(Context, paramType, + /*consumed*/ false); + return PerformCopyInitialization(entity, callLoc, arg); +} - QualType T = E->getType(); - if (!T->isScalarType()) { // C99 6.8.4.1p1 - Diag(Loc, diag::err_typecheck_statement_requires_scalar) - << T << E->getSourceRange(); +static ExprResult diagnoseUnknownAnyExpr(Sema &S, Expr *E) { + Expr *orig = E; + unsigned diagID = diag::err_uncasted_use_of_unknown_any; + while (true) { + E = E->IgnoreParenImpCasts(); + if (CallExpr *call = dyn_cast(E)) { + E = call->getCallee(); + diagID = diag::err_uncasted_call_of_unknown_any; + } else { + break; + } + } + + SourceLocation loc; + NamedDecl *d; + if (DeclRefExpr *ref = dyn_cast(E)) { + loc = ref->getLocation(); + d = ref->getDecl(); + } else if (MemberExpr *mem = dyn_cast(E)) { + loc = mem->getMemberLoc(); + d = mem->getMemberDecl(); + } else if (ObjCMessageExpr *msg = dyn_cast(E)) { + diagID = diag::err_uncasted_call_of_unknown_any; + loc = msg->getSelectorStartLoc(); + d = msg->getMethodDecl(); + if (!d) { + S.Diag(loc, diag::err_uncasted_send_to_unknown_any_method) + << static_cast(msg->isClassMessage()) << msg->getSelector() + << orig->getSourceRange(); return ExprError(); } - CheckBoolLikeConversion(E, Loc); + } else { + S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_expr) + << E->getSourceRange(); + return ExprError(); } - return E; -} - -Sema::ConditionResult Sema::ActOnCondition(Scope *S, SourceLocation Loc, - Expr *SubExpr, ConditionKind CK, - bool MissingOK) { - // MissingOK indicates whether having no condition expression is valid - // (for loop) or invalid (e.g. while loop). - if (!SubExpr) - return MissingOK ? ConditionResult() : ConditionError(); - - ExprResult Cond; - switch (CK) { - case ConditionKind::Boolean: - Cond = CheckBooleanCondition(Loc, SubExpr); - break; + S.Diag(loc, diagID) << d << orig->getSourceRange(); - case ConditionKind::ConstexprIf: - Cond = CheckBooleanCondition(Loc, SubExpr, true); - break; + // Never recoverable. + return ExprError(); +} - case ConditionKind::Switch: - Cond = CheckSwitchCondition(Loc, SubExpr); - break; - } - if (Cond.isInvalid()) { - Cond = CreateRecoveryExpr(SubExpr->getBeginLoc(), SubExpr->getEndLoc(), - {SubExpr}, PreferredConditionType(CK)); - if (!Cond.get()) - return ConditionError(); +ExprResult Sema::CheckPlaceholderExpr(Expr *E) { + if (!Context.isDependenceAllowed()) { + // C cannot handle TypoExpr nodes on either side of a binop because it + // doesn't handle dependent types properly, so make sure any TypoExprs have + // been dealt with before checking the operands. + ExprResult Result = CorrectDelayedTyposInExpr(E); + if (!Result.isUsable()) return ExprError(); + E = Result.get(); } - // FIXME: FullExprArg doesn't have an invalid bit, so check nullness instead. - FullExprArg FullExpr = MakeFullExpr(Cond.get(), Loc); - if (!FullExpr.get()) - return ConditionError(); - return ConditionResult(*this, nullptr, FullExpr, - CK == ConditionKind::ConstexprIf); -} + const BuiltinType *placeholderType = E->getType()->getAsPlaceholderType(); + if (!placeholderType) return E; -namespace { - /// A visitor for rebuilding a call to an __unknown_any expression - /// to have an appropriate type. - struct RebuildUnknownAnyFunction - : StmtVisitor { + switch (placeholderType->getKind()) { + case BuiltinType::UnresolvedTemplate: { + auto *ULE = cast(E); + const DeclarationNameInfo &NameInfo = ULE->getNameInfo(); + // There's only one FoundDecl for UnresolvedTemplate type. See + // BuildTemplateIdExpr. + NamedDecl *Temp = *ULE->decls_begin(); + const bool IsTypeAliasTemplateDecl = isa(Temp); - Sema &S; + NestedNameSpecifier *NNS = ULE->getQualifierLoc().getNestedNameSpecifier(); + // FIXME: AssumedTemplate is not very appropriate for error recovery here, + // as it models only the unqualified-id case, where this case can clearly be + // qualified. Thus we can't just qualify an assumed template. + TemplateName TN; + if (auto *TD = dyn_cast(Temp)) + TN = Context.getQualifiedTemplateName(NNS, ULE->hasTemplateKeyword(), + TemplateName(TD)); + else + TN = Context.getAssumedTemplateName(NameInfo.getName()); - RebuildUnknownAnyFunction(Sema &S) : S(S) {} + Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template) + << TN << ULE->getSourceRange() << IsTypeAliasTemplateDecl; + Diag(Temp->getLocation(), diag::note_referenced_type_template) + << IsTypeAliasTemplateDecl; - ExprResult VisitStmt(Stmt *S) { - llvm_unreachable("unexpected statement!"); + TemplateArgumentListInfo TAL(ULE->getLAngleLoc(), ULE->getRAngleLoc()); + bool HasAnyDependentTA = false; + for (const TemplateArgumentLoc &Arg : ULE->template_arguments()) { + HasAnyDependentTA |= Arg.getArgument().isDependent(); + TAL.addArgument(Arg); } - ExprResult VisitExpr(Expr *E) { - S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_call) - << E->getSourceRange(); - return ExprError(); + QualType TST; + { + SFINAETrap Trap(*this); + TST = CheckTemplateIdType(TN, NameInfo.getBeginLoc(), TAL); } + if (TST.isNull()) + TST = Context.getTemplateSpecializationType( + TN, ULE->template_arguments(), /*CanonicalArgs=*/std::nullopt, + HasAnyDependentTA ? Context.DependentTy : Context.IntTy); + QualType ET = + Context.getElaboratedType(ElaboratedTypeKeyword::None, NNS, TST); + return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {}, + ET); + } - /// Rebuild an expression which simply semantically wraps another - /// expression which it shares the type and value kind of. - template ExprResult rebuildSugarExpr(T *E) { - ExprResult SubResult = Visit(E->getSubExpr()); - if (SubResult.isInvalid()) return ExprError(); + // Overloaded expressions. + case BuiltinType::Overload: { + // Try to resolve a single function template specialization. + // This is obligatory. + ExprResult Result = E; + if (ResolveAndFixSingleFunctionTemplateSpecialization(Result, false)) + return Result; - Expr *SubExpr = SubResult.get(); - E->setSubExpr(SubExpr); - E->setType(SubExpr->getType()); - E->setValueKind(SubExpr->getValueKind()); - assert(E->getObjectKind() == OK_Ordinary); - return E; - } + // No guarantees that ResolveAndFixSingleFunctionTemplateSpecialization + // leaves Result unchanged on failure. + Result = E; + if (resolveAndFixAddressOfSingleOverloadCandidate(Result)) + return Result; - ExprResult VisitParenExpr(ParenExpr *E) { - return rebuildSugarExpr(E); - } + // If that failed, try to recover with a call. + tryToRecoverWithCall(Result, PDiag(diag::err_ovl_unresolvable), + /*complain*/ true); + return Result; + } - ExprResult VisitUnaryExtension(UnaryOperator *E) { - return rebuildSugarExpr(E); + // Bound member functions. + case BuiltinType::BoundMember: { + ExprResult result = E; + const Expr *BME = E->IgnoreParens(); + PartialDiagnostic PD = PDiag(diag::err_bound_member_function); + // Try to give a nicer diagnostic if it is a bound member that we recognize. + if (isa(BME)) { + PD = PDiag(diag::err_dtor_expr_without_call) << /*pseudo-destructor*/ 1; + } else if (const auto *ME = dyn_cast(BME)) { + if (ME->getMemberNameInfo().getName().getNameKind() == + DeclarationName::CXXDestructorName) + PD = PDiag(diag::err_dtor_expr_without_call) << /*destructor*/ 0; } + tryToRecoverWithCall(result, PD, + /*complain*/ true); + return result; + } - ExprResult VisitUnaryAddrOf(UnaryOperator *E) { - ExprResult SubResult = Visit(E->getSubExpr()); - if (SubResult.isInvalid()) return ExprError(); + // ARC unbridged casts. + case BuiltinType::ARCUnbridgedCast: { + Expr *realCast = ObjC().stripARCUnbridgedCast(E); + ObjC().diagnoseARCUnbridgedCast(realCast); + return realCast; + } - Expr *SubExpr = SubResult.get(); - E->setSubExpr(SubExpr); - E->setType(S.Context.getPointerType(SubExpr->getType())); - assert(E->isPRValue()); - assert(E->getObjectKind() == OK_Ordinary); - return E; + // Expressions of unknown type. + case BuiltinType::UnknownAny: + return diagnoseUnknownAnyExpr(*this, E); + + // Pseudo-objects. + case BuiltinType::PseudoObject: + return PseudoObject().checkRValue(E); + + case BuiltinType::BuiltinFn: { + // Accept __noop without parens by implicitly converting it to a call expr. + auto *DRE = dyn_cast(E->IgnoreParenImpCasts()); + if (DRE) { + auto *FD = cast(DRE->getDecl()); + unsigned BuiltinID = FD->getBuiltinID(); + if (BuiltinID == Builtin::BI__noop) { + E = ImpCastExprToType(E, Context.getPointerType(FD->getType()), + CK_BuiltinFnToFnPtr) + .get(); + return CallExpr::Create(Context, E, /*Args=*/{}, Context.IntTy, + VK_PRValue, SourceLocation(), + FPOptionsOverride()); + } + + if (Context.BuiltinInfo.isInStdNamespace(BuiltinID)) { + // Any use of these other than a direct call is ill-formed as of C++20, + // because they are not addressable functions. In earlier language + // modes, warn and force an instantiation of the real body. + Diag(E->getBeginLoc(), + getLangOpts().CPlusPlus20 + ? diag::err_use_of_unaddressable_function + : diag::warn_cxx20_compat_use_of_unaddressable_function); + if (FD->isImplicitlyInstantiable()) { + // Require a definition here because a normal attempt at + // instantiation for a builtin will be ignored, and we won't try + // again later. We assume that the definition of the template + // precedes this use. + InstantiateFunctionDefinition(E->getBeginLoc(), FD, + /*Recursive=*/false, + /*DefinitionRequired=*/true, + /*AtEndOfTU=*/false); + } + // Produce a properly-typed reference to the function. + CXXScopeSpec SS; + SS.Adopt(DRE->getQualifierLoc()); + TemplateArgumentListInfo TemplateArgs; + DRE->copyTemplateArgumentsInto(TemplateArgs); + return BuildDeclRefExpr( + FD, FD->getType(), VK_LValue, DRE->getNameInfo(), + DRE->hasQualifier() ? &SS : nullptr, DRE->getFoundDecl(), + DRE->getTemplateKeywordLoc(), + DRE->hasExplicitTemplateArgs() ? &TemplateArgs : nullptr); + } } - ExprResult resolveDecl(Expr *E, ValueDecl *VD) { - if (!isa(VD)) return VisitExpr(E); + Diag(E->getBeginLoc(), diag::err_builtin_fn_use); + return ExprError(); + } - E->setType(VD->getType()); + case BuiltinType::IncompleteMatrixIdx: + Diag(cast(E->IgnoreParens()) + ->getRowIdx() + ->getBeginLoc(), + diag::err_matrix_incomplete_index); + return ExprError(); - assert(E->isPRValue()); - if (S.getLangOpts().CPlusPlus && - !(isa(VD) && - cast(VD)->isInstance())) - E->setValueKind(VK_LValue); + // Expressions of unknown type. + case BuiltinType::ArraySection: + Diag(E->getBeginLoc(), diag::err_array_section_use) + << cast(E)->isOMPArraySection(); + return ExprError(); - return E; - } + // Expressions of unknown type. + case BuiltinType::OMPArrayShaping: + return ExprError(Diag(E->getBeginLoc(), diag::err_omp_array_shaping_use)); - ExprResult VisitMemberExpr(MemberExpr *E) { - return resolveDecl(E, E->getMemberDecl()); - } + case BuiltinType::OMPIterator: + return ExprError(Diag(E->getBeginLoc(), diag::err_omp_iterator_use)); - ExprResult VisitDeclRefExpr(DeclRefExpr *E) { - return resolveDecl(E, E->getDecl()); - } - }; + // Everything else should be impossible. +#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ + case BuiltinType::Id: +#include "clang/Basic/OpenCLImageTypes.def" +#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ + case BuiltinType::Id: +#include "clang/Basic/OpenCLExtensionTypes.def" +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: +#include "clang/Basic/AArch64SVEACLETypes.def" +#define PPC_VECTOR_TYPE(Name, Id, Size) \ + case BuiltinType::Id: +#include "clang/Basic/PPCTypes.def" +#define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id: +#include "clang/Basic/RISCVVTypes.def" +#define WASM_TYPE(Name, Id, SingletonId) case BuiltinType::Id: +#include "clang/Basic/WebAssemblyReferenceTypes.def" +#define AMDGPU_TYPE(Name, Id, SingletonId, Width, Align) case BuiltinType::Id: +#include "clang/Basic/AMDGPUTypes.def" +#define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) case BuiltinType::Id: +#include "clang/Basic/HLSLIntangibleTypes.def" +#define BUILTIN_TYPE(Id, SingletonId) case BuiltinType::Id: +#define PLACEHOLDER_TYPE(Id, SingletonId) +#include "clang/AST/BuiltinTypes.def" + break; + } + + llvm_unreachable("invalid placeholder type!"); } -/// Given a function expression of unknown-any type, try to rebuild it -/// to have a function type. -static ExprResult rebuildUnknownAnyFunction(Sema &S, Expr *FunctionExpr) { - ExprResult Result = RebuildUnknownAnyFunction(S).Visit(FunctionExpr); - if (Result.isInvalid()) return ExprError(); - return S.DefaultFunctionArrayConversion(Result.get()); +bool Sema::CheckCaseExpression(Expr *E) { + if (E->isTypeDependent()) + return true; + if (E->isValueDependent() || E->isIntegerConstantExpr(Context)) + return E->getType()->isIntegralOrEnumerationType(); + return false; } -namespace { - /// A visitor for rebuilding an expression of type __unknown_anytype - /// into one which resolves the type directly on the referring - /// expression. Strict preservation of the original source - /// structure is not a goal. - struct RebuildUnknownAnyExpr - : StmtVisitor { +ExprResult Sema::CreateRecoveryExpr(SourceLocation Begin, SourceLocation End, + ArrayRef SubExprs, QualType T) { + if (!Context.getLangOpts().RecoveryAST) + return ExprError(); - Sema &S; + if (isSFINAEContext()) + return ExprError(); - /// The current destination type. - QualType DestType; + if (T.isNull() || T->isUndeducedType() || + !Context.getLangOpts().RecoveryASTType) + // We don't know the concrete type, fallback to dependent type. + T = Context.DependentTy; - RebuildUnknownAnyExpr(Sema &S, QualType CastType) - : S(S), DestType(CastType) {} + return RecoveryExpr::Create(Context, T, Begin, End, SubExprs); +} - ExprResult VisitStmt(Stmt *S) { - llvm_unreachable("unexpected statement!"); - } +/* TO_UPSTREAM(BoundsSafety) ON*/ +ExprResult Sema::ActOnForgeBidiIndexable(SourceLocation KWLoc, + Expr *Addr, Expr *Size, + SourceLocation RParenLoc) { + BoundsSafetyPointerAttributes Att; + Att.setBidiIndexable(); + QualType ResultType = Context.getPointerType(Context.VoidTy, Att); + return BuildForgePtrExpr(KWLoc, RParenLoc, ResultType, Addr, Size); +} - ExprResult VisitExpr(Expr *E) { - S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_expr) - << E->getSourceRange(); - return ExprError(); - } +ExprResult Sema::ActOnForgeSingle(SourceLocation KWLoc, Expr *Addr, + SourceLocation RParenLoc) { + QualType ResultType; + if (getLangOpts().isBoundsSafetyAttributeOnlyMode()) { + ResultType = Context.getAttributedType(attr::PtrSingle, Context.VoidPtrTy, + Context.VoidPtrTy); + } else + ResultType = Context.getPointerType(Context.VoidTy, + BoundsSafetyPointerAttributes::single()); + return BuildForgePtrExpr(KWLoc, RParenLoc, ResultType, Addr); +} - ExprResult VisitCallExpr(CallExpr *E); - ExprResult VisitObjCMessageExpr(ObjCMessageExpr *E); +ExprResult Sema::ActOnForgeTerminatedBy(SourceLocation KWLoc, Expr *Addr, + Expr *Term, SourceLocation RParenLoc) { + BoundsSafetyPointerAttributes Att; + if (!getLangOpts().isBoundsSafetyAttributeOnlyMode()) + Att.setSingle(); + QualType ResultType = Context.getPointerType(Context.VoidTy, Att); + ResultType = Context.getValueTerminatedType(ResultType, Term); + return BuildForgePtrExpr(KWLoc, RParenLoc, ResultType, Addr, /*Size*/ nullptr, + Term); +} - /// Rebuild an expression which simply semantically wraps another - /// expression which it shares the type and value kind of. - template ExprResult rebuildSugarExpr(T *E) { - ExprResult SubResult = Visit(E->getSubExpr()); - if (SubResult.isInvalid()) return ExprError(); - Expr *SubExpr = SubResult.get(); - E->setSubExpr(SubExpr); - E->setType(SubExpr->getType()); - E->setValueKind(SubExpr->getValueKind()); - assert(E->getObjectKind() == OK_Ordinary); - return E; +ExprResult Sema::ActOnBoundsSafetyCall(ExprResult Call) { + CallExpr *CallE = dyn_cast_or_null(Call.get()); + if (!CallE || CallE->containsErrors()) + return Call; + + // Bail out early if there's nothing to do. (There is something to do if + // there's a dynamic bound pointer type in this function prototype, or if + // it's annotated with `alloc_size`, in which case we can derive the + // equivalent.) + QualType FT = CallE->getCallee()->getType(); + if (auto PointerTy = FT->getAs()) + FT = PointerTy->getPointeeType(); + if (!ContainsBoundsAttributedType::check(FT)) { + auto *FDecl = CallE->getDirectCallee(); + if (!FDecl || !FDecl->getAttr()) + return CallE; + } + + Expr *ResultExpr = CallE; + SmallVector OVEs; + + llvm::DenseMap> + DependentValues; + auto AddDependentParams = [&](const BoundsAttributedType *DBPT) { + for (const auto &DI : DBPT->dependent_decls()) { + if (const auto *PVD = dyn_cast(DI.getDecl())) { + unsigned Index = PVD->getFunctionScopeIndex(); + assert(Index < OVEs.size()); + Expr *Arg = OVEs[Index]; + DependentValues[PVD] = {Arg, /*Level=*/0}; + } } + }; - ExprResult VisitParenExpr(ParenExpr *E) { - return rebuildSugarExpr(E); + ReplaceDeclRefWithRHS Transform(*this, DependentValues); + + if (const FunctionProtoType *FPT = FT->getAs()) { + // Move function arguments into opaque value expressions so that we can + // refer to them without funny reordered/duplicated side effects business. + // Since there's an implicit cast to __single bounds wherever we use a + // dynamic bound pointer type, we pull the inner value of the -fbounds-safety + // pointer cast into the OVE only (and replace the -fbounds-safety pointer cast + // operand with the new OVE). + for (unsigned I = 0; I < CallE->getNumArgs(); ++I) { + Expr *Arg = CallE->getArg(I); + ImplicitCastExpr *IC = nullptr; + do { + auto *Cast = dyn_cast(Arg); + if (Cast && Cast->getCastKind() == CK_BoundsSafetyPointerCast) { + IC = Cast; + Arg = IC->getSubExpr(); + } else { + break; + } + } while (true); + auto *OVE = OpaqueValueExpr::EnsureWrapped(Context, Arg, OVEs); + if (IC) + IC->setSubExpr(OVE); + else + CallE->setArg(I, OVE); } - ExprResult VisitUnaryExtension(UnaryOperator *E) { - return rebuildSugarExpr(E); + // Do we need to make any adjustments to arguments? + auto ParamTypes = FPT->getParamTypes(); + // (there are more arguments than parameters in the case of variadic + // functions, however variadic arguments are never passed as dynamic-bound + // pointers.) + assert(ParamTypes.size() <= OVEs.size()); + + for (QualType ParamType : ParamTypes) { + if (const auto *DBPT = ParamType->getAs()) + AddDependentParams(DBPT); } + if (const auto *DBPT = + ResultExpr->getType()->getAs()) + AddDependentParams(DBPT); - ExprResult VisitUnaryAddrOf(UnaryOperator *E) { - const PointerType *Ptr = DestType->getAs(); - if (!Ptr) { - S.Diag(E->getOperatorLoc(), diag::err_unknown_any_addrof) - << E->getSourceRange(); - return ExprError(); + for (unsigned I = 0; I < ParamTypes.size(); ++I) { + if (allowBoundsUnsafeFunctionArg(CallE, I)) { + assert( + SourceMgr.isInSystemHeader(OVEs[I]->getSourceExpr()->getExprLoc())); + continue; } - - if (isa(E->getSubExpr())) { - S.Diag(E->getOperatorLoc(), diag::err_unknown_any_addrof_call) - << E->getSourceRange(); - return ExprError(); + QualType ParamType = ParamTypes[I]; + BoundsCheckBuilder Builder(OVEs); + if (auto *DCPT = ParamType->getAs()) { + ExprResult CountExpr = Transform.TransformExpr(DCPT->getCountExpr()); + if (!CountExpr.get()) + return ExprError(); + Builder.setAccessCount(CountExpr.get(), DCPT->isCountInBytes(), + DCPT->isOrNull()); + } else if (auto DRPT = ParamType->getAs()) { + // The end pointer itself may be a dynamic range pointer type, but it + // will not have an end pointer, just a start pointer. The start pointer + // will generate the entire breadth of required checks. + if (auto *EndPtr = DRPT->getEndPointer()) { + ExprResult Upper = Transform.TransformExpr(EndPtr); + if (!Upper.get()) + return ExprError(); + Builder.setAccessLowerBound(OVEs[I]); + Builder.setAccessUpperBound(Upper.get()); + } else { + continue; + } + } else { + continue; } - - assert(E->isPRValue()); - assert(E->getObjectKind() == OK_Ordinary); - E->setType(DestType); - - // Build the sub-expression as if it were an object of the pointee type. - DestType = Ptr->getPointeeType(); - ExprResult SubResult = Visit(E->getSubExpr()); - if (SubResult.isInvalid()) return ExprError(); - E->setSubExpr(SubResult.get()); - return E; + Builder.setWidePointer(OVEs[I]); + ExprResult R = Builder.Build(*this, ResultExpr); + if (!(ResultExpr = R.get())) + return ExprError(); } + } - ExprResult VisitImplicitCastExpr(ImplicitCastExpr *E); - - ExprResult resolveDecl(Expr *E, ValueDecl *VD); + // Do we need to make any adjustments to the return value? This needs to also + // account for attributes on the function which specify that the function + // allocated memory (in addition to common dynamic bound pointer types), and + // for flexible array member pointers. + QualType ResultTy = ResultExpr->getType(); + if (auto *DCPT = ResultTy->getAs()) { + ExprResult ReturnCount = Transform.TransformExpr(DCPT->getCountExpr()); + if (!ReturnCount.get()) + return ExprError(); + ReturnCount = DefaultLvalueConversion(ReturnCount.get()); + if (!ReturnCount.get()) + return ExprError(); + auto *PtrOVE = OpaqueValueExpr::EnsureWrapped( + Context, ResultExpr, OVEs); + auto *CountOVE = OpaqueValueExpr::EnsureWrapped( + Context, ReturnCount.get(), OVEs); + ExprResult Promoted = PromoteBoundsSafetyPointerWithCount( + *this, PtrOVE, CountOVE, DCPT->isCountInBytes(), DCPT->isOrNull()); + if (!(ResultExpr = Promoted.get())) + return ExprError(); + } else if (auto *DRPT = ResultTy->getAs()) { + ExprResult Upper = Transform.TransformExpr(DRPT->getEndPointer()); + if (!Upper.get()) + return ExprError(); + auto FA = BoundsSafetyPointerAttributes::bidiIndexable(); + QualType FPtrTy = Context.getPointerType(DRPT->getPointeeType(), FA); + ExprResult Promoted = BoundsSafetyPointerPromotionExpr::Create( + Context, FPtrTy, ResultExpr, Upper.get()); + if (!(ResultExpr = Promoted.get())) + return ExprError(); + } else if (auto *RD = getImmediateDeclForFlexibleArrayPromotion(ResultTy)) { + ExprResult Promoted = PromoteBoundsSafetyPointerToFlexibleArrayMember( + *this, RD, ResultExpr); + if (!(ResultExpr = Promoted.get())) + return ExprError(); + } else if (auto *FDecl = CallE->getDirectCallee()) { + if (auto Att = FDecl->getAttr()) { + if (Att->getElemSizeParam().isValid()) { + Expr *SizeExpr = OVEs[Att->getElemSizeParam().getASTIndex()]; + if (Att->getNumElemsParam().isValid()) { + Expr *CountExpr = OVEs[Att->getNumElemsParam().getASTIndex()]; + ExprResult Product = CreateBuiltinBinOp( + CallE->getBeginLoc(), BO_Mul, SizeExpr, CountExpr); + if (!(SizeExpr = Product.get())) + return ExprError(); + } - ExprResult VisitMemberExpr(MemberExpr *E) { - return resolveDecl(E, E->getMemberDecl()); - } + ExprResult SizeRValue = DefaultLvalueConversion(SizeExpr); + if (!SizeRValue.get()) + return ExprError(); - ExprResult VisitDeclRefExpr(DeclRefExpr *E) { - return resolveDecl(E, E->getDecl()); + auto *PtrOVE = OpaqueValueExpr::EnsureWrapped( + Context, ResultExpr, OVEs); + auto *SizeOVE = OpaqueValueExpr::EnsureWrapped( + Context, SizeRValue.get(), OVEs); + ExprResult Promoted = PromoteBoundsSafetyPointerWithCount( + *this, PtrOVE, SizeOVE, true, true); + if (!(ResultExpr = Promoted.get())) + return ExprError(); + } } - }; -} - -/// Rebuilds a call expression which yielded __unknown_anytype. -ExprResult RebuildUnknownAnyExpr::VisitCallExpr(CallExpr *E) { - Expr *CalleeExpr = E->getCallee(); - - enum FnKind { - FK_MemberFunction, - FK_FunctionPointer, - FK_BlockPointer - }; - - FnKind Kind; - QualType CalleeType = CalleeExpr->getType(); - if (CalleeType == S.Context.BoundMemberTy) { - assert(isa(E) || isa(E)); - Kind = FK_MemberFunction; - CalleeType = Expr::findBoundMemberType(CalleeExpr); - } else if (const PointerType *Ptr = CalleeType->getAs()) { - CalleeType = Ptr->getPointeeType(); - Kind = FK_FunctionPointer; - } else { - CalleeType = CalleeType->castAs()->getPointeeType(); - Kind = FK_BlockPointer; } - const FunctionType *FnType = CalleeType->castAs(); - - // Verify that this is a legal result type of a function. - if ((DestType->isArrayType() && !S.getLangOpts().allowArrayReturnTypes()) || - DestType->isFunctionType()) { - unsigned diagID = diag::err_func_returning_array_function; - if (Kind == FK_BlockPointer) - diagID = diag::err_block_returning_array_function; - S.Diag(E->getExprLoc(), diagID) - << DestType->isFunctionType() << DestType; - return ExprError(); + if (!OVEs.empty()) { + // Put opaque value expressions in materialize expressions and return that. + ResultExpr = MaterializeSequenceExpr::Create(Context, ResultExpr, OVEs); + ResultExpr = MaterializeSequenceExpr::Create(Context, ResultExpr, OVEs, true); } + return ResultExpr; +} - // Otherwise, go ahead and set DestType as the call's result. - E->setType(DestType.getNonLValueExprType(S.Context)); - E->setValueKind(Expr::getValueKindForType(DestType)); - assert(E->getObjectKind() == OK_Ordinary); - - // Rebuild the function type, replacing the result type with DestType. - const FunctionProtoType *Proto = dyn_cast(FnType); - if (Proto) { - // __unknown_anytype(...) is a special case used by the debugger when - // it has no idea what a function's signature is. - // - // We want to build this call essentially under the K&R - // unprototyped rules, but making a FunctionNoProtoType in C++ - // would foul up all sorts of assumptions. However, we cannot - // simply pass all arguments as variadic arguments, nor can we - // portably just call the function under a non-variadic type; see - // the comment on IR-gen's TargetInfo::isNoProtoCallVariadic. - // However, it turns out that in practice it is generally safe to - // call a function declared as "A foo(B,C,D);" under the prototype - // "A foo(B,C,D,...);". The only known exception is with the - // Windows ABI, where any variadic function is implicitly cdecl - // regardless of its normal CC. Therefore we change the parameter - // types to match the types of the arguments. - // - // This is a hack, but it is far superior to moving the - // corresponding target-specific code from IR-gen to Sema/AST. +ExprResult Sema::BuildForgePtrExpr(SourceLocation KWLoc, + SourceLocation RParenLoc, + QualType ResultType, Expr *Addr, Expr *Size, + Expr *Terminator) { + BoundsSafetyPointerAttributes Att = + ResultType->getAs()->getPointerAttributes(); + unsigned IsForgeTerminated = ResultType->isValueTerminatedType(); + unsigned IsForgeSingle = !Att.hasUpperBound() && !IsForgeTerminated; + // Ensure that there is a size if we're forging a bidi pointer, and that + // there isn't one otherwise. In the same way there should be a terminator + // value iff the type is __terminated_by. + assert(IsForgeTerminated == (Terminator != nullptr)); + assert(IsForgeSingle == ((Size == nullptr) && !IsForgeTerminated)); - ArrayRef ParamTypes = Proto->getParamTypes(); - SmallVector ArgTypes; - if (ParamTypes.empty() && Proto->isVariadic()) { // the special case - ArgTypes.reserve(E->getNumArgs()); - for (unsigned i = 0, e = E->getNumArgs(); i != e; ++i) { - ArgTypes.push_back(S.Context.getReferenceQualifiedType(E->getArg(i))); - } - ParamTypes = ArgTypes; + ExprResult RvalueAddr = + DefaultFunctionArrayLvalueConversion(Addr, true, false); + if (RvalueAddr.isInvalid()) + return ExprError(); + Addr = RvalueAddr.get(); + + if (!Addr->isTypeDependent()) { + unsigned ForgeType = 0; // ForgeBidi + if (IsForgeSingle) + ForgeType = 1; + else if (IsForgeTerminated) + ForgeType = 2; + + if (Addr->getType()->isIntegralType(Context)) { + Expr::EvalResult EvalAddr; + if (Addr->EvaluateAsInt(EvalAddr, Context, Expr::SE_AllowSideEffects) && + EvalAddr.Val.getInt() < 0) { + Diag(Addr->getBeginLoc(), diag::err_bounds_safety_forge_negative) + << 0 << ForgeType; + return ExprError(); + } + } else if (!Addr->getType()->isPointerType()) { + Diag(Addr->getBeginLoc(), diag::err_bounds_safety_forge_addr_type) + << ForgeType; + return ExprError(); } - DestType = S.Context.getFunctionType(DestType, ParamTypes, - Proto->getExtProtoInfo()); - } else { - DestType = S.Context.getFunctionNoProtoType(DestType, - FnType->getExtInfo()); } - // Rebuild the appropriate pointer-to-function type. - switch (Kind) { - case FK_MemberFunction: - // Nothing to do. - break; + ExprResult RvalueSize = ExprEmpty(); + if (Size) { + RvalueSize = DefaultLvalueConversion(Size); + if (RvalueSize.isInvalid()) + return ExprError(); + + if (!RvalueSize.get()->getType()->isIntegralType(Context)) { + Diag(Size->getBeginLoc(), diag::err_bounds_safety_forge_bidi_size_type); + return ExprError(); + } - case FK_FunctionPointer: - DestType = S.Context.getPointerType(DestType); - break; + // The size value is treated as signed in CodeGen regardless of type. By casting + // values to a wider type if possible we avoid the issue of the upper half of the range + // of unsigned types being interpreted as negative values. + RvalueSize = ImpCastExprToType(RvalueSize.get(), Context.getSizeType(), clang::CK_IntegralCast); + if (RvalueSize.isInvalid()) + return ExprError(); - case FK_BlockPointer: - DestType = S.Context.getBlockPointerType(DestType); - break; + Expr::EvalResult EvalSize; + if (RvalueSize.get()->EvaluateAsInt(EvalSize, Context, + Expr::SE_AllowSideEffects) && + EvalSize.Val.getInt().APInt::isNegative()) { + // An unsigned value that is bigger than SIZE_MAX/2 ultimately + // becomes a negative integer in CodeGen. Thus, we prevent it as + // a negative size. + Diag(Size->getBeginLoc(), diag::err_bounds_safety_forge_negative) + << 1 << IsForgeSingle; + return ExprError(); + } } - // Finally, we can recurse. - ExprResult CalleeResult = Visit(CalleeExpr); - if (!CalleeResult.isUsable()) return ExprError(); - E->setCallee(CalleeResult.get()); + ExprResult RvalueTerm = ExprEmpty(); + if (Terminator) { + RvalueTerm = DefaultLvalueConversion(Terminator); + if (RvalueTerm.isInvalid()) + return ExprError(); + } - // Bind a temporary if necessary. - return S.MaybeBindToTemporary(E); -} + ExprValueKind VK = Expr::getValueKindForType(ResultType); + return new (Context) + ForgePtrExpr(ResultType, VK, RvalueAddr.get(), RvalueSize.get(), + RvalueTerm.get(), KWLoc, RParenLoc); +} + +static ExprResult makeBoundExpr(Sema& S, Expr *SubExpr, + GetBoundExpr::BoundKind BK, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc, + bool RawPointer) { + // make sure the expression is an rvalue + ExprResult RValue = S.DefaultFunctionArrayLvalueConversion(SubExpr); + if (RValue.isInvalid()) + return ExprError(); + SubExpr = RValue.get(); -ExprResult RebuildUnknownAnyExpr::VisitObjCMessageExpr(ObjCMessageExpr *E) { - // Verify that this is a legal result type of a call. - if (DestType->isArrayType() || DestType->isFunctionType()) { - S.Diag(E->getExprLoc(), diag::err_func_returning_array_function) - << DestType->isFunctionType() << DestType; + // result type is __bidi_indexable + QualType T = SubExpr->getType(); + if (T->isDependentType()) return ExprError(); + + int Reason = -1; + const PointerType *PT = T->getAs(); + if (!PT) { + Reason = 0; + } else if (PT->getPointerAttributes().isUnsafeOrUnspecified()) { + Reason = 1; } - // Rewrite the method result type if available. - if (ObjCMethodDecl *Method = E->getMethodDecl()) { - assert(Method->getReturnType() == S.Context.UnknownAnyTy); - Method->setReturnType(DestType); + if (Reason >= 0) { + S.Diag(BuiltinLoc, diag::err_bounds_safety_no_bounds) << BK << T << Reason; + return ExprError(); } - // Change the type of the message. - E->setType(DestType.getNonReferenceType()); - E->setValueKind(Expr::getValueKindForType(DestType)); + auto FA = BoundsSafetyPointerAttributes::bidiIndexable(); + QualType RT = S.Context.getPointerType(PT->getPointeeType(), FA); - return S.MaybeBindToTemporary(E); + // must implicitly cast original to bidi_indexable if needed + if (PT->getPointerAttributes() != FA) { + if (!PT->isSafePointerType()) + return ExprError(); + ExprResult ImpCast = S.ImpCastExprToType( + SubExpr, RT, CK_BoundsSafetyPointerCast); + if (ImpCast.isInvalid()) + return ExprError(); + SubExpr = ImpCast.get(); + } + + auto &Ctx = S.Context; + QualType DstTy = RawPointer ? Ctx.getPointerType(PT->getPointeeType()) : RT; + return new (Ctx) GetBoundExpr(BuiltinLoc, RParenLoc, SubExpr, BK, DstTy); } -ExprResult RebuildUnknownAnyExpr::VisitImplicitCastExpr(ImplicitCastExpr *E) { - // The only case we should ever see here is a function-to-pointer decay. - if (E->getCastKind() == CK_FunctionToPointerDecay) { - assert(E->isPRValue()); - assert(E->getObjectKind() == OK_Ordinary); +ExprResult Sema::BuildLowerBoundExpr(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc, bool RawPointer) { + return makeBoundExpr(*this, SubExpr, GetBoundExpr::BK_Lower, BuiltinLoc, + RParenLoc, RawPointer); +} - E->setType(DestType); +ExprResult Sema::ActOnGetLowerBound(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc) { + return BuildLowerBoundExpr(SubExpr, BuiltinLoc, RParenLoc); +} - // Rebuild the sub-expression as the pointee (function) type. - DestType = DestType->castAs()->getPointeeType(); +ExprResult Sema::BuildUpperBoundExpr(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc, bool RawPointer) { + return makeBoundExpr(*this, SubExpr, GetBoundExpr::BK_Upper, BuiltinLoc, + RParenLoc, RawPointer); +} - ExprResult Result = Visit(E->getSubExpr()); - if (!Result.isUsable()) return ExprError(); +ExprResult Sema::ActOnGetUpperBound(Expr *SubExpr, SourceLocation BuiltinLoc, + SourceLocation RParenLoc) { + return BuildUpperBoundExpr(SubExpr, BuiltinLoc, RParenLoc); +} - E->setSubExpr(Result.get()); - return E; - } else if (E->getCastKind() == CK_LValueToRValue) { - assert(E->isPRValue()); - assert(E->getObjectKind() == OK_Ordinary); +ExprResult Sema::BuildPredefinedBoundsCheckExpr(Expr *SubExpr, + BoundsCheckKind Kind, + ArrayRef CheckArgs) { + return PredefinedBoundsCheckExpr::Create(Context, SubExpr, Kind, CheckArgs); +} - assert(isa(E->getType())); +ExprResult Sema::BuildBoundsCheckExpr(Expr *SubExpr, Expr *Cond, + ArrayRef CommonExprs) { + // FIXME: Bounds expressions would better have implicit casts similar to + // what we would get from Sema::CheckCompareOperands(). This, however, + // would insert implicit casts of wide pointer to raw pointer. This might not + // be matching what GetBoundsExpr CodeGen currently expects. + return BoundsCheckExpr::Create(Context, SubExpr, Cond, CommonExprs); +} - E->setType(DestType); +ExprResult Sema::BuildMaterializeSequenceExpr(Expr *WrappedExpr, + ArrayRef Values) { + return MaterializeSequenceExpr::Create(Context, WrappedExpr, Values); +} - // The sub-expression has to be a lvalue reference, so rebuild it as such. - DestType = S.Context.getLValueReferenceType(DestType); +ExprResult BoundsCheckBuilder::BuildImplicitPointerArith(Sema &S, + BinaryOperatorKind Opc, + Expr *LHS, Expr *RHS) { + QualType PointerTy = LHS->getType(); + assert(Opc == BO_Add || Opc == BO_Sub); + assert(PointerTy->isPointerType() && + RHS->getType()->isIntegralOrEnumerationType()); - ExprResult Result = Visit(E->getSubExpr()); - if (!Result.isUsable()) return ExprError(); + Expr *Pointer = LHS; - E->setSubExpr(Result.get()); - return E; - } else { - llvm_unreachable("Unhandled cast type!"); - } -} + if (PointerTy->isSinglePointerType() && !PointerTy->isBoundsAttributedType()) { + BoundsSafetyPointerAttributes UnsafeAttr; + UnsafeAttr.setUnsafeIndexable(); + QualType UnsafePointerTy = S.Context.getBoundsSafetyPointerType(PointerTy, UnsafeAttr); + ExprResult CastResult = S.BuildCStyleCastExpr(SourceLocation(), S.Context.getTrivialTypeSourceInfo(UnsafePointerTy), SourceLocation(), Pointer); + if (CastResult.isInvalid()) + return ExprError(); -ExprResult RebuildUnknownAnyExpr::resolveDecl(Expr *E, ValueDecl *VD) { - ExprValueKind ValueKind = VK_LValue; - QualType Type = DestType; + Pointer = CastResult.get(); + } - // We know how to make this work for certain kinds of decls: + return S.CreateBuiltinBinOp(SourceLocation(), Opc, Pointer, RHS); +} - // - functions - if (FunctionDecl *FD = dyn_cast(VD)) { - if (const PointerType *Ptr = Type->getAs()) { - DestType = Ptr->getPointeeType(); - ExprResult Result = resolveDecl(E, VD); - if (Result.isInvalid()) return ExprError(); - return S.ImpCastExprToType(Result.get(), Type, CK_FunctionToPointerDecay, - VK_PRValue); - } +OpaqueValueExpr *BoundsCheckBuilder::OpaqueWrap(Sema &S, Expr *E) { + return OpaqueValueExpr::EnsureWrapped(S.Context, E, OVEs); +} - if (!Type->isFunctionType()) { - S.Diag(E->getExprLoc(), diag::err_unknown_any_function) - << VD << E->getSourceRange(); - return ExprError(); - } - if (const FunctionProtoType *FT = Type->getAs()) { - // We must match the FunctionDecl's type to the hack introduced in - // RebuildUnknownAnyExpr::VisitCallExpr to vararg functions of unknown - // type. See the lengthy commentary in that routine. - QualType FDT = FD->getType(); - const FunctionType *FnType = FDT->castAs(); - const FunctionProtoType *Proto = dyn_cast_or_null(FnType); - DeclRefExpr *DRE = dyn_cast(E); - if (DRE && Proto && Proto->getParamTypes().empty() && Proto->isVariadic()) { - SourceLocation Loc = FD->getLocation(); - FunctionDecl *NewFD = FunctionDecl::Create( - S.Context, FD->getDeclContext(), Loc, Loc, - FD->getNameInfo().getName(), DestType, FD->getTypeSourceInfo(), - SC_None, S.getCurFPFeatures().isFPConstrained(), - false /*isInlineSpecified*/, FD->hasPrototype(), - /*ConstexprKind*/ ConstexprSpecKind::Unspecified); +bool BoundsCheckBuilder::BuildIndexBounds(Sema &S, Expr *Min, Expr *Max, + llvm::SmallVectorImpl &R) { + if (isCountInBytes()) { + ExprResult Lower = + CastToCharPointer(S, Min, BoundsSafetyPointerAttributes::unspecified()); + if (!(Min = Lower.get())) + return false; + ExprResult Upper = + CastToCharPointer(S, Max, BoundsSafetyPointerAttributes::unspecified()); + if (!(Max = Upper.get())) + return false; + } + ExprResult Count = S.CreateBuiltinBinOp(Max->getBeginLoc(), BO_Sub, Max, Min); + if (!Count.get()) + return false; - if (FD->getQualifier()) - NewFD->setQualifierInfo(FD->getQualifierLoc()); + bool IsSigned = false; - SmallVector Params; - for (const auto &AI : FT->param_types()) { - ParmVarDecl *Param = - S.BuildParmVarDeclForTypedef(FD, Loc, AI); - Param->setScopeInfo(0, Params.size()); - Params.push_back(Param); - } - NewFD->setParams(Params); - DRE->setDecl(NewFD); - VD = DRE->getDecl(); - } - } + Expr *AccessCount; + QualType SizeT = S.Context.getSizeType(); + if (auto *Idx = getAccessCount()) { + AccessCount = Idx; + IsSigned |= AccessCount->getType()->isSignedIntegerOrEnumerationType(); + } else { + Expr *One = S.ActOnIntegerConstant(Min->getBeginLoc(), 1).get(); + ExprResult R = S.ImpCastExprToType(One, SizeT, CK_IntegralCast); + if (!(AccessCount = R.get())) + return false; + } - if (CXXMethodDecl *MD = dyn_cast(FD)) - if (MD->isInstance()) { - ValueKind = VK_PRValue; - Type = S.Context.BoundMemberTy; + if (auto *StartIndex = getAccessStartIndex()) { + Expr *OpaqueStart = OpaqueWrap(S, StartIndex); + R.push_back(OpaqueStart); + IsSigned |= OpaqueStart->getType()->isSignedIntegerOrEnumerationType(); + ExprResult CountTotal = S.CreateBuiltinBinOp( + OpaqueStart->getBeginLoc(), BO_Add, OpaqueStart, AccessCount); + if (!(AccessCount = CountTotal.get())) + return false; + } + R.push_back(AccessCount); + + if (IsSigned) { + // If any value in R is signed, cast all of them to signed values and + // add a check for zero at first. + for (size_t I = 0; I < R.size(); ++I) { + Expr *&Elem = R[I]; + QualType Ty = Elem->getType(); + if (!Ty->isSignedIntegerOrEnumerationType()) { + QualType SignedType = S.Context.getCorrespondingSignedType(Ty); + ExprResult R = S.ImpCastExprToType(Elem, SignedType, CK_IntegralCast); + if (!(Elem = R.get())) + return false; } - - // Function references aren't l-values in C. - if (!S.getLangOpts().CPlusPlus) - ValueKind = VK_PRValue; - - // - variables - } else if (isa(VD)) { - if (const ReferenceType *RefTy = Type->getAs()) { - Type = RefTy->getPointeeType(); - } else if (Type->isFunctionType()) { - S.Diag(E->getExprLoc(), diag::err_unknown_any_var_function_type) - << VD << E->getSourceRange(); - return ExprError(); } - // - nothing else + Expr *Zero = S.ActOnIntegerConstant(Min->getBeginLoc(), 0).get(); + R.insert(R.begin(), Zero); + R.push_back(Count.get()); } else { - S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_decl) - << VD << E->getSourceRange(); - return ExprError(); + auto SizeTCount = S.ImpCastExprToType(Count.get(), SizeT, CK_IntegralCast); + if (Expr *C = SizeTCount.get()) + R.push_back(C); + else + return false; } - - // Modifying the declaration like this is friendly to IR-gen but - // also really dangerous. - VD->setType(DestType); - E->setType(Type); - E->setValueKind(ValueKind); - return E; + return true; } -ExprResult Sema::checkUnknownAnyCast(SourceRange TypeRange, QualType CastType, - Expr *CastExpr, CastKind &CastKind, - ExprValueKind &VK, CXXCastPath &Path) { - // The type we're casting to must be either void or complete. - if (!CastType->isVoidType() && - RequireCompleteType(TypeRange.getBegin(), CastType, - diag::err_typecheck_cast_to_incomplete)) - return ExprError(); - - // Rewrite the casted expression from scratch. - ExprResult result = RebuildUnknownAnyExpr(*this, CastType).Visit(CastExpr); - if (!result.isUsable()) return ExprError(); +bool BoundsCheckBuilder::BuildPtrBounds(Sema &S, Expr *Min, Expr *Max, + llvm::SmallVectorImpl &R) { + // second, expand the accessed lower bound + Expr *TestedLowerBound; + if (auto *Lower = getAccessLowerBound()) { + TestedLowerBound = Lower; + } else { + Min = OpaqueWrap(S, Min); + Expr *LowerBound = Min; + if (auto *StartIndex = getAccessStartIndex()) { + ExprResult TLBResult = BuildImplicitPointerArith( + S, BO_Add, LowerBound, StartIndex); + if (TLBResult.isInvalid()) + return false; - CastExpr = result.get(); - VK = CastExpr->getValueKind(); - CastKind = CK_NoOp; + TestedLowerBound = TLBResult.get(); + } else { + Min = OpaqueWrap(S, Min); + TestedLowerBound = Min; + } + } - return CastExpr; -} + // next, expand the accessed upper bound + Expr *TestedUpperBound; + if (auto *Upper = getAccessUpperBound()) { + TestedUpperBound = Upper; + } else { + TestedLowerBound = OpaqueWrap(S, TestedLowerBound); + ExprResult TLB = TestedLowerBound; + ExprResult TUB; + if (auto *Count = getAccessCount()) { + // If OrigPtrTy is set, it means that we must do pointer arithmetic over a + // character type instead of the original type (and that the result must + // be cast back t OrigPtrTy). + QualType OrigPtrTy; + if (isCountInBytes()) { + QualType PtrTy = TestedLowerBound->getType(); + QualType Pointee = PtrTy->getPointeeType(); + if (S.Context.getTypeSizeInChars(Pointee).getQuantity() != 1) { + OrigPtrTy = PtrTy; + } + } -ExprResult Sema::forceUnknownAnyToType(Expr *E, QualType ToType) { - return RebuildUnknownAnyExpr(*this, ToType).Visit(E); -} + if (!OrigPtrTy.isNull()) { + TLB = CastToCharPointer(S, TLB.get()); + if (!TLB.get()) + return false; + } + TUB = BuildImplicitPointerArith(S, BO_Add, TLB.get(), Count); + if (!OrigPtrTy.isNull()) { + SourceLocation Loc = TUB.get()->getBeginLoc(); + TUB = S.BuildCStyleCastExpr( + Loc, S.Context.getTrivialTypeSourceInfo(OrigPtrTy), Loc, TUB.get()); + } + } else { + // Assume count is 1. + Expr *One = S.ActOnIntegerConstant(TLB.get()->getBeginLoc(), 1).get(); + TUB = BuildImplicitPointerArith(S, BO_Add, TLB.get(), One); + } + if (TUB.isInvalid()) + return false; -ExprResult Sema::checkUnknownAnyArg(SourceLocation callLoc, - Expr *arg, QualType ¶mType) { - // If the syntactic form of the argument is not an explicit cast of - // any sort, just do default argument promotion. - ExplicitCastExpr *castArg = dyn_cast(arg->IgnoreParens()); - if (!castArg) { - ExprResult result = DefaultArgumentPromotion(arg); - if (result.isInvalid()) return ExprError(); - paramType = result.get()->getType(); - return result; + TestedUpperBound = TUB.get(); } - // Otherwise, use the type that was written in the explicit cast. - assert(!arg->hasPlaceholderType()); - paramType = castArg->getTypeAsWritten(); - - // Copy-initialize a parameter of that type. - InitializedEntity entity = - InitializedEntity::InitializeParameter(Context, paramType, - /*consumed*/ false); - return PerformCopyInitialization(entity, callLoc, arg); + R.push_back(Min); + if (Min != TestedLowerBound) + R.push_back(TestedLowerBound); + if (TestedLowerBound != TestedUpperBound) + R.push_back(TestedUpperBound); + if (TestedUpperBound != Max) + R.push_back(Max); + return true; } -static ExprResult diagnoseUnknownAnyExpr(Sema &S, Expr *E) { - Expr *orig = E; - unsigned diagID = diag::err_uncasted_use_of_unknown_any; - while (true) { - E = E->IgnoreParenImpCasts(); - if (CallExpr *call = dyn_cast(E)) { - E = call->getCallee(); - diagID = diag::err_uncasted_call_of_unknown_any; +ExprResult BoundsCheckBuilder::Build(Sema &S, Expr *GuardedValue) { + // first, expand base pointer + Expr *BasePtr, *UpperBound, *LowerBound; + if (auto *Wide = getWidePointer()) { + BasePtr = OpaqueWrap(S, Wide); + if (Wide->isNullPointerConstantIgnoreCastsAndOVEs( + S.Context, Expr::NPC_NeverValueDependent) != Expr::NPCK_NotNull) { + UpperBound = BasePtr; + LowerBound = BasePtr; } else { - break; + ExprResult Upper = S.BuildUpperBoundExpr(BasePtr, BasePtr->getExprLoc()); + if (!(UpperBound = Upper.get())) + return ExprError(); + ExprResult Lower = S.BuildLowerBoundExpr(BasePtr, BasePtr->getExprLoc()); + if (!(LowerBound = Lower.get())) + return ExprError(); } - } + } else { + auto Pair = getWidePointerBounds(); + if (Pair.first == nullptr || Pair.second == nullptr) + return ExprError(); - SourceLocation loc; - NamedDecl *d; - if (DeclRefExpr *ref = dyn_cast(E)) { - loc = ref->getLocation(); - d = ref->getDecl(); - } else if (MemberExpr *mem = dyn_cast(E)) { - loc = mem->getMemberLoc(); - d = mem->getMemberDecl(); - } else if (ObjCMessageExpr *msg = dyn_cast(E)) { - diagID = diag::err_uncasted_call_of_unknown_any; - loc = msg->getSelectorStartLoc(); - d = msg->getMethodDecl(); - if (!d) { - S.Diag(loc, diag::err_uncasted_send_to_unknown_any_method) - << static_cast(msg->isClassMessage()) << msg->getSelector() - << orig->getSourceRange(); + BasePtr = OpaqueWrap(S, Pair.first); + UpperBound = Pair.second; + LowerBound = Pair.first; + } + + bool IsIndexCheck = !AccessLowerBound && !AccessUpperBound; + SmallVector Bounds; + if (IsIndexCheck) { + // If access bounds are indices, we can generate an int range check instead + // of a pointer range check. Generating an int range check has vastly + // superior codegen outcomes: since -fbounds-safety pointer arithmetic does not + // use inbounds GEPs, clang is unable to reduce an expression like + // `ptr[A] < ptr[B]` to `A < B` (as the non-inbounds GEP carries a risk of + // arithmetic overflow). Directly expressing `A < B` works around this + // issue. + // A full check would usually be + // `Lower <= AccessLower <= AccessUpper <= Upper`, + // where Lower/Upper are the accessed pointer's lower and upper bounds, and + // AccessLower/AccessUpper are the region that needs to be accessible. + // For instance, ptr[5] has: + // * Lower = ptr + 0 + // * AccessLower = ptr + 5 + // * AccessUpper = ptr + 6 + // * Upper = upper_bound(ptr) + // Translating to indices, we get something like + // `0 <= 5 <= 6 <= upper_bound(ptr) - &ptr[0]`. + // This optimizes very nicely to `6 <= (upper-&ptr[0])`. GEPs that calculate + // the upper bound and the lower bound of a pointer are inherently inbounds, + // so arithmetic on them isn't penalized, so the whole thing is about as + // good as we could make it. This is better even though it requires us to do + // a full bounds check on `ptr` (lower(ptr) <= ptr <= upper(ptr)) before we + // can do index-based comparisons. + // XXX: picking AST patterns for their codegen-ability is crummy. :( + if (!BuildIndexBounds(S, BasePtr, UpperBound, Bounds)) + return ExprError(); + } else { + Expr *MinExpr = BasePtr; + if (S.getLangOpts().hasNewBoundsSafetyCheck( + LangOptions::BS_CHK_EndedByLowerBound)) { + MinExpr = LowerBound; + } + if (!BuildPtrBounds(S, MinExpr, UpperBound, Bounds)) return ExprError(); - } - } else { - S.Diag(E->getExprLoc(), diag::err_unsupported_unknown_any_expr) - << E->getSourceRange(); - return ExprError(); } - S.Diag(loc, diagID) << d << orig->getSourceRange(); + SourceLocation Loc = GuardedValue->getExprLoc(); + ExprResult Cond = BuildLEChecks(S, Loc, Bounds, OVEs); + if (Cond.isInvalid()) + return ExprError(); - // Never recoverable. - return ExprError(); -} + if (isOrNull()) { + ExprResult NullCheck = S.CreateBuiltinUnaryOp(Loc, UO_LNot, BasePtr); + if (NullCheck.isInvalid()) + return ExprError(); + // !Ptr || 0 <= Count <= (Upper - Ptr) + Cond = S.CreateBuiltinBinOp(Loc, BO_LOr, NullCheck.get(), Cond.get()); + if (Cond.isInvalid()) + return ExprError(); + } -ExprResult Sema::CheckPlaceholderExpr(Expr *E) { - if (!Context.isDependenceAllowed()) { - // C cannot handle TypoExpr nodes on either side of a binop because it - // doesn't handle dependent types properly, so make sure any TypoExprs have - // been dealt with before checking the operands. - ExprResult Result = CorrectDelayedTyposInExpr(E); - if (!Result.isUsable()) return ExprError(); - E = Result.get(); + if (IsIndexCheck) { + // Index checks do the blissful assumption that lower <= ptr <= upper to + // use numbers in comparisons, but in fact, both of these conditions can be + // false, so they need to be checked separately. + Expr *Bounds[] = {LowerBound, BasePtr, UpperBound}; + // Check that pointer argument being passed is in range + // Lower <= Ptr <= Upper + ExprResult BasePtrBoundsCheck = BuildLEChecks(S, Loc, Bounds, OVEs); + if (!BasePtrBoundsCheck.get()) + return ExprError(); + Cond = S.CreateBuiltinBinOp( + Loc, BO_LAnd, BasePtrBoundsCheck.get(), Cond.get()); + if (!Cond.get()) + return ExprError(); } - const BuiltinType *placeholderType = E->getType()->getAsPlaceholderType(); - if (!placeholderType) return E; + return S.BuildBoundsCheckExpr(GuardedValue, Cond.get(), {}); +} - switch (placeholderType->getKind()) { - case BuiltinType::UnresolvedTemplate: { - auto *ULE = cast(E); - const DeclarationNameInfo &NameInfo = ULE->getNameInfo(); - // There's only one FoundDecl for UnresolvedTemplate type. See - // BuildTemplateIdExpr. - NamedDecl *Temp = *ULE->decls_begin(); - const bool IsTypeAliasTemplateDecl = isa(Temp); +ExprResult BoundsCheckBuilder::BuildLEChecks( + Sema &S, SourceLocation Loc, ArrayRef Bounds, + SmallVectorImpl &OVEs) { + assert(Bounds.size() >= 2); + Expr *CondAccum = nullptr; + Expr *Next = Bounds[Bounds.size()-1]; - NestedNameSpecifier *NNS = ULE->getQualifierLoc().getNestedNameSpecifier(); - // FIXME: AssumedTemplate is not very appropriate for error recovery here, - // as it models only the unqualified-id case, where this case can clearly be - // qualified. Thus we can't just qualify an assumed template. - TemplateName TN; - if (auto *TD = dyn_cast(Temp)) - TN = Context.getQualifiedTemplateName(NNS, ULE->hasTemplateKeyword(), - TemplateName(TD)); - else - TN = Context.getAssumedTemplateName(NameInfo.getName()); + // Reverse it to check the upper bound first. Upper bounds of `__counted_by` + // pointers are calculated using gep `inbounds`; Checking such `inbounds` + // pointer arithmetic first will help to optimize the same pointer arithmetic + // that doesn't have `inbounds`. + for (unsigned i = Bounds.size() - 1; i--;) { + Expr *Bound = Bounds[i]->IgnoreImpCasts(); + bool IsOpaqueValue = isa(Bound); - Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template) - << TN << ULE->getSourceRange() << IsTypeAliasTemplateDecl; - Diag(Temp->getLocation(), diag::note_referenced_type_template) - << IsTypeAliasTemplateDecl; + // Make sure that comparison types are compatible before we wrap expressions + // in opaque values. + ExprResult NextResult = Next; + ExprResult BoundResult = Bound; + auto CmpTy = S.CheckCompareOperands(BoundResult, NextResult, Loc, BO_LE); + if (CmpTy.isNull() || !(Next = NextResult.get()) + || !(Bound = BoundResult.get())) + return ExprError(); - TemplateArgumentListInfo TAL(ULE->getLAngleLoc(), ULE->getRAngleLoc()); - bool HasAnyDependentTA = false; - for (const TemplateArgumentLoc &Arg : ULE->template_arguments()) { - HasAnyDependentTA |= Arg.getArgument().isDependent(); - TAL.addArgument(Arg); + if (i != 0 && !IsOpaqueValue) { + // Bound will be reused in 'Prev <= Bound && Bound <= Next' thus materialize + // it. If the value was originally an opaque value, it doesn't need to be + // wrapped in a new one just because of implicit casts. + OVEs.push_back(OpaqueValueExpr::Wrap(S.Context, Bound)); + Bound = OVEs.back(); } - QualType TST; - { - SFINAETrap Trap(*this); - TST = CheckTemplateIdType(TN, NameInfo.getBeginLoc(), TAL); + ExprResult LEBounds = S.CreateBuiltinBinOp(Loc, BO_LE, Bound, Next); + if (LEBounds.isInvalid()) + return ExprError(); + + if (!CondAccum) { + CondAccum = LEBounds.get(); + } else { + ExprResult CondAnd = S.CreateBuiltinBinOp(Loc, BO_LAnd, + CondAccum, LEBounds.get()); + if (CondAnd.isInvalid()) + return ExprError(); + CondAccum = CondAnd.get(); } - if (TST.isNull()) - TST = Context.getTemplateSpecializationType( - TN, ULE->template_arguments(), /*CanonicalArgs=*/std::nullopt, - HasAnyDependentTA ? Context.DependentTy : Context.IntTy); - QualType ET = - Context.getElaboratedType(ElaboratedTypeKeyword::None, NNS, TST); - return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {}, - ET); + + // Make sure an implicit cast is not reused in a different node. This + // may result in an unexpected expression type change since implicit + // casts are allowed to change their result types during Sema. + Next = Bound->IgnoreImpCasts(); } - // Overloaded expressions. - case BuiltinType::Overload: { - // Try to resolve a single function template specialization. - // This is obligatory. - ExprResult Result = E; - if (ResolveAndFixSingleFunctionTemplateSpecialization(Result, false)) - return Result; + return CondAccum; +} - // No guarantees that ResolveAndFixSingleFunctionTemplateSpecialization - // leaves Result unchanged on failure. - Result = E; - if (resolveAndFixAddressOfSingleOverloadCandidate(Result)) - return Result; +ExprResult BoundsCheckBuilder::CheckFlexibleArrayMemberSize( + Sema &S, SourceLocation Loc, BoundsCheckKind BCK, Expr *FAMPtr, + CopyExpr *DeclReplacer) { + SmallVector OVEs; + auto *OpaqueRoot = OpaqueValueExpr::EnsureWrapped(S.Context, FAMPtr, OVEs); + Expr *Result = CheckFlexibleArrayMemberSizeImpl(S, Loc, BCK, OpaqueRoot, OVEs, + OpaqueRoot, DeclReplacer) + .get(); + if (!Result) + return ExprError(); - // If that failed, try to recover with a call. - tryToRecoverWithCall(Result, PDiag(diag::err_ovl_unresolvable), - /*complain*/ true); - return Result; - } + // Materialize OpaqueValues + if (!OVEs.empty()) { + Result = MaterializeSequenceExpr::Create(S.Context, Result, OVEs); - // Bound member functions. - case BuiltinType::BoundMember: { - ExprResult result = E; - const Expr *BME = E->IgnoreParens(); - PartialDiagnostic PD = PDiag(diag::err_bound_member_function); - // Try to give a nicer diagnostic if it is a bound member that we recognize. - if (isa(BME)) { - PD = PDiag(diag::err_dtor_expr_without_call) << /*pseudo-destructor*/ 1; - } else if (const auto *ME = dyn_cast(BME)) { - if (ME->getMemberNameInfo().getName().getNameKind() == - DeclarationName::CXXDestructorName) - PD = PDiag(diag::err_dtor_expr_without_call) << /*destructor*/ 0; - } - tryToRecoverWithCall(result, PD, - /*complain*/ true); - return result; + // Unbind everything. + Result = MaterializeSequenceExpr::Create(S.Context, Result, OVEs, true); } + return Result; +} - // ARC unbridged casts. - case BuiltinType::ARCUnbridgedCast: { - Expr *realCast = ObjC().stripARCUnbridgedCast(E); - ObjC().diagnoseARCUnbridgedCast(realCast); - return realCast; +ExprResult BoundsCheckBuilder::CheckFlexibleArrayMemberSizeWithOVEs( + Sema &S, SourceLocation Loc, BoundsCheckKind BCK, Expr *FAMPtr, + SmallVectorImpl &OVEs, CopyExpr *DeclReplacer) { + return CheckFlexibleArrayMemberSizeImpl(S, Loc, BCK, FAMPtr, OVEs, FAMPtr, + DeclReplacer); +} +ExprResult BoundsCheckBuilder::CheckFlexibleArrayMemberSizeImpl( + Sema &S, SourceLocation Loc, BoundsCheckKind BCK, Expr *FAMPtr, + SmallVectorImpl &OVEs, Expr *GuardedValue, + CopyExpr *DeclReplacer) { + + FlexibleArrayMemberUtils FlexUtils(S); + + // The base address must be opaque since it will be used in multiple places. + auto *OpaqueRoot = dyn_cast(FAMPtr); + if (!OpaqueRoot) { + OVEs.push_back(OpaqueValueExpr::Wrap(S.Context, FAMPtr)); + OpaqueRoot = OVEs.back(); } - // Expressions of unknown type. - case BuiltinType::UnknownAny: - return diagnoseUnknownAnyExpr(*this, E); + if (!GuardedValue) + GuardedValue = OpaqueRoot; - // Pseudo-objects. - case BuiltinType::PseudoObject: - return PseudoObject().checkRValue(E); + SmallVector PathToFlex; + ArrayRef CountDecls; + auto *RT = FAMPtr->getType()->getPointeeType()->getAs(); + bool Found = FlexUtils.Find(RT->getDecl(), PathToFlex, CountDecls); + assert(Found); (void)Found; - case BuiltinType::BuiltinFn: { - // Accept __noop without parens by implicitly converting it to a call expr. - auto *DRE = dyn_cast(E->IgnoreParenImpCasts()); - if (DRE) { - auto *FD = cast(DRE->getDecl()); - unsigned BuiltinID = FD->getBuiltinID(); - if (BuiltinID == Builtin::BI__noop) { - E = ImpCastExprToType(E, Context.getPointerType(FD->getType()), - CK_BuiltinFnToFnPtr) - .get(); - return CallExpr::Create(Context, E, /*Args=*/{}, Context.IntTy, - VK_PRValue, SourceLocation(), - FPOptionsOverride()); - } + Expr *FlexibleObj = FlexUtils.SelectFlexibleObject(PathToFlex, OpaqueRoot); - if (Context.BuiltinInfo.isInStdNamespace(BuiltinID)) { - // Any use of these other than a direct call is ill-formed as of C++20, - // because they are not addressable functions. In earlier language - // modes, warn and force an instantiation of the real body. - Diag(E->getBeginLoc(), - getLangOpts().CPlusPlus20 - ? diag::err_use_of_unaddressable_function - : diag::warn_cxx20_compat_use_of_unaddressable_function); - if (FD->isImplicitlyInstantiable()) { - // Require a definition here because a normal attempt at - // instantiation for a builtin will be ignored, and we won't try - // again later. We assume that the definition of the template - // precedes this use. - InstantiateFunctionDefinition(E->getBeginLoc(), FD, - /*Recursive=*/false, - /*DefinitionRequired=*/true, - /*AtEndOfTU=*/false); - } - // Produce a properly-typed reference to the function. - CXXScopeSpec SS; - SS.Adopt(DRE->getQualifierLoc()); - TemplateArgumentListInfo TemplateArgs; - DRE->copyTemplateArgumentsInto(TemplateArgs); - return BuildDeclRefExpr( - FD, FD->getType(), VK_LValue, DRE->getNameInfo(), - DRE->hasQualifier() ? &SS : nullptr, DRE->getFoundDecl(), - DRE->getTemplateKeywordLoc(), - DRE->hasExplicitTemplateArgs() ? &TemplateArgs : nullptr); - } - } + // Create count expression + FieldDecl *FlexibleField = PathToFlex.back(); + ExprResult CountExpr = FlexUtils.BuildCountExpr( + FlexibleField, CountDecls, FlexibleObj, OVEs, DeclReplacer); + if (!CountExpr.get()) + return ExprError(); - Diag(E->getBeginLoc(), diag::err_builtin_fn_use); + // Create array base address + QualType FlexType = PathToFlex.back()->getType(); + Expr *ArrayBase = MemberExpr::CreateImplicit( + S.Context, FlexibleObj, FlexibleObj->getType()->isPointerType(), + PathToFlex.back(), FlexType, VK_LValue, OK_Ordinary); + // Ensure that the pointer isn't __bidi_indexable, as that would be + // problematic. + QualType DecayedTy = S.Context.getArrayDecayedType(FlexType); + DecayedTy = S.Context.getBoundsSafetyPointerType(DecayedTy, {}); + ExprResult DecayedArrayBase = S.ImpCastExprToType( + ArrayBase, DecayedTy, CK_ArrayToPointerDecay, VK_PRValue); + if (!DecayedArrayBase.get()) return ExprError(); - } - case BuiltinType::IncompleteMatrixIdx: - Diag(cast(E->IgnoreParens()) - ->getRowIdx() - ->getBeginLoc(), - diag::err_matrix_incomplete_index); + // ArrayToPointerDecay overrides the pointer attributes to be bidi_indexable, + // force it to be unspecified. + DecayedArrayBase.get()->setType(DecayedTy); + + SmallVector CheckArgs{OpaqueRoot, DecayedArrayBase.get(), + CountExpr.get()}; + + // Create bounds check expression. + return PredefinedBoundsCheckExpr::Create(S.Context, GuardedValue, BCK, + CheckArgs); +} + +ExprResult Sema::BuildTerminatedByToIndexableExpr(Expr *PointerExpr, + Expr *TerminatorExpr, + bool IncludeTerminator, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc) { + // TODO: Add C++ support. + assert(!PointerExpr->getType()->isDependentType() && + "BoundsSafety does not support C++ yet"); + + ExprResult RValue = DefaultFunctionArrayLvalueConversion(PointerExpr); + if (RValue.isInvalid()) return ExprError(); + PointerExpr = RValue.get(); - // Expressions of unknown type. - case BuiltinType::ArraySection: - Diag(E->getBeginLoc(), diag::err_array_section_use) - << cast(E)->isOMPArraySection(); + QualType PtrTy = PointerExpr->getType(); + const auto *PtrVTT = PtrTy->getAs(); + + if (!PtrVTT) { + Diag(PointerExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_to_indexable_wrong_ptr_type) + << PtrTy; return ExprError(); + } - // Expressions of unknown type. - case BuiltinType::OMPArrayShaping: - return ExprError(Diag(E->getBeginLoc(), diag::err_omp_array_shaping_use)); + // __terminated_by() applies only to arrays and __single pointers, but since + // we decay arrays, we expect a __single pointer here. + assert(PtrTy->isSinglePointerType()); - case BuiltinType::OMPIterator: - return ExprError(Diag(E->getBeginLoc(), diag::err_omp_iterator_use)); + if (TerminatorExpr && TerminatorExpr != PtrVTT->getTerminatorExpr()) { + // The terminator must be an ICE. + std::optional TermVal = + TerminatorExpr->getIntegerConstantExpr(Context); + if (!TermVal.has_value()) { + Diag(TerminatorExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_terminator_must_be_const); + return ExprError(); + } - // Everything else should be impossible. -#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ - case BuiltinType::Id: -#include "clang/Basic/OpenCLImageTypes.def" -#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ - case BuiltinType::Id: -#include "clang/Basic/OpenCLExtensionTypes.def" -#define SVE_TYPE(Name, Id, SingletonId) \ - case BuiltinType::Id: -#include "clang/Basic/AArch64SVEACLETypes.def" -#define PPC_VECTOR_TYPE(Name, Id, Size) \ - case BuiltinType::Id: -#include "clang/Basic/PPCTypes.def" -#define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id: -#include "clang/Basic/RISCVVTypes.def" -#define WASM_TYPE(Name, Id, SingletonId) case BuiltinType::Id: -#include "clang/Basic/WebAssemblyReferenceTypes.def" -#define AMDGPU_TYPE(Name, Id, SingletonId, Width, Align) case BuiltinType::Id: -#include "clang/Basic/AMDGPUTypes.def" -#define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) case BuiltinType::Id: -#include "clang/Basic/HLSLIntangibleTypes.def" -#define BUILTIN_TYPE(Id, SingletonId) case BuiltinType::Id: -#define PLACEHOLDER_TYPE(Id, SingletonId) -#include "clang/AST/BuiltinTypes.def" - break; + llvm::APSInt PtrTermVal = PtrVTT->getTerminatorValue(Context); + if (!llvm::APSInt::isSameValue(*TermVal, PtrTermVal)) { + Diag(PointerExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_to_indexable_wrong_terminator) + << TerminatorExpr << PtrVTT->getTerminatorExpr(); + return ExprError(); + } } - llvm_unreachable("invalid placeholder type!"); -} + QualType RetTy = Context.getBoundsSafetyPointerType( + PtrTy, BoundsSafetyPointerAttributes::indexable()); + return new (Context) + TerminatedByToIndexableExpr(BuiltinLoc, RParenLoc, PointerExpr, + TerminatorExpr, IncludeTerminator, RetTy); +} + +ExprResult Sema::ActOnTerminatedByToIndexable(Expr *PointerExpr, + Expr *TerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc) { + return BuildTerminatedByToIndexableExpr(PointerExpr, TerminatorExpr, + /*IncludeTerminator=*/false, + BuiltinLoc, RParenLoc); +} + +ExprResult Sema::ActOnUnsafeTerminatedByToIndexable(Expr *PointerExpr, + Expr *TerminatorExpr, + SourceLocation BuiltinLoc, + SourceLocation RParenLoc) { + return BuildTerminatedByToIndexableExpr(PointerExpr, TerminatorExpr, + /*IncludeTerminator=*/true, + BuiltinLoc, RParenLoc); +} + +ExprResult Sema::BuildTerminatedByFromIndexableExpr( + Expr *TerminatorExpr, Expr *PointerExpr, Expr *PointerToTerminatorExpr, + SourceLocation BuiltinLoc, SourceLocation RParenLoc) { + // TODO: Add C++ support. + assert(!PointerExpr->getType()->isDependentType() && + "BoundsSafety does not support C++ yet"); + + // The terminator must be an ICE. + if (!TerminatorExpr->getIntegerConstantExpr(Context).has_value()) { + Diag(TerminatorExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_terminator_must_be_const); + return ExprError(); + } -bool Sema::CheckCaseExpression(Expr *E) { - if (E->isTypeDependent()) - return true; - if (E->isValueDependent() || E->isIntegerConstantExpr(Context)) - return E->getType()->isIntegralOrEnumerationType(); - return false; -} + ExprResult RValue = DefaultFunctionArrayLvalueConversion(PointerExpr); + if (RValue.isInvalid()) + return ExprError(); + PointerExpr = RValue.get(); -ExprResult Sema::CreateRecoveryExpr(SourceLocation Begin, SourceLocation End, - ArrayRef SubExprs, QualType T) { - if (!Context.getLangOpts().RecoveryAST) + QualType PtrTy = PointerExpr->getType(); + if (!PtrTy->isSafePointerType()) { + Diag(PointerExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_from_indexable_wrong_ptr_type) + << PtrTy; return ExprError(); + } - if (isSFINAEContext()) + // The pointee must be an integer or a thin pointer. + QualType PointeeTy = PtrTy->getPointeeType(); + if (!(PointeeTy->isIntegralOrEnumerationType() || + (PointeeTy->isPointerType() && + !PointeeTy->isPointerTypeWithBounds()))) { + Diag(PointerExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_from_indexable_wrong_pointee_type); return ExprError(); + } - if (T.isNull() || T->isUndeducedType() || - !Context.getLangOpts().RecoveryASTType) - // We don't know the concrete type, fallback to dependent type. - T = Context.DependentTy; + if (PointerToTerminatorExpr) { + ExprResult RValue = + DefaultFunctionArrayLvalueConversion(PointerToTerminatorExpr); + if (RValue.isInvalid()) + return ExprError(); + PointerToTerminatorExpr = RValue.get(); + + QualType PtrToTermTy = PointerToTerminatorExpr->getType(); + if (!PtrToTermTy->isPointerType()) { + Diag( + PointerToTerminatorExpr->getBeginLoc(), + diag:: + err_bounds_safety_terminated_by_from_indexable_wrong_ptr_to_term_type) + << PointerToTerminatorExpr->getType(); + return ExprError(); + } - return RecoveryExpr::Create(Context, T, Begin, End, SubExprs); + if (!Context.hasSameUnqualifiedType(PtrToTermTy->getPointeeType(), + PointeeTy)) { + Diag(PointerToTerminatorExpr->getBeginLoc(), + diag::err_bounds_safety_terminated_by_from_indexable_pointee_mismatch); + return ExprError(); + } + } + + ExprResult TermCastRes(TerminatorExpr); + CastKind Kind = PrepareScalarCast(TermCastRes, PointeeTy); + TermCastRes = ImpCastExprToType(TerminatorExpr, PointeeTy, Kind); + if (!TermCastRes.get()) + return ExprError(); + TerminatorExpr = TermCastRes.get(); + + if (!PtrTy->isIndexablePointerType()) { + PtrTy = Context.getBoundsSafetyPointerType( + PtrTy, BoundsSafetyPointerAttributes::indexable()); + ExprResult PtrCastRes = + ImpCastExprToType(PointerExpr, PtrTy, CK_BoundsSafetyPointerCast); + if (!PtrCastRes.get()) + return ExprError(); + PointerExpr = PtrCastRes.get(); + } + + QualType SinglePtrTy = Context.getBoundsSafetyPointerType( + PtrTy, BoundsSafetyPointerAttributes::single()); + QualType RetTy = Context.getValueTerminatedType(SinglePtrTy, TerminatorExpr); + return new (Context) TerminatedByFromIndexableExpr( + BuiltinLoc, RParenLoc, PointerExpr, PointerToTerminatorExpr, RetTy); +} + +ExprResult Sema::ActOnUnsafeTerminatedByFromIndexable( + Expr *TerminatorExpr, Expr *PointerExpr, Expr *PointerToTerminatorExpr, + SourceLocation BuiltinLoc, SourceLocation RParenLoc) { + return BuildTerminatedByFromIndexableExpr(TerminatorExpr, PointerExpr, + PointerToTerminatorExpr, BuiltinLoc, + RParenLoc); } +/* TO_UPSTREAM(BoundsSafety) OFF*/ diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 73be4080ff90f..9b97ea24fd74e 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -4531,6 +4531,15 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, const ImplicitConversionSequence &ICS, AssignmentAction Action, CheckedConversionKind CCK) { + if (ToType->isPointerType() && + isCXXSafeBuffersBoundsSafetyInteropEnabledAt(From->getBeginLoc())) { + // In C++ Safe Buffers/Bounds Safety interop mode, warn about implicit + // conversions from non-`__null_terminated` to `__null_terminated`: + AssignConvertType ConvTy = + CheckValueTerminatedAssignmentConstraints(ToType, From); + DiagnoseAssignmentResult(ConvTy, From->getExprLoc(), ToType, + From->getType(), From, Action); + } // C++ [over.match.oper]p7: [...] operands of class type are converted [...] if (CCK == CheckedConversionKind::ForBuiltinOverloadedOp && !From->getType()->isRecordType()) @@ -7399,6 +7408,18 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, return QualType(); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (Context.hasSameType(LTy, RTy) && Context.getLangOpts().BoundsSafety) { + auto MergeResult = Context.canMergeTypeBounds(LTy, RTy); + if (MergeResult != ASTContext::BSPTMK_CanMerge) + Diag(QuestionLoc, + diag::err_cond_expr_nested_bounds_safety_pointer_attribute_mismatch) + << LTy << RTy << LHS.get()->getSourceRange() + << RHS.get()->getSourceRange() << MergeResult; + return QualType(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Neither is void. if (IsVectorConditional) return CheckVectorConditionalTypes(Cond, LHS, RHS, QuestionLoc); diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index b0872122ed740..b152bf95e699a 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -21,6 +21,8 @@ #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaObjC.h" #include "clang/Sema/SemaOpenMP.h" +#include "DynamicCountPointerAssignmentAnalysis.h" +#include "TreeTransform.h" using namespace clang; using namespace sema; @@ -1312,7 +1314,13 @@ static bool isPointerToRecordType(QualType T) { ExprResult Sema::PerformMemberExprBaseConversion(Expr *Base, bool IsArrow) { if (IsArrow && !Base->getType()->isFunctionType()) - return DefaultFunctionArrayLvalueConversion(Base); + return DefaultFunctionArrayLvalueConversion( + Base, + /* TO_UPSTREAM(BoundsSafety) ON */ + /*Diagnose*/ true, + /*DiagnoseBoundsSafetyIncompleteArrayPromotion*/ true, + /*DisableFlexibleArrayPromotion*/ true); + /* TO_UPSTREAM(BoundsSafety) OFF */ return CheckPlaceholderExpr(Base); } @@ -1863,6 +1871,60 @@ void Sema::CheckMemberAccessOfNoDeref(const MemberExpr *E) { } } +/* TO_UPSTREAM(BoundsSafety) ON */ +/// BoundsSafety: When dynamic count pointer type is created for FieldDecl, +/// the argument expression is created as DeclRefExpr of the FieldDecl. +/// +/// \code +/// struct S { int *__counted_by(len+1) ptr; int len; }; +/// struct S s; +/// \endcode +/// +/// After a struct is instantiated, the argument expression of __counted_by() +/// should also be instantiated so that the expression reference the field +/// as MemberExpr instead of DeclRefExpr of FieldDecl. After transformation, +/// The type of 's.ptr' will be represented as 'int *__counted_by(s.len+1)'. +class TransformDeclRefField : public TreeTransform { + typedef TreeTransform BaseTransform; + Expr *BaseExpr; + bool IsArrow; + ExprValueKind VK; + +public: + TransformDeclRefField(Sema &SemaRef, Expr *BaseExpr, bool IsArrow) + : BaseTransform(SemaRef), BaseExpr(BaseExpr), IsArrow(IsArrow) { + if (!IsArrow) { + if (BaseExpr->getObjectKind() == OK_Ordinary) + VK = BaseExpr->getValueKind(); + else + VK = VK_PRValue; + } else { + VK = VK_LValue; + } + } + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + FieldDecl *Field = dyn_cast(E->getDecl()); + if (!Field) + return Owned(E); + + ExprValueKind FieldVK = Field->getType()->isReferenceType() ? VK_LValue : VK; + // Using RebuildMemberExpr in this context can cause recursion. + return MemberExpr::CreateImplicit(SemaRef.Context, BaseExpr, IsArrow, Field, Field->getType(), + FieldVK, OK_Ordinary); + } +}; + +ExprResult Sema::InstantiateDeclRefField(Expr *BaseExpr, bool IsArrow, + Expr *FieldRef) { + // FieldRef can be a MemberExpr, but TransformDeclRefField will recurse + // until it reaches the final MemberExpr where the base is a DeclRef of a + // FieldDecl, and transform that. + return TransformDeclRefField(*this, BaseExpr, IsArrow) + .TransformExpr(FieldRef); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + ExprResult Sema::BuildFieldReferenceExpr(Expr *BaseExpr, bool IsArrow, SourceLocation OpLoc, const CXXScopeSpec &SS, diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index f731f9c198535..435d8ce3104d1 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -1405,12 +1405,8 @@ ExprResult SemaObjC::ParseObjCProtocolExpression(IdentifierInfo *ProtocolId, if (PDecl->isNonRuntimeProtocol()) Diag(ProtoLoc, diag::err_objc_non_runtime_protocol_in_protocol_expr) << PDecl; - if (!PDecl->hasDefinition()) { - Diag(ProtoLoc, diag::err_atprotocol_protocol) << PDecl; - Diag(PDecl->getLocation(), diag::note_entity_declared_at) << PDecl; - } else { + if (PDecl->hasDefinition()) PDecl = PDecl->getDefinition(); - } QualType Ty = Context.getObjCProtoType(); if (Ty.isNull()) @@ -2742,6 +2738,10 @@ ExprResult SemaObjC::BuildClassMessage( if (!isImplicit) checkCocoaAPI(SemaRef, Result); } + + if (Method && Context.isObjCMsgSendUsageFileSpecified()) + Context.recordObjCMsgSendUsage(Method); + if (Method) checkFoundationAPI(SemaRef, SelLoc, Method, ArrayRef(Args, NumArgs), ReceiverType, /*IsClassObjectCall=*/true); @@ -3334,6 +3334,10 @@ ExprResult SemaObjC::BuildInstanceMessage( if (!isImplicit) checkCocoaAPI(SemaRef, Result); } + + if (Method && Context.isObjCMsgSendUsageFileSpecified()) + Context.recordObjCMsgSendUsage(Method); + if (Method) { bool IsClassObjectCall = ClassMessage; // 'self' message receivers in class methods should be treated as message @@ -5148,8 +5152,17 @@ ExprResult SemaObjC::ActOnObjCAvailabilityCheckExpr( llvm::ArrayRef AvailSpecs, SourceLocation AtLoc, SourceLocation RParen) { ASTContext &Context = getASTContext(); - auto FindSpecVersion = - [&](StringRef Platform) -> std::optional { + + if (AvailSpecs.front().isDomainName()) { + if (FunctionScopeInfo *Info = SemaRef.getCurFunctionAvailabilityContext()) + Info->HasPotentialFeatureAvailabilityViolations = true; + auto Spec = AvailSpecs.front(); + return ObjCAvailabilityCheckExpr::CreateAvailabilityFeatureCheck( + AtLoc, RParen, Context.BoolTy, Spec.getDomainName(), Context); + } + + auto FindSpecVersion = [&](StringRef Platform) + -> std::optional { auto Spec = llvm::find_if(AvailSpecs, [&](const AvailabilitySpec &Spec) { return Spec.getPlatform() == Platform; }); @@ -5162,12 +5175,20 @@ ExprResult SemaObjC::ActOnObjCAvailabilityCheckExpr( } if (Spec == AvailSpecs.end()) return std::nullopt; - return Spec->getVersion(); + if (Platform == "macos") { + return ObjCAvailabilityCheckExpr::VersionAsWritten{ + llvm::Triple::getCanonicalVersionForOS(llvm::Triple::MacOSX, + Spec->getVersion()), + Spec->getVersion()}; + } + return ObjCAvailabilityCheckExpr::VersionAsWritten{Spec->getVersion(), + Spec->getVersion()}; }; - VersionTuple Version; - if (auto MaybeVersion = - FindSpecVersion(Context.getTargetInfo().getPlatformName())) + auto MaybeVersion = + FindSpecVersion(Context.getTargetInfo().getPlatformName()); + ObjCAvailabilityCheckExpr::VersionAsWritten Version; + if (MaybeVersion) Version = *MaybeVersion; // The use of `@available` in the enclosing context should be analyzed to diff --git a/clang/lib/Sema/SemaFeatureAvailability.cpp b/clang/lib/Sema/SemaFeatureAvailability.cpp new file mode 100644 index 0000000000000..a1f4bcb563baf --- /dev/null +++ b/clang/lib/Sema/SemaFeatureAvailability.cpp @@ -0,0 +1,223 @@ +//===--- SemaFeatureAvailability.cpp - Availability attribute handling ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file processes the feature availability attribute. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Sema/DelayedDiagnostic.h" +#include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/SmallSet.h" +#include + +using namespace clang; +using namespace sema; + +static bool isFeatureUseGuarded(const DomainAvailabilityAttr *AA, + const Decl *ContextDecl, ASTContext &Ctx) { + for (auto *Attr : ContextDecl->specific_attrs()) + if (AA->getDomain() == Attr->getDomain()) + return AA->getUnavailable() == Attr->getUnavailable(); + return false; +} + +static void diagnoseDeclFeatureAvailability(const NamedDecl *D, + SourceLocation Loc, + Decl *ContextDecl, Sema &S) { + for (auto *Attr : D->specific_attrs()) + if (!isFeatureUseGuarded(Attr, ContextDecl, S.Context)) + S.Diag(Loc, diag::err_unguarded_feature) + << D << Attr->getDomain().str() << Attr->getUnavailable(); +} + +class DiagnoseUnguardedFeatureAvailability + : public RecursiveASTVisitor { + + typedef RecursiveASTVisitor Base; + + Sema &SemaRef; + const Decl *D; + + struct FeatureAvailInfo { + StringRef Domain; + bool Unavailable; + }; + + SmallVector FeatureStack; + + bool isFeatureUseGuarded(const DomainAvailabilityAttr *Attr) const; + + bool isConditionallyGuardedByFeature() const; + +public: + DiagnoseUnguardedFeatureAvailability(Sema &SemaRef, const Decl *D, + Decl *Ctx = nullptr) + : SemaRef(SemaRef), D(D) {} + + void diagnoseDeclFeatureAvailability(const NamedDecl *D, SourceLocation Loc); + + bool TraverseIfStmt(IfStmt *If); + + bool VisitDeclRefExpr(DeclRefExpr *DRE) { + diagnoseDeclFeatureAvailability(DRE->getDecl(), DRE->getBeginLoc()); + return true; + } + + bool VisitMemberExpr(MemberExpr *ME) { + diagnoseDeclFeatureAvailability(ME->getMemberDecl(), ME->getBeginLoc()); + return true; + } + + bool VisitObjCMessageExpr(ObjCMessageExpr *OME) { + if (auto *MD = OME->getMethodDecl()) + diagnoseDeclFeatureAvailability(MD, OME->getBeginLoc()); + return true; + } + + bool VisitTypeLoc(TypeLoc Ty); + + void IssueDiagnostics() { + if (auto *FD = dyn_cast(D)) + TraverseStmt(FD->getBody()); + else if (auto *OMD = dyn_cast(D)) + TraverseStmt(OMD->getBody()); + } +}; + +static std::pair extractFeatureExpr(const Expr *IfCond) { + const auto *E = IfCond; + bool IsNegated = false; + while (true) { + E = E->IgnoreParens(); + if (const auto *AE = dyn_cast(E)) { + if (!AE->hasDomainName()) + return {}; + return {AE->getDomainName(), IsNegated}; + } + + const auto *UO = dyn_cast(E); + if (!UO || UO->getOpcode() != UO_LNot) { + return {}; + } + E = UO->getSubExpr(); + IsNegated = !IsNegated; + } +} + +bool DiagnoseUnguardedFeatureAvailability::isConditionallyGuardedByFeature() + const { + return FeatureStack.size(); +} + +bool DiagnoseUnguardedFeatureAvailability::TraverseIfStmt(IfStmt *If) { + std::pair IfCond; + if (auto *Cond = If->getCond()) + IfCond = extractFeatureExpr(Cond); + if (IfCond.first.empty()) { + // This isn't an availability checking 'if', we can just continue. + return Base::TraverseIfStmt(If); + } + + StringRef FeatureStr = IfCond.first; + auto *Guarded = If->getThen(); + auto *Unguarded = If->getElse(); + if (IfCond.second) { + std::swap(Guarded, Unguarded); + } + + FeatureStack.push_back({FeatureStr, false}); + bool ShouldContinue = TraverseStmt(Guarded); + FeatureStack.pop_back(); + + if (!ShouldContinue) + return false; + + FeatureStack.push_back({FeatureStr, true}); + ShouldContinue = TraverseStmt(Unguarded); + FeatureStack.pop_back(); + return ShouldContinue; +} + +bool DiagnoseUnguardedFeatureAvailability::isFeatureUseGuarded( + const DomainAvailabilityAttr *Attr) const { + auto Domain = Attr->getDomain(); + for (auto &Info : FeatureStack) + if (Info.Domain == Domain && Info.Unavailable == Attr->getUnavailable()) + return true; + return ::isFeatureUseGuarded(Attr, D, SemaRef.Context); +} + +void DiagnoseUnguardedFeatureAvailability::diagnoseDeclFeatureAvailability( + const NamedDecl *D, SourceLocation Loc) { + for (auto *Attr : D->specific_attrs()) { + std::string FeatureUse = Attr->getDomain().str(); + if (!isFeatureUseGuarded(Attr)) + SemaRef.Diag(Loc, diag::err_unguarded_feature) + << D << FeatureUse << Attr->getUnavailable(); + } +} + +bool DiagnoseUnguardedFeatureAvailability::VisitTypeLoc(TypeLoc Ty) { + const Type *TyPtr = Ty.getTypePtr(); + SourceLocation Loc = Ty.getBeginLoc(); + + if (Loc.isInvalid()) + return true; + + if (const auto *TT = dyn_cast(TyPtr)) { + TagDecl *TD = TT->getDecl(); + diagnoseDeclFeatureAvailability(TD, Ty.getBeginLoc()); + } else if (const auto *TD = dyn_cast(TyPtr)) { + TypedefNameDecl *D = TD->getDecl(); + diagnoseDeclFeatureAvailability(D, Ty.getBeginLoc()); + } else if (const auto *ObjCO = dyn_cast(TyPtr)) { + if (NamedDecl *D = ObjCO->getInterface()) + diagnoseDeclFeatureAvailability(D, Ty.getBeginLoc()); + } + + return true; +} + +void Sema::handleDelayedFeatureAvailabilityCheck(DelayedDiagnostic &DD, + Decl *Ctx) { + assert(DD.Kind == DelayedDiagnostic::FeatureAvailability && + "Expected a feature availability diagnostic here"); + + DD.Triggered = true; + diagnoseDeclFeatureAvailability(DD.getFeatureAvailabilityDecl(), DD.Loc, Ctx, + *this); +} + +void Sema::DiagnoseUnguardedFeatureAvailabilityViolations(Decl *D) { + assert((D->getAsFunction() || isa(D)) && + "function or ObjC method decl expected"); + DiagnoseUnguardedFeatureAvailability(*this, D).IssueDiagnostics(); +} + +void Sema::DiagnoseFeatureAvailabilityOfDecl(NamedDecl *D, + ArrayRef Locs) { + if (!Context.hasFeatureAvailabilityAttr(D)) + return; + + if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) { + Context->HasPotentialFeatureAvailabilityViolations = true; + return; + } + + if (DelayedDiagnostics.shouldDelayDiagnostics()) { + DelayedDiagnostics.add(DelayedDiagnostic::makeFeatureAvailability(D, Locs)); + return; + } + + Decl *Ctx = cast(getCurLexicalContext()); + diagnoseDeclFeatureAvailability(D, Locs.front(), Ctx, *this); +} diff --git a/clang/lib/Sema/SemaFixItUtils.cpp b/clang/lib/Sema/SemaFixItUtils.cpp index 1dad46fd6b940..74a76a5e3b7fd 100644 --- a/clang/lib/Sema/SemaFixItUtils.cpp +++ b/clang/lib/Sema/SemaFixItUtils.cpp @@ -223,3 +223,107 @@ std::string Sema::getFixItZeroLiteralForType(QualType T, SourceLocation Loc) const { return getScalarZeroExpressionForType(*T, Loc, *this); } + +/* TO_UPSTREAM(BoundsSafety) ON */ +std::tuple +BoundsSafetyFixItUtils::FindPointerAttrInsertPoint(const TypeLoc TL, Sema &S) { + SourceLocation FixitLoc; + if (!TL) { + return std::make_tuple(SourceLocation(), false); + } + + // The default is false because we don't want to add spaces unnecessarily. + // E.g. + // + // foo(int*) + // + // should be transformed to + // + // foo(int* __attribute) + // + // not + // + // foo(int* __attribute ) + // + // The later would likely break the style guide of existing projects + bool SpaceNeededAfterAttribute = false; + if (auto PTL = TL.getAs()) { + SourceLocation StarLoc = PTL.getStarLoc(); + if (StarLoc.isInvalid()) { + return std::make_tuple(StarLoc, false); + } + + if (auto TK = Lexer::findNextToken(StarLoc, S.SourceMgr, S.getLangOpts())) { + FixitLoc = TK->getLocation(); + // For identifiers that follow the type we need to do + // foo(int * __attribute param) + // not + // foo(int * __attributeparam) + // + // FIXME: Explain the macro check + if (StarLoc.isMacroID() || TK->isAnyIdentifier()) { + SpaceNeededAfterAttribute = true; + } + } + } else { + // a typedef or somesuch + FixitLoc = Lexer::findNextTokenLocationAfterTokenAt( + TL.getEndLoc(), S.SourceMgr, S.getLangOpts()); + SpaceNeededAfterAttribute = true; // Correct? + } + return std::make_tuple(FixitLoc, SpaceNeededAfterAttribute); +} + +FixItHint BoundsSafetyFixItUtils::CreateAnnotatePointerDeclFixIt( + const VarDecl *VD, const llvm::StringRef Attribute, Sema &S) { + return CreateAnnotateVarDeclOrFieldDeclFixIt(VD, Attribute, S); +} + +FixItHint BoundsSafetyFixItUtils::CreateAnnotatePointerDeclFixIt( + const FieldDecl *FD, const llvm::StringRef Attribute, Sema &S) { + return CreateAnnotateVarDeclOrFieldDeclFixIt(FD, Attribute, S); +} + +FixItHint BoundsSafetyFixItUtils::CreateAnnotateVarDeclOrFieldDeclFixIt( + const DeclaratorDecl *DD, const llvm::StringRef Attribute, Sema &S) { + assert(isa(DD) || isa(DD)); + auto AssignedVarTL = DD->getTypeSourceInfo()->getTypeLoc(); + SourceLocation InsertionPoint; + bool NeedsSpace = false; + +#ifndef NDEBUG + // The current implementation assumes that there isn't an existing attribute + // on the VarDecl/FieldDecl. This restriction could be lifted in the future. + // It requires that the fixit delete the old attribute. rdar://114478465 + auto DeclTy = DD->getType(); + assert(DeclTy->isPointerType()); + assert(DeclTy->hasAttr(attr::PtrAutoAttr)); +#endif + + std::tie(InsertionPoint, NeedsSpace) = + BoundsSafetyFixItUtils::FindPointerAttrInsertPoint(AssignedVarTL, S); + + if (InsertionPoint.isInvalid()) + return FixItHint(); + + std::string AttrStr(Attribute); + if (NeedsSpace) + AttrStr += " "; + return FixItHint::CreateInsertion(InsertionPoint, AttrStr); +} + +void BoundsSafetyFixItUtils::CreateAnnotateAllPointerDeclsFixIts( + const VarDecl *VD, const llvm::StringRef Attribute, Sema &S, + llvm::SmallVectorImpl> + &FixIts) { + + const VarDecl *CurrentDecl = VD; + while (CurrentDecl) { + auto FixIt = CreateAnnotatePointerDeclFixIt(CurrentDecl, Attribute, S); + if (!FixIt.isNull()) + FixIts.emplace_back(std::make_tuple( + FixIt, static_cast(CurrentDecl))); + CurrentDecl = CurrentDecl->getPreviousDecl(); + } +} +/* TO_UPSTREAM(BoundsSafety) OFF */ \ No newline at end of file diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index a85eca4d4ac3c..dedea4956f558 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -10,12 +10,14 @@ // //===----------------------------------------------------------------------===// +#include "DynamicCountPointerAssignmentAnalysis.h" #include "CheckExprLifetime.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/IgnoreExpr.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/SourceManager.h" @@ -43,6 +45,215 @@ using namespace clang; // Sema Initialization Checking //===----------------------------------------------------------------------===// +/* TO_UPSTREAM(BoundsSafety) ON*/ +class FindDeclRefs : public RecursiveASTVisitor { + SmallVectorImpl &Decls; +public: + FindDeclRefs(SmallVectorImpl &Decls) : Decls(Decls) {} + + bool TraverseDeclRefExpr(DeclRefExpr *D) { + Decls.push_back(D->getDecl()); + return false; + } +}; + +// Check if value-terminated types are correctly initialized. InitListChecker +// cannot be used, as it doesn't traverse all nested members. +static void checkValueTerminatedInit(Sema &S, SourceLocation Loc, + const llvm::Twine &Name, QualType T, + Expr *Init, bool ZeroInit) { + const auto *VTT = T->getAs(); + + // We shouldn't have both Init and ZeroInit flag set. + assert(!Init || !ZeroInit); + + if (Init) { + Init = Init->IgnoreParenImpCasts(); + + // Check if this is copy init from a variable with the same type. For + // pointers, __terminated_by() attributes are checked in + // CheckValueTerminatedAssignmentConstraints. + // TODO: We handle here only the basic case (rdar://103147634). + if (const auto *DRE = dyn_cast(Init)) + if (S.getASTContext().hasSameUnqualifiedType(DRE->getType(), T)) + return; + + if (auto *CLE = dyn_cast(Init)) + Init = CLE->getInitializer(); + } + + auto getInitAt = [&](unsigned Index) -> std::pair { + if (Init) { + if (isa(Init)) + return {nullptr, true}; + if (auto *ILE = dyn_cast(Init)) { + if (Index < ILE->getNumInits()) + return {ILE->getInit(Index), ZeroInit}; + return {nullptr, true}; + } + } + return {nullptr, ZeroInit}; + }; + + // Recursively check each field. + if (const auto *RT = T->getAs()) { + unsigned Index = 0; + for (const FieldDecl *FD : RT->getDecl()->fields()) { + Expr *I; + bool ZI; + std::tie(I, ZI) = getInitAt(Index); + checkValueTerminatedInit( + S, Loc, Name.concat(llvm::Twine('.').concat(FD->getName())), + FD->getType(), I, ZI); + Index++; + } + return; + } + + // Recursively check each element of an array. + // If the array has a __terminated_by attribute, don't check the terminator + // at the end. The element at the end cannot be used anyway. + if (const auto *CAT = S.Context.getAsConstantArrayType(T)) { + QualType ET = CAT->getElementType(); + unsigned End = CAT->getSize().getZExtValue(); + if (VTT && End > 0) + --End; + for (unsigned Index = 0; Index < End; ++Index) { + Expr *I; + bool ZI; + std::tie(I, ZI) = getInitAt(Index); + checkValueTerminatedInit(S, Loc, + Name.concat(llvm::Twine('[') + .concat(llvm::utostr(Index)) + .concat(llvm::Twine(']'))), + ET, I, ZI); + } + // fallthrough + } + + if (!VTT) + return; + + assert(T->isConstantArrayType() || T->isIncompleteArrayType() || + T->isPointerType()); + + // The constraints for __terminated_by() pointers are handled in + // CheckValueTerminatedAssignmentConstraints. + if (T->isPointerType()) + return; + + llvm::APSInt TermVal = VTT->getTerminatorValue(S.Context); + + llvm::APInt Size; + if (const auto *CAT = S.Context.getAsConstantArrayType(T)) { + Size = CAT->getSize(); + assert(Size.isStrictlyPositive()); + } + + std::optional ActualVal; + Expr *GotExpr = nullptr; + const StringLiteral *GotSL = nullptr; + size_t GotSLIndex = 0; + + if (ZeroInit || Init) + ActualVal = llvm::APSInt(TermVal.getBitWidth(), TermVal.isUnsigned()); + + if (Init) { + if (isa(Init)) { + // Nothing to do. + } else if (auto *ILE = dyn_cast(Init)) { + unsigned NumInits = ILE->getNumInits(); + if (!Size && NumInits == 0) { + S.Diag(Loc, + diag::err_bounds_safety_terminated_by_incomplete_array_empty_init) + << Name.str(); + return; + } + if (Size.isZero() || Size.ule(NumInits)) { + unsigned Last = (!Size.isZero() ? Size.getZExtValue() : NumInits) - 1; + Init = ILE->getInit(Last); + bool Ok = Init->EvaluateAsTerminatorValue(*ActualVal, S.Context, + Expr::SE_AllowSideEffects); + if (!Ok) { + S.Diag( + Init->getBeginLoc(), + diag:: + err_bounds_safety_terminated_by_terminator_in_array_must_be_const) + << Name.str(); + return; + } + GotExpr = Init; + } + Loc = Init->getBeginLoc(); + } else if (const auto *SL = dyn_cast(Init)) { + unsigned Len = SL->getLength(); + if (!Size.isZero() && Size.ule(Len)) { + size_t Last = Size.getZExtValue() - 1; + uint32_t Unit = SL->getCodeUnit(Last); + llvm::APInt Val(TermVal.getBitWidth(), Unit, TermVal.isSigned()); + ActualVal = llvm::APSInt(Val, TermVal.isUnsigned()); + GotSL = SL; + GotSLIndex = Last; + } + Loc = SL->getBeginLoc(); + } else { + S.Diag(Init->getBeginLoc(), + diag::err_bounds_safety_terminated_by_wrong_initializer_kind) + << Name.str(); + return; + } + } + + if (!ActualVal) { + S.Diag(Loc, diag::err_bounds_safety_terminated_by_uninitialized) << Name.str(); + return; + } + + if (*ActualVal != TermVal) { + if (GotExpr) { + S.Diag(Loc, diag::err_bounds_safety_terminated_by_terminator_mismatch) + << Name.str() << VTT->getTerminatorExpr() << GotExpr; + } else if (GotSL) { + std::string Str; + llvm::raw_string_ostream SS(Str); + CharacterLiteralKind Kind; + switch (GotSL->getKind()) { + case StringLiteralKind::Ordinary: + case StringLiteralKind::Unevaluated: + Kind = CharacterLiteralKind::Ascii; + break; + case StringLiteralKind::Wide: + Kind = CharacterLiteralKind::Wide; + break; + case StringLiteralKind::UTF8: + Kind = CharacterLiteralKind::UTF8; + break; + case StringLiteralKind::UTF16: + Kind = CharacterLiteralKind::UTF16; + break; + case StringLiteralKind::UTF32: + Kind = CharacterLiteralKind::UTF32; + break; + } + CharacterLiteral::print(GotSL->getCodeUnit(GotSLIndex), Kind, SS); + S.Diag(Loc, diag::err_bounds_safety_terminated_by_terminator_mismatch) + << Name.str() << VTT->getTerminatorExpr() << SS.str(); + } else { + S.Diag(Loc, diag::err_bounds_safety_terminated_by_terminator_mismatch) + << Name.str() << VTT->getTerminatorExpr() + << toString(*ActualVal, 10, TermVal.isSigned()); + } + } +} + +void Sema::CheckValueTerminatedUninitialized(const VarDecl *VD) { + bool ZeroInit = VD->hasGlobalStorage(); + checkValueTerminatedInit(*this, VD->getLocation(), VD->getName(), + VD->getType(), + /*Init=*/nullptr, ZeroInit); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Check whether T is compatible with a wide character type (wchar_t, /// char16_t or char32_t). static bool IsWideCharCompatible(QualType T, ASTContext &Context) { @@ -506,6 +717,8 @@ class InitListChecker { bool CheckFlexibleArrayInit(const InitializedEntity &Entity, Expr *InitExpr, FieldDecl *Field, bool TopLevelObject); + // TO_UPSTREAM(BoundsSafety) + void CheckFlexibleArrayInitCount(InitListExpr *IList); void CheckEmptyInitializable(const InitializedEntity &Entity, SourceLocation Loc); @@ -1069,6 +1282,8 @@ InitListChecker::InitListChecker( if (RequiresSecondPass && !hadError) FillInEmptyInitializations(Entity, FullyStructuredList, RequiresSecondPass, nullptr, 0); + if (!VerifyOnly) + CheckFlexibleArrayInitCount(FullyStructuredList); } if (hadError && FullyStructuredList) FullyStructuredList->markError(); @@ -2265,6 +2480,96 @@ bool InitListChecker::CheckFlexibleArrayInit(const InitializedEntity &Entity, return FlexArrayDiag != diag::ext_flexible_array_init; } +/* TO_UPSTREAM(BoundsSafety) ON*/ +void InitListChecker::CheckFlexibleArrayInitCount(InitListExpr *IList) { + if (!SemaRef.getLangOpts().BoundsSafety) + return; + assert(!VerifyOnly); + assert(!IList->getType()->isVoidType()); + assert(IList->isSemanticForm()); + + auto *RD = getRecordDecl(IList->getType()); + if (!RD || !RD->hasFlexibleArrayMember()) + return; + + assert(IList->getNumInits() > 0); + Expr *InitExpr = IList->getInit(IList->getNumInits() - 1); + assert(!InitExpr->containsErrors()); + if (!InitExpr->getType()->isArrayType()) + return; + + FieldDecl *Field = nullptr; + for (FieldDecl *FD : RD->fields()) + Field = FD; + assert(Field); + assert(Field->getType()->isIncompleteArrayType()); + + SourceLocation BestSourceLoc = InitExpr->getBeginLoc(); + if (BestSourceLoc.isInvalid()) + BestSourceLoc = IList->getBeginLoc(); + bool Diag = false; + if (const auto *DCPTy = Field->getType()->getAs()) { + Expr *CountExpr = DCPTy->getCountExpr(); + // Build count expression, replacing field declarations with their + // initializer value, and then evaluate that. + CopyExpr Copier(SemaRef); + SmallVector Decls; + FindDeclRefs(Decls).TraverseStmt(CountExpr); + for (Decl *D : Decls) { + auto VD = cast(D); + Expr *Val = IList->getInitForField(VD); + assert(Val && !isa(Val)); + Copier.UnsafelyAddDeclSubstitution(VD, Val); + } + + ExprResult R = Copier.TransformExpr(CountExpr); + if (R.isInvalid()) + return; + Expr::EvalResult Res; + if (!R.get()->EvaluateAsRValue(Res, SemaRef.Context)) + return; // error will be emitted later about not being compile-time + // constant + + if (!Res.Val.isInt()) { + SemaRef.Diag(BestSourceLoc, + diag::err_bounds_safety_flexible_global_non_int_count_init) + << CountExpr << R.get() << R.get()->getType(); + Diag = true; + } else { + llvm::APSInt CountExprValue = Res.Val.getInt(); + + llvm::APSInt InitCount; + if (InitExpr) + if (auto *ArrayTy = + SemaRef.Context.getAsConstantArrayType(InitExpr->getType())) + InitCount = ArrayTy->getSize(); + + if (!llvm::APSInt::isSameValue(InitCount, CountExprValue)) { + std::string InitCountS; + std::string CountExprValueS; + { + llvm::raw_string_ostream ICS(InitCountS); + InitCount.print(ICS, InitCount.isSigned()); + llvm::raw_string_ostream CEVS(CountExprValueS); + CountExprValue.print(CEVS, CountExprValue.isSigned()); + } + SemaRef.Diag(BestSourceLoc, + diag::err_bounds_safety_flexible_global_wrong_count) + << InitCountS << CountExprValueS << (InitCount != 1); + Diag = true; + } + } + } else { + SemaRef.Diag(BestSourceLoc, + diag::err_bounds_safety_flexible_global_not_counted); + Diag = true; + } + if (Diag) + SemaRef.Diag(Field->getLocation(), diag::note_flexible_array_member) + << Field; +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + static bool isInitializedStructuredList(const InitListExpr *StructuredList) { return StructuredList && StructuredList->getNumInits() == 1U; } @@ -8220,6 +8525,10 @@ ExprResult InitializationSequence::Perform(Sema &S, else if ((*ResultType)->isLValueReferenceType()) Ty = S.Context.getLValueReferenceType(Ty, (*ResultType)->castAs()->isSpelledAsLValue()); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (const auto *VTT = Step->Type->getAs()) + Ty = S.Context.getValueTerminatedType(Ty, VTT->getTerminatorExpr()); + /* TO_UPSTREAM(BoundsSafety) OFF*/ *ResultType = Ty; } @@ -8354,6 +8663,60 @@ ExprResult InitializationSequence::Perform(Sema &S, return ExprError(); CurInit = Result; + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (S.getLangOpts().BoundsSafety && + S.isCompatibleBoundsUnsafeAssignment(ConvTy) && + S.allowBoundsUnsafePointerAssignment(Entity.getType(), Result.get(), + Result.get()->getExprLoc())) { + ConvTy = Sema::Compatible; + } + if (S.getLangOpts().BoundsSafety && !Entity.isParameterKind()) { + if (!S.CheckDynamicBoundVariableEscape(Step->Type, Result.get())) + return ExprError(); + } + if (S.getLangOpts().BoundsSafety && + (Entity.isParameterKind() || + Entity.getKind() == InitializedEntity::EK_Result || + Entity.getKind() == InitializedEntity::EK_Member)) { + if (Step->Type->isSinglePointerType() && + !Step->Type->isBoundsAttributedType() && + (!SourceType->isSinglePointerType() || + SourceType->isBoundsAttributedType())) { + FlexibleArrayMemberUtils FlexUtils(S); + if (FlexUtils.GetFlexibleRecord(Step->Type->getPointeeType())) { + Expr *FAMPtr = CurInit.get(); + // Unwrap if it's OpaqueValueExpr since `isNullPointerConstant` + // doesn't look into the source expr of OpaqueValueExpr. + auto *PtrUnwrap = FAMPtr->IgnoreParenCasts(); + while (auto *PtrOVE = dyn_cast(PtrUnwrap)) { + PtrUnwrap = PtrOVE->getSourceExpr()->IgnoreParenCasts(); + } + + if (PtrUnwrap->isNullPointerConstant( + S.Context, Expr::NPC_NeverValueDependent) == + Expr::NPCK_NotNull) { + auto *CE = dyn_cast(FAMPtr); + // Ensure we pass an implicit bounds pointer. + if (CE && CE->getCastKind() == CK_BoundsSafetyPointerCast) + FAMPtr = CE->getSubExpr(); + else + CE = nullptr; + ExprResult Ckd = BoundsCheckBuilder::CheckFlexibleArrayMemberSize( + S, FAMPtr->getBeginLoc(), + BoundsCheckKind::FlexibleArrayCountCast, FAMPtr); + if (Ckd.isInvalid()) + return ExprError(); + + if (CE) { + CE->setSubExpr(Ckd.get()); + } else { + CurInit = Ckd; + } + } + } + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ // If this is a call, allow conversion to a transparent union. ExprResult CurInitExprRes = CurInit; if (!S.IsAssignConvertCompatible(ConvTy) && Entity.isParameterKind() && @@ -8389,11 +8752,21 @@ ExprResult InitializationSequence::Perform(Sema &S, Step->Type, InitialCurInit.get()); bool Complained; + ValueDecl *Assignee = nullptr; + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (S.getLangOpts().BoundsSafety) { + // Currently the Assignee is only needed for bounds-safety diagnostics + // so guard passing this information along in this mode. + Assignee = Entity.getDecl(); + } + + // Upstream doesn't have `Assignee`. if (S.DiagnoseAssignmentResult(ConvTy, Kind.getLocation(), Step->Type, SourceType, InitialCurInit.get(), getAssignmentAction(Entity, true), - &Complained)) { + &Complained, Assignee)) { + /* TO_UPSTREAM(BoundsSafety) OFF*/ PrintInitLocationNote(S, Entity); return ExprError(); } else if (Complained) @@ -8408,6 +8781,13 @@ ExprResult InitializationSequence::Perform(Sema &S, S.Context.getAsArrayType(Ty), S, S.getLangOpts().C23 && initializingConstexprVariable(Entity)); + /* TO_UPSTREAM(BoundsSafety) ON*/ + const auto *VTT = Step->Type->getAs(); + if (VTT && UpdateType) { + *ResultType = S.Context.getValueTerminatedType( + *ResultType, VTT->getTerminatorExpr()); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ break; } @@ -8458,6 +8838,12 @@ ExprResult InitializationSequence::Perform(Sema &S, *ResultType = S.Context.getConstantArrayType( IncompleteDest->getElementType(), ConstantSource->getSize(), ConstantSource->getSizeExpr(), ArraySizeModifier::Normal, 0); + /*TO_UPSTREAM(BoundsSafety) ON*/ + if (const auto *VTT = Step->Type->getAs()) { + *ResultType = S.Context.getValueTerminatedType( + *ResultType, VTT->getTerminatorExpr()); + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ } } } @@ -8691,6 +9077,26 @@ ExprResult InitializationSequence::Perform(Sema &S, CheckMoveOnConstruction(S, Init, Entity.getKind() == InitializedEntity::EK_Result); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (S.LangOpts.BoundsSafety || S.isCXXSafeBuffersBoundsSafetyInteropEnabledAt( + CurInit.get()->getBeginLoc())) { + auto *Init = CurInit.get(); + const auto K = Entity.getKind(); + if (Init && Entity.getParent() == nullptr && + (K == InitializedEntity::EK_Variable || + K == InitializedEntity::EK_Parameter || + K == InitializedEntity::EK_Result)) { + const ValueDecl *D = Entity.getDecl(); + StringRef Name = D ? D->getName() : StringRef(); + SourceLocation Loc = D && K == InitializedEntity::EK_Variable + ? D->getLocation() + : Init->getBeginLoc(); + checkValueTerminatedInit(S, Loc, Name, Entity.getType(), Init, + /*ZeroInit=*/false); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + return Init; } diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index 4bba57193ded6..f9a5a02557e0b 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -518,6 +518,7 @@ Sema::ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc, switch (ModuleScopes.empty() ? Module::ExplicitGlobalModuleFragment : ModuleScopes.back().Module->Kind) { case Module::ModuleMapModule: + case Module::IncludeTreeModuleMap: case Module::ExplicitGlobalModuleFragment: case Module::ImplicitGlobalModuleFragment: case Module::ModulePartitionImplementation: diff --git a/clang/lib/Sema/SemaObjC.cpp b/clang/lib/Sema/SemaObjC.cpp index 02fdac5122c28..613de08fd99fc 100644 --- a/clang/lib/Sema/SemaObjC.cpp +++ b/clang/lib/Sema/SemaObjC.cpp @@ -431,20 +431,6 @@ TypeResult SemaObjC::actOnObjCTypeArgsAndProtocolQualifiers( ResultTL = ObjCObjectPointerTL.getPointeeLoc(); } - if (auto OTPTL = ResultTL.getAs()) { - // Protocol qualifier information. - if (OTPTL.getNumProtocols() > 0) { - assert(OTPTL.getNumProtocols() == Protocols.size()); - OTPTL.setProtocolLAngleLoc(ProtocolLAngleLoc); - OTPTL.setProtocolRAngleLoc(ProtocolRAngleLoc); - for (unsigned i = 0, n = Protocols.size(); i != n; ++i) - OTPTL.setProtocolLoc(i, ProtocolLocs[i]); - } - - // We're done. Return the completed type to the parser. - return SemaRef.CreateParsedType(Result, ResultTInfo); - } - auto ObjCObjectTL = ResultTL.castAs(); // Type argument information. diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp index 3e962fcb8b0e5..1de0623797fd2 100644 --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -633,6 +633,8 @@ ObjCPropertyDecl *SemaObjC::CreatePropertyDecl( SemaRef.ProcessDeclAttributes(S, PDecl, FD.D); + SemaRef.copyFeatureAvailabilityCheck(PDecl, CDecl); + if (Attributes & ObjCPropertyAttribute::kind_readonly) PDecl->setPropertyAttributes(ObjCPropertyAttribute::kind_readonly); @@ -1303,6 +1305,8 @@ Decl *SemaObjC::ActOnPropertyImplDecl( PropertyIvarType, /*TInfo=*/nullptr, ObjCIvarDecl::Private, (Expr *)nullptr, true); + SemaRef.copyFeatureAvailabilityCheck(Ivar, property); + SemaRef.copyFeatureAvailabilityCheck(Ivar, ClassImpDecl); if (SemaRef.RequireNonAbstractType(PropertyIvarLoc, PropertyIvarType, diag::err_abstract_type_in_decl, Sema::AbstractSynthesizedIvarType)) { @@ -1407,6 +1411,9 @@ Decl *SemaObjC::ActOnPropertyImplDecl( : ObjCPropertyImplDecl::Dynamic), Ivar, PropertyIvarLoc); + SemaRef.copyFeatureAvailabilityCheck(PIDecl, property); + SemaRef.copyFeatureAvailabilityCheck(PIDecl, ClassImpDecl); + if (CompleteTypeErr || !compat) PIDecl->setInvalidDecl(); @@ -1418,9 +1425,11 @@ Decl *SemaObjC::ActOnPropertyImplDecl( // If the method hasn't been overridden, create a synthesized implementation. ObjCMethodDecl *OMD = ClassImpDecl->getMethod( getterMethod->getSelector(), getterMethod->isInstanceMethod()); - if (!OMD) + if (!OMD) { OMD = RedeclarePropertyAccessor(Context, IC, getterMethod, AtLoc, PropertyLoc); + SemaRef.copyFeatureAvailability(OMD, PIDecl); + } PIDecl->setGetterMethodDecl(OMD); } @@ -1482,9 +1491,11 @@ Decl *SemaObjC::ActOnPropertyImplDecl( if (Synthesize) { ObjCMethodDecl *OMD = ClassImpDecl->getMethod( setterMethod->getSelector(), setterMethod->isInstanceMethod()); - if (!OMD) + if (!OMD) { OMD = RedeclarePropertyAccessor(Context, IC, setterMethod, AtLoc, PropertyLoc); + SemaRef.copyFeatureAvailability(OMD, PIDecl); + } PIDecl->setSetterMethodDecl(OMD); } @@ -2347,6 +2358,7 @@ static void AddPropertyAttrs(Sema &S, ObjCMethodDecl *PropertyMethod, isa(A)) PropertyMethod->addAttr(A->clone(S.Context)); } + S.copyFeatureAvailabilityCheck(PropertyMethod, Property); } /// ProcessPropertyDecl - Make sure that any user-defined setter/getter methods @@ -2475,6 +2487,7 @@ void SemaObjC::ProcessPropertyDecl(ObjCPropertyDecl *property) { : ObjCImplementationControl::Required); CD->addDecl(GetterMethod); + SemaRef.copyFeatureAvailability(GetterMethod, CD); AddPropertyAttrs(SemaRef, GetterMethod, property); if (property->isDirectProperty()) @@ -2554,6 +2567,7 @@ void SemaObjC::ProcessPropertyDecl(ObjCPropertyDecl *property) { nullptr); SetterMethod->setMethodParams(Context, Argument, {}); + SemaRef.copyFeatureAvailability(SetterMethod, CD); AddPropertyAttrs(SemaRef, SetterMethod, property); if (property->isDirectProperty()) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 87a400e8a6291..f00877882ab05 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -26,6 +26,7 @@ #include "clang/AST/TypeLoc.h" #include "clang/AST/TypeOrdering.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Edit/RefactoringFixits.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/EnterExpressionEvaluationContext.h" #include "clang/Sema/Initialization.h" @@ -641,6 +642,7 @@ Sema::ActOnLabelStmt(SourceLocation IdentLoc, LabelDecl *TheDecl, TheDecl->setLocation(IdentLoc); } } + return LS; } @@ -1708,6 +1710,7 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, // Produce a nice diagnostic if multiple values aren't handled. if (!UnhandledNames.empty()) { + { auto DB = Diag(CondExpr->getExprLoc(), TheDefaultStmt ? diag::warn_def_missing_case : diag::warn_missing_case) @@ -1716,6 +1719,12 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch, for (size_t I = 0, E = std::min(UnhandledNames.size(), (size_t)3); I != E; ++I) DB << UnhandledNames[I]; + } + auto DB = + Diag(CondExpr->getExprLoc(), diag::note_fill_in_missing_cases); + edit::fillInMissingSwitchEnumCases( + Context, SS, ED, CurContext, + [&](const FixItHint &Hint) { DB << Hint; }); } if (!hasCasesNotInSwitch) diff --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp index 4507a21a4c111..72c46afa4b179 100644 --- a/clang/lib/Sema/SemaStmtAsm.cpp +++ b/clang/lib/Sema/SemaStmtAsm.cpp @@ -235,6 +235,38 @@ getClobberConflictLocation(MultiExprArg Exprs, Expr **Constraints, return SourceLocation(); } +/* TO_UPSTREAM(BoundsSafety) ON */ +/// Returns true if given expression has bounded pointer type that cannot be used as inline assembly argument; false otherwise. +static bool checkAsmArgumentsWithBounds(Sema &S, Expr *E) { + if (E->getType()->isBoundsAttributedType()) { + S.Diag(E->getBeginLoc(), diag::err_bounds_safety_ptr_with_bounds_in_assembly); + return true; + } else if (auto PT = E->getType()->getAs()) { + if (!PT->hasRawPointerLayout()) { + S.Diag(E->getBeginLoc(), diag::err_bounds_safety_ptr_with_bounds_in_assembly); + return true; + } + } + const Expr* const Base = E->IgnoreParenCasts(); + if (auto DRE = dyn_cast_or_null(Base)) { + if (auto ReferredVar = dyn_cast_or_null(DRE->getDecl())) { + if (ReferredVar->hasAttr()) { + S.Diag(E->getBeginLoc(), diag::err_bounds_safety_dyn_count_in_assembly); + return true; + } + } + } else if (auto DRE = dyn_cast_or_null(Base)) { + if (auto ReferredField = dyn_cast_or_null(DRE->getMemberDecl())) { + if (ReferredField->hasAttr()) { + S.Diag(E->getBeginLoc(), diag::err_bounds_safety_dyn_count_in_assembly); + return true; + } + } + } + return false; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + ExprResult Sema::ActOnGCCAsmStmtString(Expr *Expr, bool ForAsmLabel) { if (!Expr) return ExprError(); @@ -340,6 +372,13 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, << OutputExpr->getType() << 0 /*Input*/ << OutputExpr->getSourceRange()); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + if (checkAsmArgumentsWithBounds(*this, OutputExpr)) + return StmtError(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + OutputConstraintInfos.push_back(Info); // If this is dependent, just continue. @@ -438,6 +477,22 @@ StmtResult Sema::ActOnGCCAsmStmt(SourceLocation AsmLoc, bool IsSimple, if (Result.isInvalid()) return StmtError(); + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafety) { + if (checkAsmArgumentsWithBounds(*this, Result.get()->IgnoreImpCasts())) + return StmtError(); + } + // BoundsSafety: non-lvalue internal bounds pointers shall cast to a raw pointer + // to pass as the inline assembly input. + auto *PTy = Result.get()->getType()->getAs(); + if (PTy && !PTy->hasRawPointerLayout()) { + QualType RawPTy = Context.getPointerType(PTy->getPointeeType()); + Result = ImpCastExprToType(Result.get(), RawPTy, CK_BoundsSafetyPointerCast); + if (Result.isInvalid()) + return StmtError(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + InputExpr = Exprs[i] = Result.get(); if (Info.requiresImmediateConstant() && !Info.allowsRegister()) { diff --git a/clang/lib/Sema/SemaSwift.cpp b/clang/lib/Sema/SemaSwift.cpp index 4aae855a24b8f..8bc97a950d8ac 100644 --- a/clang/lib/Sema/SemaSwift.cpp +++ b/clang/lib/Sema/SemaSwift.cpp @@ -631,6 +631,11 @@ void SemaSwift::handleAsyncName(Decl *D, const ParsedAttr &AL) { SwiftAsyncNameAttr(getASTContext(), AL, Name)); } +void SemaSwift::handleTransparentStepping(Decl *D, const ParsedAttr &AL) { + D->addAttr(::new (getASTContext()) + TransparentSteppingAttr(getASTContext(), AL)); +} + void SemaSwift::handleNewType(Decl *D, const ParsedAttr &AL) { // Make sure that there is an identifier as the annotation's single argument. if (!AL.checkExactlyNumArgs(SemaRef, 1)) diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 0ecdbb3ffb89f..2923a9c6d576f 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4329,6 +4329,19 @@ static bool AdjustFunctionParmAndArgTypesForDeduction( ArgType, S.Context.getDefaultOpenCLPointeeAddrSpace()); ArgType = S.Context.getLValueReferenceType(ArgType); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + } else if (S.getLangOpts().BoundsSafety) { + if (ArgType->isArrayType()) { + ArgType = S.Context.getArrayDecayedType(ArgType); + ArgType = S.Context.getBoundsSafetyPointerType( + ArgType, BoundsSafetyPointerAttributes::bidiIndexable()); + } else if (ArgType->isFunctionType()) { + ArgType = S.Context.getPointerType(ArgType, + BoundsSafetyPointerAttributes::single()); + } else { + ArgType = ArgType.getUnqualifiedType(); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ } else { // C++ [temp.deduct.call]p2: // If P is not a reference type: @@ -5335,6 +5348,23 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, return DeductionFailed(TDK); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (getLangOpts().BoundsSafetyAttributes && + Init->getType()->isBoundsAttributedType()) { + unsigned ErrIdx; + QualType Ty = Init->getType(); + if (auto *DCPTy = Ty->getAs()) { + ErrIdx = DCPTy->getKind(); + } else { + auto *DRPTy = Ty->getAs(); + assert(DRPTy); + ErrIdx = DRPTy->getEndPointer() ? 4 : 5; + } + Diag(Loc, diag::err_bounds_safety_auto_dynamic_bound) << ErrIdx; + return DeductionFailed(TemplateDeductionResult::AlreadyDiagnosed); + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // Could be null if somehow 'auto' appears in a non-deduced context. if (Deduced[0].getKind() != TemplateArgument::Type) return DeductionFailed(TemplateDeductionResult::Incomplete); @@ -5348,7 +5378,13 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, } if (!Result.isNull()) { - if (!Context.hasSameType(DeducedType, Result)) { + if (!Context.hasSameType(DeducedType, Result) + /* TO_UPSTREAM(BoundsSafety) ON*/ + || (Context.getLangOpts().BoundsSafety && + Context.canMergeTypeBounds(DeducedType, Result) != + ASTContext::BSPTMK_CanMerge) + /* TO_UPSTREAM(BoundsSafety) OFF*/ + ) { Info.FirstArg = Result; Info.SecondArg = DeducedType; return DeductionFailed(TemplateDeductionResult::Inconsistent); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 6e7ee8b5506ff..801559fba2886 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "TreeTransform.h" #include "TypeLocBuilder.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -22,6 +23,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/LocInfoType.h" +#include "clang/AST/StmtVisitor.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/AST/TypeLocVisitor.h" @@ -47,6 +49,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/SaveAndRestore.h" #include #include @@ -181,6 +184,15 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr, case ParsedAttr::AT_TypeNullableResult: \ case ParsedAttr::AT_TypeNullUnspecified +/* TO_UPSTREAM(BoundsSafety) ON */ +// BoundsSafety type qualifiers. +#define BOUNDS_SAFETY_POINTER_TYPE_ATTRS_CASELIST \ + case ParsedAttr::AT_PtrBidiIndexable: \ + case ParsedAttr::AT_PtrSingle: \ + case ParsedAttr::AT_PtrIndexable: \ + case ParsedAttr::AT_PtrUnsafeIndexable +/* TO_UPSTREAM(BoundsSafety) OFF */ + namespace { /// An object which stores processing state for the entire /// GetTypeForDeclarator process. @@ -283,7 +295,8 @@ namespace { /// Get an attributed type for the given attribute, and remember the Attr /// object so that we can attach it to the AttributedTypeLoc. - QualType getAttributedType(Attr *A, QualType ModifiedType, + // TO_UPSTREAM(BoundsSafety): In upstream Attr *A is not `const`. + QualType getAttributedType(const Attr *A, QualType ModifiedType, QualType EquivType) { QualType T = sema.Context.getAttributedType(A, ModifiedType, EquivType); @@ -338,6 +351,31 @@ namespace { llvm_unreachable("no Attr* for AttributedType*"); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// Get the Attr* for a given attributed type. + const Attr *getAttrForAttributedType(const AttributedType *AT) { + if (!AttrsForTypesSorted) { + llvm::stable_sort(AttrsForTypes, llvm::less_first()); + AttrsForTypesSorted = true; + } + + // FIXME: This is quadratic if we have lots of reuses of the same + // attributed type. + for (auto It = std::partition_point( + AttrsForTypes.begin(), AttrsForTypes.end(), + [=](const TypeAttrPair &A) { return A.first < AT; }); + It != AttrsForTypes.end() && It->first == AT; ++It) { + if (It->second) { + return It->second; + } + } + + // If the AttributedType was created in another Decl it will have been + // removed already + return nullptr; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + SourceLocation getExpansionLocForMacroQualifiedType(const MacroQualifiedType *MQT) const { auto FoundLoc = LocsForMacros.find(MQT); @@ -737,6 +775,12 @@ static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state, // Microsoft type attributes cannot go after the declarator-id. continue; + /* TO_UPSTREAM(BoundsSafety) ON*/ + BOUNDS_SAFETY_POINTER_TYPE_ATTRS_CASELIST: + // BoundsSafety pointer type attributes cannot go after the declarator-id. + continue; + /* TO_UPSTREAM(BoundsSafety) OFF*/ + NULLABILITY_TYPE_ATTRS_CASELIST: // Nullability specifiers cannot go after the declarator-id. @@ -1804,7 +1848,8 @@ static QualType deduceOpenCLPointeeAddrSpace(Sema &S, QualType PointeeType) { return PointeeType; } -QualType Sema::BuildPointerType(QualType T, +// TO_UPSTREAM(BoundsSafety): Add `BoundsSafetyPointerAttributes A` +QualType Sema::BuildPointerType(QualType T, BoundsSafetyPointerAttributes A, SourceLocation Loc, DeclarationName Entity) { if (T->isReferenceType()) { // C++ 8.3.2p4: There shall be no ... pointers to references ... @@ -1854,7 +1899,8 @@ QualType Sema::BuildPointerType(QualType T, } // Build the pointer type. - return Context.getPointerType(T); + // TO_UPSTREAM(BoundsSafety): Pass `A` + return Context.getPointerType(T, A); } QualType Sema::BuildReferenceType(QualType T, bool SpelledAsLValue, @@ -2568,6 +2614,18 @@ bool Sema::CheckFunctionReturnType(QualType T, SourceLocation Loc) { if (T.isVolatileQualified() && getLangOpts().CPlusPlus20) Diag(Loc, diag::warn_deprecated_volatile_return) << T; + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety does not allow you to return a type with a flexible array member + // by value. + if (getLangOpts().BoundsSafety) { + auto *RecordTy = T->getAs(); + if (RecordTy && RecordTy->getDecl()->hasFlexibleArrayMember()) { + Diag(Loc, diag::err_flexible_array_member_passed_by_copy) << T; + return true; + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + if (T.getAddressSpace() != LangAS::Default && getLangOpts().HLSL) return true; return false; @@ -2660,6 +2718,10 @@ QualType Sema::BuildFunctionType(QualType T, Diag(Loc, diag::err_parameters_retval_cannot_have_fp16_type) << 0 << FixItHint::CreateInsertion(Loc, "*"); Invalid = true; + } else if (ParamType.getPointerAuth()) { + // __ptrauth is illegal on a function return type. + Diag(Loc, diag::err_ptrauth_qualifier_param) << T; + Invalid = true; } else if (ParamType->isWebAssemblyTableType()) { Diag(Loc, diag::err_wasm_table_as_function_parameter); Invalid = true; @@ -4702,8 +4764,13 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, D.setInvalidType(true); } } - - T = S.BuildPointerType(T, DeclType.Loc, Name); + /* TO_UPSTREAM(BoundsSafety) ON */ + // Declare and pass `BoundsSafetyPointerAttributes A`. + { + BoundsSafetyPointerAttributes A; + T = S.BuildPointerType(T, A, DeclType.Loc, Name); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ if (DeclType.Ptr.TypeQuals) T = S.BuildQualifiedType(T, DeclType.Loc, DeclType.Ptr.TypeQuals); break; @@ -5042,6 +5109,24 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, D.setInvalidType(true); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + // BoundsSafety does not allow you to return a type with a flexible array + // member by value. + if (S.getLangOpts().BoundsSafety) { + SourceLocation DiagLoc; + auto *RecordTy = T->getAs(); + if (RecordTy && RecordTy->getDecl()->hasFlexibleArrayMember()) { + if (TInfo) { + DiagLoc = TInfo->getTypeLoc().getBeginLoc(); + } else { + DiagLoc = D.getDeclSpec().getTypeSpecTypeLoc(); + } + S.Diag(DiagLoc, diag::err_flexible_array_member_passed_by_copy) << T; + D.setInvalidType(true); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // cv-qualifiers on return types are pointless except when the type is a // class type in C++. if ((T.getCVRQualifiers() || T->isAtomicType()) && @@ -6078,7 +6163,8 @@ namespace { TypeSourceInfo *TInfo = nullptr; Sema::GetTypeFromParser(DS.getRepAsType(), &TInfo); assert(TInfo); - TL.getValueLoc().initializeFullCopy(TInfo->getTypeLoc()); + // TO_UPSTREAM(BoundsSafety): initializeFullCopy -> copy + TL.getValueLoc().copy(TInfo->getTypeLoc()); } else { TL.setKWLoc(DS.getAtomicSpecLoc()); // No parens, to indicate this was spelled as an _Atomic qualifier. @@ -6125,6 +6211,12 @@ namespace { void VisitDecayedTypeLoc(DecayedTypeLoc TL) { llvm_unreachable("decayed type locs not expected here!"); } + /* TO_UPSTREAM(BoundsSafety) ON */ + void VisitValueTerminatedTypeLoc(ValueTerminatedTypeLoc TL) { + llvm_unreachable("value terminated type locs not expected here!"); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ + void VisitArrayParameterTypeLoc(ArrayParameterTypeLoc TL) { llvm_unreachable("array parameter type locs not expected here!"); } @@ -6284,6 +6376,14 @@ GetTypeSourceInfoForDeclarator(TypeProcessingState &State, bool HasDesugaredTypeLoc = true; while (HasDesugaredTypeLoc) { switch (CurrTL.getTypeLocClass()) { + /* TO_UPSTREAM(BoundsSafety) ON*/ + case TypeLoc::Atomic: { + auto TL = CurrTL.castAs(); + fillAtomicQualLoc(TL, D.getTypeObject(i)); + CurrTL = TL.getValueLoc().getUnqualifiedLoc(); + break; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ case TypeLoc::MacroQualified: { auto TL = CurrTL.castAs(); TL.setExpansionLoc( @@ -6300,6 +6400,9 @@ GetTypeSourceInfoForDeclarator(TypeProcessingState &State, } case TypeLoc::Adjusted: + /* TO_UPSTREAM(BoundsSafety) ON*/ + case TypeLoc::ValueTerminated: + /* TO_UPSTREAM(BoundsSafety) OFF*/ case TypeLoc::BTFTagAttributed: { CurrTL = CurrTL.getNextTypeLoc().getUnqualifiedLoc(); break; @@ -7245,8 +7348,8 @@ static bool CheckNullabilityTypeSpecifier( break; S.Diag(NullabilityLoc, diag::warn_nullability_duplicate) - << DiagNullabilityKind(Nullability, IsContextSensitive) - << FixItHint::CreateRemoval(NullabilityLoc); + << DiagNullabilityKind(Nullability, IsContextSensitive) + << FixItHint::CreateRemoval(NullabilityLoc); break; } @@ -7254,8 +7357,8 @@ static bool CheckNullabilityTypeSpecifier( if (!OverrideExisting) { // Conflicting nullability. S.Diag(NullabilityLoc, diag::err_nullability_conflicting) - << DiagNullabilityKind(Nullability, IsContextSensitive) - << DiagNullabilityKind(*ExistingNullability, false); + << DiagNullabilityKind(Nullability, IsContextSensitive) + << DiagNullabilityKind(*ExistingNullability, false); return true; } @@ -7273,8 +7376,8 @@ static bool CheckNullabilityTypeSpecifier( if (auto ExistingNullability = Desugared->getNullability()) { if (Nullability != *ExistingNullability && !Implicit) { S.Diag(NullabilityLoc, diag::err_nullability_conflicting) - << DiagNullabilityKind(Nullability, IsContextSensitive) - << DiagNullabilityKind(*ExistingNullability, false); + << DiagNullabilityKind(Nullability, IsContextSensitive) + << DiagNullabilityKind(*ExistingNullability, false); // Try to find the typedef with the existing nullability specifier. if (auto TT = Desugared->getAs()) { @@ -7284,7 +7387,7 @@ static bool CheckNullabilityTypeSpecifier( AttributedType::stripOuterNullability(underlyingType)) { if (*typedefNullability == *ExistingNullability) { S.Diag(typedefDecl->getLocation(), diag::note_nullability_here) - << DiagNullabilityKind(*ExistingNullability, false); + << DiagNullabilityKind(*ExistingNullability, false); } } } @@ -7298,7 +7401,7 @@ static bool CheckNullabilityTypeSpecifier( !(AllowOnArrayType && Desugared->isArrayType())) { if (!Implicit) S.Diag(NullabilityLoc, diag::err_nullability_nonpointer) - << DiagNullabilityKind(Nullability, IsContextSensitive) << QT; + << DiagNullabilityKind(Nullability, IsContextSensitive) << QT; return true; } @@ -7317,11 +7420,11 @@ static bool CheckNullabilityTypeSpecifier( pointeeType->isObjCObjectPointerType() || pointeeType->isMemberPointerType())) { S.Diag(NullabilityLoc, diag::err_nullability_cs_multilevel) - << DiagNullabilityKind(Nullability, true) << QT; + << DiagNullabilityKind(Nullability, true) << QT; S.Diag(NullabilityLoc, diag::note_nullability_type_specifier) - << DiagNullabilityKind(Nullability, false) << QT - << FixItHint::CreateReplacement(NullabilityLoc, - getNullabilitySpelling(Nullability)); + << DiagNullabilityKind(Nullability, false) << QT + << FixItHint::CreateReplacement(NullabilityLoc, + getNullabilitySpelling(Nullability)); return true; } } @@ -8350,6 +8453,17 @@ static void HandleNeonVectorTypeAttr(QualType &CurType, const ParsedAttr &Attr, /// Handle the __ptrauth qualifier. static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T, const ParsedAttr &Attr, Sema &S) { + if (S.checkPointerAuthEnabled(Attr.getLoc(), Attr.getRange())) { + Attr.setInvalid(); + return; + } + + /* TO_UPSTREAM(BoundsSafety) ON */ + if (T->isIndexablePointerType() || T->isBidiIndexablePointerType()) { + Attr.setInvalid(); + S.Diag(Attr.getLoc(), diag::err_bounds_safety_ptrauth_on_indexable_pointer); + } + /* TO_UPSTREAM(BoundsSafety) OFF */ assert((Attr.getNumArgs() > 0 && Attr.getNumArgs() <= 3) && "__ptrauth qualifier takes between 1 and 3 arguments"); @@ -8577,6 +8691,797 @@ static void HandleRISCVRVVVectorBitsTypeAttr(QualType &CurType, CurType = S.Context.getVectorType(EltType, NumElts, VecKind); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +/// @brief __single, __bidi_indexable and friends are macros declared in +/// ptrcheck.h. Treat them as if they were the actual token to get the spelling +/// location of. +static SourceLocation +getActualBoundsSafetyAttributeLocation(SourceManager &SrcMan, + SourceLocation AttrLoc) { + // Handles case of being passed as macro argument. + AttrLoc = SrcMan.getTopMacroCallerLoc(AttrLoc); + // Escapes ptrcheck.h macro. Loop needed for __null_terminated. + while (SrcMan.getFilename(SrcMan.getSpellingLoc(AttrLoc)) + .ends_with("ptrcheck.h")) { + AttrLoc = + SrcMan.getImmediateExpansionRange(AttrLoc).getAsRange().getBegin(); + } + // Handles case of being part of a macro body, making sure we get a location + // inside the macro. + AttrLoc = SrcMan.getSpellingLoc(AttrLoc); + return AttrLoc; +} + +/// @brief Checks whether there is a previous redundant -fbounds-safety attribute +/// that is part of the same declaration. We don't want to warn about redundant +/// attributes when the first occurrence is part of a typedef or macro body, +/// while the other is not. +static bool +shouldWarnRedundantBoundsSafetyAttribute(const TypeProcessingState &state, + const ParsedAttr &CurrAttr) { + auto &SrcMan = state.getSema().getSourceManager(); + + // If the declaration is part of a macro body, use the macro call site + // as the location of the declaration. + auto DeclLoc = SrcMan.getExpansionLoc(state.getDeclarator().getBeginLoc()); + auto CurrLoc = getActualBoundsSafetyAttributeLocation(SrcMan, CurrAttr.getLoc()); + + // Check whether the attribute is spelled out inside the declarator range. + // The attribute being spelled out outside the declarator range implies that + // it's added by a macro or typedef, and vice versa. + // We cannot use the declarator range and fullyContains() here because the + // declarator end location may not be set yet. Just comparing against the + // begin loc still works for this purpose, since macros and typedefs cannot be + // forward referenced. If the attribute location is after the start of the + // declaration it is inside the declarator range. + if (SrcMan.isBeforeInTranslationUnit(DeclLoc, CurrLoc)) { + for (auto &OtherAttr : state.getCurrentAttributes()) { + if (OtherAttr.getKind() != CurrAttr.getKind()) + continue; + SourceLocation OtherLoc = + getActualBoundsSafetyAttributeLocation(SrcMan, OtherAttr.getLoc()); + if (SrcMan.isBeforeInTranslationUnit(DeclLoc, OtherLoc) && + SrcMan.isBeforeInTranslationUnit(OtherLoc, CurrLoc)) { + return true; + } + } + } + + return false; +} + +/// Unpeel types until a PointerType is encountered and apply a bounds attribute +/// to it. Then rewrap it with the previous outer types. Uses TypeLoc when +/// possible to enable fetching the Attr* of AttributedTypes created in a +/// different DeclSpec than the one currently being processed. In theory +/// AttributedType can be created with raw attr::Kind instead Attr*, but while +/// processing decls we need to save the (AttributedType,Attr*) pair for later +/// TypeSourceInfo processing. When the TypeLoc isn't accessible a dummy +/// TypeLoc is created from the Type. +class ConstructBoundsSafetyPointerType + : public TypeLocVisitor { + using BaseClass = TypeLocVisitor; + +protected: + enum DiagBoundKind { + Bidi = 0, + Indexable = 1, + Single = 2, + Unsafe = 3, + }; + + TypeProcessingState &state; + Sema &S; + ASTContext &Ctx; + ParsedAttr &PAttr; + // Save type that was involved in removing context for rebuilding + // AttributedType + QualType ProblematicTy; + DiagBoundKind DiagIndex; + // Whether the current TypeLoc being processed is created by this class, or + // fetched from TypeSourceInfo. If true, the TypeLoc doesn't contain + // ExtraLocalData (i.e. Attr* for AttributedTypeLoc). + bool TypeLocIsForged = false; + // Errors related to the PointerType already having a bounds attribute should + // only be emitted if that attribute was explicitly added. + bool AutoBounded = false; + // Prevent the same atomics type error from being emitted twice if nested + // inside AttributedType + bool EmittedAtomicError = false; + BoundsSafetyPointerAttributes FAttrFromAttributedType; + + QualType copyQualifiers(QualType OldTy, QualType NewTy) const { + Qualifiers Qs = OldTy.getLocalQualifiers(); + if (const auto *AT = OldTy->getAs()) { + Qualifiers ModifiedTyQs = AT->getModifiedType().getQualifiers(); + Qualifiers::removeCommonQualifiers(Qs, ModifiedTyQs); + } + QualifierCollector QC(Qs); + return QC.apply(Ctx, NewTy); + } + +public: + explicit ConstructBoundsSafetyPointerType(TypeProcessingState &state, + ParsedAttr &PAttr) + : state(state), S(state.getSema()), Ctx(state.getSema().getASTContext()), + PAttr(PAttr) { + switch (PAttr.getKind()) { + case ParsedAttr::AT_PtrBidiIndexable: + DiagIndex = Bidi; + break; + case ParsedAttr::AT_PtrIndexable: + DiagIndex = Indexable; + break; + case ParsedAttr::AT_PtrSingle: + DiagIndex = Single; + break; + default: + assert(PAttr.getKind() == ParsedAttr::AT_PtrUnsafeIndexable); + DiagIndex = Unsafe; + } + } + + QualType Visit(const TypeLoc TL) { + QualType NewTy = BaseClass::Visit(TL); + return copyQualifiers(TL.getType(), NewTy); + } + + QualType VisitType(QualType T) { + assert(!isa(T)); + const DeclSpec &DS = state.getDeclarator().getDeclSpec(); + if (DS.isTypeRep()) + if (auto LIT = DS.getRepAsType().get()->getAs()) + if (LIT->getType().getAsOpaquePtr() == T.getAsOpaquePtr()) { + // This case is relevant for typeof(T) and _Atomic(type). When these + // declspecs have been parsed any Attrs inside the parentheses are + // cleared from the TypeProcessingState, but the inner TSI is saved in + // the TypeRep allowing us to fetch it again here. + SaveAndRestore FakeTLStatus(TypeLocIsForged); + TypeLocIsForged = false; + return Visit(LIT->getTypeSourceInfo()->getTypeLoc()); + } + // Create dummy TypeLoc when none is available to avoid duplicating visitors + TypeLoc TL(T, nullptr); + SaveAndRestore FakeTLStatus(TypeLocIsForged); + TypeLocIsForged = true; + return Visit(TL); + } + + QualType VisitTypeLoc(const TypeLoc TL) { + TypeLoc Next = TL.getNextTypeLoc(); + if (Next.isNull()) { + return {}; + } + return Visit(Next); + } + +// Given both TypeLoc and QualType, pick best option to visit. +// This is a macro so that expressions that would result in segfaults if +// evaluated can be passed. We detect cases where that could happen with +// TypeLocIsForged, preventing the evaluation. +#define VisitEither(TL, T) \ + (TypeLocIsForged ? VisitType(T) : VisitEitherImpl(TL, T)) + + // Keeping the rest of the implementation in this function prevents the + // parameters from being evaluated multiple times. + QualType VisitEitherImpl(TypeLoc TL, QualType T) { + assert(!TypeLocIsForged); + // The TypeLoc for an __auto_type can still contain the undeduced type even + // after the type has been deduced -> visit the correct type instead. + // Otherwise visit the TypeLoc if it is the real TypeLoc for the type. + if (TL.getTypePtr()->isUndeducedType() && !T->isUndeducedType()) + return VisitType(T); + return Visit(TL); + } + + QualType VisitAutoTypeLoc(const AutoTypeLoc TL) { + const AutoType *ATy = TL.getTypePtr(); + QualType DTy = ATy->getDeducedType(); + if (DTy.isNull()) { + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_undeduced) << DiagIndex; + return {}; + } + + QualType Res = VisitEither(TL.getNextTypeLoc(), DTy); + if (Res.isNull()) + return {}; + auto TST = state.getDeclarator().getDeclSpec().getTypeSpecType(); + if (TST == TST_auto || TST == TST_decltype_auto || TST == TST_auto_type || + TST == TST_unspecified) + Res = Ctx.getAutoType(Res, TL.getAutoKeyword(), false); + return Res; + } + + QualType VisitTypeOfTypeLoc(const TypeOfTypeLoc TL) { + QualType Res = VisitEither(TL.getUnmodifiedTInfo()->getTypeLoc(), + TL.getUnmodifiedType()); + if (Res.isNull()) + return {}; + + // We don't want to actually wrap the modified type in typeof() since + // it's misleading, but if this is the current declspec we need the type + // to match the TST. + auto TST = state.getDeclarator().getDeclSpec().getTypeSpecType(); + if (TST == DeclSpec::TST_typeofType || + TST == DeclSpec::TST_typeof_unqualType) + Res = Ctx.getTypeOfType(Res, TL.getTypePtr()->getKind()); + return Res; + } + + QualType VisitTypeOfExprTypeLoc(const TypeOfExprTypeLoc TL) { + const TypeOfExprType *T = TL.getTypePtr(); + const Expr *E = T->getUnderlyingExpr()->IgnoreParens(); + + if (auto AddrOfE = dyn_cast(E); + AddrOfE && AddrOfE->getOpcode() == UO_AddrOf) { + if (auto DerefE = dyn_cast(AddrOfE->getSubExpr()); + DerefE && DerefE->getOpcode() == UO_Deref) { + E = DerefE->getSubExpr(); + } + } + + E = E->IgnoreParenLValueCasts(); + + // Finding an appropriate decl so we can get the TypeSourceInfo is not + // always possible, but this should cover the basic cases. The TSI is + // only needed for reconstructing AttributedType. Unlike some other types + // TypeOfExprType doesn't crash when attributes are missing, luckily. + const DeclaratorDecl *DDecl = nullptr; + if (auto DRE = dyn_cast(E)) + DDecl = dyn_cast(DRE->getDecl()); + else if (auto CallE = dyn_cast(E)) + DDecl = dyn_cast(CallE->getCalleeDecl()); + else if (auto ME = dyn_cast(E)) + DDecl = dyn_cast(ME->getMemberDecl()); + + TypeSourceInfo *TSI = nullptr; + if (DDecl) + TSI = DDecl->getTypeSourceInfo(); + else if (auto CE = dyn_cast(E)) + TSI = CE->getTypeInfoAsWritten(); + + SaveAndRestore FakeTLStatus(TypeLocIsForged); + TypeLocIsForged = TSI == nullptr; + + QualType Res = VisitEither(TSI->getTypeLoc(), + TL.getType().getSingleStepDesugaredType(Ctx)); + if (Res.isNull()) + return {}; + // We don't want to actually wrap the modified type in typeof() since + // it's misleading, but if this is the current declspec we need the type + // to match the TST. + auto TST = state.getDeclarator().getDeclSpec().getTypeSpecType(); + if (TST == DeclSpec::TST_typeofExpr || + TST == DeclSpec::TST_typeof_unqualExpr) { + Expr *DummyExpr = ImplicitCastExpr::Create( + Ctx, Res, CK_BoundsSafetyPointerCast, T->getUnderlyingExpr(), + /*BasePath=*/nullptr, Expr::getValueKindForType(Res), + FPOptionsOverride()); + Res = Ctx.getTypeOfExprType(DummyExpr, TL.getTypePtr()->getKind()); + } + return Res; + } + + // If we reach a function type we arrived via typeof(call_expr()) and want the + // return type + QualType VisitFunctionProtoTypeLoc(const FunctionProtoTypeLoc FPTL) { + assert(!TypeLocIsForged); + return Visit(FPTL.getReturnLoc()); + } + + QualType VisitFunctionNoProtoTypeLoc(const FunctionNoProtoTypeLoc FPTL) { + assert(!TypeLocIsForged); + return Visit(FPTL.getReturnLoc()); + } + + QualType VisitParenTypeLoc(const ParenTypeLoc TL) { + QualType InnerTy = VisitEither( + TL.getInnerLoc(), TL.getType().getSingleStepDesugaredType(Ctx)); + return S.Context.getParenType(InnerTy); + } + + QualType VisitPointerTypeLoc(const PointerTypeLoc TL) { + QualType PTy = TL.getType(); + + BoundsSafetyPointerAttributes FAttr = TL.getPointerAttributes(); + auto BoundsAttrPrev = FAttr.getBoundsAttr(); + + const auto Kind = PAttr.getKind(); + + switch (Kind) { + case ParsedAttr::AT_PtrBidiIndexable: + FAttr.setBidiIndexable(); + break; + case ParsedAttr::AT_PtrSingle: + FAttr.setSingle(); + break; + case ParsedAttr::AT_PtrIndexable: + FAttr.setIndexable(); + break; + case ParsedAttr::AT_PtrUnsafeIndexable: + FAttr.setUnsafeIndexable(); + break; + default: + assert(0 && "Expected BoundsSafety Pointer Type Attr"); + return {}; + } + + if (!AutoBounded && (BoundsAttrPrev || FAttrFromAttributedType.getBoundsAttr())) { + if ((BoundsAttrPrev && BoundsAttrPrev != FAttr.getBoundsAttr()) || + (FAttrFromAttributedType.getBoundsAttr() && + FAttrFromAttributedType.getBoundsAttr() != FAttr.getBoundsAttr())) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_conflicting_pointer_attributes) + << /* pointer */ 1 << /* bound */ 0; + } else if (shouldWarnRedundantBoundsSafetyAttribute(state, PAttr)) { + S.Diag(PAttr.getLoc(), + diag::warn_bounds_safety_duplicate_pointer_attributes) + << 1 << BoundsAttrPrev - 1; + } + return {}; + } + + if (FAttr.hasUpperBound()) { + if (PTy->isFunctionPointerType()) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_function_pointers_cannot_be_indexable); + return {}; + } + } + + // In attribute-only mode, represent __single and __unsafe_indexable as + // sugars. + if (S.getLangOpts().BoundsSafetyAttributes && + !S.getLangOpts().BoundsSafety && + (Kind == ParsedAttr::AT_PtrSingle || + Kind == ParsedAttr::AT_PtrUnsafeIndexable)) { + const Attr *A; + if (Kind == ParsedAttr::AT_PtrSingle) + A = createSimpleAttr(Ctx, PAttr); + else + A = createSimpleAttr(Ctx, PAttr); + return state.getAttributedType(A, PTy, PTy); + } + + return Ctx.getPointerType(PTy->getPointeeType(), FAttr); + } + + QualType + VisitCountAttributedTypeLoc(const CountAttributedTypeLoc TL) { + if (DiagIndex < Single) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_conflicting_count_bound_attributes) + << PAttr << DiagIndex; + return TL.getType(); + } + + auto InnerTy = VisitEither(TL.getInnerLoc(), + TL.getType().getSingleStepDesugaredType(Ctx)); + const CountAttributedType *DCPT = TL.getTypePtr(); + + return Ctx.getCountAttributedType( + InnerTy, DCPT->getCountExpr(), DCPT->isCountInBytes(), DCPT->isOrNull(), + DCPT->getCoupledDecls()); + } + + QualType + VisitDynamicRangePointerTypeLoc(const DynamicRangePointerTypeLoc TL) { + if (DiagIndex < Single) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_conflicting_count_bound_attributes) + << PAttr << DiagIndex; + return TL.getType(); + } + + auto InnerTy = VisitEither(TL.getInnerLoc(), + TL.getType().getSingleStepDesugaredType(Ctx)); + + const DynamicRangePointerType *DRPT = TL.getTypePtr(); + return Ctx.getDynamicRangePointerType( + InnerTy, DRPT->getStartPointer(), DRPT->getEndPointer(), + DRPT->getStartPtrDecls(), DRPT->getEndPtrDecls()); + } + + QualType VisitMacroQualifiedTypeLoc(const MacroQualifiedTypeLoc TL) { + QualType NewTy = VisitEither(TL.getInnerLoc(), + TL.getType().getSingleStepDesugaredType(Ctx)); + return S.Context.getMacroQualifiedType( + NewTy, TL.getTypePtr()->getMacroIdentifier()); + } + + /// Typedef is a different declarator, so we won't have Attrs for their + /// AttributedType. Fetch the TypeLoc from the Decl in case we need to fetch + /// the Attr from the AttributedTypeLoc. + QualType VisitTypedefTypeLoc(const TypedefTypeLoc TL) { + TypeLoc ActualTypeLoc = + TL.getTypedefNameDecl()->getTypeSourceInfo()->getTypeLoc(); + + SaveAndRestore FakeTLStatus(TypeLocIsForged); + TypeLocIsForged = false; + QualType Res = VisitEither(ActualTypeLoc, + TL.getType().getSingleStepDesugaredType(Ctx)); + + if (Res.isNull()) + return {}; + + // We don't want to actually wrap the modified type in typedef() since it's + // misleading, but if this is the current declspec we need the type to match + // the TST. + if (state.getDeclarator().getDeclSpec().getTypeSpecType() == + DeclSpec::TST_typename) { + const char *Suffix; + switch (DiagIndex) { + case Bidi: + Suffix = " __bidi_indexable"; + break; + case Indexable: + Suffix = " __indexable"; + break; + case Single: + Suffix = " __single"; + break; + case Unsafe: + Suffix = " __unsafe_indexable"; + break; + } + // Create a dummy typedef with the new bounds. The bounds suffix is used + // to avoid clashing in case the same typedef is used multiple times with + // different bounds. The " " in the name makes it print as if the bounds + // were outside the typedef. This also has the nice side-effect that it + // cannot clash with typenames from source code. + DeclarationName DN(&Ctx.Idents.get( + (TL.getTypePtr()->getDecl()->getName() + Suffix).str())); + LookupResult Result(S, DN, PAttr.getLoc(), Sema::LookupOrdinaryName); + ; + if (S.LookupName(Result, S.TUScope, /*AllowBuiltinCreation=*/true)) { + NamedDecl *ND = Result.getFoundDecl(); + if (TypedefNameDecl *TD = dyn_cast(ND)) + if (TD->getUnderlyingType() == Res) + return S.Context.getTypedefType(TD); + } + TypedefDecl *NewTypedef = Ctx.buildImplicitTypedef(Res, DN.getAsString()); + S.IdResolver.AddDecl(NewTypedef); + Res = Ctx.getTypedefType(NewTypedef, Res); + } + return Res; + } + + QualType VisitAttributedTypeLoc(const AttributedTypeLoc TL) { + const AttributedType *T = TL.getTypePtr(); + + SaveAndRestore SARAutoBounded(AutoBounded); + AutoBounded |= T->getAttrKind() == attr::PtrAutoAttr; + + SaveAndRestore SARFAttrFromAttributedtype( + FAttrFromAttributedType); + if (T->getAttrKind() == attr::PtrSingle) + FAttrFromAttributedType = BoundsSafetyPointerAttributes::single(); + else if (T->getAttrKind() == attr::PtrUnsafeIndexable) + FAttrFromAttributedType = BoundsSafetyPointerAttributes::unsafeIndexable(); + + QualType NewEqTy = VisitType(T->getEquivalentType()); + + // Early exit so that Visit(ModifiedLoc) doesn't emit the same error + if (NewEqTy.isNull()) + return {}; + + if (T->getAttrKind() == attr::PtrAutoAttr) + return NewEqTy; + + const Attr *attr = TypeLocIsForged + ? state.getAttrForAttributedType(TL.getTypePtr()) + : TL.getAttr(); + // If we create an AttributedType without the corresponding Attr we'll run + // into problems when TypLocs are created, however there are cases when + // dropping the AttributedType would also cause a crash during later TypeLoc + // processing. Emit an error to prevent the crash in the rare case that we + // cannot recover the Attr. + if (!attr) { + auto TST = state.getDeclarator().getDeclSpec().getTypeSpecType(); + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_irrecoverable_attr) + << DiagIndex << TL.getType() + << (TST == TST_typeofExpr || TST == TST_typeof_unqualExpr); + return {}; + } + + if (T->getModifiedType().getAsOpaquePtr() == + T->getEquivalentType().getAsOpaquePtr()) + return state.getAttributedType(attr, NewEqTy, NewEqTy); + + QualType NewModTy = + VisitEither(TL.getModifiedLoc(), TL.getTypePtr()->getModifiedType()); + if (NewModTy.isNull()) + return {}; + return state.getAttributedType(attr, NewModTy, NewEqTy); + } + + QualType VisitAtomicTypeLoc(const AtomicTypeLoc TL) { + if (DiagIndex < Single && !EmittedAtomicError) { + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_atomic_unsupported_attribute) + << (DiagIndex == Bidi ? 1 : 0); + // Not marking the bounds as explicit (preventing + // PtrAutoAttr) will cause the error to be emitted a second time for local + // variables when the default __bidi_indexable bounds are applied, so go + // on and apply the bounds despite the error. + EmittedAtomicError = true; + } + + QualType VT = + VisitEither(TL.getValueLoc(), TL.getTypePtr()->getValueType()); + + if (VT.isNull()) + return {}; + + return Ctx.getAtomicType(VT); + } + + QualType VisitValueTerminatedTypeLoc(const ValueTerminatedTypeLoc TL) { + if (DiagIndex != Single) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_terminated_by_wrong_pointer_type); + return TL.getType(); + } + + QualType T = VisitEither(TL.getNextTypeLoc(), TL.getTypePtr()->desugar()); + + if (T.isNull()) + return {}; + + return Ctx.getValueTerminatedType(T, TL.getTypePtr()->getTerminatorExpr()); + } +#undef VisitEither +}; + +/// Handle BoundsSafety pointer type attributes - +/// bidi_indexable, single, indexable, unsafe_indexable. +static void HandleBoundsSafetyPointerTypeAttr(TypeProcessingState &state, + ParsedAttr &PAttr, QualType &type) { + Sema &S = state.getSema(); + + bool AttrSupported = false; + if (S.getLangOpts().BoundsSafety) { + AttrSupported = true; + } else if (S.getLangOpts().BoundsSafetyAttributes) { + auto K = PAttr.getKind(); + AttrSupported = + K == ParsedAttr::AT_PtrSingle || K == ParsedAttr::AT_PtrUnsafeIndexable; + } + + /// Unlike DeclAttrs which are by default skipped with unsupported LangOpts, + /// TypeAttrs require manual code to ignore unsupported attribute. + if (!AttrSupported) { + S.Diag(PAttr.getLoc(), diag::warn_attribute_ignored) << PAttr; + PAttr.setInvalid(); + return; + } + + QualType T = type; + if (auto AT = T->getAs()) + T = AT->getValueType(); + + if (!T->isPointerType() && !T->getAs()) { + S.Diag(PAttr.getLoc(), diag::err_attribute_pointers_only) << PAttr << 0; + PAttr.setInvalid(); + return; + } + + switch (PAttr.getKind()) { + case ParsedAttr::AT_PtrBidiIndexable: + case ParsedAttr::AT_PtrIndexable: + if (type.getPointerAuth()) { + PAttr.setInvalid(); + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_ptrauth_on_indexable_pointer); + } + break; + default: + break; + } + + QualType NewTy = ConstructBoundsSafetyPointerType(state, PAttr).VisitType(type); + if (!NewTy.isNull()) + type = NewTy; + else + PAttr.setInvalid(); +} + +static bool HandlePtrTerminatedByTypeAttr(TypeProcessingState &state, + const ParsedAttr &PAttr, + QualType &type) { + Sema &S = state.getSema(); + QualType T = type; + + if (auto AT = type->getAs()) { + auto ModifiedPlusVT = AT->getModifiedType(); + if (!HandlePtrTerminatedByTypeAttr(state, PAttr, ModifiedPlusVT)) + return false; // Avoid emitting errors twice + + auto EquivalentPlusVT = AT->getEquivalentType(); + if (!HandlePtrTerminatedByTypeAttr(state, PAttr, EquivalentPlusVT)) + return false; + + auto QualsOnT = type.getQualifiers(); + auto QualsOnModifTy = AT->getModifiedType().getQualifiers(); + + const Attr *attr = nullptr; + if (auto TDT = type->getAs()) { + // Make sure the AttributedType is inside the TypedefType and not vice + // versa. + if (TDT->getAs() == AT) { + auto ATLoc = TDT->getDecl() + ->getTypeSourceInfo() + ->getTypeLoc() + .getAsAdjusted(); + attr = ATLoc.getAttr(); + } + } + if (!attr) + attr = state.getAttrForAttributedType(AT); + type = state.getAttributedType(attr, ModifiedPlusVT, EquivalentPlusVT); + + Qualifiers::removeCommonQualifiers(QualsOnT, QualsOnModifTy); + if (!QualsOnT.empty()) { + QualifierCollector QC(QualsOnT); + type = QC.apply(state.getSema().Context, type); + } + return true; + } + + if (!S.getLangOpts().BoundsSafetyAttributes) { + S.Diag(PAttr.getLoc(), diag::warn_attribute_ignored) << PAttr; + PAttr.setInvalid(); + return false; + } + + if (PAttr.getNumArgs() != 1) { + S.Diag(PAttr.getLoc(), diag::err_attribute_wrong_number_arguments) + << PAttr << 1; + PAttr.setInvalid(); + return false; + } + + auto *TerminatorExpr = PAttr.getArgAsExpr(0); + + // The terminator must be an ICE. + llvm::APSInt Terminator; + if (!verifyValidIntegerConstantExpr(S, PAttr, Terminator)) { + PAttr.setInvalid(); + return false; + } + + const auto *VT = T->getAs(); + if (VT) { + llvm::APSInt TermVal = VT->getTerminatorValue(S.Context); + if (!llvm::APSInt::isSameValue(TermVal, Terminator)) { + assert(T->isPointerType() || T->isArrayType()); + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_conflicting_pointer_attributes) + << T->isPointerType() << /* terminator */ 4; + S.Diag(PAttr.getLoc(), + diag::note_bounds_safety_conflicting_pointer_attribute_args) + << /* terminator */ 2 << VT->getTerminatorExpr() << TerminatorExpr; + PAttr.setInvalid(); + return false; + } + + if (shouldWarnRedundantBoundsSafetyAttribute(state, PAttr)) { + S.Diag(PAttr.getLoc(), diag::warn_bounds_safety_duplicate_pointer_attributes) + << T->isPointerType() << 4; + PAttr.setInvalid(); + return false; + } + + SplitQualType Split = T.getSplitUnqualifiedType(); + T = S.Context.getQualifiedType(VT->desugar(), Split.Quals); + } + + if (!T->isConstantArrayType() && !T->isIncompleteArrayType() && + !T->isPointerType()) { + if (T->isAtomicType()) { + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_atomic_unsupported_attribute) + << /*terminated_by*/ 7; + } else { + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_terminated_by_wrong_type); + } + PAttr.setInvalid(); + return false; + } + + QualType PET; + + if (const auto *AT = S.Context.getAsArrayType(T)) { + // Constant arrays cannot be empty. We will check incomplete arrays during + // initialization. + if (const auto *CAT = S.Context.getAsConstantArrayType(T)) { + if (CAT->getSize().isZero()) { + S.Diag(PAttr.getLoc(), diag::err_bounds_safety_terminated_by_empty_array); + PAttr.setInvalid(); + return false; + } + } + PET = AT->getElementType(); + } + + if (const auto *PT = T->getAs()) { + // Pointers with dynamic bounds are normally handled later, but they + // can surface when using __typeof__, or probably typedefs eventually. + if (T->isBoundsAttributedType()) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_terminated_by_wrong_pointer_type); + PAttr.setInvalid(); + return false; + } + + // If the pointer is unspecified, we will add __single attribute later in + // MakeAutoPointer. + if (!PT->isUnspecified() && !PT->isSingle()) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_terminated_by_wrong_pointer_type); + PAttr.setInvalid(); + return false; + } + PET = PT->getPointeeType(); + } + + // The pointee must be an integer or a thin pointer. + if (!(PET->isIntegralOrEnumerationType() || + (PET->isPointerType() && !PET->isPointerTypeWithBounds()))) { + S.Diag(PAttr.getLoc(), + diag::err_bounds_safety_terminated_by_wrong_elem_or_pointee_type) + << T->isPointerType(); + PAttr.setInvalid(); + return false; + } + + ExprResult Res(TerminatorExpr); + CastKind Kind = S.PrepareScalarCast(Res, PET); + Res = S.ImpCastExprToType(TerminatorExpr, PET, Kind); + if (!Res.get()) { + PAttr.setInvalid(); + return false; + } + TerminatorExpr = Res.get(); + + type = S.Context.getValueTerminatedType(T, TerminatorExpr); + +#ifndef NDEBUG + if (VT) { + llvm::FoldingSetNodeID NewID; + llvm::FoldingSetNodeID OldID; + VT->Profile(OldID, S.Context); + type->getAs()->Profile(NewID, S.Context); + if (OldID != NewID) { + VT->dump(); + type->dump(); + } + assert(OldID == NewID); + } +#endif + + return true; +} + +static void HandleArrayDecayDiscardsCountInParametersAttr( + TypeProcessingState &state, ParsedAttr &Attr, QualType &type) { + // only valid with -fbounds-safety enabled + if (!state.getSema().getLangOpts().BoundsSafetyAttributes) { + state.getSema().Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr; + Attr.setInvalid(); + return; + } + + ASTContext &Context = state.getSema().Context; + // only valid on array types + if (!Context.getAsArrayType(type)) { + state.getSema().Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr; + Attr.setInvalid(); + return; + } + + type = state.getAttributedType( + createSimpleAttr(Context, Attr), + type, type); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + /// Handle OpenCL Access Qualifier Attribute. static void HandleOpenCLAccessAttr(QualType &CurType, const ParsedAttr &Attr, Sema &S) { @@ -8920,6 +9825,22 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, attr.setUsedAsTypeAttr(); break; + /*TO_UPSTREAM(BoundsSafet) ON*/ + BOUNDS_SAFETY_POINTER_TYPE_ATTRS_CASELIST: + HandleBoundsSafetyPointerTypeAttr(state, attr, type); + attr.setUsedAsTypeAttr(); + break; + + case ParsedAttr::AT_PtrTerminatedBy: + HandlePtrTerminatedByTypeAttr(state, attr, type); + attr.setUsedAsTypeAttr(); + break; + + case ParsedAttr::AT_ArrayDecayDiscardsCountInParameters: + HandleArrayDecayDiscardsCountInParametersAttr(state, attr, type); + attr.setUsedAsTypeAttr(); + break; + /*TO_UPSTREAM(BoundsSafet) OFF*/ NULLABILITY_TYPE_ATTRS_CASELIST: // Either add nullability here or try to distribute it. We @@ -9040,14 +9961,28 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, // applied to ObjC builtin attributes. if (isa(type) && attr.hasMacroIdentifier() && !type.getQualifiers().hasObjCLifetime() && - !type.getQualifiers().hasObjCGCAttr() && - attr.getKind() != ParsedAttr::AT_ObjCGC && - attr.getKind() != ParsedAttr::AT_ObjCOwnership) { - const IdentifierInfo *MacroII = attr.getMacroIdentifier(); - type = state.getSema().Context.getMacroQualifiedType(type, MacroII); - state.setExpansionLocForMacroQualifiedType( - cast(type.getTypePtr()), - attr.getMacroExpansionLoc()); + !type.getQualifiers().hasObjCGCAttr()) { + + switch(attr.getKind()) { + case ParsedAttr::AT_ObjCGC: + case ParsedAttr::AT_ObjCOwnership: + /* TO_UPSTREAM(BoundsSafety) ON*/ + case ParsedAttr::AT_CountedBy: + case ParsedAttr::AT_SizedBy: + case ParsedAttr::AT_CountedByOrNull: + case ParsedAttr::AT_SizedByOrNull: + case ParsedAttr::AT_PtrEndedBy: + case ParsedAttr::AT_PtrTerminatedBy: + BOUNDS_SAFETY_POINTER_TYPE_ATTRS_CASELIST: + /* TO_UPSTREAM(BoundsSafety) OFF*/ + break; + default: + const IdentifierInfo *MacroII = attr.getMacroIdentifier(); + type = state.getSema().Context.getMacroQualifiedType(type, MacroII); + state.setExpansionLocForMacroQualifiedType( + cast(type.getTypePtr()), + attr.getMacroExpansionLoc()); + }; } } } @@ -9619,9 +10554,523 @@ QualType Sema::BuildTypeofExprType(Expr *E, TypeOfKind Kind) { if (const TagType *TT = T->getAs()) DiagnoseUseOfDecl(TT->getDecl(), E->getExprLoc()); } + + /*TO_UPSTREAM(BoundsSafety) ON*/ + // We don't have enough context to create a DBPT with coupled declarations + // from here as we need to know the scope of the new declaration (if this is + // for a new declaration). Error out unless `getNumCoupledDecls()` is 0, + // as DBPTs without coupled decls are not scope-dependent. + if (auto DBPT = E->getType()->getAs()) { + if (DBPT->getNumCoupledDecls() != 0) { + Diag(E->getExprLoc(), diag::err_bounds_safety_typeof_dbpt) << E->getType(); + // return a bare pointer for recovery purposes + return QualType(E->getType()->getAs(), 0); + } + } + /*TO_UPSTREAM(BoundsSafety) OFF*/ return Context.getTypeOfExprType(E, Kind); } +/* TO_UPSTREAM(BoundsSafety) ON */ +namespace { + +Expr *UnwrapDerefAddrOfPairs(Expr *E) { + UnaryOperator *UO = dyn_cast(E); + if (!UO) + return E; + switch (UO->getOpcode()) { + case UO_Deref: + UO = dyn_cast(UO->getSubExpr()->IgnoreParens()); + if (!UO || UO->getOpcode() != UO_AddrOf) + return E; + break; + case UO_AddrOf: + UO = dyn_cast(UO->getSubExpr()->IgnoreParens()); + if (!UO || UO->getOpcode() != UO_Deref) + return E; + break; + default: + return E; + } + /// XXX: This way we are leaving out '*((int *)&ch)'. + return UnwrapDerefAddrOfPairs(UO->getSubExpr()->IgnoreParens()); +} + +/// CountArgChecker - performs sanity checks for an argument in +/// '__counted_by()' or '__sized_by()', and returns a constant-folded +/// count expression if feasible. +class CountArgChecker : public TreeTransform { + using BaseTransform = TreeTransform; + using DeclList = llvm::SmallVector; + using DeclSet = llvm::SmallSet; + DeclList &Dependees; + bool CountInBytes : 1; + bool OrNull : 1; + bool ScopeCheck : 1; + bool IsArray : 1; + DeclSet Visited; + bool InDeref = false; + BinaryOperator *VisitedBinOp = nullptr; + +private: + ExprResult Fallback(Expr *E) { + if (auto CF = ConstantFoldOrNull(E)) + return CF; + SemaRef.Diag( + E->getExprLoc(), + diag:: + err_attribute_invalid_argument_expression_for_pointer_bounds_attribute); + return ExprError(); + } + + IntegerLiteral *ConstantFoldOrNull(Expr *E) { + if (E->isValueDependent()) + return nullptr; + + Expr::EvalResult Eval; + /// EvaluateAsInt is enough because nodes are visited in-order and + /// we require the final results be integer. Hence, cases like + /// '(int)(float)f' should work. + if (E->EvaluateAsInt(Eval, SemaRef.Context)) + return IntegerLiteral::Create(SemaRef.Context, Eval.Val.getInt(), + E->getType(), E->getExprLoc()); + return nullptr; + } + + BoundsAttributedType::BoundsAttrKind getDynamicCountKind() { + return CountInBytes ? (OrNull ? CountAttributedType::SizedByOrNull + : CountAttributedType::SizedBy) + : (OrNull ? CountAttributedType::CountedByOrNull + : CountAttributedType::CountedBy); + } + +public: + explicit CountArgChecker(Sema &S, DeclList &DList, bool CountInBytes, + bool OrNull, bool ScopeCheck, bool IsArray) + : BaseTransform(S), Dependees(DList), CountInBytes(CountInBytes), + OrNull(OrNull), ScopeCheck(ScopeCheck), IsArray(IsArray) {} + + ExprResult TransformExpr(Expr *E) { + switch (E->getStmtClass()) { + +#define TRANSFORM_EXPR(Node) \ + case Stmt::Node##Class: \ + return Transform##Node(cast(E)); + +#define FORWARD_EXPR(Node) \ + case Stmt::Node##Class: \ + return E; + + TRANSFORM_EXPR(CallExpr) + TRANSFORM_EXPR(ImplicitCastExpr) + TRANSFORM_EXPR(CStyleCastExpr) + TRANSFORM_EXPR(ParenExpr) + TRANSFORM_EXPR(BinaryOperator) + TRANSFORM_EXPR(UnaryOperator) + TRANSFORM_EXPR(DeclRefExpr) + TRANSFORM_EXPR(MemberExpr) + FORWARD_EXPR(IntegerLiteral) + FORWARD_EXPR(FloatingLiteral) + FORWARD_EXPR(FixedPointLiteral) + +#undef TRANSFORM_EXPR +#undef FORWARD_EXPR + default: + break; + } + return Fallback(E); + } + + ExprResult TransformCallExpr(CallExpr *E) { + const auto *Callee = E->getDirectCallee(); + if (Callee && Callee->hasAttr()) { + for (auto *Arg : E->arguments()) { + if (!Arg->isEvaluatable(SemaRef.Context)) { + SemaRef.Diag(E->getExprLoc(), + diag::err_bounds_safety_dynamic_count_function_call_argument) + << E << getDynamicCountKind(); + return ExprError(); + } + } + return E; + } + SemaRef.Diag(E->getExprLoc(), + diag::err_bounds_safety_dynamic_count_function_call) + << getDynamicCountKind(); + if (Callee) { + SemaRef.Diag(Callee->getLocation(), diag::note_callee_decl) << Callee; + } + return ExprError(); + } + + ExprResult TransformImplicitCastExpr(ImplicitCastExpr *E) { + return BaseTransform::TransformImplicitCastExpr(E); + } + + ExprResult TransformCStyleCastExpr(CStyleCastExpr *E) { + Expr::EvalResult Eval; + /// EvaluateAsInt is enough because nodes are visited in-order and + /// we require the final results be integer. Hence, cases like + /// '(int)(float)f' should work. + if (!E->EvaluateAsInt(Eval, SemaRef.Context)) + return BaseTransform::TransformCStyleCastExpr(E); + return IntegerLiteral::Create(SemaRef.Context, Eval.Val.getInt(), + E->getType(), E->getExprLoc()); + ; + } + + ExprResult TransformParenExpr(ParenExpr *E) { + if (auto CF = ConstantFoldOrNull(E)) + return CF; + return BaseTransform::TransformParenExpr(E); + } + + ExprResult TransformBinaryOperator(BinaryOperator *E) { + VisitedBinOp = E; + if (auto CF = ConstantFoldOrNull(E)) + return CF; + return BaseTransform::TransformBinaryOperator(E); + } + + /// Add support for UO_Deref in particular to support out parameters. + /// e.g., 'void foo(int *__counted_by(len)* out_buf, int len)' + ExprResult TransformUnaryOperator(UnaryOperator *E) { + if (auto CF = ConstantFoldOrNull(E)) + return CF; + + Expr *UnwrappedExpr = UnwrapDerefAddrOfPairs(E); + E = dyn_cast(UnwrappedExpr); + if (!E) + return TransformExpr(UnwrappedExpr); + + if (E->getOpcode() == UO_Deref && !InDeref) { + if (VisitedBinOp) + return Fallback(E); + SaveAndRestore InDerefLocal(InDeref, true); + Expr *SubExpr = E->getSubExpr()->IgnoreParenCasts(); + if (auto *DR = dyn_cast(SubExpr)) { + if (!isa(DR->getDecl()) || ScopeCheck) { + SemaRef.Diag(E->getExprLoc(), + diag::err_deref_in_bounds_safety_count_non_parm_decl) + << getDynamicCountKind(); + return E; + } + return BaseTransform::TransformUnaryOperator(E); + } + return Fallback(E); + } + if (E->isArithmeticOp()) + return BaseTransform::TransformUnaryOperator(E); + + return Fallback(E); + } + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + if (auto CF = ConstantFoldOrNull(E)) + return CF; + + ValueDecl *VD = E->getDecl(); + bool IsNewVD = Visited.insert(VD).second; + if (IsNewVD) { + Dependees.push_back(TypeCoupledDeclRefInfo(VD, InDeref)); + } + return E; + } + + ExprResult TransformMemberExpr(MemberExpr *E) { + if (auto CF = ConstantFoldOrNull(E)) + return CF; + + const Expr *Base = E->getBase(); + + // For structs/classes in C++, referring to a field creates a MemberExpr + // instead of DeclRefExpr. To support other parts of bounds-safety logic, + // replace the MemberExpr by DeclRefExpr. + // TODO: Add support for MemberExpr (rdar://134311605). + if (SemaRef.getLangOpts().CPlusPlus && + // The MemberExprs that are allowed in C++ but not in C are those having + // `this->` implicitly or explicitly as their bases. + (Base->isImplicitCXXThis() || + isa(Base->IgnoreParenImpCasts()))) { + ValueDecl *VD = E->getMemberDecl(); + bool IsNewVD = Visited.insert(VD).second; + if (IsNewVD) + Dependees.push_back(TypeCoupledDeclRefInfo(VD, InDeref)); + Expr *DRE = DeclRefExpr::Create( + SemaRef.Context, NestedNameSpecifierLoc{}, SourceLocation{}, VD, + false, DeclarationNameInfo{VD->getDeclName(), VD->getLocation()}, + VD->getType(), VK_LValue); + return DRE; + } + + if (!IsArray) { + SemaRef.Diag( + E->getExprLoc(), + diag:: + err_attribute_invalid_argument_expression_for_pointer_bounds_attribute); + SemaRef.Diag(E->getOperatorLoc(), + diag::note_bounds_safety_struct_fields_only_in_fam); + return ExprError(); + } + + if (E->isArrow()) { + SemaRef.Diag(E->getExprLoc(), diag::error_bounds_safety_no_arrow_members); + return ExprError(); + } + + // Recursive call adds base decls to Dependees, i.e. for `a.b.c` it adds `a` + // and `b`. `c` is added by the call to Dependees.push_back() further down. + ExprResult BaseRes = TransformExpr(E->getBase()); + if (BaseRes.isInvalid()) + return ExprError(); + + if (BaseRes.get()->getType()->isUnionType()) { + SemaRef.Diag(E->getExprLoc(), diag::error_bounds_safety_no_count_in_unions) + << BaseRes.get() << BaseRes.get()->getType(); + return ExprError(); + } + + ValueDecl *VD = E->getMemberDecl(); + bool IsNewVD = Visited.insert(VD).second; + if (IsNewVD) { + Dependees.push_back( + TypeCoupledDeclRefInfo(VD, InDeref, /* Member */ true)); + } + + return E; + } +}; + +/// RangeArgChecker - performs sanity checks for an argument in '__ended_by()'. +/// Moreover, this checker extracts the dependent decl in the 'end' expression +/// of '__ended_by(end)'. +class RangeArgChecker : public TreeTransform { + using BaseTransform = TreeTransform; + std::optional DependeeInfo; + bool ScopeCheck; + bool InDeref = false; + + void setDependeeInfo(ValueDecl *VD, bool Deref) { + assert(!DependeeInfo.has_value()); + DependeeInfo = TypeCoupledDeclRefInfo(VD, Deref); + } + +public: + explicit RangeArgChecker(Sema &SemaRef, bool ScopeCheck) + : BaseTransform(SemaRef), ScopeCheck(ScopeCheck) {} + + ExprResult TransformExpr(Expr *E) { + switch (E->getStmtClass()) { + +#define TRANSFORM_EXPR(Node) \ + case Stmt::Node##Class: \ + return Transform##Node(cast(E)); + + TRANSFORM_EXPR(ImplicitCastExpr) + TRANSFORM_EXPR(ParenExpr) + TRANSFORM_EXPR(UnaryOperator) + TRANSFORM_EXPR(DeclRefExpr) + TRANSFORM_EXPR(MemberExpr) + +#undef TRANSFORM_EXPR + default: + break; + } + + return HandleInvalidExpr(E); + } + + ExprResult HandleInvalidExpr(Expr *E) { + SemaRef.Diag( + E->getExprLoc(), + diag:: + err_attribute_invalid_argument_expression_for_pointer_bounds_attribute); + return ExprError(); + } + + ExprResult TransformImplicitCastExpr(ImplicitCastExpr *E) { + return BaseTransform::TransformImplicitCastExpr(E); + } + + ExprResult TransformParenExpr(ParenExpr *E) { + return BaseTransform::TransformParenExpr(E); + } + + ExprResult TransformUnaryOperator(UnaryOperator *E) { + Expr *UnwrappedExpr = UnwrapDerefAddrOfPairs(E); + E = dyn_cast(UnwrappedExpr); + if (!E) + return TransformExpr(UnwrappedExpr); + + // No support for unary operators other than UO_Deref nor multiple UO_Deref. + if (E->getOpcode() != UO_Deref || InDeref) + return HandleInvalidExpr(E); + + if (ScopeCheck) { + // We support deref operator for parameters, check if the `end` decl is + // also a parameter. + const auto *DRE = + dyn_cast(E->getSubExpr()->IgnoreParenCasts()); + if (!DRE || !isa(DRE->getDecl())) { + SemaRef.Diag(E->getExprLoc(), + diag::err_deref_in_bounds_safety_count_non_parm_decl) + << 4 /* __ended_by */; + // Return ExprError() instead of calling HandleInvalidExpr(E) to avoid + // emitting another error. + return ExprError(); + } + } + + SaveAndRestore InDerefLocal(InDeref, true); + return BaseTransform::TransformUnaryOperator(E); + } + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + // If the end pointer is not __single because of our implicit pointer + // annotation, we fix that during rebuilding the type of the end pointer. + QualType Ty = E->getType(); + if (!Ty->isSinglePointerType() && !Ty->isUnspecifiedPointerType() && + !Ty->isDynamicRangePointerType() && !Ty->hasAttr(attr::PtrAutoAttr)) { + SemaRef.Diag(E->getExprLoc(), diag::err_bounds_safety_end_pointer_single); + return ExprError(); + } + + setDependeeInfo(E->getDecl(), InDeref); + return E; + } + + ExprResult TransformMemberExpr(MemberExpr *E) { + // Using `__ended_by(end)` annotation on a struct/class member where `end` + // refers to another member creates a MemberExpr with an implicit this in + // C++. We don't support MemberExpr in general, so here we replace the + // MemberExpr with an implicit this by a DeclRefExpr to the field in order + // to have the same representation as in C. + + if (!E->isImplicitAccess()) + return HandleInvalidExpr(E); + + ValueDecl *VD = E->getMemberDecl(); + setDependeeInfo(VD, InDeref); + Expr *DRE = DeclRefExpr::Create( + SemaRef.Context, NestedNameSpecifierLoc{}, SourceLocation{}, VD, false, + DeclarationNameInfo{VD->getDeclName(), VD->getLocation()}, + VD->getType(), VK_LValue); + return DRE; + } + + TypeCoupledDeclRefInfo getDependeeInfo() const { + return DependeeInfo.value(); + } +}; + +} // end anonymous namespace + +QualType Sema::BuildCountAttributedType(QualType PointerTy, Expr *CountExpr, + bool CountInBytes, bool OrNull, + bool ScopeCheck) { + + llvm::SmallVector Decls; + ExprResult R = CountArgChecker(*this, Decls, CountInBytes, OrNull, ScopeCheck, + PointerTy->isArrayType()) + .TransformExpr(CountExpr); + /// When the resulting expression is invalid, we still create the AST using + /// the original count expression for the sake of AST dump. + if (!R.isInvalid()) + CountExpr = R.get(); + R = DefaultLvalueConversion(CountExpr); + if (!R.isInvalid()) + CountExpr = R.get(); + + if (!getLangOpts().isBoundsSafetyAttributeOnlyMode() && + !PointerTy->isSinglePointerType()) { + PointerTy = Context.getBoundsSafetyPointerType( + PointerTy, BoundsSafetyPointerAttributes::single()); + } + + return Context.getCountAttributedType( + PointerTy, CountExpr, CountInBytes, OrNull, + llvm::ArrayRef(Decls.begin(), Decls.end())); +} + +namespace { + +QualType DropAutoNullTerminated(ASTContext &Ctx, QualType T) { + if (!T->isImplicitlyNullTerminatedType(Ctx)) + return T; + + std::function DropPtrAutoNullTerminatedAttr; + DropPtrAutoNullTerminatedAttr = [&](QualType T) -> QualType { + const auto *AT = T->getAs(); + if (!AT) { + auto *VTT = T->getAs(); + assert(VTT); + return VTT->desugar(); + } + + QualType ModifiedTy = DropPtrAutoNullTerminatedAttr(AT->getModifiedType()); + if (AT->getAttrKind() == attr::PtrAutoNullTerminatedAttr) { + return ModifiedTy; + } + QualType EquivalentTy = + DropPtrAutoNullTerminatedAttr(AT->getEquivalentType()); + return Ctx.getAttributedType(AT->getAttrKind(), ModifiedTy, EquivalentTy); + }; + + return DropPtrAutoNullTerminatedAttr(T); +} + +} // namespace + +QualType Sema::BuildDynamicRangePointerType(QualType PointerTy, Expr *StartPtr, + Expr *EndPtr, bool ScopeCheck) { + std::optional StartDecl, EndDecl; + + if (StartPtr) { + RangeArgChecker RAC(*this, ScopeCheck); + ExprResult Res = RAC.TransformExpr(StartPtr); + if (Res.isInvalid()) + return QualType(); + StartPtr = Res.get(); + StartDecl = RAC.getDependeeInfo(); + } + + if (EndPtr) { + RangeArgChecker RAC(*this, ScopeCheck); + ExprResult Res = RAC.TransformExpr(EndPtr); + if (Res.isInvalid()) + return QualType(); + EndPtr = Res.get(); + EndDecl = RAC.getDependeeInfo(); + } + + if (!getLangOpts().isBoundsSafetyAttributeOnlyMode() && + !PointerTy->isSinglePointerType()) { + PointerTy = Context.getBoundsSafetyPointerType( + PointerTy, BoundsSafetyPointerAttributes::single()); + } + + if (EndDecl.has_value()) { + ValueDecl *VD = EndDecl->getDecl(); + QualType Ty = VD->getType(); + if (Ty->hasAttr(attr::PtrAutoNullTerminatedAttr) && + Ty->isValueTerminatedType()) { + assert(Ty->getAs() + ->getTerminatorValue(Context) + .isZero()); + QualType NewTy = DropAutoNullTerminated(Context, Ty); + VD->setType(NewTy); + } + } + + return Context.getDynamicRangePointerType( + PointerTy, StartPtr, EndPtr, + StartDecl.has_value() ? ArrayRef(*StartDecl) + : ArrayRef(), + EndDecl.has_value() ? ArrayRef(*EndDecl) + : ArrayRef()); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + static void BuildTypeCoupledDecls(Expr *E, llvm::SmallVectorImpl &Decls) { @@ -9814,10 +11263,14 @@ QualType Sema::BuiltinEnumUnderlyingType(QualType BaseType, } QualType Sema::BuiltinAddPointer(QualType BaseType, SourceLocation Loc) { + /* TO_UPSTREAM(BoundsSafety) ON */ + // Declare and pass `BoundsSafetyPointerAttributes A` + BoundsSafetyPointerAttributes A; QualType Pointer = BaseType.isReferenceable() || BaseType->isVoidType() - ? BuildPointerType(BaseType.getNonReferenceType(), Loc, - DeclarationName()) + ? BuildPointerType(BaseType.getNonReferenceType(), A, + Loc, DeclarationName()) : BaseType; + /* TO_UPSTREAM(BoundsSafety) OFF */ return Pointer.isNull() ? QualType() : Pointer; } @@ -10066,6 +11519,28 @@ QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) { return QualType(); } + /* TO_UPSTREAM(BoundsSafety) ON*/ + if (LangOpts.BoundsSafety) { + assert(!T->isBoundsAttributedType()); + int DiagIndex = -1; + if (T->isValueTerminatedType()) { + DiagIndex = 7; + } else if (const auto *PT = T->getAs()) { + BoundsSafetyPointerAttributes FAttr = PT->getPointerAttributes(); + if (!FAttr.isUnspecified() && !FAttr.isSingle() && + !FAttr.isUnsafeIndexable()) { + assert(FAttr.isIndexable() || FAttr.isBidiIndexable()); + DiagIndex = FAttr.isIndexable() ? 0 : 1; + } + } + if (DiagIndex != -1) { + Diag(Loc, diag::err_bounds_safety_atomic_unsupported_attribute) + << DiagIndex; + return QualType(); + } + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ + // FIXME: Do we need any handling for ARC here? } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index b6af919463124..bd718abfafaca 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -137,6 +137,12 @@ class TreeTransform { /// rather than in the subclass (e.g., lambda closure types). llvm::DenseMap TransformedLocalDecls; + /* TO_UPSTREAM(BoundsSafety) ON*/ + /// When MaterializeSequenceExpr is stripped, its children OpaqueValueExprs + /// should also be removed and all the subexpressions of the OVEs should + /// also be unwrapped. + llvm::SmallPtrSet RemovedOVEs; + /* TO_UPSTREAM(BoundsSafety) OFF*/ public: /// Initializes a new tree transformer. TreeTransform(Sema &SemaRef) : SemaRef(SemaRef) { } @@ -839,7 +845,9 @@ class TreeTransform { /// /// By default, performs semantic analysis when building the pointer type. /// Subclasses may override this routine to provide different behavior. - QualType RebuildPointerType(QualType PointeeType, SourceLocation Sigil); + QualType RebuildPointerType(QualType PointeeType, + BoundsSafetyPointerAttributes Attr, + SourceLocation Sigil); /// Build a new block pointer type given its pointee type. /// @@ -3015,9 +3023,13 @@ class TreeTransform { /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. ExprResult RebuildCompoundLiteralExpr(SourceLocation LParenLoc, - TypeSourceInfo *TInfo, - SourceLocation RParenLoc, - Expr *Init) { + TypeSourceInfo *TInfo, + SourceLocation RParenLoc, Expr *Init, + /*TO_UPSTREAM(BoundsSafety) ON */ + bool IsFileScope + /*TO_UPSTREAM(BoundsSafety) OFF */ + ) { + return getSema().BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, Init); } @@ -4084,6 +4096,59 @@ class TreeTransform { return getSema().CreateRecoveryExpr(BeginLoc, EndLoc, SubExprs, Type); } + /// Build a BoundsSafety '__builtin_unsafe_forge_bidi_indexable' or + /// '__builtin_forge_single' expression. + ExprResult RebuildForgePtrExpr(SourceLocation KWLoc, QualType ResultType, + Expr *Addr, Expr *Size, + SourceLocation RParenLoc) { + return getSema().BuildForgePtrExpr(KWLoc, RParenLoc, ResultType, Addr, + Size); + } + + /// Build a BoundsSafety '__builtin_get_pointer_{lower,upper}_bound' expression. + ExprResult RebuildGetBoundExpr(SourceLocation BeginLoc, SourceLocation EndLoc, + Expr *SubExpr, GetBoundExpr::BoundKind Kind, + bool RawPointer) { + return Kind == GetBoundExpr::BK_Lower + ? getSema().BuildLowerBoundExpr(SubExpr, BeginLoc, EndLoc, RawPointer) + : getSema().BuildUpperBoundExpr(SubExpr, BeginLoc, EndLoc, RawPointer); + } + + ExprResult + RebuildPredefinedBoundsCheckExpr(Expr *GuardedExpr, BoundsCheckKind Kind, + llvm::ArrayRef CheckArgs) { + return getSema().BuildPredefinedBoundsCheckExpr(GuardedExpr, Kind, + CheckArgs); + } + + /// Build a BoundsSafety bounds check expression. + ExprResult + RebuildBoundsCheckExpr(Expr *GuardedExpr, Expr *Cond, + llvm::ArrayRef CommonExprs) { + return getSema().BuildBoundsCheckExpr(GuardedExpr, Cond, CommonExprs); + } + + ExprResult RebuildMaterializeSequenceExpr(Expr *Wrapped, + ArrayRef Values) { + return getSema().BuildMaterializeSequenceExpr(Wrapped, Values); + } + + ExprResult RebuildTerminatedByToIndexableExpr(SourceLocation BeginLoc, + SourceLocation EndLoc, + Expr *PointerExpr, + Expr *TerminatorExpr, + bool IncludeTerminator) { + return getSema().BuildTerminatedByToIndexableExpr( + PointerExpr, TerminatorExpr, IncludeTerminator, BeginLoc, EndLoc); + } + + ExprResult RebuildTerminatedByFromIndexableExpr( + SourceLocation BeginLoc, SourceLocation EndLoc, Expr *TerminatorExpr, + Expr *PointerExpr, Expr *PointerToTerminatorExpr) { + return getSema().BuildTerminatedByFromIndexableExpr( + TerminatorExpr, PointerExpr, PointerToTerminatorExpr, BeginLoc, EndLoc); + } + StmtResult RebuildOpenACCComputeConstruct(OpenACCDirectiveKind K, SourceLocation BeginLoc, SourceLocation DirLoc, @@ -5523,7 +5588,8 @@ QualType TreeTransform::TransformPointerType(TypeLocBuilder &TLB, if (getDerived().AlwaysRebuild() || PointeeType != TL.getPointeeLoc().getType()) { - Result = getDerived().RebuildPointerType(PointeeType, TL.getSigilLoc()); + Result = getDerived().RebuildPointerType( + PointeeType, TL.getPointerAttributes(), TL.getSigilLoc()); if (Result.isNull()) return QualType(); } @@ -7618,15 +7684,119 @@ QualType TreeTransform::TransformCountAttributedType( QualType Result = TL.getType(); if (getDerived().AlwaysRebuild() || InnerTy != OldTy->desugar() || OldCount != NewCount) { - // Currently, CountAttributedType can only wrap incomplete array types. - Result = SemaRef.BuildCountAttributedArrayOrPointerType( - InnerTy, NewCount, OldTy->isCountInBytes(), OldTy->isOrNull()); + /* TO_UPSTREAM(BoundsSafety) ON */ + if (SemaRef.getLangOpts().BoundsSafetyAttributes) { + Result = SemaRef.BuildCountAttributedType( + InnerTy, NewCount, OldTy->isCountInBytes(), OldTy->isOrNull()); + } else { + /* TO_UPSTREAM(BoundsSafety) OFF */ + // Currently, CountAttributedType can only wrap incomplete array types. + Result = SemaRef.BuildCountAttributedArrayOrPointerType( + InnerTy, NewCount, OldTy->isCountInBytes(), OldTy->isOrNull()); + } } TLB.push(Result); return Result; } +/* TO_UPSTREAM(BoundsSafety) ON */ +template +QualType TreeTransform::TransformDynamicRangePointerType( + TypeLocBuilder &TLB, DynamicRangePointerTypeLoc TL) { + const DynamicRangePointerType *OldTy = TL.getTypePtr(); + QualType PointerTy = getDerived().TransformType(TLB, TL.getInnerLoc()); + if (PointerTy.isNull()) + return QualType(); + + Expr *OldStartPtr = TL.getStartPointer(); + Expr *NewStartPtr = nullptr; + if (OldStartPtr) { + using ExpressionKind = + Sema::ExpressionEvaluationContextRecord::ExpressionKind; + EnterExpressionEvaluationContext EC( + SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, + nullptr, ExpressionKind::EK_AttrArgument); + + ExprResult StartPtrResult = getDerived().TransformExpr(OldStartPtr); + if (StartPtrResult.isInvalid()) + return QualType(); + NewStartPtr = StartPtrResult.get(); + } + + Expr *OldEndPtr = TL.getEndPointer(); + Expr *NewEndPtr = nullptr; + if (OldEndPtr) { + using ExpressionKind = + Sema::ExpressionEvaluationContextRecord::ExpressionKind; + EnterExpressionEvaluationContext EC( + SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, + nullptr, ExpressionKind::EK_AttrArgument); + + ExprResult EndPtrResult = getDerived().TransformExpr(OldEndPtr); + if (EndPtrResult.isInvalid()) + return QualType(); + NewEndPtr = EndPtrResult.get(); + } + + QualType Result = TL.getType(); + if (getDerived().AlwaysRebuild() || PointerTy != OldTy->desugar() || + OldStartPtr != NewStartPtr || OldEndPtr != NewEndPtr) { + Result = + SemaRef.BuildDynamicRangePointerType(PointerTy, NewStartPtr, NewEndPtr); + if (Result.isNull()) + return QualType(); + } + + TLB.push(Result); + return Result; +} + +template +QualType TreeTransform::TransformValueTerminatedType( + TypeLocBuilder &TLB, ValueTerminatedTypeLoc TL) { + const ValueTerminatedType *OldTy = TL.getTypePtr(); + QualType OriginalTy = getDerived().TransformType(TLB, TL.getOriginalLoc()); + if (OriginalTy.isNull() || + !(OriginalTy->isConstantArrayType() || + OriginalTy->isIncompleteArrayType() || OriginalTy->isPointerType()) || + OriginalTy->isValueTerminatedType()) { + return QualType(); + } + + Expr *OldTerminator = TL.getTerminatorExpr(); + Expr *NewTerminator = nullptr; + if (OldTerminator) { + ExprResult TransformRes = getDerived().TransformExpr(OldTerminator); + if (!TransformRes.get()) + return QualType(); + NewTerminator = TransformRes.get(); + + QualType PET; + if (const auto *PT = OriginalTy->getAs()) + PET = PT->getPointeeType(); + else + PET = SemaRef.Context.getAsArrayType(OriginalTy)->getElementType(); + + ExprResult CastRes(NewTerminator); + CastKind Kind = SemaRef.PrepareScalarCast(CastRes, PET); + CastRes = SemaRef.ImpCastExprToType(NewTerminator, PET, Kind); + if (!CastRes.get()) + return QualType(); + NewTerminator = CastRes.get(); + } + + QualType Result = TL.getType(); + if (getDerived().AlwaysRebuild() || OriginalTy != OldTy->desugar() || + OldTerminator != NewTerminator) { + Result = SemaRef.Context.getValueTerminatedType(OriginalTy, NewTerminator); + } + + TLB.push(Result); + return Result; +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + template QualType TreeTransform::TransformBTFTagAttributedType( TypeLocBuilder &TLB, BTFTagAttributedTypeLoc TL) { @@ -13695,10 +13865,12 @@ TreeTransform::TransformCompoundLiteralExpr(CompoundLiteralExpr *E) { // Note: the expression type doesn't necessarily match the // type-as-written, but that's okay, because it should always be // derivable from the initializer. - + /* TO_UPSTREAM(BoundsSafety) ON */ return getDerived().RebuildCompoundLiteralExpr( E->getLParenLoc(), NewT, - /*FIXME:*/ E->getInitializer()->getEndLoc(), Init.get()); + /*FIXME:*/ E->getInitializer()->getEndLoc(), Init.get(), + E->isFileScope()); + /* TO_UPSTREAM(BoundsSafety) OFF */ } template @@ -17049,14 +17221,149 @@ TreeTransform::TransformAtomicExpr(AtomicExpr *E) { E->getOp(), E->getRParenLoc()); } +/* TO_UPSTREAM(BoundsSafety) ON */ +template +ExprResult +TreeTransform::TransformBoundsSafetyPointerPromotionExpr(BoundsSafetyPointerPromotionExpr *E) { + // Pointer promotion expressions, like implicit casts, are eliminated during + // transformation, since they will be recomputed by semantic analysis after + // transformation. + return getDerived().TransformExpr(E->getSubExprAsWritten()); +} + +template +ExprResult +TreeTransform::TransformAssumptionExpr(AssumptionExpr *E) { + // Assumption expressions, like implicit casts, are eliminated during + // transformation, since they will be recomputed by semantic analysis after + // transformation. + return getDerived().TransformExpr(E->getWrappedExpr()); +} + +template +ExprResult TreeTransform::TransformForgePtrExpr(ForgePtrExpr *E) { + ExprResult Addr = getDerived().TransformExpr(E->getAddr()); + if (Addr.isInvalid()) + return ExprError(); + + ExprResult Size = ExprEmpty(); + if (Expr *OldSize = E->getSize()) { + Size = getDerived().TransformExpr(OldSize); + if (Size.isInvalid()) + return ExprError(); + } + + if (Addr.get() == E->getAddr() && Size.get() == E->getSize()) + return E; + + return getDerived().RebuildForgePtrExpr(E->getBeginLoc(), E->getType(), + Addr.get(), Size.get(), + E->getEndLoc()); +} + +template +ExprResult TreeTransform::TransformGetBoundExpr(GetBoundExpr *E) { + ExprResult Addr = getDerived().TransformExpr(E->getSubExpr()); + if (Addr.isInvalid()) + return ExprError(); + + return getDerived().RebuildGetBoundExpr(E->getBeginLoc(), E->getEndLoc(), + Addr.get(), E->getBoundKind(), + !E->getType()->isPointerTypeWithBounds()); +} + +template +ExprResult TreeTransform::TransformPredefinedBoundsCheckExpr( + PredefinedBoundsCheckExpr *E) { + return getDerived().TransformExpr(E->getGuardedExpr()); +} + +template +ExprResult +TreeTransform::TransformBoundsCheckExpr(BoundsCheckExpr *E) { + for (auto *OVE : E->opaquevalues()) { + RemovedOVEs.insert(OVE); + } + + ExprResult NewGuarded = getDerived().TransformExpr(E->getGuardedExpr()); + + for (auto *OVE : E->opaquevalues()) { + RemovedOVEs.erase(OVE); + } + return NewGuarded; +} + +template +ExprResult +TreeTransform::TransformMaterializeSequenceExpr(MaterializeSequenceExpr *E) { + if (E->isBinding()) { + for (auto *OVE : E->opaquevalues()) { + RemovedOVEs.insert(OVE); + } + } + ExprResult NewWrap = getDerived().TransformExpr(E->getWrappedExpr()); + + if (E->isUnbinding()) { + for (auto *OVE : E->opaquevalues()) { + RemovedOVEs.erase(OVE); + } + } + return NewWrap; +} + +template +ExprResult TreeTransform::TransformTerminatedByToIndexableExpr( + TerminatedByToIndexableExpr *E) { + ExprResult Pointer = getDerived().TransformExpr(E->getPointer()); + if (Pointer.isInvalid()) + return ExprError(); + + Expr *Terminator = E->getTerminator(); + if (Terminator) { + ExprResult Res = getDerived().TransformExpr(Terminator); + if (Res.isInvalid()) + return ExprError(); + Terminator = Res.get(); + } + + return getDerived().RebuildTerminatedByToIndexableExpr( + E->getBeginLoc(), E->getEndLoc(), Pointer.get(), Terminator, + E->includesTerminator()); +} + +template +ExprResult TreeTransform::TransformTerminatedByFromIndexableExpr( + TerminatedByFromIndexableExpr *E) { + const auto *VTT = cast(E->getType()); + + ExprResult Pointer = getDerived().TransformExpr(E->getPointer()); + if (Pointer.isInvalid()) + return ExprError(); + + Expr *PointerToTerminator = E->getPointerToTerminator(); + if (PointerToTerminator) { + ExprResult Res = getDerived().TransformExpr(PointerToTerminator); + if (Res.isInvalid()) + return ExprError(); + PointerToTerminator = Res.get(); + } + + return getDerived().RebuildTerminatedByFromIndexableExpr( + E->getBeginLoc(), E->getEndLoc(), VTT->getTerminatorExpr(), Pointer.get(), + PointerToTerminator); +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + //===----------------------------------------------------------------------===// // Type reconstruction //===----------------------------------------------------------------------===// -template -QualType TreeTransform::RebuildPointerType(QualType PointeeType, - SourceLocation Star) { - return SemaRef.BuildPointerType(PointeeType, Star, +template +QualType +TreeTransform::RebuildPointerType(QualType PointeeType, + BoundsSafetyPointerAttributes Attr, + SourceLocation Star) { + return SemaRef.BuildPointerType(PointeeType, Attr, Star, getDerived().getBaseEntity()); } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index f1ed67afaab7a..75a116c49e8a1 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -261,6 +261,21 @@ bool ChainedASTReaderListener::visitInputFile(StringRef Filename, isExplicitModule); return Continue; } +bool ChainedASTReaderListener::readCASFileSystemRootID(StringRef RootID, + bool Complain) { + return First->readCASFileSystemRootID(RootID, Complain) || + Second->readCASFileSystemRootID(RootID, Complain); +} +bool ChainedASTReaderListener::readIncludeTreeID(StringRef ID, bool Complain) { + return First->readIncludeTreeID(ID, Complain) || + Second->readIncludeTreeID(ID, Complain); +} +bool ChainedASTReaderListener::readModuleCacheKey(StringRef ModuleName, + StringRef Filename, + StringRef CacheKey) { + return First->readModuleCacheKey(ModuleName, Filename, CacheKey) || + Second->readModuleCacheKey(ModuleName, Filename, CacheKey); +} void ChainedASTReaderListener::readModuleFileExtension( const ModuleFileExtensionMetadata &Metadata) { @@ -2828,6 +2843,10 @@ InputFile ASTReader::getInputFile(ModuleFile &F, unsigned ID, bool Complain) { Change MTimeChange = {Change::ModTime, StoredTime, File->getModificationTime()}; + // FIXME: Ignore ModificationTime == 0 because that is from CAS. + if (File->getModificationTime() == 0) + return Change{Change::None}; + // In case the modification time changes but not the content, // accept the cached file as legit. if (ValidateASTInputFilesContent) @@ -3005,14 +3024,6 @@ ASTReader::ASTReadResult ASTReader::ReadOptionsBlock( break; } - case FILE_SYSTEM_OPTIONS: { - bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch) == 0; - if (!AllowCompatibleConfigurationMismatch && - ParseFileSystemOptions(Record, Complain, Listener)) - Result = ConfigurationMismatch; - break; - } - case HEADER_SEARCH_OPTIONS: { bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch) == 0; if (!AllowCompatibleConfigurationMismatch && @@ -3288,6 +3299,7 @@ ASTReader::ReadControlBlock(ModuleFile &F, std::string ImportedFile; std::string StoredFile; bool IgnoreImportedByNote = false; + StringRef ImportedCacheKey; // For prebuilt and explicit modules first consult the file map for // an override. Note that here we don't search prebuilt module @@ -3334,6 +3346,19 @@ ASTReader::ReadControlBlock(ModuleFile &F, IgnoreImportedByNote = true; } } + + ImportedCacheKey = ReadStringBlob(Record, Idx, Blob); + } + + if (!ImportedCacheKey.empty()) { + if (!Listener || Listener->readModuleCacheKey( + ImportedName, ImportedFile, ImportedCacheKey)) { + Diag(diag::err_ast_file_not_found) + << moduleKindForDiagnostic(ImportedKind) << ImportedFile << true + << ("missing or unloadable module cache key" + ImportedCacheKey) + .str(); + return Failure; + } } // If our client can't cope with us being out of date, we can't cope with @@ -3459,10 +3484,101 @@ ASTReader::ReadControlBlock(ModuleFile &F, F.InputFileInfosLoaded.resize(NumInputs); F.NumUserInputFiles = NumUserInputs; break; + + case MODULE_CACHE_KEY: + F.ModuleCacheKey = Blob.str(); + break; + + case CASFS_ROOT_ID: + F.CASFileSystemRootID = Blob.str(); + if (Listener) { + bool Complain = + !canRecoverFromOutOfDate(F.FileName, ClientLoadCapabilities); + if (Listener->readCASFileSystemRootID(F.CASFileSystemRootID, Complain)) + return OutOfDate; + } + break; + case CAS_INCLUDE_TREE_ID: + F.IncludeTreeID = Blob.str(); + if (Listener) { + bool Complain = + !canRecoverFromOutOfDate(F.FileName, ClientLoadCapabilities); + if (Listener->readIncludeTreeID(F.IncludeTreeID, Complain)) + return OutOfDate; + } + break; } } } +AvailabilityDomainsTableReaderTrait::hash_value_type +AvailabilityDomainsTableReaderTrait::ComputeHash(internal_key_type Key) { + return llvm::djbHash(Key); +} + +std::pair +AvailabilityDomainsTableReaderTrait::ReadKeyDataLength(const uint8_t *&Data) { + unsigned KeyLength = + llvm::support::endian::readNext(Data); + unsigned DataLength = + llvm::support::endian::readNext(Data); + return {KeyLength, DataLength}; +} + +AvailabilityDomainsTableReaderTrait::internal_key_type +AvailabilityDomainsTableReaderTrait::ReadKey(const uint8_t *Data, + unsigned Length) { + return llvm::StringRef(reinterpret_cast(Data), Length); +} + +AvailabilityDomainsTableReaderTrait::data_type +AvailabilityDomainsTableReaderTrait::ReadData(internal_key_type Key, + const uint8_t *Data, + unsigned Length) { + return llvm::support::endian::readNext( + Data); +} + +class AvailabilityDomainVisitor { + std::optional D; + StringRef DomainName; + ASTReader &Reader; + +public: + explicit AvailabilityDomainVisitor(StringRef DomainName, ASTReader &Reader) + : DomainName(DomainName), Reader(Reader) {} + + bool operator()(ModuleFile &M) { + AvailabilityDomainLookupTable *Table = + static_cast(M.AvailabilityDomainTable); + + if (!Table) + return false; + + // Look in the on-disk hash table for an entry for this domain name. + AvailabilityDomainLookupTable::iterator Pos = Table->find(DomainName); + if (Pos == Table->end()) + return false; + + LocalDeclID LocalID = LocalDeclID::get(Reader, M, *Pos); + GlobalDeclID ID = Reader.getGlobalDeclID(M, LocalID); + D = Reader.GetDecl(ID); + return true; + } + + std::optional getDecl() const { return D; } +}; + +Decl *ASTReader::getAvailabilityDomainDecl(StringRef DomainName) { + AvailabilityDomainVisitor Visitor(DomainName, *this); + ModuleMgr.visit(Visitor); + + if (std::optional D = Visitor.getDecl()) + return *D; + + return nullptr; +} + llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { BitstreamCursor &Stream = F.Stream; @@ -3805,6 +3921,14 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, UnusedFileScopedDecls.push_back(ReadDeclID(F, Record, I)); break; + case AVAILABILITY_DOMAIN_TABLE: { + auto Data = Blob.data(); + F.AvailabilityDomainTable = AvailabilityDomainLookupTable::Create( + (const unsigned char *)Data + Record[0], (const unsigned char *)Data, + AvailabilityDomainsTableReaderTrait()); + break; + } + case DELEGATING_CTORS: for (unsigned I = 0, N = Record.size(); I != N; /*in loop*/) DelegatingCtorDecls.push_back(ReadDeclID(F, Record, I)); @@ -4047,24 +4171,6 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, break; } - case PPD_SKIPPED_RANGES: { - F.PreprocessedSkippedRangeOffsets = (const PPSkippedRange*)Blob.data(); - assert(Blob.size() % sizeof(PPSkippedRange) == 0); - F.NumPreprocessedSkippedRanges = Blob.size() / sizeof(PPSkippedRange); - - if (!PP.getPreprocessingRecord()) - PP.createPreprocessingRecord(); - if (!PP.getPreprocessingRecord()->getExternalSource()) - PP.getPreprocessingRecord()->SetExternalSource(*this); - F.BasePreprocessedSkippedRangeID = PP.getPreprocessingRecord() - ->allocateSkippedRanges(F.NumPreprocessedSkippedRanges); - - if (F.NumPreprocessedSkippedRanges > 0) - GlobalSkippedRangeMap.insert( - std::make_pair(F.BasePreprocessedSkippedRangeID, &F)); - break; - } - case DECL_UPDATE_OFFSETS: if (Record.size() % 2 != 0) return llvm::createStringError( @@ -5170,6 +5276,11 @@ ASTReader::readUnhashedControlBlock(ModuleFile &F, bool WasImportedBy, return Failure; } + // FIXME: Should we check the signature even if DisableValidation? + if (PP.getLangOpts().NeededByPCHOrCompilationUsesPCH || DisableValidation || + (AllowConfigurationMismatch && Result == ConfigurationMismatch)) + return Success; + if (Result == OutOfDate && F.Kind == MK_ImplicitModule) { // If this module has already been finalized in the ModuleCache, we're stuck // with it; we can only load a single version of each module. @@ -5281,6 +5392,14 @@ ASTReader::ASTReadResult ASTReader::readUnhashedControlBlockImpl( Result = ConfigurationMismatch; break; } + case FILE_SYSTEM_OPTIONS: { + bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch) == 0; + if (Listener && !AllowCompatibleConfigurationMismatch && + ParseFileSystemOptions(Record, Complain, *Listener)) + Result = ConfigurationMismatch; + break; + } + case DIAG_PRAGMA_MAPPINGS: if (!F) break; @@ -5935,6 +6054,9 @@ bool ASTReader::readASTFileControlBlock( StringRef FilenameStr = ReadStringBlob(Record, Idx, Blob); auto Filename = ResolveImportedPath(PathBuf, FilenameStr, ModuleDir); + StringRef CacheKey = ReadStringBlob(Record, Idx, Blob); + if (!CacheKey.empty()) + Listener.readModuleCacheKey(ModuleName, *Filename, CacheKey); Listener.visitImport(ModuleName, *Filename); break; } @@ -6086,7 +6208,10 @@ llvm::Error ASTReader::ReadSubmoduleBlock(ModuleFile &F, break; case SUBMODULE_DEFINITION: { - if (Record.size() < 13) + // Factor this out into a separate constant to make it easier to resolve + // merge conflicts. + static const unsigned NUM_SWIFT_SPECIFIC_FIELDS = 1; + if (Record.size() < 13 + NUM_SWIFT_SPECIFIC_FIELDS) return llvm::createStringError(std::errc::illegal_byte_sequence, "malformed module definition"); @@ -6095,6 +6220,11 @@ llvm::Error ASTReader::ReadSubmoduleBlock(ModuleFile &F, SubmoduleID GlobalID = getGlobalSubmoduleID(F, Record[Idx++]); SubmoduleID Parent = getGlobalSubmoduleID(F, Record[Idx++]); Module::ModuleKind Kind = (Module::ModuleKind)Record[Idx++]; + + // SWIFT-SPECIFIC FIELDS HERE. Handling them separately helps avoid merge + // conflicts. See also NUM_SWIFT_SPECIFIC_FIELDS above. + bool IsSwiftInferImportAsMember = Record[Idx++]; + SourceLocation DefinitionLoc = ReadSourceLocation(F, Record[Idx++]); FileID InferredAllowedBy = ReadFileID(F, Record, Idx); bool IsFramework = Record[Idx++]; @@ -6146,6 +6276,8 @@ llvm::Error ASTReader::ReadSubmoduleBlock(ModuleFile &F, F.DidReadTopLevelSubmodule = true; CurrentModule->setASTFile(F.File); CurrentModule->PresumedModuleMapFile = F.ModuleMapPath; + if (!F.ModuleCacheKey.empty()) + CurrentModule->setModuleCacheKey(F.ModuleCacheKey); } CurrentModule->Kind = Kind; @@ -6168,6 +6300,11 @@ llvm::Error ASTReader::ReadSubmoduleBlock(ModuleFile &F, CurrentModule->ConfigMacrosExhaustive = ConfigMacrosExhaustive; CurrentModule->ModuleMapIsPrivate = ModuleMapIsPrivate; CurrentModule->NamedModuleHasInit = NamedModuleHasInit; + + // SWIFT-SPECIFIC FIELDS HERE. Putting them last helps avoid merge + // conflicts. + CurrentModule->IsSwiftInferImportAsMember = IsSwiftInferImportAsMember; + if (DeserializationListener) DeserializationListener->ModuleRead(GlobalID, CurrentModule); @@ -6577,20 +6714,6 @@ ASTReader::getModuleFileLevelDecls(ModuleFile &Mod) { Mod.FileSortedDecls + Mod.NumFileSortedDecls)); } -SourceRange ASTReader::ReadSkippedRange(unsigned GlobalIndex) { - auto I = GlobalSkippedRangeMap.find(GlobalIndex); - assert(I != GlobalSkippedRangeMap.end() && - "Corrupted global skipped range map"); - ModuleFile *M = I->second; - unsigned LocalIndex = GlobalIndex - M->BasePreprocessedSkippedRangeID; - assert(LocalIndex < M->NumPreprocessedSkippedRanges); - PPSkippedRange RawRange = M->PreprocessedSkippedRangeOffsets[LocalIndex]; - SourceRange Range(ReadSourceLocation(*M, RawRange.getBegin()), - ReadSourceLocation(*M, RawRange.getEnd())); - assert(Range.isValid()); - return Range; -} - PreprocessedEntity *ASTReader::ReadPreprocessedEntity(unsigned Index) { PreprocessedEntityID PPID = Index+1; std::pair PPInfo = getModulePreprocessedEntity(Index); @@ -7340,6 +7463,17 @@ void TypeLocReader::VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) { // Nothing to do } +/* TO_UPSTREAM(BoundsSafety) ON */ +void TypeLocReader::VisitDynamicRangePointerTypeLoc( + DynamicRangePointerTypeLoc TL) { + // nothing to do +} + +void TypeLocReader::VisitValueTerminatedTypeLoc(ValueTerminatedTypeLoc TL) { + // nothing to do +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void TypeLocReader::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) { // Nothing to do. } diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index b1bec20b40390..00e7e322205fc 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1474,6 +1474,8 @@ void ASTDeclReader::VisitObjCCategoryDecl(ObjCCategoryDecl *CD) { void ASTDeclReader::VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *CAD) { VisitNamedDecl(CAD); CAD->setClassInterface(readDeclAs()); + CAD->setClassInterfaceLoc(readSourceLocation()); + CAD->setAtLoc(readSourceLocation()); } void ASTDeclReader::VisitObjCPropertyDecl(ObjCPropertyDecl *D) { @@ -3248,7 +3250,7 @@ bool ASTReader::isConsumerInterestedIn(Decl *D) { // emitted when we import the relevant module. if (isPartOfPerModuleInitializer(D)) { auto *M = D->getImportedOwningModule(); - if (M && M->Kind == Module::ModuleMapModule && + if (M && M->isModuleMapModule() && getContext().DeclMustBeEmitted(D)) return false; } diff --git a/clang/lib/Serialization/ASTReaderInternals.h b/clang/lib/Serialization/ASTReaderInternals.h index 4a7794889b039..a537df41755c5 100644 --- a/clang/lib/Serialization/ASTReaderInternals.h +++ b/clang/lib/Serialization/ASTReaderInternals.h @@ -410,6 +410,38 @@ class HeaderFileInfoTrait { using HeaderFileInfoLookupTable = llvm::OnDiskChainedHashTable; +/// This trait class is used for searching the on-disk hash table that stores +/// feature availability information. The hash table maps feature names to Decl +/// IDs that corresponds to the variables declared in header files. +class AvailabilityDomainsTableReaderTrait { +public: + using internal_key_type = llvm::StringRef; + using external_key_type = internal_key_type; + using data_type = clang::serialization::DeclID; + using hash_value_type = uint32_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type Key) { return Key; } + external_key_type GetExternalKey(internal_key_type Key) { return Key; } + + hash_value_type ComputeHash(internal_key_type Key); + + static bool EqualKey(internal_key_type LHS, internal_key_type RHS) { + return LHS == RHS; + } + + static std::pair ReadKeyDataLength(const uint8_t *&Data); + + static internal_key_type ReadKey(const uint8_t *Data, unsigned Length); + + static data_type ReadData(internal_key_type Key, const uint8_t *Data, + unsigned Length); +}; + +/// The on-disk hash table used for availability domain lookup. +using AvailabilityDomainLookupTable = + llvm::OnDiskChainedHashTable; + } // namespace reader } // namespace serialization diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index f41cfcc53a35d..a156b4dd099d9 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1678,10 +1678,18 @@ void ASTStmtReader::VisitObjCBoolLiteralExpr(ObjCBoolLiteralExpr *E) { void ASTStmtReader::VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) { VisitExpr(E); + unsigned DomainNameLength = Record.readInt(); + E->setHasDomainName(Record.readInt()); SourceRange R = Record.readSourceRange(); E->AtLoc = R.getBegin(); E->RParen = R.getEnd(); - E->VersionToCheck = Record.readVersionTuple(); + E->VersionToCheck.Version = Record.readVersionTuple(); + E->VersionToCheck.SourceVersion = Record.readVersionTuple(); + if (E->hasDomainName()) { + std::string DomainName = Record.readString(); + assert(DomainNameLength == DomainName.size()); + strcpy(E->getTrailingObjects(), DomainName.data()); + } } //===----------------------------------------------------------------------===// @@ -2401,6 +2409,101 @@ void ASTStmtReader::VisitAsTypeExpr(AsTypeExpr *E) { E->SrcExpr = Record.readSubExpr(); } +//===----------------------------------------------------------------------===// +// BoundsSafety Expressions and Statements. +//===----------------------------------------------------------------------===// +void ASTStmtReader::VisitBoundsSafetyPointerPromotionExpr( + BoundsSafetyPointerPromotionExpr *E) { + VisitExpr(E); + switch (Record.readInt()) { + case 3: E->setLowerBound(Record.readSubExpr()); [[clang::fallthrough]]; + case 2: E->setUpperBound(Record.readSubExpr()); [[clang::fallthrough]]; + case 1: E->setPointer(Record.readSubExpr()); break; + default: llvm_unreachable("incorrect number of child nodes"); + } +} + +void ASTStmtReader::VisitAssumptionExpr(AssumptionExpr *E) { + VisitExpr(E); + unsigned NumExprs = Record.readInt(); + assert(NumExprs == E->AssumptionExprBits.NumExprs); + for (unsigned I = 0; I < NumExprs; ++I) { + E->setSubExpr(I, Record.readSubExpr()); + } +} + +void ASTStmtReader::VisitForgePtrExpr(ForgePtrExpr *E) { + VisitExpr(E); + E->setAddr(Record.readSubExpr()); + E->setSize(Record.readSubExpr()); + E->setBeginLoc(readSourceLocation()); + E->setEndLoc(readSourceLocation()); +} + +void ASTStmtReader::VisitGetBoundExpr(GetBoundExpr *E) { + VisitExpr(E); + E->setSubExpr(Record.readSubExpr()); + E->setBoundKind((GetBoundExpr::BoundKind)Record.readInt()); + E->setBuiltinLoc(readSourceLocation()); + E->setRParenLoc(readSourceLocation()); +} + +void ASTStmtReader::VisitPredefinedBoundsCheckExpr( + PredefinedBoundsCheckExpr *E) { + VisitExpr(E); + unsigned NumExprs = Record.readInt(); +#ifndef NDEBUG + BoundsCheckKind Kind = static_cast(Record.readInt()); + assert(Kind == E->getKind() && NumExprs == E->getNumSubExprs()); +#endif + for (unsigned I = 0; I < NumExprs; ++I) { + E->setSubExpr(I, Record.readSubExpr()); + } +} + +void ASTStmtReader::VisitBoundsCheckExpr(BoundsCheckExpr *E) { + VisitExpr(E); + unsigned NumExprs = Record.readInt(); + assert(NumExprs == E->BoundsCheckExprBits.NumChildren); + for (unsigned I = 0; I < NumExprs; ++I) { + E->setSubExpr(I, Record.readSubExpr()); + } +} + +void ASTStmtReader::VisitMaterializeSequenceExpr(MaterializeSequenceExpr *E) { + VisitExpr(E); + unsigned numExprs = Record.readInt(); + assert(numExprs == E->MaterializeSequenceExprBits.NumExprs); + E->MaterializeSequenceExprBits.Unbind = Record.readInt(); + + for (unsigned i = 0; i != numExprs; ++i) { + E->getSubExprs()[i] = Record.readSubExpr(); + } +} + +void ASTStmtReader::VisitTerminatedByToIndexableExpr( + TerminatedByToIndexableExpr *E) { + VisitExpr(E); + E->setPointer(Record.readSubExpr()); + bool HasTerm = Record.readInt(); + if (HasTerm) + E->setTerminator(Record.readSubExpr()); + E->setIncludeTerminator(Record.readInt()); + E->setBuiltinLoc(readSourceLocation()); + E->setRParenLoc(readSourceLocation()); +} + +void ASTStmtReader::VisitTerminatedByFromIndexableExpr( + TerminatedByFromIndexableExpr *E) { + VisitExpr(E); + E->setPointer(Record.readSubExpr()); + bool HasPtrToTerm = Record.readInt(); + if (HasPtrToTerm) + E->setPointerToTerminator(Record.readSubExpr()); + E->setBuiltinLoc(readSourceLocation()); + E->setRParenLoc(readSourceLocation()); +} + //===----------------------------------------------------------------------===// // OpenMP Directives. //===----------------------------------------------------------------------===// @@ -3521,7 +3624,8 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { break; case EXPR_OBJC_AVAILABILITY_CHECK: - S = new (Context) ObjCAvailabilityCheckExpr(Empty); + S = ObjCAvailabilityCheckExpr::CreateEmpty( + Context, Empty, Record[ASTStmtReader::NumExprFields]); break; case STMT_SEH_LEAVE: @@ -4150,6 +4254,48 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { break; } + case EXPR_BOUNDS_SAFETY_POINTER_PROMOTION: + S = BoundsSafetyPointerPromotionExpr::CreateEmpty( + Context, Record[ASTStmtReader::NumExprFields]); + break; + + case EXPR_ASSUMPTION: + S = AssumptionExpr::CreateEmpty(Context, + Record[ASTStmtReader::NumExprFields]); + break; + + case EXPR_FORGE_PTR: + S = new (Context) ForgePtrExpr(Empty); + break; + + case EXPR_GET_BOUND: + S = new (Context) GetBoundExpr(Empty); + break; + + case EXPR_PREDEFINED_BOUNDS_CHECK: + S = PredefinedBoundsCheckExpr::CreateEmpty( + Context, + /*NumChildren*/ Record[ASTStmtReader::NumExprFields + 1]); + break; + + case EXPR_BOUNDS_CHECK: + S = BoundsCheckExpr::CreateEmpty(Context, + /*NumChildren*/Record[ASTStmtReader::NumExprFields]); + break; + + case EXPR_MATERIALIZE_SEQUENCE: + S = MaterializeSequenceExpr::CreateEmpty(Context, + /*NumExprs*/Record[ASTStmtReader::NumExprFields]); + break; + + case EXPR_TERMINATED_BY_TO_INDEXABLE: + S = new (Context) TerminatedByToIndexableExpr(Empty); + break; + + case EXPR_TERMINATED_BY_FROM_INDEXABLE: + S = new (Context) TerminatedByFromIndexableExpr(Empty); + break; + case EXPR_USER_DEFINED_LITERAL: { auto NumArgs = Record[ASTStmtReader::NumExprFields]; BitsUnpacker CallExprBits(Record[ASTStmtReader::NumExprFields + 1]); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 2772038bf3755..9fc19d1db8ef6 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -595,6 +595,17 @@ void TypeLocWriter::VisitCountAttributedTypeLoc(CountAttributedTypeLoc TL) { // Nothing to do } +/* TO_UPSTREAM(BoundsSafety) ON */ +void TypeLocWriter::VisitDynamicRangePointerTypeLoc( + DynamicRangePointerTypeLoc TL) { + // nothing to do +} + +void TypeLocWriter::VisitValueTerminatedTypeLoc(ValueTerminatedTypeLoc TL) { + // nothing to do +} +/* TO_UPSTREAM(BoundsSafety) OFF */ + void TypeLocWriter::VisitBTFTagAttributedTypeLoc(BTFTagAttributedTypeLoc TL) { // Nothing to do. } @@ -903,11 +914,13 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(ORIGINAL_FILE); RECORD(ORIGINAL_FILE_ID); RECORD(INPUT_FILE_OFFSETS); + RECORD(MODULE_CACHE_KEY); + RECORD(CASFS_ROOT_ID); + RECORD(CAS_INCLUDE_TREE_ID); BLOCK(OPTIONS_BLOCK); RECORD(LANGUAGE_OPTIONS); RECORD(TARGET_OPTIONS); - RECORD(FILE_SYSTEM_OPTIONS); RECORD(HEADER_SEARCH_OPTIONS); RECORD(PREPROCESSOR_OPTIONS); @@ -934,7 +947,6 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(UNUSED_FILESCOPED_DECLS); RECORD(PPD_ENTITIES_OFFSETS); RECORD(VTABLE_USES); - RECORD(PPD_SKIPPED_RANGES); RECORD(REFERENCED_SELECTOR_POOL); RECORD(TU_UPDATE_LEXICAL); RECORD(SEMA_DECL_REFS); @@ -974,6 +986,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(PP_ASSUME_NONNULL_LOC); RECORD(PP_UNSAFE_BUFFER_USAGE); RECORD(VTABLES_TO_EMIT); + RECORD(AVAILABILITY_DOMAIN_TABLE); // SourceManager Block. BLOCK(SOURCE_MANAGER_BLOCK); @@ -1156,6 +1169,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(AST_BLOCK_HASH); RECORD(DIAGNOSTIC_OPTIONS); RECORD(HEADER_SEARCH_PATHS); + RECORD(FILE_SYSTEM_OPTIONS); RECORD(DIAG_PRAGMA_MAPPINGS); RECORD(HEADER_SEARCH_ENTRY_USAGE); RECORD(VFS_USAGE); @@ -1412,6 +1426,13 @@ void ASTWriter::writeUnhashedControlBlock(Preprocessor &PP) { Stream.EmitRecord(HEADER_SEARCH_PATHS, Record); } + // File system options. + Record.clear(); + const FileSystemOptions &FSOpts = + PP.getSourceManager().getFileManager().getFileSystemOpts(); + AddString(FSOpts.WorkingDir, Record); + Stream.EmitRecord(FILE_SYSTEM_OPTIONS, Record); + if (!HSOpts.ModulesSkipPragmaDiagnosticMappings) WritePragmaDiagnosticMappings(Diags, /* isModule = */ WritingModule); @@ -1563,6 +1584,37 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) { Stream.EmitRecord(MODULE_MAP_FILE, Record); } + if (WritingModule) { + // Module Cache Key + if (auto Key = WritingModule->getModuleCacheKey()) { + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(MODULE_CACHE_KEY)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + RecordData::value_type Record[] = {MODULE_CACHE_KEY}; + Stream.EmitRecordWithBlob(AbbrevCode, Record, *Key); + } + } + + // CAS include-tree id, for the scanner. + if (auto ID = PP.getCASIncludeTreeID()) { + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(CAS_INCLUDE_TREE_ID)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + RecordData::value_type Record[] = {CAS_INCLUDE_TREE_ID}; + Stream.EmitRecordWithBlob(AbbrevCode, Record, *ID); + } + // CAS filesystem root id, for the scanner. + if (auto ID = PP.getCASFileSystemRootID()) { + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(CASFS_ROOT_ID)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); + RecordData::value_type Record[] = {CASFS_ROOT_ID}; + Stream.EmitRecordWithBlob(AbbrevCode, Record, *ID); + } + // Imports if (Chain) { auto Abbrev = std::make_shared(); @@ -1574,6 +1626,7 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) { Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // File size Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // File timestamp Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // File name len + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Cache key len Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Strings unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev)); @@ -1599,6 +1652,8 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) { Record.push_back(0); Record.push_back(0); Record.push_back(0); + // FIXME: cache support for standard C++ modules. + Record.push_back(0); } else { // If we have calculated signature, there is no need to store // the size or timestamp. @@ -1608,6 +1663,7 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) { llvm::append_range(Blob, M.Signature); AddPathBlob(M.FileName, Record, Blob); + AddStringBlob(M.ModuleCacheKey, Record, Blob); } Stream.EmitRecordWithBlob(AbbrevCode, Record, Blob); @@ -1672,12 +1728,6 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) { } Stream.EmitRecord(TARGET_OPTIONS, Record); - // File system options. - Record.clear(); - const FileSystemOptions &FSOpts = FileMgr.getFileSystemOpts(); - AddString(FSOpts.WorkingDir, Record); - Stream.EmitRecord(FILE_SYSTEM_OPTIONS, Record); - // Header search options. Record.clear(); const HeaderSearchOptions &HSOpts = @@ -2879,28 +2929,6 @@ void ASTWriter::WritePreprocessorDetail(PreprocessingRecord &PPRec, Stream.EmitRecordWithBlob(PPEOffsetAbbrev, Record, bytes(PreprocessedEntityOffsets)); } - - // Write the skipped region table for the preprocessing record. - ArrayRef SkippedRanges = PPRec.getSkippedRanges(); - if (SkippedRanges.size() > 0) { - std::vector SerializedSkippedRanges; - SerializedSkippedRanges.reserve(SkippedRanges.size()); - for (auto const& Range : SkippedRanges) - SerializedSkippedRanges.emplace_back( - getRawSourceLocationEncoding(Range.getBegin()), - getRawSourceLocationEncoding(Range.getEnd())); - - using namespace llvm; - auto Abbrev = std::make_shared(); - Abbrev->Add(BitCodeAbbrevOp(PPD_SKIPPED_RANGES)); - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); - unsigned PPESkippedRangeAbbrev = Stream.EmitAbbrev(std::move(Abbrev)); - - Record.clear(); - Record.push_back(PPD_SKIPPED_RANGES); - Stream.EmitRecordWithBlob(PPESkippedRangeAbbrev, Record, - bytes(SerializedSkippedRanges)); - } } unsigned ASTWriter::getLocalOrImportedSubmoduleID(const Module *Mod) { @@ -2952,6 +2980,11 @@ void ASTWriter::WriteSubmodules(Module *WritingModule, ASTContext *Context) { Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // ID Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Parent Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 4)); // Kind + + // SWIFT-SPECIFIC FIELDS HERE. Handling them separately helps avoid merge + // conflicts. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSwiftInferIAM... + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Definition location Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // Inferred allowed by Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsFramework @@ -3070,6 +3103,12 @@ void ASTWriter::WriteSubmodules(Module *WritingModule, ASTContext *Context) { ID, ParentID, (RecordData::value_type)Mod->Kind, + + // SWIFT-SPECIFIC FIELDS HERE. + // Handling them separately helps + // avoid merge conflicts. + Mod->IsSwiftInferImportAsMember, + DefinitionLoc, (RecordData::value_type)InferredFID, Mod->IsFramework, @@ -3409,6 +3448,76 @@ uint64_t ASTWriter::WriteDeclContextLexicalBlock(ASTContext &Context, return Offset; } +namespace { +class AvailabilityDomainsTableTrait { +public: + using key_type = StringRef; + using key_type_ref = key_type; + using data_type = clang::serialization::DeclID; + using data_type_ref = const data_type &; + using hash_value_type = uint32_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref Key) { return llvm::djbHash(Key); } + + std::pair + EmitKeyDataLength(raw_ostream &Out, key_type_ref Key, data_type_ref) { + uint32_t KeyLength = Key.size(); + uint32_t DataLength = sizeof(data_type); + + llvm::support::endian::Writer Writer(Out, llvm::endianness::little); + Writer.write(KeyLength); + Writer.write(DataLength); + return {KeyLength, DataLength}; + } + + void EmitKey(raw_ostream &Out, key_type_ref Key, unsigned) { Out << Key; } + + void EmitData(raw_ostream &Out, key_type_ref, data_type_ref Data, unsigned) { + llvm::support::endian::Writer writer(Out, llvm::endianness::little); + writer.write(Data); + } +}; +} // namespace + +void ASTWriter::writeAvailabilityDomainDecls(ASTContext &Context) { + using namespace llvm; + if (Context.AvailabilityDomainMap.empty()) + return; + + AvailabilityDomainsTableTrait GeneratorTrait; + llvm::OnDiskChainedHashTableGenerator + Generator; + + for (auto &P : Context.AvailabilityDomainMap) + Generator.insert(P.first, GetDeclRef(P.second).getRawValue(), + GeneratorTrait); + + // Create the on-disk hash table in a buffer. + SmallString<4096> AvailDomainTable; + uint32_t BucketOffset; + { + using namespace llvm::support; + llvm::raw_svector_ostream Out(AvailDomainTable); + // Make sure that no bucket is at offset 0 + endian::write(Out, 0, llvm::endianness::little); + BucketOffset = Generator.Emit(Out, GeneratorTrait); + } + + // Create a blob abbreviation. + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(AVAILABILITY_DOMAIN_TABLE)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + unsigned MethodPoolAbbrev = Stream.EmitAbbrev(std::move(Abbrev)); + + // Write the table. + { + RecordData::value_type Record[] = {AVAILABILITY_DOMAIN_TABLE, BucketOffset}; + Stream.EmitRecordWithBlob(MethodPoolAbbrev, Record, AvailDomainTable); + } +} + void ASTWriter::WriteTypeDeclOffsets() { using namespace llvm; @@ -4561,14 +4670,12 @@ void ASTWriter::GenerateNameLookupTable( // order. SmallVector Names; - // We also build up small sets of the constructor and conversion function - // names which are visible. - llvm::SmallPtrSet ConstructorNameSet, ConversionNameSet; - - for (auto &Lookup : *DC->buildLookup()) { - auto &Name = Lookup.first; - auto &Result = Lookup.second; + // We also track whether we're writing out the DeclarationNameKey for + // constructors or conversion functions. + bool IncludeConstructorNames = false; + bool IncludeConversionNames = false; + for (auto &[Name, Result] : *DC->buildLookup()) { // If there are no local declarations in our lookup result, we // don't need to write an entry for the name at all. If we can't // write out a lookup set without performing more deserialization, @@ -4600,24 +4707,20 @@ void ASTWriter::GenerateNameLookupTable( // results for them. This in almost certainly a bug in Clang's name lookup, // but that is likely to be hard or impossible to fix and so we tolerate it // here by omitting lookups with empty results. - if (Lookup.second.getLookupResult().empty()) + if (Result.getLookupResult().empty()) continue; - switch (Lookup.first.getNameKind()) { + switch (Name.getNameKind()) { default: - Names.push_back(Lookup.first); + Names.push_back(Name); break; case DeclarationName::CXXConstructorName: - assert(isa(DC) && - "Cannot have a constructor name outside of a class!"); - ConstructorNameSet.insert(Name); + IncludeConstructorNames = true; break; case DeclarationName::CXXConversionFunctionName: - assert(isa(DC) && - "Cannot have a conversion function name outside of a class!"); - ConversionNameSet.insert(Name); + IncludeConversionNames = true; break; } } @@ -4625,55 +4728,34 @@ void ASTWriter::GenerateNameLookupTable( // Sort the names into a stable order. llvm::sort(Names); - if (auto *D = dyn_cast(DC)) { + if (IncludeConstructorNames || IncludeConversionNames) { // We need to establish an ordering of constructor and conversion function - // names, and they don't have an intrinsic ordering. - - // First we try the easy case by forming the current context's constructor - // name and adding that name first. This is a very useful optimization to - // avoid walking the lexical declarations in many cases, and it also - // handles the only case where a constructor name can come from some other - // lexical context -- when that name is an implicit constructor merged from - // another declaration in the redecl chain. Any non-implicit constructor or - // conversion function which doesn't occur in all the lexical contexts - // would be an ODR violation. - auto ImplicitCtorName = Context.DeclarationNames.getCXXConstructorName( - Context.getCanonicalType(Context.getRecordType(D))); - if (ConstructorNameSet.erase(ImplicitCtorName)) - Names.push_back(ImplicitCtorName); - - // If we still have constructors or conversion functions, we walk all the - // names in the decl and add the constructors and conversion functions - // which are visible in the order they lexically occur within the context. - if (!ConstructorNameSet.empty() || !ConversionNameSet.empty()) - for (Decl *ChildD : cast(DC)->decls()) - if (auto *ChildND = dyn_cast(ChildD)) { - auto Name = ChildND->getDeclName(); - switch (Name.getNameKind()) { - default: - continue; - - case DeclarationName::CXXConstructorName: - if (ConstructorNameSet.erase(Name)) - Names.push_back(Name); - break; + // names, and they don't have an intrinsic ordering. We also need to write + // out all constructor and conversion function results if we write out any + // of them, because they're all tracked under the same lookup key. + llvm::SmallPtrSet AddedNames; + for (Decl *ChildD : cast(DC)->decls()) { + if (auto *ChildND = dyn_cast(ChildD)) { + auto Name = ChildND->getDeclName(); + switch (Name.getNameKind()) { + default: + continue; - case DeclarationName::CXXConversionFunctionName: - if (ConversionNameSet.erase(Name)) - Names.push_back(Name); - break; - } + case DeclarationName::CXXConstructorName: + if (!IncludeConstructorNames) + continue; + break; - if (ConstructorNameSet.empty() && ConversionNameSet.empty()) - break; + case DeclarationName::CXXConversionFunctionName: + if (!IncludeConversionNames) + continue; + break; } - - assert(ConstructorNameSet.empty() && "Failed to find all of the visible " - "constructors by walking all the " - "lexical members of the context."); - assert(ConversionNameSet.empty() && "Failed to find all of the visible " - "conversion functions by walking all " - "the lexical members of the context."); + // We should include lookup results for this name. + if (AddedNames.insert(Name).second) + Names.push_back(Name); + } + } } // Next we need to do a lookup with each name into this decl context to fully @@ -6331,6 +6413,8 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) { // Write the visible updates to DeclContexts. for (auto *DC : UpdatedDeclContexts) WriteDeclContextVisibleUpdate(Context, DC); + + writeAvailabilityDomainDecls(Context); } void ASTWriter::WriteSpecializationsUpdates(bool IsPartial) { @@ -7656,6 +7740,20 @@ void ASTWriter::AddedAttributeToRecord(const Attr *Attr, DeclUpdate(DeclUpdateKind::AddedAttrToRecord, Attr)); } +/* TO_UPSTREAM(BoundsSafety) ON*/ +// This function uses DeclUpdates to store the attribute in a separate record, +// so as to avoid deserialization cycle between the attribute and the Decl. +// Thus, this will be called during 'WritingAST' and should cover Decl either +// from parsing or an AST file. +void ASTWriter::LazyAttributeToDecl(const Attr *Attr, + const Decl *D) { + if (Chain && Chain->isProcessingUpdateRecords()) return; + // Reuse UPD_ADDED_ATTR_TO_RECORD to share its functionality to write and + // read the attribute for Decl. + DeclUpdates[D].push_back(DeclUpdate(DeclUpdateKind::AddedAttrToRecord, Attr)); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + void ASTWriter::AddedCXXTemplateSpecialization( const ClassTemplateDecl *TD, const ClassTemplateSpecializationDecl *D) { assert(!WritingAST && "Already writing the AST!"); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index f3d260e8671b2..759420915bbf6 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -468,6 +468,19 @@ void ASTDeclWriter::Visit(Decl *D) { } void ASTDeclWriter::VisitDecl(Decl *D) { + /// BoundsSafety: Workaround to avoid a cycle during deserialization. + /// \code + /// int len; + /// int *__counted_by(len) ptr; + /// \endcode + /// In the above example, DependerDeclsAttr referencing 'ptr' is attached + /// to 'len' as a Decl attribute. To workaround the issue, we serialize + /// DependerDeclsAttr separately, creating a map between Decl and Attribute. + if (DependerDeclsAttr *DDA = D->getAttr()) { + Writer.LazyAttributeToDecl(DDA, D); + D->dropAttr(); + } + BitsPacker DeclBits; // The order matters here. It will be better to put the bit with higher @@ -1093,6 +1106,8 @@ void ASTDeclWriter::VisitObjCCategoryDecl(ObjCCategoryDecl *D) { void ASTDeclWriter::VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *D) { VisitNamedDecl(D); Record.AddDeclRef(D->getClassInterface()); + Record.AddSourceLocation(D->getClassInterfaceLoc()); + Record.AddSourceLocation(D->getAtLoc()); Code = serialization::DECL_OBJC_COMPATIBLE_ALIAS; } diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index b9eabd5ddb64c..a7ca06d3ac0c1 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1644,8 +1644,13 @@ void ASTStmtWriter::VisitObjCBoolLiteralExpr(ObjCBoolLiteralExpr *E) { void ASTStmtWriter::VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) { VisitExpr(E); + Record.push_back(E->hasDomainName() ? E->getDomainName().size() : 0); + Record.push_back(E->hasDomainName()); Record.AddSourceRange(E->getSourceRange()); Record.AddVersionTuple(E->getVersion()); + Record.AddVersionTuple(E->getVersionAsWritten()); + if (E->hasDomainName()) + Record.AddString(E->getDomainName()); Code = serialization::EXPR_OBJC_AVAILABILITY_CHECK; } @@ -2340,6 +2345,106 @@ void ASTStmtWriter::VisitAsTypeExpr(AsTypeExpr *E) { Code = serialization::EXPR_ASTYPE; } +//===----------------------------------------------------------------------===// +// BoundsSafety Expressions and Statements. +//===----------------------------------------------------------------------===// +void ASTStmtWriter::VisitBoundsSafetyPointerPromotionExpr( + BoundsSafetyPointerPromotionExpr *E) { + VisitExpr(E); + Record.push_back(E->getNumChildren()); + switch (E->getNumChildren()) { + case 3: Record.AddStmt(E->getLowerBound()); [[clang::fallthrough]]; + case 2: Record.AddStmt(E->getUpperBound()); [[clang::fallthrough]]; + case 1: Record.AddStmt(E->getPointer()); break; + default: llvm_unreachable("incorrect number of child nodes"); + } + Code = serialization::EXPR_BOUNDS_SAFETY_POINTER_PROMOTION; +} + +void ASTStmtWriter::VisitAssumptionExpr(AssumptionExpr *E) { + VisitExpr(E); + Record.push_back(E->getNumSubExprs()); + for (Stmt *Sub : E->children()) { + Record.AddStmt(Sub); + } + Code = serialization::EXPR_ASSUMPTION; +} + +void ASTStmtWriter::VisitForgePtrExpr(ForgePtrExpr *E) { + VisitExpr(E); + Record.AddStmt(E->getAddr()); + Record.AddStmt(E->getSize()); + Record.AddSourceLocation(E->getBeginLoc()); + Record.AddSourceLocation(E->getEndLoc()); + Code = serialization::EXPR_FORGE_PTR; +} + +void ASTStmtWriter::VisitGetBoundExpr(GetBoundExpr *E) { + VisitExpr(E); + Record.AddStmt(E->getSubExpr()); + Record.push_back((int)E->getBoundKind()); + Record.AddSourceLocation(E->getBuiltinLoc()); + Record.AddSourceLocation(E->getRParenLoc()); + Code = serialization::EXPR_GET_BOUND; +} + +void ASTStmtWriter::VisitPredefinedBoundsCheckExpr( + PredefinedBoundsCheckExpr *E) { + VisitExpr(E); + Record.push_back(static_cast(E->getKind())); + Record.push_back(E->getNumSubExprs()); + for (Stmt *Sub : E->children()) { + Record.AddStmt(Sub); + } + Code = serialization::EXPR_PREDEFINED_BOUNDS_CHECK; +} + +void ASTStmtWriter::VisitBoundsCheckExpr(BoundsCheckExpr *E) { + VisitExpr(E); + Record.push_back(E->getNumSubExprs()); + for (Stmt *Sub : E->children()) { + Record.AddStmt(Sub); + } + Code = serialization::EXPR_BOUNDS_CHECK; +} + +void ASTStmtWriter::VisitMaterializeSequenceExpr(MaterializeSequenceExpr *E) { + VisitExpr(E); + Record.push_back(E->getNumSubExprs()); + Record.push_back(E->isUnbinding()); + for (Stmt *Sub : E->children()) { + Record.AddStmt(Sub); + } + Code = serialization::EXPR_MATERIALIZE_SEQUENCE; +} + +void ASTStmtWriter::VisitTerminatedByToIndexableExpr( + TerminatedByToIndexableExpr *E) { + VisitExpr(E); + Record.AddStmt(E->getPointer()); + bool HasTerm = E->getTerminator() != nullptr; + Record.push_back(HasTerm); + if (HasTerm) + Record.AddStmt(E->getTerminator()); + Record.push_back(E->includesTerminator()); + Record.AddSourceLocation(E->getBuiltinLoc()); + Record.AddSourceLocation(E->getRParenLoc()); + Code = serialization::EXPR_TERMINATED_BY_TO_INDEXABLE; +} + +void ASTStmtWriter::VisitTerminatedByFromIndexableExpr( + TerminatedByFromIndexableExpr *E) { + VisitExpr(E); + Record.AddStmt(E->getPointer()); + bool HasPtrToTerm = E->getPointerToTerminator() != nullptr; + Record.push_back(HasPtrToTerm); + if (HasPtrToTerm) + Record.AddStmt(E->getPointerToTerminator()); + Record.AddSourceLocation(E->getBuiltinLoc()); + Record.AddSourceLocation(E->getRParenLoc()); + Code = serialization::EXPR_TERMINATED_BY_FROM_INDEXABLE; +} + //===----------------------------------------------------------------------===// // Microsoft Expressions and Statements. //===----------------------------------------------------------------------===// diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp index d466ea06301a6..3c5fa9079f64c 100644 --- a/clang/lib/Serialization/ModuleManager.cpp +++ b/clang/lib/Serialization/ModuleManager.cpp @@ -45,6 +45,16 @@ using namespace serialization; ModuleFile *ModuleManager::lookupByFileName(StringRef Name) const { auto Entry = FileMgr.getOptionalFileRef(Name, /*OpenFile=*/false, /*CacheFailure=*/false); +#if !defined(__APPLE__) + if (Entry) { + // On Linux ext4 FileManager's inode caching system does not + // provide us correct behaviour for ModuleCache directories. + // inode can be reused after PCM delete resulting in cache misleading. + if (auto BypassFile = FileMgr.getBypassFile(*Entry)) + Entry = *BypassFile; + } +#endif + if (Entry) return lookup(*Entry); @@ -453,6 +463,28 @@ bool ModuleManager::lookupModuleFile(StringRef FileName, off_t ExpectedSize, File = FileMgr.getOptionalFileRef(FileName, /*OpenFile=*/true, /*CacheFailure=*/false); +#if !defined(__APPLE__) + if (File) { + // On Linux ext4 FileManager's inode caching system does not + // provide us correct behaviour for ModuleCache directories. + // inode can be reused after PCM delete resulting in cache misleading. + // Only use the bypass file if bypass succeed in case the underlying file + // system doesn't support bypass (thus there is no need for the workaround). + if (auto Bypass = FileMgr.getBypassFile(*File)) + File = *Bypass; + } +#endif + + // If the file is known to the module cache but not in the filesystem, it is + // a memory buffer. Create a virtual file for it. + if (!File) { + if (auto *KnownBuffer = + getModuleCache().getInMemoryModuleCache().lookupPCM(FileName)) { + File = + FileMgr.getVirtualFileRef(FileName, KnownBuffer->getBufferSize(), 0); + } + } + if (File && ((ExpectedSize && ExpectedSize != File->getSize()) || (ExpectedModTime && ExpectedModTime != File->getModificationTime()))) diff --git a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index e58329817d7cd..edd2feb995ee1 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -714,7 +714,7 @@ static bool isObjCTypeParamDependent(QualType Type) { : public DynamicRecursiveASTVisitor { public: IsObjCTypeParamDependentTypeVisitor() = default; - bool VisitObjCTypeParamType(ObjCTypeParamType *Type) override { + bool VisitTypedefType(TypedefType *Type) override { if (isa(Type->getDecl())) { Result = true; return false; diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 86e2e8f634bfd..c2ee8c7d53224 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1721,6 +1721,16 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, assert(!isa(S) || S == cast(S)->IgnoreParens()); switch (S->getStmtClass()) { + // BoundsSafety expressions are not supported yet. + case Stmt::AssumptionExprClass: + case Stmt::BoundsSafetyPointerPromotionExprClass: + case Stmt::ForgePtrExprClass: + case Stmt::GetBoundExprClass: + case Stmt::PredefinedBoundsCheckExprClass: + case Stmt::BoundsCheckExprClass: + case Stmt::MaterializeSequenceExprClass: + case Stmt::TerminatedByToIndexableExprClass: + case Stmt::TerminatedByFromIndexableExprClass: // C++, OpenMP and ARC stuff we don't support yet. case Stmt::CXXDependentScopeMemberExprClass: case Stmt::CXXTryStmtClass: diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 3d0a69a515ab8..be4040e8d2313 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -535,6 +535,16 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, Bldr.generateNode(CastE, Pred, state); continue; } + /* TO_UPSTREAM(BoundsSafety) ON*/ + case CK_BoundsSafetyPointerCast: { + SVal V = state->getSVal(Ex, LCtx); + SVal CastedV = + svalBuilder.evalCast(V, CastE->getType(), Ex->getType()); + state = state->BindExpr(CastE, LCtx, CastedV); + Bldr.generateNode(CastE, Pred, state); + continue; + } + /* TO_UPSTREAM(BoundsSafety) OFF*/ } } } diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp index 9e0800ba9e2fa..1b8a1921da120 100644 --- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -635,6 +635,16 @@ SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val, // `evalCast` and its helper `EvalCastVisitor` //===----------------------------------------------------------------------===// +/* TO_UPSTREAM(BoundsSafety) ON*/ +static bool isConcretePointerWidthChangingCast(ASTContext &ACtx, SVal V, + QualType CastTy, + QualType OriginalTy) { + return V.getAs() && + CastTy->isPointerType() && OriginalTy->isPointerType() && + ACtx.getTypeSize(CastTy) != ACtx.getTypeSize(OriginalTy); +} +/* TO_UPSTREAM(BoundsSafety) OFF*/ + namespace { class EvalCastVisitor : public SValVisitor { private: @@ -663,7 +673,8 @@ class EvalCastVisitor : public SValVisitor { // FIXME: Move this check to the most appropriate // evalCastKind/evalCastSubKind function. For const casts, casts to void, // just propagate the value. - if (!CastTy->isVariableArrayType() && !OriginalTy->isVariableArrayType()) + if (!CastTy->isVariableArrayType() && !OriginalTy->isVariableArrayType() && + !isConcretePointerWidthChangingCast(Context, V, CastTy, OriginalTy)) if (shouldBeModeledWithNoOp(Context, Context.getPointerType(CastTy), Context.getPointerType(OriginalTy))) return V; diff --git a/clang/lib/Tooling/CMakeLists.txt b/clang/lib/Tooling/CMakeLists.txt index fc1f1f9f9d367..f8825af2bb002 100644 --- a/clang/lib/Tooling/CMakeLists.txt +++ b/clang/lib/Tooling/CMakeLists.txt @@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS add_subdirectory(Core) add_subdirectory(Inclusions) +add_subdirectory(Refactor) add_subdirectory(Refactoring) add_subdirectory(ASTDiff) add_subdirectory(Syntax) @@ -46,5 +47,7 @@ add_clang_library(clangTooling clangRewrite clangSerialization clangToolingCore + clangToolingRefactor + clangToolingRefactoring ${LLVM_PTHREAD_LIB} ) diff --git a/clang/lib/Tooling/DependencyScanning/CASFSActionController.cpp b/clang/lib/Tooling/DependencyScanning/CASFSActionController.cpp new file mode 100644 index 0000000000000..f1681522df659 --- /dev/null +++ b/clang/lib/Tooling/DependencyScanning/CASFSActionController.cpp @@ -0,0 +1,244 @@ +//===- CASFSActionController.cpp ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CachingActions.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/CAS/CachingOnDiskFileSystem.h" +#include "llvm/CAS/ObjectStore.h" +#include "llvm/Support/PrefixMapper.h" + +using namespace clang; +using namespace tooling; +using namespace dependencies; +using llvm::Error; + +namespace { +class CASFSActionController : public CallbackActionController { +public: + CASFSActionController(LookupModuleOutputCallback LookupModuleOutput, + llvm::cas::CachingOnDiskFileSystem &CacheFS); + + llvm::Error initialize(CompilerInstance &ScanInstance, + CompilerInvocation &NewInvocation) override; + llvm::Error finalize(CompilerInstance &ScanInstance, + CompilerInvocation &NewInvocation) override; + std::optional + getCacheKey(const CompilerInvocation &NewInvocation) override; + llvm::Error + initializeModuleBuild(CompilerInstance &ModuleScanInstance) override; + llvm::Error + finalizeModuleBuild(CompilerInstance &ModuleScanInstance) override; + llvm::Error finalizeModuleInvocation(CowCompilerInvocation &CI, + const ModuleDeps &MD) override; + +private: + llvm::cas::CachingOnDiskFileSystem &CacheFS; + std::optional Mapper; + CASOptions CASOpts; + llvm::StringMap OutputToCacheKey; +}; +} // anonymous namespace + +CASFSActionController::CASFSActionController( + LookupModuleOutputCallback LookupModuleOutput, + llvm::cas::CachingOnDiskFileSystem &CacheFS) + : CallbackActionController(std::move(LookupModuleOutput)), + CacheFS(CacheFS) {} + +Error CASFSActionController::initialize(CompilerInstance &ScanInstance, + CompilerInvocation &NewInvocation) { + // Setup prefix mapping. + Mapper.emplace(&CacheFS); + DepscanPrefixMapping::configurePrefixMapper(NewInvocation, *Mapper); + + const PreprocessorOptions &PPOpts = ScanInstance.getPreprocessorOpts(); + if (!PPOpts.Includes.empty() || !PPOpts.ImplicitPCHInclude.empty()) + addReversePrefixMappingFileSystem(*Mapper, ScanInstance); + + CacheFS.trackNewAccesses(); + if (auto CWD = + ScanInstance.getVirtualFileSystem().getCurrentWorkingDirectory()) + CacheFS.setCurrentWorkingDirectory(*CWD); + // Track paths that are accessed by the scanner before we reach here. + for (const auto &File : ScanInstance.getHeaderSearchOpts().VFSOverlayFiles) + (void)CacheFS.status(File); + // Enable caching in the resulting commands. + ScanInstance.getFrontendOpts().CacheCompileJob = true; + CASOpts = ScanInstance.getCASOpts(); + return Error::success(); +} + +/// Ensure that all files reachable from imported modules/pch are tracked. +/// These could be loaded lazily during compilation. +static void trackASTFileInputs(CompilerInstance &CI, + llvm::cas::CachingOnDiskFileSystem &CacheFS) { + auto Reader = CI.getASTReader(); + if (!Reader) + return; + + for (serialization::ModuleFile &MF : Reader->getModuleManager()) { + Reader->visitInputFiles( + MF, /*IncludeSystem=*/true, /*Complain=*/false, + [](const serialization::InputFile &IF, bool isSystem) { + // Visiting input files triggers the file system lookup. + }); + } +} + +/// Ensure files that are not accessed during the scan (or accessed before the +/// tracking scope) are tracked. +static void trackFilesCommon(CompilerInstance &CI, + llvm::cas::CachingOnDiskFileSystem &CacheFS) { + trackASTFileInputs(CI, CacheFS); + + // Exclude the module cache from tracking. The implicit build pcms should + // not be needed after scanning. + if (!CI.getHeaderSearchOpts().ModuleCachePath.empty()) + (void)CacheFS.excludeFromTracking(CI.getHeaderSearchOpts().ModuleCachePath); + + // Normally this would be looked up while creating the VFS, but implicit + // modules share their VFS and it happens too early for the TU scan. + for (const auto &File : CI.getHeaderSearchOpts().VFSOverlayFiles) + (void)CacheFS.status(File); + + StringRef Sysroot = CI.getHeaderSearchOpts().Sysroot; + if (!Sysroot.empty()) { + // Include 'SDKSettings.json', if it exists, to accomodate availability + // checks during the compilation. + llvm::SmallString<256> FilePath = Sysroot; + llvm::sys::path::append(FilePath, "SDKSettings.json"); + (void)CacheFS.status(FilePath); + } +} + +Error CASFSActionController::finalize(CompilerInstance &ScanInstance, + CompilerInvocation &NewInvocation) { + // Handle profile mappings. + (void)CacheFS.status(NewInvocation.getCodeGenOpts().ProfileInstrumentUsePath); + (void)CacheFS.status(NewInvocation.getCodeGenOpts().SampleProfileFile); + (void)CacheFS.status(NewInvocation.getCodeGenOpts().ProfileRemappingFile); + + auto GetInputCacheKey = [&]() -> std::optional { + if (NewInvocation.getFrontendOpts().Inputs.size() != 1) + return {}; + const auto &FIF = NewInvocation.getFrontendOpts().Inputs.front(); + if (!FIF.isFile()) + return {}; + auto It = OutputToCacheKey.find(FIF.getFile()); + if (It == OutputToCacheKey.end()) + return {}; + + return It->second; + }; + + std::string InputID; + CachingInputKind InputKind; + + if (auto InputCacheKey = GetInputCacheKey()) { + InputID = InputCacheKey->str(); + InputKind = CachingInputKind::CachedCompilation; + } else { + trackFilesCommon(ScanInstance, CacheFS); + + auto CASFileSystemRootID = CacheFS.createTreeFromNewAccesses( + [&](const llvm::vfs::CachedDirectoryEntry &Entry, + SmallVectorImpl &Storage) { + return Mapper->mapDirEntry(Entry, Storage); + }); + if (!CASFileSystemRootID) + return CASFileSystemRootID.takeError(); + InputID = CASFileSystemRootID->getID().toString(); + InputKind = CachingInputKind::FileSystemRoot; + } + + configureInvocationForCaching(NewInvocation, CASOpts, InputID, InputKind, + CacheFS.getCurrentWorkingDirectory().get()); + + if (Mapper) + DepscanPrefixMapping::remapInvocationPaths(NewInvocation, *Mapper); + + auto &CAS = ScanInstance.getOrCreateObjectStore(); + auto Key = createCompileJobCacheKey(CAS, ScanInstance.getDiagnostics(), + NewInvocation); + if (Key) + OutputToCacheKey[NewInvocation.getFrontendOpts().OutputFile] = + Key->toString(); + return Error::success(); +} + +std::optional +CASFSActionController::getCacheKey(const CompilerInvocation &NewInvocation) { + auto It = OutputToCacheKey.find(NewInvocation.getFrontendOpts().OutputFile); + // FIXME: Assert this does not happen. + if (It == OutputToCacheKey.end()) + return std::nullopt; + return It->second; +} + +Error CASFSActionController::initializeModuleBuild( + CompilerInstance &ModuleScanInstance) { + + CacheFS.trackNewAccesses(); + // If the working directory is not otherwise accessed by the module build, + // we still need it due to -fcas-fs-working-directory being set. + if (auto CWD = CacheFS.getCurrentWorkingDirectory()) + (void)CacheFS.status(*CWD); + + return Error::success(); +} + +Error CASFSActionController::finalizeModuleBuild( + CompilerInstance &ModuleScanInstance) { + trackFilesCommon(ModuleScanInstance, CacheFS); + + std::optional RootID; + auto E = CacheFS + .createTreeFromNewAccesses( + [&](const llvm::vfs::CachedDirectoryEntry &Entry, + SmallVectorImpl &Storage) { + return Mapper->mapDirEntry(Entry, Storage); + }) + .moveInto(RootID); + if (E) + return E; + +#ifndef NDEBUG + Module *M = ModuleScanInstance.getPreprocessor().getCurrentModule(); + assert(M && "finalizing without a module"); +#endif + + ModuleScanInstance.getPreprocessor().setCASFileSystemRootID( + RootID->toString()); + return Error::success(); +} + +Error CASFSActionController::finalizeModuleInvocation( + CowCompilerInvocation &CowCI, const ModuleDeps &MD) { + // TODO: Avoid this copy. + CompilerInvocation CI(CowCI); + + if (auto ID = MD.CASFileSystemRootID) { + configureInvocationForCaching(CI, CASOpts, *ID, + CachingInputKind::FileSystemRoot, + CacheFS.getCurrentWorkingDirectory().get()); + } + + if (Mapper) + DepscanPrefixMapping::remapInvocationPaths(CI, *Mapper); + + CowCI = CI; + return llvm::Error::success(); +} + +std::unique_ptr +dependencies::createCASFSActionController( + LookupModuleOutputCallback LookupModuleOutput, + llvm::cas::CachingOnDiskFileSystem &CacheFS) { + return std::make_unique(LookupModuleOutput, CacheFS); +} diff --git a/clang/lib/Tooling/DependencyScanning/CMakeLists.txt b/clang/lib/Tooling/DependencyScanning/CMakeLists.txt index 42a63faa26d3e..917b3f5810e96 100644 --- a/clang/lib/Tooling/DependencyScanning/CMakeLists.txt +++ b/clang/lib/Tooling/DependencyScanning/CMakeLists.txt @@ -3,15 +3,20 @@ set(LLVM_LINK_COMPONENTS Option Support TargetParser + CAS ) add_clang_library(clangDependencyScanning + CASFSActionController.cpp + DependencyScanningCASFilesystem.cpp DependencyScanningFilesystem.cpp DependencyScanningService.cpp DependencyScanningWorker.cpp DependencyScanningTool.cpp + IncludeTreeActionController.cpp InProcessModuleCache.cpp ModuleDepCollector.cpp + ScanAndUpdateArgs.cpp DEPENDS ClangDriverOptions @@ -19,6 +24,7 @@ add_clang_library(clangDependencyScanning LINK_LIBS clangAST clangBasic + clangCAS clangDriver clangFrontend clangLex diff --git a/clang/lib/Tooling/DependencyScanning/CachingActions.h b/clang/lib/Tooling/DependencyScanning/CachingActions.h new file mode 100644 index 0000000000000..8cb39dc649641 --- /dev/null +++ b/clang/lib/Tooling/DependencyScanning/CachingActions.h @@ -0,0 +1,36 @@ +//===- CachingActions.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_CACHINGACTIONS_H +#define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_CACHINGACTIONS_H + +#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" +#include "clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h" + +namespace llvm::cas { +class CachingOnDiskFileSystem; +} + +namespace clang::tooling::dependencies { + +std::unique_ptr +createIncludeTreeActionController(LookupModuleOutputCallback LookupModuleOutput, + cas::ObjectStore &DB); + +std::unique_ptr +createCASFSActionController(LookupModuleOutputCallback LookupModuleOutput, + llvm::cas::CachingOnDiskFileSystem &CacheFS); + +/// The PCH recorded file paths with canonical paths, create a VFS that +/// allows remapping back to the non-canonical source paths so that they are +/// found during dep-scanning. +void addReversePrefixMappingFileSystem(const llvm::PrefixMapper &PrefixMapper, + CompilerInstance &ScanInstance); + +} // namespace clang::tooling::dependencies +#endif // LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_CACHINGACTIONS_H diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningCASFilesystem.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningCASFilesystem.cpp new file mode 100644 index 0000000000000..a2924e5259747 --- /dev/null +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningCASFilesystem.cpp @@ -0,0 +1,351 @@ +//===- DependencyScanningCASFilesystem.cpp - clang-scan-deps fs -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/DependencyScanning/DependencyScanningCASFilesystem.h" +#include "clang/Basic/Version.h" +#include "clang/Lex/DependencyDirectivesScanner.h" +#include "llvm/CAS/ActionCache.h" +#include "llvm/CAS/CachingOnDiskFileSystem.h" +#include "llvm/CAS/HierarchicalTreeBuilder.h" +#include "llvm/CAS/ObjectStore.h" +#include "llvm/Support/EndianStream.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Threading.h" + +using namespace clang; +using namespace tooling; +using namespace dependencies; + +template static T reportAsFatalIfError(Expected ValOrErr) { + if (!ValOrErr) + llvm::report_fatal_error(ValOrErr.takeError()); + return std::move(*ValOrErr); +} + +static void reportAsFatalIfError(llvm::Error E) { + if (E) + llvm::report_fatal_error(std::move(E)); +} + +using llvm::Error; + +DependencyScanningCASFilesystem::DependencyScanningCASFilesystem( + IntrusiveRefCntPtr WorkerFS, + llvm::cas::ActionCache &Cache) + : FS(WorkerFS), Entries(EntryAlloc), CAS(WorkerFS->getCAS()), Cache(Cache) { +} + +DependencyScanningCASFilesystem::~DependencyScanningCASFilesystem() = default; + +static Expected +storeDepDirectives(cas::ObjectStore &CAS, + ArrayRef Directives) { + llvm::SmallString<1024> Buffer; + llvm::raw_svector_ostream OS(Buffer); + llvm::support::endian::Writer W(OS, llvm::endianness::little); + size_t NumTokens = 0; + for (const auto &Directive : Directives) + NumTokens += Directive.Tokens.size(); + W.write(NumTokens); + for (const auto &Directive : Directives) { + for (const auto &Token : Directive.Tokens) { + W.write(Token.Offset); + W.write(Token.Length); + W.write(static_cast::type>( + Token.Kind)); + W.write(Token.Flags); + } + } + + size_t TokenIdx = 0; + W.write(Directives.size()); + for (const auto &Directive : Directives) { + W.write(static_cast::type>( + Directive.Kind)); + W.write(TokenIdx); + W.write(Directive.Tokens.size()); + TokenIdx += Directive.Tokens.size(); + } + + return CAS.storeFromString(std::nullopt, Buffer); +} + +template static void readle(StringRef &Slice, T &Out) { + using namespace llvm::support::endian; + if (Slice.size() < sizeof(T)) + llvm::report_fatal_error("buffer too small"); + Out = read(Slice.begin()); + Slice = Slice.drop_front(sizeof(T)); +} + +static Error loadDepDirectives( + cas::ObjectStore &CAS, cas::ObjectRef Ref, + llvm::SmallVectorImpl &DepTokens, + llvm::SmallVectorImpl + &DepDirectives) { + using namespace dependency_directives_scan; + auto Blob = CAS.getProxy(Ref); + if (!Blob) + return Blob.takeError(); + + StringRef Data = Blob->getData(); + StringRef Cursor = Data; + + size_t NumTokens = 0; + readle(Cursor, NumTokens); + DepTokens.reserve(NumTokens); + for (size_t I = 0; I < NumTokens; ++I) { + DepTokens.emplace_back(0, 0, (tok::TokenKind)0, 0); + auto &Token = DepTokens.back(); + readle(Cursor, Token.Offset); + readle(Cursor, Token.Length); + std::underlying_type::type Kind; + readle(Cursor, Kind); + Token.Kind = static_cast(Kind); + readle(Cursor, Token.Flags); + } + + size_t NumDirectives = 0; + readle(Cursor, NumDirectives); + DepDirectives.reserve(NumDirectives); + for (size_t I = 0; I < NumDirectives; ++I) { + std::underlying_type::type Kind; + readle(Cursor, Kind); + size_t TokenStart, NumTokens; + readle(Cursor, TokenStart); + readle(Cursor, NumTokens); + assert(NumTokens <= DepTokens.size() && + TokenStart <= DepTokens.size() - NumTokens); + DepDirectives.emplace_back( + static_cast(Kind), + ArrayRef(DepTokens.begin() + TokenStart, + DepTokens.begin() + TokenStart + NumTokens)); + } + assert(Cursor.empty()); + return Error::success(); +} + +void DependencyScanningCASFilesystem::scanForDirectives( + llvm::cas::ObjectRef InputDataID, StringRef Identifier, + SmallVectorImpl &Tokens, + SmallVectorImpl &Directives) { + using namespace llvm; + using namespace llvm::cas; + + // Get a blob for the clang version string. + if (!ClangFullVersionID) + ClangFullVersionID = reportAsFatalIfError( + CAS.storeFromString(std::nullopt, getClangFullVersion())); + + // Get a blob for the dependency directives scan command. + if (!DepDirectivesID) + DepDirectivesID = + reportAsFatalIfError(CAS.storeFromString(std::nullopt, "directives")); + + // Get an empty blob. + if (!EmptyBlobID) + EmptyBlobID = reportAsFatalIfError(CAS.storeFromString(std::nullopt, "")); + + // Construct a tree for the input. + std::optional InputID; + { + HierarchicalTreeBuilder Builder; + Builder.push(*ClangFullVersionID, TreeEntry::Regular, "version"); + Builder.push(*DepDirectivesID, TreeEntry::Regular, "command"); + Builder.push(InputDataID, TreeEntry::Regular, "data"); + InputID = reportAsFatalIfError(Builder.create(CAS)).getID(); + } + + // Check the result cache. + if (std::optional OutputID = + reportAsFatalIfError(Cache.get(*InputID))) { + if (std::optional OutputRef = CAS.getReference(*OutputID)) { + if (OutputRef == EmptyBlobID) + return; // Cached directive scanning failure. + reportAsFatalIfError( + loadDepDirectives(CAS, *OutputRef, Tokens, Directives)); + return; + } + } + + StringRef InputData = + reportAsFatalIfError(CAS.getProxy(InputDataID)).getData(); + + if (scanSourceForDependencyDirectives(InputData, Tokens, Directives)) { + // FIXME: Propagate the diagnostic if desired by the client. + // Failure. Cache empty directives. + Tokens.clear(); + Directives.clear(); + reportAsFatalIfError(Cache.put(*InputID, CAS.getID(*EmptyBlobID))); + return; + } + + // Success. Add to the CAS and get back persistent output data. + cas::ObjectRef DirectivesID = + reportAsFatalIfError(storeDepDirectives(CAS, Directives)); + // Cache the computation. + reportAsFatalIfError(Cache.put(*InputID, CAS.getID(DirectivesID))); +} + +Expected +DependencyScanningCASFilesystem::getOriginal(cas::CASID InputDataID) { + Expected Blob = CAS.getProxy(InputDataID); + if (Blob) + return Blob->getData(); + return Blob.takeError(); +} + +static bool shouldCacheStatFailures(StringRef Filename) { + StringRef Ext = llvm::sys::path::extension(Filename); + if (Ext.empty()) + return false; // This may be the module cache directory. + return true; +} + +llvm::cas::CachingOnDiskFileSystem & +DependencyScanningCASFilesystem::getCachingFS() { + return static_cast(*FS); +} + +DependencyScanningCASFilesystem::LookupPathResult +DependencyScanningCASFilesystem::lookupPath(const Twine &Path) { + SmallString<256> PathStorage; + StringRef PathRef = Path.toStringRef(PathStorage); + + { + auto I = Entries.find(PathRef); + if (I != Entries.end()) { + // FIXME: Gross hack to ensure this file gets tracked as part of the + // compilation. Instead, we should add an explicit hook somehow / + // somewhere. + (void)getCachingFS().status(PathRef); + return LookupPathResult{&I->second, std::error_code()}; + } + } + + std::optional FileID; + llvm::ErrorOr MaybeStatus = + getCachingFS().statusAndFileID(PathRef, FileID); + if (!MaybeStatus) { + if (shouldCacheStatFailures(PathRef)) + Entries[PathRef].EC = MaybeStatus.getError(); + return LookupPathResult{nullptr, MaybeStatus.getError()}; + } + + // Underlying file system caches directories. No need to duplicate. + if (MaybeStatus->isDirectory()) + return LookupPathResult{nullptr, std::move(MaybeStatus)}; + + auto &Entry = Entries[PathRef]; + Entry.CASContents = CAS.getReference(*FileID); + llvm::ErrorOr Buffer = expectedToErrorOr(getOriginal(*FileID)); + if (!Buffer) { + // Cache CAS failures. Not going to recover later. + Entry.EC = Buffer.getError(); + return LookupPathResult{&Entry, std::error_code()}; + } + + Entry.Buffer = std::move(*Buffer); + Entry.Status = llvm::vfs::Status( + PathRef, MaybeStatus->getUniqueID(), + MaybeStatus->getLastModificationTime(), MaybeStatus->getUser(), + MaybeStatus->getGroup(), Entry.Buffer->size(), MaybeStatus->getType(), + MaybeStatus->getPermissions()); + return LookupPathResult{&Entry, std::error_code()}; +} + +llvm::ErrorOr +DependencyScanningCASFilesystem::status(const Twine &Path) { + LookupPathResult Result = lookupPath(Path); + if (!Result.Entry) + return std::move(Result.Status); + if (Result.Entry->EC) + return Result.Entry->EC; + return Result.Entry->Status; +} + +bool DependencyScanningCASFilesystem::exists(const Twine &Path) { + // Existence check does not require caching the result at the dependency + // scanning level. The CachingOnDiskFileSystem tracks the exists call, which + // ensures it is included in any resulting CASFileSystem. + return getCachingFS().exists(Path); +} + +IntrusiveRefCntPtr +DependencyScanningCASFilesystem::createThreadSafeProxyFS() { + llvm::report_fatal_error("not implemented"); +} + +namespace { + +class DepScanFile final : public llvm::vfs::File { +public: + DepScanFile(StringRef Buffer, std::optional CASContents, + llvm::vfs::Status Stat) + : Buffer(Buffer), CASContents(std::move(CASContents)), + Stat(std::move(Stat)) {} + + llvm::ErrorOr status() override { return Stat; } + + llvm::ErrorOr> + getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, + bool IsVolatile) override { + SmallString<256> Storage; + return llvm::MemoryBuffer::getMemBuffer(Buffer, Name.toStringRef(Storage)); + } + + llvm::ErrorOr> + getObjectRefForContent() override { + return CASContents; + } + + std::error_code close() override { return {}; } + +private: + StringRef Buffer; + std::optional CASContents; + llvm::vfs::Status Stat; +}; + +} // end anonymous namespace + +llvm::ErrorOr> +DependencyScanningCASFilesystem::openFileForRead(const Twine &Path) { + LookupPathResult Result = lookupPath(Path); + if (!Result.Entry) { + if (std::error_code EC = Result.Status.getError()) + return EC; + assert(Result.Status->getType() == + llvm::sys::fs::file_type::directory_file); + return std::make_error_code(std::errc::is_a_directory); + } + if (Result.Entry->EC) + return Result.Entry->EC; + + return std::make_unique( + *Result.Entry->Buffer, Result.Entry->CASContents, Result.Entry->Status); +} + +std::optional> +DependencyScanningCASFilesystem::getDirectiveTokens(const Twine &Path) { + LookupPathResult Result = lookupPath(Path); + + if (Result.Entry) { + if (Result.Entry->DepDirectives.empty()) { + SmallString<256> PathStorage; + StringRef PathRef = Path.toStringRef(PathStorage); + FileEntry &Entry = const_cast(*Result.Entry); + scanForDirectives(*Entry.CASContents, PathRef, Entry.DepTokens, + Entry.DepDirectives); + } + + if (!Result.Entry->DepDirectives.empty()) + return ArrayRef(Result.Entry->DepDirectives); + } + return std::nullopt; +} diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp index 191b6ef439860..b88d83d072cb7 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp @@ -34,11 +34,16 @@ DependencyScanningWorkerFilesystem::readFile(StringRef Filename) { return MaybeBuffer.getError(); auto Buffer = std::move(*MaybeBuffer); + auto MaybeCASContents = File->getObjectRefForContent(); + if (!MaybeCASContents) + return MaybeCASContents.getError(); + auto CASContents = std::move(*MaybeCASContents); + // If the file size changed between read and stat, pretend it didn't. if (Stat.getSize() != Buffer->getBufferSize()) Stat = llvm::vfs::Status::copyWithNewSize(Stat, Buffer->getBufferSize()); - return TentativeEntry(Stat, std::move(Buffer)); + return TentativeEntry(Stat, std::move(Buffer), std::move(CASContents)); } bool DependencyScanningWorkerFilesystem::ensureDirectiveTokensArePopulated( @@ -172,7 +177,8 @@ DependencyScanningFilesystemSharedCache::CacheShard:: const CachedFileSystemEntry & DependencyScanningFilesystemSharedCache::CacheShard::getOrEmplaceEntryForUID( llvm::sys::fs::UniqueID UID, llvm::vfs::Status Stat, - std::unique_ptr Contents) { + std::unique_ptr Contents, + std::optional CASContents) { std::lock_guard LockGuard(CacheLock); auto [It, Inserted] = EntriesByUID.insert({UID, nullptr}); auto &CachedEntry = It->getSecond(); @@ -180,7 +186,7 @@ DependencyScanningFilesystemSharedCache::CacheShard::getOrEmplaceEntryForUID( CachedFileContents *StoredContents = nullptr; if (Contents) StoredContents = new (ContentsStorage.Allocate()) - CachedFileContents(std::move(Contents)); + CachedFileContents(std::move(Contents), std::move(CASContents)); CachedEntry = new (EntryStorage.Allocate()) CachedFileSystemEntry(std::move(Stat), StoredContents); } @@ -246,9 +252,9 @@ const CachedFileSystemEntry & DependencyScanningWorkerFilesystem::getOrEmplaceSharedEntryForUID( TentativeEntry TEntry) { auto &Shard = SharedCache.getShardForUID(TEntry.Status.getUniqueID()); - return Shard.getOrEmplaceEntryForUID(TEntry.Status.getUniqueID(), - std::move(TEntry.Status), - std::move(TEntry.Contents)); + return Shard.getOrEmplaceEntryForUID( + TEntry.Status.getUniqueID(), std::move(TEntry.Status), + std::move(TEntry.Contents), std::move(TEntry.CASContents)); } const CachedFileSystemEntry * @@ -340,8 +346,9 @@ namespace { class DepScanFile final : public llvm::vfs::File { public: DepScanFile(std::unique_ptr Buffer, - llvm::vfs::Status Stat) - : Buffer(std::move(Buffer)), Stat(std::move(Stat)) {} + std::optional CASContents, llvm::vfs::Status Stat) + : Buffer(std::move(Buffer)), CASContents(std::move(CASContents)), + Stat(std::move(Stat)) {} static llvm::ErrorOr> create(EntryRef Entry); @@ -353,10 +360,16 @@ class DepScanFile final : public llvm::vfs::File { return std::move(Buffer); } + llvm::ErrorOr> + getObjectRefForContent() override { + return CASContents; + } + std::error_code close() override { return {}; } private: std::unique_ptr Buffer; + std::optional CASContents; llvm::vfs::Status Stat; }; @@ -373,7 +386,7 @@ DepScanFile::create(EntryRef Entry) { llvm::MemoryBuffer::getMemBuffer(Entry.getContents(), Entry.getStatus().getName(), /*RequiresNullTerminator=*/false), - Entry.getStatus()); + Entry.getObjectRefForContent(), Entry.getStatus()); return llvm::ErrorOr>( std::unique_ptr(std::move(Result))); diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp index 96fe40c079c65..72241111365f1 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp @@ -7,13 +7,31 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" +#include "clang/Basic/BitmaskEnum.h" +#include "llvm/CAS/ActionCache.h" +#include "llvm/CAS/CachingOnDiskFileSystem.h" +#include "llvm/CAS/ObjectStore.h" using namespace clang; using namespace tooling; using namespace dependencies; DependencyScanningService::DependencyScanningService( - ScanningMode Mode, ScanningOutputFormat Format, + ScanningMode Mode, ScanningOutputFormat Format, CASOptions CASOpts, + std::shared_ptr CAS, + std::shared_ptr Cache, + IntrusiveRefCntPtr SharedFS, ScanningOptimizations OptimizeArgs, bool EagerLoadModules, bool TraceVFS) - : Mode(Mode), Format(Format), OptimizeArgs(OptimizeArgs), - EagerLoadModules(EagerLoadModules), TraceVFS(TraceVFS) {} + : Mode(Mode), Format(Format), CASOpts(std::move(CASOpts)), + CAS(std::move(CAS)), Cache(std::move(Cache)), OptimizeArgs(OptimizeArgs), + EagerLoadModules(EagerLoadModules), TraceVFS(TraceVFS), + SharedFS(std::move(SharedFS)) { + if (!this->SharedFS) + SharedCache.emplace(); + + // The FullIncludeTree output format completely subsumes header search and + // VFS optimizations due to how it works. Disable these optimizations so + // we're not doing unneeded work. + if (Format == ScanningOutputFormat::FullIncludeTree) + this->OptimizeArgs &= ~ScanningOptimizations::FullIncludeTreeIrrelevant; +} diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp index b015e79f400cf..e158b89aa21e6 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp @@ -7,12 +7,17 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" +#include "CachingActions.h" +#include "clang/CAS/IncludeTree.h" +#include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/Utils.h" -#include +#include "clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h" +#include "llvm/CAS/ObjectStore.h" using namespace clang; using namespace tooling; using namespace dependencies; +using llvm::Error; DependencyScanningTool::DependencyScanningTool( DependencyScanningService &Service, @@ -83,6 +88,127 @@ llvm::Expected DependencyScanningTool::getDependencyFile( return Output; } +namespace { +class EmptyDependencyConsumer : public DependencyConsumer { + void + handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {} + + void handleFileDependency(StringRef Filename) override {} + + void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {} + + void handleModuleDependency(ModuleDeps MD) override {} + + void handleDirectModuleDependency(ModuleID ID) override {} + + void handleContextHash(std::string Hash) override {} +}; + +/// Returns a CAS tree containing the dependencies. +class GetDependencyTree : public EmptyDependencyConsumer { +public: + void handleCASFileSystemRootID(std::string ID) override { + CASFileSystemRootID = ID; + } + + Expected getTree() { + if (CASFileSystemRootID) { + auto ID = FS.getCAS().parseID(*CASFileSystemRootID); + if (!ID) + return ID.takeError(); + return FS.getCAS().getProxy(*ID); + } + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "failed to get casfs"); + } + + GetDependencyTree(llvm::cas::CachingOnDiskFileSystem &FS) : FS(FS) {} + +private: + llvm::cas::CachingOnDiskFileSystem &FS; + std::optional CASFileSystemRootID; +}; + +/// Returns an IncludeTree containing the dependencies. +class GetIncludeTree : public EmptyDependencyConsumer { +public: + void handleIncludeTreeID(std::string ID) override { IncludeTreeID = ID; } + + Expected getIncludeTree() { + if (IncludeTreeID) { + auto ID = DB.parseID(*IncludeTreeID); + if (!ID) + return ID.takeError(); + auto Ref = DB.getReference(*ID); + if (!Ref) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + llvm::Twine("missing expected include-tree ") + ID->toString()); + return cas::IncludeTreeRoot::get(DB, *Ref); + } + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "failed to get include-tree"); + } + + GetIncludeTree(cas::ObjectStore &DB) : DB(DB) {} + +private: + cas::ObjectStore &DB; + std::optional IncludeTreeID; +}; +} + +llvm::Expected +DependencyScanningTool::getDependencyTree( + const std::vector &CommandLine, StringRef CWD) { + GetDependencyTree Consumer(*Worker.getCASFS()); + auto Controller = createCASFSActionController(nullptr, *Worker.getCASFS()); + auto Result = + Worker.computeDependencies(CWD, CommandLine, Consumer, *Controller); + if (Result) + return std::move(Result); + return Consumer.getTree(); +} + +llvm::Expected +DependencyScanningTool::getDependencyTreeFromCompilerInvocation( + std::shared_ptr Invocation, StringRef CWD, + DiagnosticConsumer &DiagsConsumer, raw_ostream *VerboseOS, + bool DiagGenerationAsCompilation) { + GetDependencyTree Consumer(*Worker.getCASFS()); + auto Controller = createCASFSActionController(nullptr, *Worker.getCASFS()); + Worker.computeDependenciesFromCompilerInvocation( + std::move(Invocation), CWD, Consumer, *Controller, DiagsConsumer, + VerboseOS, DiagGenerationAsCompilation); + return Consumer.getTree(); +} + +Expected DependencyScanningTool::getIncludeTree( + cas::ObjectStore &DB, const std::vector &CommandLine, + StringRef CWD, LookupModuleOutputCallback LookupModuleOutput) { + GetIncludeTree Consumer(DB); + auto Controller = createIncludeTreeActionController(LookupModuleOutput, DB); + llvm::Error Result = + Worker.computeDependencies(CWD, CommandLine, Consumer, *Controller); + if (Result) + return std::move(Result); + return Consumer.getIncludeTree(); +} + +Expected +DependencyScanningTool::getIncludeTreeFromCompilerInvocation( + cas::ObjectStore &DB, std::shared_ptr Invocation, + StringRef CWD, LookupModuleOutputCallback LookupModuleOutput, + DiagnosticConsumer &DiagsConsumer, raw_ostream *VerboseOS, + bool DiagGenerationAsCompilation) { + GetIncludeTree Consumer(DB); + auto Controller = createIncludeTreeActionController(LookupModuleOutput, DB); + Worker.computeDependenciesFromCompilerInvocation( + std::move(Invocation), CWD, Consumer, *Controller, DiagsConsumer, + VerboseOS, DiagGenerationAsCompilation); + return Consumer.getIncludeTree(); +} + llvm::Expected DependencyScanningTool::getP1689ModuleDependencyFile( const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput, std::string &MakeformatOutputPath) { @@ -145,10 +271,9 @@ DependencyScanningTool::getTranslationUnitDependencies( LookupModuleOutputCallback LookupModuleOutput, std::optional TUBuffer) { FullDependencyConsumer Consumer(AlreadySeen); - CallbackActionController Controller(LookupModuleOutput); + auto Controller = createActionController(LookupModuleOutput); llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer, - Controller, TUBuffer); - + *Controller, TUBuffer); if (Result) return std::move(Result); return Consumer.takeTranslationUnitDeps(); @@ -159,9 +284,9 @@ llvm::Expected DependencyScanningTool::getModuleDependencies( StringRef CWD, const llvm::DenseSet &AlreadySeen, LookupModuleOutputCallback LookupModuleOutput) { FullDependencyConsumer Consumer(AlreadySeen); - CallbackActionController Controller(LookupModuleOutput); + auto Controller = createActionController(LookupModuleOutput); llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer, - Controller, ModuleName); + *Controller, ModuleName); if (Result) return std::move(Result); return Consumer.takeModuleGraphDeps(); @@ -174,6 +299,8 @@ TranslationUnitDeps FullDependencyConsumer::takeTranslationUnitDeps() { TU.FileDeps = std::move(Dependencies); TU.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps); TU.Commands = std::move(Commands); + TU.CASFileSystemRootID = std::move(CASFileSystemRootID); + TU.IncludeTreeID = std::move(IncludeTreeID); for (auto &&M : ClangModuleDeps) { auto &MD = M.second; @@ -204,3 +331,21 @@ ModuleDepsGraph FullDependencyConsumer::takeModuleGraphDeps() { } CallbackActionController::~CallbackActionController() {} + +std::unique_ptr +DependencyScanningTool::createActionController( + DependencyScanningWorker &Worker, + LookupModuleOutputCallback LookupModuleOutput) { + if (Worker.getScanningFormat() == ScanningOutputFormat::FullIncludeTree) + return createIncludeTreeActionController(LookupModuleOutput, + *Worker.getCAS()); + if (auto CacheFS = Worker.getCASFS()) + return createCASFSActionController(LookupModuleOutput, *CacheFS); + return std::make_unique(LookupModuleOutput); +} + +std::unique_ptr +DependencyScanningTool::createActionController( + LookupModuleOutputCallback LookupModuleOutput) { + return createActionController(Worker, std::move(LookupModuleOutput)); +} diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp index 8e05a678fcdbc..37bcb4499e311 100644 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" +#include "clang/Basic/DiagnosticCAS.h" #include "clang/Basic/DiagnosticDriver.h" #include "clang/Basic/DiagnosticFrontend.h" #include "clang/Basic/DiagnosticSerialization.h" @@ -14,9 +15,11 @@ #include "clang/Driver/Driver.h" #include "clang/Driver/Job.h" #include "clang/Driver/Tool.h" +#include "clang/Frontend/CompileJobCacheKey.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/MultiplexConsumer.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/Utils.h" #include "clang/Lex/PreprocessorOptions.h" @@ -24,18 +27,24 @@ #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" #include "clang/Tooling/DependencyScanning/InProcessModuleCache.h" #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" +#include "clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/ScopeExit.h" +#include "llvm/CAS/CASProvidingFileSystem.h" +#include "llvm/CAS/CachingOnDiskFileSystem.h" +#include "llvm/CAS/ObjectStore.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/PrefixMapper.h" #include "llvm/TargetParser/Host.h" #include using namespace clang; using namespace tooling; using namespace dependencies; +using llvm::Error; namespace { @@ -43,9 +52,10 @@ namespace { class DependencyConsumerForwarder : public DependencyFileGenerator { public: DependencyConsumerForwarder(std::unique_ptr Opts, - StringRef WorkingDirectory, DependencyConsumer &C) + StringRef WorkingDirectory, DependencyConsumer &C, + bool EmitDependencyFile) : DependencyFileGenerator(*Opts), WorkingDirectory(WorkingDirectory), - Opts(std::move(Opts)), C(C) {} + Opts(std::move(Opts)), C(C), EmitDependencyFile(EmitDependencyFile) {} void finishedMainFile(DiagnosticsEngine &Diags) override { C.handleDependencyOutputOpts(*Opts); @@ -56,12 +66,15 @@ class DependencyConsumerForwarder : public DependencyFileGenerator { llvm::sys::fs::make_absolute(WorkingDirectory, CanonPath); C.handleFileDependency(CanonPath); } + if (EmitDependencyFile) + DependencyFileGenerator::finishedMainFile(Diags); } private: StringRef WorkingDirectory; std::unique_ptr Opts; DependencyConsumer &C; + bool EmitDependencyFile = false; }; static bool checkHeaderSearchPaths(const HeaderSearchOptions &HSOpts, @@ -95,16 +108,16 @@ using PrebuiltModuleFilesT = decltype(HeaderSearchOptions::PrebuiltModuleFiles); /// whether prebuilt modules fully resolve in stable directories. class PrebuiltModuleListener : public ASTReaderListener { public: - PrebuiltModuleListener(PrebuiltModuleFilesT &PrebuiltModuleFiles, + PrebuiltModuleListener(CompilerInstance &CI, + PrebuiltModuleFilesT &PrebuiltModuleFiles, llvm::SmallVector &NewModuleFiles, PrebuiltModulesAttrsMap &PrebuiltModulesASTMap, - const HeaderSearchOptions &HSOpts, - const LangOptions &LangOpts, DiagnosticsEngine &Diags, + DiagnosticsEngine &Diags, const ArrayRef StableDirs) - : PrebuiltModuleFiles(PrebuiltModuleFiles), + : CI(CI), PrebuiltModuleFiles(PrebuiltModuleFiles), NewModuleFiles(NewModuleFiles), - PrebuiltModulesASTMap(PrebuiltModulesASTMap), ExistingHSOpts(HSOpts), - ExistingLangOpts(LangOpts), Diags(Diags), StableDirs(StableDirs) {} + PrebuiltModulesASTMap(PrebuiltModulesASTMap), Diags(Diags), + StableDirs(StableDirs) {} bool needsImportVisitation() const override { return true; } bool needsInputFileVisitation() override { return true; } @@ -188,15 +201,22 @@ class PrebuiltModuleListener : public ASTReaderListener { llvm::StringSet<>(llvm::from_range, HSOpts.VFSOverlayFiles)); return checkHeaderSearchPaths( - HSOpts, ExistingHSOpts, Complain ? &Diags : nullptr, ExistingLangOpts); + HSOpts, CI.getHeaderSearchOpts(), Complain ? &Diags : nullptr, CI.getLangOpts()); + } + + bool readModuleCacheKey(StringRef ModuleName, StringRef Filename, + StringRef CacheKey) override { + CI.getFrontendOpts().ModuleCacheKeys.emplace_back(std::string(Filename), + std::string(CacheKey)); + // FIXME: add name/path of the importing module? + return CI.addCachedModuleFile(Filename, CacheKey, "imported module"); } private: + CompilerInstance &CI; PrebuiltModuleFilesT &PrebuiltModuleFiles; llvm::SmallVector &NewModuleFiles; PrebuiltModulesAttrsMap &PrebuiltModulesASTMap; - const HeaderSearchOptions &ExistingHSOpts; - const LangOptions &ExistingLangOpts; DiagnosticsEngine &Diags; std::string CurrentFile; const ArrayRef StableDirs; @@ -212,10 +232,8 @@ static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename, const ArrayRef StableDirs) { // List of module files to be processed. llvm::SmallVector Worklist; - - PrebuiltModuleListener Listener(ModuleFiles, Worklist, PrebuiltModulesASTMap, - CI.getHeaderSearchOpts(), CI.getLangOpts(), - Diags, StableDirs); + PrebuiltModuleListener Listener(CI, ModuleFiles, Worklist, + PrebuiltModulesASTMap, Diags, StableDirs); Listener.visitModuleFile(PrebuiltModuleFilename, serialization::MK_ExplicitModule); @@ -349,6 +367,98 @@ static void canonicalizeDefines(PreprocessorOptions &PPOpts) { std::swap(PPOpts.Macros, NewMacros); } +/// Builds a dependency file after reversing prefix mappings. This allows +/// emitting a .d file that has real paths where they would otherwise be +/// canonicalized. +class ReversePrefixMappingDependencyFileGenerator + : public DependencyFileGenerator { + llvm::PrefixMapper ReverseMapper; + +public: + ReversePrefixMappingDependencyFileGenerator( + const DependencyOutputOptions &Opts) + : DependencyFileGenerator(Opts) {} + + void initialize(const CompilerInvocation &CI) { + llvm::PrefixMapper Mapper; + DepscanPrefixMapping::configurePrefixMapper(CI, Mapper); + if (Mapper.empty()) + return; + + ReverseMapper.addInverseRange(Mapper.getMappings()); + ReverseMapper.sort(); + } + + void maybeAddDependency(StringRef Filename, bool FromModule, bool IsSystem, + bool IsModuleFile, bool IsMissing) override { + if (ReverseMapper.empty()) + return DependencyFileGenerator::maybeAddDependency( + Filename, FromModule, IsSystem, IsModuleFile, IsMissing); + + // We may get canonicalized paths if prefix headers/PCH are used, so make + // sure to remap them back to original source paths. + SmallString<256> New{Filename}; + ReverseMapper.mapInPlace(New); + return DependencyFileGenerator::maybeAddDependency( + New, FromModule, IsSystem, IsModuleFile, IsMissing); + } +}; + +/// See \c WrapScanModuleBuildAction. +class WrapScanModuleBuildConsumer : public ASTConsumer { +public: + WrapScanModuleBuildConsumer(CompilerInstance &CI, + DependencyActionController &Controller) + : CI(CI), Controller(Controller) {} + + void HandleTranslationUnit(ASTContext &Ctx) override { + if (auto E = Controller.finalizeModuleBuild(CI)) + Ctx.getDiagnostics().Report(diag::err_cas_depscan_failed) << std::move(E); + } + +private: + CompilerInstance &CI; + DependencyActionController &Controller; +}; + +/// A wrapper for implicit module build actions in the scanner. +class WrapScanModuleBuildAction : public WrapperFrontendAction { +public: + WrapScanModuleBuildAction(std::unique_ptr WrappedAction, + DependencyActionController &Controller) + : WrapperFrontendAction(std::move(WrappedAction)), + Controller(Controller) {} + +private: + bool BeginInvocation(CompilerInstance &CI) override { + if (auto E = Controller.initializeModuleBuild(CI)) { + CI.getDiagnostics().Report(diag::err_cas_depscan_failed) << std::move(E); + return false; + } + return WrapperFrontendAction::BeginInvocation(CI); + } + + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); + if (!OtherConsumer) + return nullptr; + Module *M = CI.getPreprocessor().getCurrentModule(); + assert(M && "WrapScanModuleBuildAction should only be used with module"); + if (!M) + return OtherConsumer; + auto Consumer = + std::make_unique(CI, Controller); + std::vector> Consumers; + Consumers.push_back(std::move(Consumer)); + Consumers.push_back(std::move(OtherConsumer)); + return std::make_unique(std::move(Consumers)); + } + +private: + DependencyActionController &Controller; +}; + class ScanningDependencyDirectivesGetter : public DependencyDirectivesGetter { DependencyScanningWorkerFilesystem *DepFS; @@ -375,6 +485,26 @@ class ScanningDependencyDirectivesGetter : public DependencyDirectivesGetter { } }; +// FIXME: Make this thread-safe by pulling the FS out of `FileManager`. +class CASDependencyDirectivesGetter : public DependencyDirectivesGetter { + DependencyScanningCASFilesystem *DepCASFS; + +public: + CASDependencyDirectivesGetter(DependencyScanningCASFilesystem *DepCASFS) + : DepCASFS(DepCASFS) {} + + std::unique_ptr + cloneFor(FileManager &FileMgr) override { + (void)FileMgr; + return std::make_unique(DepCASFS); + } + + std::optional> + operator()(FileEntryRef File) override { + return DepCASFS->getDirectiveTokens(File.getName()); + } +}; + /// A clang tool that runs the preprocessor in a mode that's optimized for /// dependency scanning for the given compiler invocation. class DependencyScanningAction : public tooling::ToolAction { @@ -383,10 +513,19 @@ class DependencyScanningAction : public tooling::ToolAction { DependencyScanningService &Service, StringRef WorkingDirectory, DependencyConsumer &Consumer, DependencyActionController &Controller, llvm::IntrusiveRefCntPtr DepFS, - bool DisableFree, std::optional ModuleName = std::nullopt) - : Service(Service), WorkingDirectory(WorkingDirectory), - Consumer(Consumer), Controller(Controller), DepFS(std::move(DepFS)), - DisableFree(DisableFree), ModuleName(ModuleName) {} + llvm::IntrusiveRefCntPtr DepCASFS, + llvm::IntrusiveRefCntPtr CacheFS, + bool DisableFree, bool EmitDependencyFile, + bool DiagGenerationAsCompilation, const CASOptions &CASOpts, + std::optional ModuleName = std::nullopt, + raw_ostream *VerboseOS = nullptr) + : Service(Service), WorkingDirectory(WorkingDirectory), Consumer(Consumer), + Controller(Controller), DepFS(std::move(DepFS)), + DepCASFS(std::move(DepCASFS)), CacheFS(std::move(CacheFS)), + DisableFree(DisableFree), + CASOpts(CASOpts), EmitDependencyFile(EmitDependencyFile), + DiagGenerationAsCompilation(DiagGenerationAsCompilation), + ModuleName(ModuleName), VerboseOS(VerboseOS) {} bool runInvocation(std::shared_ptr Invocation, FileManager *DriverFileMgr, @@ -400,11 +539,25 @@ class DependencyScanningAction : public tooling::ToolAction { canonicalizeDefines(OriginalInvocation.getPreprocessorOpts()); if (Scanned) { + CompilerInstance &ScanInstance = *ScanInstanceStorage; + auto reportError = [&ScanInstance](Error &&E) -> bool { + ScanInstance.getDiagnostics().Report(diag::err_cas_depscan_failed) + << std::move(E); + return false; + }; + // Scanning runs once for the first -cc1 invocation in a chain of driver // jobs. For any dependent jobs, reuse the scanning result and just // update the LastCC1Arguments to correspond to the new invocation. // FIXME: to support multi-arch builds, each arch requires a separate scan - setLastCC1Arguments(std::move(OriginalInvocation)); + if (MDC) + MDC->applyDiscoveredDependencies(OriginalInvocation); + + if (Error E = Controller.finalize(ScanInstance, OriginalInvocation)) + return reportError(std::move(E)); + + LastCC1Arguments = OriginalInvocation.getCC1CommandLine(); + LastCC1CacheKey = Controller.getCacheKey(OriginalInvocation); return true; } @@ -415,15 +568,19 @@ class DependencyScanningAction : public tooling::ToolAction { ScanInstanceStorage.emplace(std::move(PCHContainerOps), ModCache.get()); CompilerInstance &ScanInstance = *ScanInstanceStorage; ScanInstance.setInvocation(std::move(Invocation)); + ScanInstance.getInvocation().getCASOpts() = CASOpts; ScanInstance.setBuildingModule(false); // Create the compiler's actual diagnostics engine. - sanitizeDiagOpts(ScanInstance.getDiagnosticOpts()); + if (!DiagGenerationAsCompilation) + sanitizeDiagOpts(ScanInstance.getDiagnosticOpts()); assert(!DiagConsumerFinished && "attempt to reuse finished consumer"); ScanInstance.createDiagnostics(DriverFileMgr->getVirtualFileSystem(), DiagConsumer, /*ShouldOwnClient=*/false); if (!ScanInstance.hasDiagnostics()) return false; + if (VerboseOS) + ScanInstance.setVerboseOutputStream(*VerboseOS); ScanInstance.getPreprocessorOpts().AllowPCHWithDifferentModulesCachePath = true; @@ -433,6 +590,8 @@ class DependencyScanningAction : public tooling::ToolAction { // This will prevent us compiling individual modules asynchronously since // FileManager is not thread-safe, but it does improve performance for now. ScanInstance.getFrontendOpts().ModulesShareFileManager = true; + if (DepCASFS) + ScanInstance.getFrontendOpts().ModulesShareFileManager = false; ScanInstance.getHeaderSearchOpts().ModuleFormat = "raw"; ScanInstance.getHeaderSearchOpts().ModulesIncludeVFSUsage = any(Service.getOptimizeArgs() & ScanningOptimizations::VFS); @@ -458,6 +617,11 @@ class DependencyScanningAction : public tooling::ToolAction { std::make_unique(*FileMgr)); } + // CAS Implementation. + if (DepCASFS) + ScanInstance.setDependencyDirectivesGetter( + std::make_unique(DepCASFS.get())); + ScanInstance.createSourceManager(*FileMgr); // Create a collection of stable directories derived from the ScanInstance @@ -498,20 +662,56 @@ class DependencyScanningAction : public tooling::ToolAction { Opts->Targets = { deduceDepTarget(ScanInstance.getFrontendOpts().OutputFile, ScanInstance.getFrontendOpts().Inputs)}; - Opts->IncludeSystemHeaders = true; + if (Service.getFormat() == ScanningOutputFormat::Make) { + // Only 'Make' scanning needs to force this because that mode depends on + // getting the dependencies directly from \p DependencyFileGenerator. + Opts->IncludeSystemHeaders = true; + } + auto reportError = [&ScanInstance](Error &&E) -> bool { + ScanInstance.getDiagnostics().Report(diag::err_cas_depscan_failed) + << std::move(E); + return false; + }; + + // FIXME: The caller APIs in \p DependencyScanningTool expect a specific + // DependencyCollector to get attached to the preprocessor in order to + // function properly (e.g. \p FullDependencyConsumer needs \p + // ModuleDepCollector) but this association is very indirect via the value + // of the \p ScanningOutputFormat. We should remove \p Format field from + // \p DependencyScanningAction, and have the callers pass in a + // “DependencyCollector factory” so the connection of collector<->consumer + // is explicit in each \p DependencyScanningTool function. switch (Service.getFormat()) { case ScanningOutputFormat::Make: + case ScanningOutputFormat::Tree: ScanInstance.addDependencyCollector( std::make_shared( - std::move(Opts), WorkingDirectory, Consumer)); + std::move(Opts), WorkingDirectory, Consumer, EmitDependencyFile)); break; + case ScanningOutputFormat::IncludeTree: case ScanningOutputFormat::P1689: case ScanningOutputFormat::Full: + case ScanningOutputFormat::FullTree: + case ScanningOutputFormat::FullIncludeTree: + if (EmitDependencyFile) { + auto DFG = + std::make_shared( + *Opts); + DFG->initialize(ScanInstance.getInvocation()); + ScanInstance.addDependencyCollector(std::move(DFG)); + } + MDC = std::make_shared( Service, std::move(Opts), ScanInstance, Consumer, Controller, OriginalInvocation, std::move(PrebuiltModulesASTMap), StableDirs); ScanInstance.addDependencyCollector(MDC); + ScanInstance.setGenModuleActionWrapper( + [&Controller = Controller](const FrontendOptions &Opts, + std::unique_ptr Wrapped) { + return std::make_unique( + std::move(Wrapped), Controller); + }); break; } @@ -539,21 +739,43 @@ class DependencyScanningAction : public tooling::ToolAction { else Action = std::make_unique(); + // Normally this would be handled by GeneratePCHAction + if (ScanInstance.getFrontendOpts().ProgramAction == frontend::GeneratePCH) + ScanInstance.getLangOpts().CompilingPCH = true; + + if (Error E = Controller.initialize(ScanInstance, OriginalInvocation)) + return reportError(std::move(E)); + if (ScanInstance.getDiagnostics().hasErrorOccurred()) return false; - const bool Result = ScanInstance.ExecuteAction(*Action); - // ExecuteAction is responsible for calling finish. DiagConsumerFinished = true; - if (Result) - setLastCC1Arguments(std::move(OriginalInvocation)); + if (!ScanInstance.ExecuteAction(*Action)) + return false; + + if (MDC) + MDC->applyDiscoveredDependencies(OriginalInvocation); + + if (Error E = Controller.finalize(ScanInstance, OriginalInvocation)) + return reportError(std::move(E)); + + // Forward any CAS results to consumer. + std::string ID = OriginalInvocation.getFileSystemOpts().CASFileSystemRootID; + if (!ID.empty()) + Consumer.handleCASFileSystemRootID(std::move(ID)); + ID = OriginalInvocation.getFrontendOpts().CASIncludeTreeID; + if (!ID.empty()) + Consumer.handleIncludeTreeID(std::move(ID)); + + LastCC1Arguments = OriginalInvocation.getCC1CommandLine(); + LastCC1CacheKey = Controller.getCacheKey(OriginalInvocation); // Propagate the statistics to the parent FileManager. DriverFileMgr->AddStats(ScanInstance.getFileManager()); - return Result; + return true; } bool hasScanned() const { return Scanned; } @@ -568,11 +790,22 @@ class DependencyScanningAction : public tooling::ToolAction { return Result; } -private: - void setLastCC1Arguments(CompilerInvocation &&CI) { - if (MDC) - MDC->applyDiscoveredDependencies(CI); - LastCC1Arguments = CI.getCC1CommandLine(); + std::optional takeLastCC1CacheKey() { + std::optional Result; + std::swap(Result, LastCC1CacheKey); + return Result; + } + + IntrusiveRefCntPtr getDepScanFS() { + if (DepFS) { + assert(!DepCASFS && "CAS DepFS should not be set"); + return DepFS; + } + if (DepCASFS) { + assert(!DepFS && "DepFS should not be set"); + return DepCASFS; + } + return nullptr; } DependencyScanningService &Service; @@ -580,13 +813,20 @@ class DependencyScanningAction : public tooling::ToolAction { DependencyConsumer &Consumer; DependencyActionController &Controller; llvm::IntrusiveRefCntPtr DepFS; + llvm::IntrusiveRefCntPtr DepCASFS; + llvm::IntrusiveRefCntPtr CacheFS; bool DisableFree; + const CASOptions &CASOpts; + bool EmitDependencyFile = false; + bool DiagGenerationAsCompilation; std::optional ModuleName; std::optional ScanInstanceStorage; std::shared_ptr MDC; std::vector LastCC1Arguments; + std::optional LastCC1CacheKey; bool Scanned = false; bool DiagConsumerFinished = false; + raw_ostream *VerboseOS; }; } // end anonymous namespace @@ -594,7 +834,8 @@ class DependencyScanningAction : public tooling::ToolAction { DependencyScanningWorker::DependencyScanningWorker( DependencyScanningService &Service, llvm::IntrusiveRefCntPtr FS) - : Service(Service) { + : Service(Service), + CASOpts(Service.getCASOpts()), CAS(Service.getCAS()) { PCHContainerOps = std::make_shared(); // We need to read object files from PCH built outside the scanner. PCHContainerOps->registerReader( @@ -605,6 +846,13 @@ DependencyScanningWorker::DependencyScanningWorker( if (Service.shouldTraceVFS()) FS = llvm::makeIntrusiveRefCnt(std::move(FS)); + if (Service.useCASFS()) { + CacheFS = Service.getSharedFS().createProxyFS(); + DepCASFS = new DependencyScanningCASFilesystem(CacheFS, *Service.getCache()); + BaseFS = DepCASFS; + return; + } + switch (Service.getMode()) { case ScanningMode::DependencyDirectivesScan: DepFS = @@ -618,6 +866,11 @@ DependencyScanningWorker::DependencyScanningWorker( } } +llvm::IntrusiveRefCntPtr +DependencyScanningWorker::getOrCreateFileManager() const { + return new FileManager(FileSystemOptions(), BaseFS); +} + static std::unique_ptr createDiagOptions(const std::vector &CommandLine) { std::vector CLI; @@ -720,7 +973,9 @@ static bool createAndRunToolInvocation( return false; std::vector Args = Action.takeLastCC1Arguments(); - Consumer.handleBuildCommand({std::move(Executable), std::move(Args)}); + std::optional CacheKey = Action.takeLastCC1CacheKey(); + Consumer.handleBuildCommand( + {std::move(Executable), std::move(Args), std::move(CacheKey)}); return true; } @@ -750,9 +1005,12 @@ bool DependencyScanningWorker::scanDependencies( // in-process; preserve the original value, which is // always true for a driver invocation. bool DisableFree = true; - DependencyScanningAction Action(Service, WorkingDirectory, Consumer, - Controller, DepFS, DisableFree, ModuleName); - + DependencyScanningAction Action(Service, WorkingDirectory, Consumer, Controller, DepFS, + DepCASFS, CacheFS, + DisableFree, + /*EmitDependencyFile=*/false, + /*DiagGenerationAsCompilation=*/false, getCASOpts(), + ModuleName); bool Success = false; if (CommandLine[1] == "-cc1") { Success = createAndRunToolInvocation(CommandLine, Action, *FileMgr, @@ -765,7 +1023,8 @@ bool DependencyScanningWorker::scanDependencies( // consumer. Consumer.handleBuildCommand( {Cmd.getExecutable(), - {Cmd.getArguments().begin(), Cmd.getArguments().end()}}); + {Cmd.getArguments().begin(), Cmd.getArguments().end()}, + {}}); return true; } @@ -820,6 +1079,12 @@ bool DependencyScanningWorker::computeDependencies( llvm::IntrusiveRefCntPtr InMemoryOverlay = InMemoryFS; + // If we are using a CAS but not dependency CASFS, we need to provide the + // fake input file in a CASProvidingFS for include-tree. + if (CAS && !DepCASFS) + InMemoryOverlay = + llvm::cas::createCASProvidingFileSystem(CAS, std::move(InMemoryFS)); + OverlayFS->pushOverlay(InMemoryOverlay); ModifiedFS = OverlayFS; ModifiedCommandLine = CommandLine; @@ -855,6 +1120,12 @@ bool DependencyScanningWorker::computeDependencies( InMemoryFS->addFile(FakeInputPath, 0, llvm::MemoryBuffer::getMemBuffer("")); llvm::IntrusiveRefCntPtr InMemoryOverlay = InMemoryFS; + // If we are using a CAS but not dependency CASFS, we need to provide the + // fake input file in a CASProvidingFS for include-tree. + if (CAS && !DepCASFS) + InMemoryOverlay = + llvm::cas::createCASProvidingFileSystem(CAS, std::move(InMemoryFS)); + OverlayFS->pushOverlay(InMemoryOverlay); auto ModifiedCommandLine = CommandLine; ModifiedCommandLine.emplace_back(FakeInputPath); @@ -864,3 +1135,48 @@ bool DependencyScanningWorker::computeDependencies( } DependencyActionController::~DependencyActionController() {} + +void DependencyScanningWorker::computeDependenciesFromCompilerInvocation( + std::shared_ptr Invocation, StringRef WorkingDirectory, + DependencyConsumer &DepsConsumer, DependencyActionController &Controller, + DiagnosticConsumer &DiagsConsumer, raw_ostream *VerboseOS, + bool DiagGenerationAsCompilation) { + BaseFS->setCurrentWorkingDirectory(WorkingDirectory); + + // Adjust the invocation. + auto &Frontend = Invocation->getFrontendOpts(); + Frontend.OutputFile = "/dev/null"; + Frontend.DisableFree = false; + + // // Reset dependency options. + // Dependencies = DependencyOutputOptions(); + // Dependencies.IncludeSystemHeaders = true; + // Dependencies.OutputFile = "/dev/null"; + + // Make the output file path absolute relative to WorkingDirectory. + std::string &DepFile = Invocation->getDependencyOutputOpts().OutputFile; + if (!DepFile.empty() && !llvm::sys::path::is_absolute(DepFile)) { + // FIXME: On Windows, WorkingDirectory is insufficient for making an + // absolute path if OutputFile has a root name. + llvm::SmallString<128> Path = StringRef(DepFile); + llvm::sys::fs::make_absolute(WorkingDirectory, Path); + DepFile = Path.str().str(); + } + + // FIXME: EmitDependencyFile should only be set when it's for a real + // compilation. + DependencyScanningAction Action(Service, WorkingDirectory, DepsConsumer, + Controller, DepFS, DepCASFS, CacheFS, + /*DisableFree=*/false, + /*EmitDependencyFile=*/!DepFile.empty(), + DiagGenerationAsCompilation, getCASOpts(), + /*ModuleName=*/std::nullopt, VerboseOS); + + // Ignore result; we're just collecting dependencies. + // + // FIXME: will clients other than -cc1scand care? + IntrusiveRefCntPtr ActiveFiles = + new FileManager(Invocation->getFileSystemOpts(), BaseFS); + (void)Action.runInvocation(std::move(Invocation), ActiveFiles.get(), + PCHContainerOps, &DiagsConsumer); +} diff --git a/clang/lib/Tooling/DependencyScanning/IncludeTreeActionController.cpp b/clang/lib/Tooling/DependencyScanning/IncludeTreeActionController.cpp new file mode 100644 index 0000000000000..afee5dbf93aa0 --- /dev/null +++ b/clang/lib/Tooling/DependencyScanning/IncludeTreeActionController.cpp @@ -0,0 +1,945 @@ +//===- IncludeTreeActionController.cpp ------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CachingActions.h" +#include "clang/APINotes/APINotesManager.h" +#include "clang/APINotes/APINotesReader.h" +#include "clang/CAS/IncludeTree.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/CAS/ObjectStore.h" +#include "llvm/Support/PrefixMapper.h" +#include "llvm/Support/PrefixMappingFileSystem.h" + +using namespace clang; +using namespace tooling; +using namespace dependencies; +using llvm::Error; + +namespace { +class IncludeTreeBuilder; + +class IncludeTreeActionController : public CallbackActionController { +public: + IncludeTreeActionController(cas::ObjectStore &DB, + LookupModuleOutputCallback LookupOutput) + : CallbackActionController(LookupOutput), DB(DB) {} + + Expected getIncludeTree(); + +private: + Error initialize(CompilerInstance &ScanInstance, + CompilerInvocation &NewInvocation) override; + Error finalize(CompilerInstance &ScanInstance, + CompilerInvocation &NewInvocation) override; + std::optional + getCacheKey(const CompilerInvocation &NewInvocation) override; + + Error initializeModuleBuild(CompilerInstance &ModuleScanInstance) override; + Error finalizeModuleBuild(CompilerInstance &ModuleScanInstance) override; + Error finalizeModuleInvocation(CowCompilerInvocation &CI, + const ModuleDeps &MD) override; + +private: + IncludeTreeBuilder ¤t() { + assert(!BuilderStack.empty()); + return *BuilderStack.back(); + } + +private: + cas::ObjectStore &DB; + CASOptions CASOpts; + llvm::PrefixMapper PrefixMapper; + // IncludeTreePPCallbacks keeps a pointer to the current builder, so use a + // pointer so the builder cannot move when resizing. + SmallVector> BuilderStack; + std::optional IncludeTreeResult; + llvm::StringMap OutputToCacheKey; +}; + +/// Callbacks for building an include-tree for a given translation unit or +/// module. The \c IncludeTreeActionController is responsiblee for pushing and +/// popping builders from the stack as modules are required. +class IncludeTreeBuilder { +public: + IncludeTreeBuilder(cas::ObjectStore &DB, llvm::PrefixMapper &PrefixMapper) + : DB(DB), PrefixMapper(PrefixMapper) {} + + Expected + finishIncludeTree(CompilerInstance &ScanInstance, + CompilerInvocation &NewInvocation); + + void enteredInclude(Preprocessor &PP, FileID FID); + + void exitedInclude(Preprocessor &PP, FileID IncludedBy, FileID Include, + SourceLocation ExitLoc); + + void handleHasIncludeCheck(Preprocessor &PP, bool Result); + + void moduleImport(Preprocessor &PP, const Module *M, SourceLocation EndLoc); + + void enteredSubmodule(Preprocessor &PP, Module *M, SourceLocation ImportLoc, + bool ForPragma); + void exitedSubmodule(Preprocessor &PP, Module *M, SourceLocation ImportLoc, + bool ForPragma); + +private: + struct FilePPState { + SrcMgr::CharacteristicKind FileCharacteristic; + cas::ObjectRef File; + SmallVector Includes; + std::optional SubmoduleName; + llvm::SmallBitVector HasIncludeChecks; + }; + + Error addModuleInputs(ASTReader &Reader); + Expected getObjectForFile(Preprocessor &PP, FileID FID); + Expected + getObjectForFileNonCached(FileManager &FM, const SrcMgr::FileInfo &FI); + Expected getObjectForBuffer(const SrcMgr::FileInfo &FI); + Expected addToFileList(FileManager &FM, FileEntryRef FE); + Expected getCASTreeForFileIncludes(FilePPState &&PPState); + Expected createIncludeFile(StringRef Filename, + cas::ObjectRef Contents); + + bool hasErrorOccurred() const { return ErrorToReport.has_value(); } + + template std::optional check(Expected &&E) { + if (!E) { + ErrorToReport = E.takeError(); + return std::nullopt; + } + return *E; + } + +private: + cas::ObjectStore &DB; + llvm::PrefixMapper &PrefixMapper; + + std::optional PCHRef; + bool StartedEnteringIncludes = false; + // When a PCH is used this lists the filenames of the included files as they + // are recorded in the PCH, ordered by \p FileEntry::UID index. + SmallVector PreIncludedFileNames; + llvm::BitVector SeenIncludeFiles; + SmallVector IncludedFiles; + SmallVector IncludedFileLists; + std::optional PredefinesBufferRef; + std::optional ModuleIncludesBufferRef; + std::optional ModuleMapRef; + std::optional APINotesRef; + /// When the builder is created from an existing tree, the main include tree. + std::optional MainIncludeTreeRef; + SmallVector IncludeStack; + llvm::DenseMap> + ObjectForFile; + std::optional ErrorToReport; +}; + +/// A utility for adding \c PPCallbacks and/or \cASTReaderListener to a compiler +/// instance at the appropriate time. +struct AttachOnlyDependencyCollector : public DependencyCollector { + using MakePPCB = + llvm::unique_function(Preprocessor &)>; + using MakeASTReaderL = + llvm::unique_function(ASTReader &R)>; + MakePPCB CreatePPCB; + MakeASTReaderL CreateASTReaderL; + AttachOnlyDependencyCollector(MakePPCB CreatePPCB, MakeASTReaderL CreateL) + : CreatePPCB(std::move(CreatePPCB)), + CreateASTReaderL(std::move(CreateL)) {} + + void attachToPreprocessor(Preprocessor &PP) final { + if (CreatePPCB) { + std::unique_ptr CB = CreatePPCB(PP); + assert(CB); + PP.addPPCallbacks(std::move(CB)); + } + } + + void attachToASTReader(ASTReader &R) final { + if (CreateASTReaderL) { + std::unique_ptr L = CreateASTReaderL(R); + assert(L); + R.addListener(std::move(L)); + } + } +}; + +struct IncludeTreePPCallbacks : public PPCallbacks { + IncludeTreeBuilder &Builder; + Preprocessor &PP; + +public: + IncludeTreePPCallbacks(IncludeTreeBuilder &Builder, Preprocessor &PP) + : Builder(Builder), PP(PP) {} + + void LexedFileChanged(FileID FID, LexedFileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, FileID PrevFID, + SourceLocation Loc) override { + switch (Reason) { + case LexedFileChangeReason::EnterFile: + Builder.enteredInclude(PP, FID); + break; + case LexedFileChangeReason::ExitFile: { + Builder.exitedInclude(PP, FID, PrevFID, Loc); + break; + } + } + } + + void HasInclude(SourceLocation Loc, StringRef FileName, bool IsAngled, + OptionalFileEntryRef File, + SrcMgr::CharacteristicKind FileType) override { + Builder.handleHasIncludeCheck(PP, File.has_value()); + } + + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, + OptionalFileEntryRef File, StringRef SearchPath, + StringRef RelativePath, const Module *SuggestedModule, + bool ModuleImported, + SrcMgr::CharacteristicKind FileType) override { + // File includes are handled by LexedFileChanged. + if (!ModuleImported) + return; + + // Calculate EndLoc for the directive + // FIXME: pass EndLoc through PPCallbacks; it is already calculated + SourceManager &SM = PP.getSourceManager(); + std::pair LocInfo = SM.getDecomposedExpansionLoc(HashLoc); + StringRef Buffer = SM.getBufferData(LocInfo.first); + Lexer L(SM.getLocForStartOfFile(LocInfo.first), PP.getLangOpts(), + Buffer.begin(), Buffer.begin() + LocInfo.second, Buffer.end()); + L.setParsingPreprocessorDirective(true); + Token Tok; + do { + L.LexFromRawLexer(Tok); + } while (!Tok.isOneOf(tok::eod, tok::eof)); + SourceLocation EndLoc = L.getSourceLocation(); + + Builder.moduleImport(PP, SuggestedModule, EndLoc); + } + + void EnteredSubmodule(Module *M, SourceLocation ImportLoc, + bool ForPragma) override { + Builder.enteredSubmodule(PP, M, ImportLoc, ForPragma); + } + void LeftSubmodule(Module *M, SourceLocation ImportLoc, + bool ForPragma) override { + Builder.exitedSubmodule(PP, M, ImportLoc, ForPragma); + } +}; + +/// Utility to trigger module lookup in header search for modules loaded via +/// PCH. This causes dependency scanning via PCH to parse modulemap files at +/// roughly the same point they would with modulemap files embedded in the pcms, +/// which is disabled with include-tree modules. Without this, we can fail to +/// find modules that are in the same directory as a named import, since +/// it may be skipped during search (see \c loadFrameworkModule). +/// +/// The specific lookup we do matches what happens in ASTReader for the +/// MODULE_DIRECTORY record, and ignores the result. +class LookupPCHModulesListener : public ASTReaderListener { +public: + LookupPCHModulesListener(ASTReader &R) : Reader(R) {} + +private: + void visitModuleFile(StringRef Filename, + serialization::ModuleKind Kind) final { + // Any prebuilt or explicit modules seen during scanning are "full" modules + // rather than implicitly built scanner modules. + if (Kind == serialization::MK_PrebuiltModule || + Kind == serialization::MK_ExplicitModule) { + serialization::ModuleManager &Manager = Reader.getModuleManager(); + serialization::ModuleFile *MF = Manager.lookupByFileName(Filename); + assert(MF && "module file missing in visitModuleFile"); + // Match MODULE_DIRECTORY: allow full search and ignore failure to find + // the module. + HeaderSearch &HS = Reader.getPreprocessor().getHeaderSearchInfo(); + (void)HS.lookupModule(MF->ModuleName, SourceLocation(), + /*AllowSearch=*/true, + /*AllowExtraModuleMapSearch=*/true); + } + } + +private: + ASTReader &Reader; +}; +} // namespace + +/// The PCH recorded file paths with canonical paths, create a VFS that +/// allows remapping back to the non-canonical source paths so that they are +/// found during dep-scanning. +void dependencies::addReversePrefixMappingFileSystem( + const llvm::PrefixMapper &PrefixMapper, CompilerInstance &ScanInstance) { + llvm::PrefixMapper ReverseMapper; + ReverseMapper.addInverseRange(PrefixMapper.getMappings()); + ReverseMapper.sort(); + std::unique_ptr FS = + llvm::vfs::createPrefixMappingFileSystem( + std::move(ReverseMapper), &ScanInstance.getVirtualFileSystem()); + + ScanInstance.getFileManager().setVirtualFileSystem(std::move(FS)); +} + +Expected IncludeTreeActionController::getIncludeTree() { + if (IncludeTreeResult) + return *IncludeTreeResult; + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "failed to produce include-tree"); +} + +Error IncludeTreeActionController::initialize( + CompilerInstance &ScanInstance, CompilerInvocation &NewInvocation) { + DepscanPrefixMapping::configurePrefixMapper(NewInvocation, PrefixMapper); + + auto ensurePathRemapping = [&]() { + if (PrefixMapper.empty()) + return; + + PreprocessorOptions &PPOpts = ScanInstance.getPreprocessorOpts(); + if (PPOpts.Includes.empty() && PPOpts.ImplicitPCHInclude.empty() && + !ScanInstance.getLangOpts().Modules) + return; + + addReversePrefixMappingFileSystem(PrefixMapper, ScanInstance); + + // These are written in the predefines buffer, so we need to remap them. + for (std::string &Include : PPOpts.Includes) + PrefixMapper.mapInPlace(Include); + }; + ensurePathRemapping(); + + BuilderStack.push_back( + std::make_unique(DB, PrefixMapper)); + + // Attach callbacks for the IncludeTree of the TU. The preprocessor + // does not exist yet, so we need to indirect this via DependencyCollector. + auto DC = std::make_shared( + [&Builder = current()](Preprocessor &PP) { + return std::make_unique(Builder, PP); + }, + [](ASTReader &R) { + return std::make_unique(R); + }); + ScanInstance.addDependencyCollector(std::move(DC)); + + // Enable caching in the resulting commands. + ScanInstance.getFrontendOpts().CacheCompileJob = true; + ScanInstance.getFrontendOpts().ForIncludeTreeScan = true; + CASOpts = ScanInstance.getCASOpts(); + + return Error::success(); +} + +Error IncludeTreeActionController::finalize(CompilerInstance &ScanInstance, + CompilerInvocation &NewInvocation) { + auto GetInputCacheKey = [&]() -> std::optional { + if (NewInvocation.getFrontendOpts().Inputs.size() != 1) + return {}; + const auto &FIF = NewInvocation.getFrontendOpts().Inputs.front(); + if (!FIF.isFile()) + return {}; + auto It = OutputToCacheKey.find(FIF.getFile()); + if (It == OutputToCacheKey.end()) + return {}; + + return It->second; + }; + + std::string InputID; + CachingInputKind InputKind; + if (auto InputCacheKey = GetInputCacheKey()) { + InputID = InputCacheKey->str(); + InputKind = CachingInputKind::CachedCompilation; + } else { + assert(!IncludeTreeResult); + assert(BuilderStack.size() == 1); + auto Builder = BuilderStack.pop_back_val(); + Error E = Builder->finishIncludeTree(ScanInstance, NewInvocation) + .moveInto(IncludeTreeResult); + if (E) + return E; + InputID = IncludeTreeResult->getID().toString(); + InputKind = CachingInputKind::IncludeTree; + } + + configureInvocationForCaching(NewInvocation, CASOpts, InputID, InputKind, + // FIXME: working dir? + /*CASFSWorkingDir=*/""); + + DepscanPrefixMapping::remapInvocationPaths(NewInvocation, PrefixMapper); + + auto &CAS = ScanInstance.getOrCreateObjectStore(); + // FIXME: Make this return an error and propagate it up. + auto Key = createCompileJobCacheKey(CAS, ScanInstance.getDiagnostics(), + NewInvocation); + if (Key) + OutputToCacheKey[NewInvocation.getFrontendOpts().OutputFile] = + Key->toString(); + return Error::success(); +} + +std::optional IncludeTreeActionController::getCacheKey( + const CompilerInvocation &NewInvocation) { + auto It = OutputToCacheKey.find(NewInvocation.getFrontendOpts().OutputFile); + // FIXME: Assert this does not happen. + if (It == OutputToCacheKey.end()) + return std::nullopt; + return It->second; +} + +Error IncludeTreeActionController::initializeModuleBuild( + CompilerInstance &ModuleScanInstance) { + BuilderStack.push_back( + std::make_unique(DB, PrefixMapper)); + + // Attach callbacks for the IncludeTree of the module. The preprocessor + // does not exist yet, so we need to indirect this via DependencyCollector. + auto DC = std::make_shared( + [&Builder = current()](Preprocessor &PP) { + return std::make_unique(Builder, PP); + }, + [](ASTReader &R) { + return std::make_unique(R); + }); + ModuleScanInstance.addDependencyCollector(std::move(DC)); + ModuleScanInstance.setPrefixMapper(PrefixMapper); + + return Error::success(); +} + +Error IncludeTreeActionController::finalizeModuleBuild( + CompilerInstance &ModuleScanInstance) { + // FIXME: the scan invocation is incorrect here; we need the `NewInvocation` + // from `finalizeModuleInvocation` to finish the tree. + resetBenignCodeGenOptions( + frontend::GenerateModule, + ModuleScanInstance.getInvocation().getLangOpts(), + ModuleScanInstance.getInvocation().getCodeGenOpts()); + auto Builder = BuilderStack.pop_back_val(); + auto Tree = Builder->finishIncludeTree(ModuleScanInstance, + ModuleScanInstance.getInvocation()); + if (!Tree) + return Tree.takeError(); + + ModuleScanInstance.getPreprocessor().setCASIncludeTreeID( + Tree->getID().toString()); + + return Error::success(); +} + +Error IncludeTreeActionController::finalizeModuleInvocation( + CowCompilerInvocation &CowCI, const ModuleDeps &MD) { + if (!MD.IncludeTreeID) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "missing include-tree for module '%s'", + MD.ID.ModuleName.c_str()); + + // TODO: Avoid this copy. + CompilerInvocation CI(CowCI); + + configureInvocationForCaching(CI, CASOpts, *MD.IncludeTreeID, + CachingInputKind::IncludeTree, + /*CASFSWorkingDir=*/""); + + DepscanPrefixMapping::remapInvocationPaths(CI, PrefixMapper); + + CowCI = CI; + return Error::success(); +} + +void IncludeTreeBuilder::enteredInclude(Preprocessor &PP, FileID FID) { + if (hasErrorOccurred()) + return; + + if (!StartedEnteringIncludes) { + StartedEnteringIncludes = true; + + SmallVector UIDToFE; + PP.getFileManager().GetUniqueIDMapping(UIDToFE); + + // Get the included files (coming from a PCH), and keep track of the + // filenames that were recorded in the PCH. + for (const FileEntry *FE : PP.getIncludedFiles()) { + unsigned UID = FE->getUID(); + if (UID >= PreIncludedFileNames.size()) + PreIncludedFileNames.resize(UID + 1); + OptionalFileEntryRef FERef = UIDToFE[FE->getUID()]; + assert(FERef && "No FileEntryRef with given UID"); + PreIncludedFileNames[UID] = FERef->getName(); + } + } + + std::optional FileRef = check(getObjectForFile(PP, FID)); + if (!FileRef) + return; + const SrcMgr::FileInfo &FI = + PP.getSourceManager().getSLocEntry(FID).getFile(); + IncludeStack.push_back({FI.getFileCharacteristic(), *FileRef, {}, {}, {}}); +} + +void IncludeTreeBuilder::exitedInclude(Preprocessor &PP, FileID IncludedBy, + FileID Include, SourceLocation ExitLoc) { + if (hasErrorOccurred()) + return; + + assert(*check(getObjectForFile(PP, Include)) == IncludeStack.back().File); + std::optional IncludeTree = + check(getCASTreeForFileIncludes(IncludeStack.pop_back_val())); + if (!IncludeTree) + return; + assert(*check(getObjectForFile(PP, IncludedBy)) == IncludeStack.back().File); + SourceManager &SM = PP.getSourceManager(); + std::pair LocInfo = SM.getDecomposedExpansionLoc(ExitLoc); + + // If the exited header belongs to a sub-module that's marked as missing from + // the umbrella, we must've first loaded its PCM file to find that out. + // We need to match this behavior with include-tree. Let's mark this as + // spurious import. For this node, Clang will load the top-level module, emit + // the appropriate diagnostics and then fall back to textual inclusion of the + // header itself. + if (auto FE = PP.getSourceManager().getFileEntryRefForID(Include)) { + ModuleMap &ModMap = PP.getHeaderSearchInfo().getModuleMap(); + Module *M = ModMap.findModuleForHeader(*FE).getModule(); + if (M && M->IsInferredMissingFromUmbrellaHeader) { + assert(!IncludeTree->isSubmodule() && + "Include of header missing from umbrella header is modular"); + + moduleImport(PP, M, ExitLoc); + auto Import = IncludeStack.back().Includes.pop_back_val(); + + auto SpuriousImport = check(cas::IncludeTree::SpuriousImport::create( + DB, Import.Ref, IncludeTree->getRef())); + if (!SpuriousImport) + return; + IncludeStack.back().Includes.push_back( + {SpuriousImport->getRef(), LocInfo.second, + cas::IncludeTree::NodeKind::SpuriousImport}); + return; + } + } + + IncludeStack.back().Includes.push_back({IncludeTree->getRef(), LocInfo.second, + cas::IncludeTree::NodeKind::Tree}); +} + +void IncludeTreeBuilder::handleHasIncludeCheck(Preprocessor &PP, bool Result) { + if (hasErrorOccurred()) + return; + + IncludeStack.back().HasIncludeChecks.push_back(Result); +} + +void IncludeTreeBuilder::moduleImport(Preprocessor &PP, const Module *M, + SourceLocation EndLoc) { + bool VisibilityOnly = M->isForBuilding(PP.getLangOpts()); + auto Import = check(cas::IncludeTree::ModuleImport::create( + DB, M->getFullModuleName(), VisibilityOnly)); + if (!Import) + return; + + std::pair EndLocInfo = + PP.getSourceManager().getDecomposedExpansionLoc(EndLoc); + IncludeStack.back().Includes.push_back( + {Import->getRef(), EndLocInfo.second, + cas::IncludeTree::NodeKind::ModuleImport}); +} + +void IncludeTreeBuilder::enteredSubmodule(Preprocessor &PP, Module *M, + SourceLocation ImportLoc, + bool ForPragma) { + if (ForPragma) + return; // Will be parsed as normal. + if (hasErrorOccurred()) + return; + assert(!IncludeStack.back().SubmoduleName && "repeated enteredSubmodule"); + auto Ref = check(DB.storeFromString({}, M->getFullModuleName())); + IncludeStack.back().SubmoduleName = Ref; +} +void IncludeTreeBuilder::exitedSubmodule(Preprocessor &PP, Module *M, + SourceLocation ImportLoc, + bool ForPragma) { + // Submodule exit is handled automatically when leaving a modular file. +} + +static Expected +getIncludeTreeModule(cas::ObjectStore &DB, Module *M) { + using ITModule = cas::IncludeTree::Module; + SmallVector Submodules; + for (Module *Sub : M->submodules()) { + Expected SubTree = getIncludeTreeModule(DB, Sub); + if (!SubTree) + return SubTree.takeError(); + Submodules.push_back(SubTree->getRef()); + } + + ITModule::ModuleFlags Flags; + Flags.IsFramework = M->IsFramework; + Flags.IsExplicit = M->IsExplicit; + Flags.IsExternC = M->IsExternC; + Flags.IsSystem = M->IsSystem; + Flags.InferSubmodules = M->InferSubmodules; + Flags.InferExplicitSubmodules = M->InferExplicitSubmodules; + Flags.InferExportWildcard = M->InferExportWildcard; + Flags.UseExportAsModuleLinkName = M->UseExportAsModuleLinkName; + + bool GlobalWildcardExport = false; + SmallVector Exports; + llvm::BumpPtrAllocator Alloc; + llvm::StringSaver Saver(Alloc); + for (Module::ExportDecl &Export : M->Exports) { + if (Export.getPointer() == nullptr && Export.getInt()) { + GlobalWildcardExport = true; + } else if (Export.getPointer()) { + StringRef Name = Saver.save(Export.getPointer()->getFullModuleName()); + Exports.push_back({Name, Export.getInt()}); + } + } + std::optional ExportList; + if (GlobalWildcardExport || !Exports.empty()) { + auto EL = ITModule::ExportList::create(DB, Exports, GlobalWildcardExport); + if (!EL) + return EL.takeError(); + ExportList = EL->getRef(); + } + + SmallVector Libraries; + for (Module::LinkLibrary &LL : M->LinkLibraries) { + Libraries.push_back({LL.Library, LL.IsFramework}); + } + std::optional LinkLibraries; + if (!Libraries.empty()) { + auto LL = ITModule::LinkLibraryList::create(DB, Libraries); + if (!LL) + return LL.takeError(); + LinkLibraries = LL->getRef(); + } + + return ITModule::create(DB, M->Name, M->ExportAsModule, Flags, Submodules, + ExportList, LinkLibraries); +} + +Expected +IncludeTreeBuilder::finishIncludeTree(CompilerInstance &ScanInstance, + CompilerInvocation &NewInvocation) { + if (ErrorToReport) + return std::move(*ErrorToReport); + + FileManager &FM = ScanInstance.getFileManager(); + + auto addFile = [&](StringRef FilePath, + bool IgnoreFileError = false) -> Error { + if (FilePath.empty()) + return Error::success(); + llvm::Expected FE = FM.getFileRef(FilePath); + if (!FE) { + auto Err = FE.takeError(); + if (IgnoreFileError) { + llvm::consumeError(std::move(Err)); + return Error::success(); + } + return Err; + } + std::optional Ref; + return addToFileList(FM, *FE).moveInto(Ref); + }; + + for (StringRef FilePath : NewInvocation.getLangOpts().NoSanitizeFiles) { + if (Error E = addFile(FilePath)) + return std::move(E); + } + // Add profile files. + // FIXME: Do not have the logic here to determine which path should be set + // but ideally only the path needed for the compilation is set and we already + // checked the file needed exists. Just try load and ignore errors. + if (Error E = addFile(NewInvocation.getCodeGenOpts().ProfileInstrumentUsePath, + /*IgnoreFileError=*/true)) + return std::move(E); + if (Error E = addFile(NewInvocation.getCodeGenOpts().SampleProfileFile, + /*IgnoreFileError=*/true)) + return std::move(E); + if (Error E = addFile(NewInvocation.getCodeGenOpts().ProfileRemappingFile, + /*IgnoreFileError=*/true)) + return std::move(E); + + StringRef Sysroot = NewInvocation.getHeaderSearchOpts().Sysroot; + if (!Sysroot.empty()) { + // Include 'SDKSettings.json', if it exists, to accomodate availability + // checks during the compilation. + llvm::SmallString<256> FilePath = Sysroot; + llvm::sys::path::append(FilePath, "SDKSettings.json"); + if (Error E = addFile(FilePath, /*IgnoreFileError*/ true)) + return std::move(E); + } + + auto FinishIncludeTree = [&]() -> Error { + IntrusiveRefCntPtr Reader = ScanInstance.getASTReader(); + if (!Reader) + return Error::success(); // no need for additional work. + + // Go through all the recorded input files. + if (Error E = addModuleInputs(*Reader)) + return E; + + PreprocessorOptions &PPOpts = NewInvocation.getPreprocessorOpts(); + if (PPOpts.ImplicitPCHInclude.empty()) + return Error::success(); // no need for additional work. + + llvm::ErrorOr> CASContents = + FM.getObjectRefForFileContent(PPOpts.ImplicitPCHInclude); + if (!CASContents) + return llvm::errorCodeToError(CASContents.getError()); + + StringRef PCHFilename = ""; + if (NewInvocation.getFrontendOpts().IncludeTreePreservePCHPath) + PCHFilename = PPOpts.ImplicitPCHInclude; + + auto PCHFile = + cas::IncludeTree::File::create(DB, PCHFilename, **CASContents); + if (!PCHFile) + return PCHFile.takeError(); + PCHRef = PCHFile->getRef(); + return llvm::Error::success(); + }; + + if (Error E = FinishIncludeTree()) + return std::move(E); + + if (ErrorToReport) + return std::move(*ErrorToReport); + + assert(IncludeStack.size() == 1); + Expected MainIncludeTree = + getCASTreeForFileIncludes(IncludeStack.pop_back_val()); + if (!MainIncludeTree) + return MainIncludeTree.takeError(); + + if (!ScanInstance.getLangOpts().CurrentModule.empty()) { + SmallVector Modules; + SmallVector APINotes; + auto AddModule = [&](Module *M) -> llvm::Error { + Expected Mod = getIncludeTreeModule(DB, M); + if (!Mod) + return Mod.takeError(); + Modules.push_back(Mod->getRef()); + return Error::success(); + }; + if (Module *M = ScanInstance.getPreprocessor().getCurrentModule()) { + if (Error E = AddModule(M)) + return std::move(E); + + // If it is currently module, load its APINotes. + api_notes::APINotesManager ANM(ScanInstance.getSourceManager(), + ScanInstance.getLangOpts()); + auto Notes = ANM.getCurrentModuleAPINotes( + M, ScanInstance.getLangOpts().APINotesModules, + ScanInstance.getAPINotesOpts().ModuleSearchPaths); + for (auto File : Notes) { + if (auto Buf = + ScanInstance.getSourceManager().getMemoryBufferForFileOrNone( + File)) { + auto Note = DB.storeFromString({}, Buf->getBuffer()); + if (!Note) + return Note.takeError(); + APINotes.push_back(*Note); + } + } + } else { + // When building a TU or PCH, we can have headers files that are part of + // both the public and private modules that are included textually. In + // that case we need both of those modules. + ModuleMap &MMap = + ScanInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap(); + if (Module *M = MMap.findModule(ScanInstance.getLangOpts().CurrentModule)) + if (Error E = AddModule(M)) + return std::move(E); + if (Module *PM = + MMap.findModule(ScanInstance.getLangOpts().ModuleName + "_Private")) + if (Error E = AddModule(PM)) + return std::move(E); + } + + auto ModMap = cas::IncludeTree::ModuleMap::create(DB, Modules); + if (!ModMap) + return ModMap.takeError(); + ModuleMapRef = ModMap->getRef(); + + if (!APINotes.empty()) { + auto ModAPINotes = cas::IncludeTree::APINotes::create(DB, APINotes); + if (!ModAPINotes) + return ModAPINotes.takeError(); + APINotesRef = ModAPINotes->getRef(); + } + } + + auto FileList = + cas::IncludeTree::FileList::create(DB, IncludedFiles, IncludedFileLists); + if (!FileList) + return FileList.takeError(); + + return cas::IncludeTreeRoot::create(DB, MainIncludeTree->getRef(), + FileList->getRef(), PCHRef, ModuleMapRef, + APINotesRef); +} + +Error IncludeTreeBuilder::addModuleInputs(ASTReader &Reader) { + for (serialization::ModuleFile &MF : Reader.getModuleManager()) { + // Only add direct imports to avoid duplication. Each include tree is a + // superset of its imported modules' include trees. + if (!MF.isDirectlyImported()) + continue; + + assert(!MF.IncludeTreeID.empty() && "missing include-tree for import"); + + std::optional ID; + if (Error E = DB.parseID(MF.IncludeTreeID).moveInto(ID)) + return E; + std::optional Ref = DB.getReference(*ID); + if (!Ref) + return DB.createUnknownObjectError(*ID); + std::optional Root; + if (Error E = cas::IncludeTreeRoot::get(DB, *Ref).moveInto(Root)) + return E; + + IncludedFileLists.push_back(Root->getFileListRef()); + } + + return Error::success(); +} + +Expected IncludeTreeBuilder::getObjectForFile(Preprocessor &PP, + FileID FID) { + SourceManager &SM = PP.getSourceManager(); + const SrcMgr::FileInfo &FI = SM.getSLocEntry(FID).getFile(); + if (PP.getPredefinesFileID() == FID) { + if (!PredefinesBufferRef) { + auto Ref = getObjectForBuffer(FI); + if (!Ref) + return Ref.takeError(); + PredefinesBufferRef = *Ref; + } + return *PredefinesBufferRef; + } + if (!FI.getContentCache().OrigEntry && + FI.getName() == Module::getModuleInputBufferName()) { + // Virtual buffer + if (!ModuleIncludesBufferRef) { + if (Error E = getObjectForBuffer(FI).moveInto(ModuleIncludesBufferRef)) + return std::move(E); + } + return *ModuleIncludesBufferRef; + } + assert(FI.getContentCache().OrigEntry); + auto &FileRef = ObjectForFile[*FI.getContentCache().OrigEntry]; + if (!FileRef) { + auto Ref = getObjectForFileNonCached(SM.getFileManager(), FI); + if (!Ref) + return Ref.takeError(); + FileRef = *Ref; + } + return *FileRef; +} + +Expected +IncludeTreeBuilder::getObjectForFileNonCached(FileManager &FM, + const SrcMgr::FileInfo &FI) { + OptionalFileEntryRef FE = FI.getContentCache().OrigEntry; + assert(FE); + + // Mark the include as already seen. + if (FE->getUID() >= SeenIncludeFiles.size()) + SeenIncludeFiles.resize(FE->getUID() + 1); + SeenIncludeFiles.set(FE->getUID()); + + return addToFileList(FM, *FE); +} + +Expected +IncludeTreeBuilder::getObjectForBuffer(const SrcMgr::FileInfo &FI) { + // This is a non-file buffer, like the predefines. + auto Ref = DB.storeFromString( + {}, FI.getContentCache().getBufferIfLoaded()->getBuffer()); + if (!Ref) + return Ref.takeError(); + Expected FileNode = + createIncludeFile(FI.getName(), *Ref); + if (!FileNode) + return FileNode.takeError(); + return FileNode->getRef(); +} + +Expected IncludeTreeBuilder::addToFileList(FileManager &FM, + FileEntryRef FE) { + SmallString<128> PathStorage; + StringRef Filename = FE.getName(); + // Apply -working-directory to relative paths. This option causes filesystem + // lookups to use absolute paths, so make paths in the include-tree filesystem + // absolute to match. + if (!llvm::sys::path::is_absolute(Filename) && + !FM.getFileSystemOpts().WorkingDir.empty()) { + PathStorage = Filename; + FM.FixupRelativePath(PathStorage); + Filename = PathStorage; + } + + llvm::ErrorOr> CASContents = + FM.getObjectRefForFileContent(Filename); + if (!CASContents) + return llvm::errorCodeToError(CASContents.getError()); + assert(*CASContents); + + auto addFile = [&](StringRef Filename) -> Expected { + assert(!Filename.empty()); + auto FileNode = createIncludeFile(Filename, **CASContents); + if (!FileNode) + return FileNode.takeError(); + IncludedFiles.push_back( + {FileNode->getRef(), + static_cast(FE.getSize())}); + return FileNode->getRef(); + }; + + // Check whether another path coming from the PCH is associated with the same + // file. + unsigned UID = FE.getUID(); + if (UID < PreIncludedFileNames.size() && !PreIncludedFileNames[UID].empty() && + PreIncludedFileNames[UID] != Filename) { + auto FileNode = addFile(PreIncludedFileNames[UID]); + if (!FileNode) + return FileNode.takeError(); + } + + return addFile(Filename); +} + +Expected +IncludeTreeBuilder::getCASTreeForFileIncludes(FilePPState &&PPState) { + return cas::IncludeTree::create(DB, PPState.FileCharacteristic, PPState.File, + PPState.Includes, PPState.SubmoduleName, + PPState.HasIncludeChecks); +} + +Expected +IncludeTreeBuilder::createIncludeFile(StringRef Filename, + cas::ObjectRef Contents) { + SmallString<256> MappedPath; + if (!PrefixMapper.empty()) { + PrefixMapper.map(Filename, MappedPath); + Filename = MappedPath; + } + return cas::IncludeTree::File::create(DB, Filename, std::move(Contents)); +} + +std::unique_ptr +dependencies::createIncludeTreeActionController( + LookupModuleOutputCallback LookupModuleOutput, cas::ObjectStore &DB) { + return std::make_unique(DB, LookupModuleOutput); +} diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp index 07856dbdba4b4..a912fa5f9f839 100644 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp @@ -8,12 +8,17 @@ #include "clang/Tooling/DependencyScanning/ModuleDepCollector.h" +#include "clang/Basic/DiagnosticCAS.h" #include "clang/Basic/MakeSupport.h" +#include "clang/Frontend/CompileJobCacheKey.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/CAS/CASID.h" +#include "llvm/CAS/ObjectStore.h" #include "llvm/Support/BLAKE3.h" +#include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/StringSaver.h" #include @@ -250,11 +255,12 @@ bool dependencies::isPathInStableDir(const ArrayRef Directories, bool dependencies::areOptionsInStableDir(const ArrayRef Directories, const HeaderSearchOptions &HSOpts) { - assert(isPathInStableDir(Directories, HSOpts.Sysroot) && - "Sysroots differ between module dependencies and current TU"); + // FIXME: This breaks CAS prefix mapping tests. + // assert(isPathInStableDir(Directories, HSOpts.Sysroot) && + // "Sysroots differ between module dependencies and current TU"); - assert(isPathInStableDir(Directories, HSOpts.ResourceDir) && - "ResourceDirs differ between module dependencies and current TU"); + // assert(isPathInStableDir(Directories, HSOpts.ResourceDir) && + // "ResourceDirs differ between module dependencies and current TU"); for (const auto &Entry : HSOpts.UserEntries) { if (!Entry.IgnoreSysRoot) @@ -285,7 +291,10 @@ makeCommonInvocationForModuleBuild(CompilerInvocation CI) { // differ between identical modules discovered from different translation // units. CI.getFrontendOpts().Inputs.clear(); - CI.getFrontendOpts().OutputFile.clear(); + // CAS: OutputFile cannot be empty when computing a cache key. + CI.getFrontendOpts().OutputFile = "-"; + // FIXME: a build system may want to provide a new path. + CI.getFrontendOpts().IndexUnitOutputPath.clear(); // LLVM options are not going to affect the AST CI.getFrontendOpts().LLVMArgs.clear(); @@ -298,7 +307,9 @@ makeCommonInvocationForModuleBuild(CompilerInvocation CI) { CI.getDiagnosticOpts().DiagnosticSerializationFile = "-"; if (!CI.getDependencyOutputOpts().OutputFile.empty()) CI.getDependencyOutputOpts().OutputFile = "-"; - CI.getDependencyOutputOpts().Targets.clear(); + // CAS: -MT must be preserved for cache key. + if (!CI.getDependencyOutputOpts().Targets.empty()) + CI.getDependencyOutputOpts().Targets = {"-"}; CI.getFrontendOpts().ProgramAction = frontend::GenerateModule; CI.getLangOpts().ModuleName.clear(); @@ -368,8 +379,12 @@ ModuleDepCollector::getInvocationAdjustedForModuleBuildWithoutOutputs( } // Report the prebuilt modules this module uses. - for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) + for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) { CI.getMutFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile); + if (PrebuiltModule.ModuleCacheKey) + CI.getMutFrontendOpts().ModuleCacheKeys.emplace_back( + PrebuiltModule.PCMFile, *PrebuiltModule.ModuleCacheKey); + } // Add module file inputs from dependencies. addModuleFiles(CI, Deps.ClangModuleDeps); @@ -422,6 +437,10 @@ void ModuleDepCollector::addModuleFiles( std::string PCMPath = Controller.lookupModuleOutput(*MD, ModuleOutputKind::ModuleFile); + assert(MD && "Inconsistent dependency info"); + if (MD->ModuleCacheKey) + CI.getFrontendOpts().ModuleCacheKeys.emplace_back(PCMPath, + *MD->ModuleCacheKey); if (Service.shouldEagerLoadModules()) CI.getFrontendOpts().ModuleFiles.push_back(std::move(PCMPath)); else @@ -437,6 +456,10 @@ void ModuleDepCollector::addModuleFiles( std::string PCMPath = Controller.lookupModuleOutput(*MD, ModuleOutputKind::ModuleFile); + assert(MD && "Inconsistent dependency info"); + if (MD->ModuleCacheKey) + CI.getMutFrontendOpts().ModuleCacheKeys.emplace_back(PCMPath, + *MD->ModuleCacheKey); if (Service.shouldEagerLoadModules()) CI.getMutFrontendOpts().ModuleFiles.push_back(std::move(PCMPath)); else @@ -576,6 +599,22 @@ static std::string getModuleContextHash(const ModuleDeps &MD, HashBuilder; SmallString<32> Scratch; + auto FormatHash = [&](llvm::BLAKE3Result<16> Hash) { + std::array Words; + static_assert(sizeof(Hash) == sizeof(Words), "Hash must match Words"); + std::memcpy(Words.data(), Hash.data(), sizeof(Hash)); + return toString(llvm::APInt(sizeof(Words) * 8, Words), 36, + /*Signed=*/false); + }; + + if (MD.ModuleCacheKey) { + // Cache keys have better canonicalization, so use them as the context hash. + // This reduces the number of modules needed when compatible configurations + // are used (e.g. change in -fmessage-length). + HashBuilder.add(*MD.ModuleCacheKey); + return FormatHash(HashBuilder.final()); + } + // Hash the compiler version and serialization version to ensure the module // will be readable. HashBuilder.add(getClangFullRepositoryVersion()); @@ -584,6 +623,15 @@ static std::string getModuleContextHash(const ModuleDeps &MD, if (CWD && !IgnoreCWD) HashBuilder.add(*CWD); + // Save and restore options that should not affect the hash, e.g. the exact + // contents of input files, or prefix mappings. + auto &FSOpts = const_cast(CI.getFileSystemOpts()); + auto &FEOpts = const_cast(CI.getFrontendOpts()); + auto &CASOpts = const_cast(CI.getCASOpts()); + llvm::SaveAndRestore RestoreCASFSRootID(FSOpts.CASFileSystemRootID, {}); + llvm::SaveAndRestore RestorePrefixMappings(FEOpts.PathPrefixMappings, {}); + llvm::SaveAndRestore RestoreCASOptions(CASOpts, {}); + // Hash the BuildInvocation without any input files. SmallString<0> ArgVec; ArgVec.reserve(4096); @@ -604,13 +652,30 @@ static std::string getModuleContextHash(const ModuleDeps &MD, } HashBuilder.add(EagerLoadModules); + return FormatHash(HashBuilder.final()); +} + +#ifndef NDEBUG +static void checkCompileCacheKeyMatch(cas::ObjectStore &CAS, + StringRef OldKeyStr, cas::CASID NewKey) { + if (NewKey.toString() == OldKeyStr) + return; - llvm::BLAKE3Result<16> Hash = HashBuilder.final(); - std::array Words; - static_assert(sizeof(Hash) == sizeof(Words), "Hash must match Words"); - std::memcpy(Words.data(), Hash.data(), sizeof(Hash)); - return toString(llvm::APInt(sizeof(Words) * 8, Words), 36, /*Signed=*/false); + // Mismatched keys, report error. + auto OldKey = CAS.parseID(OldKeyStr); + if (!OldKey) + llvm::report_fatal_error(OldKey.takeError()); + SmallString<256> Err; + llvm::raw_svector_ostream OS(Err); + OS << "Compile cache key for module changed; previously:"; + if (auto E = printCompileJobCacheKey(CAS, *OldKey, OS)) + OS << std::move(E); + OS << "\nkey is now:"; + if (auto E = printCompileJobCacheKey(CAS, NewKey, OS)) + OS << std::move(E); + llvm::report_fatal_error(OS.str()); } +#endif void ModuleDepCollector::associateWithContextHash( const CowCompilerInvocation &CI, bool IgnoreCWD, ModuleDeps &Deps) { @@ -633,7 +698,8 @@ void ModuleDepCollectorPP::LexedFileChanged(FileID FID, // This has to be delayed as the context hash can change at the start of // `CompilerInstance::ExecuteAction`. if (MDC.ContextHash.empty()) { - MDC.ContextHash = MDC.ScanInstance.getInvocation().getModuleHash(); + MDC.ContextHash = MDC.ScanInstance.getInvocation().getModuleHash( + MDC.ScanInstance.getDiagnostics()); MDC.Consumer.handleContextHash(MDC.ContextHash); } @@ -825,6 +891,12 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { MD.ModuleMapFileDeps.emplace_back(*ResolvedFilenameAsRequested); }); + if (!MF->IncludeTreeID.empty()) + MD.IncludeTreeID = MF->IncludeTreeID; + + if (!MF->CASFileSystemRootID.empty()) + MD.CASFileSystemRootID = MF->CASFileSystemRootID; + bool IgnoreCWD = false; CowCompilerInvocation CI = MDC.getInvocationAdjustedForModuleBuildWithoutOutputs( @@ -855,6 +927,20 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { } }); + auto &Diags = MDC.ScanInstance.getDiagnostics(); + + if (auto E = MDC.Controller.finalizeModuleInvocation(CI, MD)) + Diags.Report(diag::err_cas_depscan_failed) << std::move(E); + + // Compute the cache key, if needed. Requires dependencies. + if (MDC.ScanInstance.getFrontendOpts().CacheCompileJob) { + auto &CAS = MDC.ScanInstance.getOrCreateObjectStore(); + if (auto Key = createCompileJobCacheKey(CAS, Diags, CI)) + MD.ModuleCacheKey = Key->toString(); + } + + MD.IgnoreCWD = IgnoreCWD; + // Check provided input paths from the invocation for determining // IsInStableDirectories. if (MD.IsInStableDirectories) @@ -866,6 +952,16 @@ ModuleDepCollectorPP::handleTopLevelModule(const Module *M) { // Finish the compiler invocation. Requires dependencies and the context hash. MDC.addOutputPaths(CI, MD); +#ifndef NDEBUG + // Verify the key has not changed with final arguments. + if (MD.ModuleCacheKey) { + auto &CAS = MDC.ScanInstance.getOrCreateObjectStore(); + auto Key = createCompileJobCacheKey(CAS, Diags, CI); + assert(Key); + checkCompileCacheKeyMatch(CAS, *MD.ModuleCacheKey, *Key); + } +#endif + MD.BuildInfo = std::move(CI); MDC.ModularDeps.insert({M, std::move(OwnedMD)}); @@ -996,7 +1092,7 @@ bool ModuleDepCollector::isPrebuiltModule(const Module *M) { if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end()) return false; assert("Prebuilt module came from the expected AST file" && - PrebuiltModuleFileIt->second == M->getASTFile()->getName()); + PrebuiltModuleFileIt->second == M->getASTFile()->getNameAsRequested()); return true; } diff --git a/clang/lib/Tooling/DependencyScanning/ScanAndUpdateArgs.cpp b/clang/lib/Tooling/DependencyScanning/ScanAndUpdateArgs.cpp new file mode 100644 index 0000000000000..d2d67298e9fea --- /dev/null +++ b/clang/lib/Tooling/DependencyScanning/ScanAndUpdateArgs.cpp @@ -0,0 +1,314 @@ +//===- ScanAndUpdateArgs.cpp - Util for CC1 Dependency Scanning -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/DependencyScanning/ScanAndUpdateArgs.h" +#include "clang/CAS/IncludeTree.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Lex/HeaderSearchOptions.h" +#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" +#include "llvm/CAS/ObjectStore.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrefixMapper.h" + +using namespace clang; +using namespace clang::tooling::dependencies; +using llvm::Error; + +static void updateRelativePath(std::string &Path, + const std::string &WorkingDir) { + if (Path.empty() || llvm::sys::path::is_absolute(Path) || WorkingDir.empty()) + return; + + SmallString<128> PathStorage(WorkingDir); + llvm::sys::path::append(PathStorage, Path); + Path = PathStorage.str(); +} + +void tooling::dependencies::configureInvocationForCaching( + CompilerInvocation &CI, CASOptions CASOpts, std::string InputID, + CachingInputKind InputKind, std::string WorkingDir) { + CI.getCASOpts() = std::move(CASOpts); + auto &FrontendOpts = CI.getFrontendOpts(); + FrontendOpts.CacheCompileJob = true; + FrontendOpts.IncludeTimestamps = false; + + // Clear this otherwise it defeats the purpose of making the compilation key + // independent of certain arguments. + auto &CodeGenOpts = CI.getCodeGenOpts(); + if (CI.getFrontendOpts().ProgramAction != frontend::ActionKind::EmitObj) { + CodeGenOpts.UseCASBackend = false; + CodeGenOpts.EmitCASIDFile = false; + auto &LLVMArgs = FrontendOpts.LLVMArgs; + llvm::erase(LLVMArgs, "-cas-friendly-debug-info"); + } + CodeGenOpts.DwarfDebugFlags.clear(); + resetBenignCodeGenOptions(FrontendOpts.ProgramAction, CI.getLangOpts(), + CodeGenOpts); + + HeaderSearchOptions &HSOpts = CI.getHeaderSearchOpts(); + // Avoid writing potentially volatile diagnostic options into pcms. + HSOpts.ModulesSkipDiagnosticOptions = true; + + // "Fix" the CAS options. + auto &FileSystemOpts = CI.getFileSystemOpts(); + switch (InputKind) { + case CachingInputKind::IncludeTree: { + FrontendOpts.CASIncludeTreeID = std::move(InputID); + FrontendOpts.Inputs.clear(); + FrontendOpts.ModuleMapFiles.clear(); + HeaderSearchOptions OriginalHSOpts; + std::swap(HSOpts, OriginalHSOpts); + HSOpts.ModulesSkipDiagnosticOptions = + OriginalHSOpts.ModulesSkipDiagnosticOptions; + // Preserve sysroot path to accommodate lookup for 'SDKSettings.json' during + // availability checking. + HSOpts.Sysroot = std::move(OriginalHSOpts.Sysroot); + // Preserve resource-dir, which is added back by cc1_main if missing, and + // affects the cache key. + HSOpts.ResourceDir = std::move(OriginalHSOpts.ResourceDir); + // Preserve fmodule-file options. + HSOpts.PrebuiltModuleFiles = std::move(OriginalHSOpts.PrebuiltModuleFiles); + // Preserve -gmodules (see below for caveats). + HSOpts.ModuleFormat = OriginalHSOpts.ModuleFormat; + HSOpts.UseBuiltinIncludes = false; + HSOpts.UseStandardSystemIncludes = false; + HSOpts.UseStandardCXXIncludes = false; + + auto &PPOpts = CI.getPreprocessorOpts(); + // We don't need this because we save the contents of the PCH file in the + // include tree root. + PPOpts.ImplicitPCHInclude.clear(); + if (FrontendOpts.ProgramAction != frontend::GeneratePCH) { + // We don't need these because we save the contents of the predefines + // buffer in the include tree. But if we generate a PCH file we still need + // to keep them as preprocessor options so that they are preserved in a + // PCH file and compared with the preprocessor options of the dep-scan + // invocation that uses the PCH. + PPOpts.Macros.clear(); + PPOpts.MacroIncludes.clear(); + PPOpts.Includes.clear(); + } + if (!FrontendOpts.IncludeTreePreservePCHPath) { + // Disable `-gmodules` to avoid debug info referencing a non-existent PCH + // filename. + // FIXME: we should also allow -gmodules if there is no PCH involved. + CodeGenOpts.DebugTypeExtRefs = false; + HSOpts.ModuleFormat = "raw"; + } + // Clear APINotes options. + CI.getAPINotesOpts().ModuleSearchPaths = {}; + + // Update output paths, and clear working directory. + auto CWD = FileSystemOpts.WorkingDir; + updateRelativePath(FrontendOpts.OutputFile, CWD); + updateRelativePath(CI.getDiagnosticOpts().DiagnosticSerializationFile, CWD); + updateRelativePath(CI.getDiagnosticOpts().DiagnosticLogFile, CWD); + updateRelativePath(CI.getDependencyOutputOpts().OutputFile, CWD); + FileSystemOpts.WorkingDir.clear(); + break; + } + case CachingInputKind::FileSystemRoot: { + FileSystemOpts.CASFileSystemRootID = std::move(InputID); + FileSystemOpts.CASFileSystemWorkingDirectory = std::move(WorkingDir); + break; + } + case CachingInputKind::CachedCompilation: { + FrontendOpts.Inputs.clear(); + FrontendOpts.CASInputFileCacheKey = std::move(InputID); + FrontendOpts.ModuleMapFiles.clear(); + // TODO: Strip more of the things we already strip for include-tree. + break; + case CachingInputKind::Object: { + assert(false && "Object should not be available during scanning"); + } + } + } +} + +void DepscanPrefixMapping::remapInvocationPaths(CompilerInvocation &Invocation, + llvm::PrefixMapper &Mapper) { + auto &FrontendOpts = Invocation.getFrontendOpts(); + FrontendOpts.PathPrefixMappings.clear(); + + // If there are no mappings, we're done. Otherwise, continue and remap + // everything. + if (Mapper.empty()) + return; + + // Pass the remappings so that we can map cached diagnostics to the local + // paths during diagnostic rendering. + for (const llvm::MappedPrefix &Map : Mapper.getMappings()) { + FrontendOpts.PathPrefixMappings.push_back(Map.Old + "=" + Map.New); + } + + auto mapInPlaceAll = [&](std::vector &Vector) { + for (auto &Path : Vector) + Mapper.mapInPlace(Path); + }; + + auto &FileSystemOpts = Invocation.getFileSystemOpts(); + Mapper.mapInPlace(FileSystemOpts.CASFileSystemWorkingDirectory); + + // Remap header search. + auto &HeaderSearchOpts = Invocation.getHeaderSearchOpts(); + Mapper.mapInPlace(HeaderSearchOpts.Sysroot); + for (auto &Entry : HeaderSearchOpts.UserEntries) + if (Entry.IgnoreSysRoot) + Mapper.mapInPlace(Entry.Path); + + for (auto &Prefix : HeaderSearchOpts.SystemHeaderPrefixes) + Mapper.mapInPlace(Prefix.Prefix); + Mapper.mapInPlace(HeaderSearchOpts.ResourceDir); + Mapper.mapInPlace(HeaderSearchOpts.ModuleCachePath); + Mapper.mapInPlace(HeaderSearchOpts.ModuleUserBuildPath); + for (auto I = HeaderSearchOpts.PrebuiltModuleFiles.begin(), + E = HeaderSearchOpts.PrebuiltModuleFiles.end(); + I != E;) { + auto Current = I++; + Mapper.mapInPlace(Current->second); + } + mapInPlaceAll(HeaderSearchOpts.PrebuiltModulePaths); + mapInPlaceAll(HeaderSearchOpts.VFSOverlayFiles); + + // Preprocessor options. + auto &PPOpts = Invocation.getPreprocessorOpts(); + mapInPlaceAll(PPOpts.MacroIncludes); + mapInPlaceAll(PPOpts.Includes); + Mapper.mapInPlace(PPOpts.ImplicitPCHInclude); + + // Frontend options. + for (FrontendInputFile &Input : FrontendOpts.Inputs) { + if (Input.isBuffer()) + continue; // FIXME: Can this happen when parsing command-line? + + SmallString<256> RemappedFile; + Mapper.map(Input.getFile(), RemappedFile); + if (RemappedFile != Input.getFile()) + Input = + FrontendInputFile(RemappedFile, Input.getKind(), Input.isSystem()); + } + + // Skip the output file. That's not the input CAS filesystem. + // Mapper.mapInPlace(OutputFile); <-- this doesn't make sense. + + Mapper.mapInPlace(FrontendOpts.CodeCompletionAt.FileName); + + // Don't remap plugins (for now), since we don't know how to remap their + // arguments. Maybe they should be loaded outside of the CAS filesystem? + // Maybe we should error? + // + // Mapper.mapInPlaceOrFilterOut(FrontendOpts.Plugins); + + mapInPlaceAll(FrontendOpts.ModuleMapFiles); + mapInPlaceAll(FrontendOpts.ModuleFiles); + mapInPlaceAll(FrontendOpts.ModulesEmbedFiles); + mapInPlaceAll(FrontendOpts.ASTMergeFiles); + Mapper.mapInPlace(FrontendOpts.OverrideRecordLayoutsFile); + Mapper.mapInPlace(FrontendOpts.StatsFile); + for (auto &[Path, _] : FrontendOpts.ModuleCacheKeys) + Mapper.mapInPlace(Path); + + // Filesystem options. + Mapper.mapInPlace(FileSystemOpts.WorkingDir); + + // Code generation options. + auto &CodeGenOpts = Invocation.getCodeGenOpts(); + Mapper.mapInPlace(CodeGenOpts.DebugCompilationDir); + Mapper.mapInPlace(CodeGenOpts.CoverageCompilationDir); + + // Sanitizer options. + mapInPlaceAll(Invocation.getLangOpts().NoSanitizeFiles); + + // Handle coverage mappings. + Mapper.mapInPlace(CodeGenOpts.ProfileInstrumentUsePath); + Mapper.mapInPlace(CodeGenOpts.SampleProfileFile); + Mapper.mapInPlace(CodeGenOpts.ProfileRemappingFile); + + // Dependency output options. + // Note: these are not in the cache key, but they are in the module context + // hash, which indirectly impacts the cache key when importing a module. + // In the future we may change how -fmodule-file-cache-key works when + // remapping to avoid needing this. + for (auto &ExtraDep : Invocation.getDependencyOutputOpts().ExtraDeps) + Mapper.mapInPlace(ExtraDep.first); +} + +void DepscanPrefixMapping::configurePrefixMapper(const CompilerInvocation &CI, + llvm::PrefixMapper &Mapper) { + return configurePrefixMapper(CI.getFrontendOpts().PathPrefixMappings, Mapper); +} + +void DepscanPrefixMapping::configurePrefixMapper( + ArrayRef PathPrefixMappings, llvm::PrefixMapper &Mapper) { + if (PathPrefixMappings.empty()) + return; + + llvm::SmallVector Split; + llvm::MappedPrefix::transformJoinedIfValid(PathPrefixMappings, Split); + for (auto &MappedPrefix : Split) + Mapper.add(MappedPrefix); + + Mapper.sort(); +} + +Expected clang::scanAndUpdateCC1InlineWithTool( + DependencyScanningTool &Tool, DiagnosticConsumer &DiagsConsumer, + raw_ostream *VerboseOS, CompilerInvocation &Invocation, + StringRef WorkingDirectory, llvm::cas::ObjectStore &DB) { + // Override the CASOptions. They may match (the caller having sniffed them + // out of InputArgs) but if they have been overridden we want the new ones. + Invocation.getCASOpts() = Tool.getCASOpts(); + + bool ProduceIncludeTree = + Tool.getScanningFormat() == + tooling::dependencies::ScanningOutputFormat::IncludeTree; + + std::unique_ptr MapperPtr; + if (ProduceIncludeTree) { + MapperPtr = std::make_unique(); + } else { + MapperPtr = std::make_unique( + Tool.getCachingFileSystem()); + } + llvm::PrefixMapper &Mapper = *MapperPtr; + DepscanPrefixMapping::configurePrefixMapper(Invocation, Mapper); + + auto ScanInvocation = std::make_shared(Invocation); + // An error during dep-scanning is treated as if the main compilation has + // failed, but warnings are ignored and deferred for the main compilation. + ScanInvocation->getDiagnosticOpts().IgnoreWarnings = true; + + std::optional Root; + if (ProduceIncludeTree) { + if (Error E = + Tool.getIncludeTreeFromCompilerInvocation( + DB, std::move(ScanInvocation), WorkingDirectory, + /*LookupModuleOutput=*/nullptr, DiagsConsumer, VerboseOS, + /*DiagGenerationAsCompilation*/ true) + .moveInto(Root)) + return std::move(E); + } else { + if (Error E = Tool.getDependencyTreeFromCompilerInvocation( + std::move(ScanInvocation), WorkingDirectory, + DiagsConsumer, VerboseOS, + /*DiagGenerationAsCompilation*/ true) + .moveInto(Root)) + return std::move(E); + } + + // Turn off dependency outputs. Should have already been emitted. + Invocation.getDependencyOutputOpts().OutputFile.clear(); + + configureInvocationForCaching(Invocation, Tool.getCASOpts(), Root->toString(), + ProduceIncludeTree + ? CachingInputKind::IncludeTree + : CachingInputKind::FileSystemRoot, + WorkingDirectory.str()); + DepscanPrefixMapping::remapInvocationPaths(Invocation, Mapper); + return *Root; +} diff --git a/clang/lib/Tooling/Refactor/ASTSlice.cpp b/clang/lib/Tooling/Refactor/ASTSlice.cpp new file mode 100644 index 0000000000000..d3e4d266352ac --- /dev/null +++ b/clang/lib/Tooling/Refactor/ASTSlice.cpp @@ -0,0 +1,638 @@ +//===--- ASTSlice.cpp - Represents a portion of the AST -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ASTSlice.h" +#include "SourceLocationUtilities.h" +#include "StmtUtils.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/SaveAndRestore.h" +#include + +using namespace clang; +using namespace clang::tooling; + +namespace { + +/// Searches for AST nodes around the given source location and range that can +/// be used to initiate a refactoring operation. +class ASTSliceFinder : public clang::RecursiveASTVisitor { +public: + explicit ASTSliceFinder(SourceLocation Location, SourceRange SelectionRange, + const ASTContext &Context) + : Location(Location), SelectionRange(SelectionRange), Context(Context) {} + + bool TraverseDecl(Decl *D) { + if (!D) + return true; + if (isa(D) && !D->isImplicit()) + collectDeclIfInRange(D); + // TODO: Handle Lambda/Blocks. + if (!isa(D) && !isa(D)) { + RecursiveASTVisitor::TraverseDecl(D); + return true; + } + const Decl *PreviousDecl = CurrentDecl; + CurrentDecl = D; + RecursiveASTVisitor::TraverseDecl(D); + CurrentDecl = PreviousDecl; + return true; + } + + bool TraverseStmt(Stmt *S) { + if (!S) + return true; + // PseudoObjectExpressions don't have to be parents. + if (isa(S)) + return RecursiveASTVisitor::TraverseStmt(S); + llvm::SaveAndRestore Parent(ParentStmt, CurrentStmt); + llvm::SaveAndRestore Current(CurrentStmt, S); + RecursiveASTVisitor::TraverseStmt(S); + return true; + } + + bool TraversePseudoObjectExpr(PseudoObjectExpr *E) { + // Avoid traversing the getter/setter message sends for property + // expressions. + TraverseStmt(E->getSyntacticForm()); + return true; + } + + bool TraverseObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { + RecursiveASTVisitor::TraverseObjCPropertyRefExpr(E); + // Visit the opaque base manually as it won't be traversed by the + // PseudoObjectExpr. + if (E->isObjectReceiver()) { + if (const auto *Opaque = dyn_cast(E->getBase())) + TraverseStmt(Opaque->getSourceExpr()); + } + return true; + } + + // Statement visitors: + + bool VisitStmt(Stmt *S) { + collectStmtIfInRange(S, S->getSourceRange()); + return true; + } + + // Ignore some implicit expressions. + + bool WalkUpFromMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) { + return true; + } + + bool WalkUpFromCXXThisExpr(CXXThisExpr *E) { + if (E->isImplicit()) + return true; + return RecursiveASTVisitor::WalkUpFromCXXThisExpr(E); + } + + /// Checks if the given statement and its source range has the location + /// of interest or overlaps with the selection range, and adds this node to + /// the set of statements for the slice that's being constructed. + void collectStmtIfInRange(const Stmt *S, SourceRange Range) { + SourceLocation Start = Range.getBegin(); + const auto &SM = Context.getSourceManager(); + bool IsStartMacroArg = false; + if (Start.isMacroID()) { + if (SM.isMacroArgExpansion(Start)) { + Start = SM.getSpellingLoc(Start); + IsStartMacroArg = true; + } else { + Start = SM.getExpansionLoc(Start); + } + } + SourceLocation End = Range.getEnd(); + if (End.isMacroID() && SM.isMacroArgExpansion(End)) { + // Ignore the node that's span across normal code and a macro argument. + if (IsStartMacroArg) + End = SM.getSpellingLoc(End); + } + End = getPreciseTokenLocEnd(End, SM, Context.getLangOpts()); + if (!isPairOfFileLocations(Start, End)) + return; + if (SelectionRange.isValid()) { + if (!areRangesOverlapping(SelectionRange, SourceRange(Start, End), + Context.getSourceManager())) + return; + } else if (!isPointWithin(Location, Start, End, Context.getSourceManager())) + return; + Matches.emplace_back(S, ParentStmt, CurrentDecl, SourceRange(Start, End)); + } + + void collectDeclIfInRange(const Decl *D) { + SourceLocation Start = D->getSourceRange().getBegin(); + SourceLocation End = getPreciseTokenLocEnd( + getLexicalEndLocForDecl(D, Context.getSourceManager(), + Context.getLangOpts()), + Context.getSourceManager(), Context.getLangOpts()); + if (!isPairOfFileLocations(Start, End)) + return; + if (SelectionRange.isValid()) { + if (!areRangesOverlapping(SelectionRange, SourceRange(Start, End), + Context.getSourceManager())) + return; + } else if (!isPointWithin(Location, Start, End, Context.getSourceManager())) + return; + Matches.emplace_back(D, CurrentDecl, SourceRange(Start, End)); + } + + SmallVector Matches; + /// The point of interest. + /// + /// Represents a location at which refactoring should be initiated. + const SourceLocation Location; + const SourceRange SelectionRange; + const ASTContext &Context; + const Decl *CurrentDecl = nullptr; + const Stmt *ParentStmt = nullptr, *CurrentStmt = nullptr; +}; + +} // end anonymous namespace + +ASTSlice::SelectedStmt::SelectedStmt(ASTSlice &Slice, const Stmt *S, + unsigned Index) + : Slice(Slice), S(S), Index(Index) { + assert(S && "No statement given!"); +} + +ASTSlice::SelectedDecl::SelectedDecl(const Decl *D) : D(D) { + assert(D && "No decl given!"); +} + +const Decl *ASTSlice::SelectedStmt::getParentDecl() { + return Slice.parentDeclForIndex(Index); +} + +ASTSlice::ASTSlice(SourceLocation Location, SourceRange SelectionRange, + ASTContext &Context) + : Context(Context), SelectionLocation(Location), + SelectionRange(SelectionRange) { + FileID SearchFile = Context.getSourceManager().getFileID(Location); + ASTSliceFinder Visitor(Location, SelectionRange, Context); + SourceLocation EndLoc; + for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) { + if (CurrDecl->getBeginLoc().isInvalid()) + continue; + + if (EndLoc.isValid() && + !Context.getSourceManager().isBeforeInTranslationUnit( + CurrDecl->getBeginLoc(), EndLoc)) + break; + const SourceLocation FileLoc = + Context.getSourceManager().getSpellingLoc(CurrDecl->getBeginLoc()); + if (Context.getSourceManager().getFileID(FileLoc) == SearchFile) + Visitor.TraverseDecl(CurrDecl); + // We are only interested in looking at a single top level declaration + // even if our selection range spans across multiple top level declarations. + if (!Visitor.Matches.empty()) { + // Objective-C @implementation declarations might have trailing functions + // that are declared outside of the @implementation, so continue looking + // through them. + if (isa(CurrDecl)) { + EndLoc = CurrDecl->getEndLoc(); + continue; + } + break; + } + } + + for (auto I = Visitor.Matches.rbegin(), E = Visitor.Matches.rend(); I != E; + ++I) + NodeTree.push_back(*I); +} + +bool ASTSlice::isSourceRangeSelected(CharSourceRange Range) const { + SourceRange R = Range.getAsRange(); + if (Range.isTokenRange()) + R.setEnd(getPreciseTokenLocEnd(R.getEnd(), Context.getSourceManager(), + Context.getLangOpts())); + if (SelectionRange.isInvalid()) + return isPointWithin(SelectionLocation, R.getBegin(), R.getEnd(), + Context.getSourceManager()); + return areRangesOverlapping(SelectionRange, R, Context.getSourceManager()); +} + +/// Find the 'if' statement that acts as the start of the +/// 'if'/'else if'/'else' construct. +static std::pair +findIfStmtStart(const IfStmt *If, unsigned Index, + ArrayRef NodeTree) { + if (Index >= NodeTree.size()) + return {If, Index}; // We've reached the top of the tree, return. + const auto *ParentIf = + dyn_cast_or_null(NodeTree[Index + 1].getStmtOrNull()); + // The current 'if' is actually an 'else if' when the next 'if' has an else + // statement that points to the current 'if'. + if (!ParentIf || ParentIf->getElse() != If) + return {If, Index}; + return findIfStmtStart(ParentIf, Index + 1, NodeTree); +} + +/// Find an expression that best represents the given selected expression. +static std::pair +canonicalizeSelectedExpr(const Stmt *S, unsigned Index, + ArrayRef NodeTree) { + const auto Same = std::make_pair(S, Index); + if (Index + 1 >= NodeTree.size()) + return Same; + const Stmt *Parent = NodeTree[Index + 1].getStmtOrNull(); + if (!Parent) + return Same; + auto Next = std::make_pair(Parent, Index + 1); + // The entire pseudo expression is selected when just its syntactic + // form is selected. + if (isa(S)) { + if (const auto *POE = dyn_cast_or_null(Parent)) { + if (POE->getSyntacticForm() == S) + return Next; + } + } + + // Look through the implicit casts in the parents. + unsigned ParentIndex = Index + 1; + for (; (ParentIndex + 1) < NodeTree.size() && isa(Parent); + ++ParentIndex) { + const Stmt *NewParent = NodeTree[ParentIndex + 1].getStmtOrNull(); + if (!NewParent) + break; + Parent = NewParent; + } + Next = std::make_pair(Parent, ParentIndex); + + // The entire ObjC string literal is selected when just its string + // literal is selected. + if (isa(S) && isa(Parent)) + return Next; + // The entire call should be selected when just the member expression + // that refers to the method is selected. + // FIXME: Check if this can be one of the call arguments. + if (isa(S) && isa(Parent)) + return Next; + // The entire call should be selected when just the callee is selected. + if (const auto *DRE = dyn_cast(S)) { + if (const auto *Call = dyn_cast(Parent)) { + if (Call->getCalleeDecl() == DRE->getDecl()) + return Next; + } + } + return Same; +} + +std::optional ASTSlice::nearestSelectedStmt( + llvm::function_ref Predicate) { + for (const auto &Node : llvm::enumerate(NodeTree)) { + const Stmt *S = Node.value().getStmtOrNull(); + if (!S || !Predicate(S)) + continue; + + // Found the match. Perform any additional adjustments. + if (isa(S)) { + auto CanonicalExpr = canonicalizeSelectedExpr(S, Node.index(), NodeTree); + return SelectedStmt(*this, CanonicalExpr.first, CanonicalExpr.second); + } + switch (S->getStmtClass()) { + case Stmt::IfStmtClass: { + // TODO: Fix findIfStmtStart bug with Index where it will return the + // index of the last statement. + auto If = findIfStmtStart(cast(S), Node.index(), NodeTree); + return SelectedStmt(*this, If.first, If.second); + } + default: + break; + } + + return SelectedStmt(*this, S, Node.index()); + } + return std::nullopt; +} + +std::optional +ASTSlice::nearestSelectedStmt(Stmt::StmtClass Class) { + return nearestSelectedStmt( + [Class](const Stmt *S) -> bool { return S->getStmtClass() == Class; }); +} + +const Stmt *ASTSlice::nearestStmt(Stmt::StmtClass Class) { + auto Result = nearestSelectedStmt(Class); + return Result ? Result->getStmt() : nullptr; +} + +std::optional ASTSlice::innermostSelectedDecl( + llvm::function_ref Predicate, unsigned Options) { + if (SelectionRange.isValid()) { + if (Options & ASTSlice::InnermostDeclOnly) { + auto Result = getInnermostCompletelySelectedDecl(); + if (!Result) + return std::nullopt; + if (Predicate(Result->getDecl())) + return Result; + return std::nullopt; + } + // Traverse down through all of the selected node checking the predicate. + // TODO: Cache the SelectionRangeOverlap kinds properly instead of relying + // on getInnermostCompletelySelectedDecl. + getInnermostCompletelySelectedDecl(); + for (const auto &N : NodeTree) { + const Decl *D = N.getDeclOrNull(); + if (!D) + continue; + if (N.SelectionRangeOverlap != Node::ContainsSelectionRange) + continue; + if (Predicate(D)) + return SelectedDecl(D); + } + return std::nullopt; + } + for (const auto &Node : llvm::enumerate(NodeTree)) { + const Decl *D = Node.value().getDeclOrNull(); + if (!D) + continue; + if (Predicate(D)) + return SelectedDecl(D); + if (Options & ASTSlice::InnermostDeclOnly) + return std::nullopt; + } + return std::nullopt; +} + +std::optional +ASTSlice::innermostSelectedDecl(ArrayRef Classes, + unsigned Options) { + assert(!Classes.empty() && "Expected at least one decl kind"); + return innermostSelectedDecl( + [&](const Decl *D) { + for (Decl::Kind Class : Classes) { + if (D->getKind() == Class) + return true; + } + return false; + }, + Options); +} + +/// Compute the SelectionRangeOverlap kinds for matched AST nodes. +/// +/// The overlap kinds are computed only upto the first node that contains the +/// entire selection range. +static void +computeSelectionRangeOverlapKinds(MutableArrayRef NodeTree, + SourceRange SelectionRange, + const SourceManager &SM) { + for (ASTSlice::Node &Node : NodeTree) { + bool HasStart = + isPointWithin(SelectionRange.getBegin(), Node.Range.getBegin(), + Node.Range.getEnd(), SM); + bool HasEnd = isPointWithin(SelectionRange.getEnd(), Node.Range.getBegin(), + Node.Range.getEnd(), SM); + if (HasStart && HasEnd) + Node.SelectionRangeOverlap = ASTSlice::Node::ContainsSelectionRange; + else if (HasStart) + Node.SelectionRangeOverlap = ASTSlice::Node::ContainsSelectionRangeStart; + else if (HasEnd) + Node.SelectionRangeOverlap = ASTSlice::Node::ContainsSelectionRangeEnd; + } +} + +const Stmt *findFirstStatementAfter(const CompoundStmt *CS, SourceLocation Loc, + const SourceManager &SM) { + for (const Stmt *S : CS->body()) { + if (!SM.isBeforeInTranslationUnit(S->getBeginLoc(), Loc)) + return S; + } + return nullptr; +} + +const Stmt *findLastStatementBefore(const CompoundStmt *CS, SourceLocation Loc, + const Stmt *StartAt, + const SourceManager &SM) { + auto It = std::find(CS->body_begin(), CS->body_end(), StartAt); + assert(It != CS->body_end()); + const Stmt *Last = StartAt; + for (auto E = CS->body_end(); It != E; ++It) { + const Stmt *S = *It; + if (!SM.isBeforeInTranslationUnit(S->getBeginLoc(), Loc)) + return Last; + Last = S; + } + return Last; +} + +/// Return the source construct that contains the given compound statement. +/// +/// This is useful to find the source construct to which the given compound +/// statement belongs to lexically. For example, if we've selected just the +/// body of an if statement, we ideally want to select the entire if statement. +static std::pair +findCompoundStatementSourceConstruct(const CompoundStmt *CS, + ArrayRef NodeTree) { + for (const auto &Node : llvm::enumerate(NodeTree)) { + const Stmt *S = Node.value().getStmtOrNull(); + if (!S) + continue; + for (const Stmt *Child : S->children()) { + if (Child == CS) { + if (isa(S)) + return {CS, 0}; + if (const auto *If = dyn_cast(S)) + return findIfStmtStart(If, Node.index(), NodeTree); + return {S, Node.index()}; + } + } + } + // This is the outer compound statement. + return {CS, 0}; +} + +/// Return the source construct that contains the given switch case. +static std::pair +findSwitchSourceConstruct(const SwitchCase *Case, + ArrayRef NodeTree) { + for (const auto &Node : llvm::enumerate(NodeTree)) { + const Stmt *S = Node.value().getStmtOrNull(); + if (!S) + continue; + if (isa(S)) + return {S, Node.index()}; + } + return {Case, 0}; +} + +SelectedStmtSet SelectedStmtSet::createFromEntirelySelected(const Stmt *S, + unsigned Index) { + SelectedStmtSet Result; + Result.containsSelectionRange = S; + Result.containsSelectionRangeIndex = Index; + return Result; +} + +std::optional +ASTSlice::getInnermostCompletelySelectedDecl() { + assert(SelectionRange.isValid() && "No selection range!"); + if (CachedSelectedInnermostDecl) + return *CachedSelectedInnermostDecl; + computeSelectionRangeOverlapKinds(NodeTree, SelectionRange, + Context.getSourceManager()); + std::optional Result; + for (const auto &N : llvm::enumerate(NodeTree)) { + const Decl *D = N.value().getDeclOrNull(); + if (!D) + continue; + if (N.value().SelectionRangeOverlap != Node::ContainsSelectionRange) + continue; + Result = SelectedDecl(D); + break; + } + CachedSelectedInnermostDecl = Result; + return Result; +} + +static bool isCaseSelected(const SwitchStmt *S, SourceRange SelectionRange, + const SourceManager &SM) { + for (const SwitchCase *Case = S->getSwitchCaseList(); Case; + Case = Case->getNextSwitchCase()) { + SourceRange Range(Case->getBeginLoc(), Case->getColonLoc()); + if (areRangesOverlapping(Range, SelectionRange, SM)) + return true; + } + return false; +} + +std::optional ASTSlice::computeSelectedStmtSet() { + if (SelectionRange.isInvalid()) + return std::nullopt; + computeSelectionRangeOverlapKinds(NodeTree, SelectionRange, + Context.getSourceManager()); + + SelectedStmtSet Result; + for (const auto &N : llvm::enumerate(NodeTree)) { + const auto *S = N.value().getStmtOrNull(); + if (!S) + continue; + switch (N.value().SelectionRangeOverlap) { + case Node::ContainsSelectionRange: { + Result.containsSelectionRange = S; + Result.containsSelectionRangeIndex = N.index(); + + const auto *CS = dyn_cast(Result.containsSelectionRange); + if (!CS) { + // The entire if should be selected when just the 'else if' overlaps + // with the selection range. + if (const auto *If = dyn_cast(Result.containsSelectionRange)) { + auto IfConstruct = findIfStmtStart(If, N.index(), NodeTree); + return SelectedStmtSet::createFromEntirelySelected( + IfConstruct.first, IfConstruct.second); + } + // The entire switch should be selected when just a 'case' overlaps + // with the selection range. + if (const auto *Case = + dyn_cast(Result.containsSelectionRange)) { + auto Switch = findSwitchSourceConstruct( + Case, ArrayRef(NodeTree).drop_front(N.index() + 1)); + return SelectedStmtSet::createFromEntirelySelected( + Switch.first, N.index() + Switch.second); + } + + auto CanonicalExpr = canonicalizeSelectedExpr(S, N.index(), NodeTree); + Result.containsSelectionRange = CanonicalExpr.first; + Result.containsSelectionRangeIndex = CanonicalExpr.second; + return Result; + } + + bool IsLBraceSelected = + !Context.getSourceManager().isBeforeInTranslationUnit( + CS->getLBracLoc(), SelectionRange.getBegin()); + bool IsRBraceSelected = + Context.getSourceManager().isBeforeInTranslationUnit( + CS->getRBracLoc(), SelectionRange.getEnd()); + + // Return the entire source construct that has the compound statement + // when one of the braces is selected, or when an actual `case` of the + // switch is selected. + auto Construct = findCompoundStatementSourceConstruct( + CS, ArrayRef(NodeTree).drop_front(N.index() + 1)); + if (Construct.first != CS && + ((IsLBraceSelected || IsRBraceSelected) || + (isa(Construct.first) && + isCaseSelected(cast(Construct.first), SelectionRange, + Context.getSourceManager())))) + return SelectedStmtSet::createFromEntirelySelected( + Construct.first, N.index() + Construct.second); + + // When both braces are selected the entire compound statement is + // considered to be selected. + if (IsLBraceSelected && IsRBraceSelected) + return Result; + if (IsLBraceSelected) + Result.containsSelectionRangeStart = CS->body_front(); + else if (IsRBraceSelected) + Result.containsSelectionRangeEnd = CS->body_back(); + + if (!Result.containsSelectionRangeStart) + Result.containsSelectionRangeStart = findFirstStatementAfter( + CS, SelectionRange.getBegin(), Context.getSourceManager()); + + // Return an empty set when the compound statements os empty or the + // selection range starts after the last statement or the selection range + // doesn't overlap with any actual statements. + if (!Result.containsSelectionRangeStart || + !Context.getSourceManager().isBeforeInTranslationUnit( + Result.containsSelectionRangeStart->getBeginLoc(), + SelectionRange.getEnd())) + return std::nullopt; + + if (!Result.containsSelectionRangeEnd) + Result.containsSelectionRangeEnd = findLastStatementBefore( + CS, SelectionRange.getEnd(), Result.containsSelectionRangeStart, + Context.getSourceManager()); + + return Result; + } + case Node::ContainsSelectionRangeStart: + Result.containsSelectionRangeStart = S; + break; + case Node::ContainsSelectionRangeEnd: + Result.containsSelectionRangeEnd = S; + break; + case Node::UnknownOverlap: + break; + } + } + return Result; +} + +std::optional ASTSlice::getSelectedStmtSet() { + if (CachedSelectedStmtSet) + return *CachedSelectedStmtSet; + CachedSelectedStmtSet = computeSelectedStmtSet(); + return *CachedSelectedStmtSet; +} + +bool ASTSlice::isContainedInCompoundStmt(unsigned Index) { + assert(Index < NodeTree.size() && "Invalid node index"); + for (unsigned I = Index + 1, E = NodeTree.size(); I != E; ++I) { + const Stmt *S = NodeTree[I].getStmtOrNull(); + if (!S) + continue; + if (isa(S)) + return true; + } + return false; +} + +const Decl *ASTSlice::parentDeclForIndex(unsigned Index) { + return NodeTree[Index].ParentDecl; +} + +const Stmt *ASTSlice::parentStmtForIndex(unsigned Index) { + return NodeTree[Index].ParentStmt; +} diff --git a/clang/lib/Tooling/Refactor/ASTSlice.h b/clang/lib/Tooling/Refactor/ASTSlice.h new file mode 100644 index 0000000000000..d3e2e8701ead2 --- /dev/null +++ b/clang/lib/Tooling/Refactor/ASTSlice.h @@ -0,0 +1,181 @@ +//===--- ASTSlice.h - Represents a portion of the AST ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_ASTSLICE_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_ASTSLICE_H + +#include "clang/AST/DeclBase.h" +#include "clang/AST/Stmt.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang { + +class NamedDecl; + +namespace tooling { + +/// Represents a set of statements that overlap with the selection range. +struct SelectedStmtSet { + /// The outermost statement that contains the start of the selection range. + const Stmt *containsSelectionRangeStart = nullptr; + + /// The outermost statement that contains the end of the selection range. + const Stmt *containsSelectionRangeEnd = nullptr; + + /// The innermost statement that contains the entire selection range. + const Stmt *containsSelectionRange = nullptr; + + /// The index of the innermost statement that contains the entire selection + /// range. The index points into the NodeTree stored in the \c ASTSlice. + std::optional containsSelectionRangeIndex; + + static SelectedStmtSet createFromEntirelySelected(const Stmt *S, + unsigned Index); + + /// Returns true if the compound statement is not fully selected. + bool isCompoundStatementPartiallySelected() const { + assert(containsSelectionRange && "No statement selected"); + return isa(containsSelectionRange) && + (containsSelectionRangeStart || containsSelectionRangeEnd); + } +}; + +/// A portion of the AST that is located around the location and/or source +/// range of interest. +class ASTSlice { +public: + struct Node { + enum SelectionRangeOverlapKind { + UnknownOverlap, + ContainsSelectionRangeStart, + ContainsSelectionRangeEnd, + ContainsSelectionRange + }; + llvm::PointerUnion StmtOrDecl; + const Stmt *ParentStmt; + const Decl *ParentDecl; + SourceRange Range; + SelectionRangeOverlapKind SelectionRangeOverlap = UnknownOverlap; + + const Stmt *getStmtOrNull() const { + return StmtOrDecl.dyn_cast(); + } + + const Decl *getDeclOrNull() const { + return StmtOrDecl.dyn_cast(); + } + + Node(const Stmt *S, const Stmt *ParentStmt, const Decl *ParentDecl, + SourceRange Range) + : StmtOrDecl(S), ParentStmt(ParentStmt), ParentDecl(ParentDecl), + Range(Range) {} + Node(const Decl *D, const Decl *ParentDecl, SourceRange Range) + : StmtOrDecl(D), ParentStmt(nullptr), ParentDecl(ParentDecl), + Range(Range) {} + }; + + /// Represents a statement that overlaps with the selection range/point. + class SelectedStmt { + ASTSlice &Slice; + const Stmt *S; + unsigned Index; + + friend class ASTSlice; + + SelectedStmt(ASTSlice &Slice, const Stmt *S, unsigned Index); + + public: + const Stmt *getStmt() { return S; } + const Decl *getParentDecl(); + }; + + /// Represents a declaration that overlaps with the selection range/point. + class SelectedDecl { + const Decl *D; + + friend class ASTSlice; + + SelectedDecl(const Decl *D); + + public: + const Decl *getDecl() { return D; } + }; + + ASTSlice(SourceLocation Location, SourceRange SelectionRange, + ASTContext &Context); + + /// Returns true if the given source range overlaps with the selection. + bool isSourceRangeSelected(CharSourceRange Range) const; + + enum SelectionSearchOptions { + /// Search with-in the innermost declaration only, including the declaration + /// itself without inspecting any other outer declarations. + InnermostDeclOnly = 1 + }; + + /// Returns the statement that results in true when passed into \p Predicate + /// that's nearest to the location of interest, or \c None if such statement + /// isn't found. + std::optional + nearestSelectedStmt(llvm::function_ref Predicate); + + /// Returns the statement of the given class that's nearest to the location + /// of interest, or \c None if such statement isn't found. + std::optional nearestSelectedStmt(Stmt::StmtClass Class); + + /// TODO: Remove in favour of nearestStmt that returns \c SelectedStmt + const Stmt *nearestStmt(Stmt::StmtClass Class); + + /// Returns the declaration that overlaps with the selection range, is + /// nearest to the location of interest and that results in true when passed + /// into \p Predicate, or \c None if such declaration isn't found. + std::optional + innermostSelectedDecl(llvm::function_ref Predicate, + unsigned Options = 0); + + /// Returns the declaration closest to the location of interest whose decl + /// kind is in \p Classes, or \c None if no such decl can't be found. + std::optional + innermostSelectedDecl(ArrayRef Classes, unsigned Options = 0); + + /// Returns the set of statements that overlap with the selection range. + std::optional getSelectedStmtSet(); + + /// Returns true if the statement with the given index is contained in a + /// compound statement that overlaps with the selection range. + bool isContainedInCompoundStmt(unsigned Index); + + /// Returns the declaration that contains the statement at the given index. + const Decl *parentDeclForIndex(unsigned Index); + + /// Returns the statement that contains the statement at the given index. + const Stmt *parentStmtForIndex(unsigned Index); + +private: + std::optional computeSelectedStmtSet(); + + /// Returns the innermost declaration that contains both the start and the + /// end of the selection range. + std::optional getInnermostCompletelySelectedDecl(); + + /// The lowest element is the top of the hierarchy + SmallVector NodeTree; + ASTContext &Context; + SourceLocation SelectionLocation; + SourceRange SelectionRange; + std::optional> CachedSelectedStmtSet; + std::optional> CachedSelectedInnermostDecl; +}; + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_ASTSLICE_H diff --git a/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp b/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp new file mode 100644 index 0000000000000..abc666e4660ec --- /dev/null +++ b/clang/lib/Tooling/Refactor/ASTStateSerialization.cpp @@ -0,0 +1,69 @@ +//===-- ASTStateSerialization.cpp - Persists TU-specific state across TUs -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RefactoringContinuations.h" +#include "clang/AST/RecursiveASTVisitor.h" + +using namespace clang; +using namespace clang::tooling; +using namespace clang::tooling::detail; + +namespace { + +class USRToDeclConverter + : public clang::RecursiveASTVisitor { + llvm::StringMap &USRs; + unsigned NumFound = 0; + +public: + USRToDeclConverter(llvm::StringMap &USRs) : USRs(USRs) {} + + bool isDone() const { return NumFound == USRs.size(); } + + bool VisitNamedDecl(const NamedDecl *D) { + std::string USR = rename::getUSRForDecl(D); + auto It = USRs.find(USR); + if (It == USRs.end() || It->second) + return true; + It->second = D; + ++NumFound; + return NumFound != USRs.size(); + } +}; + +} // end anonymous namespace + +const Decl *PersistentToASTSpecificStateConverter::lookupDecl(StringRef USR) { + if (USR.empty()) + return nullptr; + auto It = ConvertedDeclRefs.find(USR); + if (It != ConvertedDeclRefs.end()) + return It->second; + // FIXME: If we ever need to convert a PersistentDeclRef through the ASTQuery, + // we have to support conversion without coalesced conversion. + assert(false && "Persistent decl refs should be converted all at once"); + return nullptr; +} + +void PersistentToASTSpecificStateConverter::runCoalescedConversions() { + USRToDeclConverter Converter(ConvertedDeclRefs); + for (Decl *D : Context.getTranslationUnitDecl()->decls()) { + Converter.TraverseDecl(D); + if (Converter.isDone()) + break; + } +} + +FileID +PersistentToASTSpecificStateConverter::convert(const PersistentFileID &Ref) { + FileManager &FM = Context.getSourceManager().getFileManager(); + OptionalFileEntryRef Entry = FM.getOptionalFileRef(Ref.Filename); + if (!Entry) + return FileID(); + return Context.getSourceManager().translateFile(*Entry); +} diff --git a/clang/lib/Tooling/Refactor/CMakeLists.txt b/clang/lib/Tooling/Refactor/CMakeLists.txt new file mode 100644 index 0000000000000..eec7d8a4107b2 --- /dev/null +++ b/clang/lib/Tooling/Refactor/CMakeLists.txt @@ -0,0 +1,48 @@ +set(LLVM_LINK_COMPONENTS support FrontendOpenMP) + +add_clang_library(clangToolingRefactor + ASTSlice.cpp + ASTStateSerialization.cpp + Extract.cpp + ExtractRepeatedExpressionIntoVariable.cpp + ExtractionUtils.cpp + FillInEnumSwitchCases.cpp + FillInMissingMethodStubsFromAbstractClasses.cpp + FillInMissingProtocolStubs.cpp + IfSwitchConversion.cpp + ImplementDeclaredMethods.cpp + IndexerQueries.cpp + LocalizeObjCStringLiteral.cpp + RefactoringActions.cpp + RefactoringActionFinder.cpp + RefactoringOperation.cpp + RefactoringOptions.cpp + RenamingOperation.cpp + RenameIndexedFile.cpp + RenamedSymbol.cpp + SourceLocationUtilities.cpp + StmtUtils.cpp + SymbolOperation.cpp + SymbolOccurrenceFinder.cpp + SymbolUSRFinder.cpp + TypeUtils.cpp + USRFinder.cpp + + DEPENDS + ClangDriverOptions + + LINK_LIBS + clangAST + clangASTMatchers + clangBasic + clangEdit + clangFrontend + clangIndex + clangLex + clangToolingCore + clangRewrite + ) + +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + set_source_files_properties(Extract.cpp PROPERTIES COMPILE_FLAGS /bigobj) +endif() diff --git a/clang/lib/Tooling/Refactor/Extract.cpp b/clang/lib/Tooling/Refactor/Extract.cpp new file mode 100644 index 0000000000000..a4f0e5e2ba5ad --- /dev/null +++ b/clang/lib/Tooling/Refactor/Extract.cpp @@ -0,0 +1,2017 @@ +//===--- Extract.cpp - ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements the "extract" refactoring operation. +// +//===----------------------------------------------------------------------===// + +#include "ExtractionUtils.h" +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "StmtUtils.h" +#include "TypeUtils.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/MacroInfo.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/Refactor/RefactoringOptions.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/Path.h" +#include + +using namespace clang; +using namespace clang::tooling; + +namespace { + +struct CompoundStatementRange { + CompoundStmt::const_body_iterator First, Last; + + const Stmt *getFirst() const { + // We must have selected just the child of the case, since a selection that + // includes the case is treated like a selection of the entire switch. + if (const auto *Case = dyn_cast(*First)) { + if (const Stmt *S = Case->getSubStmt()) + return S; + } + return *First; + } + + const Stmt *getLast() const { return *Last; } + + // TODO: We might not want to iterate over the switch case if we've just + // selected its child. We should switch over to an array of nodes instead of + // an iterator pair instead. + CompoundStmt::const_body_iterator begin() const { return First; } + CompoundStmt::const_body_iterator end() const { return Last + 1; } +}; + +enum class ExtractionKind { Function, Method, Expression }; + +class ExtractOperation : public RefactoringOperation { +public: + struct CandidateInfo { + CandidateInfo(SourceRange Range, StringRef PreInsertedText = "", + const Stmt *AnalyzedStatement = nullptr) + : Range(Range), PreInsertedText(PreInsertedText), + AnalyzedStatement(AnalyzedStatement) {} + + /// The candidate token range, i.e. the end location is the starting + /// location of the last token. + SourceRange Range; + /// The text that should be inserted before the call to the extracted + /// function. + StringRef PreInsertedText; + /// The expression that should be analyzed for captured variables and the + /// return value. + const Stmt *AnalyzedStatement; + }; + + ExtractOperation(const Stmt *S, const Stmt *ParentStmt, + const Decl *FunctionLikeParentDecl, + std::vector Candidates, + std::optional ExtractedStmtRange, + std::optional FirstCandidateInfo, + ExtractionKind Kind) + : S(S), ParentStmt(ParentStmt), + FunctionLikeParentDecl(FunctionLikeParentDecl), + Candidates(std::move(Candidates)), + ExtractedStmtRange(ExtractedStmtRange), Kind(Kind) { + if (FirstCandidateInfo) + CandidateExtractionInfo.push_back(*FirstCandidateInfo); + } + + const Stmt *getTransformedStmt() const override { + if (ExtractedStmtRange) + return ExtractedStmtRange->getFirst(); + return S; + } + + const Stmt *getLastTransformedStmt() const override { + if (ExtractedStmtRange) + return ExtractedStmtRange->getLast(); + return nullptr; + } + + std::vector getRefactoringCandidates() override { + return Candidates; + } + + std::vector getAvailableSubActions() override { + std::vector SubActions; + if (isa(FunctionLikeParentDecl) || + isa(FunctionLikeParentDecl)) + SubActions.push_back(RefactoringActionType::Extract_Method); + if (isLexicalExpression(S, ParentStmt)) + SubActions.push_back(RefactoringActionType::Extract_Expression); + return SubActions; + } + + bool isMethodExtraction() const { return Kind == ExtractionKind::Method; } + + bool isExpressionExtraction() const { + return Kind == ExtractionKind::Expression; + } + + llvm::Expected perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + llvm::Expected + performExpressionExtraction(ASTContext &Context, PrintingPolicy &PP); + + const Stmt *S, *ParentStmt; + const Decl *FunctionLikeParentDecl; + std::vector Candidates; + /// A set of extraction candidates that correspond to the extracted code. + SmallVector CandidateExtractionInfo; + std::optional ExtractedStmtRange; + ExtractionKind Kind; +}; + +} // end anonymous namespace + +bool isSimpleExpression(const Expr *E) { + switch (E->IgnoreParenCasts()->getStmtClass()) { + case Stmt::DeclRefExprClass: + case Stmt::PredefinedExprClass: + case Stmt::IntegerLiteralClass: + case Stmt::FloatingLiteralClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::StringLiteralClass: + return true; + default: + return false; + } +} + +static bool isMultipleCandidateBinOp(BinaryOperatorKind Op) { + return Op == BO_Add || Op == BO_Sub; +} + +/// Searches for the selected statement in the given CompoundStatement, looking +/// through things like PseudoObjectExpressions. +static CompoundStmt::const_body_iterator +findSelectedStmt(CompoundStmt::body_const_range Statements, + const Stmt *Target) { + return llvm::find_if(Statements, [=](const Stmt *S) { + if (S == Target) + return true; + if (const auto *POE = dyn_cast(S)) { + if (POE->getSyntacticForm() == Target) + return true; + } + return false; + }); +} + +/// Returns the first and the last statements that should be extracted from a +/// compound statement. +std::optional +getExtractedStatements(const CompoundStmt *CS, const Stmt *Begin, + const Stmt *End) { + if (CS->body_empty()) + return std::nullopt; + assert(Begin && End); + CompoundStatementRange Result; + Result.First = findSelectedStmt(CS->body(), Begin); + if (Result.First == CS->body_end()) + return std::nullopt; + Result.Last = findSelectedStmt( + CompoundStmt::body_const_range(Result.First, CS->body_end()), End); + if (Result.Last == CS->body_end()) + return std::nullopt; + return Result; +} + +static RefactoringOperationResult +initiateAnyExtractOperation(ASTSlice &Slice, ASTContext &Context, + SourceLocation Location, SourceRange SelectionRange, + bool CreateOperation, + ExtractionKind Kind = ExtractionKind::Function) { + auto SelectedStmtsOpt = Slice.getSelectedStmtSet(); + if (!SelectedStmtsOpt) + return std::nullopt; + SelectedStmtSet Stmts = *SelectedStmtsOpt; + // The selection range is contained entirely within this statement (without + // taking leading/trailing comments and whitespace into account). + const Stmt *Selected = Stmts.containsSelectionRange; + + // We only want to perform the extraction if the selection range is entirely + // within a body of a function or method. + if (!Selected) + return std::nullopt; + const Decl *ParentDecl = + Slice.parentDeclForIndex(*Stmts.containsSelectionRangeIndex); + + if (!ParentDecl || + (!Stmts.isCompoundStatementPartiallySelected() && + !Slice.isContainedInCompoundStmt(*Stmts.containsSelectionRangeIndex))) + return RefactoringOperationResult( + "the selected expression is not in a function"); + + if (isa(Selected) && isSimpleExpression(cast(Selected))) + return RefactoringOperationResult("the selected expression is too simple"); + if (const auto *PRE = dyn_cast(Selected)) { + if (!PRE->isMessagingGetter()) + return RefactoringOperationResult("property setter can't be extracted"); + } + + const Stmt *ParentStmt = + Slice.parentStmtForIndex(*Stmts.containsSelectionRangeIndex); + if (Kind == ExtractionKind::Expression && + !isLexicalExpression(Selected, ParentStmt)) + return std::nullopt; + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + + std::optional ExtractedStmtRange; + + // Check if there are multiple candidates that can be extracted. + std::vector Candidates; + std::optional FirstCandidateInfo; + if (const auto *BinOp = dyn_cast(Selected)) { + // Binary '+' and '-' operators allow multiple candidates when the + // selection range starts after the LHS expression but still overlaps + // with the RHS. + if (isMultipleCandidateBinOp(BinOp->getOpcode()) && + (!Stmts.containsSelectionRangeStart || + getPreciseTokenLocEnd( + BinOp->getLHS()->getEndLoc(), Context.getSourceManager(), + Context.getLangOpts()) == SelectionRange.getBegin()) && + Stmts.containsSelectionRangeEnd) { + SourceRange FirstCandidateRange = + SourceRange(SelectionRange.getBegin(), BinOp->getEndLoc()); + if (FirstCandidateRange.getEnd().isMacroID()) + FirstCandidateRange.setEnd(Context.getSourceManager().getExpansionLoc( + FirstCandidateRange.getEnd())); + FirstCandidateInfo = ExtractOperation::CandidateInfo( + FirstCandidateRange, "+ ", + /*AnalyzedStatement=*/BinOp->getRHS()); + Candidates.push_back( + std::string(Lexer::getSourceText( + CharSourceRange::getTokenRange(FirstCandidateRange), + Context.getSourceManager(), Context.getLangOpts()) + .trim())); + Candidates.push_back(std::string(Lexer::getSourceText( + CharSourceRange::getTokenRange(BinOp->getSourceRange()), + Context.getSourceManager(), Context.getLangOpts()))); + } + } else if (const auto *CS = dyn_cast(Selected)) { + // We want to extract some child statements from a compound statement unless + // we've selected the entire compound statement including the opening and + // closing brace. + if (Stmts.containsSelectionRangeStart) + ExtractedStmtRange = + getExtractedStatements(CS, Stmts.containsSelectionRangeStart, + Stmts.containsSelectionRangeEnd); + } + + auto Operation = std::make_unique( + Selected, ParentStmt, ParentDecl, std::move(Candidates), + ExtractedStmtRange, FirstCandidateInfo, Kind); + auto &CandidateExtractionInfo = Operation->CandidateExtractionInfo; + SourceRange Range; + if (ExtractedStmtRange) + Range = SourceRange(ExtractedStmtRange->getFirst()->getBeginLoc(), + ExtractedStmtRange->getLast()->getEndLoc()); + else + Range = Selected->getSourceRange(); + bool IsBeginMacroArgument = false; + if (Range.getBegin().isMacroID()) { + if (Context.getSourceManager().isMacroArgExpansion(Range.getBegin())) { + Range.setBegin( + Context.getSourceManager().getSpellingLoc(Range.getBegin())); + IsBeginMacroArgument = true; + } else { + Range.setBegin( + Context.getSourceManager().getExpansionLoc(Range.getBegin())); + } + } + if (Range.getEnd().isMacroID()) { + if (IsBeginMacroArgument && + Context.getSourceManager().isMacroArgExpansion(Range.getEnd())) + Range.setEnd(Context.getSourceManager().getSpellingLoc(Range.getEnd())); + else + Range.setEnd(Context.getSourceManager() + .getExpansionRange(Range.getEnd()) + .getEnd()); + } + CandidateExtractionInfo.push_back(ExtractOperation::CandidateInfo(Range)); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +RefactoringOperationResult clang::tooling::initiateExtractOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + return initiateAnyExtractOperation(Slice, Context, Location, SelectionRange, + CreateOperation); +} + +RefactoringOperationResult clang::tooling::initiateExtractMethodOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + // TODO: Verify that method extraction is actually possible. + return initiateAnyExtractOperation(Slice, Context, Location, SelectionRange, + CreateOperation, ExtractionKind::Method); +} + +RefactoringOperationResult clang::tooling::initiateExtractExpressionOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + RefactoringOperationResult R = + initiateAnyExtractOperation(Slice, Context, Location, SelectionRange, + CreateOperation, ExtractionKind::Expression); + return R; +} + +using ReferencedEntity = + llvm::PointerUnion; + +/// Iterate over the entities (variables/instance variables) that are directly +/// referenced by the given expression \p E. +/// +/// Note: Objective-C ivars are always captured via 'self'. +static void findEntitiesDirectlyReferencedInExpr( + const Expr *E, + llvm::function_ref Handler) { + E = E->IgnoreParenCasts(); + if (const auto *DRE = dyn_cast(E)) + return Handler(DRE); + + if (const auto *ME = dyn_cast(E)) { + if (isa(ME->getBase()->IgnoreParenCasts())) { + if (const auto *FD = dyn_cast_or_null(ME->getMemberDecl())) + Handler(FD); + return; + } + if (const auto *MD = ME->getMemberDecl()) { + if (isa(MD) || isa(MD)) + findEntitiesDirectlyReferencedInExpr(ME->getBase(), Handler); + } + return; + } + + if (const auto *CO = dyn_cast(E)) { + findEntitiesDirectlyReferencedInExpr(CO->getTrueExpr(), Handler); + findEntitiesDirectlyReferencedInExpr(CO->getFalseExpr(), Handler); + return; + } + + if (const auto *BO = dyn_cast(E)) { + if (BO->getOpcode() == BO_Comma) + return findEntitiesDirectlyReferencedInExpr(BO->getRHS(), Handler); + } +} + +template +static void +findMatchingParameters(Matcher &ParameterMatcher, const Stmt *S, + ASTContext &Context, StringRef Node, + llvm::function_ref Handler) { + using namespace clang::ast_matchers; + auto Matches = match(findAll(callExpr(ParameterMatcher)), *S, Context); + for (const auto &Match : Matches) + Handler(Match.template getNodeAs(Node)); + Matches = match(findAll(cxxConstructExpr(ParameterMatcher)), *S, Context); + for (const auto &Match : Matches) + Handler(Match.template getNodeAs(Node)); +} + +static void +findUseOfConstThis(const Stmt *S, ASTContext &Context, + llvm::function_ref Handler) { + using namespace clang::ast_matchers; + // Check the receiver in method call and member operator calls. + auto This = cxxThisExpr().bind("this"); + auto ThisReceiver = ignoringParenCasts( + anyOf(This, unaryOperator(hasOperatorName("*"), + hasUnaryOperand(ignoringParenCasts(This))))); + auto ConstMethodCallee = callee(cxxMethodDecl(isConst())); + auto Matches = match( + findAll(expr(anyOf(cxxMemberCallExpr(ConstMethodCallee, on(ThisReceiver)), + cxxOperatorCallExpr(ConstMethodCallee, + hasArgument(0, ThisReceiver))))), + *S, Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs("this")); + // Check parameters in calls. + auto ConstPointee = pointee(qualType(isConstQualified())); + auto RefParameter = forEachArgumentWithParam( + ThisReceiver, + parmVarDecl(hasType(qualType(referenceType(ConstPointee))))); + findMatchingParameters(RefParameter, S, Context, "this", Handler); + auto PtrParameter = forEachArgumentWithParam( + ignoringParenCasts(This), + parmVarDecl(hasType(qualType(pointerType(ConstPointee))))); + findMatchingParameters(PtrParameter, S, Context, "this", Handler); +} + +static void findArgumentsPassedByNonConstReference( + const Stmt *S, ASTContext &Context, + llvm::function_ref Handler) { + using namespace clang::ast_matchers; + // Check the receiver in method call and member operator calls. + auto NonPointerReceiver = + expr(unless(hasType(qualType(pointerType())))).bind("arg"); + auto NonConstMethodCallee = callee(cxxMethodDecl(unless(isConst()))); + auto Matches = match( + traverse( + TK_AsIs, + findAll(expr(anyOf( + cxxMemberCallExpr(NonConstMethodCallee, on(NonPointerReceiver)), + cxxOperatorCallExpr(NonConstMethodCallee, + hasArgument(0, NonPointerReceiver)))))), + *S, Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs("arg")); + // Check parameters in calls. + auto RefParameter = forEachArgumentWithParam( + expr().bind("arg"), parmVarDecl(hasType(qualType(referenceType(unless( + pointee(qualType(isConstQualified())))))))); + Matches = + match(traverse(TK_AsIs, findAll(callExpr(RefParameter))), *S, Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs("arg")); + Matches = match(traverse(TK_AsIs, findAll(cxxConstructExpr(RefParameter))), + *S, Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs("arg")); +} + +static void findAddressExpressionsPassedByConstPointer( + const Stmt *S, ASTContext &Context, + llvm::function_ref Handler) { + using namespace clang::ast_matchers; + auto ConstPtrParameter = forEachArgumentWithParam( + ignoringParenImpCasts(unaryOperator(hasOperatorName("&")).bind("arg")), + parmVarDecl(hasType( + qualType(pointerType(pointee(qualType(isConstQualified()))))))); + auto Matches = match(traverse(TK_AsIs, findAll(callExpr(ConstPtrParameter))), + *S, Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs("arg")); + Matches = + match(traverse(TK_AsIs, findAll(cxxConstructExpr(ConstPtrParameter))), *S, + Context); + for (const auto &Match : Matches) + Handler(Match.getNodeAs("arg")); +} + +static bool isImplicitInitializer(const VarDecl *VD) { + assert(VD->hasInit()); + const auto *E = VD->getInit(); + if (isa(E)) + return false; + const auto *Construct = dyn_cast(E); + if (!Construct) + return E->getBeginLoc() == VD->getLocation(); + return Construct->getParenOrBraceRange().isInvalid(); +} + +static const Expr *getInitializerExprWithLexicalRange(const Expr *E) { + if (const auto *EWC = dyn_cast(E)) { + if (const auto *Construct = dyn_cast(EWC->getSubExpr())) { + if (Construct->getNumArgs() == 1) { + if (const auto *ME = + dyn_cast(Construct->getArg(0))) + return ME; + } + } + } + return E; +} + +namespace { + +class ExtractedCodeVisitor : public RecursiveASTVisitor { + int DefineOrdering = 0; + +public: + struct CaptureInfo { + bool IsMutated = false; + bool IsDefined = false; + bool IsAddressTaken = false; + bool IsConstAddressTaken = false; + bool IsFieldCapturedWithThis = false; + bool IsUsed = false; + int DefineOrderingPriority = 0; + + bool isPassedByRefOrPtr() const { + return IsMutated || IsAddressTaken || IsConstAddressTaken; + } + bool isRefOrPtrConst() const { + return IsConstAddressTaken && !IsMutated && !IsAddressTaken; + } + }; + + const ImplicitParamDecl *SelfDecl; + + ExtractedCodeVisitor(const ImplicitParamDecl *SelfDecl) + : SelfDecl(SelfDecl) {} + + bool HasReturnInExtracted = false; + + CaptureInfo &captureVariable(const VarDecl *VD) { + CaptureInfo &Result = CapturedVariables[VD]; + Result.IsUsed = true; + return Result; + } + + CaptureInfo &captureField(const FieldDecl *FD) { return CapturedFields[FD]; } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + const VarDecl *VD = dyn_cast(E->getDecl()); + if (!VD) + return true; + if (VD == SelfDecl) { + CaptureSelf = true; + SelfType = VD->getType(); + return true; + } + if (!VD->isLocalVarDeclOrParm()) + return true; + captureVariable(VD); + return true; + } + + void captureThisWithoutConstConcerns(const CXXThisExpr *E) { + CaptureThis = true; + ThisRecordType = E->getType()->getPointeeType(); + } + + bool VisitCXXThisExpr(const CXXThisExpr *E) { + captureThisWithoutConstConcerns(E); + ThisUsesWithUnknownConstness.insert(E); + return true; + } + + bool TraverseMemberExpr(MemberExpr *E) { + const auto *Base = dyn_cast(E->getBase()->IgnoreParenCasts()); + if (!Base) + return RecursiveASTVisitor::TraverseMemberExpr(E); + const FieldDecl *FD = dyn_cast_or_null(E->getMemberDecl()); + if (!FD) + return RecursiveASTVisitor::TraverseMemberExpr(E); + CaptureInfo &Info = captureField(FD); + // Don't capture the implicit 'this' for private fields as we don't want to + // capture this if we only use the private fields. + if (FD->getAccess() == AS_public || !Base->isImplicit()) { + Info.IsFieldCapturedWithThis = true; + // The member might have an effect on the constness of the captured 'this' + // but this is checked via mutation/const tracking for the field itself, + // so we just capture 'this' without worrying about checking if it's used + // in a 'const' manner here. + captureThisWithoutConstConcerns(Base); + } + return true; + } + + void captureSuper(QualType T) { + if (CaptureSuper) + return; + SuperType = T; + CaptureSuper = true; + } + + bool TraverseObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { + if (E->isSuperReceiver()) + captureSuper(E->getSuperReceiverType()); + // Base might be an opaque expression, so we have to visit it manually as + // we don't necessarily visit the setter/getter message sends if just the + // property was selected. + if (E->isObjectReceiver()) { + if (const auto *OVE = dyn_cast(E->getBase())) + TraverseStmt(OVE->getSourceExpr()); + } + return RecursiveASTVisitor::TraverseObjCPropertyRefExpr(E); + } + + bool TraverseBinaryOperator(BinaryOperator *S) { + if (S->getOpcode() != BO_Assign) + return RecursiveASTVisitor::TraverseBinaryOperator(S); + // RHS might be an opaque expression, if this is a property assignment. We + // have to visit it manually as we don't necessarily visit the setter/getter + // message sends if just the property was selected. + if (const auto *OVE = dyn_cast(S->getRHS())) + TraverseStmt(OVE->getSourceExpr()); + return RecursiveASTVisitor::TraverseBinaryOperator(S); + } + + void findCapturedVariableOrFieldsInExpression( + const Expr *E, llvm::function_ref Handler) { + findEntitiesDirectlyReferencedInExpr( + E, [&Handler, this](const ReferencedEntity &Entity) { + if (const auto *DRE = Entity.dyn_cast()) { + const VarDecl *VD = dyn_cast(DRE->getDecl()); + if (!VD || !VD->isLocalVarDeclOrParm() || VD->isImplicit()) + return; + return Handler(captureVariable(VD)); + } + return Handler(captureField(cast(Entity))); + }); + } + + void + markDirectlyReferencedVariableOrFieldInExpressionAsMutated(const Expr *E) { + findCapturedVariableOrFieldsInExpression( + E, [](CaptureInfo &Capture) { Capture.IsMutated = true; }); + } + + bool VisitBinaryOperator(const BinaryOperator *E) { + if (E->isAssignmentOp()) + markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getLHS()); + return true; + } + + bool VisitUnaryOperator(const UnaryOperator *E) { + auto Op = E->getOpcode(); + if (Op != UO_PreInc && Op != UO_PostInc && Op != UO_PreDec && + Op != UO_PostDec) { + if (Op == UO_AddrOf) { + // Capture the entity with 'const' reference/pointer when its address is + // passed into a function that takes a 'const' pointer and no other + // mutations or non-const address/reference acquisitions occur. + if (AddressExpressionsPassedToConstPointerParameter.count(E)) + findCapturedVariableOrFieldsInExpression( + E->getSubExpr(), + [](CaptureInfo &Capture) { Capture.IsConstAddressTaken = true; }); + else + captureVariableOrFieldInExpressionByReference(E->getSubExpr()); + } + return true; + } + markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getSubExpr()); + return true; + } + + /// If the given expression refers to a local/instance variable or a + /// a member of such variable that variable is marked as captured by + /// reference. + void captureVariableOrFieldInExpressionByReference(const Expr *E) { + findCapturedVariableOrFieldsInExpression( + E, [](CaptureInfo &Capture) { Capture.IsAddressTaken = true; }); + } + + bool VisitObjCMessageExpr(const ObjCMessageExpr *E) { + if (E->getSuperLoc().isValid()) + captureSuper(E->getSuperType()); + const ObjCMethodDecl *MD = E->getMethodDecl(); + if (!MD) + return true; + for (const auto &Param : llvm::enumerate(MD->parameters())) { + QualType T = Param.value()->getType(); + if (Param.index() >= E->getNumArgs()) + break; + if (T->isReferenceType() && !T->getPointeeType().isConstQualified()) + captureVariableOrFieldInExpressionByReference(E->getArg(Param.index())); + if (T->isPointerType() && T->getPointeeType().isConstQualified()) { + // Check if this is an '&' passed into a const pointer parameter. + const Expr *Arg = E->getArg(Param.index()); + if (const auto *Op = + dyn_cast(Arg->IgnoreParenImpCasts())) { + if (Op->getOpcode() == UO_AddrOf) + AddressExpressionsPassedToConstPointerParameter.insert(Op); + } + } + } + return true; + } + + bool VisitVarDecl(const VarDecl *VD) { + // Don't capture using the captureVariable method as we don't want to mark + // the declaration as a 'use'. This allows us to avoid passing in variables + // that are defined in extracted code, used afterwards, but never actually + // used in the extracted code. + CaptureInfo &Capture = CapturedVariables[VD]; + Capture.IsDefined = true; + Capture.DefineOrderingPriority = ++DefineOrdering; + // Ensure the capture is marked as 'used' when the variable declaration has + // an explicit initialization expression. This allows us to pass it by + // reference when it's defined in extracted code, used afterwards, but never + // actually used in the extracted code. The main reason why we want to try + // to keep this initialization in the extracted code is to preserve + // semantics as the initialization expression might have side-effects. + if (!Capture.IsUsed && VD->hasInit() && !isImplicitInitializer(VD)) + Capture.IsUsed = true; + QualType T = VD->getType(); + if (T->isReferenceType() && !T->getPointeeType().isConstQualified() && + VD->hasInit()) + captureVariableOrFieldInExpressionByReference(VD->getInit()); + return true; + } + + bool VisitReturnStmt(const ReturnStmt *S) { + HasReturnInExtracted = true; + return true; + } + + void InspectExtractedStmt(Stmt *S, ASTContext &Context) { + findAddressExpressionsPassedByConstPointer( + S, Context, [this](const UnaryOperator *Arg) { + AddressExpressionsPassedToConstPointerParameter.insert(Arg); + }); + TraverseStmt(S); + findArgumentsPassedByNonConstReference(S, Context, [this](const Expr *Arg) { + captureVariableOrFieldInExpressionByReference(Arg); + }); + if (CaptureThis && !ThisUsesWithUnknownConstness.empty()) { + // Compare the definite 'const' uses of 'this' to all the seen uses + // (except for the known field uses). + findUseOfConstThis(S, Context, [this](const CXXThisExpr *Arg) { + ThisUsesWithUnknownConstness.erase(Arg); + }); + IsThisConstForNonCapturedFieldUses = ThisUsesWithUnknownConstness.empty(); + } + } + + llvm::DenseMap CapturedVariables; + llvm::DenseMap CapturedFields; + llvm::SmallPtrSet + AddressExpressionsPassedToConstPointerParameter; + llvm::SmallPtrSet ThisUsesWithUnknownConstness; + bool CaptureThis = false; + bool IsThisConstForNonCapturedFieldUses = true; + QualType ThisRecordType; + bool CaptureSelf = false, CaptureSuper = false; + QualType SelfType, SuperType; +}; + +/// Traverses the extracted code and finds the uses of captured variables +/// that are passed into the extracted function using a pointer. +class VariableDefinedInExtractedCodeUseAfterExtractionFinder + : public RecursiveASTVisitor< + VariableDefinedInExtractedCodeUseAfterExtractionFinder> { + bool IsAfterExtracted = false; + +public: + const Stmt *LastExtractedStmt; + const llvm::SmallPtrSetImpl &VariablesDefinedInExtractedCode; + llvm::SmallPtrSet VariablesUsedAfterExtraction; + + VariableDefinedInExtractedCodeUseAfterExtractionFinder( + const Stmt *LastExtractedStmt, + const llvm::SmallPtrSetImpl + &VariablesDefinedInExtractedCode) + : LastExtractedStmt(LastExtractedStmt), + VariablesDefinedInExtractedCode(VariablesDefinedInExtractedCode) {} + + bool TraverseStmt(Stmt *S) { + RecursiveASTVisitor::TraverseStmt(S); + if (S == LastExtractedStmt) + IsAfterExtracted = true; + return true; + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + if (!IsAfterExtracted) + return true; + const VarDecl *VD = dyn_cast(E->getDecl()); + if (!VD) + return true; + if (VariablesDefinedInExtractedCode.count(VD)) + VariablesUsedAfterExtraction.insert(VD); + return true; + } +}; + +class PossibleShadowingVariableFinder + : public RecursiveASTVisitor { + const VarDecl *TargetVD; + + PossibleShadowingVariableFinder(const VarDecl *TargetVD) + : TargetVD(TargetVD) {} + +public: + bool VisitVarDecl(const VarDecl *VD) { + if (VD == TargetVD || VD->getName() != TargetVD->getName()) + return true; + return false; + } + + /// Returns true if the given statement \p S has a variable declaration whose + /// name is identical to the given variable declaration \p VD. + static bool hasShadowingVar(const VarDecl *VD, const Stmt *S) { + return !PossibleShadowingVariableFinder(VD).TraverseStmt( + const_cast(S)); + } +}; + +/// Traverses the extracted code and rewrites the 'return' statements to ensure +/// that they now return some value. +class ReturnRewriter : public RecursiveASTVisitor { + Rewriter &SourceRewriter; + std::string Text; + +public: + ReturnRewriter(Rewriter &SourceRewriter, StringRef Text) + : SourceRewriter(SourceRewriter), Text(std::string(" ") + Text.str()) {} + + bool VisitReturnStmt(const ReturnStmt *S) { + SourceRewriter.InsertText( + getPreciseTokenLocEnd(S->getEndLoc(), SourceRewriter.getSourceMgr(), + SourceRewriter.getLangOpts()), + Text); + return true; + } +}; + +/// Prints the given initializer expression using the original source code if +/// possible. +static void printInitializerExpressionUsingOriginalSyntax( + const VarDecl *VD, const Expr *E, bool IsDeclaration, const ASTContext &Ctx, + llvm::raw_ostream &OS, const PrintingPolicy &PP) { + E = getInitializerExprWithLexicalRange(E); + SourceRange Range = E->getSourceRange(); + bool UseEquals = true; + bool UseTypeName = false; + if (const auto *Construct = dyn_cast(E)) { + SourceRange SubRange = Construct->getParenOrBraceRange(); + if (SubRange.isValid()) { + UseEquals = false; + UseTypeName = true; + Range = SubRange; + } + } + if (Range.getBegin().isMacroID()) + Range.setBegin(Ctx.getSourceManager().getExpansionLoc(Range.getBegin())); + if (Range.getEnd().isMacroID()) + Range.setEnd(Ctx.getSourceManager().getExpansionLoc(Range.getEnd())); + bool IsInvalid = false; + StringRef Text = Lexer::getSourceText(CharSourceRange::getTokenRange(Range), + Ctx.getSourceManager(), + Ctx.getLangOpts(), &IsInvalid); + if (IsDeclaration && UseEquals) + OS << " = "; + else if (!IsDeclaration && UseTypeName) + VD->getType().print(OS, PP); + if (IsInvalid) + E->printPretty(OS, nullptr, PP); + else + OS << Text; +} + +/// Traverses the extracted code and rewrites the declaration statements that +/// declare variables that are used after the extracted code. +class DefinedInExtractedCodeDeclStmtRewriter + : public RecursiveASTVisitor { +public: + Rewriter &SourceRewriter; + const llvm::SmallPtrSetImpl &VariablesUsedAfterExtraction; + const PrintingPolicy &PP; + + DefinedInExtractedCodeDeclStmtRewriter( + Rewriter &SourceRewriter, const llvm::SmallPtrSetImpl + &VariablesUsedAfterExtraction, + const PrintingPolicy &PP) + : SourceRewriter(SourceRewriter), + VariablesUsedAfterExtraction(VariablesUsedAfterExtraction), PP(PP) {} + + /// When a declaration statement declares variables that are all used + /// after extraction, we can rewrite it completely into a set of assignments + /// while still preserving the original initializer expressions when we + /// can. + void rewriteAllVariableDeclarationsToAssignments(const DeclStmt *S) { + SourceLocation StartLoc = S->getBeginLoc(); + for (const Decl *D : S->decls()) { + const auto *VD = dyn_cast(D); + if (!VD || !VariablesUsedAfterExtraction.count(VD)) + continue; + if (!VD->hasInit() || isImplicitInitializer(VD)) { + // Remove the variable declarations without explicit initializers. + // This can affect the semantics of the program if the implicit + // initialization expression has side effects. + SourceRange Range = SourceRange( + StartLoc, S->isSingleDecl() ? S->getEndLoc() : VD->getLocation()); + SourceRewriter.RemoveText(Range); + continue; + } + std::string Str; + llvm::raw_string_ostream OS(Str); + if (StartLoc != S->getBeginLoc()) + OS << "; "; + const ASTContext &Ctx = D->getASTContext(); + // Dereference the variable unless the source uses C++. + if (!Ctx.getLangOpts().CPlusPlus) + OS << '*'; + OS << VD->getName() << " = "; + const Expr *Init = getInitializerExprWithLexicalRange(VD->getInit()); + SourceLocation End = Init->getBeginLoc(); + if (const auto *Construct = dyn_cast(Init)) { + SourceRange SubRange = Construct->getParenOrBraceRange(); + if (SubRange.isValid()) { + End = SubRange.getBegin(); + VD->getType().print(OS, PP); + } + } + if (End.isMacroID()) + End = Ctx.getSourceManager().getExpansionLoc(End); + auto Range = CharSourceRange::getCharRange(StartLoc, End); + SourceRewriter.ReplaceText(StartLoc, SourceRewriter.getRangeSize(Range), + OS.str()); + StartLoc = getPreciseTokenLocEnd(D->getEndLoc(), Ctx.getSourceManager(), + Ctx.getLangOpts()); + } + } + + /// When a declaration statement has variables that are both used after + /// extraction and not used after extraction, we create new declaration + /// statements that declare the unused variables, while creating assignment + /// statements that "initialize" the variables that are used after the + /// extraction. This way we can preserve the order of + /// initialization/assignment from the original declaration statement. + void rewriteMixedDeclarations(const DeclStmt *S) { + // Completely rewrite the declaration statement. + std::string Str; + llvm::raw_string_ostream OS(Str); + for (const Decl *D : S->decls()) { + const ASTContext &Ctx = D->getASTContext(); + const VarDecl *VD = dyn_cast(D); + bool IsLast = D == S->decl_end()[-1]; + if (!VD) { + OS << "<>;"; + continue; + } + + auto PrintInit = [&](bool IsDeclaration) { + printInitializerExpressionUsingOriginalSyntax( + VD, VD->getInit(), IsDeclaration, Ctx, OS, PP); + }; + if (!VariablesUsedAfterExtraction.count(VD)) { + VD->getType().print(OS, PP); + OS << " " << VD->getName(); + if (VD->hasInit() && !isImplicitInitializer(VD)) + PrintInit(/*IsDeclaration=*/true); + OS << ";"; + if (!IsLast) + OS << ' '; + continue; + } + if (VD->hasInit() && !isImplicitInitializer(VD)) { + // Dereference the variable unless the source uses C++. + if (!Ctx.getLangOpts().CPlusPlus) + OS << '*'; + OS << VD->getName() << " = "; + PrintInit(/*IsDeclaration=*/false); + OS << ";"; + if (!IsLast) + OS << ' '; + } + } + SourceRewriter.ReplaceText(S->getSourceRange(), OS.str()); + } + + bool VisitDeclStmt(const DeclStmt *S) { + bool AreAllUsed = true; + bool AreNoneUsed = true; + for (const Decl *D : S->decls()) { + const auto *VD = dyn_cast(D); + if (!VD || !VariablesUsedAfterExtraction.count(VD)) { + AreAllUsed = false; + continue; + } + AreNoneUsed = false; + // Exit early when both flags were set in the loop. + if (!AreAllUsed) + break; + } + if (AreNoneUsed) + return true; + + if (AreAllUsed) + rewriteAllVariableDeclarationsToAssignments(S); + else + rewriteMixedDeclarations(S); + return true; + } +}; + +/// Takes care of pseudo object expressions and Objective-C properties to avoid +/// duplicate rewrites and missing rewrites. +template +class PseudoObjectRewriter : public RecursiveASTVisitor { + typedef RecursiveASTVisitor Base; + +public: + bool TraversePseudoObjectExpr(PseudoObjectExpr *E) { + return Base::TraverseStmt(E->getSyntacticForm()); + } + + bool TraverseObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { + // Base might be an opaque expression, so we have to visit it manually as + // we don't necessarily visit the setter/getter message sends if just the + // property was selected. + if (E->isObjectReceiver()) { + if (const auto *OVE = dyn_cast(E->getBase())) + Base::TraverseStmt(OVE->getSourceExpr()); + } + return Base::TraverseObjCPropertyRefExpr(E); + } + + bool TraverseBinaryOperator(BinaryOperator *S) { + if (S->getOpcode() != BO_Assign) + return Base::TraverseBinaryOperator(S); + // RHS might be an opaque expression, if this is a property assignment. We + // have to visit it manually as we don't necessarily visit the setter/getter + // message sends if just the property was selected. + if (const auto *OVE = dyn_cast(S->getRHS())) + Base::TraverseStmt(OVE->getSourceExpr()); + return Base::TraverseBinaryOperator(S); + } +}; + +/// Traverses the extracted code and rewrites the uses of captured variables +/// that are passed into the extracted function using a pointer. +class CapturedVariableCaptureByPointerRewriter + : public PseudoObjectRewriter { +public: + const VarDecl *TargetVD; + Rewriter &SourceRewriter; + + CapturedVariableCaptureByPointerRewriter(const VarDecl *VD, + Rewriter &SourceRewriter) + : TargetVD(VD), SourceRewriter(SourceRewriter) {} + + bool isTargetDeclRefExpr(const Expr *E) { + const auto *DRE = dyn_cast(E); + if (!DRE) + return false; + return dyn_cast(DRE->getDecl()) == TargetVD; + } + + void dereferenceTargetVar(const Expr *E, bool WrapInParens = false) { + SourceRewriter.InsertTextBefore(E->getBeginLoc(), + WrapInParens ? "(*" : "*"); + if (WrapInParens) + SourceRewriter.InsertTextAfterToken(E->getEndLoc(), ")"); + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + const VarDecl *VD = dyn_cast(E->getDecl()); + if (VD != TargetVD) + return true; + dereferenceTargetVar(E); + return true; + } + + bool TraverseUnaryOperator(UnaryOperator *E) { + if (E->getOpcode() != UO_AddrOf) + return RecursiveASTVisitor::TraverseUnaryOperator(E); + if (const auto *DRE = + dyn_cast(E->getSubExpr()->IgnoreParenCasts())) { + const VarDecl *VD = dyn_cast(DRE->getDecl()); + if (VD == TargetVD) { + // Remove the '&' as the variable is now a pointer. + SourceRewriter.RemoveText( + CharSourceRange::getTokenRange(E->getBeginLoc(), E->getBeginLoc())); + return true; + } + } + return RecursiveASTVisitor::TraverseUnaryOperator(E); + } + + bool TraverseMemberExpr(MemberExpr *E) { + if (!E->isArrow()) { + if (const auto *DRE = + dyn_cast(E->getBase()->IgnoreParenCasts())) { + const VarDecl *VD = dyn_cast(DRE->getDecl()); + if (VD == TargetVD) { + // Replace '.' with '->'. + SourceRewriter.ReplaceText(E->getOperatorLoc(), 1, "->"); + return true; + } + } + } else if (isTargetDeclRefExpr(E->getBase()->IgnoreImpCasts())) { + // Ensure the variable is wrapped in parenthesis when it's the base of + // '->' operator. + dereferenceTargetVar(E->getBase(), /*WrapInParens=*/true); + return true; + } + return RecursiveASTVisitor::TraverseMemberExpr(E); + } +}; + +/// Traverses the extracted code and rewrites the uses of 'this' that can be +/// rewritten as references. +class CapturedThisReferenceRewriter + : public PseudoObjectRewriter { +public: + Rewriter &SourceRewriter; + llvm::SmallPtrSet RewrittenExpressions; + + CapturedThisReferenceRewriter(Rewriter &SourceRewriter) + : SourceRewriter(SourceRewriter) {} + + void rewriteThis(const CXXThisExpr *E) { + RewrittenExpressions.insert(E); + if (!E->isImplicit()) + SourceRewriter.ReplaceText(E->getBeginLoc(), 4, "object"); + else + SourceRewriter.InsertText(E->getBeginLoc(), "object"); + } + + bool VisitMemberExpr(const MemberExpr *E) { + const auto *This = + dyn_cast(E->getBase()->IgnoreParenImpCasts()); + if (This) { + rewriteThis(This); + if (!This->isImplicit() && E->isArrow()) + SourceRewriter.ReplaceText(E->getOperatorLoc(), 2, "."); + else + SourceRewriter.InsertText(E->getBase()->getEndLoc(), "."); + } + return true; + } +}; + +/// Traverses the extracted code and rewrites the uses of 'this' into '&object'. +class CapturedThisPointerRewriter + : public PseudoObjectRewriter { +public: + Rewriter &SourceRewriter; + const llvm::SmallPtrSetImpl &RewrittenExpressions; + + CapturedThisPointerRewriter( + Rewriter &SourceRewriter, + const llvm::SmallPtrSetImpl &RewrittenExpressions) + : SourceRewriter(SourceRewriter), + RewrittenExpressions(RewrittenExpressions) {} + + void replace(const CXXThisExpr *E, StringRef Text) { + SourceRewriter.ReplaceText(E->getBeginLoc(), 4, Text); + } + + bool VisitCXXThisExpr(const CXXThisExpr *E) { + if (RewrittenExpressions.count(E)) + return true; + if (!E->isImplicit()) + replace(E, "&object"); + return true; + } + + bool TraverseUnaryOperator(UnaryOperator *E) { + if (E->getOpcode() != UO_Deref) + return RecursiveASTVisitor::TraverseUnaryOperator(E); + if (const auto *This = + dyn_cast(E->getSubExpr()->IgnoreParenImpCasts())) { + if (!This->isImplicit()) { + // Remove the '*' as the variable is now a reference. + SourceRewriter.RemoveText( + CharSourceRange::getTokenRange(E->getBeginLoc(), E->getBeginLoc())); + replace(This, "object"); + return true; + } + } + return RecursiveASTVisitor::TraverseUnaryOperator(E); + } +}; + +/// Traverses the extracted code and rewrites the uses of 'self' into 'object'. +class CapturedSelfRewriter : public PseudoObjectRewriter { +public: + Rewriter &SourceRewriter; + const ImplicitParamDecl *SelfDecl; + + CapturedSelfRewriter(Rewriter &SourceRewriter, + const ImplicitParamDecl *SelfDecl) + : SourceRewriter(SourceRewriter), SelfDecl(SelfDecl) { + assert(SelfDecl); + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + const VarDecl *VD = dyn_cast(E->getDecl()); + if (!VD || VD != SelfDecl) + return true; + if (E->getBeginLoc().isInvalid()) + return true; + SourceRewriter.ReplaceText(E->getBeginLoc(), 4, "object"); + return true; + } + + void insertObjectForImplicitSelf(const Expr *E, SourceLocation Loc, + StringRef Text) { + const auto *DRE = dyn_cast(E); + if (!DRE) + return; + const VarDecl *VD = dyn_cast(DRE->getDecl()); + if (!VD || VD != SelfDecl || DRE->getBeginLoc().isValid()) + return; + SourceRewriter.InsertText(Loc, Text); + } + + bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *E) { + insertObjectForImplicitSelf(E->getBase()->IgnoreImpCasts(), + E->getBeginLoc(), "object->"); + return true; + } +}; + +/// Traverses the extracted code and rewrites the uses of 'self' into the name +/// of the class. +class CapturedClassSelfRewriter + : public PseudoObjectRewriter { +public: + Rewriter &SourceRewriter; + StringRef ClassName; + const ImplicitParamDecl *SelfDecl; + + CapturedClassSelfRewriter(Rewriter &SourceRewriter, StringRef ClassName, + const ImplicitParamDecl *SelfDecl) + : SourceRewriter(SourceRewriter), ClassName(ClassName), + SelfDecl(SelfDecl) { + + assert(SelfDecl); + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + const VarDecl *VD = dyn_cast(E->getDecl()); + if (!VD || VD != SelfDecl || E->getBeginLoc().isInvalid()) + return true; + SourceRewriter.ReplaceText(E->getBeginLoc(), 4, ClassName); + return true; + } +}; + +/// Traverses the extracted code and rewrites the uses of 'super' into +/// 'superObject' or the name of the super class. +class CapturedSuperRewriter + : public PseudoObjectRewriter { +public: + Rewriter &SourceRewriter; + StringRef ReplacementString; + + CapturedSuperRewriter(Rewriter &SourceRewriter, StringRef ReplacementString) + : SourceRewriter(SourceRewriter), ReplacementString(ReplacementString) {} + + void rewriteSuper(SourceLocation Loc) { + SourceRewriter.ReplaceText(Loc, strlen("super"), ReplacementString); + } + + bool VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *E) { + if (E->isSuperReceiver()) + rewriteSuper(E->getReceiverLocation()); + return true; + } + + bool VisitObjCMessageExpr(const ObjCMessageExpr *E) { + if (E->getSuperLoc().isValid()) + rewriteSuper(E->getSuperLoc()); + return true; + } +}; + +struct ExtractionSemicolonPolicy { + bool IsNeededInExtractedFunction; + bool IsNeededInOriginalFunction; + + static ExtractionSemicolonPolicy neededInExtractedFunction() { + return {true, false}; + } + static ExtractionSemicolonPolicy neededInOriginalFunction() { + return {false, true}; + } + static ExtractionSemicolonPolicy neededInBoth() { return {true, true}; } +}; + +} // end anonymous namespace + +ExtractionSemicolonPolicy +computeSemicolonExtractionPolicy(const Stmt *S, SourceRange &ExtractedRange, + const SourceManager &SM, + const LangOptions &LangOpts) { + if (isa(S)) + return ExtractionSemicolonPolicy::neededInExtractedFunction(); + bool NeedsSemi = isSemicolonRequiredAfter(S); + if (!NeedsSemi) + return ExtractionSemicolonPolicy::neededInOriginalFunction(); + SourceLocation End = ExtractedRange.getEnd(); + if (isSemicolonAtLocation(End, SM, LangOpts)) + return ExtractionSemicolonPolicy::neededInOriginalFunction(); + SourceLocation NextTokenLoc = + Lexer::findNextTokenLocationAfterTokenAt(End, SM, LangOpts); + if (NextTokenLoc.isValid() && + isSemicolonAtLocation(NextTokenLoc, SM, LangOpts) && + areOnSameLine(NextTokenLoc, End, SM)) { + ExtractedRange.setEnd(NextTokenLoc); + return ExtractionSemicolonPolicy::neededInOriginalFunction(); + } + return ExtractionSemicolonPolicy::neededInBoth(); +} + +PrintingPolicy getPrintingPolicy(const ASTContext &Context, + const Preprocessor &PP) { + PrintingPolicy Policy = Context.getPrintingPolicy(); + // Our printing policy is copied over the ASTContext printing policy whenever + // a diagnostic is emitted, so recompute it. + Policy.Bool = Context.getLangOpts().Bool; + // FIXME: This is duplicated with Sema.cpp. When upstreaming this should be + // cleaned up. + if (!Policy.Bool) { + if (const MacroInfo *BoolMacro = PP.getMacroInfo(Context.getBoolName())) { + Policy.Bool = BoolMacro->isObjectLike() && + BoolMacro->getNumTokens() == 1 && + BoolMacro->getReplacementToken(0).is(tok::kw__Bool); + } + } + return Policy; +} + +static QualType getFunctionLikeParentDeclReturnType(const Decl *D) { + // FIXME: might need to handle ObjC blocks in the future. + if (const auto *M = dyn_cast(D)) + return M->getReturnType(); + return cast(D)->getReturnType(); +} + +static const Stmt *getEnclosingDeclBody(const Decl *D) { + // FIXME: might need to handle ObjC blocks in the future. + if (const auto *M = dyn_cast(D)) + return M->getBody(); + return cast(D)->getBody(); +} + +static bool isEnclosingMethodConst(const Decl *D) { + if (const auto *MD = dyn_cast(D)) + return MD->isConst(); + return false; +} + +static bool isEnclosingMethodStatic(const Decl *D) { + if (const auto *MD = dyn_cast(D)) + return MD->isStatic(); + return false; +} + +static bool isEnclosingMethodOutOfLine(const Decl *D) { + const auto *MD = dyn_cast(D); + if (!MD) + return false; + return MD->isOutOfLine(); +} + +static void printEnclosingMethodScope(const Decl *D, llvm::raw_ostream &OS, + const PrintingPolicy &PP) { + const auto *MD = dyn_cast(D); + if (!MD) + return; + if (!MD->isOutOfLine() || !MD->getQualifier()) + return; + MD->getQualifier()->print(OS, PP); +} + +static SourceLocation +computeFunctionExtractionLocation(const Decl *D, bool IsMethodExtraction) { + if (!IsMethodExtraction && isa(D)) { + // Code from methods that defined in class bodies should be extracted to a + // function defined just before the class. + while (const auto *RD = dyn_cast(D->getLexicalDeclContext())) + D = RD; + } + return D->getBeginLoc(); +} + +namespace { +enum class MethodDeclarationPlacement { After, Before }; + +/// \brief Represents an entity captured from the original function that's +/// passed into the new function/method. +struct CapturedVariable { + const VarDecl *VD; + const FieldDecl *FD; + QualType ThisType; + bool PassByRefOrPtr; + bool IsRefOrPtrConst; + bool IsThisSelf = false; + bool IsThisSuper = false; + bool TakeAddress = false; + QualType ParameterType; + + CapturedVariable(const VarDecl *VD, bool PassByRefOrPtr, bool IsRefOrPtrConst) + : VD(VD), FD(nullptr), PassByRefOrPtr(PassByRefOrPtr), + IsRefOrPtrConst(IsRefOrPtrConst) {} + CapturedVariable(const FieldDecl *FD, bool PassByRefOrPtr, + bool IsRefOrPtrConst) + : VD(nullptr), FD(FD), PassByRefOrPtr(PassByRefOrPtr), + IsRefOrPtrConst(IsRefOrPtrConst) {} + CapturedVariable(QualType ThisType, bool PassByRefOrPtr, bool IsConst) + : VD(nullptr), FD(nullptr), ThisType(ThisType), + PassByRefOrPtr(PassByRefOrPtr), IsRefOrPtrConst(IsConst) {} + + static CapturedVariable getThis(QualType T, bool IsConst) { + return CapturedVariable(T, /*PassByRefOrPtr=*/true, /*IsConst*/ IsConst); + } + + static CapturedVariable getSelf(QualType T) { + auto Result = + CapturedVariable(T, /*PassByRefOrPtr=*/false, /*IsConst*/ false); + Result.IsThisSelf = true; + return Result; + } + + static CapturedVariable getSuper(QualType T) { + auto Result = + CapturedVariable(T, /*PassByRefOrPtr=*/false, /*IsConst*/ false); + Result.IsThisSuper = true; + return Result; + } + + StringRef getName() const { + return VD ? VD->getName() + : FD ? FD->getName() : IsThisSuper ? "superObject" : "object"; + } + StringRef getExpr() const { + return ThisType.isNull() + ? getName() + : IsThisSelf ? "self" : IsThisSuper ? "super.self" : "*this"; + } + QualType getType() const { + return VD ? VD->getType() : FD ? FD->getType() : ThisType; + } +}; +} // end anonymous namespace + +static std::pair +computeAppropriateExtractionLocationForMethodDeclaration( + const CXXMethodDecl *D) { + const CXXRecordDecl *RD = D->getParent(); + // Try to put the new declaration after the last method, or just before the + // end of the class. + SourceLocation Loc; + for (const CXXMethodDecl *M : RD->methods()) { + if (M->isImplicit()) + continue; + Loc = M->getEndLoc(); + } + return Loc.isValid() ? std::make_pair(Loc, MethodDeclarationPlacement::After) + : std::make_pair(RD->getEndLoc(), + MethodDeclarationPlacement::Before); +} + +static bool isInHeader(SourceLocation Loc, const SourceManager &SM) { + // Base the header decision on the filename. + StringRef Extension = llvm::sys::path::extension(SM.getFilename(Loc)); + if (Extension.empty()) + return false; + return llvm::StringSwitch(Extension.drop_front()) + .Case("h", true) + .Case("hpp", true) + .Case("hh", true) + .Case("h++", true) + .Case("hxx", true) + .Case("inl", true) + .Case("def", true) + .Default(false); +} + +llvm::Expected +ExtractOperation::performExpressionExtraction(ASTContext &Context, + PrintingPolicy &PP) { + assert(isExpressionExtraction() && "Not an expression extraction"); + std::vector Replacements; + const Expr *E = cast(S); + QualType VarType = findExpressionLexicalType(FunctionLikeParentDecl, E, + E->getType(), PP, Context); + StringRef VarName = "extractedExpr"; + auto CreatedSymbol = std::make_unique( + SymbolName(VarName, /*IsObjectiveCSelector=*/false)); + + SourceRange ExtractedTokenRange = CandidateExtractionInfo[0].Range; + SourceRange ExtractedCharRange = SourceRange( + ExtractedTokenRange.getBegin(), + getPreciseTokenLocEnd(ExtractedTokenRange.getEnd(), + Context.getSourceManager(), Context.getLangOpts())); + + // Create the variable that will hold the value of the duplicate expression. + std::string VariableDeclarationString; + llvm::raw_string_ostream OS(VariableDeclarationString); + VarType.print(OS, PP, /*PlaceHolder*/ VarName); + // FIXME: We should hook into the TypePrinter when moving over to llvm.org + // instead and get the offset from it. + unsigned NameOffset = StringRef(OS.str()).find(VarName); + OS << " = "; + OS << Lexer::getSourceText(CharSourceRange::getCharRange(ExtractedCharRange), + Context.getSourceManager(), Context.getLangOpts()); + OS << ";\n"; + + // Variable declaration. + SourceLocation InsertionLoc = + extract::locationForExtractedVariableDeclaration( + E, FunctionLikeParentDecl, Context.getSourceManager()); + Replacements.push_back(RefactoringReplacement( + SourceRange(InsertionLoc, InsertionLoc), OS.str(), CreatedSymbol.get(), + RefactoringReplacement::AssociatedSymbolLocation( + ArrayRef(NameOffset), /*IsDeclaration=*/true))); + // Replace the expression with the variable. + Replacements.push_back( + RefactoringReplacement(ExtractedCharRange, VarName, CreatedSymbol.get(), + /*NameOffset=*/ArrayRef(unsigned(0)))); + + RefactoringResult Result(std::move(Replacements)); + Result.AssociatedSymbols.push_back(std::move(CreatedSymbol)); + return std::move(Result); +} + +llvm::Expected ExtractOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + std::vector Replacements; + SourceManager &SM = Context.getSourceManager(); + const LangOptions &LangOpts = Context.getLangOpts(); + Rewriter SourceRewriter(SM, LangOpts); + PrintingPolicy PP = getPrintingPolicy(Context, ThePreprocessor); + PP.UseStdFunctionForLambda = true; + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + + if (isExpressionExtraction()) + return performExpressionExtraction(Context, PP); + + const Stmt *S = + CandidateExtractionInfo[SelectedCandidateIndex].AnalyzedStatement + ? CandidateExtractionInfo[SelectedCandidateIndex].AnalyzedStatement + : this->S; + + const auto *EnclosingObjCMethod = + dyn_cast(FunctionLikeParentDecl); + + // Find the variables that are captured by the extracted code. + ExtractedCodeVisitor Visitor(/*SelfDecl=*/EnclosingObjCMethod + ? EnclosingObjCMethod->getSelfDecl() + : nullptr); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + Visitor.InspectExtractedStmt(const_cast(S), Context); + } else + Visitor.InspectExtractedStmt(const_cast(S), Context); + // Compute the return type. + bool IsExpr = isLexicalExpression(S, ParentStmt); + QualType ReturnType; + if (IsExpr || Visitor.HasReturnInExtracted) { + if (const auto *E = dyn_cast(S)) { + assert(!ExtractedStmtRange); + ReturnType = findExpressionLexicalType(FunctionLikeParentDecl, E, + E->getType(), PP, Context); + } else + ReturnType = getFunctionLikeParentDeclReturnType(FunctionLikeParentDecl); + } else + ReturnType = Context.VoidTy; + // Sort the captured variables. + std::vector CapturedVariables; + llvm::SmallPtrSet VariablesDefinedInExtractedCode; + CapturedVariables.reserve(Visitor.CapturedVariables.size() + + Visitor.CapturedFields.size()); + for (const auto &I : Visitor.CapturedVariables) { + if (I.getSecond().IsDefined) { + VariablesDefinedInExtractedCode.insert(I.getFirst()); + continue; + } + CapturedVariables.push_back( + CapturedVariable(I.getFirst(), I.getSecond().isPassedByRefOrPtr(), + I.getSecond().isRefOrPtrConst())); + } + // Take a look at the variables that are defined in the extracted code. + VariableDefinedInExtractedCodeUseAfterExtractionFinder + UsedAfterExtractionFinder(ExtractedStmtRange ? *ExtractedStmtRange->Last + : S, + VariablesDefinedInExtractedCode); + UsedAfterExtractionFinder.TraverseStmt( + const_cast(getEnclosingDeclBody(FunctionLikeParentDecl))); + struct RedeclaredVariable { + const VarDecl *VD; + int OrderingPriority; + }; + llvm::SmallVector RedeclaredVariables; + bool CanUseReturnForVariablesUsedAfterwards = + !isa(S) && ReturnType->isVoidType() && + UsedAfterExtractionFinder.VariablesUsedAfterExtraction.size() == 1; + if (CanUseReturnForVariablesUsedAfterwards) { + // Avoid using the return value for the variable that's used afterwards as + // another variable might shadow it at the point of a 'return' that we + // have to rewrite to 'return var'. + const VarDecl *VD = + *UsedAfterExtractionFinder.VariablesUsedAfterExtraction.begin(); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) { + if (PossibleShadowingVariableFinder::hasShadowingVar(VD, S)) { + CanUseReturnForVariablesUsedAfterwards = false; + break; + } + } + } else + CanUseReturnForVariablesUsedAfterwards = + !PossibleShadowingVariableFinder::hasShadowingVar(VD, S); + } + if (CanUseReturnForVariablesUsedAfterwards) { + for (const auto &I : Visitor.CapturedVariables) { + if (!I.getSecond().IsDefined || + !UsedAfterExtractionFinder.VariablesUsedAfterExtraction.count( + I.getFirst())) + continue; + RedeclaredVariables.push_back( + {I.getFirst(), I.getSecond().DefineOrderingPriority}); + ReturnType = I.getFirst()->getType(); + // Const qualifier can be dropped as we don't want to declare the return + // type as 'const'. + if (ReturnType.isConstQualified()) + ReturnType.removeLocalConst(); + break; + } + if (Visitor.HasReturnInExtracted) { + ReturnRewriter ReturnsRewriter(SourceRewriter, + RedeclaredVariables.front().VD->getName()); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + ReturnsRewriter.TraverseStmt(const_cast(S)); + } else + ReturnsRewriter.TraverseStmt(const_cast(S)); + } + } else { + for (const auto &I : Visitor.CapturedVariables) { + if (!I.getSecond().IsDefined || + !UsedAfterExtractionFinder.VariablesUsedAfterExtraction.count( + I.getFirst())) + continue; + RedeclaredVariables.push_back( + {I.getFirst(), I.getSecond().DefineOrderingPriority}); + if (!I.getSecond().IsUsed) + continue; + // Pass the variable that's defined in the extracted code but used + // afterwards as a parameter only when it's actually used in the extracted + // code. + CapturedVariables.push_back(CapturedVariable(I.getFirst(), + /*PassByRefOrPtr=*/true, + /*IsRefOrPtrConst=*/false)); + } + std::sort(RedeclaredVariables.begin(), RedeclaredVariables.end(), + [](const RedeclaredVariable &X, const RedeclaredVariable &Y) { + return X.OrderingPriority < Y.OrderingPriority; + }); + DefinedInExtractedCodeDeclStmtRewriter DeclRewriter( + SourceRewriter, UsedAfterExtractionFinder.VariablesUsedAfterExtraction, + PP); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + DeclRewriter.TraverseStmt(const_cast(S)); + } else + DeclRewriter.TraverseStmt(const_cast(S)); + } + // Capture any fields if necessary. + bool IsThisConstInCapturedFieldUses = true; + if (!isMethodExtraction()) { + for (const auto &I : Visitor.CapturedFields) { + if (I.getSecond().isPassedByRefOrPtr() && + !I.getSecond().isRefOrPtrConst()) + IsThisConstInCapturedFieldUses = false; + // Private fields that use explicit 'this' should be captured using 'this' + // even if they might end up being inaccessible in the extracted function. + if (I.getSecond().IsFieldCapturedWithThis) + continue; + CapturedVariables.push_back( + CapturedVariable(I.getFirst(), I.getSecond().isPassedByRefOrPtr(), + I.getSecond().isRefOrPtrConst())); + } + } + std::sort(CapturedVariables.begin(), CapturedVariables.end(), + [](const CapturedVariable &X, const CapturedVariable &Y) { + return X.getName() < Y.getName(); + }); + // 'This'/'self' should be passed-in first. + if (!isMethodExtraction() && Visitor.CaptureThis) { + CapturedVariables.insert( + CapturedVariables.begin(), + CapturedVariable::getThis( + Visitor.ThisRecordType, + IsThisConstInCapturedFieldUses && + Visitor.IsThisConstForNonCapturedFieldUses)); + CapturedThisReferenceRewriter ThisRewriter(SourceRewriter); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + ThisRewriter.TraverseStmt(const_cast(S)); + } else + ThisRewriter.TraverseStmt(const_cast(S)); + CapturedThisPointerRewriter PtrThisRewriter( + SourceRewriter, ThisRewriter.RewrittenExpressions); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + PtrThisRewriter.TraverseStmt(const_cast(S)); + } else + PtrThisRewriter.TraverseStmt(const_cast(S)); + } else if (!isMethodExtraction() && Visitor.CaptureSelf && + EnclosingObjCMethod) { + if (EnclosingObjCMethod->isInstanceMethod()) { + // Instance methods rewrite 'self' into an 'object' parameter. + CapturedVariables.insert(CapturedVariables.begin(), + CapturedVariable::getSelf(Visitor.SelfType)); + CapturedSelfRewriter SelfRewriter(SourceRewriter, + EnclosingObjCMethod->getSelfDecl()); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + SelfRewriter.TraverseStmt(const_cast(S)); + } else + SelfRewriter.TraverseStmt(const_cast(S)); + } else { + // Class methods rewrite 'self' into the class name and don't pass 'self' + // as a parameter. + CapturedClassSelfRewriter SelfRewriter( + SourceRewriter, EnclosingObjCMethod->getClassInterface()->getName(), + EnclosingObjCMethod->getSelfDecl()); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + SelfRewriter.TraverseStmt(const_cast(S)); + } else + SelfRewriter.TraverseStmt(const_cast(S)); + } + } + if (!isMethodExtraction() && Visitor.CaptureSuper && EnclosingObjCMethod) { + if (EnclosingObjCMethod->isInstanceMethod()) + // Instance methods rewrite 'super' into an 'superObject' parameter. + CapturedVariables.insert(Visitor.CaptureSelf + ? CapturedVariables.begin() + 1 + : CapturedVariables.begin(), + CapturedVariable::getSuper(Visitor.SuperType)); + CapturedSuperRewriter SuperRewriter( + SourceRewriter, EnclosingObjCMethod->isInstanceMethod() + ? "superObject" + : EnclosingObjCMethod->getClassInterface() + ->getSuperClass() + ->getName()); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + SuperRewriter.TraverseStmt(const_cast(S)); + } else + SuperRewriter.TraverseStmt(const_cast(S)); + } + + // Compute the parameter types. + for (auto &Var : CapturedVariables) { + QualType T = Var.getType(); + + // Array types are passed into the extracted function using a pointer. + if (const auto *AT = Context.getAsArrayType(T)) + T = Context.getPointerType(AT->getElementType()); + + // Captured records and other mutated variables are passed into the + // extracted function either using a reference (C++) or a pointer. + if ((T->isRecordType() || Var.PassByRefOrPtr) && !T->isReferenceType()) { + // Add a 'const' qualifier to the record when it's not mutated in the + // extracted code or when we are taking the address of the captured + // variable for just a 'const' use. + if (!Var.PassByRefOrPtr || Var.IsRefOrPtrConst) + T.addConst(); + + if (LangOpts.CPlusPlus) + T = Context.getLValueReferenceType(T); + else { + T = Context.getPointerType(T); + CapturedVariableCaptureByPointerRewriter UseRewriter(Var.VD, + SourceRewriter); + if (ExtractedStmtRange) { + for (const Stmt *S : *ExtractedStmtRange) + UseRewriter.TraverseStmt(const_cast(S)); + } else + UseRewriter.TraverseStmt(const_cast(S)); + Var.TakeAddress = true; + } + } + // Const qualifier can be dropped as we don't want to declare the parameter + // as 'const'. + else if (T.isLocalConstQualified()) + T.removeLocalConst(); + + Var.ParameterType = T; + } + + // TODO: Choose a better name if there are collisions. + StringRef ExtractedName = "extracted"; + llvm::SmallVector ExtractedNamePieces; + ExtractedNamePieces.push_back(ExtractedName); + if (isMethodExtraction() && EnclosingObjCMethod && + !CapturedVariables.empty()) { + for (const auto &Var : ArrayRef(CapturedVariables).drop_front()) + ExtractedNamePieces.push_back(Var.getName()); + } + std::unique_ptr CreatedSymbol = + std::make_unique( + SymbolName(ExtractedNamePieces)); + + SourceLocation FunctionExtractionLoc = computeFunctionExtractionLocation( + FunctionLikeParentDecl, isMethodExtraction()); + FunctionExtractionLoc = + getLocationOfPrecedingComment(FunctionExtractionLoc, SM, LangOpts); + + // Create the replacement that contains the new function. + auto PrintFunctionHeader = + [&](llvm::raw_string_ostream &OS, + bool IsDefinition = + true) -> RefactoringReplacement::AssociatedSymbolLocation { + if (isMethodExtraction() && EnclosingObjCMethod) { + OS << (EnclosingObjCMethod->isClassMethod() ? '+' : '-') << " ("; + ReturnType.print(OS, PP); + OS << ')'; + llvm::SmallVector NameOffsets; + NameOffsets.push_back(OS.str().size()); + OS << ExtractedName; + bool IsFirst = true; + for (const auto &Var : CapturedVariables) { + if (!IsFirst) { + OS << ' '; + NameOffsets.push_back(OS.str().size()); + OS << Var.getName(); + } + IsFirst = false; + OS << ":("; + Var.ParameterType.print(OS, PP); + OS << ')' << Var.getName(); + } + return RefactoringReplacement::AssociatedSymbolLocation( + NameOffsets, /*IsDeclaration=*/true); + } + auto *FD = dyn_cast(FunctionLikeParentDecl); + if (isMethodExtraction() && IsDefinition && + !FD->getDescribedFunctionTemplate()) { + // Print the class template parameter lists for an out-of-line method. + for (unsigned I = 0, + NumTemplateParams = FD->getNumTemplateParameterLists(); + I < NumTemplateParams; ++I) { + FD->getTemplateParameterList(I)->print(OS, Context, PP); + OS << "\n"; + } + } + if (isMethodExtraction() && isEnclosingMethodStatic(FunctionLikeParentDecl)) + OS << "static "; + else if (!isMethodExtraction()) + OS << (isInHeader(FunctionExtractionLoc, SM) ? "inline " : "static "); + std::string QualifiedName; + llvm::raw_string_ostream NameOS(QualifiedName); + if (isMethodExtraction() && IsDefinition) + printEnclosingMethodScope(FunctionLikeParentDecl, NameOS, PP); + NameOS << ExtractedName; + NameOS << '('; + bool IsFirst = true; + for (const auto &Var : CapturedVariables) { + if (!IsFirst) + NameOS << ", "; + IsFirst = false; + Var.ParameterType.print(NameOS, PP, /*PlaceHolder=*/Var.getName()); + } + NameOS << ')'; + ReturnType.print(OS, PP, NameOS.str()); + unsigned NameOffset = OS.str().find(std::string(ExtractedName)); + if (isMethodExtraction() && isEnclosingMethodConst(FunctionLikeParentDecl)) + OS << " const"; + return RefactoringReplacement::AssociatedSymbolLocation( + NameOffset, /*IsDeclaration=*/true); + ; + }; + + if (isMethodExtraction() && + isEnclosingMethodOutOfLine(FunctionLikeParentDecl)) { + // The location of the declaration should be either before the original + // declararation, or, if this method has not declaration, somewhere + // appropriate in the class. + MethodDeclarationPlacement Placement; + SourceLocation DeclarationLoc; + if (FunctionLikeParentDecl->getCanonicalDecl() != FunctionLikeParentDecl) { + DeclarationLoc = computeFunctionExtractionLocation( + FunctionLikeParentDecl->getCanonicalDecl(), isMethodExtraction()); + Placement = MethodDeclarationPlacement::Before; + } else { + auto LocAndPlacement = + computeAppropriateExtractionLocationForMethodDeclaration( + cast(FunctionLikeParentDecl)); + DeclarationLoc = LocAndPlacement.first; + Placement = LocAndPlacement.second; + } + if (Placement == MethodDeclarationPlacement::Before) + DeclarationLoc = + getLocationOfPrecedingComment(DeclarationLoc, SM, LangOpts); + else + DeclarationLoc = getLastLineLocationUnlessItHasOtherTokens( + getPreciseTokenLocEnd(DeclarationLoc, SM, LangOpts), SM, LangOpts); + // Add a replacement for the method declaration if necessary. + std::string DeclarationString; + llvm::raw_string_ostream OS(DeclarationString); + if (Placement == MethodDeclarationPlacement::After) + OS << "\n\n"; + RefactoringReplacement::AssociatedSymbolLocation SymbolLoc = + PrintFunctionHeader(OS, /*IsDefinition=*/false); + OS << ";\n"; + if (Placement == MethodDeclarationPlacement::Before) + OS << "\n"; + Replacements.push_back(RefactoringReplacement( + SourceRange(DeclarationLoc, DeclarationLoc), std::move(OS.str()), + CreatedSymbol.get(), SymbolLoc)); + } + std::string ExtractedCode; + llvm::raw_string_ostream ExtractedOS(ExtractedCode); + RefactoringReplacement::AssociatedSymbolLocation SymbolLoc = + PrintFunctionHeader(ExtractedOS); + ExtractedOS << " {\n"; + if (IsExpr && !ReturnType->isVoidType()) + ExtractedOS << "return "; + SourceRange ExtractedTokenRange = + CandidateExtractionInfo[SelectedCandidateIndex].Range; + auto Semicolons = computeSemicolonExtractionPolicy( + ExtractedStmtRange ? *(ExtractedStmtRange->Last) : S, ExtractedTokenRange, + SM, LangOpts); + bool ShouldCopyBlock = false; + if (IsExpr && !LangOpts.ObjCAutoRefCount && + ReturnType->isBlockPointerType()) { + // We can't return local blocks directly without ARC; they should be copied. + // FIXME: This is overly pessimistic, as we only need the copy for local + // blocks. + ExtractedOS << "[("; + ShouldCopyBlock = true; + } + ExtractedOS << SourceRewriter.getRewrittenText(ExtractedTokenRange); + if (ShouldCopyBlock) + ExtractedOS << ") copy]"; + if (Semicolons.IsNeededInExtractedFunction) + ExtractedOS << ';'; + if (CanUseReturnForVariablesUsedAfterwards) + ExtractedOS << "\nreturn " << RedeclaredVariables.front().VD->getName() + << ";"; + ExtractedOS << "\n}\n\n"; + Replacements.push_back(RefactoringReplacement( + SourceRange(FunctionExtractionLoc, FunctionExtractionLoc), + std::move(ExtractedOS.str()), CreatedSymbol.get(), SymbolLoc)); + + // Create a replacements that removes the extracted code in favor of the + // function call. + std::string InsertedCode; + llvm::raw_string_ostream InsertedOS(InsertedCode); + // We might have to declare variables that were declared in the extracted code + // but still used afterwards. + if (CanUseReturnForVariablesUsedAfterwards) { + const auto &Var = RedeclaredVariables.front(); + Var.VD->getType().print(InsertedOS, PP); + InsertedOS << ' ' << Var.VD->getName() << " = "; + } else { + for (const auto &Var : RedeclaredVariables) { + Var.VD->getType().print(InsertedOS, PP); + InsertedOS << ' ' << Var.VD->getName() << ";\n"; + } + } + InsertedOS << CandidateExtractionInfo[SelectedCandidateIndex].PreInsertedText; + llvm::SmallVector NameOffsets; + if (isMethodExtraction() && EnclosingObjCMethod) { + InsertedOS << "[self "; + NameOffsets.push_back(InsertedOS.str().size()); + InsertedOS << ExtractedName; + bool IsFirst = true; + for (const auto &Var : CapturedVariables) { + if (!IsFirst) { + InsertedOS << ' '; + NameOffsets.push_back(InsertedOS.str().size()); + InsertedOS << Var.getName(); + } + IsFirst = false; + InsertedOS << ':'; + if (Var.TakeAddress) + InsertedOS << '&'; + InsertedOS << Var.getExpr(); + } + InsertedOS << ']'; + } else { + NameOffsets.push_back(InsertedOS.str().size()); + InsertedOS << ExtractedName << '('; + bool IsFirst = true; + for (const auto &Var : CapturedVariables) { + if (!IsFirst) + InsertedOS << ", "; + IsFirst = false; + if (Var.TakeAddress) + InsertedOS << '&'; + InsertedOS << Var.getExpr(); + } + InsertedOS << ')'; + } + if (Semicolons.IsNeededInOriginalFunction) + InsertedOS << ';'; + SourceRange ExtractedCharRange = SourceRange( + ExtractedTokenRange.getBegin(), + getPreciseTokenLocEnd(ExtractedTokenRange.getEnd(), SM, LangOpts)); + Replacements.push_back(RefactoringReplacement( + ExtractedCharRange, std::move(InsertedOS.str()), CreatedSymbol.get(), + ArrayRef(NameOffsets))); + + RefactoringResult Result(std::move(Replacements)); + Result.AssociatedSymbols.push_back(std::move(CreatedSymbol)); + return std::move(Result); +} diff --git a/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp new file mode 100644 index 0000000000000..53b9020032af3 --- /dev/null +++ b/clang/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp @@ -0,0 +1,298 @@ +//===--- ExtractRepeatedExpressionIntoVariable.cpp - ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements the "Extract repeated expression into variable" refactoring +// operation. +// +//===----------------------------------------------------------------------===// + +#include "ExtractionUtils.h" +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/RecursiveASTVisitor.h" + +using namespace clang; +using namespace clang::tooling; + +namespace { + +class ExtractRepeatedExpressionIntoVariableOperation + : public RefactoringOperation { +public: + ExtractRepeatedExpressionIntoVariableOperation( + const Expr *E, ArrayRef Duplicates, const Decl *ParentDecl) + : E(E), DuplicateExpressions(Duplicates.begin(), Duplicates.end()), + ParentDecl(ParentDecl) {} + + const Stmt *getTransformedStmt() const override { return E; } + + llvm::Expected perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const Expr *E; + SmallVector DuplicateExpressions; + const Decl *ParentDecl; +}; + +using UseOfDeclaration = std::pair; + +bool shouldIgnoreParens(const ParenExpr *E) { + if (!E) + return false; + const Expr *Child = E->getSubExpr(); + // Ignore the parens unless they are around an expression that + // really needs them. + if (isa(Child) || isa(Child) || + isa(Child) || + isa(Child)) + return false; + return true; +} + +/// Builds up a list of declarations that are used in an expression. +class DuplicateExprSemanticProfiler + : public RecursiveASTVisitor { + unsigned Index = 0; + llvm::SmallVectorImpl &DeclRefs; + +public: + DuplicateExprSemanticProfiler( + llvm::SmallVectorImpl &DeclRefs) + : DeclRefs(DeclRefs) { + DeclRefs.clear(); + } + + bool VisitStmt(const Stmt *S) { + if (!shouldIgnoreParens(dyn_cast(S))) + ++Index; + return true; + } + + bool VisitDeclRefExpr(const DeclRefExpr *E) { + if (E->getDecl()) + DeclRefs.emplace_back(E->getDecl(), Index); + return true; + } +}; + +class DuplicateExprFinder : public RecursiveASTVisitor, + PrinterHelper { + const Expr *Target; + const ASTContext &Context; + const PrintingPolicy &PP; + Stmt::StmtClass ExprKind; + QualType T; + std::string ExprString, OSString; + llvm::SmallVector ExprDecls, DeclUses; + + void printExpr(std::string &Str, const Expr *E) { + llvm::raw_string_ostream OS(Str); + E->printPretty(OS, /*Helper=*/this, PP); + } + +public: + SmallVector DuplicateExpressions; + + DuplicateExprFinder(const Expr *E, const ASTContext &Context, + const PrintingPolicy &PP) + : Target(E), Context(Context), PP(PP), ExprKind(E->getStmtClass()), + T(E->getType()) { + printExpr(ExprString, E); + DuplicateExprSemanticProfiler(ExprDecls).TraverseStmt( + const_cast(E)); + } + + bool handledStmt(Stmt *E, raw_ostream &OS) final override { + if (const auto *Paren = dyn_cast(E)) { + if (!shouldIgnoreParens(Paren)) + return false; + Paren->getSubExpr()->printPretty(OS, /*Helper=*/this, PP); + return true; + } + return false; + } + + bool VisitStmt(const Stmt *S) { + if (S->getStmtClass() != ExprKind) + return true; + const auto *E = cast(S); + if (E == Target) { + DuplicateExpressions.push_back(E); + return true; + } + // The expression should not be in a macro. + SourceRange R = E->getSourceRange(); + if (R.getBegin().isMacroID()) { + if (!Context.getSourceManager().isMacroArgExpansion(R.getBegin())) + return true; + } + if (R.getEnd().isMacroID()) { + if (!Context.getSourceManager().isMacroArgExpansion(R.getEnd())) + return true; + } + // The expression types should match. + if (E->getType() != T) + return true; + // Check if the expression is a duplicate by comparing their lexical + // representations. + OSString.clear(); + printExpr(OSString, E); + if (OSString == ExprString) { + DuplicateExprSemanticProfiler(DeclUses).TraverseStmt( + const_cast(E)); + // Check if they're semantically equivalent. + if (ExprDecls.size() == DeclUses.size() && + std::equal(ExprDecls.begin(), ExprDecls.end(), DeclUses.begin())) + DuplicateExpressions.push_back(E); + } + return true; + } +}; + +} // end anonymous namespace + +static QualType returnTypeOfCall(const Expr *E) { + if (const auto *Call = dyn_cast(E)) { + if (const auto *Fn = Call->getDirectCallee()) + return Fn->getReturnType(); + } else if (const auto *Msg = dyn_cast(E)) { + if (const auto *M = Msg->getMethodDecl()) + return M->getReturnType(); + } else if (const auto *PRE = dyn_cast(E)) { + if (PRE->isImplicitProperty()) { + if (const auto *M = PRE->getImplicitPropertyGetter()) + return M->getReturnType(); + } else if (const auto *Prop = PRE->getExplicitProperty()) + return Prop->getType(); + } + return QualType(); +} + +static bool isRepeatableExpression(const Stmt *S) { + if (const auto *Op = dyn_cast(S)) + return Op->getOperator() == OO_Call || Op->getOperator() == OO_Subscript; + return isa(S) || isa(S) || + isa(S); +} + +RefactoringOperationResult +clang::tooling::initiateExtractRepeatedExpressionIntoVariableOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + const Stmt *S; + const Decl *ParentDecl; + if (SelectionRange.isValid()) { + auto SelectedStmt = Slice.getSelectedStmtSet(); + if (!SelectedStmt) + return std::nullopt; + if (!SelectedStmt->containsSelectionRange) + return std::nullopt; + if (!isRepeatableExpression(SelectedStmt->containsSelectionRange)) + return std::nullopt; + S = SelectedStmt->containsSelectionRange; + ParentDecl = + Slice.parentDeclForIndex(*SelectedStmt->containsSelectionRangeIndex); + } else { + auto SelectedStmt = Slice.nearestSelectedStmt(isRepeatableExpression); + if (!SelectedStmt) + return std::nullopt; + S = SelectedStmt->getStmt(); + ParentDecl = SelectedStmt->getParentDecl(); + } + + const Expr *E = cast(S); + // Check if the function/method returns a reference/pointer. + QualType T = returnTypeOfCall(E); + if (!T.getTypePtrOrNull() || + (!T->isAnyPointerType() && !T->isReferenceType())) + return std::nullopt; + + DuplicateExprFinder DupFinder(E, Context, Context.getPrintingPolicy()); + DupFinder.TraverseDecl(const_cast(ParentDecl)); + if (DupFinder.DuplicateExpressions.size() < 2) + return std::nullopt; + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = + std::make_unique( + E, DupFinder.DuplicateExpressions, ParentDecl); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +static StringRef nameForExtractedVariable(const Expr *E) { + auto SuggestedName = extract::nameForExtractedVariable(E); + if (!SuggestedName) + return "duplicate"; + return *SuggestedName; +} + +llvm::Expected +ExtractRepeatedExpressionIntoVariableOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + RefactoringResult Result(std::vector{}); + std::vector &Replacements = Result.Replacements; + + const SourceManager &SM = Context.getSourceManager(); + SourceLocation InsertionLoc = + extract::locationForExtractedVariableDeclaration(DuplicateExpressions, + ParentDecl, SM); + if (InsertionLoc.isInvalid()) + return llvm::make_error( + "no appropriate insertion location found"); + + StringRef Name = nameForExtractedVariable(E); + Result.AssociatedSymbols.push_back( + std::make_unique( + SymbolName(Name, /*IsObjectiveCSelector=*/false))); + RefactoringResultAssociatedSymbol *CreatedSymbol = + Result.AssociatedSymbols.back().get(); + + // Create the variable that will hold the value of the duplicate expression. + std::string VariableDeclarationString; + llvm::raw_string_ostream OS(VariableDeclarationString); + QualType T = returnTypeOfCall(E); + PrintingPolicy PP = Context.getPrintingPolicy(); + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + T.print(OS, PP, /*PlaceHolder*/ Name); + // FIXME: We should hook into the TypePrinter when moving over to llvm.org + // instead and get the offset from it. + unsigned NameOffset = StringRef(OS.str()).find(Name); + OS << " = "; + PrintingPolicy ExprPP = Context.getPrintingPolicy(); + ExprPP.SuppressStrongLifetime = true; + ExprPP.SuppressImplicitBase = true; + E->printPretty(OS, /*Helper=*/nullptr, ExprPP); + OS << ";\n"; + Replacements.push_back(RefactoringReplacement( + SourceRange(InsertionLoc, InsertionLoc), OS.str(), CreatedSymbol, + RefactoringReplacement::AssociatedSymbolLocation( + ArrayRef(NameOffset), /*IsDeclaration=*/true))); + + // Replace the duplicates with a reference to the variable. + for (const Expr *E : DuplicateExpressions) { + Replacements.push_back(RefactoringReplacement( + SourceRange(SM.getSpellingLoc(E->getBeginLoc()), + getPreciseTokenLocEnd(SM.getSpellingLoc(E->getEndLoc()), SM, + Context.getLangOpts())), + Name, CreatedSymbol, + /*NameOffset=*/ArrayRef(unsigned(0)))); + } + + return std::move(Result); +} diff --git a/clang/lib/Tooling/Refactor/ExtractionUtils.cpp b/clang/lib/Tooling/Refactor/ExtractionUtils.cpp new file mode 100644 index 0000000000000..aa9e5e5810fe7 --- /dev/null +++ b/clang/lib/Tooling/Refactor/ExtractionUtils.cpp @@ -0,0 +1,138 @@ +//===--- ExtractionUtils.cpp - Extraction helper functions ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ExtractionUtils.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/SaveAndRestore.h" + +using namespace clang; + +std::optional +tooling::extract::nameForExtractedVariable(const Expr *E) { + if (const auto *Call = dyn_cast(E)) { + if (const auto *Fn = Call->getDirectCallee()) + return Fn->getName(); + } else if (const auto *Msg = dyn_cast(E)) { + if (const auto *M = Msg->getMethodDecl()) { + if (M->getSelector().isUnarySelector()) + return M->getSelector().getNameForSlot(0); + } + } else if (const auto *PRE = dyn_cast(E)) { + if (PRE->isImplicitProperty()) { + if (const auto *M = PRE->getImplicitPropertyGetter()) + return M->getSelector().getNameForSlot(0); + } else if (const auto *Prop = PRE->getExplicitProperty()) + return Prop->getName(); + } + return std::nullopt; +} + +namespace { + +/// Checks if a set of expressions is directly contained in some AST region. +class StmtReachabilityChecker + : public RecursiveASTVisitor { + const llvm::SmallPtrSetImpl &Expressions; + unsigned Count = 0; + + StmtReachabilityChecker( + const llvm::SmallPtrSetImpl &Expressions) + : Expressions(Expressions) {} + + bool areAllExpressionsReached() const { return Count == Expressions.size(); } + +public: + bool VisitStmt(const Stmt *S) { + if (Expressions.count(S)) { + ++Count; + if (areAllExpressionsReached()) + return false; + } + return true; + } + + static bool areAllExpressionsReachableFrom( + CompoundStmt *S, const llvm::SmallPtrSetImpl &Expressions) { + StmtReachabilityChecker Checker(Expressions); + Checker.TraverseStmt(S); + return Checker.areAllExpressionsReached(); + } +}; + +/// Figures out where the extracted variable should go. +class ExtractedVariableInsertionLocFinder + : public RecursiveASTVisitor { + llvm::SmallPtrSet Expressions; + llvm::SmallVector, 4> + InsertionCandidateStack; + bool IsPrevCompoundStmt = false; + +public: + SourceLocation Loc; + + /// Initializes the insertion location finder using the set of duplicate + /// \p Expressions from one function. + ExtractedVariableInsertionLocFinder(ArrayRef Expressions) { + for (const Expr *E : Expressions) + this->Expressions.insert(E); + } + + bool TraverseStmt(Stmt *S) { + if (!S) + return RecursiveASTVisitor::TraverseStmt(S); + if (IsPrevCompoundStmt && !InsertionCandidateStack.empty()) + InsertionCandidateStack.back().second = S; + llvm::SaveAndRestore IsPrevCompoundStmtTracker(IsPrevCompoundStmt, + false); + if (auto *CS = dyn_cast(S)) { + IsPrevCompoundStmt = true; + InsertionCandidateStack.emplace_back(CS, nullptr); + RecursiveASTVisitor::TraverseStmt(S); + InsertionCandidateStack.pop_back(); + return true; + } + return RecursiveASTVisitor::TraverseStmt(S); + } + + bool VisitStmt(const Stmt *S) { + if (Expressions.count(S)) { + // The insertion location should be in the first compound statement that + // includes all of the expressions as descendants as we want the new + // variable to be visible to all uses. + for (auto I = InsertionCandidateStack.rbegin(), + E = InsertionCandidateStack.rend(); + I != E; ++I) { + if (StmtReachabilityChecker::areAllExpressionsReachableFrom( + I->first, Expressions) && + I->second) { + Loc = I->second->getBeginLoc(); + break; + } + } + return false; + } + return true; + } +}; + +} // end anonymous namespace + +SourceLocation tooling::extract::locationForExtractedVariableDeclaration( + ArrayRef Expressions, const Decl *ParentDecl, + const SourceManager &SM) { + ExtractedVariableInsertionLocFinder LocFinder(Expressions); + LocFinder.TraverseDecl(const_cast(ParentDecl)); + SourceLocation Result = LocFinder.Loc; + if (Result.isValid() && Result.isMacroID()) + return SM.getExpansionLoc(Result); + return Result; +} diff --git a/clang/lib/Tooling/Refactor/ExtractionUtils.h b/clang/lib/Tooling/Refactor/ExtractionUtils.h new file mode 100644 index 0000000000000..6f31329f02f8c --- /dev/null +++ b/clang/lib/Tooling/Refactor/ExtractionUtils.h @@ -0,0 +1,39 @@ +//===--- ExtractionUtils.h - Extraction helper functions ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_EXTRACTION_UTILS_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_EXTRACTION_UTILS_H + +#include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" + +namespace clang { + +class Expr; +class Decl; +class SourceManager; + +namespace tooling { +namespace extract { + +/// Returns a good name for an extracted variable based on the declaration +/// that's used in the given expression \p E. +std::optional nameForExtractedVariable(const Expr *E); + +/// Returns an appropriate location for a variable declaration that will be +/// visible to all the given expressions. +SourceLocation +locationForExtractedVariableDeclaration(ArrayRef Expressions, + const Decl *ParentDecl, + const SourceManager &SM); + +} // end namespace extract +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_EXTRACTION_UTILS_H diff --git a/clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp b/clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp new file mode 100644 index 0000000000000..d62d3aa2e2c0d --- /dev/null +++ b/clang/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp @@ -0,0 +1,109 @@ +//===--- FillInEnumSwitchCases.cpp - -------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements the "Add missing switch cases" refactoring operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringOperations.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/Edit/RefactoringFixits.h" + +using namespace clang; +using namespace clang::tooling; + +namespace { + +class FillInEnumSwitchCasesOperation : public RefactoringOperation { +public: + FillInEnumSwitchCasesOperation(const EnumDecl *Enum, const SwitchStmt *Switch, + const DeclContext *SwitchContext) + : Enum(Enum), Switch(Switch), SwitchContext(SwitchContext) {} + + const Stmt *getTransformedStmt() const override { return Switch; } + + llvm::Expected + perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const EnumDecl *Enum; + const SwitchStmt *Switch; + const DeclContext *SwitchContext; +}; + +} // end anonymous namespace + +RefactoringOperationResult +clang::tooling::initiateFillInEnumSwitchCasesOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + const SwitchStmt *Switch; + const Decl *ParentDecl; + if (SelectionRange.isValid()) { + auto SelectedSet = Slice.getSelectedStmtSet(); + if (!SelectedSet) + return std::nullopt; + Switch = dyn_cast_or_null(SelectedSet->containsSelectionRange); + // FIXME: Improve the interface for this to make it similar to SelectedStmt + if (SelectedSet->containsSelectionRange) + ParentDecl = + Slice.parentDeclForIndex(*SelectedSet->containsSelectionRangeIndex); + } else { + auto SelectedStmt = Slice.nearestSelectedStmt(Stmt::SwitchStmtClass); + if (!SelectedStmt) + return std::nullopt; + Switch = cast(SelectedStmt->getStmt()); + ParentDecl = SelectedStmt->getParentDecl(); + } + if (!Switch) + return std::nullopt; + + // Ensure that the type is an enum. + const Expr *Cond = Switch->getCond()->IgnoreImpCasts(); + const EnumDecl *ED = nullptr; + if (const auto *ET = Cond->getType()->getAs()) + ED = ET->getDecl(); + else { + // Enum literals are 'int' in C. + if (const auto *DRE = dyn_cast(Cond)) { + if (const auto *EC = dyn_cast(DRE->getDecl())) + ED = dyn_cast(EC->getDeclContext()); + } + } + + if (!ED) + return RefactoringOperationResult("The switch doesn't operate on an enum"); + if (!ED->isCompleteDefinition()) + return RefactoringOperationResult("The enum type is incomplete"); + + if (Switch->isAllEnumCasesCovered()) + return RefactoringOperationResult("All enum cases are already covered"); + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = std::make_unique( + ED, Switch, dyn_cast(ParentDecl)); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +llvm::Expected +FillInEnumSwitchCasesOperation::perform(ASTContext &Context, + const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) { + std::vector Replacements; + edit::fillInMissingSwitchEnumCases( + Context, Switch, Enum, SwitchContext, + [&](const FixItHint &Hint) { Replacements.push_back(Hint); }); + return std::move(Replacements); +} diff --git a/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp b/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp new file mode 100644 index 0000000000000..d5f14910a3a78 --- /dev/null +++ b/clang/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp @@ -0,0 +1,292 @@ +//===--- FillInMissingMethodStubsFromAbstractClasses.cpp - ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements the "Add missing abstract class method overrides" refactoring +// operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "llvm/ADT/DenseSet.h" + +using namespace clang; +using namespace clang::tooling; + +namespace { + +class FillInMissingMethodStubsFromAbstractClassesOperation + : public RefactoringOperation { +public: + FillInMissingMethodStubsFromAbstractClassesOperation( + const CXXRecordDecl *Class) + : Class(Class) {} + + const Decl *getTransformedDecl() const override { return Class; } + + llvm::Expected perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const CXXRecordDecl *Class; +}; + +} // end anonymous namespace + +static bool hasAbstractBases(const CXXRecordDecl *Class) { + for (const CXXBaseSpecifier &Base : Class->bases()) { + if (const auto *RD = Base.getType()->getAsCXXRecordDecl()) { + if (RD->isAbstract()) + return true; + } + } + return false; +} + +RefactoringOperationResult +clang::tooling::initiateFillInMissingMethodStubsFromAbstractClassesOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + auto SelectedDecl = Slice.innermostSelectedDecl( + ArrayRef(Decl::CXXRecord), ASTSlice::InnermostDeclOnly); + if (!SelectedDecl) + return std::nullopt; + const auto *Class = cast(SelectedDecl->getDecl()); + if (Class->isUnion() || !Class->isThisDeclarationADefinition()) + return std::nullopt; + if (!hasAbstractBases(Class)) + return RefactoringOperationResult("The class has no abstract bases"); + if (!Class->isDependentType() && !Class->isAbstract()) + return RefactoringOperationResult( + "The class has no missing abstract class methods"); + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = + std::make_unique( + Class); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +namespace { + +class PureMethodSet { + llvm::DenseMap Methods; + + void addPureMethodsFromAbstractClasses(const CXXRecordDecl *Class, + int &Priority) { + for (const CXXBaseSpecifier &Base : Class->bases()) { + const auto *RD = Base.getType()->getAsCXXRecordDecl(); + if (!RD || !RD->isAbstract()) + continue; + for (const CXXMethodDecl *M : RD->methods()) { + if (M->isPureVirtual()) + Methods.insert(std::make_pair(M->getCanonicalDecl(), Priority++)); + } + addPureMethodsFromAbstractClasses(RD, Priority); + } + } + + void addPureMethodsFromAbstractClasses(const CXXRecordDecl *Class) { + int Priority = 0; + addPureMethodsFromAbstractClasses(Class, Priority); + } + + void subtractImplementedPureMethods(const CXXRecordDecl *Class) { + for (const CXXMethodDecl *M : Class->methods()) { + if (!M->isVirtual() || M->isPureVirtual()) + continue; + for (const CXXMethodDecl *OM : M->overridden_methods()) { + OM = OM->getCanonicalDecl(); + if (OM->isPureVirtual()) + Methods.erase(OM); + } + } + for (const CXXBaseSpecifier &Base : Class->bases()) { + const auto *RD = Base.getType()->getAsCXXRecordDecl(); + if (!RD || !RD->isAbstract()) + continue; + subtractImplementedPureMethods(RD); + } + } + +public: + static std::vector + gatherMissingMethods(const CXXRecordDecl *Class) { + PureMethodSet MethodSet; + MethodSet.addPureMethodsFromAbstractClasses(Class); + MethodSet.subtractImplementedPureMethods(Class); + // Sort the missing methods. That will place methods from the same abstract + // class together in the order in which they were declared. + struct MethodInfo { + const CXXMethodDecl *M; + int Priority; + }; + std::vector MissingMethods; + for (const auto &M : MethodSet.Methods) + MissingMethods.push_back({M.first, M.second}); + std::sort(MissingMethods.begin(), MissingMethods.end(), + [](const MethodInfo &LHS, const MethodInfo &RHS) { + return LHS.Priority < RHS.Priority; + }); + std::vector Result; + Result.reserve(MissingMethods.size()); + for (const auto &M : MissingMethods) + Result.push_back(M.M); + return Result; + } +}; + +} // end anonymous namespace + +static SourceLocation findInsertionLocationForMethodsFromAbstractClass( + const CXXRecordDecl *AbstractClass, const CXXRecordDecl *Class, + const SourceManager &SM, const LangOptions &LangOpts) { + SourceLocation Loc; + for (const CXXMethodDecl *M : Class->methods()) { + if (!M->isVirtual() || M->isPureVirtual() || M->isImplicit()) + continue; + for (const CXXMethodDecl *OM : M->overridden_methods()) { + OM = OM->getCanonicalDecl(); + if (OM->getLexicalDeclContext() == AbstractClass) { + SourceLocation EndLoc = M->getEndLoc(); + if (EndLoc.isMacroID()) + EndLoc = SM.getExpansionRange(EndLoc).getEnd(); + if (Loc.isInvalid()) + Loc = EndLoc; + else if (SM.isBeforeInTranslationUnit(Loc, EndLoc)) + Loc = EndLoc; + break; + } + } + } + if (Loc.isInvalid()) + return Loc; + return getLastLineLocationUnlessItHasOtherTokens(Loc, SM, LangOpts); +} + +/// Returns true if the given \p Class implements the majority of declared +/// methods in the class itself. +static bool shouldImplementMethodsInClass(const CXXRecordDecl *Class) { + // Check if this class implements the methods in the class itself. + unsigned NumMethods = 0, NumImplementedMethods = 0; + for (const CXXMethodDecl *M : Class->methods()) { + if (M->isImplicit()) + continue; + // Only look at methods/operators. + if (isa(M) || isa(M)) + continue; + ++NumMethods; + if (M->hasBody()) + ++NumImplementedMethods; + } + if (!NumMethods) + return false; + // Use the following arbitrary heuristic: + // If the number of method declarations is less than 4, then all of the + // methods must have bodies. Otherwise, at least 75% of the methods must + // have bodies. + return NumMethods < 4 + ? NumMethods == NumImplementedMethods + : float(NumImplementedMethods) / float(NumMethods) > 0.75; +} + +llvm::Expected +FillInMissingMethodStubsFromAbstractClassesOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + std::vector Replacements; + + std::vector MissingMethods = + PureMethodSet::gatherMissingMethods(Class); + + bool GenerateBodyDummies = shouldImplementMethodsInClass(Class); + + PrintingPolicy PP = Context.getPrintingPolicy(); + PP.PolishForDeclaration = true; + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + + std::string EndInsertionOSStr; + llvm::raw_string_ostream EndInsertionOS(EndInsertionOSStr); + + std::string InsertionGroupOSStr; + llvm::raw_string_ostream InsertionGroupOS(InsertionGroupOSStr); + + SourceLocation InsertionLoc = Class->getEndLoc(); + const CXXRecordDecl *CurrentAbstractClass = nullptr; + SourceLocation CurrentGroupInsertionLoc; + for (const auto &I : llvm::enumerate(MissingMethods)) { + const CXXMethodDecl *Method = I.value(); + const CXXRecordDecl *AbstractClass = Method->getParent(); + if (CurrentAbstractClass != AbstractClass) { + if (!InsertionGroupOS.str().empty()) { + assert(CurrentGroupInsertionLoc.isValid()); + Replacements.emplace_back( + SourceRange(CurrentGroupInsertionLoc, CurrentGroupInsertionLoc), + InsertionGroupOS.str()); + } + InsertionGroupOSStr.clear(); + CurrentAbstractClass = AbstractClass; + CurrentGroupInsertionLoc = + findInsertionLocationForMethodsFromAbstractClass( + CurrentAbstractClass, Class, Context.getSourceManager(), + Context.getLangOpts()); + } + bool IsInsertingAfterRelatedMethods = CurrentGroupInsertionLoc.isValid(); + raw_ostream &OS = + IsInsertingAfterRelatedMethods ? InsertionGroupOS : EndInsertionOS; + + if (IsInsertingAfterRelatedMethods && InsertionGroupOS.str().empty()) + OS << "\n\n"; + // Print the method without the 'virtual' specifier and the pure '= 0' + // annotation. + auto *MD = const_cast(Method); + bool IsVirtual = MD->isVirtualAsWritten(); + MD->setVirtualAsWritten(false); + bool IsPure = MD->isPureVirtual(); + MD->setIsPureVirtual(false); + MD->print(OS, PP); + MD->setVirtualAsWritten(IsVirtual); + MD->setIsPureVirtual(IsPure); + + OS << " override"; + if (GenerateBodyDummies) + OS << " { \n <#code#>\n}\n"; + else + OS << ";\n"; + // Avoid an additional newline for the last method in an insertion group. + if (IsInsertingAfterRelatedMethods) { + const CXXRecordDecl *NextAbstractClass = + (I.index() + 1) != MissingMethods.size() + ? MissingMethods[I.index() + 1]->getParent() + : nullptr; + if (NextAbstractClass == CurrentAbstractClass) + OS << "\n"; + } else + OS << "\n"; + } + if (!InsertionGroupOS.str().empty()) { + assert(CurrentGroupInsertionLoc.isValid()); + Replacements.emplace_back( + SourceRange(CurrentGroupInsertionLoc, CurrentGroupInsertionLoc), + InsertionGroupOS.str()); + } + if (!EndInsertionOS.str().empty()) + Replacements.emplace_back(SourceRange(InsertionLoc, InsertionLoc), + EndInsertionOS.str()); + + return std::move(Replacements); +} diff --git a/clang/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp b/clang/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp new file mode 100644 index 0000000000000..0e4fae969690f --- /dev/null +++ b/clang/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp @@ -0,0 +1,90 @@ +//===--- FillInMissingProtocolStubs.cpp - --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements the "Add methods from protocol(s)" refactoring operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringOperations.h" +#include "clang/AST/AST.h" +#include "clang/Edit/RefactoringFixits.h" + +using namespace clang; +using namespace clang::tooling; +using namespace edit::fillInMissingProtocolStubs; + +namespace { + +class FillInMissingProtocolStubsOperation : public RefactoringOperation { +public: + FillInMissingProtocolStubsOperation(const ObjCContainerDecl *Container, + FillInMissingProtocolStubs Impl) + : Container(Container), Impl(std::move(Impl)) {} + + const Decl *getTransformedDecl() const override { return Container; } + + llvm::Expected perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const ObjCContainerDecl *Container; + FillInMissingProtocolStubs Impl; +}; + +} // end anonymous namespace + +RefactoringOperationResult +clang::tooling::initiateFillInMissingProtocolStubsOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + auto SelectedDecl = Slice.innermostSelectedDecl( + {Decl::ObjCImplementation, Decl::ObjCCategoryImpl, Decl::ObjCInterface, + Decl::ObjCCategory}, + ASTSlice::InnermostDeclOnly); + if (!SelectedDecl) + return std::nullopt; + const auto *Container = cast(SelectedDecl->getDecl()); + + // If this in a class extension, initiate the operation on the @implementation + // if it's in the same TU. + if (const auto *Category = dyn_cast(Container)) { + if (Category->IsClassExtension()) { + const ObjCInterfaceDecl *I = Category->getClassInterface(); + if (I && I->getImplementation()) + Container = I->getImplementation(); + else + return RefactoringOperationResult( + "Class extension without suitable @implementation"); + } + } + + FillInMissingProtocolStubs Impl; + if (Impl.initiate(Context, Container)) + return std::nullopt; + if (!Impl.hasMissingRequiredMethodStubs()) + return RefactoringOperationResult("All of the @required methods are there"); + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = std::make_unique( + Container, std::move(Impl)); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +llvm::Expected +FillInMissingProtocolStubsOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + std::vector Replacements; + Impl.perform(Context, + [&](const FixItHint &Hint) { Replacements.push_back(Hint); }); + return std::move(Replacements); +} diff --git a/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp new file mode 100644 index 0000000000000..f9430723f881d --- /dev/null +++ b/clang/lib/Tooling/Refactor/IfSwitchConversion.cpp @@ -0,0 +1,468 @@ +//===--- IfSwitchConversion.cpp - ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements the "convert to switch" refactoring operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/RecursiveASTVisitor.h" + +using namespace clang; +using namespace clang::tooling; + +namespace { + +class IfSwitchConversionOperation : public RefactoringOperation { +public: + IfSwitchConversionOperation(const IfStmt *If) : If(If) {} + + const Stmt *getTransformedStmt() const override { return If; } + + llvm::Expected perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const IfStmt *If; +}; + +class ValidIfBodyVerifier : public RecursiveASTVisitor { + bool CheckBreaks = true; + +public: + bool IsValid = true; + + bool VisitBreakStmt(const BreakStmt *S) { + if (!CheckBreaks) + return true; + IsValid = false; + return false; + } + bool VisitDefaultStmt(const DefaultStmt *S) { + IsValid = false; + return false; + } + bool VisitCaseStmt(const CaseStmt *S) { + IsValid = false; + return false; + } + +// Handle nested loops: + +#define TRAVERSE_LOOP(STMT) \ + bool Traverse##STMT(STMT *S) { \ + bool Prev = CheckBreaks; \ + CheckBreaks = false; \ + RecursiveASTVisitor::Traverse##STMT(S); \ + CheckBreaks = Prev; \ + return true; \ + } + + TRAVERSE_LOOP(ForStmt) + TRAVERSE_LOOP(WhileStmt) + TRAVERSE_LOOP(DoStmt) + TRAVERSE_LOOP(CXXForRangeStmt) + TRAVERSE_LOOP(ObjCForCollectionStmt) + +#undef TRAVERSE_LOOP + + // Handle switches: + + bool TraverseSwitchStmt(SwitchStmt *S) { + // Don't visit the body as 'break'/'case'/'default' are all allowed inside + // switches. + return true; + } +}; + +} // end anonymous namespace + +/// Returns true if any of the if statements in the given if construct have +/// conditions that aren't allowed by the "convert to switch" operation. +static bool checkIfsHaveConditionExpression(const IfStmt *If) { + for (; If; If = dyn_cast_or_null(If->getElse())) { + if (If->getConditionVariable() || If->getInit() || !If->getCond()) + return true; + } + return false; +} + +static std::optional> +matchBinOp(const Expr *E, BinaryOperator::Opcode Kind) { + const auto *BinOp = dyn_cast(E->IgnoreParens()); + if (!BinOp || BinOp->getOpcode() != Kind) + return std::nullopt; + return std::pair( + BinOp->getLHS()->IgnoreParenImpCasts(), BinOp->getRHS()->IgnoreParens()); +} + +typedef llvm::SmallDenseSet RHSValueSet; + +/// Returns true if the conditional expression of an 'if' statement allows +/// the "convert to switch" refactoring action. +static bool +isConditionValid(const Expr *E, ASTContext &Context, + std::optional &MatchedLHSNodeID, + RHSValueSet &RHSValues) { + auto Equals = matchBinOp(E, BO_EQ); + if (!Equals) { + auto LogicalOr = matchBinOp(E, BO_LOr); + if (!LogicalOr) + return false; + return isConditionValid(LogicalOr->first, Context, + MatchedLHSNodeID, RHSValues) && + isConditionValid(LogicalOr->second, Context, + MatchedLHSNodeID, RHSValues); + } + const Expr *LHS = Equals->first; + const Expr *RHS = Equals->second; + if (!LHS->getType()->isIntegralOrEnumerationType() || + !RHS->getType()->isIntegralOrEnumerationType()) + return false; + + // RHS must be a constant and unique. + Expr::EvalResult Result; + if (!RHS->EvaluateAsInt(Result, Context)) + return false; + // Only allow constant that fix into 64 bits. + if (Result.Val.getInt().getSignificantBits() > 64 || + !RHSValues.insert(Result.Val.getInt().getExtValue()).second) + return false; + + // LHS must be identical to the other LHS expressions. + llvm::FoldingSetNodeID LHSNodeID; + LHS->Profile(LHSNodeID, Context, /*Canonical=*/false); + if (MatchedLHSNodeID) { + if (*MatchedLHSNodeID != LHSNodeID) + return false; + } else + MatchedLHSNodeID = std::move(LHSNodeID); + return true; +} + +RefactoringOperationResult clang::tooling::initiateIfSwitchConversionOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + // FIXME: Add support for selections. + const auto *If = cast_or_null(Slice.nearestStmt(Stmt::IfStmtClass)); + if (!If) + return std::nullopt; + + // Don't allow if statements without any 'else' or 'else if'. + if (!If->getElse()) + return std::nullopt; + + // Don't allow ifs with variable declarations in conditions or C++17 + // initializer statements. + if (checkIfsHaveConditionExpression(If)) + return std::nullopt; + + // Find the ranges in which initiation can be performed and verify that the + // ifs don't have any initialization expressions or condition variables. + SmallVector Ranges; + SourceLocation RangeStart = If->getBeginLoc(); + const IfStmt *CurrentIf = If; + const SourceManager &SM = Context.getSourceManager(); + while (true) { + const Stmt *Then = CurrentIf->getThen(); + Ranges.emplace_back(RangeStart, + findLastLocationOfSourceConstruct( + CurrentIf->getCond()->getEndLoc(), Then, SM)); + const auto *Else = CurrentIf->getElse(); + if (!Else) + break; + RangeStart = + findFirstLocationOfSourceConstruct(CurrentIf->getElseLoc(), Then, SM); + if (const auto *If = dyn_cast(Else)) { + CurrentIf = If; + continue; + } + Ranges.emplace_back(RangeStart, findLastLocationOfSourceConstruct( + CurrentIf->getElseLoc(), Else, SM)); + break; + } + + if (!isLocationInAnyRange(Location, Ranges, SM)) + return std::nullopt; + + // Verify that the bodies don't have any 'break'/'default'/'case' statements. + ValidIfBodyVerifier BodyVerifier; + BodyVerifier.TraverseStmt(const_cast(If)); + if (!BodyVerifier.IsValid) + return RefactoringOperationResult( + "if's body contains a 'break'/'default'/'case' statement"); + + // FIXME: Use ASTMatchers if possible. + std::optional MatchedLHSNodeID; + RHSValueSet RHSValues; + for (const IfStmt *CurrentIf = If; CurrentIf; + CurrentIf = dyn_cast_or_null(CurrentIf->getElse())) { + if (!isConditionValid(CurrentIf->getCond(), Context, MatchedLHSNodeID, + RHSValues)) + return RefactoringOperationResult("unsupported conditional expression"); + } + + RefactoringOperationResult Result; + Result.Initiated = true; + if (CreateOperation) + Result.RefactoringOp.reset(new IfSwitchConversionOperation(If)); + return Result; +} + +/// Returns the first LHS expression in the if's condition. +const Expr *getConditionFirstLHS(const Expr *E) { + auto Equals = matchBinOp(E, BO_EQ); + if (!Equals) { + auto LogicalOr = matchBinOp(E, BO_LOr); + if (!LogicalOr) + return nullptr; + return getConditionFirstLHS(LogicalOr->first); + } + return Equals->first; +} + +/// Gathers all of the RHS operands of the == expressions in the if's condition. +void gatherCaseValues(const Expr *E, + SmallVectorImpl &CaseValues) { + auto Equals = matchBinOp(E, BO_EQ); + if (Equals) { + CaseValues.push_back(Equals->second); + return; + } + auto LogicalOr = matchBinOp(E, BO_LOr); + if (!LogicalOr) + return; + gatherCaseValues(LogicalOr->first, CaseValues); + gatherCaseValues(LogicalOr->second, CaseValues); +} + +/// Return true iff the given body should be terminated with a 'break' statement +/// when used inside of a switch. +static bool isBreakNeeded(const Stmt *Body) { + const auto *CS = dyn_cast(Body); + if (!CS) + return !isa(Body); + return CS->body_empty() ? true : isBreakNeeded(CS->body_back()); +} + +/// Returns true if the given statement declares a variable. +static bool isVarDeclaringStatement(const Stmt *S) { + const auto *DS = dyn_cast(S); + if (!DS) + return false; + for (const Decl *D : DS->decls()) { + if (isa(D)) + return true; + } + return false; +} + +/// Return true if the body of an if/else if/else needs to be wrapped in braces +/// when put in a switch. +static bool areBracesNeeded(const Stmt *Body) { + const auto *CS = dyn_cast(Body); + if (!CS) + return isVarDeclaringStatement(Body); + for (const Stmt *S : CS->body()) { + if (isVarDeclaringStatement(S)) + return true; + } + return false; +} + +namespace { + +/// Information about the replacement that replaces 'if'/'else' with a 'case' or +/// a 'default'. +struct CasePlacement { + /// The location of the 'case' or 'default'. + SourceLocation CaseStartLoc; + /// True when this 'case' or 'default' statement needs a newline. + bool NeedsNewLine; + /// True if this the first 'if' in the source construct. + bool IsFirstIf; + /// True if we need to insert a 'break' to terminate the previous body + /// before the 'case' or 'default'. + bool IsBreakNeeded; + /// True if we need to insert a '}' before the case. + bool ArePreviousBracesNeeded; + + CasePlacement(SourceLocation Loc) + : CaseStartLoc(Loc), NeedsNewLine(false), IsFirstIf(true), + IsBreakNeeded(false), ArePreviousBracesNeeded(false) {} + + CasePlacement(const IfStmt *If, const SourceManager &SM, + bool AreBracesNeeded) { + CaseStartLoc = SM.getSpellingLoc(isa(If->getThen()) + ? If->getThen()->getEndLoc() + : If->getElseLoc()); + SourceLocation BodyEndLoc = findLastNonCompoundLocation(If->getThen()); + NeedsNewLine = BodyEndLoc.isValid() + ? areOnSameLine(CaseStartLoc, BodyEndLoc, SM) + : false; + IsFirstIf = false; + IsBreakNeeded = isBreakNeeded(If->getThen()); + ArePreviousBracesNeeded = AreBracesNeeded; + } + + std::string getCaseReplacementString(bool IsDefault = false, + bool AreNextBracesNeeded = false) const { + if (IsFirstIf) + return ") {\ncase "; + std::string Result; + llvm::raw_string_ostream OS(Result); + if (NeedsNewLine) + OS << '\n'; + if (IsBreakNeeded) + OS << "break;\n"; + if (ArePreviousBracesNeeded) + OS << "}\n"; + OS << (IsDefault ? "default:" : "case "); + if (IsDefault && AreNextBracesNeeded) + OS << " {"; + return std::move(OS.str()); + } +}; + +} // end anonymous namespace + +static llvm::Error +addCaseReplacements(const IfStmt *If, const CasePlacement &CaseInfo, + bool &AreBracesNeeded, + std::vector &Replacements, + const SourceManager &SM, const LangOptions &LangOpts) { + SmallVector CaseValues; + gatherCaseValues(If->getCond(), CaseValues); + assert(!CaseValues.empty()); + Replacements.emplace_back( + SourceRange(CaseInfo.CaseStartLoc, + SM.getSpellingLoc(CaseValues[0]->getBeginLoc())), + CaseInfo.getCaseReplacementString()); + + SourceLocation PrevCaseEnd = getPreciseTokenLocEnd( + SM.getSpellingLoc(CaseValues[0]->getEndLoc()), SM, LangOpts); + for (const Expr *CaseValue : ArrayRef(CaseValues).drop_front()) { + Replacements.emplace_back( + SourceRange(PrevCaseEnd, SM.getSpellingLoc(CaseValue->getBeginLoc())), + StringRef(":\ncase ")); + PrevCaseEnd = getPreciseTokenLocEnd( + SM.getSpellingLoc(CaseValue->getEndLoc()), SM, LangOpts); + } + + AreBracesNeeded = areBracesNeeded(If->getThen()); + StringRef ColonReplacement = AreBracesNeeded ? ": {" : ":"; + if (isa(If->getThen())) { + Replacements.emplace_back( + SourceRange( + PrevCaseEnd, + getPreciseTokenLocEnd( + SM.getSpellingLoc(If->getThen()->getBeginLoc()), SM, LangOpts)), + ColonReplacement); + } else { + // Find the location of the if's ')' + SourceLocation End = findClosingParenLocEnd( + SM.getSpellingLoc(If->getCond()->getEndLoc()), SM, LangOpts); + if (!End.isValid()) + return llvm::make_error( + "couldn't find the location of ')'"); + Replacements.emplace_back(SourceRange(PrevCaseEnd, End), ColonReplacement); + } + return llvm::Error::success(); +} + +llvm::Expected +IfSwitchConversionOperation::perform(ASTContext &Context, + const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) { + std::vector Replacements; + const SourceManager &SM = Context.getSourceManager(); + const LangOptions &LangOpts = Context.getLangOpts(); + + // The first if should be replaced with a 'switch' and the text for first LHS + // should be preserved. + const Expr *LHS = getConditionFirstLHS(If->getCond()); + assert(LHS && "Missing == expression"); + Replacements.emplace_back(SourceRange(SM.getSpellingLoc(If->getBeginLoc()), + SM.getSpellingLoc(LHS->getBeginLoc())), + StringRef("switch (")); + + bool AreBracesNeeded = false; + if (auto Error = addCaseReplacements( + If, CasePlacement(getPreciseTokenLocEnd( + SM.getSpellingLoc(LHS->getEndLoc()), SM, LangOpts)), + AreBracesNeeded, Replacements, SM, LangOpts)) + return std::move(Error); + + // Convert the remaining ifs to 'case' statements. + const IfStmt *CurrentIf = If; + while (true) { + const IfStmt *NextIf = dyn_cast_or_null(CurrentIf->getElse()); + if (!NextIf) + break; + if (auto Error = addCaseReplacements( + NextIf, CasePlacement(CurrentIf, SM, AreBracesNeeded), + AreBracesNeeded, Replacements, SM, LangOpts)) + return std::move(Error); + CurrentIf = NextIf; + } + + // Convert the 'else' to 'default' + if (const Stmt *Else = CurrentIf->getElse()) { + CasePlacement DefaultInfo(CurrentIf, SM, AreBracesNeeded); + AreBracesNeeded = areBracesNeeded(Else); + + SourceLocation EndLoc = getPreciseTokenLocEnd( + SM.getSpellingLoc(isa(Else) ? Else->getBeginLoc() + : CurrentIf->getElseLoc()), + SM, LangOpts); + Replacements.emplace_back(SourceRange(DefaultInfo.CaseStartLoc, EndLoc), + DefaultInfo.getCaseReplacementString( + /*IsDefault=*/true, AreBracesNeeded)); + } + + // Add the trailing break and one or two '}' if needed. + const Stmt *LastBody = + CurrentIf->getElse() ? CurrentIf->getElse() : CurrentIf->getThen(); + bool IsLastBreakNeeded = isBreakNeeded(LastBody); + SourceLocation TerminatingReplacementLoc; + std::string TerminatingReplacement; + llvm::raw_string_ostream OS(TerminatingReplacement); + if (!isa(LastBody)) { + TerminatingReplacementLoc = LastBody->getEndLoc(); + // Try to adjust the location in order to preserve any trailing comments on + // the last line of the last body. + if (!TerminatingReplacementLoc.isMacroID()) + TerminatingReplacementLoc = getLastLineLocationUnlessItHasOtherTokens( + TerminatingReplacementLoc, SM, LangOpts); + if (IsLastBreakNeeded) + OS << "\nbreak;"; + OS << "\n}"; + if (AreBracesNeeded) + OS << "\n}"; + } else { + TerminatingReplacementLoc = LastBody->getEndLoc(); + if (IsLastBreakNeeded) + OS << "break;\n"; + if (AreBracesNeeded) + OS << "}\n"; + } + + if (!OS.str().empty()) { + TerminatingReplacementLoc = SM.getSpellingLoc(TerminatingReplacementLoc); + Replacements.emplace_back( + SourceRange(TerminatingReplacementLoc, TerminatingReplacementLoc), + std::move(OS.str())); + } + + return std::move(Replacements); +} diff --git a/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp new file mode 100644 index 0000000000000..46b746253ed13 --- /dev/null +++ b/clang/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp @@ -0,0 +1,460 @@ +//===--- ImplementDeclaredMethods.cpp - ----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements the "Generate missing method definitions" refactoring +// operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringContinuations.h" +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" + +using namespace clang; +using namespace clang::tooling; + +namespace { + +template +class ImplementDeclaredMethodsOperation : public RefactoringOperation { +public: + ImplementDeclaredMethodsOperation( + const ClassType *Container, ArrayRef SelectedMethods) + : Container(Container), + SelectedMethods(SelectedMethods.begin(), SelectedMethods.end()) {} + + const Decl *getTransformedDecl() const override { + return SelectedMethods.front(); + } + + const Decl *getLastTransformedDecl() const override { + return SelectedMethods.back(); + } + + static RefactoringOperationResult + initiate(const ClassType *Container, ArrayRef Methods, + bool CreateOperation) { + if (Methods.empty()) + return std::nullopt; + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = std::make_unique(Container, Methods); + Result.RefactoringOp = std::move(Operation); + return Result; + } + + const ClassType *Container; + llvm::SmallVector SelectedMethods; +}; + +class ImplementDeclaredCXXMethodsOperation + : public ImplementDeclaredMethodsOperation< + CXXRecordDecl, CXXMethodDecl, ImplementDeclaredCXXMethodsOperation> { +public: + ImplementDeclaredCXXMethodsOperation( + const CXXRecordDecl *Container, + ArrayRef SelectedMethods) + : ImplementDeclaredMethodsOperation(Container, SelectedMethods) {} + + llvm::Expected + perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + static void addInlineBody(const CXXMethodDecl *MD, const ASTContext &Context, + std::vector &Replacements); + + static llvm::Expected runInImplementationAST( + ASTContext &Context, const FileID &File, const CXXRecordDecl *Class, + ArrayRef> SelectedMethods); +}; + +class ImplementDeclaredObjCMethodsOperation + : public ImplementDeclaredMethodsOperation< + ObjCContainerDecl, ObjCMethodDecl, + ImplementDeclaredObjCMethodsOperation> { + const ObjCInterfaceDecl *Interface; + +public: + ImplementDeclaredObjCMethodsOperation( + const ObjCContainerDecl *Container, + ArrayRef SelectedMethods) + : ImplementDeclaredMethodsOperation(Container, SelectedMethods) { + if (const auto *CD = dyn_cast(Container)) + Interface = CD->getClassInterface(); + else + Interface = nullptr; + } + + llvm::Expected + perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + static llvm::Expected runInImplementationAST( + ASTContext &Context, const FileID &File, + const ObjCContainerDecl *Container, const ObjCInterfaceDecl *Interface, + ArrayRef MethodDeclarations, + ArrayRef> SelectedMethods); +}; + +/// Returns true if the given Objective-C method has an implementation. +bool isImplemented(const ObjCMethodDecl *M) { + if (M->hasBody() || M->isDefined()) + return true; + return false; +} + +} // end anonymous namespace + +RefactoringOperationResult +clang::tooling::initiateImplementDeclaredMethodsOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + // Find the selected Class. + auto SelectedDecl = Slice.innermostSelectedDecl([](const Decl *D) { + return isa(D) || isa(D) || + isa(D); + }); + if (!SelectedDecl) + return std::nullopt; + // Look at the set of methods that intersect with the selection. + if (const auto *CXXClass = dyn_cast(SelectedDecl->getDecl())) { + if (CXXClass->isDependentType()) + return RefactoringOperationResult("templates are unsupported"); + llvm::SmallVector SelectedMethods; + for (const CXXMethodDecl *M : CXXClass->methods()) { + if (M->isImplicit() || M->hasBody() || M->isPureVirtual() || M->isDefaulted() || + M->isDeletedAsWritten() || M->getDescribedFunctionTemplate()) + continue; + if (Slice.isSourceRangeSelected( + CharSourceRange::getTokenRange(M->getSourceRange()))) + SelectedMethods.push_back(M); + } + return ImplementDeclaredCXXMethodsOperation::initiate( + CXXClass, SelectedMethods, CreateOperation); + } + const ObjCContainerDecl *Container = + cast(SelectedDecl->getDecl()); + llvm::SmallVector SelectedMethods; + for (const ObjCMethodDecl *M : Container->methods()) { + if (M->isImplicit() || isImplemented(M)) + continue; + if (Slice.isSourceRangeSelected( + CharSourceRange::getTokenRange(M->getSourceRange()))) + SelectedMethods.push_back(M); + } + // Method declarations from class extensions should be defined in class + // @implementations. + if (const auto *Category = dyn_cast(Container)) { + if (Category->IsClassExtension()) + Container = Category->getClassInterface(); + } + return ImplementDeclaredObjCMethodsOperation::initiate( + Container, SelectedMethods, CreateOperation); +} + +static bool isInLocalScope(const Decl *D) { + const DeclContext *LDC = D->getLexicalDeclContext(); + while (true) { + if (LDC->isFunctionOrMethod()) + return true; + if (!isa(LDC)) + return false; + if (const auto *CRD = dyn_cast(LDC)) + if (CRD->isLambda()) + return true; + LDC = LDC->getLexicalParent(); + } + return false; +} + +llvm::Expected +ImplementDeclaredCXXMethodsOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + if (isInLocalScope(Container)) { + // Local methods can be implemented inline. + std::vector Replacements; + for (const CXXMethodDecl *MD : SelectedMethods) + addInlineBody(MD, Context, Replacements); + return std::move(Replacements); + } + using namespace indexer; + return continueInExternalASTUnit( + fileThatShouldContainImplementationOf(Container), runInImplementationAST, + Container, filter(ArrayRef(SelectedMethods), + [](const DeclEntity &D) { return !D.isDefined(); })); +} + +void ImplementDeclaredCXXMethodsOperation::addInlineBody( + const CXXMethodDecl *MD, const ASTContext &Context, + std::vector &Replacements) { + SourceLocation EndLoc = MD->getEndLoc(); + SourceRange SemiRange = getRangeOfNextToken( + EndLoc, tok::semi, Context.getSourceManager(), Context.getLangOpts()); + if (SemiRange.isValid()) { + Replacements.push_back(RefactoringReplacement(SemiRange)); + EndLoc = SemiRange.getEnd(); + } + SourceLocation InsertionLoc = getLastLineLocationUnlessItHasOtherTokens( + EndLoc, Context.getSourceManager(), Context.getLangOpts()); + Replacements.push_back( + RefactoringReplacement(SourceRange(InsertionLoc, InsertionLoc), + StringRef(" { \n <#code#>;\n}"))); +} + +static const RecordDecl *findOutermostRecord(const RecordDecl *RD) { + const RecordDecl *Result = RD; + for (const DeclContext *DC = Result->getLexicalDeclContext(); + isa(DC); DC = Result->getLexicalDeclContext()) + Result = cast(DC); + return Result; +} + +static bool containsUsingOf(const NamespaceDecl *ND, + const ASTContext &Context) { + for (const Decl *D : Context.getTranslationUnitDecl()->decls()) { + if (const auto *UDD = dyn_cast(D)) { + if (UDD->getNominatedNamespace() == ND) + return true; + } + } + return false; +} + +llvm::Expected +ImplementDeclaredCXXMethodsOperation::runInImplementationAST( + ASTContext &Context, const FileID &File, const CXXRecordDecl *Class, + ArrayRef> SelectedMethods) { + if (!Class) + return llvm::make_error( + "the target class is not defined in the continuation AST unit"); + + SourceManager &SM = Context.getSourceManager(); + + // Find the defined methods of the class. + llvm::SmallVector DefinedOutOfLineMethods; + for (const CXXMethodDecl *M : Class->methods()) { + if (M->isImplicit()) + continue; + if (const FunctionDecl *MD = M->getDefinition()) { + if (!MD->isOutOfLine()) + continue; + SourceLocation Loc = SM.getExpansionLoc(MD->getBeginLoc()); + if (SM.getFileID(Loc) == File) + DefinedOutOfLineMethods.push_back(cast(MD)); + } + } + + std::vector Replacements; + std::string MethodString; + llvm::raw_string_ostream OS(MethodString); + + // Pick a good insertion location. + SourceLocation InsertionLoc; + const CXXMethodDecl *InsertAfterMethod = nullptr; + NestedNameSpecifier *NamePrefix = nullptr; + if (DefinedOutOfLineMethods.empty()) { + const RecordDecl *OutermostRecord = findOutermostRecord(Class); + InsertionLoc = SM.getExpansionRange(OutermostRecord->getEndLoc()).getEnd(); + if (SM.getFileID(InsertionLoc) == File) { + // We can insert right after the class. Compute the appropriate + // qualification. + NamePrefix = NestedNameSpecifier::getRequiredQualification( + Context, OutermostRecord->getLexicalDeclContext(), + Class->getLexicalDeclContext()); + } else { + // We can't insert after the end of the class, since the indexer told us + // that some file should have the implementation of it, even when there + // are no methods here. We should try to insert at the end of the file. + InsertionLoc = SM.getLocForEndOfFile(File); + NamePrefix = NestedNameSpecifier::getRequiredQualification( + Context, Context.getTranslationUnitDecl(), + Class->getLexicalDeclContext()); + llvm::SmallVector Namespaces; + for (const NestedNameSpecifier *Qualifier = NamePrefix; Qualifier; + Qualifier = Qualifier->getPrefix()) { + if (const NamespaceDecl *ND = Qualifier->getAsNamespace()) + Namespaces.push_back(ND); + } + // When the class is in a namespace, add a 'using' declaration if it's + // needed and adjust the out-of-line qualification. + if (!Namespaces.empty()) { + const NamespaceDecl *InnermostNamespace = Namespaces[0]; + if (!containsUsingOf(InnermostNamespace, Context)) { + std::string NamespaceString; + llvm::raw_string_ostream NamespaceOS(NamespaceString); + for (const NamespaceDecl *ND : llvm::reverse(Namespaces)) { + if (!NamespaceOS.str().empty()) + NamespaceOS << "::"; + NamespaceOS << ND->getDeclName(); + } + OS << "\nusing namespace " << NamespaceOS.str() << ";"; + } + // Re-compute the name qualifier without the namespace. + NamePrefix = NestedNameSpecifier::getRequiredQualification( + Context, InnermostNamespace, Class->getLexicalDeclContext()); + } + } + } else { + // Insert at the end of the defined methods. + for (const CXXMethodDecl *M : DefinedOutOfLineMethods) { + SourceLocation EndLoc = SM.getExpansionRange(M->getEndLoc()).getEnd(); + if (InsertionLoc.isInvalid() || + SM.isBeforeInTranslationUnit(InsertionLoc, EndLoc)) { + InsertionLoc = EndLoc; + InsertAfterMethod = M; + } + } + } + InsertionLoc = getLastLineLocationUnlessItHasOtherTokens( + InsertionLoc, SM, Context.getLangOpts()); + + PrintingPolicy PP = Context.getPrintingPolicy(); + PP.PolishForDeclaration = true; + PP.SupressStorageClassSpecifiers = true; + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + OS << "\n"; + for (const auto &I : SelectedMethods) { + const CXXMethodDecl *MD = I.Decl; + // Check if the method is already defined. + if (!MD) + continue; + + // Drop the 'virtual' specifier. + bool IsVirtual = MD->isVirtualAsWritten(); + const_cast(MD)->setVirtualAsWritten(false); + + // Drop the default arguments. + llvm::SmallVector, 4> DefaultArgs; + for (const ParmVarDecl *P : MD->parameters()) { + if (!P->hasDefaultArg()) + continue; + Expr *E = const_cast(P)->getDefaultArg(); + const_cast(P)->setDefaultArg(nullptr); + DefaultArgs.emplace_back(const_cast(P), E); + } + + // Add the nested name specifiers that are appropriate for an out-of-line + // method. + auto *Qualifier = + InsertAfterMethod + ? InsertAfterMethod->getQualifier() + : NestedNameSpecifier::Create( + Context, /*Prefix=*/NamePrefix, + Context.getRecordType(Class).getTypePtr()); + NestedNameSpecifierLoc PrevQualifierInfo = MD->getQualifierLoc(); + const_cast(MD)->setQualifierInfo( + NestedNameSpecifierLoc(Qualifier, /*Loc=*/nullptr)); + + OS << "\n"; + MD->print(OS, PP); + OS << " { \n <#code#>;\n}\n"; + + // Restore the original method + for (const auto &DefaultArg : DefaultArgs) + DefaultArg.first->setDefaultArg(DefaultArg.second); + const_cast(MD)->setVirtualAsWritten(IsVirtual); + const_cast(MD)->setQualifierInfo(PrevQualifierInfo); + } + + Replacements.push_back(RefactoringReplacement( + SourceRange(InsertionLoc, InsertionLoc), std::move(OS.str()))); + + return std::move(Replacements); +} + +llvm::Expected +ImplementDeclaredObjCMethodsOperation::perform( + ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) { + using namespace indexer; + + // Print the methods before running the continuation because the continuation + // TU might not have these method declarations (e.g. category implemented in + // the class implementation). + PrintingPolicy PP = Context.getPrintingPolicy(); + PP.PolishForDeclaration = true; + PP.SuppressStrongLifetime = true; + PP.SuppressLifetimeQualifiers = true; + PP.SuppressUnwrittenScope = true; + std::vector MethodDeclarations; + for (const ObjCMethodDecl *MD : SelectedMethods) { + std::string MethodDeclStr; + llvm::raw_string_ostream MethodOS(MethodDeclStr); + MD->print(MethodOS, PP); + MethodDeclarations.push_back(std::move(MethodOS.str())); + } + + return continueInExternalASTUnit( + fileThatShouldContainImplementationOf(Container), runInImplementationAST, + Container, Interface, MethodDeclarations, + filter(ArrayRef(SelectedMethods), + [](const DeclEntity &D) { return !D.isDefined(); })); +} + +static const ObjCImplDecl * +getImplementationContainer(const ObjCContainerDecl *Container, + const ObjCInterfaceDecl *Interface = nullptr) { + if (!Container) + return Interface ? getImplementationContainer(Interface) : nullptr; + if (const auto *ID = dyn_cast(Container)) + return ID->getImplementation(); + if (const auto *CD = dyn_cast(Container)) { + if (const auto *Impl = CD->getImplementation()) + return Impl; + return getImplementationContainer(Interface); + } + return nullptr; +} + +llvm::Expected +ImplementDeclaredObjCMethodsOperation::runInImplementationAST( + ASTContext &Context, const FileID &File, const ObjCContainerDecl *Container, + const ObjCInterfaceDecl *Interface, + ArrayRef MethodDeclarations, + ArrayRef> SelectedMethods) { + const ObjCImplDecl *ImplementationContainer = + getImplementationContainer(Container, Interface); + if (!ImplementationContainer) + return llvm::make_error( + "the target @interface is not implemented in the continuation AST " + "unit"); + + std::vector Replacements; + + std::string MethodString; + llvm::raw_string_ostream OS(MethodString); + + assert(MethodDeclarations.size() >= SelectedMethods.size() && + "fewer declarations than selected methods?"); + for (const auto &I : llvm::enumerate(SelectedMethods)) { + indexer::Indexed Decl = I.value(); + // Skip methods that are already defined. + if (!Decl.isNotDefined()) + continue; + + OS << StringRef(MethodDeclarations[I.index()]).drop_back(); // Drop the ';' + OS << " { \n <#code#>;\n}\n\n"; + } + SourceLocation InsertionLoc = ImplementationContainer->getEndLoc(); + + Replacements.push_back(RefactoringReplacement( + SourceRange(InsertionLoc, InsertionLoc), std::move(OS.str()))); + + return std::move(Replacements); +} diff --git a/clang/lib/Tooling/Refactor/IndexerQueries.cpp b/clang/lib/Tooling/Refactor/IndexerQueries.cpp new file mode 100644 index 0000000000000..9a891de380183 --- /dev/null +++ b/clang/lib/Tooling/Refactor/IndexerQueries.cpp @@ -0,0 +1,171 @@ +//===--- IndexerQueries.cpp - Indexer queries -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactoring/RefactoringDiagnostic.h" +#include "clang/Tooling/Refactor/IndexerQuery.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace clang; +using namespace clang::tooling; +using namespace clang::tooling::indexer; +using namespace clang::tooling::indexer::detail; +using namespace llvm::yaml; + +const char *ASTProducerQuery::BaseUIDString = "ast.producer.query"; +const char *DeclarationsQuery::BaseUIDString = "decl.query"; +const char *ASTUnitForImplementationOfDeclarationQuery::NameUIDString = + "file.for.impl.of.decl"; + +const char *DeclPredicateNodePredicate::NameUIDString = "decl.predicate"; +const char *DeclPredicateNotPredicate::NameUIDString = "not.decl.predicate"; + +std::unique_ptr +DeclPredicateNode::create(const DeclPredicate &Predicate) { + return std::make_unique(Predicate); +} + +std::unique_ptr +DeclPredicateNode::create(const BoolDeclPredicate &Predicate) { + if (Predicate.IsInverted) + return std::make_unique( + create(Predicate.Predicate)); + return create(Predicate.Predicate); +} + +std::unique_ptr +clang::tooling::indexer::fileThatShouldContainImplementationOf(const Decl *D) { + return std::make_unique(D); +} + +bool ASTUnitForImplementationOfDeclarationQuery::verify(ASTContext &Context) { + if (!D) { + assert(false && "Query should be verified before persisting"); + return false; + } + // Check if we've got the filename. + if (!Result.Filename.empty()) + return false; + Context.getDiagnostics().Report( + D->getLocation(), diag::err_ref_continuation_missing_implementation) + << isa(D) << cast(D); + return true; +} + +bool DeclarationsQuery::verify(ASTContext &Context) { + if (Input.empty()) { + assert(false && "Query should be verified before persisting"); + return false; + } + if (!Output.empty()) { + // At least one output declaration must be valid. + for (const auto &Ref : Output) { + if (!Ref.Decl.USR.empty()) + return false; + } + } + // FIXME: This is too specific, the new refactoring engine at llvm.org should + // generalize this. + Context.getDiagnostics().Report( + Input[0]->getLocation(), + diag::err_implement_declared_methods_all_implemented); + return true; +} + +namespace { + +struct QueryPredicateNode { + std::string Name; + std::vector IntegerValues; +}; + +struct QueryYAMLNode { + std::string Name; + std::vector PredicateResults; + std::string FilenameResult; +}; + +} // end anonymous namespace + +LLVM_YAML_IS_SEQUENCE_VECTOR(QueryPredicateNode) +LLVM_YAML_IS_SEQUENCE_VECTOR(QueryYAMLNode) + +namespace llvm { +namespace yaml { + +template <> struct MappingTraits { + static void mapping(IO &Yaml, QueryPredicateNode &Predicate) { + Yaml.mapRequired("name", Predicate.Name); + Yaml.mapRequired("intValues", Predicate.IntegerValues); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &Yaml, QueryYAMLNode &Query) { + Yaml.mapRequired("name", Query.Name); + Yaml.mapOptional("predicateResults", Query.PredicateResults); + Yaml.mapOptional("filenameResult", Query.FilenameResult); + // FIXME: Report an error if no results are provided at all. + } +}; + +} // end namespace yaml +} // end namespace llvm + +llvm::Error +IndexerQuery::loadResultsFromYAML(StringRef Source, + ArrayRef Queries) { + std::vector QueryResults; + Input YamlIn(Source); + YamlIn >> QueryResults; + if (YamlIn.error()) + return llvm::make_error("Failed to parse query results", + YamlIn.error()); + if (QueryResults.size() != Queries.size()) + return llvm::make_error("Mismatch in query results size", + llvm::errc::invalid_argument); + for (auto QueryTuple : llvm::zip(Queries, QueryResults)) { + IndexerQuery *Query = std::get<0>(QueryTuple); + const QueryYAMLNode &Result = std::get<1>(QueryTuple); + if ((Query->NameUID && Query->NameUID != Result.Name) && + (Query->BaseUID && Query->BaseUID != Result.Name)) + continue; + if (auto *DQ = dyn_cast(Query)) { + const DeclPredicateNode &Predicate = DQ->getPredicateNode(); + DeclPredicate ActualPredicate(""); + bool IsNot = false; + if (const auto *Not = dyn_cast(&Predicate)) { + ActualPredicate = + cast(Not->getChild()).getPredicate(); + IsNot = true; + } else + ActualPredicate = + cast(Predicate).getPredicate(); + for (const auto &PredicateResult : Result.PredicateResults) { + if (PredicateResult.Name != ActualPredicate.Name) + continue; + std::vector>> Output; + for (auto ResultTuple : + zip(DQ->getInputs(), PredicateResult.IntegerValues)) { + const Decl *D = std::get<0>(ResultTuple); + int Result = std::get<1>(ResultTuple); + bool Value = (IsNot ? !Result : !!Result); + Output.push_back(Indexed>( + PersistentDeclRef::create(Value ? D : nullptr), + Value ? QueryBoolResult::Yes : QueryBoolResult::No)); + } + DQ->setOutput(std::move(Output)); + break; + } + } else if (auto *AQ = + dyn_cast(Query)) + AQ->setResult(Result.FilenameResult); + } + return llvm::Error::success(); +} diff --git a/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp b/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp new file mode 100644 index 0000000000000..b38fb881a7e24 --- /dev/null +++ b/clang/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp @@ -0,0 +1,82 @@ +//===--- LocalizeObjCString.cpp - ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements the "Wrap in NSLocalizedString" refactoring operation. +// +//===----------------------------------------------------------------------===// + +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" + +using namespace clang; +using namespace clang::tooling; + +namespace { + +class LocalizeObjCStringLiteralOperation : public RefactoringOperation { +public: + LocalizeObjCStringLiteralOperation(const ObjCStringLiteral *E) : E(E) {} + + const Stmt *getTransformedStmt() const override { return E; } + + llvm::Expected perform(ASTContext &Context, const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) override; + + const ObjCStringLiteral *E; +}; + +} // end anonymous namespace + +RefactoringOperationResult +clang::tooling::initiateLocalizeObjCStringLiteralOperation( + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, + SourceRange SelectionRange, bool CreateOperation) { + const ObjCStringLiteral *E; + if (SelectionRange.isValid()) { + auto SelectedSet = Slice.getSelectedStmtSet(); + if (!SelectedSet) + return std::nullopt; + E = dyn_cast_or_null( + SelectedSet->containsSelectionRange); + } else + E = cast_or_null( + Slice.nearestStmt(Stmt::ObjCStringLiteralClass)); + if (!E) + return std::nullopt; + + RefactoringOperationResult Result; + Result.Initiated = true; + if (!CreateOperation) + return Result; + auto Operation = std::make_unique(E); + Result.RefactoringOp = std::move(Operation); + return Result; +} + +llvm::Expected +LocalizeObjCStringLiteralOperation::perform(ASTContext &Context, + const Preprocessor &ThePreprocessor, + const RefactoringOptionSet &Options, + unsigned SelectedCandidateIndex) { + std::vector Replacements; + // TODO: New API: Replace by something like Node.wrap("NSLocalizedString(", ", + // @""") + SourceLocation LocStart = + Context.getSourceManager().getSpellingLoc(E->getBeginLoc()); + Replacements.emplace_back(SourceRange(LocStart, LocStart), + StringRef("NSLocalizedString(")); + SourceLocation LocEnd = getPreciseTokenLocEnd( + Context.getSourceManager().getSpellingLoc(E->getEndLoc()), + Context.getSourceManager(), Context.getLangOpts()); + Replacements.emplace_back(SourceRange(LocEnd, LocEnd), StringRef(", @\"\")")); + return std::move(Replacements); +} diff --git a/clang/lib/Tooling/Refactor/RefactoringActionFinder.cpp b/clang/lib/Tooling/Refactor/RefactoringActionFinder.cpp new file mode 100644 index 0000000000000..74c73afc811f8 --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringActionFinder.cpp @@ -0,0 +1,56 @@ +//===--- RefactoringActionFinder.cpp - Clang refactoring library ----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Implements methods that find the refactoring actions that can be +/// performed at specific locations / source ranges in a translation unit. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RefactoringActionFinder.h" +#include "clang/Tooling/Refactor/RefactoringOperation.h" +#include "clang/Tooling/Refactor/USRFinder.h" + +namespace clang { +namespace tooling { + +RefactoringActionSet findActionSetAt(SourceLocation Location, + SourceRange SelectionRange, + ASTContext &Context) { + RefactoringActionSet Result; + if (const auto *ND = rename::getNamedDeclAt(Context, Location)) + Result.Actions.push_back(isLocalSymbol(ND, Context.getLangOpts()) + ? RefactoringActionType::Rename_Local + : RefactoringActionType::Rename); + + // FIXME: We can avoid checking if some actions can be initiated when they're + // not allowed in the current language mode. + RefactoringActionType Actions[] = { +#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command) \ + RefactoringActionType::Name, +#include "clang/Tooling/Refactor/RefactoringActions.def" + }; + + for (auto Action : Actions) { + auto Op = initiateRefactoringOperationAt(Location, SelectionRange, Context, + Action, + /*CreateOperation=*/true); + if (Op.Initiated) { + Result.Actions.push_back(Action); + if (Op.RefactoringOp) { + for (const auto &SubAction : Op.RefactoringOp->getAvailableSubActions()) + Result.Actions.push_back(SubAction); + } + } + } + + return Result; +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/RefactoringActions.cpp b/clang/lib/Tooling/Refactor/RefactoringActions.cpp new file mode 100644 index 0000000000000..18170506a4aaa --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringActions.cpp @@ -0,0 +1,31 @@ +//===--- RefactoringActions.cpp - Clang refactoring library ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Contains a list of all the supported refactoring actions. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RefactoringActions.h" +#include "llvm/Support/ErrorHandling.h" + +namespace clang { +namespace tooling { + +StringRef getRefactoringActionTypeName(RefactoringActionType Action) { + switch (Action) { +#define REFACTORING_ACTION(Name, Spelling) \ + case RefactoringActionType::Name: \ + return Spelling; +#include "clang/Tooling/Refactor/RefactoringActions.def" + } + llvm_unreachable("unexpected RefactoringActionType value"); +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/RefactoringContinuations.h b/clang/lib/Tooling/Refactor/RefactoringContinuations.h new file mode 100644 index 0000000000000..0717ac0500ba6 --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringContinuations.h @@ -0,0 +1,398 @@ +//===--- RefactoringContinuations.h - Defines refactoring continuations ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORING_CONTINUATIONS_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORING_CONTINUATIONS_H + +#include "clang/AST/Decl.h" +#include "clang/Tooling/Refactor/IndexerQuery.h" +#include "clang/Tooling/Refactor/RefactoringOperation.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/Optional.h" +#include + +namespace clang { +namespace tooling { + +namespace detail { + +struct ValidBase {}; + +/// The ContinuationPassType determine which type is passed into the refactoring +/// continuation. +template struct ContinuationPassType { using Type = T; }; + +template struct ContinuationPassType> { + using Type = ArrayRef; +}; + +/// Refactoring operations can pass state to the continuations. Valid state +/// values should have a corresponding \c StateTraits specialization. +template struct StateTraits { + /// Specializations should define the following types: + /// + /// StoredResultType: The TU-specific type which is then passed into the + /// continuation function. The continuation receives the result whose type is + /// \c ContinuationPassType::Type. + /// + /// PersistentType: The TU-independent type that's persisted even after the + /// TU in which the continuation was created is disposed. +}; + +template +struct StateTraits + : std::enable_if::value, ValidBase>::type { + using StoredResultType = const T *; + using PersistentType = PersistentDeclRef; +}; + +template +struct StateTraits> + : std::enable_if::value, ValidBase>::type { + using StoredResultType = std::vector; + using PersistentType = std::vector>; +}; + +template +struct StateTraits>> + : std::enable_if::value, ValidBase>::type { + using StoredResultType = std::vector>; + using PersistentType = std::vector>>; +}; + +template <> struct StateTraits> { + using StoredResultType = std::vector; + using PersistentType = std::vector; +}; + +/// Conversion functions convert the TU-specific state to a TU independent +/// state and vice-versa. +template +PersistentDeclRef convertToPersistentRepresentation( + const T *Declaration, + typename std::enable_if::value>::type * = + nullptr) { + return PersistentDeclRef::create(Declaration); +} + +template +std::vector> convertToPersistentRepresentation( + ArrayRef Declarations, + typename std::enable_if::value>::type * = + nullptr) { + std::vector> Result; + Result.reserve(Declarations.size()); + for (const T *D : Declarations) + Result.push_back(PersistentDeclRef::create(D)); + return Result; +} + +template +std::vector>> +convertToPersistentRepresentation( + std::unique_ptr> &Query, + typename std::enable_if::value>::type * = + nullptr) { + Query->invalidateTUSpecificState(); + return Query->getOutput(); +} + +inline std::vector +convertToPersistentRepresentation(const std::vector &Values) { + return Values; +} + +/// Converts the TU-independent state to the TU-specific state. +class PersistentToASTSpecificStateConverter { + ASTContext &Context; + llvm::StringMap ConvertedDeclRefs; + + const Decl *lookupDecl(StringRef USR); + +public: + // FIXME: We can hide the addConvertible/convert interface so that + // the continuation will just invoke one conversion function for the entire + // tuple. + PersistentToASTSpecificStateConverter(ASTContext &Context) + : Context(Context) {} + + template + bool addConvertible( + const PersistentDeclRef &Ref, + typename std::enable_if::value>::type * = + nullptr) { + if (!Ref.USR.empty()) + ConvertedDeclRefs[Ref.USR] = nullptr; + return true; + } + + template + const T * + convert(const PersistentDeclRef &Ref, + typename std::enable_if::value>::type * = + nullptr) { + return dyn_cast_or_null(lookupDecl(Ref.USR)); + } + + template + bool addConvertible( + const std::vector> &Refs, + typename std::enable_if::value>::type * = + nullptr) { + for (const auto &Ref : Refs) { + if (!Ref.USR.empty()) + ConvertedDeclRefs[Ref.USR] = nullptr; + } + return true; + } + + template + std::vector + convert(const std::vector> &Refs, + typename std::enable_if::value>::type * = + nullptr) { + std::vector Results; + Results.reserve(Refs.size()); + // Allow nulls in the produced array, the continuation will have to deal + // with them by itself. + for (const auto &Ref : Refs) + Results.push_back(dyn_cast_or_null(lookupDecl(Ref.USR))); + return Results; + } + + template + bool addConvertible( + const std::vector>> &Refs, + typename std::enable_if::value>::type * = + nullptr) { + for (const auto &Ref : Refs) { + if (!Ref.Decl.USR.empty()) + ConvertedDeclRefs[Ref.Decl.USR] = nullptr; + } + return true; + } + + template + std::vector> + convert(const std::vector>> &Refs, + typename std::enable_if::value>::type * = + nullptr) { + std::vector> Results; + Results.reserve(Refs.size()); + // Allow nulls in the produced array, the continuation will have to deal + // with them by itself. + for (const auto &Ref : Refs) + Results.push_back(indexer::Indexed( + dyn_cast_or_null(lookupDecl(Ref.Decl.USR)), Ref.IsNotDefined)); + return Results; + } + + bool addConvertible(const PersistentFileID &) { + // Do nothing since FileIDs are converted one-by-one. + return true; + } + + FileID convert(const PersistentFileID &Ref); + + bool addConvertible(const std::vector &) { return true; } + + std::vector convert(const std::vector &Values) { + return Values; + } + + /// Converts the added persistent state into TU-specific state using one + /// efficient operation. + void runCoalescedConversions(); +}; + +template +struct ContinuationFunction { + using Type = llvm::Expected (*)( + ASTContext &, const T &, + typename ContinuationPassType< + typename StateTraits::StoredResultType>::Type...); + + template + static llvm::Expected dispatch( + Type Fn, detail::PersistentToASTSpecificStateConverter &Converter, + ASTContext &Context, const ASTQueryType &Query, + const std::tuple::StoredResultType...> + &Arguments, + std::index_sequence) { + auto ASTQueryResult = Converter.convert(Query.getResult()); + return Fn(Context, ASTQueryResult, std::get(Arguments)...); + } +}; + +template +struct ContinuationFunction { + using Type = llvm::Expected (*)( + ASTContext &, + typename ContinuationPassType< + typename StateTraits::StoredResultType>::Type...); + + template + static llvm::Expected dispatch( + Type Fn, detail::PersistentToASTSpecificStateConverter &, + ASTContext &Context, const ASTQueryType &, + const std::tuple::StoredResultType...> + &Arguments, + std::index_sequence) { + return Fn(Context, std::get(Arguments)...); + } +}; + +/// The refactoring contination contains a set of structures that implement +/// the refactoring operation continuation mechanism. +template +class SpecificRefactoringContinuation final : public RefactoringContinuation { +public: + static_assert(std::is_base_of::value, + "Invalid AST Query"); + // TODO: Validate the QueryOrState types. + + /// The consumer function is the actual continuation. It receives the state + /// that was passed-in in the request or the results of the indexing queries + /// that were passed-in in the request. + using ConsumerFn = + typename ContinuationFunction::Type; + +private: + ConsumerFn Consumer; + std::unique_ptr ASTQuery; + /// Inputs store state that's dependent on the original TU. + std::optional> Inputs; + /// State contains TU-independent values. + std::optional< + std::tuple::PersistentType...>> + State; + + /// Converts a tuple that contains the TU dependent state to a tuple with + /// TU independent state. + template + std::tuple::PersistentType...> + convertToPersistentImpl(std::index_sequence) { + assert(Inputs && "TU-dependent state is already converted"); + return std::make_tuple( + detail::convertToPersistentRepresentation(std::get(*Inputs))...); + } + + template + bool gatherQueries( + std::vector &Queries, + const std::unique_ptr &Query, + typename std::enable_if< + std::is_base_of::value>::type * = nullptr) { + Queries.push_back(Query.get()); + return true; + } + + template + bool gatherQueries(std::vector &, const T &) { + // This input element is not a query. + return true; + } + + template + std::vector + gatherQueries(std::index_sequence) { + assert(Inputs && "TU-dependent state is already converted"); + std::vector Queries; + std::make_tuple(gatherQueries(Queries, std::get(*Inputs))...); + return Queries; + } + + /// Calls the consumer function with the given \p Context and the state + /// whose values are converted from the TU-independent to TU-specific values. + template + llvm::Expected dispatch(ASTContext &Context, + std::index_sequence Seq) { + assert(State && "TU-independent state is not yet produced"); + detail::PersistentToASTSpecificStateConverter Converter(Context); + (void)std::make_tuple(Converter.addConvertible(std::get(*State))...); + Converter.runCoalescedConversions(); + auto Results = std::make_tuple(Converter.convert(std::get(*State))...); + // TODO: Check for errors? + return detail::ContinuationFunction< + typename ASTQueryType::ResultTy, ASTQueryType, + QueryOrState...>::dispatch(Consumer, Converter, Context, *ASTQuery, + Results, Seq); + } + +public: + SpecificRefactoringContinuation(ConsumerFn Consumer, + std::unique_ptr ASTQuery, + QueryOrState... Inputs) + : Consumer(Consumer), ASTQuery(std::move(ASTQuery)), + Inputs(std::make_tuple(std::move(Inputs)...)) {} + + SpecificRefactoringContinuation(SpecificRefactoringContinuation &&) = default; + SpecificRefactoringContinuation & + operator=(SpecificRefactoringContinuation &&) = default; + + indexer::ASTProducerQuery *getASTUnitIndexerQuery() override { + return ASTQuery.get(); + } + + std::vector getAdditionalIndexerQueries() override { + return gatherQueries(std::index_sequence_for()); + } + + /// Query results are fetched. State is converted to a persistent + /// representation. + void persistTUSpecificState() override { + ASTQuery->invalidateTUSpecificState(); + State = + convertToPersistentImpl(std::index_sequence_for()); + Inputs = std::nullopt; + } + + /// The state is converted to the AST representation in the given ASTContext + /// and the continuation is dispatched. + llvm::Expected + runInExternalASTUnit(ASTContext &Context) override { + return dispatch(Context, std::index_sequence_for()); + } +}; + +} // end namespace detail + +/// Returns a refactoring continuation that will run within the context of a +/// single external AST unit. +/// +/// The indexer determines which AST unit should receive the continuation by +/// evaluation the AST query operation \p ASTQuery. +/// +/// \param ASTQuery The query that will determine which AST unit should the +/// continuation run in. +/// +/// \param Consumer The continuation function that will be called once the +/// external AST unit is loaded. +/// +/// \param Inputs Each individiual input element can contain either some +/// state value that will be passed into the \p Consumer function or an +/// indexer query whose results will be passed into the \p Consumer function. +template +typename std::enable_if< + std::is_base_of::value, + std::unique_ptr>::type +continueInExternalASTUnit( + std::unique_ptr ASTQuery, + typename detail::SpecificRefactoringContinuation< + ASTQueryType, QueryOrState...>::ConsumerFn Consumer, + QueryOrState... Inputs) { + return std::make_unique< + detail::SpecificRefactoringContinuation>( + Consumer, std::move(ASTQuery), std::move(Inputs)...); +} + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORING_CONTINUATIONS_H diff --git a/clang/lib/Tooling/Refactor/RefactoringOperation.cpp b/clang/lib/Tooling/Refactor/RefactoringOperation.cpp new file mode 100644 index 0000000000000..83a78592c854a --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringOperation.cpp @@ -0,0 +1,92 @@ +//===--- RefactoringOperation.cpp - Defines a refactoring operation -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RefactoringOperation.h" +#include "ASTSlice.h" +#include "RefactoringOperations.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/Tooling/Refactor/SymbolOperation.h" +#include "clang/Tooling/Refactor/USRFinder.h" +#include "llvm/Support/Errc.h" + +using namespace clang; +using namespace clang::tooling; + +char RefactoringOperationError::ID; + +void RefactoringOperationError::log(raw_ostream &OS) const { + OS << "Refactoring operation failed: " << FailureReason; +} + +std::error_code RefactoringOperationError::convertToErrorCode() const { + return make_error_code(llvm::errc::operation_not_permitted); +} + +RefactoringOperationResult clang::tooling::initiateRefactoringOperationAt( + SourceLocation Location, SourceRange SelectionRange, ASTContext &Context, + RefactoringActionType ActionType, bool CreateOperation) { + if (Location.isInvalid()) + return std::nullopt; + if (ActionType == RefactoringActionType::Rename || + ActionType == RefactoringActionType::Rename_Local) { + const NamedDecl *FoundDecl = rename::getNamedDeclAt(Context, Location); + if (!FoundDecl) + return std::nullopt; + RefactoringOperationResult Result; + Result.Initiated = true; + if (CreateOperation) + Result.SymbolOp = std::make_unique(FoundDecl, Context); + return Result; + } + SourceManager &SM = Context.getSourceManager(); + if (Location.isMacroID()) + Location = SM.getSpellingLoc(Location); + assert(Location.isFileID() && "Invalid location"); + + // TODO: Don't perform duplicate work when initiateRefactoringOperationAt is + // called from findRefactoringActionsAt. + if (SelectionRange.isValid()) { + if (SelectionRange.getBegin().isMacroID() || + SelectionRange.getEnd().isMacroID()) + SelectionRange = SourceRange(SM.getSpellingLoc(SelectionRange.getBegin()), + SM.getSpellingLoc(SelectionRange.getEnd())); + SelectionRange = trimSelectionRange( + SelectionRange, Context.getSourceManager(), Context.getLangOpts()); + } + ASTSlice Slice(Location, SelectionRange, Context); + + switch (ActionType) { +#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command) \ + case RefactoringActionType::Name: \ + return initiate##Name##Operation(Slice, Context, Location, SelectionRange, \ + CreateOperation); +#define REFACTORING_OPERATION_SUB_ACTION(Name, Parent, Spelling, Command) \ + case RefactoringActionType::Parent##_##Name: \ + return initiate##Parent##Name##Operation(Slice, Context, Location, \ + SelectionRange, CreateOperation); +#include "clang/Tooling/Refactor/RefactoringActions.def" + default: + break; + } + return RefactoringOperationResult(); +} + +RefactoringOperationResult clang::tooling::initiateRefactoringOperationOnDecl( + StringRef DeclUSR, ASTContext &Context, RefactoringActionType ActionType) { + if (ActionType != RefactoringActionType::Rename) + return std::nullopt; + const NamedDecl *FoundDecl = rename::getNamedDeclWithUSR(Context, DeclUSR); + if (!FoundDecl) + return std::nullopt; + RefactoringOperationResult Result; + Result.Initiated = true; + Result.SymbolOp = std::make_unique(FoundDecl, Context); + return Result; +} diff --git a/clang/lib/Tooling/Refactor/RefactoringOperations.h b/clang/lib/Tooling/Refactor/RefactoringOperations.h new file mode 100644 index 0000000000000..242c8f2d4c449 --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringOperations.h @@ -0,0 +1,36 @@ +//===--- RefactoringOperations.h - The supported refactoring operations ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORINGOPERATIONS_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORINGOPERATIONS_H + +#include "ASTSlice.h" +#include "clang/Tooling/Refactor/RefactoringOperation.h" + +namespace clang { + +class Expr; +class IfStmt; +class VarDecl; + +namespace tooling { + +#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command) \ + RefactoringOperationResult initiate##Name##Operation( \ + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, \ + SourceRange SelectionRange, bool CreateOperation = true); +#define REFACTORING_OPERATION_SUB_ACTION(Name, Parent, Spelling, Command) \ + RefactoringOperationResult initiate##Parent##Name##Operation( \ + ASTSlice &Slice, ASTContext &Context, SourceLocation Location, \ + SourceRange SelectionRange, bool CreateOperation = true); +#include "clang/Tooling/Refactor/RefactoringActions.def" + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORINGOPERATIONS_H diff --git a/clang/lib/Tooling/Refactor/RefactoringOptions.cpp b/clang/lib/Tooling/Refactor/RefactoringOptions.cpp new file mode 100644 index 0000000000000..454a18c02d495 --- /dev/null +++ b/clang/lib/Tooling/Refactor/RefactoringOptions.cpp @@ -0,0 +1,68 @@ +//===--- RefactoringOptions.cpp - A set of all the refactoring options ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RefactoringOptions.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace clang; +using namespace clang::tooling; +using namespace clang::tooling::option; +using namespace llvm::yaml; + +void RefactoringOptionSet::print(llvm::raw_ostream &OS) const { + Output YamlOut(OS); + if (YamlOut.preflightDocument(0)) { + YamlOut.beginFlowMapping(); + for (const auto &Option : Options) + Option.getValue()->serialize(YamlOut); + YamlOut.endFlowMapping(); + YamlOut.postflightDocument(); + } +} + +namespace llvm { +namespace yaml { +template <> struct CustomMappingTraits { + static void inputOne(IO &YamlIn, StringRef Key, + RefactoringOptionSet &Result) { +#define HANDLE(Type) \ + if (Key == Type::Name) { \ + Type Value; \ + Value.serialize(YamlIn); \ + Result.add(Value); \ + return; \ + } + HANDLE(AvoidTextualMatches) +#undef HANDLE + YamlIn.setError(Twine("Unknown refactoring option ") + Key); + } + static void output(IO &, RefactoringOptionSet &) { + llvm_unreachable("Output is done without mapping traits"); + } +}; +} +} + +llvm::Expected +RefactoringOptionSet::parse(StringRef Source) { + Input YamlIn(Source); + // FIXME: Don't dump errors to stderr. + RefactoringOptionSet Result; + YamlIn >> Result; + if (YamlIn.error()) + return llvm::make_error("Failed to parse the option set", + YamlIn.error()); + return std::move(Result); +} + +void OldRefactoringOption::serialize(const SerializationContext &) {} + +void clang::tooling::option::detail::BoolOptionBase::serializeImpl( + const SerializationContext &Context, const char *Name) { + Context.IO.mapRequired(Name, Value); +} diff --git a/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp new file mode 100644 index 0000000000000..4407512122989 --- /dev/null +++ b/clang/lib/Tooling/Refactor/RenameIndexedFile.cpp @@ -0,0 +1,605 @@ +//===--- RenameIndexedFile.cpp - ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RenameIndexedFile.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Lex/LiteralSupport.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Tooling/Refactor/RefactoringOptions.h" +#include "clang/Tooling/Refactoring/Rename/RenamingAction.h" +#include "clang/Tooling/Syntax/Tokens.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Path.h" + +using namespace clang; + +namespace clang { +namespace tooling { +namespace rename { + +IndexedFileOccurrenceProducer::IndexedFileOccurrenceProducer( + ArrayRef Symbols, IndexedFileOccurrenceConsumer &Consumer, + IndexedFileRenamerLock &Lock, const RefactoringOptionSet *Options) + : Symbols(Symbols), Consumer(Consumer), Lock(Lock), Options(Options) { + IsMultiPiece = false; + for (const auto &Symbol : Symbols) { + if (Symbol.Name.getNamePieces().size() > 1) { + IsMultiPiece = true; + break; + } + } + if (IsMultiPiece) { + for (const auto &Symbol : Symbols) { + (void)Symbol; + assert(Symbol.Name.getNamePieces().size() > 1 && + "Mixed multi-piece and single piece symbols " + "are unsupported"); + } + } +} + +namespace { + +enum class MatchKind { + SourceMatch, + SourcePropSetterMatch, + MacroExpansion, + None +}; + +} // end anonymous namespace + +static bool isSetterNameEqualToPropName(StringRef SetterName, + StringRef PropertyName) { + assert(SetterName.starts_with("set") && "invalid setter name"); + SetterName = SetterName.drop_front(3); + return SetterName[0] == toUppercase(PropertyName[0]) && + SetterName.drop_front() == PropertyName.drop_front(); +} + +static MatchKind checkOccurrence(const IndexedOccurrence &Occurrence, + const IndexedSymbol &Symbol, + const SourceManager &SM, + const LangOptions &LangOpts, + SourceRange &SymbolRange, + bool AllowObjCSetterProp = false) { + if (!Occurrence.Line || !Occurrence.Column) + return MatchKind::None; // Ignore any invalid indexed locations. + + // Ensure that the first string in the name is present at the given + // location. + SourceLocation BeginLoc = SM.translateLineCol( + SM.getMainFileID(), Occurrence.Line, Occurrence.Column); + if (BeginLoc.isInvalid()) + return MatchKind::None; + StringRef SymbolNameStart = Symbol.Name.getNamePieces()[0]; + // Extract the token at the location. + auto DecomposedLoc = SM.getDecomposedLoc(BeginLoc); + const auto File = SM.getBufferOrFake(DecomposedLoc.first); + Lexer RawLex( + BeginLoc, LangOpts, File.getBufferStart() + DecomposedLoc.second, + File.getBufferStart() + DecomposedLoc.second, File.getBufferEnd()); + Token Tok; + RawLex.LexFromRawLexer(Tok); + if (Tok.isNot(tok::raw_identifier) || Tok.getLocation() != BeginLoc) { + if (SymbolNameStart.empty() && Tok.is(tok::colon) && + Tok.getLocation() == BeginLoc) { + // Must be the location of an empty Objective-C selector piece. + SymbolRange = SourceRange(BeginLoc, BeginLoc); + return MatchKind::SourceMatch; + } + // FIXME: Handle empty selector piece in a macro? + return MatchKind::None; + } + SymbolRange = SourceRange(BeginLoc, Tok.getEndLoc()); + if (Tok.getRawIdentifier() == SymbolNameStart) + return MatchKind::SourceMatch; + // Match 'prop' when looking for 'setProp'. + // FIXME: Verify that the previous token is a '.' to be sure. + if (AllowObjCSetterProp && + Occurrence.Kind == IndexedOccurrence::IndexedObjCMessageSend && + SymbolNameStart.starts_with("set") && + isSetterNameEqualToPropName(SymbolNameStart, Tok.getRawIdentifier())) + return MatchKind::SourcePropSetterMatch; + return MatchKind::MacroExpansion; +} + +static void +findObjCMultiPieceSelectorOccurrences(CompilerInstance &CI, + ArrayRef Symbols, + IndexedFileOccurrenceConsumer &Consumer); + +namespace { + +struct TextualMatchOccurrence { + SourceLocation Location; + unsigned SymbolIndex; +}; + +/// Finds '@selector' expressions by looking at tokens one-by-one. +class SelectorParser { + enum ParseState { + None, + At, + Selector, + ExpectingSelectorPiece, + ExpectingColon, + ExpectingRParenOrColon, + ExpectingRParen, + Success + }; + ParseState State = None; + const SymbolName &Name; + + ParseState stateForToken(const Token &RawTok); + +public: + unsigned SymbolIndex; + llvm::SmallVector SelectorLocations; + + SelectorParser(const SymbolName &Name, unsigned SymbolIndex) + : Name(Name), SymbolIndex(SymbolIndex) {} + + /// Returns true if the parses has found a '@selector' expression. + bool handleToken(const Token &RawTok); +}; + +class InclusionLexer final : public Lexer { +public: + InclusionLexer(SourceLocation FileLoc, const LangOptions &LangOpts, + const char *BufStart, const char *BufEnd) + : Lexer(FileLoc, LangOpts, BufStart, BufStart, BufEnd) {} + + void IndirectLex(Token &Result) override { LexFromRawLexer(Result); } +}; + +/// Finds matching textual occurrences in string literals. +class StringLiteralTextualParser { + const SymbolName &Name; + +public: + unsigned SymbolIndex; + + StringLiteralTextualParser(const SymbolName &Name, unsigned SymbolIndex) + : Name(Name), SymbolIndex(SymbolIndex) { + assert(Name.getNamePieces().size() == 1 && + "can't search for multi-piece names in strings"); + } + + /// Returns the name's location if the parses has found a matching textual + /// name in a string literal. + SourceLocation handleToken(const Token &RawTok, Preprocessor &PP); +}; + +} // end anonymous namespace + +SelectorParser::ParseState SelectorParser::stateForToken(const Token &RawTok) { + assert(RawTok.isNot(tok::comment) && "unexpected comment token"); + switch (State) { + case None: + break; + case At: + if (RawTok.is(tok::raw_identifier) && + RawTok.getRawIdentifier() == "selector") + return Selector; + break; + case Selector: + if (RawTok.isNot(tok::l_paren)) + break; + SelectorLocations.clear(); + return ExpectingSelectorPiece; + case ExpectingSelectorPiece: { + assert(SelectorLocations.size() < Name.getNamePieces().size() && + "Expecting invalid selector piece"); + StringRef NamePiece = Name.getNamePieces()[SelectorLocations.size()]; + if ((RawTok.isNot(tok::raw_identifier) || + RawTok.getRawIdentifier() != NamePiece) && + !(NamePiece.empty() && RawTok.is(tok::colon))) { + break; + } + SelectorLocations.push_back(RawTok.getLocation()); + if (SelectorLocations.size() == Name.getNamePieces().size()) { + // We found the selector that we were looking for, now check for ')'. + return NamePiece.empty() ? ExpectingRParen : ExpectingRParenOrColon; + } + return NamePiece.empty() ? ExpectingSelectorPiece : ExpectingColon; + } + case ExpectingColon: + if (RawTok.is(tok::colon)) + return ExpectingSelectorPiece; + break; + case ExpectingRParenOrColon: + if (RawTok.is(tok::colon)) + return ExpectingRParen; + LLVM_FALLTHROUGH; + case ExpectingRParen: + if (RawTok.is(tok::r_paren)) { + // We found the selector that we were looking for. + return Success; + } + break; + case Success: + llvm_unreachable("should not get here"); + } + // Look for the start of the selector expression. + return RawTok.is(tok::at) ? At : None; +} + +bool SelectorParser::handleToken(const Token &RawTok) { + if (RawTok.is(tok::coloncolon)) { + // Split the '::' into two ':'. + Token T(RawTok); + T.setKind(tok::colon); + T.setLength(1); + handleToken(T); + T.setLocation(T.getLocation().getLocWithOffset(1)); + return handleToken(T); + } + State = stateForToken(RawTok); + if (State != Success) + return false; + State = None; + return true; +} + +SourceLocation StringLiteralTextualParser::handleToken(const Token &RawTok, + Preprocessor &PP) { + if (!tok::isStringLiteral(RawTok.getKind())) + return SourceLocation(); + StringLiteralParser Literal(RawTok, PP); + if (Literal.hadError) + return SourceLocation(); + return Literal.GetString() == Name.getNamePieces()[0] + ? RawTok.getLocation().getLocWithOffset( + Literal.getOffsetOfStringByte(RawTok, 0)) + : SourceLocation(); +} + +static bool containsEmptyPiece(const SymbolName &Name) { + for (const auto &String : Name.getNamePieces()) { + if (String.empty()) + return true; + } + return false; +} + +static void collectTextualMatchesInComment( + ArrayRef Symbols, SourceLocation CommentLoc, + StringRef Comment, llvm::SmallVectorImpl &Result) { + for (const auto &Symbol : llvm::enumerate(Symbols)) { + const SymbolName &Name = Symbol.value().Name; + if (containsEmptyPiece(Name)) // Ignore Objective-C selectors with empty + // pieces. + continue; + size_t Offset = 0; + while (true) { + Offset = Comment.find(Name.getNamePieces()[0], /*From=*/Offset); + if (Offset == StringRef::npos) + break; + Result.push_back( + {CommentLoc.getLocWithOffset(Offset), (unsigned)Symbol.index()}); + Offset += Name.getNamePieces()[0].size(); + } + } +} + +/// Lex the comment to figure out if textual matches in a comment are standalone +/// tokens. +static void findTextualMatchesInComment( + const SourceManager &SM, const LangOptions &LangOpts, + ArrayRef Symbols, + ArrayRef TextualMatches, SourceRange CommentRange, + llvm::function_ref Locations, + unsigned SymbolIndex)> + MatchHandler) { + std::string Source = + Lexer::getSourceText(CharSourceRange::getCharRange(CommentRange), SM, + LangOpts) + .str(); + OldSymbolOccurrence::OccurrenceKind Kind = + RawComment(SM, CommentRange, LangOpts.CommentOpts, /*Merged=*/false) + .isDocumentation() + ? OldSymbolOccurrence::MatchingDocComment + : OldSymbolOccurrence::MatchingComment; + // Replace some special characters with ' ' to avoid comments and literals. + std::replace_if( + Source.begin(), Source.end(), + [](char c) -> bool { return c == '/' || c == '"' || c == '\''; }, ' '); + Lexer RawLex(CommentRange.getBegin(), LangOpts, Source.c_str(), + Source.c_str(), Source.c_str() + Source.size()); + Token RawTok; + RawLex.LexFromRawLexer(RawTok); + while (RawTok.isNot(tok::eof)) { + auto It = std::find_if(TextualMatches.begin(), TextualMatches.end(), + [&](const TextualMatchOccurrence &Match) { + return Match.Location == RawTok.getLocation(); + }); + if (It != TextualMatches.end()) { + StringRef TokenName = + Lexer::getSourceText(CharSourceRange::getCharRange( + RawTok.getLocation(), RawTok.getEndLoc()), + SM, LangOpts); + // Only report matches that are identical to the symbol. When dealing with + // multi-piece selectors we only look for the first selector piece as we + // assume that textual matches correspond to a match of the first selector + // piece. + if (TokenName == Symbols[It->SymbolIndex].Name.getNamePieces()[0]) + MatchHandler(Kind, It->Location, It->SymbolIndex); + } + RawLex.LexFromRawLexer(RawTok); + } +} + +static void findMatchingTextualOccurrences( + Preprocessor &PP, const SourceManager &SM, const LangOptions &LangOpts, + ArrayRef Symbols, + llvm::function_ref Locations, + unsigned SymbolIndex)> + MatchHandler) { + const auto FromFile = SM.getBufferOrFake(SM.getMainFileID()); + Lexer RawLex(SM.getMainFileID(), FromFile, SM, LangOpts); + RawLex.SetCommentRetentionState(true); + + llvm::SmallVector CommentMatches; + llvm::SmallVector SelectorParsers; + for (const auto &Symbol : llvm::enumerate(Symbols)) { + if (Symbol.value().IsObjCSelector) + SelectorParsers.push_back( + SelectorParser(Symbol.value().Name, Symbol.index())); + } + llvm::SmallVector StringParsers; + for (const auto &Symbol : llvm::enumerate(Symbols)) { + if (Symbol.value().SearchForStringLiteralOccurrences) + StringParsers.push_back( + StringLiteralTextualParser(Symbol.value().Name, Symbol.index())); + } + + Token RawTok; + RawLex.LexFromRawLexer(RawTok); + bool ScanNonCommentTokens = + !SelectorParsers.empty() || !StringParsers.empty(); + while (RawTok.isNot(tok::eof)) { + if (RawTok.is(tok::comment)) { + SourceRange Range(RawTok.getLocation(), RawTok.getEndLoc()); + StringRef Comment = Lexer::getSourceText( + CharSourceRange::getCharRange(Range), SM, LangOpts); + collectTextualMatchesInComment(Symbols, Range.getBegin(), Comment, + CommentMatches); + if (!CommentMatches.empty()) { + findTextualMatchesInComment(SM, LangOpts, Symbols, CommentMatches, + Range, MatchHandler); + CommentMatches.clear(); + } + } else if (ScanNonCommentTokens) { + for (auto &Parser : SelectorParsers) { + if (Parser.handleToken(RawTok)) + MatchHandler(OldSymbolOccurrence::MatchingSelector, + Parser.SelectorLocations, Parser.SymbolIndex); + } + for (auto &Parser : StringParsers) { + SourceLocation Loc = Parser.handleToken(RawTok, PP); + if (Loc.isValid()) + MatchHandler(OldSymbolOccurrence::MatchingStringLiteral, Loc, + Parser.SymbolIndex); + } + } + RawLex.LexFromRawLexer(RawTok); + } +} + +static void findInclusionDirectiveOccurrence( + const IndexedOccurrence &Occurrence, const IndexedSymbol &Symbol, + unsigned SymbolIndex, SourceManager &SM, const LangOptions &LangOpts, + IndexedFileOccurrenceConsumer &Consumer) { + if (!Occurrence.Line || !Occurrence.Column) + return; // Ignore any invalid indexed locations. + + SourceLocation Loc = SM.translateLineCol(SM.getMainFileID(), Occurrence.Line, + Occurrence.Column); + if (Loc.isInvalid()) + return; + unsigned Offset = SM.getDecomposedLoc(Loc).second; + const auto File = SM.getBufferOrFake(SM.getMainFileID()); + + InclusionLexer RawLex(Loc, LangOpts, File.getBufferStart() + Offset, + File.getBufferEnd()); + Token RawTok; + RawLex.LexFromRawLexer(RawTok); + if (RawTok.isNot(tok::hash)) + return; + // include/import + RawLex.LexFromRawLexer(RawTok); + if (RawTok.isNot(tok::raw_identifier)) + return; + // string literal/angled literal. + RawLex.setParsingPreprocessorDirective(true); + RawLex.LexIncludeFilename(RawTok); + if (RawTok.isNot(tok::string_literal) && + RawTok.isNot(tok::header_name)) + return; + StringRef Filename = llvm::sys::path::filename( + StringRef(RawTok.getLiteralData(), RawTok.getLength()) + .drop_front() + .drop_back()); + size_t NameOffset = + Filename.rfind_insensitive(Symbol.Name.getNamePieces()[0]); + if (NameOffset == StringRef::npos) + return; + OldSymbolOccurrence Result( + OldSymbolOccurrence::MatchingFilename, + /*IsMacroExpansion=*/false, SymbolIndex, + RawTok.getLocation().getLocWithOffset( + NameOffset + (Filename.data() - RawTok.getLiteralData()))); + Consumer.handleOccurrence(Result, SM, LangOpts); +} + +void IndexedFileOccurrenceProducer::ExecuteAction() { + Lock.unlock(); // The execution should now be thread-safe. + Preprocessor &PP = getCompilerInstance().getPreprocessor(); + PP.EnterMainSourceFile(); + + SourceManager &SM = getCompilerInstance().getSourceManager(); + const LangOptions &LangOpts = getCompilerInstance().getLangOpts(); + if (IsMultiPiece) { + findObjCMultiPieceSelectorOccurrences(getCompilerInstance(), Symbols, + Consumer); + } else { + for (const auto &Symbol : llvm::enumerate(Symbols)) { + for (const IndexedOccurrence &Occurrence : + Symbol.value().IndexedOccurrences) { + if (Occurrence.Kind == IndexedOccurrence::InclusionDirective) { + findInclusionDirectiveOccurrence(Occurrence, Symbol.value(), + Symbol.index(), SM, LangOpts, + Consumer); + continue; + } + SourceRange SymbolRange; + MatchKind Match = checkOccurrence(Occurrence, Symbol.value(), SM, + LangOpts, SymbolRange, + /*AllowObjCSetterProp=*/true); + if (Match == MatchKind::None) + continue; + llvm::SmallVector Locs; + Locs.push_back(SymbolRange.getBegin()); + bool IsImpProp = Match == MatchKind::SourcePropSetterMatch; + if (IsImpProp) + Locs.push_back(SymbolRange.getEnd()); + OldSymbolOccurrence Result( + IsImpProp ? OldSymbolOccurrence::MatchingImplicitProperty + : OldSymbolOccurrence::MatchingSymbol, + /*IsMacroExpansion=*/Match == MatchKind::MacroExpansion, + Symbol.index(), Locs); + Consumer.handleOccurrence(Result, SM, LangOpts); + } + } + } + + if (Options && Options->get(option::AvoidTextualMatches())) + return; + findMatchingTextualOccurrences( + PP, SM, LangOpts, Symbols, + [&](OldSymbolOccurrence::OccurrenceKind Kind, + ArrayRef Locations, unsigned SymbolIndex) { + OldSymbolOccurrence Result(Kind, /*IsMacroExpansion=*/false, + SymbolIndex, Locations); + Consumer.handleOccurrence(Result, SM, LangOpts); + }); +} + +namespace { + +/// Maps from source locations to the indexed occurrences. +typedef llvm::DenseMap> + SourceLocationsToIndexedOccurrences; + +} // end anonymous namespace + +// Scan the file and find multi-piece selector occurrences in a token stream. +static void +findObjCMultiPieceSelectorOccurrences(CompilerInstance &CI, + ArrayRef Symbols, + IndexedFileOccurrenceConsumer &Consumer) { + for (const auto &Symbol : Symbols) { + (void)Symbol; + assert(Symbol.Name.getNamePieces().size() > 1 && + "Not a multi-piece symbol!"); + } + + SourceManager &SM = CI.getSourceManager(); + const LangOptions &LangOpts = CI.getLangOpts(); + // Create a mapping from source locations to the indexed occurrences. + SourceLocationsToIndexedOccurrences MappedIndexedOccurrences; + for (const auto &Symbol : llvm::enumerate(Symbols)) { + for (const IndexedOccurrence &Occurrence : + Symbol.value().IndexedOccurrences) { + // Selectors and names in #includes shouldn't really mix. + if (Occurrence.Kind == IndexedOccurrence::InclusionDirective) + continue; + SourceRange SymbolRange; + MatchKind Match = checkOccurrence(Occurrence, Symbol.value(), SM, + LangOpts, SymbolRange); + if (Match == MatchKind::None) + continue; + SourceLocation Loc = SymbolRange.getBegin(); + if (Match == MatchKind::MacroExpansion) { + OldSymbolOccurrence Result(OldSymbolOccurrence::MatchingSymbol, + /*IsMacroExpansion=*/true, Symbol.index(), + Loc); + Consumer.handleOccurrence(Result, SM, LangOpts); + continue; + } + MappedIndexedOccurrences.try_emplace(Loc.getRawEncoding(), Occurrence, + Symbol.index()); + } + } + + // Lex the file and look for tokens. + // Start lexing the specified input file. + const auto FromFile = SM.getBufferOrFake(SM.getMainFileID()); + Lexer RawLex(SM.getMainFileID(), FromFile, SM, LangOpts); + + std::vector Tokens; + bool SaveTokens = false; + Token RawTok; + RawLex.LexFromRawLexer(RawTok); + while (RawTok.isNot(tok::eof)) { + // Start saving tokens only when we've got a match + if (!SaveTokens) { + if (MappedIndexedOccurrences.find( + RawTok.getLocation().getRawEncoding()) != + MappedIndexedOccurrences.end()) + SaveTokens = true; + } + if (SaveTokens) + Tokens.emplace_back(RawTok); + RawLex.LexFromRawLexer(RawTok); + } + + for (const auto &I : llvm::enumerate(Tokens)) { + const auto &Tok = I.value(); + auto It = MappedIndexedOccurrences.find(Tok.location().getRawEncoding()); + if (It == MappedIndexedOccurrences.end()) + continue; + unsigned SymbolIndex = It->second.second; + if (Tok.kind() != tok::raw_identifier && + !(Symbols[SymbolIndex].Name.getNamePieces()[0].empty() && + Tok.kind() == tok::colon)) + continue; + const IndexedOccurrence &Occurrence = It->second.first; + + // Scan the source for the remaining selector pieces. + ObjCSymbolSelectorKind Kind = + Occurrence.Kind == IndexedOccurrence::IndexedObjCMessageSend + ? ObjCSymbolSelectorKind::MessageSend + : ObjCSymbolSelectorKind::MethodDecl; + SmallVector SelectorPieces; + llvm::Error Error = findObjCSymbolSelectorPieces(Tokens, SM, Tok.location(), + Symbols[SymbolIndex].Name, + Kind, SelectorPieces); + if (Error) { + // Ignore the error. We simply skip over all selectors that didn't match. + consumeError(std::move(Error)); + continue; + } + OldSymbolOccurrence Result(OldSymbolOccurrence::MatchingSymbol, + /*IsMacroExpansion=*/false, SymbolIndex, + std::move(SelectorPieces)); + Consumer.handleOccurrence(Result, SM, LangOpts); + } +} + +} // end namespace rename +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/RenamedSymbol.cpp b/clang/lib/Tooling/Refactor/RenamedSymbol.cpp new file mode 100644 index 0000000000000..29e767f514156 --- /dev/null +++ b/clang/lib/Tooling/Refactor/RenamedSymbol.cpp @@ -0,0 +1,41 @@ +//===--- RenamedSymbol.cpp - ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RenamedSymbol.h" +#include "clang/AST/DeclObjC.h" +#include + +using namespace clang; + +namespace clang { +namespace tooling { +namespace rename { + +Symbol::Symbol(const NamedDecl *FoundDecl, unsigned SymbolIndex, + const LangOptions &LangOpts) + : Name(FoundDecl->getNameAsString(), LangOpts), SymbolIndex(SymbolIndex), + FoundDecl(FoundDecl) { + if (const auto *MD = dyn_cast(FoundDecl)) + ObjCSelector = MD->getSelector(); +} + +bool operator<(const OldSymbolOccurrence &LHS, const OldSymbolOccurrence &RHS) { + assert(!LHS.Locations.empty() && !RHS.Locations.empty()); + return LHS.Locations[0] < RHS.Locations[0]; +} + +bool operator==(const OldSymbolOccurrence &LHS, + const OldSymbolOccurrence &RHS) { + return LHS.Kind == RHS.Kind && LHS.SymbolIndex == RHS.SymbolIndex && + std::equal(LHS.Locations.begin(), LHS.Locations.end(), + RHS.Locations.begin()); +} + +} // end namespace rename +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/RenamingOperation.cpp b/clang/lib/Tooling/Refactor/RenamingOperation.cpp new file mode 100644 index 0000000000000..b957a75cab4fe --- /dev/null +++ b/clang/lib/Tooling/Refactor/RenamingOperation.cpp @@ -0,0 +1,98 @@ +//===--- RenamingOperation.cpp - ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/RenamingOperation.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Lex/Lexer.h" +#include "clang/Tooling/Refactor/SymbolOperation.h" + +using namespace clang; + +/// \brief Lexes the given name string. +/// +/// \return False if the name was consumed fully, true otherwise. +static bool lexNameString(StringRef Name, Token &Result, + const LangOptions &LangOpts) { + Lexer Lex(SourceLocation(), LangOpts, Name.data(), Name.data(), + Name.data() + Name.size()); + return !Lex.LexFromRawLexer(Result); +} + +namespace clang { +namespace tooling { +namespace rename { + +bool isNewNameValid(const SymbolName &NewName, bool IsSymbolObjCSelector, + IdentifierTable &IDs, const LangOptions &LangOpts) { + Token Tok; + if (IsSymbolObjCSelector) { + // Check if the name is a valid selector. + for (const auto &Name : NewName.getNamePieces()) { + // Lex the name and verify that it was fully consumed. Then make sure that + // it's a valid identifier. + if (lexNameString(Name, Tok, LangOpts) || !Tok.isAnyIdentifier()) + return false; + } + return true; + } + + for (const auto &Name : NewName.getNamePieces()) { + // Lex the name and verify that it was fully consumed. Then make sure that + // it's a valid identifier that's also not a language keyword. + if (lexNameString(Name, Tok, LangOpts) || !Tok.isAnyIdentifier() || + !tok::isAnyIdentifier(IDs.get(Name).getTokenID())) + return false; + } + return true; +} + +bool isNewNameValid(const SymbolName &NewName, const SymbolOperation &Operation, + IdentifierTable &IDs, const LangOptions &LangOpts) { + assert(!Operation.symbols().empty()); + return isNewNameValid(NewName, + Operation.symbols().front().ObjCSelector.has_value(), + IDs, LangOpts); +} + +void determineNewNames(SymbolName NewName, const SymbolOperation &Operation, + SmallVectorImpl &NewNames, + const LangOptions &LangOpts) { + auto Symbols = Operation.symbols(); + assert(!Symbols.empty()); + NewNames.push_back(std::move(NewName)); + if (const auto *PropertyDecl = + dyn_cast(Symbols.front().FoundDecl)) { + assert(NewNames.front().getNamePieces().size() == 1 && + "Property's name should have one string only"); + StringRef PropertyName = NewNames.front().getNamePieces()[0]; + Symbols = Symbols.drop_front(); + + auto AddName = [&](const NamedDecl *D, StringRef Name) { + assert(Symbols.front().FoundDecl == D && "decl is missing"); + NewNames.push_back( + SymbolName(Name, /*IsObjectiveCSelector=*/LangOpts.ObjC)); + Symbols = Symbols.drop_front(); + }; + + if (!PropertyDecl->hasExplicitGetterName()) { + if (const auto *Getter = PropertyDecl->getGetterMethodDecl()) + AddName(Getter, PropertyName); + } + if (!PropertyDecl->hasExplicitSetterName()) { + if (const auto *Setter = PropertyDecl->getSetterMethodDecl()) { + auto SetterName = SelectorTable::constructSetterName(PropertyName); + AddName(Setter, SetterName); + } + } + } +} + +} // end namespace rename +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/SourceLocationUtilities.cpp b/clang/lib/Tooling/Refactor/SourceLocationUtilities.cpp new file mode 100644 index 0000000000000..b9bc45350bce9 --- /dev/null +++ b/clang/lib/Tooling/Refactor/SourceLocationUtilities.cpp @@ -0,0 +1,259 @@ +//===--- SourceLocationUtilities.cpp - Source location helper functions ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SourceLocationUtilities.h" +#include "clang/AST/Stmt.h" +#include "clang/Lex/Lexer.h" +#include + +namespace clang { +namespace tooling { + +SourceLocation findLastLocationOfSourceConstruct(SourceLocation HeaderEnd, + const Stmt *Body, + const SourceManager &SM) { + SourceLocation BodyStart = SM.getSpellingLoc(Body->getBeginLoc()); + unsigned BodyLine = SM.getSpellingLineNumber(BodyStart); + unsigned HeaderLine = SM.getSpellingLineNumber(HeaderEnd); + + if (BodyLine > HeaderLine) { + // The Last location on the previous line if the body is not on the same + // line as the last known location. + SourceLocation LineLocThatPrecedesBody = + SM.translateLineCol(SM.getFileID(BodyStart), BodyLine - 1, + std::numeric_limits::max()); + if (LineLocThatPrecedesBody.isValid()) + return LineLocThatPrecedesBody; + } + // We want to include the location of the '{'. + return isa(Body) ? BodyStart : BodyStart.getLocWithOffset(-1); +} + +SourceLocation findFirstLocationOfSourceConstruct(SourceLocation HeaderStart, + const Stmt *PreviousBody, + const SourceManager &SM) { + if (!isa(PreviousBody)) + return HeaderStart; + SourceLocation BodyEnd = SM.getSpellingLoc(PreviousBody->getEndLoc()); + unsigned BodyLine = SM.getSpellingLineNumber(BodyEnd); + unsigned HeaderLine = SM.getSpellingLineNumber(HeaderStart); + if (BodyLine >= HeaderLine) + return BodyEnd; + return HeaderStart; +} + +bool isLocationInAnyRange(SourceLocation Location, ArrayRef Ranges, + const SourceManager &SM) { + for (const SourceRange &Range : Ranges) { + if (!isPointWithin(Location, Range.getBegin(), Range.getEnd(), SM)) + continue; + return true; + } + return false; +} + +SourceLocation getPreciseTokenLocEnd(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts) { + return Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts); +} + +SourceLocation findClosingParenLocEnd(SourceLocation LastKnownLoc, + const SourceManager &SM, + const LangOptions &LangOpts) { + return Lexer::findLocationAfterToken( + LastKnownLoc, tok::r_paren, SM, LangOpts, + /*SkipTrailingWhitespaceAndNewLine=*/false); +} + +SourceRange getRangeOfNextToken(SourceLocation Loc, tok::TokenKind Kind, + const SourceManager &SM, + const LangOptions &LangOpts) { + SourceLocation NextLoc = + Lexer::findLocationAfterToken(Loc, Kind, SM, LangOpts, + /*SkipTrailingWhitespaceAndNewLine=*/false); + if (NextLoc.isInvalid()) + return SourceRange(); + return SourceRange( + Lexer::GetBeginningOfToken(NextLoc.getLocWithOffset(-1), SM, LangOpts), + NextLoc); +} + +SourceLocation findLastNonCompoundLocation(const Stmt *S) { + const auto *CS = dyn_cast(S); + if (!CS) + return S->getEndLoc(); + return CS->body_back() ? CS->body_back()->getEndLoc() : SourceLocation(); +} + +bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2, + const SourceManager &SM) { + return !Loc1.isMacroID() && !Loc2.isMacroID() && + SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2); +} + +SourceLocation +getLastLineLocationUnlessItHasOtherTokens(SourceLocation SpellingLoc, + const SourceManager &SM, + const LangOptions &LangOpts) { + assert(!SpellingLoc.isMacroID() && "Expecting a spelling location"); + SourceLocation NextTokenLoc = + Lexer::findNextTokenLocationAfterTokenAt(SpellingLoc, SM, LangOpts); + if (NextTokenLoc.isValid()) { + bool IsSameLine = areOnSameLine(SpellingLoc, NextTokenLoc, SM); + if (IsSameLine) { + // Could be a ';' on the same line, so try looking after the ';' + if (isSemicolonAtLocation(NextTokenLoc, SM, LangOpts)) + return getLastLineLocationUnlessItHasOtherTokens(NextTokenLoc, SM, + LangOpts); + } else { + SourceLocation LastLoc = SM.translateLineCol( + SM.getFileID(SpellingLoc), SM.getSpellingLineNumber(SpellingLoc), + std::numeric_limits::max()); + if (LastLoc.isValid()) + return LastLoc; + } + } + return getPreciseTokenLocEnd(SpellingLoc, SM, LangOpts); +} + +bool isSemicolonAtLocation(SourceLocation TokenLoc, const SourceManager &SM, + const LangOptions &LangOpts) { + return Lexer::getSourceText( + CharSourceRange::getTokenRange(TokenLoc, TokenLoc), SM, + LangOpts) == ";"; +} + +SourceRange trimSelectionRange(SourceRange Range, const SourceManager &SM, + const LangOptions &LangOpts) { + bool IsInvalid = false; + StringRef Text = Lexer::getSourceText(CharSourceRange::getCharRange(Range), + SM, LangOpts, &IsInvalid); + if (IsInvalid || Text.empty()) + return Range; + assert(Range.getBegin().isFileID() && "Not a file range!"); + + std::string Source = Text.str(); + Lexer Lex(Range.getBegin(), LangOpts, Source.c_str(), Source.c_str(), + Source.c_str() + Source.size()); + // Get comment tokens as well. + Lex.SetCommentRetentionState(true); + SourceLocation StartLoc, EndLoc; + while (true) { + Token Tok; + Lex.LexFromRawLexer(Tok); + if (Tok.getKind() == tok::eof) + break; + if (StartLoc.isInvalid()) + StartLoc = Tok.getLocation(); + if (Tok.getKind() != tok::semi) + EndLoc = Tok.getEndLoc(); + } + return StartLoc.isValid() && EndLoc.isValid() ? SourceRange(StartLoc, EndLoc) + : SourceRange(); +} + +/// Tokenize the given file and check if it contains a comment that ends at the +/// given location. +static SourceLocation findCommentThatEndsAt(FileID FID, + SourceLocation StartOfFile, + const SourceManager &SM, + const LangOptions &LangOpts, + SourceLocation ExpectedEndLoc) { + // Try to load the file buffer. + bool InvalidTemp = false; + StringRef File = SM.getBufferData(FID, &InvalidTemp); + if (InvalidTemp) + return SourceLocation(); + + // Search for the comment that ends at the given location. + Lexer Lex(StartOfFile, LangOpts, File.begin(), File.begin(), File.end()); + Lex.SetCommentRetentionState(true); + Token Tok; + while (!Lex.LexFromRawLexer(Tok)) { + if (Tok.is(tok::comment) && Tok.getEndLoc() == ExpectedEndLoc) + return Tok.getLocation(); + } + // Find the token. + return SourceLocation(); +} + +SourceLocation getLocationOfPrecedingComment(SourceLocation Location, + const SourceManager &SM, + const LangOptions &LangOpts) { + SourceLocation PrevResult = Location; + SourceLocation Result = Location; + if (Result.isMacroID()) + Result = SM.getExpansionLoc(Result); + FileID FID = SM.getFileID(Result); + SourceLocation StartOfFile = SM.getLocForStartOfFile(FID); + Token Tok; + Tok.setKind(tok::unknown); + SourceLocation TokenLoc = Result; + auto GetPreviousToken = [&]() -> bool { + TokenLoc = + Lexer::GetBeginningOfToken(TokenLoc.getLocWithOffset(-1), SM, LangOpts); + return !Lexer::getRawToken(TokenLoc, Tok, SM, LangOpts); + }; + // Look for a comment token. + while (TokenLoc != StartOfFile) { + bool LocHasToken = GetPreviousToken(); + if (LocHasToken && Tok.is(tok::slash)) { + // Check if this the end of a multiline '/*' comment before returning. + SourceLocation CommentLoc = findCommentThatEndsAt( + FID, StartOfFile, SM, LangOpts, Tok.getEndLoc()); + return CommentLoc.isInvalid() ? Result : CommentLoc; + } + if (LocHasToken && Tok.isNot(tok::comment)) + break; + if (!LocHasToken) + continue; + // We found a preceding comment. Check if there are other preceding + // comments. + PrevResult = Result; + Result = Tok.getLocation(); + while (TokenLoc != StartOfFile) { + bool LocHasToken = GetPreviousToken(); + if (LocHasToken && Tok.isNot(tok::comment)) { + // Reset the result to the previous location if this comment trails + // another token on the same line. + if (SM.getSpellingLineNumber(Tok.getEndLoc()) == + SM.getSpellingLineNumber(Result)) + Result = PrevResult; + break; + } + if (!LocHasToken) + continue; + // The location of this comment is accepted only when the next comment + // is located immediately after this comment. + if (SM.getSpellingLineNumber(Tok.getEndLoc()) != + SM.getSpellingLineNumber(Result) - 1) + break; + PrevResult = Result; + Result = Tok.getLocation(); + } + break; + } + return Result; +} + +SourceLocation getLocationOfPrecedingToken(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts) { + SourceLocation Result = Loc; + if (Result.isMacroID()) + Result = SM.getExpansionLoc(Result); + FileID FID = SM.getFileID(Result); + SourceLocation StartOfFile = SM.getLocForStartOfFile(FID); + if (Loc == StartOfFile) + return SourceLocation(); + return Lexer::GetBeginningOfToken(Result.getLocWithOffset(-1), SM, LangOpts); +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/SourceLocationUtilities.h b/clang/lib/Tooling/Refactor/SourceLocationUtilities.h new file mode 100644 index 0000000000000..fea7272dba15a --- /dev/null +++ b/clang/lib/Tooling/Refactor/SourceLocationUtilities.h @@ -0,0 +1,173 @@ +//===--- SourceLocationUtilities.h - Source location helper functions -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_SOURCE_LOCATION_UTILITIES_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_SOURCE_LOCATION_UTILITIES_H + +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.h" + +namespace clang { + +class Stmt; +class LangOptions; + +namespace tooling { + +inline bool isPairOfFileLocations(SourceLocation Start, SourceLocation End) { + return Start.isValid() && Start.isFileID() && End.isValid() && End.isFileID(); +} + +/// Return true if the Point is within Start and End. +inline bool isPointWithin(SourceLocation Location, SourceLocation Start, + SourceLocation End, const SourceManager &SM) { + return Location == Start || Location == End || + (SM.isBeforeInTranslationUnit(Start, Location) && + SM.isBeforeInTranslationUnit(Location, End)); +} + +/// Return true if the two given ranges overlap with each other. +inline bool areRangesOverlapping(SourceRange R1, SourceRange R2, + const SourceManager &SM) { + return isPointWithin(R1.getBegin(), R2.getBegin(), R2.getEnd(), SM) || + isPointWithin(R2.getBegin(), R1.getBegin(), R1.getEnd(), SM); +} + +/// \brief Return the source location that can be considered the last location +/// of the source construct before its body. +/// +/// The returned location is determined using the following rules: +/// +/// 1) If the source construct has a compound body that starts on the same line, +/// then this function will return the location of the opening '{'. +/// +/// if (condition) { +/// ^ +/// +/// 2) If the source construct's body is not a compound statement that starts +/// on the same line, then this function will return the location just before +/// the starting location of the body. +/// +/// if (condition) foo() +/// ^ +/// +/// 3) Otherwise, this function will return the last location on the line prior +/// to the the line on which the body starts. +/// +/// if (condition) +/// ^ +/// foo() +/// +/// \param HeaderEnd The last known location of the pre-body portion of the +/// source construct. For example, for an if statement, HeaderEnd should +/// be the ending location of its conditional expression. +SourceLocation findLastLocationOfSourceConstruct(SourceLocation HeaderEnd, + const Stmt *Body, + const SourceManager &SM); + +/// \brief Return the source location that can be considered the first location +/// of the source construct prior to the previous portion of its body. +/// +/// The returned location is determined using the following rules: +/// +/// 1) If the source construct's body is a compound statement that ends +/// on the same line, then this function will return the location of the +/// closing '}'. +/// +/// } else if (condition) +/// ^ +/// +/// 2) Otherwise, this function will return the starting location of the source +/// construct. +/// +/// foo(); +/// else if (condition) +/// ^ +/// +/// } +/// else if (condition) +/// ^ +/// +/// \param HeaderStart The first known location of the post-body portion of the +/// source construct. For example, for an if statement, HeaderStart should +/// be the starting location of the if keyword. +SourceLocation findFirstLocationOfSourceConstruct(SourceLocation HeaderStart, + const Stmt *PreviousBody, + const SourceManager &SM); + +/// Return true if the given \p Location is within any range. +bool isLocationInAnyRange(SourceLocation Location, ArrayRef Ranges, + const SourceManager &SM); + +/// Return the precise end location for the given token. +SourceLocation getPreciseTokenLocEnd(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts); + +/// \brief Find the source location right after the location of the next ')'. +/// +/// If the token that's located after \p LastKnownLoc isn't ')', then this +/// function returns an invalid source location. +SourceLocation findClosingParenLocEnd(SourceLocation LastKnownLoc, + const SourceManager &SM, + const LangOptions &LangOpts); + +/// Return the range of the next token if it has the given kind. +SourceRange getRangeOfNextToken(SourceLocation Loc, tok::TokenKind Kind, + const SourceManager &SM, + const LangOptions &LangOpts); + +/// Return the end location of the body when \p S is a compound statement or an +/// invalid location when \p S is an empty compound statement. Otherwise, +/// return the end location of the given statement \p S. +SourceLocation findLastNonCompoundLocation(const Stmt *S); + +/// Return true if the two locations are on the same line and aren't +/// macro locations. +bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2, + const SourceManager &SM); + +/// Return the last location of the line which contains the given spellling +/// location \p SpellingLoc unless that line has other tokens after the given +/// location. +SourceLocation +getLastLineLocationUnlessItHasOtherTokens(SourceLocation SpellingLoc, + const SourceManager &SM, + const LangOptions &LangOpts); + +/// Return true if the token at the given location is a semicolon. +bool isSemicolonAtLocation(SourceLocation TokenLoc, const SourceManager &SM, + const LangOptions &LangOpts); + +/// Shrink the given range by ignoring leading whitespace and trailing +/// whitespace and semicolons. +/// +/// Returns an invalid source range if the source range consists of whitespace +/// or semicolons only. +SourceRange trimSelectionRange(SourceRange Range, const SourceManager &SM, + const LangOptions &LangOpts); + +/// Return the source location of the conjoined comment(s) that precede the +/// given location \p Loc, or the same location if there's no comment before +/// \p Loc. +SourceLocation getLocationOfPrecedingComment(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts); + +/// Return the source location of the token that comes before the token at the +/// given location. +SourceLocation getLocationOfPrecedingToken(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_SOURCE_LOCATION_UTILITIES_H diff --git a/clang/lib/Tooling/Refactor/StmtUtils.cpp b/clang/lib/Tooling/Refactor/StmtUtils.cpp new file mode 100644 index 0000000000000..59f74336fe951 --- /dev/null +++ b/clang/lib/Tooling/Refactor/StmtUtils.cpp @@ -0,0 +1,71 @@ +//===--- StmtUtils.cpp - Statement helper functions -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "StmtUtils.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/StmtCXX.h" +#include "clang/AST/StmtObjC.h" +#include "clang/Lex/Lexer.h" + +using namespace clang; + +SourceLocation +clang::tooling::getLexicalEndLocForDecl(const Decl *D, const SourceManager &SM, + const LangOptions &LangOpts) { + if (!isa(D)) + return D->getSourceRange().getEnd(); + auto AtEnd = D->getSourceRange().getEnd(); + auto AdjustedEnd = + Lexer::findNextTokenLocationAfterTokenAt(AtEnd, SM, LangOpts); + return AdjustedEnd.isValid() ? AdjustedEnd : AtEnd; +} + +bool clang::tooling::isSemicolonRequiredAfter(const Stmt *S) { + if (isa(S)) + return false; + if (const auto *If = dyn_cast(S)) + return isSemicolonRequiredAfter(If->getElse() ? If->getElse() + : If->getThen()); + if (const auto *While = dyn_cast(S)) + return isSemicolonRequiredAfter(While->getBody()); + if (const auto *For = dyn_cast(S)) + return isSemicolonRequiredAfter(For->getBody()); + if (const auto *CXXFor = dyn_cast(S)) + return isSemicolonRequiredAfter(CXXFor->getBody()); + if (const auto *ObjCFor = dyn_cast(S)) + return isSemicolonRequiredAfter(ObjCFor->getBody()); + switch (S->getStmtClass()) { + case Stmt::SwitchStmtClass: + case Stmt::CXXTryStmtClass: + case Stmt::ObjCAtSynchronizedStmtClass: + case Stmt::ObjCAutoreleasePoolStmtClass: + case Stmt::ObjCAtTryStmtClass: + return false; + default: + return true; + } +} + +static bool isAssignmentOperator(const Stmt *S) { + if (const auto *PseudoExpr = dyn_cast(S)) + return isAssignmentOperator(PseudoExpr->getSyntacticForm()); + if (const auto *BO = dyn_cast(S)) + return BO->isAssignmentOp(); + return false; +} + +bool clang::tooling::isLexicalExpression(const Stmt *S, const Stmt *Parent) { + if (!isa(S)) + return false; + // Assignment operators should be treated as statements unless they are a part + // of an expression. + if (isAssignmentOperator(S) && (!Parent || !isa(Parent))) + return false; + return !cast(S)->getType()->isVoidType(); +} diff --git a/clang/lib/Tooling/Refactor/StmtUtils.h b/clang/lib/Tooling/Refactor/StmtUtils.h new file mode 100644 index 0000000000000..a52da132cbcae --- /dev/null +++ b/clang/lib/Tooling/Refactor/StmtUtils.h @@ -0,0 +1,37 @@ +//===--- StmtUtils.h - Statement helper functions -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_STMT_UTILS_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_STMT_UTILS_H + +#include "clang/Basic/SourceLocation.h" + +namespace clang { + +class Decl; +class LangOptions; +class Stmt; + +namespace tooling { + +SourceLocation getLexicalEndLocForDecl(const Decl *D, const SourceManager &SM, + const LangOptions &LangOpts); + +/// \brief Returns true if there should be a semicolon after the given +/// statement. +bool isSemicolonRequiredAfter(const Stmt *S); + +/// Returns true if the given statement \p S is an actual expression in the +/// source. Assignment expressions are considered to be statements unless they +/// are a part of an expression. +bool isLexicalExpression(const Stmt *S, const Stmt *Parent); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_STMT_UTILS_H diff --git a/clang/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp b/clang/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp new file mode 100644 index 0000000000000..668c418f1e7e0 --- /dev/null +++ b/clang/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp @@ -0,0 +1,412 @@ +//===--- SymbolOccurrenceFinder.cpp - Clang refactoring library -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Methods for finding all instances of a USR. Our strategy is very +/// simple; we just compare the USR at every relevant AST node with the one +/// provided. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/SymbolOccurrenceFinder.h" +#include "clang/AST/ASTContext.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Index/USRGeneration.h" +#include "clang/Lex/Lexer.h" +#include "clang/Sema/DependentASTVisitor.h" +#include "clang/Tooling/Refactor/USRFinder.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" + +using namespace llvm; + +namespace clang { +namespace tooling { +namespace rename { + +namespace { +// \brief This visitor recursively searches for all instances of a USR in a +// translation unit and stores them for later usage. +class SymbolOccurrenceFinderASTVisitor + : public DependentASTVisitor { +public: + explicit SymbolOccurrenceFinderASTVisitor( + const SymbolOperation &Operation, const ASTContext &Context, + std::vector &Occurrences) + : Operation(Operation), Context(Context), Occurrences(Occurrences) {} + + /// Returns a \c Symbol if the given declaration corresponds to the symbol + /// that we're looking for. + const Symbol *symbolForDecl(const Decl *D) const { + if (!D) + return nullptr; + std::string USR = getUSRForDecl(D); + return Operation.getSymbolForUSR(USR); + } + + void checkDecl(const Decl *D, SourceLocation Loc, + OldSymbolOccurrence::OccurrenceKind Kind = + OldSymbolOccurrence::MatchingSymbol) { + if (!D) + return; + std::string USR = getUSRForDecl(D); + if (const Symbol *S = Operation.getSymbolForUSR(USR)) + checkAndAddLocations(S->SymbolIndex, Loc, Kind); + } + + // Declaration visitors: + + bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) { + for (const auto *Initializer : ConstructorDecl->inits()) { + // Ignore implicit initializers. + if (!Initializer->isWritten()) + continue; + if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) + checkDecl(FieldDecl, Initializer->getSourceLocation()); + } + return true; + } + + bool VisitNamedDecl(const NamedDecl *Decl) { + checkDecl(Decl, Decl->getLocation()); + return true; + } + + bool WalkUpFromTypedefNameDecl(const TypedefNameDecl *D) { + // Don't visit the NamedDecl for TypedefNameDecl. + return VisitTypedefNamedDecl(D); + } + + bool VisitTypedefNamedDecl(const TypedefNameDecl *D) { + if (D->isTransparentTag()) { + if (const auto *Underlying = D->getUnderlyingType()->getAsTagDecl()) { + checkDecl(Underlying, D->getLocation()); + return true; + } + } + return VisitNamedDecl(D); + } + + bool WalkUpFromUsingDecl(const UsingDecl *D) { + // Don't visit the NamedDecl for UsingDecl. + return VisitUsingDecl(D); + } + + bool VisitUsingDecl(const UsingDecl *D) { + for (const auto *Shadow : D->shadows()) { + const NamedDecl *UD = Shadow->getUnderlyingDecl(); + if (UD->isImplicit() || UD == D) + continue; + if (const auto *FTD = dyn_cast(UD)) { + UD = FTD->getTemplatedDecl(); + if (!UD) + continue; + } + checkDecl(UD, D->getLocation()); + } + return true; + } + + bool WalkUpFromUsingDirectiveDecl(const UsingDirectiveDecl *D) { + // Don't visit the NamedDecl for UsingDirectiveDecl. + return VisitUsingDirectiveDecl(D); + } + + bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { + checkDecl(D->getNominatedNamespaceAsWritten(), D->getLocation()); + return true; + } + + bool WalkUpFromUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) { + // Don't visit the NamedDecl for UnresolvedUsingValueDecl. + // FIXME: Can we try to lookup the name? + return true; + } + + bool + WalkUpFromUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) { + // Don't visit the NamedDecl for UnresolvedUsingTypenameDecl. + // FIXME: Can we try to lookup the name? + return true; + } + + bool WalkUpFromObjCMethodDecl(const ObjCMethodDecl *Decl) { + // Don't visit the NamedDecl for Objective-C methods. + return VisitObjCMethodDecl(Decl); + } + + bool VisitObjCMethodDecl(const ObjCMethodDecl *Decl) { + const Symbol *S = symbolForDecl(Decl); + if (!S) + return true; + SmallVector SelectorLocs; + Decl->getSelectorLocs(SelectorLocs); + checkAndAddLocations(S->SymbolIndex, SelectorLocs); + return true; + } + + bool handleObjCProtocolList(const ObjCProtocolList &Protocols) { + for (auto It : enumerate(Protocols)) + checkDecl(It.value(), Protocols.loc_begin()[It.index()]); + return true; + } + + bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) { + if (!Decl->hasDefinition()) + return true; + return handleObjCProtocolList(Decl->getReferencedProtocols()); + } + + bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) { + if (!Decl->hasDefinition()) + return true; + return handleObjCProtocolList(Decl->getReferencedProtocols()); + } + + bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) { + checkDecl(Decl, Decl->getCategoryNameLoc()); + // The location of the class name is the location of the declaration. + checkDecl(Decl->getClassInterface(), Decl->getLocation()); + return handleObjCProtocolList(Decl->getReferencedProtocols()); + } + + bool VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *Decl) { + checkDecl(Decl, Decl->getCategoryNameLoc()); + // The location of the class name is the location of the declaration. + checkDecl(Decl->getClassInterface(), Decl->getLocation()); + return true; + } + + bool VisitObjCCompatibleAliasDecl(const ObjCCompatibleAliasDecl *Decl) { + checkDecl(Decl->getClassInterface(), Decl->getClassInterfaceLoc()); + return true; + } + + bool VisitObjCPropertyDecl(const ObjCPropertyDecl *Decl) { + if (Decl->hasExplicitGetterName()) + checkDecl(Decl->getGetterMethodDecl(), Decl->getGetterNameLoc()); + if (Decl->hasExplicitSetterName()) + checkDecl(Decl->getSetterMethodDecl(), Decl->getSetterNameLoc()); + return true; + } + + bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *Decl) { + checkDecl(Decl->getPropertyDecl(), Decl->getLocation()); + if (Decl->isIvarNameSpecified()) + checkDecl(Decl->getPropertyIvarDecl(), Decl->getPropertyIvarDeclLoc()); + return true; + } + + // Expression visitors: + + bool VisitDeclRefExpr(const DeclRefExpr *Expr) { + checkDecl(Expr->getFoundDecl(), Expr->getLocation()); + return true; + } + + bool VisitMemberExpr(const MemberExpr *Expr) { + checkDecl(Expr->getFoundDecl().getDecl(), Expr->getMemberLoc()); + return true; + } + + bool VisitObjCMessageExpr(const ObjCMessageExpr *Expr) { + const Symbol *S = symbolForDecl(Expr->getMethodDecl()); + if (!S) + return true; + SmallVector SelectorLocs; + Expr->getSelectorLocs(SelectorLocs); + checkAndAddLocations(S->SymbolIndex, SelectorLocs); + return true; + } + + bool VisitObjCProtocolExpr(const ObjCProtocolExpr *Expr) { + checkDecl(Expr->getProtocol(), Expr->getProtocolIdLoc()); + return true; + } + + bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Expr) { + checkDecl(Expr->getDecl(), Expr->getLocation()); + return true; + } + + bool VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *Expr) { + if (Expr->isClassReceiver()) + checkDecl(Expr->getClassReceiver(), Expr->getReceiverLocation()); + if (Expr->isImplicitProperty()) { + // Class properties that are explicitly defined using @property + // declarations are represented implicitly as there is no ivar for class + // properties. + if (const ObjCMethodDecl *Getter = Expr->getImplicitPropertyGetter()) { + if (Getter->isClassMethod()) + if (const auto *PD = Getter->getCanonicalDecl()->findPropertyDecl()) { + checkDecl(PD, Expr->getLocation()); + return true; + } + } + + checkDecl(Expr->getImplicitPropertyGetter(), Expr->getLocation(), + OldSymbolOccurrence::MatchingImplicitProperty); + // Add a manual location for a setter since a token like 'property' won't + // match the the name of the renamed symbol like 'setProperty'. + if (const auto *S = symbolForDecl(Expr->getImplicitPropertySetter())) + addLocation(S->SymbolIndex, Expr->getLocation(), + OldSymbolOccurrence::MatchingImplicitProperty); + return true; + } + checkDecl(Expr->getExplicitProperty(), Expr->getLocation()); + return true; + } + + // Other visitors: + + bool VisitTypeLoc(const TypeLoc Loc) { + TypedefTypeLoc TTL = Loc.getAs(); + if (TTL) { + const auto *TND = TTL.getTypedefNameDecl(); + if (TND->isTransparentTag()) { + if (const auto *Underlying = TND->getUnderlyingType()->getAsTagDecl()) { + checkDecl(Underlying, TTL.getNameLoc()); + return true; + } + } + checkDecl(TND, TTL.getNameLoc()); + return true; + } + TypeSpecTypeLoc TSTL = Loc.getAs(); + if (TSTL) { + checkDecl(Loc.getType()->getAsTagDecl(), TSTL.getNameLoc()); + } + if (const auto *TemplateTypeParm = + dyn_cast(Loc.getType())) { + checkDecl(TemplateTypeParm->getDecl(), Loc.getBeginLoc()); + } + if (const auto *TemplateSpecType = + dyn_cast(Loc.getType())) { + checkDecl(TemplateSpecType->getTemplateName().getAsTemplateDecl(), + Loc.getBeginLoc()); + } + return true; + } + + bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc Loc) { + checkDecl(Loc.getIFaceDecl(), Loc.getNameLoc()); + return true; + } + + bool VisitObjCObjectTypeLoc(ObjCObjectTypeLoc Loc) { + for (unsigned I = 0, E = Loc.getNumProtocols(); I < E; ++I) + checkDecl(Loc.getProtocol(I), Loc.getProtocolLoc(I)); + return true; + } + + bool VisitDependentSymbolReference(const NamedDecl *Symbol, + SourceLocation SymbolNameLoc) { + checkDecl(Symbol, SymbolNameLoc); + return true; + } + + // Non-visitors: + + // Namespace traversal: + void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) { + while (NameLoc) { + checkDecl(NameLoc.getNestedNameSpecifier()->getAsNamespace(), + NameLoc.getLocalBeginLoc()); + NameLoc = NameLoc.getPrefix(); + } + } + +private: + size_t getOffsetForString(SourceLocation Loc, StringRef PrevNameString) { + const SourceLocation BeginLoc = Loc; + const SourceLocation EndLoc = Lexer::getLocForEndOfToken( + BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts()); + StringRef TokenName = + Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc), + Context.getSourceManager(), Context.getLangOpts()); + return TokenName.find(PrevNameString); + } + + void checkAndAddLocations(unsigned SymbolIndex, + ArrayRef Locations, + OldSymbolOccurrence::OccurrenceKind Kind = + OldSymbolOccurrence::MatchingSymbol) { + if (Locations.size() != + Operation.symbols()[SymbolIndex].Name.getNamePieces().size()) + return; + + SmallVector StringLocations; + for (size_t I = 0, E = Locations.size(); I != E; ++I) { + SourceLocation Loc = Locations[I]; + bool IsMacroExpansion = Loc.isMacroID(); + if (IsMacroExpansion) { + const SourceManager &SM = Context.getSourceManager(); + if (SM.isMacroArgExpansion(Loc)) { + Loc = SM.getSpellingLoc(Loc); + IsMacroExpansion = false; + } else + Loc = SM.getExpansionLoc(Loc); + } + if (IsMacroExpansion) { + Occurrences.push_back(OldSymbolOccurrence( + Kind, /*IsMacroExpansion=*/true, SymbolIndex, Loc)); + return; + } + size_t Offset = getOffsetForString( + Loc, Operation.symbols()[SymbolIndex].Name.getNamePieces()[I]); + if (Offset == StringRef::npos) + return; + StringLocations.push_back(Loc.getLocWithOffset(Offset)); + } + + Occurrences.push_back(OldSymbolOccurrence(Kind, /*IsMacroExpansion=*/false, + SymbolIndex, StringLocations)); + } + + /// Adds a location without checking if the name is actually there. + void addLocation(unsigned SymbolIndex, SourceLocation Location, + OldSymbolOccurrence::OccurrenceKind Kind) { + if (1 != Operation.symbols()[SymbolIndex].Name.getNamePieces().size()) + return; + bool IsMacroExpansion = Location.isMacroID(); + if (IsMacroExpansion) { + const SourceManager &SM = Context.getSourceManager(); + if (SM.isMacroArgExpansion(Location)) { + Location = SM.getSpellingLoc(Location); + IsMacroExpansion = false; + } else + Location = SM.getExpansionLoc(Location); + } + Occurrences.push_back( + OldSymbolOccurrence(Kind, IsMacroExpansion, SymbolIndex, Location)); + } + + const SymbolOperation &Operation; + const ASTContext &Context; + std::vector &Occurrences; +}; +} // namespace + +std::vector +findSymbolOccurrences(const SymbolOperation &Operation, Decl *Decl) { + std::vector Occurrences; + SymbolOccurrenceFinderASTVisitor Visitor(Operation, Decl->getASTContext(), + Occurrences); + Visitor.TraverseDecl(Decl); + NestedNameSpecifierLocFinder Finder(Decl->getASTContext()); + + for (const auto &Location : Finder.getNestedNameSpecifierLocations()) + Visitor.handleNestedNameSpecifierLoc(Location); + + return Occurrences; +} + +} // end namespace rename +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/SymbolOperation.cpp b/clang/lib/Tooling/Refactor/SymbolOperation.cpp new file mode 100644 index 0000000000000..834df9f3c213e --- /dev/null +++ b/clang/lib/Tooling/Refactor/SymbolOperation.cpp @@ -0,0 +1,224 @@ +//===--- SymbolOperation.cpp - --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/SymbolOperation.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Tooling/Refactor/RefactoringActionFinder.h" + +using namespace clang; + +/// Return true if the given local record decl escapes the given enclosing +/// function or block \p Ctx. +static bool escapesEnclosingDecl(const DeclContext *Ctx, const RecordDecl *RD) { + QualType ReturnType; + bool DependentBlock = false; + if (const auto *FD = dyn_cast(Ctx)) + ReturnType = FD->getReturnType(); + else if (const auto *BD = dyn_cast(Ctx)) { + ReturnType = BD->getSignatureAsWritten()->getType(); + // Blocks that don't have an explicitly specified type (represented with a + // dependent type) could potentially return the record, e.g. + // auto block = ^ { + // struct Foo { }; + // return Foo(); + // }; + if (const auto *FT = ReturnType->getAs()) + ReturnType = FT->getReturnType(); + if (ReturnType->isDependentType()) + DependentBlock = true; + } else + return false; + + // The record can be returned from its enclosing function when the function's + // return type is auto. + // + // FIXME: Use a smarter heuristic that detects if the record type is + // actually returned from the function. Have to account for inner records, + // like in the example below: + // + // auto foo() { + // struct Foo { struct Bar { }; }; + // return Foo::Bar(); + // }; + // + // for types that depend on the record, like in the example below: + // + // auto foo() { + // template struct C { T x; }; + // struct Foo { struct Bar { }; }; + // return C(); + // } + // + // and for things like typedefs and function types as well. + if (!DependentBlock && !ReturnType->getContainedAutoType()) + return false; + + // Even if the enclosing function returns the local record, this record is + // still local if the enclosing function is inside a function/method that + // doesn't return this record. + const auto *D = cast(Ctx); + if (!D->isDefinedOutsideFunctionOrMethod()) + return escapesEnclosingDecl(D->getParentFunctionOrMethod(), RD); + + return true; +} + +static bool escapesEnclosingDecl(const RecordDecl *RD, + const LangOptions &LangOpts) { + // We only care about things that escape in header files since things that + // escape in source files will be used only in the initial TU. + return LangOpts.IsHeaderFile && + escapesEnclosingDecl(RD->getParentFunctionOrMethod(), RD); +} + +static bool isInLocalScope(const Decl *D) { + const DeclContext *LDC = D->getLexicalDeclContext(); + while (true) { + if (LDC->isFunctionOrMethod()) + return true; + if (!isa(LDC)) + return false; + if (const auto *CRD = dyn_cast(LDC)) + if (CRD->isLambda()) + return true; + LDC = LDC->getLexicalParent(); + } + return false; +} + +/// Return true if the given declaration corresponds to a local symbol. +bool clang::tooling::isLocalSymbol(const NamedDecl *FoundDecl, + const LangOptions &LangOpts) { + // Template parameters aren't indexed, so use local rename. + if (isa(FoundDecl) || + isa(FoundDecl) || + isa(FoundDecl)) + return true; + + if (const auto *VD = dyn_cast(FoundDecl)) + return VD->isLocalVarDeclOrParm(); + + // Objective-C selector renames must be global. + if (isa(FoundDecl)) + return false; + + // Local declarations are defined in a function or a method, or are anonymous. + if (!isInLocalScope(FoundDecl)) + return false; + + // A locally defined record is global when it is returned from the enclosing + // function because we can refer to its destructor externally. + if (const auto *RD = dyn_cast(FoundDecl)) + return !escapesEnclosingDecl(RD, LangOpts); + + // A locally defined field is global when its record is returned from the + // enclosing function. + if (const auto *FD = dyn_cast(FoundDecl)) + return !escapesEnclosingDecl(FD->getParent(), LangOpts); + + if (const auto *MD = dyn_cast(FoundDecl)) { + // A locally defined method is global when its record is returned from the + // enclosing function. + if (escapesEnclosingDecl(MD->getParent(), LangOpts)) + return false; + + // Method renames can be local only iff this method doesn't override + // a global method, for example: + // + // void func() { + // struct Foo: GlobalSuper { + // // When renaming foo we should also rename GlobalSuper's foo + // void foo() override; + // } + // } + // + // FIXME: We can try to be smarter about it and check if we override + // a local method, which would make this method local as well. + return !MD->isVirtual(); + } + + return true; +} + +static const NamedDecl * +findDeclThatRequiresImplementationTU(const NamedDecl *FoundDecl) { + // TODO: implement the rest. + if (const ObjCIvarDecl *IVarDecl = dyn_cast(FoundDecl)) { + // We need the implementation TU when the IVAR is declared in an @interface + // without an @implementation. + if (const auto *ID = + dyn_cast(IVarDecl->getDeclContext())) { + if (!ID->getImplementation()) + return IVarDecl; + } + } + return nullptr; +} + +namespace clang { +namespace tooling { + +SymbolOperation::SymbolOperation(const NamedDecl *FoundDecl, + ASTContext &Context) + : IsLocal(isLocalSymbol(FoundDecl, Context.getLangOpts())) { + // Take the category declaration if this is a category implementation. + if (const auto *CategoryImplDecl = + dyn_cast(FoundDecl)) { + if (const auto *CategoryDecl = CategoryImplDecl->getCategoryDecl()) + FoundDecl = CategoryDecl; + } + // Use the property if this method is a getter/setter. + else if (const auto *MethodDecl = dyn_cast(FoundDecl)) { + if (const auto *PropertyDecl = + MethodDecl->getCanonicalDecl()->findPropertyDecl()) { + // Don't use the property if the getter/setter method has an explicitly + // specified name. + if (MethodDecl->param_size() == 0 + ? !PropertyDecl->hasExplicitGetterName() + : !PropertyDecl->hasExplicitSetterName()) + FoundDecl = PropertyDecl; + } + } + + DeclThatRequiresImplementationTU = + findDeclThatRequiresImplementationTU(FoundDecl); + + // TODO: Split into initiation that works after implementation TU is loaded. + + // Find the set of symbols that this operation has to work on. + auto AddSymbol = [this, &Context](const NamedDecl *FoundDecl) { + unsigned Index = Symbols.size(); + Symbols.push_back(rename::Symbol(FoundDecl, Index, Context.getLangOpts())); + for (const auto &USR : findSymbolsUSRSet(FoundDecl, Context)) + USRToSymbol.insert(std::make_pair(USR.getKey(), Index)); + }; + AddSymbol(FoundDecl); + // Take getters, setters and ivars into account when dealing with + // Objective-C @property declarations. + if (const auto *PropertyDecl = dyn_cast(FoundDecl)) { + // FIXME: findSymbolsUSRSet is called for every symbol we add, which is + // inefficient since we currently have to traverse the AST every time it is + // called. Fix this so that the AST isn't traversed more than once. + if (!PropertyDecl->hasExplicitGetterName()) { + if (const auto *Getter = PropertyDecl->getGetterMethodDecl()) + AddSymbol(Getter); + } + if (!PropertyDecl->hasExplicitSetterName()) { + if (const auto *Setter = PropertyDecl->getSetterMethodDecl()) + AddSymbol(Setter); + } + } +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/SymbolUSRFinder.cpp b/clang/lib/Tooling/Refactor/SymbolUSRFinder.cpp new file mode 100644 index 0000000000000..557be85ac8fdc --- /dev/null +++ b/clang/lib/Tooling/Refactor/SymbolUSRFinder.cpp @@ -0,0 +1,205 @@ +//===--- SymbolUSRFinder.cpp - Clang refactoring library ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Implements methods that find the set of USRs that correspond to +/// a symbol that's required for a refactoring operation. +/// +//===----------------------------------------------------------------------===// + +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Tooling/Refactor/RefactoringActionFinder.h" +#include "clang/Tooling/Refactor/USRFinder.h" +#include "llvm/ADT/StringRef.h" + +#include + +using namespace clang; +using namespace clang::tooling::rename; + +namespace { + +/// \brief NamedDeclFindingConsumer delegates finding USRs of a found Decl to +/// \c AdditionalUSRFinder. \c AdditionalUSRFinder adds USRs of ctors and dtor +/// if the found declaration refers to a class and adds USRs of all overridden +/// methods if the declaration refers to a virtual C++ method or an ObjC method. +class AdditionalUSRFinder : public RecursiveASTVisitor { +public: + AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context) + : FoundDecl(FoundDecl), Context(Context) {} + + llvm::StringSet<> Find() { + llvm::StringSet<> USRSet; + + // Fill OverriddenMethods and PartialSpecs storages. + TraverseDecl(Context.getTranslationUnitDecl()); + if (const auto *MethodDecl = dyn_cast(FoundDecl)) { + addUSRsOfOverridenFunctions(MethodDecl, USRSet); + // FIXME: Use a more efficient/optimal algorithm to find the related + // methods. + for (const auto &OverriddenMethod : OverriddenMethods) { + if (checkIfOverriddenFunctionAscends(OverriddenMethod, USRSet)) + USRSet.insert(getUSRForDecl(OverriddenMethod)); + } + } else if (const auto *RecordDecl = dyn_cast(FoundDecl)) { + handleCXXRecordDecl(RecordDecl, USRSet); + } else if (const auto *TemplateDecl = + dyn_cast(FoundDecl)) { + handleClassTemplateDecl(TemplateDecl, USRSet); + } else if (const auto *MethodDecl = dyn_cast(FoundDecl)) { + addUSRsOfOverriddenObjCMethods(MethodDecl, USRSet); + for (const auto &PotentialOverrider : PotentialObjCMethodOverridders) + if (checkIfPotentialObjCMethodOverriddes(PotentialOverrider, USRSet)) + USRSet.insert(getUSRForDecl(PotentialOverrider)); + } else { + USRSet.insert(getUSRForDecl(FoundDecl)); + } + return USRSet; + } + + bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) { + if (MethodDecl->isVirtual()) + OverriddenMethods.push_back(MethodDecl); + return true; + } + + bool VisitObjCMethodDecl(const ObjCMethodDecl *MethodDecl) { + if (const auto *FoundMethodDecl = dyn_cast(FoundDecl)) + if (DeclarationName::compare(MethodDecl->getDeclName(), + FoundMethodDecl->getDeclName()) == 0 && + MethodDecl->isOverriding()) + PotentialObjCMethodOverridders.push_back(MethodDecl); + return true; + } + + bool VisitClassTemplatePartialSpecializationDecl( + const ClassTemplatePartialSpecializationDecl *PartialSpec) { + if (!isa(FoundDecl) && !isa(FoundDecl)) + return true; + PartialSpecs.push_back(PartialSpec); + return true; + } + +private: + void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl, + llvm::StringSet<> &USRSet) { + const auto *RD = RecordDecl->getDefinition(); + if (!RD) { + USRSet.insert(getUSRForDecl(RecordDecl)); + return; + } + if (const auto *ClassTemplateSpecDecl = + dyn_cast(RD)) + handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate(), + USRSet); + addUSRsOfCtorDtors(RD, USRSet); + } + + void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl, + llvm::StringSet<> &USRSet) { + for (const auto *Specialization : TemplateDecl->specializations()) + addUSRsOfCtorDtors(Specialization, USRSet); + + for (const auto *PartialSpec : PartialSpecs) { + if (PartialSpec->getSpecializedTemplate() == TemplateDecl) + addUSRsOfCtorDtors(PartialSpec, USRSet); + } + addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl(), USRSet); + } + + void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl, + llvm::StringSet<> &USRSet) { + const CXXRecordDecl *RD = RecordDecl; + RecordDecl = RD->getDefinition(); + if (!RecordDecl) { + USRSet.insert(getUSRForDecl(RD)); + return; + } + + for (const auto *CtorDecl : RecordDecl->ctors()) { + auto USR = getUSRForDecl(CtorDecl); + if (!USR.empty()) + USRSet.insert(USR); + } + + auto USR = getUSRForDecl(RecordDecl->getDestructor()); + if (!USR.empty()) + USRSet.insert(USR); + USRSet.insert(getUSRForDecl(RecordDecl)); + } + + void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl, + llvm::StringSet<> &USRSet) { + USRSet.insert(getUSRForDecl(MethodDecl)); + // Recursively visit each OverridenMethod. + for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) + addUSRsOfOverridenFunctions(OverriddenMethod, USRSet); + } + + bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl, + const llvm::StringSet<> &USRSet) { + for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) { + if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end()) + return true; + return checkIfOverriddenFunctionAscends(OverriddenMethod, USRSet); + } + return false; + } + + /// \brief Recursively visit all the methods which the given method + /// declaration overrides and adds them to the USR set. + void addUSRsOfOverriddenObjCMethods(const ObjCMethodDecl *MethodDecl, + llvm::StringSet<> &USRSet) { + // Exit early if this method was already visited. + if (!USRSet.insert(getUSRForDecl(MethodDecl)).second) + return; + SmallVector Overrides; + MethodDecl->getOverriddenMethods(Overrides); + for (const auto &OverriddenMethod : Overrides) + addUSRsOfOverriddenObjCMethods(OverriddenMethod, USRSet); + } + + /// \brief Returns true if the given Objective-C method overrides the + /// found Objective-C method declaration. + bool checkIfPotentialObjCMethodOverriddes(const ObjCMethodDecl *MethodDecl, + const llvm::StringSet<> &USRSet) { + SmallVector Overrides; + MethodDecl->getOverriddenMethods(Overrides); + for (const auto &OverriddenMethod : Overrides) { + if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end()) + return true; + if (checkIfPotentialObjCMethodOverriddes(OverriddenMethod, USRSet)) + return true; + } + return false; + } + + const Decl *FoundDecl; + ASTContext &Context; + std::vector OverriddenMethods; + std::vector PartialSpecs; + /// \brief An array of Objective-C methods that potentially override the + /// found Objective-C method declaration \p FoundDecl. + std::vector PotentialObjCMethodOverridders; +}; +} // end anonymous namespace + +namespace clang { +namespace tooling { + +llvm::StringSet<> findSymbolsUSRSet(const NamedDecl *FoundDecl, + ASTContext &Context) { + return AdditionalUSRFinder(FoundDecl, Context).Find(); +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/TypeUtils.cpp b/clang/lib/Tooling/Refactor/TypeUtils.cpp new file mode 100644 index 0000000000000..ef7801e6b0980 --- /dev/null +++ b/clang/lib/Tooling/Refactor/TypeUtils.cpp @@ -0,0 +1,199 @@ +//===--- TypeUtils.cpp - Type helper functions ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "TypeUtils.h" +#include "clang/AST/Expr.h" +#include "clang/AST/NSAPI.h" +#include "clang/AST/RecursiveASTVisitor.h" + +using namespace clang; + +namespace { + +/// Returns false if a BOOL expression is found. +class BOOLUseFinder : public RecursiveASTVisitor { +public: + NSAPI API; + + BOOLUseFinder(const ASTContext &Context) + : API(const_cast(Context)) {} + + bool VisitStmt(const Stmt *S) { + if (const auto *E = dyn_cast(S)) + return !API.isObjCBOOLType(E->getType()); + return true; + } + + static bool hasUseOfObjCBOOL(const ASTContext &Ctx, const Expr *E) { + return !BOOLUseFinder(Ctx).TraverseStmt(const_cast(E)); + } +}; + +} // end anonymous namespace + +static QualType preferredBoolType(const Decl *FunctionLikeParentDecl, + const Expr *E, QualType T, + const PrintingPolicy &Policy, + const ASTContext &Ctx) { + // We want to target expressions that return either 'int' or 'bool' + const auto *BTy = T->getAs(); + if (!BTy) + return T; + switch (BTy->getKind()) { + case BuiltinType::Int: + case BuiltinType::Bool: + // In Objective-C[++] we want to try to use 'BOOL' when the 'BOOL' typedef + // is defined. + if (Ctx.getLangOpts().ObjC && Ctx.getBOOLDecl()) { + if (Ctx.getLangOpts().CPlusPlus && FunctionLikeParentDecl) { + // When extracting expression from a standalone function in + // Objective-C++ we should use BOOL when expression uses BOOL, otherwise + // we should use bool. + if (isa(FunctionLikeParentDecl)) { + if (BOOLUseFinder::hasUseOfObjCBOOL(Ctx, E)) + return Ctx.getBOOLType(); + return T; + } + } + return Ctx.getBOOLType(); + } + // In C mode we want to use 'bool' instead of 'int' when the 'bool' macro + // is defined. + if (!Ctx.getLangOpts().CPlusPlus && Policy.Bool) + return Ctx.BoolTy; + break; + default: + break; + } + return T; +} + +static bool isInStdNamespace(const Decl *D) { + const DeclContext *DC = D->getDeclContext()->getEnclosingNamespaceContext(); + const NamespaceDecl *ND = dyn_cast(DC); + if (!ND) + return false; + + while (const DeclContext *Parent = ND->getParent()) { + if (!isa(Parent)) + break; + ND = cast(Parent); + } + + return ND->isStdNamespace(); +} + +static QualType desugarStdTypedef(QualType T) { + const auto *TT = T->getAs(); + if (!TT) + return QualType(); + const TypedefNameDecl *TND = TT->getDecl(); + if (!isInStdNamespace(TND)) + return QualType(); + return TT->desugar(); +} + +// Desugars a typedef of a typedef that are both defined in STL. +// +// This is used to find the right type for a c_str() call on a std::string +// object: we want to return const char *, not const value_type *. +static QualType desugarStdType(QualType T) { + QualType DesugaredType = T; + if (const auto *PT = T->getAs()) + DesugaredType = PT->getPointeeType(); + DesugaredType = desugarStdTypedef(DesugaredType); + if (DesugaredType.isNull()) + return T; + if (const auto *ET = DesugaredType->getAs()) + DesugaredType = ET->desugar(); + DesugaredType = desugarStdTypedef(DesugaredType); + if (DesugaredType.isNull()) + return T; + return T.getCanonicalType(); +} + +// Given an operator call like std::string() + "", we would like to ensure +// that we return std::string instead of std::basic_string. +static QualType canonicalizeStdOperatorReturnType(const Expr *E, QualType T) { + const auto *OCE = dyn_cast(E->IgnoreParenImpCasts()); + if (!OCE) + return T; + if (OCE->getNumArgs() < 2 || !isInStdNamespace(OCE->getCalleeDecl())) + return T; + QualType CanonicalReturn = T.getCanonicalType(); + if (const auto *RD = CanonicalReturn->getAsCXXRecordDecl()) { + if (!isInStdNamespace(RD)) + return T; + } else + return T; + for (unsigned I = 0, E = OCE->getNumArgs(); I < E; ++I) { + const Expr *Arg = OCE->getArgs()[I]->IgnoreImpCasts(); + QualType T = Arg->getType(); + if (const auto *ET = dyn_cast(T)) + T = ET->desugar(); + if (desugarStdTypedef(T).isNull()) + continue; + QualType CanonicalArg = Arg->getType().getCanonicalType(); + CanonicalArg.removeLocalFastQualifiers(); + if (CanonicalArg == CanonicalReturn) { + QualType Result = Arg->getType(); + Result.removeLocalFastQualifiers(); + return Result; + } + } + return T; +} + +namespace clang { +namespace tooling { + +/// Tthe return type of the extracted function should match user's intent, +/// e.g. we want to use bool type whenever possible. +QualType findExpressionLexicalType(const Decl *FunctionLikeParentDecl, + const Expr *E, QualType T, + const PrintingPolicy &Policy, + const ASTContext &Ctx) { + // Get the correct property type. + if (const auto *PRE = dyn_cast(E)) { + if (PRE->isMessagingGetter()) { + if (PRE->isExplicitProperty()) { + QualType ReceiverType = PRE->getReceiverType(Ctx); + return PRE->getExplicitProperty()->getUsageType(ReceiverType); + } + if (const ObjCMethodDecl *M = PRE->getImplicitPropertyGetter()) { + if (!PRE->isObjectReceiver()) + return M->getSendResultType(PRE->getReceiverType(Ctx)); + const Expr *Base = PRE->getBase(); + return M->getSendResultType(findExpressionLexicalType( + FunctionLikeParentDecl, Base, Base->getType(), Policy, Ctx)); + } + } + } + + // Perform STL-specific type corrections. + if (Ctx.getLangOpts().CPlusPlus) { + T = desugarStdType(T); + T = canonicalizeStdOperatorReturnType(E, T); + } + + // The bool type adjustment is required only in C or Objective-C[++]. + if (Ctx.getLangOpts().CPlusPlus && !Ctx.getLangOpts().ObjC) + return T; + E = E->IgnoreParenImpCasts(); + if (const auto *BinOp = dyn_cast(E)) { + if (BinOp->isLogicalOp() || BinOp->isComparisonOp()) + return preferredBoolType(FunctionLikeParentDecl, E, T, Policy, Ctx); + } else if (const auto *UnOp = dyn_cast(E)) { + if (UnOp->getOpcode() == UO_LNot) + return preferredBoolType(FunctionLikeParentDecl, E, T, Policy, Ctx); + } + return T; +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactor/TypeUtils.h b/clang/lib/Tooling/Refactor/TypeUtils.h new file mode 100644 index 0000000000000..698147340c1d5 --- /dev/null +++ b/clang/lib/Tooling/Refactor/TypeUtils.h @@ -0,0 +1,34 @@ +//===--- TypeUtils.h - Type helper functions ------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_TYPE_UTILS_H +#define LLVM_CLANG_LIB_TOOLING_REFACTOR_TYPE_UTILS_H + +#include "clang/AST/Type.h" + +namespace clang { + +class Decl; + +namespace tooling { + +/// \brief Find the most lexically appropriate type that can be used to describe +/// the return type of the given expression \p E. +/// +/// When extracting code, we want to produce a function that returns a type +/// that matches the user's intent. This function can be used to find such a +/// type. +QualType findExpressionLexicalType(const Decl *FunctionLikeParentDecl, + const Expr *E, QualType T, + const PrintingPolicy &Policy, + const ASTContext &Ctx); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_TYPE_UTILS_H diff --git a/clang/lib/Tooling/Refactor/USRFinder.cpp b/clang/lib/Tooling/Refactor/USRFinder.cpp new file mode 100644 index 0000000000000..86b3c3e1babb0 --- /dev/null +++ b/clang/lib/Tooling/Refactor/USRFinder.cpp @@ -0,0 +1,705 @@ +//===--- USRFinder.cpp - Clang refactoring library ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file Implements a recursive AST visitor that finds the USR of a symbol at a +/// point. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactor/USRFinder.h" +#include "SourceLocationUtilities.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/Index/USRGeneration.h" +#include "clang/Lex/Lexer.h" +#include "clang/Sema/DependentASTVisitor.h" +#include "clang/Tooling/Refactoring/RefactoringDiagnostic.h" +#include "llvm/ADT/SmallVector.h" +#include + +using namespace llvm; + +namespace clang { +namespace tooling { +namespace rename { + +typedef std::function + OccurrenceCheckerType; + +// NamedDeclFindingASTVisitor recursively visits each AST node to find the +// symbol underneath the cursor. +// FIXME: move to seperate .h/.cc file if this gets too large. +namespace { +class NamedDeclFindingASTVisitor + : public DependentASTVisitor { +public: + // \brief Finds the NamedDecl at a point in the source. + // \param Point the location in the source to search for the NamedDecl. + explicit NamedDeclFindingASTVisitor( + const OccurrenceCheckerType &OccurrenceChecker, const ASTContext &Context) + : Result(nullptr), OccurrenceChecker(OccurrenceChecker), + Context(Context) {} + + // Declaration visitors: + + // \brief Checks if the point falls within the NameDecl. This covers every + // declaration of a named entity that we may come across. Usually, just + // checking if the point lies within the length of the name of the declaration + // and the start location is sufficient. + bool VisitNamedDecl(const NamedDecl *Decl) { + return dyn_cast(Decl) + ? true + : checkOccurrence(Decl, Decl->getLocation(), + Decl->getNameAsString().length()); + } + + bool WalkUpFromTypedefNameDecl(const TypedefNameDecl *D) { + // Don't visit the NamedDecl for TypedefNameDecl. + return VisitTypedefNamedDecl(D); + } + + bool VisitTypedefNamedDecl(const TypedefNameDecl *D) { + if (D->isTransparentTag()) { + if (const auto *Underlying = D->getUnderlyingType()->getAsTagDecl()) + return checkOccurrence(Underlying, D->getLocation(), + D->getNameAsString().size()); + } + return VisitNamedDecl(D); + } + + bool WalkUpFromUsingDecl(const UsingDecl *D) { + // Don't visit the NamedDecl for UsingDecl. + return VisitUsingDecl(D); + } + + bool VisitUsingDecl(const UsingDecl *D) { + for (const auto *Shadow : D->shadows()) { + // Currently we always find the first declaration, but is this the right + // behaviour? + const NamedDecl *UD = Shadow->getUnderlyingDecl(); + if (UD->isImplicit() || UD == D) + continue; + if (const auto *FTD = dyn_cast(UD)) { + UD = FTD->getTemplatedDecl(); + if (!UD) + continue; + } + if (!checkOccurrence(UD, D->getLocation())) + return false; + } + return true; + } + + bool WalkUpFromUsingDirectiveDecl(const UsingDirectiveDecl *D) { + // Don't visit the NamedDecl for UsingDirectiveDecl. + return VisitUsingDirectiveDecl(D); + } + + bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { + return checkOccurrence(D->getNominatedNamespaceAsWritten(), + D->getLocation()); + } + + bool WalkUpFromUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) { + // Don't visit the NamedDecl for UnresolvedUsingValueDecl. + // FIXME: Can we try to lookup the name? + return true; + } + + bool + WalkUpFromUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) { + // Don't visit the NamedDecl for UnresolvedUsingTypenameDecl. + // FIXME: Can we try to lookup the name? + return true; + } + + bool WalkUpFromObjCMethodDecl(const ObjCMethodDecl *Decl) { + // Don't visit the NamedDecl for Objective-C methods. + return VisitObjCMethodDecl(Decl); + } + + bool VisitObjCMethodDecl(const ObjCMethodDecl *Decl) { + // Check all of the selector source ranges. + for (unsigned I = 0, E = Decl->getNumSelectorLocs(); I != E; ++I) { + SourceLocation Loc = Decl->getSelectorLoc(I); + if (!checkOccurrence(Decl, Loc, + Loc.getLocWithOffset( + Decl->getSelector().getNameForSlot(I).size()))) + return false; + } + return true; + } + + bool VisitObjCProtocolList(const ObjCProtocolList &Protocols) { + for (unsigned I = 0, E = Protocols.size(); I != E; ++I) { + if (!checkOccurrence(Protocols[I], Protocols.loc_begin()[I])) + return false; + } + return true; + } + + bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) { + if (!Decl->hasDefinition()) + return true; + return VisitObjCProtocolList(Decl->getReferencedProtocols()); + } + + bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) { + if (!Decl->hasDefinition()) + return true; + return VisitObjCProtocolList(Decl->getReferencedProtocols()); + } + + bool WalkUpFromObjCCategoryDecl(const ObjCCategoryDecl *Decl) { + // Don't visit the NamedDecl for Objective-C categories because the location + // of the name refers to the interface declaration. + return VisitObjCCategoryDecl(Decl); + } + + bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) { + if (!checkOccurrence(Decl, Decl->getCategoryNameLoc())) + return false; + if (const auto *Class = Decl->getClassInterface()) { + // The location of the class name is the location of the declaration. + if (!checkOccurrence(Class, Decl->getLocation())) + return false; + } + return VisitObjCProtocolList(Decl->getReferencedProtocols()); + } + + bool WalkUpFromObjCCategoryImplDecl(const ObjCCategoryImplDecl *Decl) { + // Don't visit the NamedDecl for Objective-C categories because the location + // of the name refers to the interface declaration. + return VisitObjCCategoryImplDecl(Decl); + } + + bool VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *Decl) { + if (!checkOccurrence(Decl, Decl->getCategoryNameLoc())) + return false; + if (const auto *Class = Decl->getClassInterface()) { + // The location of the class name is the location of the declaration. + if (!checkOccurrence(Class, Decl->getLocation())) + return false; + } + return true; + } + + bool VisitObjCCompatibleAliasDecl(const ObjCCompatibleAliasDecl *Decl) { + return checkOccurrence(Decl->getClassInterface(), + Decl->getClassInterfaceLoc()); + } + + bool WalkUpFromObjCIvarDecl(ObjCIvarDecl *Decl) { + // Don't visit the NamedDecl for automatically synthesized ivars as the + // implicit ivars have the same location as the property declarations, and + // we want to find the property declarations. + if (Decl->getSynthesize()) + return true; + return RecursiveASTVisitor::WalkUpFromObjCIvarDecl(Decl); + } + + bool VisitObjCPropertyDecl(const ObjCPropertyDecl *Decl) { + if (Decl->hasExplicitGetterName()) { + if (const auto *Getter = Decl->getGetterMethodDecl()) + if (!checkOccurrence(Getter, Decl->getGetterNameLoc(), + Decl->getGetterName().getNameForSlot(0).size())) + return false; + } + if (Decl->hasExplicitSetterName()) { + if (const auto *Setter = Decl->getSetterMethodDecl()) + return checkOccurrence(Setter, Decl->getSetterNameLoc(), + Decl->getSetterName().getNameForSlot(0).size()); + } + return true; + } + + bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *Decl) { + if (!checkOccurrence(Decl->getPropertyDecl(), Decl->getLocation())) + return false; + if (Decl->isIvarNameSpecified()) + return checkOccurrence(Decl->getPropertyIvarDecl(), + Decl->getPropertyIvarDeclLoc()); + return true; + } + + // Expression visitors: + + bool VisitDeclRefExpr(const DeclRefExpr *Expr) { + const NamedDecl *Decl = Expr->getFoundDecl(); + return checkOccurrence(Decl, Expr->getLocation(), + Decl->getNameAsString().length()); + } + + bool VisitMemberExpr(const MemberExpr *Expr) { + const NamedDecl *Decl = Expr->getFoundDecl().getDecl(); + return checkOccurrence(Decl, Expr->getMemberLoc(), + Decl->getNameAsString().length()); + } + + bool VisitObjCMessageExpr(const ObjCMessageExpr *Expr) { + const ObjCMethodDecl *Decl = Expr->getMethodDecl(); + if (Decl == nullptr) + return true; + + // Check all of the selector source ranges. + for (unsigned I = 0, E = Expr->getNumSelectorLocs(); I != E; ++I) { + SourceLocation Loc = Expr->getSelectorLoc(I); + if (!checkOccurrence(Decl, Loc, + Loc.getLocWithOffset( + Decl->getSelector().getNameForSlot(I).size()))) + return false; + } + return true; + } + + bool VisitObjCProtocolExpr(const ObjCProtocolExpr *Expr) { + return checkOccurrence(Expr->getProtocol(), Expr->getProtocolIdLoc()); + } + + bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Expr) { + return checkOccurrence(Expr->getDecl(), Expr->getLocation()); + } + + bool VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *Expr) { + if (Expr->isClassReceiver()) + checkOccurrence(Expr->getClassReceiver(), Expr->getReceiverLocation()); + if (Expr->isImplicitProperty()) { + // Class properties that are explicitly defined using @property + // declarations are represented implicitly as there is no ivar for class + // properties. + if (const ObjCMethodDecl *Getter = Expr->getImplicitPropertyGetter()) { + if (Getter->isClassMethod()) { + if (const auto *PD = Getter->getCanonicalDecl()->findPropertyDecl()) + return checkOccurrence(PD, Expr->getLocation()); + } + } + + if (Expr->isMessagingGetter()) { + if (const ObjCMethodDecl *Getter = Expr->getImplicitPropertyGetter()) + return checkOccurrence(Getter, Expr->getLocation()); + } else if (const ObjCMethodDecl *Setter = + Expr->getImplicitPropertySetter()) { + return checkOccurrence(Setter, Expr->getLocation()); + } + + return true; + } + return checkOccurrence(Expr->getExplicitProperty(), Expr->getLocation()); + } + + // Other visitors: + + bool VisitTypeLoc(const TypeLoc Loc) { + const SourceLocation TypeBeginLoc = Loc.getBeginLoc(); + const SourceLocation TypeEndLoc = Lexer::getLocForEndOfToken( + TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts()); + if (const auto *TemplateTypeParm = + dyn_cast(Loc.getType())) + return checkOccurrence(TemplateTypeParm->getDecl(), TypeBeginLoc, + TypeEndLoc); + if (const auto *TemplateSpecType = + dyn_cast(Loc.getType())) { + return checkOccurrence( + TemplateSpecType->getTemplateName().getAsTemplateDecl(), TypeBeginLoc, + TypeEndLoc); + } + TypedefTypeLoc TTL = Loc.getAs(); + if (TTL) { + const auto *TND = TTL.getTypedefNameDecl(); + if (TND->isTransparentTag()) { + if (const auto *Underlying = TND->getUnderlyingType()->getAsTagDecl()) + return checkOccurrence(Underlying, TTL.getNameLoc()); + } + return checkOccurrence(TND, TTL.getNameLoc()); + } + TypeSpecTypeLoc TSTL = Loc.getAs(); + if (TSTL) { + return checkOccurrence(Loc.getType()->getAsTagDecl(), TSTL.getNameLoc()); + } + return true; + } + + bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc Loc) { + return checkOccurrence(Loc.getIFaceDecl(), Loc.getNameLoc()); + } + + bool VisitObjCObjectTypeLoc(ObjCObjectTypeLoc Loc) { + for (unsigned I = 0, E = Loc.getNumProtocols(); I < E; ++I) { + if (!checkOccurrence(Loc.getProtocol(I), Loc.getProtocolLoc(I))) + return false; + } + return true; + } + + bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) { + for (const auto *Initializer : ConstructorDecl->inits()) { + // Ignore implicit initializers. + if (!Initializer->isWritten()) + continue; + if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) { + const SourceLocation InitBeginLoc = Initializer->getSourceLocation(), + InitEndLoc = Lexer::getLocForEndOfToken( + InitBeginLoc, 0, Context.getSourceManager(), + Context.getLangOpts()); + if (!checkOccurrence(FieldDecl, InitBeginLoc, InitEndLoc)) + return false; + } + } + return true; + } + + bool VisitDependentSymbolReference(const NamedDecl *Symbol, + SourceLocation SymbolNameLoc) { + return checkOccurrence(Symbol, SymbolNameLoc); + } + + // Other: + + const NamedDecl *getNamedDecl() { return Result; } + + bool isDone() const { return Result; } + + // \brief Determines if a namespace qualifier contains the point. + // \returns false on success and sets Result. + void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) { + while (NameLoc) { + const NamespaceDecl *Decl = + NameLoc.getNestedNameSpecifier()->getAsNamespace(); + checkOccurrence(Decl, NameLoc.getLocalBeginLoc(), + NameLoc.getLocalEndLoc()); + NameLoc = NameLoc.getPrefix(); + } + } + +private: + /// \brief Sets Result to \p Decl if the occurrence checker returns true. + /// + /// \returns false on success. + bool checkRange(const NamedDecl *Decl, SourceLocation Start, + SourceLocation End) { + assert(!Start.isMacroID() && !End.isMacroID() && "Macro location?"); + if (!Decl) + return true; + if (isa(Decl)) + return true; + if (const auto *FD = dyn_cast(Decl)) { + // Don't match operators. + if (FD->isOverloadedOperator()) + return true; + } + if (!OccurrenceChecker(Decl, Start, End)) + return true; + Result = Decl; + return false; + } + + /// Checks if the given declaration is valid, and if it is, sets Result to + /// \p Decl if the occurrence checker returns true. + /// + /// \returns false if the point of interest is inside the range that + /// corresponds the occurrence of this declaration. + bool checkOccurrence(const NamedDecl *Decl, SourceLocation Loc) { + if (!Decl) + return true; + return checkOccurrence(Decl, Loc, Decl->getNameAsString().size()); + } + + /// \brief Sets Result to \p Decl if the occurrence checker returns true. + /// + /// \returns false on success. + bool checkOccurrence(const NamedDecl *Decl, SourceLocation Loc, + unsigned Length) { + if (Loc.isMacroID()) { + const SourceManager &SM = Context.getSourceManager(); + if (SM.isMacroArgExpansion(Loc)) + Loc = SM.getSpellingLoc(Loc); + else + return true; + } + + return Length == 0 || + checkRange(Decl, Loc, Loc.getLocWithOffset(Length - 1)); + } + + bool checkOccurrence(const NamedDecl *ND, SourceLocation Start, + SourceLocation End) { + const SourceManager &SM = Context.getSourceManager(); + if (Start.isMacroID()) { + if (SM.isMacroArgExpansion(Start)) + Start = SM.getSpellingLoc(Start); + else + return true; + } + if (End.isMacroID()) { + if (SM.isMacroArgExpansion(End)) + End = SM.getSpellingLoc(End); + else + return true; + } + return checkRange(ND, Start, End); + } + + const NamedDecl *Result; + const OccurrenceCheckerType &OccurrenceChecker; + const ASTContext &Context; +}; + +} // namespace + +static const ExternalSourceSymbolAttr *getExternalSymAttr(const Decl *D) { + if (const auto *A = D->getAttr()) + return A; + if (const auto *DCD = dyn_cast(D->getDeclContext())) { + if (const auto *A = DCD->getAttr()) + return A; + } + return nullptr; +} + +static bool overridesSystemMethod(const ObjCMethodDecl *MD, + const SourceManager &SM) { + SmallVector Overrides; + MD->getOverriddenMethods(Overrides); + for (const auto *Override : Overrides) { + SourceLocation Loc = Override->getBeginLoc(); + if (Loc.isValid()) { + if (SM.getFileCharacteristic(Loc) != SrcMgr::C_User) + return true; + } + if (overridesSystemMethod(Override, SM)) + return true; + } + return false; +} + +// TODO: Share with the indexer? +static bool isTemplateImplicitInstantiation(const Decl *D) { + TemplateSpecializationKind TKind = TSK_Undeclared; + if (const ClassTemplateSpecializationDecl *SD = + dyn_cast(D)) { + TKind = SD->getSpecializationKind(); + } else if (const FunctionDecl *FD = dyn_cast(D)) { + TKind = FD->getTemplateSpecializationKind(); + } else if (auto *VD = dyn_cast(D)) { + TKind = VD->getTemplateSpecializationKind(); + } else if (const auto *RD = dyn_cast(D)) { + if (RD->getInstantiatedFromMemberClass()) + TKind = RD->getTemplateSpecializationKind(); + } else if (const auto *ED = dyn_cast(D)) { + if (ED->getInstantiatedFromMemberEnum()) + TKind = ED->getTemplateSpecializationKind(); + } else if (isa(D) || isa(D) || + isa(D)) { + if (const auto *Parent = dyn_cast(D->getDeclContext())) + return isTemplateImplicitInstantiation(Parent); + } + switch (TKind) { + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + return false; + case TSK_ImplicitInstantiation: + case TSK_ExplicitInstantiationDeclaration: + case TSK_ExplicitInstantiationDefinition: + return true; + } + llvm_unreachable("invalid TemplateSpecializationKind"); +} + +static const CXXRecordDecl * +getDeclContextForTemplateInstationPattern(const Decl *D) { + if (const auto *CTSD = + dyn_cast(D->getDeclContext())) + return CTSD->getTemplateInstantiationPattern(); + else if (const auto *RD = dyn_cast(D->getDeclContext())) + return RD->getInstantiatedFromMemberClass(); + return nullptr; +} + +static const NamedDecl * +adjustTemplateImplicitInstantiation(const NamedDecl *D) { + if (const ClassTemplateSpecializationDecl *SD = + dyn_cast(D)) { + return SD->getTemplateInstantiationPattern(); + } else if (const FunctionDecl *FD = dyn_cast(D)) { + return FD->getTemplateInstantiationPattern(); + } else if (auto *VD = dyn_cast(D)) { + return VD->getTemplateInstantiationPattern(); + } else if (const auto *RD = dyn_cast(D)) { + return RD->getInstantiatedFromMemberClass(); + } else if (const auto *ED = dyn_cast(D)) { + return ED->getInstantiatedFromMemberEnum(); + } else if (isa(D) || isa(D)) { + const auto *ND = cast(D); + if (const CXXRecordDecl *Pattern = + getDeclContextForTemplateInstationPattern(ND)) { + for (const NamedDecl *BaseND : Pattern->lookup(ND->getDeclName())) { + if (BaseND->isImplicit()) + continue; + if (BaseND->getKind() == ND->getKind()) + return BaseND; + } + } + } else if (const auto *ECD = dyn_cast(D)) { + if (const auto *ED = dyn_cast(ECD->getDeclContext())) { + if (const EnumDecl *Pattern = ED->getInstantiatedFromMemberEnum()) { + for (const NamedDecl *BaseECD : Pattern->lookup(ECD->getDeclName())) + return BaseECD; + } + } + } + return D; +} + +const NamedDecl *getNamedDeclAt(const ASTContext &Context, + SourceLocation Point) { + if (Point.isMacroID()) + Point = Context.getSourceManager().getSpellingLoc(Point); + // FIXME: If point is in a system header, return early here. + + OccurrenceCheckerType PointChecker = [Point, &Context]( + const NamedDecl *Decl, SourceLocation Start, SourceLocation End) -> bool { + return Start.isValid() && Start.isFileID() && End.isValid() && + End.isFileID() && + isPointWithin(Point, Start, End, Context.getSourceManager()); + }; + NamedDeclFindingASTVisitor Visitor(PointChecker, Context); + + // We only want to search the decls that exist in the same file as the point. + FileID InitiationFile = Context.getSourceManager().getFileID(Point); + for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) { + const SourceRange DeclRange = CurrDecl->getSourceRange(); + SourceLocation FileLoc; + if (DeclRange.getBegin().isMacroID() && !DeclRange.getEnd().isMacroID()) + FileLoc = DeclRange.getEnd(); + else + FileLoc = Context.getSourceManager().getSpellingLoc(DeclRange.getBegin()); + // FIXME: Add test. + if (Context.getSourceManager().getFileID(FileLoc) == InitiationFile) + Visitor.TraverseDecl(CurrDecl); + if (Visitor.isDone()) + break; + } + + if (!Visitor.isDone()) { + NestedNameSpecifierLocFinder Finder(const_cast(Context)); + for (const auto &Location : Finder.getNestedNameSpecifierLocations()) { + Visitor.handleNestedNameSpecifierLoc(Location); + if (Visitor.isDone()) + break; + } + } + + const auto Diag = [&](unsigned DiagID) -> DiagnosticBuilder { + return Context.getDiagnostics().Report(Point, DiagID); + }; + const auto *ND = Visitor.getNamedDecl(); + if (!ND) + return nullptr; + + // Canonicalize the found declaration. + // + // If FoundDecl is a constructor or destructor, we want to instead take + // the Decl of the corresponding class. + if (const auto *CtorDecl = dyn_cast(ND)) + ND = CtorDecl->getParent(); + else if (const auto *DtorDecl = dyn_cast(ND)) + ND = DtorDecl->getParent(); + + if (isTemplateImplicitInstantiation(ND)) + ND = adjustTemplateImplicitInstantiation(ND); + + // Builtins can't be renamed. + if (const auto *FD = dyn_cast(ND)) { + if (FD->getBuiltinID()) { + Diag(diag::err_rename_builtin_function) << ND->getDeclName(); + return nullptr; + } + } + // Declarations with invalid locations are probably implicit. + if (ND->getBeginLoc().isInvalid()) + return nullptr; + // Declarations in system headers can't be renamed. + auto CheckSystemLoc = [&](SourceLocation Loc) -> bool { + if (Context.getSourceManager().getFileCharacteristic(Loc) != + SrcMgr::C_User) { + Diag(diag::err_rename_sys_header) << ND->getDeclName(); + return true; + } + return false; + }; + if (CheckSystemLoc(ND->getBeginLoc())) + return nullptr; + if (const auto *TD = dyn_cast(ND)) { + if (const TypedefNameDecl *CTD = TD->getCanonicalDecl()) { + if (CheckSystemLoc(CTD->getBeginLoc())) + return nullptr; + } + } else if (const auto *TD = dyn_cast(ND)) { + if (const TagDecl *CTD = TD->getCanonicalDecl()) { + if (CheckSystemLoc(CTD->getBeginLoc())) + return nullptr; + } + } else if (const auto *FD = dyn_cast(ND)) { + if (const FunctionDecl *CFD = FD->getCanonicalDecl()) { + if (CheckSystemLoc(CFD->getBeginLoc())) + return nullptr; + } + } else if (const auto *VD = dyn_cast(ND)) { + if (const VarDecl *CVD = VD->getCanonicalDecl()) { + if (CheckSystemLoc(CVD->getBeginLoc())) + return nullptr; + } + } + // Declarations from other languages can't be renamed. + if (const ExternalSourceSymbolAttr *ESSA = getExternalSymAttr(ND)) { + Diag(diag::err_rename_external_source_symbol) << ND->getDeclName() + << ESSA->getLanguage(); + return nullptr; + } + // Methods that override methods from system headers can't be renamed. + if (const auto *MD = dyn_cast(ND)) { + if (overridesSystemMethod(MD, Context.getSourceManager())) { + Diag(diag::err_method_rename_override_sys_framework) << ND->getDeclName(); + return nullptr; + } + } + return ND; +} + +const NamedDecl *getNamedDeclWithUSR(const ASTContext &Context, StringRef USR) { + // TODO: Remove in favour of the new converter. + OccurrenceCheckerType USRChecker = + [USR](const NamedDecl *Decl, SourceLocation Start, SourceLocation End) { + return USR == getUSRForDecl(Decl); + }; + NamedDeclFindingASTVisitor Visitor(USRChecker, Context); + + for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) { + Visitor.TraverseDecl(CurrDecl); + if (Visitor.isDone()) + break; + } + + // Don't need to visit nested name specifiers as they refer to previously + // declared declarations that we've already seen. + return Visitor.getNamedDecl(); +} + +std::string getUSRForDecl(const Decl *Decl) { + llvm::SmallVector Buff; + + // FIXME: Add test for the nullptr case. + if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff)) + return ""; + + return std::string(Buff.data(), Buff.size()); +} + +} // end namespace rename +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Refactoring/CMakeLists.txt b/clang/lib/Tooling/Refactoring/CMakeLists.txt index d3077be8810aa..f78f64ea2ef64 100644 --- a/clang/lib/Tooling/Refactoring/CMakeLists.txt +++ b/clang/lib/Tooling/Refactoring/CMakeLists.txt @@ -13,6 +13,7 @@ add_clang_library(clangToolingRefactoring Rename/USRFinder.cpp Rename/USRFindingAction.cpp Rename/USRLocFinder.cpp + SymbolName.cpp LINK_LIBS clangAST @@ -23,6 +24,7 @@ add_clang_library(clangToolingRefactoring clangLex clangRewrite clangToolingCore + clangToolingSyntax DEPENDS omp_gen diff --git a/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp b/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp index 72598601d47d6..20802d3a5f101 100644 --- a/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp +++ b/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp @@ -82,7 +82,7 @@ RenameOccurrences::createSourceReplacements(RefactoringRuleContext &Context) { if (!Occurrences) return Occurrences.takeError(); // FIXME: Verify that the new name is valid. - SymbolName Name(NewName); + SymbolName Name(NewName, /*IsObjectiveCSelector=*/false); return createRenameReplacements( *Occurrences, Context.getASTContext().getSourceManager(), Name); } @@ -219,7 +219,7 @@ class RenamingASTConsumer : public ASTConsumer { } // FIXME: Support multi-piece names. // FIXME: better error handling (propagate error out). - SymbolName NewNameRef(NewName); + SymbolName NewNameRef(NewName, /*IsObjectiveCSelector=*/false); Expected> Change = createRenameReplacements(Occurrences, SourceMgr, NewNameRef); if (!Change) { @@ -275,5 +275,99 @@ std::unique_ptr QualifiedRenamingAction::newASTConsumer() { return std::make_unique(NewNames, USRList, FileToReplaces); } +static bool isMatchingSelectorName(const syntax::Token &Tok, + const syntax::Token &Next, + StringRef NamePiece, + const SourceManager &SrcMgr) { + if (NamePiece.empty()) + return Tok.kind() == tok::colon; + return (Tok.kind() == tok::identifier || Tok.kind() == tok::raw_identifier) && + Next.kind() == tok::colon && Tok.text(SrcMgr) == NamePiece; +} + +Error findObjCSymbolSelectorPieces(ArrayRef AllTokens, + const SourceManager &SM, + SourceLocation RenameLoc, + const SymbolName &OldName, + ObjCSymbolSelectorKind Kind, + SmallVectorImpl &Result) { + ArrayRef Tokens = + AllTokens.drop_while([RenameLoc](syntax::Token Tok) -> bool { + return Tok.location() != RenameLoc; + }); + assert(!Tokens.empty() && "no tokens"); + assert(OldName.getNamePieces()[0].empty() || + Tokens[0].text(SM) == OldName.getNamePieces()[0]); + assert(OldName.getNamePieces().size() > 1); + + Result.push_back(Tokens[0].location()); + + // We have to track square brackets, parens and braces as we want to skip the + // tokens inside them. This ensures that we don't use identical selector + // pieces in inner message sends, blocks, lambdas and @selector expressions. + unsigned SquareCount = 0; + unsigned ParenCount = 0; + unsigned BraceCount = 0; + + // Start looking for the next selector piece. + unsigned Last = Tokens.size() - 1; + // Skip the ':' or any other token after the first selector piece token. + for (unsigned Index = OldName.getNamePieces()[0].empty() ? 1 : 2; + Index < Last; ++Index) { + const auto &Tok = Tokens[Index]; + + bool NoScoping = SquareCount == 0 && BraceCount == 0 && ParenCount == 0; + if (NoScoping && + isMatchingSelectorName(Tok, Tokens[Index + 1], + OldName.getNamePieces()[Result.size()], SM)) { + if (!OldName.getNamePieces()[Result.size()].empty()) { + // Skip the ':' after the name. This ensures that it won't match a + // follow-up selector piece with an empty name. + ++Index; + } + Result.push_back(Tok.location()); + // All the selector pieces have been found. + if (Result.size() == OldName.getNamePieces().size()) + return Error::success(); + } else if (Tok.kind() == tok::r_square) { + // Stop scanning at the end of the message send. + // Also account for spurious ']' in blocks or lambdas. + if (Kind == ObjCSymbolSelectorKind::MessageSend && !SquareCount && + !BraceCount) + break; + if (SquareCount) + --SquareCount; + } else if (Tok.kind() == tok::l_square) { + ++SquareCount; + } else if (Tok.kind() == tok::l_paren) { + ++ParenCount; + } else if (Tok.kind() == tok::r_paren) { + if (!ParenCount) + break; + --ParenCount; + } else if (Tok.kind() == tok::l_brace) { + // Stop scanning at the start of the of the method's body. + // Also account for any spurious blocks inside argument parameter types + // or parameter attributes. + if (Kind == ObjCSymbolSelectorKind::MethodDecl && !BraceCount && + !ParenCount) + break; + ++BraceCount; + } else if (Tok.kind() == tok::r_brace) { + if (!BraceCount) + break; + --BraceCount; + } + // Stop scanning at the end of the method's declaration. + if (Kind == ObjCSymbolSelectorKind::MethodDecl && NoScoping && + (Tok.kind() == tok::semi || Tok.kind() == tok::minus || + Tok.kind() == tok::plus)) + break; + } + return llvm::make_error( + "failed to find all selector pieces in the source code", + inconvertibleErrorCode()); +} + } // end namespace tooling } // end namespace clang diff --git a/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp b/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp index c18f9290471fe..43e48f24caa9e 100644 --- a/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp +++ b/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp @@ -60,8 +60,8 @@ class USRLocFindingASTVisitor const ASTContext &Context) : RecursiveSymbolVisitor(Context.getSourceManager(), Context.getLangOpts()), - USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) { - } + USRSet(USRs.begin(), USRs.end()), + PrevName(PrevName, /*IsObjectiveCSelector=*/false), Context(Context) {} bool visitSymbolOccurrence(const NamedDecl *ND, ArrayRef NameRanges) { diff --git a/clang/lib/Tooling/Refactoring/SymbolName.cpp b/clang/lib/Tooling/Refactoring/SymbolName.cpp new file mode 100644 index 0000000000000..896a6cf09a3a9 --- /dev/null +++ b/clang/lib/Tooling/Refactoring/SymbolName.cpp @@ -0,0 +1,70 @@ +//===--- SymbolName.cpp - Clang refactoring library -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Refactoring/Rename/SymbolName.h" +#include "clang/Basic/LangOptions.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace tooling { + +SymbolName::SymbolName() : NamePieces({}) {} + +SymbolName::SymbolName(const DeclarationName &DeclName) + : SymbolName(DeclName.getAsString(), + /*IsObjectiveCSelector=*/DeclName.getNameKind() == + DeclarationName::NameKind::ObjCMultiArgSelector || + DeclName.getNameKind() == + DeclarationName::NameKind::ObjCOneArgSelector) {} + +SymbolName::SymbolName(StringRef Name, const LangOptions &LangOpts) + : SymbolName(Name, LangOpts.ObjC) {} + +SymbolName::SymbolName(StringRef Name, bool IsObjectiveCSelector) { + if (!IsObjectiveCSelector) { + NamePieces.push_back(Name.str()); + return; + } + // Decompose an Objective-C selector name into multiple strings. + do { + auto StringAndName = Name.split(':'); + NamePieces.push_back(StringAndName.first.str()); + Name = StringAndName.second; + } while (!Name.empty()); +} + +SymbolName::SymbolName(ArrayRef NamePieces) { + for (const auto &Piece : NamePieces) + this->NamePieces.push_back(Piece.str()); +} + +SymbolName::SymbolName(ArrayRef NamePieces) { + for (const auto &Piece : NamePieces) + this->NamePieces.push_back(Piece); +} + +std::optional SymbolName::getSinglePiece() const { + if (getNamePieces().size() == 1) { + return NamePieces.front(); + } + return std::nullopt; +} + +std::string SymbolName::getAsString() const { + std::string Result; + llvm::raw_string_ostream OS(Result); + this->print(OS); + return Result; +} + +void SymbolName::print(raw_ostream &OS) const { + llvm::interleave(NamePieces, OS, ":"); +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Syntax/Tokens.cpp b/clang/lib/Tooling/Syntax/Tokens.cpp index 0a656dff38421..08a7ad085acb8 100644 --- a/clang/lib/Tooling/Syntax/Tokens.cpp +++ b/clang/lib/Tooling/Syntax/Tokens.cpp @@ -225,6 +225,13 @@ llvm::StringRef FileRange::text(const SourceManager &SM) const { return Text.substr(Begin, length()); } +UnexpandedTokenBuffer::UnexpandedTokenBuffer(StringRef Code, + const LangOptions &LangOpts) { + SrcMgr = std::make_unique("mock_file_name.cpp", Code); + Tokens = syntax::tokenize(sourceManager().getMainFileID(), sourceManager(), + LangOpts); +} + void TokenBuffer::indexExpandedTokens() { // No-op if the index is already created. if (!ExpandedTokIndex.empty()) diff --git a/clang/test/APINotes/Inputs/Frameworks/CXXInteropKit.framework/Headers/CXXInteropKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/CXXInteropKit.framework/Headers/CXXInteropKit.apinotes new file mode 100644 index 0000000000000..6bfb8701da6be --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/CXXInteropKit.framework/Headers/CXXInteropKit.apinotes @@ -0,0 +1,15 @@ +--- +Name: CxxInterop +Classes: +- Name: NSSomeClass + SwiftName: SomeClass + Methods: + - Selector: 'didMoveToParentViewController:' + SwiftName: didMove(toParent:) + MethodKind: Instance +Enumerators: +- Name: SomeClassRed + SwiftName: red +Tags: +- Name: NSSomeEnumOptions + SwiftName: SomeEnum.Options diff --git a/clang/test/APINotes/Inputs/Frameworks/CXXInteropKit.framework/Headers/CXXInteropKit.h b/clang/test/APINotes/Inputs/Frameworks/CXXInteropKit.framework/Headers/CXXInteropKit.h new file mode 100644 index 0000000000000..0fba5044d355a --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/CXXInteropKit.framework/Headers/CXXInteropKit.h @@ -0,0 +1,23 @@ +@interface NSSomeClass + -(instancetype)init; +@end + +// Extension, inspired by UIKit UIViewController.h +@interface NSSomeClass (UIContainerViewControllerCallbacks) + +- (void)didMoveToParentViewController:(NSSomeClass *)parent; + +@end + +// Named "SomeClassRed" for ast node filtering in the test. +enum ColorEnum { SomeClassRed, SomeClassGreen, SomeClassBlue }; + +#define CF_OPTIONS(_type, _name) _type __attribute__((availability(swift, unavailable))) _name; enum : _name +#define NS_OPTIONS(_type, _name) CF_OPTIONS(_type, _name) + +typedef unsigned long NSUInteger; +typedef NS_OPTIONS(NSUInteger, NSSomeEnumOptions) { + NSSomeEnumWithRed = 1, + NSSomeEnumWithGreen, + NSSomeEnumWithBlue, +}; diff --git a/clang/test/APINotes/Inputs/Frameworks/CXXInteropKit.framework/Modules/module.modulemap b/clang/test/APINotes/Inputs/Frameworks/CXXInteropKit.framework/Modules/module.modulemap new file mode 100644 index 0000000000000..6e788ed37690e --- /dev/null +++ b/clang/test/APINotes/Inputs/Frameworks/CXXInteropKit.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module CXXInteropKit [extern_c] { + umbrella header "CXXInteropKit.h" + export * + module * { export * } +} diff --git a/clang/test/APINotes/lifetimebound.cpp b/clang/test/APINotes/lifetimebound.cpp index f6fdb8535b181..6a2d439eb56ba 100644 --- a/clang/test/APINotes/lifetimebound.cpp +++ b/clang/test/APINotes/lifetimebound.cpp @@ -11,6 +11,8 @@ // CHECK-METHOD: CXXMethodDecl {{.+}} methodToAnnotate // CHECK-METHOD-NEXT: ParmVarDecl {{.+}} p +// CHECK-METHOD-NEXT: SwiftVersionedAdditionAttr +// CHECK-METHOD-NEXT: LifetimeBoundAttr // CHECK-METHOD-NEXT: LifetimeBoundAttr // CHECK-METHOD-THIS: CXXMethodDecl {{.+}} annotateThis 'int *() {{\[\[}}clang::lifetimebound{{\]\]}}' diff --git a/clang/test/APINotes/objcxx-swift-name.m b/clang/test/APINotes/objcxx-swift-name.m new file mode 100644 index 0000000000000..dbbb54310ad5b --- /dev/null +++ b/clang/test/APINotes/objcxx-swift-name.m @@ -0,0 +1,23 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -x objective-c++ +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter SomeClass -x objective-c++ | FileCheck %s +// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/CxxInterop -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter "unnamed enum" -x objective-c++ | FileCheck -check-prefix=CHECK-ANONYMOUS-ENUM %s + +#import + +// CHECK: Dumping NSSomeClass: +// CHECK-NEXT: ObjCInterfaceDecl {{.+}} imported in CXXInteropKit NSSomeClass +// CHECK-NEXT: SwiftNameAttr {{.+}} <> "SomeClass" + +// CHECK: Dumping NSSomeClass::didMoveToParentViewController:: +// CHECK-NEXT: ObjCMethodDecl {{.+}} imported in CXXInteropKit - didMoveToParentViewController: 'void' +// CHECK-NEXT: ParmVarDecl +// CHECK-NEXT: SwiftNameAttr {{.+}} <> "didMove(toParent:)" + +// CHECK: Dumping SomeClassRed: +// CHECK-NEXT: EnumConstantDecl {{.+}} imported in CXXInteropKit SomeClassRed 'ColorEnum' +// CHECK-NEXT: SwiftNameAttr {{.+}} <> "red" + +// CHECK-ANONYMOUS-ENUM: Dumping (unnamed enum at {{.+}}/Frameworks/CXXInteropKit.framework/Headers/CXXInteropKit.h:19:9): +// CHECK-ANONYMOUS-ENUM-NEXT: EnumDecl {{.+}} imported in CXXInteropKit 'NSSomeEnumOptions':'unsigned long' +// CHECK-ANONYMOUS-ENUM-NEXT: SwiftNameAttr {{.+}} <> "SomeEnum.Options" diff --git a/clang/test/APINotes/search-order.m b/clang/test/APINotes/search-order.m index 17e81d5eb2d69..aa2f21a2eaaa3 100644 --- a/clang/test/APINotes/search-order.m +++ b/clang/test/APINotes/search-order.m @@ -1,6 +1,6 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_SEARCH_PATH=1 -verify diff --git a/clang/test/AST/Inputs/objc-method-tracing.h b/clang/test/AST/Inputs/objc-method-tracing.h new file mode 100644 index 0000000000000..ea71c42c8dd88 --- /dev/null +++ b/clang/test/AST/Inputs/objc-method-tracing.h @@ -0,0 +1,19 @@ +@interface A +-(void)m0; +@end + +@interface B +-(void)m0; ++(void)m1; +@end + +#define CDef \ +@interface C \ +-(void)m4; \ +@end + +CDef + +@protocol P0 +-(void)m5; +@end diff --git a/clang/test/AST/ast-dump-decl-json.m b/clang/test/AST/ast-dump-decl-json.m index f7067ac0d3b77..b50cf637e6083 100644 --- a/clang/test/AST/ast-dump-decl-json.m +++ b/clang/test/AST/ast-dump-decl-json.m @@ -1042,8 +1042,8 @@ void f(void) { // CHECK-NEXT: "offset": {{[0-9]+}}, // CHECK-NEXT: "file": "{{.*}}", // CHECK-NEXT: "line": 60, -// CHECK-NEXT: "col": 1, -// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: "col": 22, +// CHECK-NEXT: "tokLen": 27 // CHECK-NEXT: }, // CHECK-NEXT: "range": { // CHECK-NEXT: "begin": { diff --git a/clang/test/AST/ast-dump-decl.m b/clang/test/AST/ast-dump-decl.m index 5f09b6042f409..ffd3f9439f375 100644 --- a/clang/test/AST/ast-dump-decl.m +++ b/clang/test/AST/ast-dump-decl.m @@ -162,3 +162,6 @@ void f(void) { __typeof__(B.foo) Test; } // CHECK: VarDecl{{.*}}Test 'typeof (B.foo)':'int' + +@compatibility_alias TestCompatibilityAlias A; +// CHECK: ObjCCompatibleAliasDecl{{.*}}col:22 TestCompatibilityAlias col:45 diff --git a/clang/test/AST/objc-method-tracing.mm b/clang/test/AST/objc-method-tracing.mm new file mode 100644 index 0000000000000..dc80b3faef93b --- /dev/null +++ b/clang/test/AST/objc-method-tracing.mm @@ -0,0 +1,129 @@ +// RUN: env CLANG_COMPILER_OBJC_MESSAGE_TRACE_PATH=%t.txt %clang_cc1 -fsyntax-only -triple arm64-apple-macosx15.0.0 -I %S/Inputs %s +// RUN: cat %t.txt | FileCheck %s + +#include "objc-method-tracing.h" + +@interface B(Cat1) +-(void)m2; +@end + +@interface B() +-(void)m6; +@end + +@implementation B +-(void)m7:(int)i arg1:(float)f { +} +@end + +@implementation B(Cat1) +-(void)m8 { +} +@end + +void test0(A *a) { + [a m0]; +} + +void test1(B *b) { + [b m0]; +} + +void test2(B *b) { + [B m1]; +} + +void test3(B *b) { + [b m2]; +} + +void test4(C *c) { + [c m4]; +} + +void test5(id p0) { + [p0 m5]; +} + +void test6(B *b) { + [b m6]; +} + +void test7(B *b) { + [b m7:123 arg1:4.5f]; +} + +void test8(B *b) { + [b m8]; +} + +// CHECK: { +// CHECK-NEXT: "clang-compiler-version": "{{.*}}clang version +// CHECK-NEXT: "format-version": 1, +// CHECK-NEXT: "target": "arm64-apple-macosx15.0.0", +// CHECK-NEXT: "references": [ +// CHECK-NEXT: { +// CHECK-NEXT: "interface_type": "A", +// CHECK-NEXT: "instance_method": "-[A m0]", +// CHECK-NEXT: "declared_at": "[[HEADER_FILE:.*]]:2:1", +// CHECK-NEXT: "referenced_at_file_id": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "interface_type": "B", +// CHECK-NEXT: "instance_method": "-[B m0]", +// CHECK-NEXT: "declared_at": "[[HEADER_FILE]]:6:1", +// CHECK-NEXT: "referenced_at_file_id": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "interface_type": "B", +// CHECK-NEXT: "class_method": "+[B m1]", +// CHECK-NEXT: "declared_at": "[[HEADER_FILE]]:7:1", +// CHECK-NEXT: "referenced_at_file_id": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "interface_type": "B", +// CHECK-NEXT: "category_type": "Cat1", +// CHECK-NEXT: "instance_method": "-[B(Cat1) m2]", +// CHECK-NEXT: "declared_at": "[[SOURCE_FILE:.*]]:7:1", +// CHECK-NEXT: "referenced_at_file_id": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "interface_type": "C", +// CHECK-NEXT: "instance_method": "-[C m4]", +// CHECK-NEXT: "declared_at": "[[HEADER_FILE]]:15:1 ", +// CHECK-NEXT: "referenced_at_file_id": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "protocol_type": "P0", +// CHECK-NEXT: "instance_method": "-[P0 m5]", +// CHECK-NEXT: "declared_at": "[[HEADER_FILE]]:18:1", +// CHECK-NEXT: "referenced_at_file_id": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "interface_type": "B", +// CHECK-NEXT: "category_type": "", +// CHECK-NEXT: "instance_method": "-[B() m6]", +// CHECK-NEXT: "declared_at": "[[SOURCE_FILE]]:11:1", +// CHECK-NEXT: "referenced_at_file_id": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "implementation_type": "B", +// CHECK-NEXT: "instance_method": "-[B m7:arg1:]", +// CHECK-NEXT: "declared_at": "[[SOURCE_FILE]]:15:1", +// CHECK-NEXT: "referenced_at_file_id": 1 +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "interface_type": "B", +// CHECK-NEXT: "category_implementation_type": "Cat1", +// CHECK-NEXT: "instance_method": "-[B(Cat1) m8]", +// CHECK-NEXT: "declared_at": "[[SOURCE_FILE]]:20:1", +// CHECK-NEXT: "referenced_at_file_id": 1 +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "fileMap": [ +// CHECK-NEXT: { +// CHECK-NEXT: "file_id": 1, +// CHECK-NEXT: "file_path": "[[SOURCE_FILE]]" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } diff --git a/clang/test/Analysis/misc-ps.m b/clang/test/Analysis/misc-ps.m index 9c19d0cb4556f..7302c3adf7a49 100644 --- a/clang/test/Analysis/misc-ps.m +++ b/clang/test/Analysis/misc-ps.m @@ -1075,7 +1075,7 @@ void test_enum_cases(enum Cases C) { } void test_enum_cases_positive(enum Cases C) { - switch (C) { // expected-warning{{enumeration value 'C4' not handled in switch}} + switch (C) { // expected-warning{{enumeration value 'C4' not handled in switch}} expected-note {{add missing switch cases}} case C1: case C2: case C3: diff --git a/clang/test/BoundsSafety/AST/Inputs/abi-ptr-attr-unsafe/mock-system-header.h b/clang/test/BoundsSafety/AST/Inputs/abi-ptr-attr-unsafe/mock-system-header.h new file mode 100644 index 0000000000000..68acd6e429396 --- /dev/null +++ b/clang/test/BoundsSafety/AST/Inputs/abi-ptr-attr-unsafe/mock-system-header.h @@ -0,0 +1,7 @@ +#pragma clang system_header + +inline void FUnspecified(int *x) { + int *y = x; + y = y + 1; + int **z = &y; +} diff --git a/clang/test/BoundsSafety/AST/Inputs/system-count-return/mock-header.h b/clang/test/BoundsSafety/AST/Inputs/system-count-return/mock-header.h new file mode 100644 index 0000000000000..2633dc2bf06f4 --- /dev/null +++ b/clang/test/BoundsSafety/AST/Inputs/system-count-return/mock-header.h @@ -0,0 +1,4 @@ +#include + +int *__sized_by(len) alloc_sized_by(int len); +int *alloc_attributed(int len) __attribute__((alloc_size(1))); diff --git a/clang/test/BoundsSafety/AST/Inputs/system-merge-dynamic-bound-attr/header-no-attr.h b/clang/test/BoundsSafety/AST/Inputs/system-merge-dynamic-bound-attr/header-no-attr.h new file mode 100644 index 0000000000000..83450c6d4f38c --- /dev/null +++ b/clang/test/BoundsSafety/AST/Inputs/system-merge-dynamic-bound-attr/header-no-attr.h @@ -0,0 +1,2 @@ +void *myalloc(unsigned); +void *memcpy(void *, void *, unsigned long long); \ No newline at end of file diff --git a/clang/test/BoundsSafety/AST/Inputs/system-merge-dynamic-bound-attr/header-with-attr.h b/clang/test/BoundsSafety/AST/Inputs/system-merge-dynamic-bound-attr/header-with-attr.h new file mode 100644 index 0000000000000..00f4859f1d735 --- /dev/null +++ b/clang/test/BoundsSafety/AST/Inputs/system-merge-dynamic-bound-attr/header-with-attr.h @@ -0,0 +1 @@ +void *myalloc(unsigned) __attribute__((alloc_size(1))); \ No newline at end of file diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/array-param-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/array-param-main.c new file mode 100644 index 0000000000000..68db2368f9416 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/array-param-main.c @@ -0,0 +1,9 @@ + +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include | FileCheck %S/include/array-param-sys.h --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --implicit-check-not RecoveryExpr + +void foo(int * __counted_by(size) arr, int size) { + funcInSDK(size, arr); +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/builtin-function-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/builtin-function-main.c new file mode 100644 index 0000000000000..c2b1dafbfeb0a --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/builtin-function-main.c @@ -0,0 +1,312 @@ +#include +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" + +char * __counted_by(len) func(char * __counted_by(len) src_str, int len) { + int len2 = 0; + char * __counted_by(len2) dst_str; + dst_str = __unsafe_forge_bidi_indexable(char*, malloc(len), len); + len2 = len; + memcpy(dst_str, src_str, len); + return dst_str; +} + +// CHECK: TranslationUnitDecl +// CHECK: |-FunctionDecl [[func_static:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_dst:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_src:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: | | | | `-AssumptionExpr +// CHECK: | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_dst]] +// CHECK: | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_src]] +// CHECK: | | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | | `-OpaqueValueExpr [[ove]] +// CHECK: | | `-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(function-parameter-0-2)(*)(void *__single __sized_by(function-parameter-0-2), const void *__single __sized_by(function-parameter-0-2), unsigned long)' +// CHECK: | | | `-DeclRefExpr {{.+}} +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const void *__single __sized_by(function-parameter-0-2)':'const void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'void *' +// CHECK: | |-OpaqueValueExpr [[ove_2]] {{.*}} 'void *' +// CHECK: | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: |-FunctionDecl [[func_static_1:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_dst_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_src_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: | | | | `-AssumptionExpr +// CHECK: | | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_dst_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_src_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_1]] +// CHECK: | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(function-parameter-0-2)(*)(void *__single __sized_by(function-parameter-0-2), const void *__single __sized_by(function-parameter-0-2), unsigned long)' +// CHECK: | | | `-DeclRefExpr {{.+}} +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const void *__single __sized_by(function-parameter-0-2)':'const void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *' +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long' +// CHECK: | |-OpaqueValueExpr [[ove_5]] {{.*}} 'void *' +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} 'void *' +// CHECK: | |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long' +// CHECK: | `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: |-FunctionDecl [[func_static_2:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_dst_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_src_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_tmp:0x[^ ]+]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: | | | | | `-AssumptionExpr +// CHECK: | | | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_dst_2]] +// CHECK: | | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_src_2]] +// CHECK: | | | |-OpaqueValueExpr [[ove_11]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_2]] +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | `-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(function-parameter-0-2)(*)(void *__single __sized_by(function-parameter-0-2), const void *__single __sized_by(function-parameter-0-2), unsigned long)' +// CHECK: | | | | `-DeclRefExpr {{.+}} +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'void *' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const void *__single __sized_by(function-parameter-0-2)':'const void *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'void *' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long' +// CHECK: | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'void *' +// CHECK: | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'void *' +// CHECK: | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long' +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | `-DeclRefExpr {{.+}} [[var_tmp]] +// CHECK: |-FunctionDecl [[func_static_3:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_3:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *(*)(unsigned long)' +// CHECK: | | `-DeclRefExpr {{.+}} +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: |-FunctionDecl [[func_static_4:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_4:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_tmp_1:0x[^ ]+]] +// CHECK: | | `-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *(*)(unsigned long)' +// CHECK: | | | `-DeclRefExpr {{.+}} +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size_4]] +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | `-DeclRefExpr {{.+}} [[var_tmp_1]] +// CHECK: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// CHECK: |-ParmVarDecl [[var_src_str:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_len2:0x[^ ]+]] +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-DependerDeclsAttr +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_dst_str:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} '((char *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((malloc(len)), (len))) <= __builtin_get_pointer_upper_bound(((char *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((malloc(len)), (len)))) && __builtin_get_pointer_lower_bound(((char *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((malloc(len)), (len)))) <= ((char *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((malloc(len)), (len))) && len <= __builtin_get_pointer_upper_bound(((char *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((malloc(len)), (len)))) - ((char *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((malloc(len)), (len))) && 0 <= len' +// CHECK: | | |-BinaryOperator {{.+}} 'char *__single __counted_by(len2)':'char *__single' '=' +// CHECK: | | | |-DeclRefExpr {{.+}} [[var_dst_str]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | `-ParenExpr +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-ForgePtrExpr +// CHECK: | | |-ParenExpr +// CHECK: | | | `-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *(*__single)(int)' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_static_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | `-OpaqueValueExpr [[ove_13]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_len2]] +// CHECK: | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_12]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: |-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(void *restrict, void *restrict, int)' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_static]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_14]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_dst_str]] +// CHECK: | | | `-OpaqueValueExpr [[ove_15]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len2]] +// CHECK: | | |-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_16]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_src_str]] +// CHECK: | | | `-OpaqueValueExpr [[ove_17]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | |-OpaqueValueExpr [[ove_16]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_18]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_dst_str]] +// CHECK: | `-OpaqueValueExpr [[ove_19]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len2]] +// CHECK: |-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__single __counted_by(len2)':'char *__single' +// CHECK: `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/array-param-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/array-param-sys.h new file mode 100644 index 0000000000000..f4e3bc90f788b --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/array-param-sys.h @@ -0,0 +1,244 @@ +#include + +void extFunc(int size, int arr[size]); +void extFunc3(int size, int * __null_terminated arr); // strict-note{{passing argument to parameter 'arr' here}} + +#pragma clang system_header + +extern void extFunc2(int size, int *sizeLessArr); + +// CHECK: |-FunctionDecl [[func_extFunc:0x[^ ]+]] {{.+}} extFunc +// CHECK: | |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_arr:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_extFunc3:0x[^ ]+]] {{.+}} extFunc3 +// CHECK: | |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_extFunc2:0x[^ ]+]] {{.+}} 'void (int, int *)' +// CHECK: | |-ParmVarDecl [[var_size_2:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_sizeLessArr:0x[^ ]+]] + +static inline void funcInSDK(int size, int arr[size]) { + extFunc(size, arr); + extFunc2(size, arr); +} + +// CHECK: |-FunctionDecl [[func_static:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_3:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_arr_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && size <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= size' +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __counted_by(size))' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_extFunc]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *)' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_extFunc2]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + +static inline void funcInSDK2(int size, int *sizeLessArr) { + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single __counted_by(size)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcInSDK(size, sizeLessArr); +} + +// CHECK: |-FunctionDecl [[func_static_1:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_4:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_sizeLessArr_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __counted_by(size))' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_static]] +// CHECK: | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_sizeLessArr_1]] +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *' + +static inline void funcInSDK3(int size, int arr[size]) { + funcInSDK(size+1, arr); + // strict-error@+2{{passing 'int *__single __counted_by(size)' (aka 'int *__single') to parameter of incompatible type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // strict-note@21{{passing argument to parameter 'arr' here}} + extFunc3(size+1, arr); + // strict-error@+1{{assignment to 'size' requires corresponding assignment to 'int *__single __counted_by(size)' (aka 'int *__single') 'arr'; add self assignment 'arr = arr' if the value has not changed}} + size++; +} + +// CHECK: |-FunctionDecl [[func_static_2:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_5:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_arr_3:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && size + 1 <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= size + 1' +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __counted_by(size))' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_static]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_11]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __terminated_by(0))' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_extFunc3]] +// CHECK: | | |-BinaryOperator {{.+}} 'int' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_13]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | `-UnaryOperator {{.+}} postfix '++' +// CHECK: | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} lvalue + +static void tmp() { +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/builtin-function-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/builtin-function-sys.h new file mode 100644 index 0000000000000..c4e61a3fa57a6 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/builtin-function-sys.h @@ -0,0 +1,24 @@ +#pragma clang system_header + +static inline void memcpy(void *__restrict__ dst, void *__restrict__ src, int size) { + __builtin_memcpy(dst, src, size); +} + +static inline void* memcpy2(void *__restrict__ dst, void *__restrict__ src, int size) { + return __builtin_memcpy(dst, src, size); +} + +static inline void* memcpy3(void *__restrict__ dst, void *__restrict__ src, int size) { + void * tmp = __builtin_memcpy(dst, src, size); + return tmp; +} + +static inline void* malloc(int size) { + return __builtin_malloc(size); +} + +static inline void* malloc2(int size) { + void *tmp = __builtin_malloc(size); + return tmp; +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/default-attributes-in-preprocessed.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/default-attributes-in-preprocessed.h new file mode 100644 index 0000000000000..797da3ba99c7a --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/default-attributes-in-preprocessed.h @@ -0,0 +1,5 @@ +#pragma once + +inline void increment_unsafe_p(int *p) { + p++; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/int-to-ptr-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/int-to-ptr-sys.h new file mode 100644 index 0000000000000..0219965228e71 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/int-to-ptr-sys.h @@ -0,0 +1,19 @@ +#include +#include + +// both-note@+2{{passing argument to parameter 'p' here}} +// strict-note@+1{{passing argument to parameter 'p' here}} +static inline int * __single funcAdopted(int * __single p) { + return p; +} + +#pragma clang system_header + +static inline int* funcSDK(intptr_t x) { + if (x % 128) + // both-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + return funcAdopted(x); + else + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return funcAdopted((int*)x); +} diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/struct-fields-middle-man.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/struct-fields-middle-man.h new file mode 100644 index 0000000000000..12dcd459e6239 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/struct-fields-middle-man.h @@ -0,0 +1,6 @@ +#ifdef EXTINCLUDE2 +#include +#else +#include "struct-fields-ext.h" +#endif + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/struct-fields-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/struct-fields-sys.h new file mode 100644 index 0000000000000..18fdd09dcdeca --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/struct-fields-sys.h @@ -0,0 +1,101 @@ +#pragma clang system_header + +#include + +struct foo { + int *__counted_by(count) p; + int count; +}; + +struct bar { + int *__ended_by(end) p; + int *end; +}; + +static inline struct foo funcInSDK1(int *p, int count) { + //strict-error@+1{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct foo f = { p, count }; + return f; +} + +static inline struct foo funcInSDK2(int *p, int count) { + //strict-error@+1{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return (struct foo){ p, count }; +} + +static inline struct foo funcInSDK3(int *p, int count) { + struct foo f; + //strict-error@+1{{assigning to 'int *__single __counted_by(count)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + f.p = p; // This results in a RecoveryExpr, so later analysis cannot see the assignment. + //strict-error@+1{{assignment to 'f.count' requires corresponding assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p'; add self assignment 'f.p = f.p' if the value has not changed}} + f.count = count; + return f; +} + + +static inline struct foo funcInSDK4(int *p, int count) { + struct foo f; + //strict-error@+1{{assigning to 'int *__single __counted_by(count)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + f.p = p; + return f; +} + +static inline struct foo funcInSDK5(int *p, int count) { + struct foo f; + //strict-error@+1{{assignment to 'f.count' requires corresponding assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p'; add self assignment 'f.p = f.p' if the value has not changed}} + f.count = count; + return f; +} + +static inline struct bar funcInSDK6(int *p, int *end) { + //strict-error@+2{{initializing 'int *__single __ended_by(end)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + //strict-error@+1{{initializing 'int *__single /* __started_by(p) */ ' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct bar b = { p, end }; + return b; +} + +static inline struct bar funcInSDK7(int *p, int *end) { + //strict-error@+2{{initializing 'int *__single __ended_by(end)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + //strict-error@+1{{initializing 'int *__single /* __started_by(p) */ ' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return (struct bar){ p, end }; +} + +static inline struct bar funcInSDK8(int *p, int *end) { + struct bar b; + //strict-error@+1{{assigning to 'int *__single __ended_by(end)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + b.p = p; + //strict-error@+1{{assigning to 'int *__single /* __started_by(p) */ ' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + b.end = end; + return b; +} + +static inline struct bar funcInSDK9(struct bar in) { + struct bar b; + //strict-error@+1{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'b.p' requires corresponding assignment to 'b.end'; add self assignment 'b.end = b.end' if the value has not changed}} + b.p = in.p; + return b; +} + +static inline struct bar funcInSDK10(struct bar in) { + struct bar b; + //strict-error@+1{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'b.end' requires corresponding assignment to 'b.p'; add self assignment 'b.p = b.p' if the value has not changed}} + b.end = in.end; + return b; +} + +static inline struct foo funcInSDK11(struct foo in) { + struct foo f; + //strict-error@+1{{assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p' requires corresponding assignment to 'f.count'; add self assignment 'f.count = f.count' if the value has not changed}} + f.p = in.p; + return f; +} + +static inline struct foo funcInSDK12(struct foo in) { + struct foo f; + //strict-error@+1{{assignment to 'f.count' requires corresponding assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p'; add self assignment 'f.p = f.p' if the value has not changed}} + f.count = in.count; + return f; +} + +static tmp() { +} diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/system-header-func-decl.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/system-header-func-decl.h new file mode 100644 index 0000000000000..0dd43c66b1c47 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/system-header-func-decl.h @@ -0,0 +1,3 @@ +#pragma clang system_header + +int* foo(int ** fp); diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/system-header-unsafe-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/system-header-unsafe-sys.h new file mode 100644 index 0000000000000..070f52e923a24 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/system-header-unsafe-sys.h @@ -0,0 +1,13 @@ +#include + +//strict-note@+1{{passing argument to parameter 'foo' here}} +void funcWithAnnotation(char *__sized_by(4) foo, char *__sized_by(5) bar); + + +#pragma clang system_header + +void funcInSDK(char *ptr, char * __bidi_indexable bidi) { + //strict-error@+1{{passing 'char *' to parameter of incompatible type 'char *__single __sized_by(4)' (aka 'char *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcWithAnnotation(ptr, bidi); +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/typedefs-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/typedefs-sys.h new file mode 100644 index 0000000000000..31bf896e49502 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/typedefs-sys.h @@ -0,0 +1,44 @@ +#include + +typedef const char * __null_terminated my_str_explicit_t; +typedef const char * my_str_implicit_t; +typedef int * __null_terminated my_nt_int_ptr_t; +typedef int * my_int_ptr_t; + +// both-error@+1{{'__counted_by' inside typedef is only allowed for function type}} +typedef int * __counted_by(4) ivec4_t; // If this is ever allowed we need to handle it for system headers. + +#pragma clang system_header + +static inline my_str_implicit_t funcInSDK1(const char *p) { + my_str_implicit_t str = p; + return str; + return p; +} + +static inline my_str_explicit_t funcInSDK2(const char *p) { + //strict-error@+1{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + my_str_explicit_t str = p; + return str; + //strict-error@+1{{returning 'const char *' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; +} + +static inline my_nt_int_ptr_t funcInSDK3(int *p) { + //strict-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + my_nt_int_ptr_t p2 = p; + return p2; + //strict-error@+1{{returning 'int *' from a function with incompatible result type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; +} + +typedef my_int_ptr_t __null_terminated nt_local_t; + +static inline nt_local_t funcInSDK4(int *p) { + //strict-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + nt_local_t p2 = p; + return p2; + //strict-error@+1{{returning 'int *' from a function with incompatible result type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-global-ext.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-global-ext.h new file mode 100644 index 0000000000000..561dc1051d12d --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-global-ext.h @@ -0,0 +1,5 @@ +#include + +extern int *__sized_by(2) sizedGlobal; +extern int *__terminated_by(2) valueTerminatedGlobal; +extern int *__bidi_indexable bidiGlobal; diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-global-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-global-sys.h new file mode 100644 index 0000000000000..3d44db2f51a52 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-global-sys.h @@ -0,0 +1,216 @@ +#include + +extern int *__sized_by(2) sizedGlobal; +extern int *__terminated_by(2) valueTerminatedGlobal; +extern int *__bidi_indexable bidiGlobal; + +// RELAXED: |-VarDecl [[var_sizedGlobal:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_bidiGlobal:0x[^ ]+]] + +// STRICT: VarDecl [[var_sizedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_bidiGlobal:0x[^ ]+]] + +#pragma clang system_header + +void funcInSDK(int * unsafePointer) { + sizedGlobal = unsafePointer; //strict-error{{assigning to 'int *__single __sized_by(2)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + valueTerminatedGlobal = unsafePointer; //strict-error{{assigning to 'int *__single __terminated_by(2)' (aka 'int *__single') from incompatible type 'int *' is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + // This should result in an unsafe BoundsSafetyPointerCast rdar://99202425 +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single __sized_by(2)':'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *' +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] + +void funcInSDK2(int * __single __terminated_by(2) safePointer) { + sizedGlobal = safePointer; //strict-error{{assigning to 'int *__single __sized_by(2)' (aka 'int *__single') from incompatible type 'int *__single __terminated_by(2)' (aka 'int *__single') requires a linear search for the terminator; use '__terminated_by_to_indexable()' to perform this conversion explicitly}} + valueTerminatedGlobal = safePointer; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// RELAXED: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single __sized_by(2)':'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +// STRICT: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// STRICT: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +void funcInSDK3(int * unsafePointer) { + unsafePointer = sizedGlobal; + unsafePointer = valueTerminatedGlobal; + unsafePointer = bidiGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// RELAXED: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// RELAXED: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// RELAXED: | | | |-OpaqueValueExpr [[ove_2]] +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | | `-OpaqueValueExpr [[ove_3]] +// RELAXED: | | | `-IntegerLiteral {{.+}} 2 +// RELAXED: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// RELAXED: | `-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-MaterializeSequenceExpr {{.+}} +// STRICT: | | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// STRICT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// STRICT: | | | |-OpaqueValueExpr [[ove]] +// STRICT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | | `-OpaqueValueExpr [[ove_1]] +// STRICT: | | | `-IntegerLiteral {{.+}} 2 +// STRICT: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// STRICT: | `-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +void funcInSDK4(int * __single __terminated_by(2) safePointer) { + safePointer = sizedGlobal; //strict-error{{assigning to 'int *__single __terminated_by(2)' (aka 'int *__single') from incompatible type 'int *__single __sized_by(2)' (aka 'int *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + safePointer = valueTerminatedGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// RELAXED: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// RELAXED: | | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// RELAXED: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// RELAXED: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// RELAXED: | | | |-OpaqueValueExpr [[ove_4]] +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | | `-OpaqueValueExpr [[ove_5]] +// RELAXED: | | | `-IntegerLiteral {{.+}} 2 +// RELAXED: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// STRICT: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +// MAINCHECK: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// MAINCHECK: |-ParmVarDecl [[var_unsafe:0x[^ ]+]] +// MAINCHECK: |-ParmVarDecl [[var_term:0x[^ ]+]] +// MAINCHECK: `-CompoundStmt +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __terminated_by(2))' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK2]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_term]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: `-CallExpr +// MAINCHECK: |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __terminated_by(2))' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[func_funcInSDK4]] +// MAINCHECK: `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// MAINCHECK: `-DeclRefExpr {{.+}} [[var_term]] + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-inter-sysheader-other-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-inter-sysheader-other-sys.h new file mode 100644 index 0000000000000..e0e0269f51bde --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-inter-sysheader-other-sys.h @@ -0,0 +1,9 @@ +#include + +#pragma clang system_header + +// strict-note@+1{{passing argument to parameter 'foo' here}} +void funcWithAnnotation(int *__sized_by(4) foo); +void funcWithoutAnnotation(int * foo); +extern int * __single safeGlobal; +extern int * unsafeGlobal; diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-inter-sysheader-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-inter-sysheader-sys.h new file mode 100644 index 0000000000000..ff58385741e39 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-inter-sysheader-sys.h @@ -0,0 +1,308 @@ +#include + +// RELAXED: |-FunctionDecl [[func_funcWithAnnotation:0x[^ ]+]] {{.+}} funcWithAnnotation +// RELAXED: | `-ParmVarDecl [[var_foo:0x[^ ]+]] +// RELAXED: |-FunctionDecl [[func_funcWithoutAnnotation:0x[^ ]+]] {{.+}} funcWithoutAnnotation +// RELAXED: | `-ParmVarDecl [[var_foo_1:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_safeGlobal:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_unsafeGlobal:0x[^ ]+]] + +// STRICT: |-FunctionDecl [[func_funcWithAnnotation:0x[^ ]+]] {{.+}} funcWithAnnotation +// STRICT: | `-ParmVarDecl [[var_foo:0x[^ ]+]] +// STRICT: |-FunctionDecl [[func_funcWithoutAnnotation:0x[^ ]+]] {{.+}} funcWithoutAnnotation +// STRICT: | `-ParmVarDecl [[var_foo_1:0x[^ ]+]] +// STRICT: VarDecl [[var_safeGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_unsafeGlobal:0x[^ ]+]] + +#pragma clang system_header + +void funcInSDK(int * unsafePointer) { + // strict-error@+1{{assigning to 'int *__single' from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + safeGlobal = unsafePointer; + unsafeGlobal = unsafePointer; + unsafePointer = safeGlobal; + unsafePointer = unsafeGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | `-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | `-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +void funcInSDK2(int * unsafePointer) { + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single __sized_by(4)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcWithAnnotation(unsafePointer); + funcWithoutAnnotation(unsafePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-CallExpr +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(4))' +// RELAXED: | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(4)':'int *__single' +// RELAXED: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *' +// RELAXED: | | | `-OpaqueValueExpr [[ove]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *' +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +// STRICT: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | `-CallExpr +// STRICT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +// strict-note@+1{{passing argument to parameter 'safePointer' here}} +void funcInSDK3(int * __single safePointer) { + safeGlobal = safePointer; + unsafeGlobal = safePointer; + safePointer = safeGlobal; + // strict-error@+1{{assigning to 'int *__single' from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + safePointer = unsafeGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// RELAXED: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// STRICT: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-BinaryOperator {{.+}} 'int *__single' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *__single' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +void funcInSDK4(int * __single safePointer) { + funcWithAnnotation(safePointer); + funcWithoutAnnotation(safePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// RELAXED: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-CallExpr +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(4))' +// RELAXED: | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__single' +// RELAXED: | | | `-OpaqueValueExpr [[ove_1]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_safePointer_1]] +// RELAXED: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single' +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer_1]] + +// STRICT: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// STRICT: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | | |-BoundsCheckExpr {{.+}} 'safePointer <= __builtin_get_pointer_upper_bound(safePointer) && __builtin_get_pointer_lower_bound(safePointer) <= safePointer && 4 <= (char *)__builtin_get_pointer_upper_bound(safePointer) - (char *)safePointer && 0 <= 4' +// STRICT: | | | | |-CallExpr +// STRICT: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(4))' +// STRICT: | | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// STRICT: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single' +// STRICT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// STRICT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// STRICT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | | `-GetBoundExpr {{.+}} upper +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | | `-GetBoundExpr {{.+}} lower +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// STRICT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'long' +// STRICT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// STRICT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// STRICT: | | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// STRICT: | | | | | | `-GetBoundExpr {{.+}} upper +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | | | | `-DeclRefExpr {{.+}} 'int *__single' lvalue ParmVar {{.+}} 'safePointer' 'int *__single' +// STRICT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// STRICT: | | | | | `-IntegerLiteral {{.+}} 0 +// STRICT: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// STRICT: | | | |-OpaqueValueExpr [[ove]] +// STRICT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | | | `-DeclRefExpr {{.+}} [[var_safePointer_1]] +// STRICT: | | | `-OpaqueValueExpr [[ove_1]] +// STRICT: | | | `-ImplicitCastExpr {{.+}} 'long' +// STRICT: | | | `-IntegerLiteral {{.+}} 4 +// STRICT: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// STRICT: | `-CallExpr +// STRICT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer_1]] + +void funcInSDK5(int * unsafePointer) { + funcInSDK(unsafePointer); + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcInSDK3(unsafePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_2:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-CallExpr +// RELAXED: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] + +// STRICT: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_2:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-CallExpr +// STRICT: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] + +void funcInSDK6(int * __single safePointer) { + funcInSDK(safePointer); + funcInSDK3(safePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// RELAXED: | |-ParmVarDecl [[var_safePointer_2:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-CallExpr +// RELAXED: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safePointer_2]] +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer_2]] + +// STRICT: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// STRICT: | |-ParmVarDecl [[var_safePointer_2:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-CallExpr +// STRICT: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer_2]] +// STRICT: | `-CallExpr +// STRICT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single)' +// STRICT: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer_2]] + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-return-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-return-sys.h new file mode 100644 index 0000000000000..484efba4f7fc9 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/unsafe-return-sys.h @@ -0,0 +1,177 @@ +#include + +// RELAXED: VarDecl [[var_sizedGlobal:0x[^ ]+]] +// RELAXED: VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// RELAXED: VarDecl [[var_bidiGlobal:0x[^ ]+]] + +// STRICT: VarDecl [[var_sizedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_bidiGlobal:0x[^ ]+]] + +#pragma clang system_header + +int * __unsafe_indexable funcInSDK(int * __unsafe_indexable unsafePointer) { + return unsafePointer; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] + +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] + +int * __unsafe_indexable funcInSDK2(int * __single __terminated_by(2) safePointer) { + return safePointer; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// RELAXED: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +// STRICT: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// STRICT: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +int * __single funcInSDK3(int * __unsafe_indexable unsafePointer) { + return unsafePointer; // strict-error{{returning 'int *__unsafe_indexable' from a function with incompatible result type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +// STRICT: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-RecoveryExpr +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +int * __unsafe_indexable funcInSDK4(void) { + return sizedGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | |-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// RELAXED: | | | | |-CStyleCastExpr {{.+}} 'char *' +// RELAXED: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// RELAXED: | | |-OpaqueValueExpr [[ove]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | `-OpaqueValueExpr [[ove_1]] +// RELAXED: | | `-IntegerLiteral {{.+}} 2 +// RELAXED: | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' + +// STRICT: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// STRICT: | `-MaterializeSequenceExpr {{.+}} +// STRICT: | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | |-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// STRICT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// STRICT: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// STRICT: | | |-OpaqueValueExpr [[ove]] +// STRICT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | `-OpaqueValueExpr [[ove_1]] +// STRICT: | | `-IntegerLiteral {{.+}} 2 +// STRICT: | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' + +int * __unsafe_indexable funcInSDK5(void) { + return valueTerminatedGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +int * __unsafe_indexable funcInSDK6(void) { + return bidiGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +// MAINCHECK: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// MAINCHECK: |-ParmVarDecl [[var_unsafe:0x[^ ]+]] +// MAINCHECK: |-ParmVarDecl [[var_term:0x[^ ]+]] +// MAINCHECK: `-CompoundStmt +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(int *__unsafe_indexable)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(int *__single __terminated_by(2))' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK2]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_term]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'int *__single(*__single)(int *__unsafe_indexable)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(void)' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[func_funcInSDK4]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(void)' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[func_funcInSDK5]] +// MAINCHECK: `-CallExpr +// MAINCHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(void)' +// MAINCHECK: `-DeclRefExpr {{.+}} [[func_funcInSDK6]] diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/include/va-list-sys.h b/clang/test/BoundsSafety/AST/SystemHeaders/include/va-list-sys.h new file mode 100644 index 0000000000000..4e46443003dca --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/include/va-list-sys.h @@ -0,0 +1,17 @@ +#include +#include + +#pragma clang system_header + +typedef void * (*variable_length_function)(va_list args); +static inline void* call_func_internal(variable_length_function f, va_list args) { + return f(args); +} + +static inline void* call_func(variable_length_function f, ...) { + va_list ap; + + va_start(ap, f); + return call_func_internal(f, ap); +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/int-to-ptr-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/int-to-ptr-main.c new file mode 100644 index 0000000000000..8ef141b95701c --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/int-to-ptr-main.c @@ -0,0 +1,92 @@ + +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=both -I %S/include | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix RELAXED +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=both -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix RELAXED +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict,both -fno-bounds-safety-relaxed-system-headers -I %S/include | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix STRICT +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict,both -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix STRICT + +int * func(intptr_t y) { + // both-error@+1{{returning 'int *' from a function with incompatible result type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return funcSDK(y); +} + +// RELAXED: |-FunctionDecl [[func_static:0x[^ ]+]] {{.+}} static +// RELAXED: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_p]] +// +// STRICT: |-FunctionDecl [[func_static:0x[^ ]+]] {{.+}} static +// STRICT: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_p]] + + +// RELAXED: |-FunctionDecl [[func_static_1:0x[^ ]+]] {{.+}} static +// RELAXED: | |-ParmVarDecl [[var_x:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-IfStmt +// RELAXED: | |-BinaryOperator {{.+}} 'intptr_t':'long' '%' +// RELAXED: | | |-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_x]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// RELAXED: | | `-IntegerLiteral {{.+}} 128 +// RELAXED: | |-ReturnStmt +// RELAXED: | | `-RecoveryExpr +// RELAXED: | | |-DeclRefExpr {{.+}} [[func_static]] +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_x]] +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'int *__single(*__single)(int *__single)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_static]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-CStyleCastExpr {{.+}} 'int *' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_x]] +// +// STRICT: |-FunctionDecl [[func_static_1:0x[^ ]+]] {{.+}} static +// STRICT: | |-ParmVarDecl [[var_x:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-IfStmt +// STRICT: | |-BinaryOperator {{.+}} 'intptr_t':'long' '%' +// STRICT: | | |-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// STRICT: | | | `-DeclRefExpr {{.+}} [[var_x]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// STRICT: | | `-IntegerLiteral {{.+}} 128 +// STRICT: | |-ReturnStmt +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[func_static]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_x]] +// STRICT: | `-ReturnStmt +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[func_static]] +// STRICT: | `-CStyleCastExpr {{.+}} 'int *' +// STRICT: | `-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// STRICT: | `-DeclRefExpr {{.+}} [[var_x]] + +// RELAXED: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// RELAXED: |-ParmVarDecl [[var_y:0x[^ ]+]] +// RELAXED: `-CompoundStmt +// RELAXED: `-ReturnStmt +// RELAXED: `-RecoveryExpr +// RELAXED: `-CallExpr +// RELAXED: |-ImplicitCastExpr {{.+}} 'int *(*__single)(intptr_t)' +// RELAXED: | `-DeclRefExpr {{.+}} [[func_static_1]] +// RELAXED: `-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// RELAXED: `-DeclRefExpr {{.+}} [[var_y]] +// +// STRICT: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// STRICT: |-ParmVarDecl [[var_y:0x[^ ]+]] +// STRICT: `-CompoundStmt +// STRICT: `-ReturnStmt +// STRICT: `-RecoveryExpr +// STRICT: `-CallExpr +// STRICT: |-ImplicitCastExpr {{.+}} 'int *(*__single)(intptr_t)' +// STRICT: | `-DeclRefExpr {{.+}} [[func_static_1]] +// STRICT: `-ImplicitCastExpr {{.+}} 'intptr_t':'long' +// STRICT: `-DeclRefExpr {{.+}} [[var_y]] diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/system-header-func-redecl-single.c b/clang/test/BoundsSafety/AST/SystemHeaders/system-header-func-redecl-single.c new file mode 100644 index 0000000000000..dfc4edd7fa342 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/system-header-func-redecl-single.c @@ -0,0 +1,23 @@ + +// RUN: %clang_cc1 -fsyntax-only -ast-dump -fbounds-safety -I %S/include %s | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -ast-dump -fbounds-safety -I %S/include -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include +#include + +int *__single foo(int *__single *__single); + +void bar(void) { + int *__single s; + foo(&s); +} +// CHECK: `-FunctionDecl {{.+}} bar 'void (void)' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used s 'int *__single' +// CHECK: `-CallExpr {{.+}} 'int *__single' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single(*__single)(int *__single*__single)' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__single(int *__single*__single)' Function {{.+}} 'foo' 'int *__single(int *__single*__single)' +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single*__single' +// CHECK: `-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow +// CHECK: `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 's' 'int *__single' diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/system-header-func-redecl-unsafe-indexable.c b/clang/test/BoundsSafety/AST/SystemHeaders/system-header-func-redecl-unsafe-indexable.c new file mode 100644 index 0000000000000..0caa739784824 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/system-header-func-redecl-unsafe-indexable.c @@ -0,0 +1,23 @@ + +// RUN: %clang_cc1 -fsyntax-only -ast-dump -fbounds-safety -I %S/include %s | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -ast-dump -fbounds-safety -I %S/include -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include +#include + +int *__unsafe_indexable foo(int *__unsafe_indexable *__unsafe_indexable); + +void bar(void) { + int *__unsafe_indexable s; + foo(&s); +} +// CHECK: `-FunctionDecl {{.+}} bar 'void (void)' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used s 'int *__unsafe_indexable' +// CHECK: `-CallExpr {{.+}} 'int *__unsafe_indexable' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(int *__unsafe_indexable*__unsafe_indexable)' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable(int *__unsafe_indexable*__unsafe_indexable)' Function {{.+}} 'foo' 'int *__unsafe_indexable(int *__unsafe_indexable*__unsafe_indexable)' +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable*__unsafe_indexable' +// CHECK: `-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK: `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue Var {{.+}} 's' 'int *__unsafe_indexable' diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/system-header-unsafe-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/system-header-unsafe-main.c new file mode 100644 index 0000000000000..c750036016a4a --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/system-header-unsafe-main.c @@ -0,0 +1,116 @@ + +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix RELAXED +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix RELAXED +// expected-no-diagnostics + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix STRICT +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" --check-prefix STRICT + +void func(char * __unsafe_indexable ptr, char * __bidi_indexable bidi) { + funcInSDK(ptr, bidi); +} + +// RELAXED: TranslationUnitDecl +// RELAXED: |-FunctionDecl [[func_funcWithAnnotation:0x[^ ]+]] {{.+}} funcWithAnnotation +// RELAXED: | |-ParmVarDecl [[var_foo:0x[^ ]+]] +// RELAXED: | `-ParmVarDecl [[var_bar:0x[^ ]+]] +// +// STRICT: TranslationUnitDecl +// STRICT: |-FunctionDecl [[func_funcWithAnnotation:0x[^ ]+]] {{.+}} funcWithAnnotation +// STRICT: | |-ParmVarDecl [[var_foo:0x[^ ]+]] +// STRICT: | `-ParmVarDecl [[var_bar:0x[^ ]+]] + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_ptr:0x[^ ]+]] +// RELAXED: | |-ParmVarDecl [[var_bidi:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-BoundsCheckExpr +// RELAXED: | | | |-CallExpr +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(char *__single __sized_by(4), char *__single __sized_by(5))' +// RELAXED: | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(4)':'char *__single' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'char *' +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(5)':'char *__single' +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | `-BinaryOperator {{.+}} 'int' '&&' +// RELAXED: | | | |-BinaryOperator {{.+}} 'int' '&&' +// RELAXED: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// RELAXED: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | | | `-GetBoundExpr {{.+}} upper +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | | | `-GetBoundExpr {{.+}} lower +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | `-BinaryOperator {{.+}} 'int' '&&' +// RELAXED: | | | |-BinaryOperator {{.+}} 'int' '<=' +// RELAXED: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'long' +// RELAXED: | | | | `-BinaryOperator {{.+}} 'long' '-' +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// RELAXED: | | | | | `-GetBoundExpr {{.+}} upper +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | | | `-BinaryOperator {{.+}} 'int' '<=' +// RELAXED: | | | |-ImplicitCastExpr {{.+}} 'long' +// RELAXED: | | | | `-IntegerLiteral {{.+}} 0 +// RELAXED: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +// RELAXED: | | |-OpaqueValueExpr [[ove]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_ptr]] +// RELAXED: | | |-OpaqueValueExpr [[ove_1]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_bidi]] +// RELAXED: | | `-OpaqueValueExpr [[ove_2]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'long' +// RELAXED: | | `-IntegerLiteral {{.+}} 5 +// RELAXED: | |-OpaqueValueExpr [[ove]] {{.*}} 'char *' +// RELAXED: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// RELAXED: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +// +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_ptr:0x[^ ]+]] +// STRICT: | |-ParmVarDecl [[var_bidi:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// STRICT: | |-DeclRefExpr {{.+}} [[var_ptr]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_bidi]] + +// RELAXED: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// RELAXED: |-ParmVarDecl [[var_ptr_1:0x[^ ]+]] +// RELAXED: |-ParmVarDecl [[var_bidi_1:0x[^ ]+]] +// RELAXED: `-CompoundStmt +// RELAXED: `-CallExpr +// RELAXED: |-ImplicitCastExpr {{.+}} 'void (*__single)(char *, char *__bidi_indexable)' +// RELAXED: | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// RELAXED: |-ImplicitCastExpr {{.+}} 'char *' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'char *__unsafe_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_ptr_1]] +// RELAXED: `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// RELAXED: `-DeclRefExpr {{.+}} [[var_bidi_1]] +// +// STRICT: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// STRICT: |-ParmVarDecl [[var_ptr_1:0x[^ ]+]] +// STRICT: |-ParmVarDecl [[var_bidi_1:0x[^ ]+]] +// STRICT: `-CompoundStmt +// STRICT: `-CallExpr +// STRICT: |-ImplicitCastExpr {{.+}} 'void (*__single)(char *, char *__bidi_indexable)' +// STRICT: | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// STRICT: |-ImplicitCastExpr {{.+}} 'char *' +// STRICT: | `-ImplicitCastExpr {{.+}} 'char *__unsafe_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_ptr_1]] +// STRICT: `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// STRICT: `-DeclRefExpr {{.+}} [[var_bidi_1]] + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-global-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-global-main.c new file mode 100644 index 0000000000000..adadcf919cd40 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-global-main.c @@ -0,0 +1,15 @@ + +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include | FileCheck --check-prefixes RELAXED %S/include/unsafe-global-sys.h +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck --check-prefixes RELAXED %S/include/unsafe-global-sys.h +// +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include | FileCheck --check-prefixes STRICT,MAINCHECK %S/include/unsafe-global-sys.h +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck --check-prefixes STRICT,MAINCHECK %S/include/unsafe-global-sys.h + +void func(int * __unsafe_indexable unsafe, int * __terminated_by(2) term) { + funcInSDK(unsafe); + funcInSDK2(term); + funcInSDK3(unsafe); + funcInSDK4(term); +} diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-inter-sysheader-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-inter-sysheader-main.c new file mode 100644 index 0000000000000..cb9194acb0147 --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-inter-sysheader-main.c @@ -0,0 +1,16 @@ + +#include +// +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -DSYSHEADER -I %S/include | FileCheck --check-prefixes RELAXED %S/include/unsafe-inter-sysheader-sys.h +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -DSYSHEADER -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck --check-prefixes RELAXED %S/include/unsafe-inter-sysheader-sys.h +// +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include | FileCheck --check-prefixes STRICT %S/include/unsafe-inter-sysheader-sys.h +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck --check-prefixes STRICT %S/include/unsafe-inter-sysheader-sys.h + +void func(int * __unsafe_indexable unsafe, int * __single safe) { + funcInSDK(unsafe); + funcInSDK2(unsafe); + funcInSDK3(safe); + funcInSDK4(safe); +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-return-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-return-main.c new file mode 100644 index 0000000000000..13517d548933c --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/unsafe-return-main.c @@ -0,0 +1,19 @@ + +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify -I %S/include | FileCheck --check-prefixes RELAXED %S/include/unsafe-return-sys.h +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck --check-prefixes RELAXED %S/include/unsafe-return-sys.h +// expected-no-diagnostics +// +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include | FileCheck --check-prefixes STRICT,MAINCHECK %S/include/unsafe-return-sys.h +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck --check-prefixes STRICT,MAINCHECK %S/include/unsafe-return-sys.h + +void func(int * __unsafe_indexable unsafe, int * __terminated_by(2) term) { + funcInSDK(unsafe); + funcInSDK2(term); + funcInSDK3(unsafe); + funcInSDK4(); + funcInSDK5(); + funcInSDK6(); +} + diff --git a/clang/test/BoundsSafety/AST/SystemHeaders/va-list-main.c b/clang/test/BoundsSafety/AST/SystemHeaders/va-list-main.c new file mode 100644 index 0000000000000..de6304d9efffd --- /dev/null +++ b/clang/test/BoundsSafety/AST/SystemHeaders/va-list-main.c @@ -0,0 +1,89 @@ + +#include + +// RUN: %clang_cc1 -triple arm64-apple-macosx -ast-dump -fbounds-safety %s -I %S/include | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" +// RUN: %clang_cc1 -triple arm64-apple-macosx -ast-dump -fbounds-safety %s -I %S/include -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --implicit-check-not "GetBoundExpr {{.+}} 'char *__single'" --implicit-check-not "GetBoundExpr {{.+}} 'char *'" +extern variable_length_function func_ptr; +typedef void * (*variable_length_function2)(va_list args); +extern variable_length_function2 func_ptr2; + +void func(char *dst_str, char *src_str, int len) { + call_func(func_ptr, dst_str, src_str, len); + call_func(func_ptr2, dst_str, src_str, len); +} + +// CHECK: |-FunctionDecl [[func_static:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_f:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_args:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *(*)(va_list)' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'va_list':'char *' +// CHECK: | `-DeclRefExpr {{.+}} [[var_args]] +// CHECK: |-FunctionDecl [[func_static_1:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_f_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_ap:0x[^ ]+]] +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void (*)(__builtin_va_list &, ...)' +// CHECK: | | | `-DeclRefExpr {{.+}} +// CHECK: | | |-DeclRefExpr {{.+}} [[var_ap]] +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f_1]] +// CHECK: | `-ReturnStmt +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *(*__single)(void *(*)(va_list), va_list)' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_static]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *(*)(va_list)' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'va_list':'char *' +// CHECK: | `-DeclRefExpr {{.+}} [[var_ap]] +// CHECK: |-VarDecl [[var_func_ptr:0x[^ ]+]] +// CHECK: |-TypedefDecl +// CHECK: | `-PointerType +// CHECK: | `-ParenType +// CHECK: | `-FunctionProtoType +// CHECK: | |-PointerType +// CHECK: | | `-BuiltinType +// CHECK: | `-ElaboratedType +// CHECK: | `-TypedefType +// CHECK: | |-Typedef +// CHECK: | `-ElaboratedType +// CHECK: | `-TypedefType +// CHECK: | |-Typedef +// CHECK: | `-PointerType +// CHECK: | `-BuiltinType +// CHECK: |-VarDecl [[var_func_ptr2:0x[^ ]+]] +// CHECK: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// CHECK: |-ParmVarDecl [[var_dst_str:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_src_str:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *(*__single)(void *(*)(va_list), ...)' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_static_1]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *(*)(va_list)' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *(*__single)(char *)' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__single(*__single)(va_list)' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_func_ptr]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_dst_str]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_src_str]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: `-CallExpr +// CHECK: |-ImplicitCastExpr {{.+}} 'void *(*__single)(void *(*)(va_list), ...)' +// CHECK: | `-DeclRefExpr {{.+}} [[func_static_1]] +// CHECK: |-ImplicitCastExpr {{.+}} 'void *(*)(va_list)' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *(*__single)(char *)' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__single(*__single)(va_list)' +// CHECK: | `-DeclRefExpr {{.+}} [[var_func_ptr2]] +// CHECK: |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_dst_str]] +// CHECK: |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_src_str]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_len]] diff --git a/clang/test/BoundsSafety/AST/__bidi_indexable.c b/clang/test/BoundsSafety/AST/__bidi_indexable.c new file mode 100644 index 0000000000000..dc19fb10b2192 --- /dev/null +++ b/clang/test/BoundsSafety/AST/__bidi_indexable.c @@ -0,0 +1,12 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fbounds-safety -fbounds-attributes-cxx-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -fbounds-safety -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct Foo { + int *__bidi_indexable foo; + // CHECK: FieldDecl {{.+}} foo 'int *__bidi_indexable' +}; diff --git a/clang/test/BoundsSafety/AST/__indexable.c b/clang/test/BoundsSafety/AST/__indexable.c new file mode 100644 index 0000000000000..8f61579a0d0af --- /dev/null +++ b/clang/test/BoundsSafety/AST/__indexable.c @@ -0,0 +1,12 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fbounds-safety -fbounds-attributes-cxx-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -fbounds-safety -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct Foo { + int *__indexable foo; + // CHECK: FieldDecl {{.+}} foo 'int *__indexable' +}; diff --git a/clang/test/BoundsSafety/AST/__single.c b/clang/test/BoundsSafety/AST/__single.c new file mode 100644 index 0000000000000..7c06b6e595f5a --- /dev/null +++ b/clang/test/BoundsSafety/AST/__single.c @@ -0,0 +1,16 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fbounds-safety -fbounds-attributes-cxx-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -fbounds-safety -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c++ -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s + +#include + +struct Foo { + int *__single foo; + // CHECK: FieldDecl {{.+}} foo 'int *__single' +}; diff --git a/clang/test/BoundsSafety/AST/__unsafe_forge_bidi_indexable.c b/clang/test/BoundsSafety/AST/__unsafe_forge_bidi_indexable.c new file mode 100644 index 0000000000000..caf0bcaf7a09e --- /dev/null +++ b/clang/test/BoundsSafety/AST/__unsafe_forge_bidi_indexable.c @@ -0,0 +1,15 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +void Test() { + int *__bidi_indexable ptr = __unsafe_forge_bidi_indexable(int *, 0, sizeof(int)); + // CHECK: VarDecl {{.+}} ptr 'int *__bidi_indexable' cinit + // CHECK: CStyleCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: ForgePtrExpr {{.+}} 'void *__bidi_indexable' + // CHECK-NEXT: ParenExpr + // CHECK-NEXT: IntegerLiteral {{.+}} 'int' 0 +} diff --git a/clang/test/BoundsSafety/AST/__unsafe_forge_single.c b/clang/test/BoundsSafety/AST/__unsafe_forge_single.c new file mode 100644 index 0000000000000..2036dc4f2c3bb --- /dev/null +++ b/clang/test/BoundsSafety/AST/__unsafe_forge_single.c @@ -0,0 +1,25 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: FunctionDecl {{.+}} Test +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.+}} ptr 'int *__single' +// CHECK: `-ParenExpr {{.+}} 'int *__single' +// CHECK: `-CStyleCastExpr {{.+}} 'int *__single'{{.*}} +// CHECK: `-ForgePtrExpr {{.+}} 'void *__single' +// CHECK: |-ParenExpr {{.+}} 'int' +// CHECK: | `-IntegerLiteral {{.+}} 'int' 17 +// CHECK: |-<<>> +// CHECK: `-<<>> +void Test(void) { + int *__single ptr = __unsafe_forge_single(int *, 17); +} diff --git a/clang/test/BoundsSafety/AST/__unsafe_forge_terminated_by.c b/clang/test/BoundsSafety/AST/__unsafe_forge_terminated_by.c new file mode 100644 index 0000000000000..86f05920a3efb --- /dev/null +++ b/clang/test/BoundsSafety/AST/__unsafe_forge_terminated_by.c @@ -0,0 +1,42 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + +void Test() { + int *__terminated_by(42) ptr = __unsafe_forge_terminated_by(int *, 17, 42); +} + +// CHECK: FunctionDecl [[func_Test:0x[^ ]+]] {{.+}} Test +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_ptr:0x[^ ]+]] +// CHECK: `-ParenExpr +// CHECK: `-CStyleCastExpr {{.+}} 'int *{{(__single)?}} __terminated_by(42)':'int *{{(__single)?}}' +// CHECK: `-ForgePtrExpr {{.+}} 'void *{{(__single)?}} __terminated_by((42))':'void *{{(__single)?}}' +// CHECK: |-ParenExpr +// CHECK: | `-IntegerLiteral {{.+}} 17 +// CHECK: `-ParenExpr +// CHECK: `-IntegerLiteral {{.+}} 42 + +void Test2() { + int **__terminated_by(0) ptr = __unsafe_forge_terminated_by(int **, 17, 0); +} + +// CHECK: FunctionDecl [[func_Test2:0x[^ ]+]] {{.+}} Test2 +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_ptr_1:0x[^ ]+]] +// CHECK: `-ParenExpr +// CHECK: `-CStyleCastExpr {{.+}} 'int *{{(__single)?}}*{{(__single)?}} __terminated_by(0)':'int *{{(__single)?}}*{{(__single)?}}' +// CHECK: `-ForgePtrExpr {{.+}} 'void *{{(__single)?}} __terminated_by((0))':'void *{{(__single)?}}' +// CHECK: |-ParenExpr +// CHECK: | `-IntegerLiteral {{.+}} 17 +// CHECK: `-ParenExpr +// CHECK: `-IntegerLiteral {{.+}} 0 diff --git a/clang/test/BoundsSafety/AST/__unsafe_indexable.c b/clang/test/BoundsSafety/AST/__unsafe_indexable.c new file mode 100644 index 0000000000000..ae6f49861e246 --- /dev/null +++ b/clang/test/BoundsSafety/AST/__unsafe_indexable.c @@ -0,0 +1,16 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fbounds-safety -fbounds-attributes-cxx-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -fbounds-safety -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c++ -ast-dump -fexperimental-bounds-safety-attributes %s 2>&1 | FileCheck %s + +#include + +struct Foo { + int *__unsafe_indexable foo; + // CHECK: FieldDecl {{.+}} foo 'int *__unsafe_indexable' +}; diff --git a/clang/test/BoundsSafety/AST/abi-ptr-attr-unsafe.c b/clang/test/BoundsSafety/AST/abi-ptr-attr-unsafe.c new file mode 100644 index 0000000000000..ebcad63adf848 --- /dev/null +++ b/clang/test/BoundsSafety/AST/abi-ptr-attr-unsafe.c @@ -0,0 +1,127 @@ + + +// RUN: %clang_cc1 -verify -include %S/Inputs/abi-ptr-attr-unsafe/mock-system-header.h -fbounds-safety %s +// RUN: %clang_cc1 -ast-dump -include %S/Inputs/abi-ptr-attr-unsafe/mock-system-header.h -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -verify -include %S/Inputs/abi-ptr-attr-unsafe/mock-system-header.h -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s +// RUN: %clang_cc1 -ast-dump -include %S/Inputs/abi-ptr-attr-unsafe/mock-system-header.h -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +// expected-no-diagnostics + +#include + + +// CHECK-LABEL: FUnspecified 'void (int *)' inline +// CHECK: |-ParmVarDecl {{.*}} used x 'int *' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used y 'int *' cinit +// CHECK: | `-ImplicitCastExpr {{.*}} 'int *' +// CHECK: | `-DeclRefExpr {{.*}} 'int *' lvalue ParmVar {{.*}} 'x' 'int *' +// CHECK: |-BinaryOperator {{.*}} 'int *' '=' +// CHECK: | |-DeclRefExpr {{.*}} 'int *' lvalue Var {{.*}} 'y' 'int *' +// CHECK: | `-BinaryOperator {{.*}} 'int *' '+' +// CHECK: | |-ImplicitCastExpr {{.*}} 'int *' +// CHECK: | | `-DeclRefExpr {{.*}} 'int *' lvalue Var {{.*}} 'y' 'int *' +// CHECK: | `-IntegerLiteral {{.*}} 'int' 1 +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} z 'int **' cinit +// CHECK: `-ImplicitCastExpr {{.*}} 'int **' +// CHECK: `-UnaryOperator {{.*}} 'int **__bidi_indexable' prefix '&' cannot overflow +// CHECK: `-DeclRefExpr {{.*}} 'int *' lvalue Var {{.*}} 'y' 'int *' + +// CHECK-LABEL: FUnspecifiedInline 'void (int *__single)' +// CHECK: |-ParmVarDecl {{.*}} x 'int *__single' +// CHECK: `-CompoundStmt +// CHECK: `-CallExpr +// CHECK: |-ImplicitCastExpr {{.*}} 'void (*__single)(int *)' +// CHECK: | `-DeclRefExpr {{.*}} 'void (int *)' Function {{.*}} 'FUnspecified' 'void (int *)' +// CHECK: `-ImplicitCastExpr {{.*}} 'int *' +// CHECK: `-ImplicitCastExpr {{.*}} 'int *__single' +// CHECK: `-DeclRefExpr {{.*}} 'int *__single' lvalue ParmVar {{.*}} 'x' 'int *__single' +void FUnspecifiedInline(int *x) { + FUnspecified(x); +} + +__ptrcheck_abi_assume_unsafe_indexable() + +// CHECK-LABEL: FUnsafeIndexable 'void (int *__unsafe_indexable)' +// CHECK: |-ParmVarDecl {{.*}} x 'int *__unsafe_indexable' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used y 'int *__unsafe_indexable' cinit +// CHECK: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// CHECK: | `-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue ParmVar {{.*}} 'x' 'int *__unsafe_indexable' +// CHECK: |-CompoundAssignOperator {{.*}} 'int *__unsafe_indexable' '-=' ComputeLHSTy='int *__unsafe_indexable' ComputeResultTy='int *__unsafe_indexable' +// CHECK: | |-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'y' 'int *__unsafe_indexable' +// CHECK: | `-ImplicitCastExpr {{.*}} 'int' +// CHECK: | `-UnaryOperator {{.*}} 'int' lvalue prefix '*' cannot overflow +// CHECK: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// CHECK: | `-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue ParmVar {{.*}} 'x' 'int *__unsafe_indexable' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} z 'int *__unsafe_indexable*__unsafe_indexable' cinit +// CHECK: `-UnaryOperator {{.*}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK: `-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'y' 'int *__unsafe_indexable' +void FUnsafeIndexable(int *x) { + int *y = x; + y -= *x; + int **z = &y; +} + +// CHECK-LABEL: FUnsafeIndexableAddrOf 'void (void)' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used x 'int' cinit +// CHECK: | `-IntegerLiteral {{.*}} 'int' 0 +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} y 'int *__bidi_indexable' cinit +// CHECK: `-UnaryOperator {{.*}} 'int *__bidi_indexable' prefix '&' cannot overflow +// CHECK: `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'x' 'int' +void FUnsafeIndexableAddrOf(void) { + int x = 0; + int *__bidi_indexable y = &x; +} + +// CHECK-LABEL: FUnsafeIndexableArrayDecay 'void (int *__unsafe_indexable)' +// CHECK: |-ParmVarDecl {{.*}} x 'int *__unsafe_indexable' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used arr 'int[2]' cinit +// CHECK: | `-InitListExpr {{.*}} 'int[2]' +// CHECK: | |-array_filler: ImplicitValueInitExpr {{.*}} 'int' +// CHECK: | `-IntegerLiteral {{.*}} 'int' 0 +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} y 'int *__bidi_indexable' cinit +// CHECK: `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.*}} 'int[2]' lvalue Var {{.*}} 'arr' 'int[2]' + +void FUnsafeIndexableArrayDecay(int *x) { + int arr[2] = { 0 }; + int *__bidi_indexable y = arr; +} + +// CHECK-LABEL: FUnsafeIndexableCountedBy 'void (int *__single __counted_by(len), unsigned int)' +// CHECK: |-ParmVarDecl [[var_ptr:0x[^ ]+]] {{.+}} ptr +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] {{.+}} len +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_y_4:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_ptr]] +// CHECK: | `-OpaqueValueExpr [[ove_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: `-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' +void FUnsafeIndexableCountedBy(int *__counted_by(len) ptr, unsigned len) { + int *y = ptr; +} diff --git a/clang/test/BoundsSafety/AST/addr-of-ptr.c b/clang/test/BoundsSafety/AST/addr-of-ptr.c new file mode 100644 index 0000000000000..7e919f78b1fa7 --- /dev/null +++ b/clang/test/BoundsSafety/AST/addr-of-ptr.c @@ -0,0 +1,24 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +void foo(int *a) { +// CHECK: -VarDecl {{.+}} b 'int *__single*__bidi_indexable' cinit +// CHECK: -VarDecl {{.+}} c 'int *__single*__bidi_indexable' cinit + int *__single *b = &a; + __auto_type c = &a; + + // PointerTypeLoc should have the SourceLocation of __single, __indexable, + // __bidi_indexable, __unsafe_indexable: + // without it, TreeTransform::TransformPointerType doesn't know what + // attributes to use. Keeping this as CHECK-NOT as a reminder to update + // this test when it's fixed. + +// CHECK-NOT: -VarDecl {{.+}} d 'int *__single*__bidi_indexable' cinit +// CHECK-NOT: -VarDecl {{.+}} e 'int *__single*__bidi_indexable' cinit + __auto_type *d = &a; + __auto_type *__single *e = &a; +} diff --git a/clang/test/BoundsSafety/AST/address-of-sizeless.c b/clang/test/BoundsSafety/AST/address-of-sizeless.c new file mode 100644 index 0000000000000..1bd925b084ab0 --- /dev/null +++ b/clang/test/BoundsSafety/AST/address-of-sizeless.c @@ -0,0 +1,67 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +extern struct incomplete incomplete; +// CHECK: {{^}}|-VarDecl [[var_incomplete:0x[^ ]+]] + +extern struct flexible flexible; +// CHECK: {{^}}|-VarDecl [[var_flexible:0x[^ ]+]] + +extern void void_global; +// CHECK: {{^}}|-VarDecl [[var_void_global:0x[^ ]+]] + +extern void function(void); +// CHECK: {{^}}|-FunctionDecl [[func_function:0x[^ ]+]] {{.+}} 'void (void)' + +extern int array[]; +// CHECK: {{^}}|-VarDecl [[var_array:0x[^ ]+]] + +void address_of(void) { +// CHECK: {{^}}`-FunctionDecl [[func_address_of:0x[^ ]+]] {{.+}} address_of + + (void) &incomplete; +// CHECK: {{^}} |-CStyleCastExpr {{.+}} 'void' +// CHECK: {{^}} | `-UnaryOperator {{.+}} 'struct incomplete *__single'{{.*}} prefix '&' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_incomplete]] + + (void) &flexible; +// CHECK: {{^}} |-CStyleCastExpr {{.+}} 'void' +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | | `-MemberExpr {{.+}} ->count +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_flexible]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' + + (void) &void_global; +// CHECK: {{^}} |-CStyleCastExpr {{.+}} 'void' +// CHECK: {{^}} | `-UnaryOperator {{.+}} 'void *__single'{{.*}} prefix '&' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_void_global]] + + (void) &function; +// CHECK: {{^}} |-CStyleCastExpr {{.+}} 'void' +// CHECK: {{^}} | `-UnaryOperator {{.+}} 'void (*__single)(void)'{{.*}} prefix '&' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[func_function]] + + (void) &array; +// CHECK: {{^}} `-CStyleCastExpr {{.+}} 'void' +// CHECK: {{^}} `-UnaryOperator {{.+}} 'int (*__single)[]'{{.*}} prefix '&' +// CHECK: {{^}} `-DeclRefExpr {{.+}} [[var_array]] +} diff --git a/clang/test/BoundsSafety/AST/addrof-deref-attributes.c b/clang/test/BoundsSafety/AST/addrof-deref-attributes.c new file mode 100644 index 0000000000000..20402c0cd62ab --- /dev/null +++ b/clang/test/BoundsSafety/AST/addrof-deref-attributes.c @@ -0,0 +1,70 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +void test(int *__bidi_indexable bidi, int *__indexable unidi, + int *__unsafe_indexable unsafe, int *__null_terminated tb) { + (void)&*bidi; + (void)&bidi[4]; + + (void)&*unidi; + (void)&unidi[4]; + + (void)&*unsafe; + (void)&unsafe[4]; + + (void)&*tb; + (void)&tb[0]; +} + +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'bidi' 'int *__bidi_indexable' +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: `-ArraySubscriptExpr {{.+}} 'int' lvalue +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'bidi' 'int *__bidi_indexable' +// CHECK-NEXT: `-IntegerLiteral + +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__indexable' prefix '&' cannot overflow +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'unidi' 'int *__indexable' +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: `-ArraySubscriptExpr {{.+}} 'int' lvalue +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'unidi' 'int *__indexable' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 4 + +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__unsafe_indexable' prefix '&' cannot overflow +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue ParmVar {{.+}} 'unsafe' 'int *__unsafe_indexable' +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__unsafe_indexable' prefix '&' cannot overflow +// CHECK-NEXT: `-ArraySubscriptExpr {{.+}} 'int' lvalue +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue ParmVar {{.+}} 'unsafe' 'int *__unsafe_indexable' +// CHECK-NEXT: `-IntegerLiteral + +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__single __terminated_by(0)':'int *__single' prefix '&' cannot overflow +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue ParmVar {{.+}} 'tb' 'int *__single __terminated_by(0)':'int *__single' +// CHECK: CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: `-UnaryOperator {{.+}} 'int *__single __terminated_by(0)':'int *__single' prefix '&' cannot overflow +// CHECK-NEXT: `-ArraySubscriptExpr {{.+}} 'int' lvalue +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue ParmVar {{.+}} 'tb' 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-IntegerLiteral diff --git a/clang/test/BoundsSafety/AST/alloc-sized-calloc/alloc-sized-calloc.c b/clang/test/BoundsSafety/AST/alloc-sized-calloc/alloc-sized-calloc.c new file mode 100644 index 0000000000000..86e4880d03b94 --- /dev/null +++ b/clang/test/BoundsSafety/AST/alloc-sized-calloc/alloc-sized-calloc.c @@ -0,0 +1,110 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include +#include "mock-header.h" + +int foo() { + int cnt = 10; + int siz = sizeof(int); + int *ptr1 = my_calloc(cnt, siz); + int *__bidi_indexable ptr2; + ptr2 = my_calloc(cnt, siz); + return ptr2[cnt-1]; +} + +// CHECK: {{^}}TranslationUnitDecl +// CHECK: {{^}}|-FunctionDecl [[func_my_calloc:0x[^ ]+]] {{.+}} my_calloc +// CHECK: {{^}}| |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: {{^}}| `-AllocSizeAttr +// CHECK: {{^}}`-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_cnt:0x[^ ]+]] +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_siz:0x[^ ]+]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-UnaryExprOrTypeTraitExpr +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_ptr1:0x[^ ]+]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_cnt]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | | | `-CallExpr +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *(*__single)(int, int)' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_my_calloc]] +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '*' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_ptr2:0x[^ ]+]] +// CHECK: {{^}} |-BinaryOperator {{.+}} 'int *__bidi_indexable' '=' +// CHECK: {{^}} | |-DeclRefExpr {{.+}} [[var_ptr2]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_cnt]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}} | | | `-CallExpr +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *(*__single)(int, int)' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_my_calloc]] +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '*' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_4]] {{.*}} 'void *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}} `-ReturnStmt +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} `-ArraySubscriptExpr +// CHECK: {{^}} |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_ptr2]] +// CHECK: {{^}} `-BinaryOperator {{.+}} 'int' '-' +// CHECK: {{^}} |-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_cnt]] +// CHECK: {{^}} `-IntegerLiteral {{.+}} 1 diff --git a/clang/test/BoundsSafety/AST/alloc-sized-calloc/mock-header.h b/clang/test/BoundsSafety/AST/alloc-sized-calloc/mock-header.h new file mode 100644 index 0000000000000..a4e57d475c428 --- /dev/null +++ b/clang/test/BoundsSafety/AST/alloc-sized-calloc/mock-header.h @@ -0,0 +1,7 @@ +#ifndef MOCK_HEADER_H +#define MOCK_HEADER_H + +#pragma clang system_header +void *my_calloc(int count, int size) __attribute__((alloc_size(1,2))); + +#endif /* MOCK_HEADER_H */ diff --git a/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-assign.c b/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-assign.c new file mode 100644 index 0000000000000..9aa14c735a29c --- /dev/null +++ b/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-assign.c @@ -0,0 +1,189 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include +#include + +void param_with_count(int *__counted_by(len - 2) buf, int len) { + int arr[10]; + len = 12; + buf = arr; +} +// CHECK: -FunctionDecl [[func_param_with_count:0x[^ ]+]] {{.+}} param_with_count +// CHECK: |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} 'int' 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && 12 - 2 <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= 12 - 2' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | |-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | `-IntegerLiteral {{.+}} 2 +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-IntegerLiteral {{.+}} 12 +// CHECK: | `-OpaqueValueExpr [[ove_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single __counted_by(len - 2)':'int *__single' '=' +// CHECK: | |-DeclRefExpr {{.+}} [[var_buf]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len - 2)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' + +void local_count(void) { + int arr[10]; + int len = 8; + int *__counted_by(len + 2) buf = arr; +} +// CHECK: -FunctionDecl [[func_local_count:0x[^ ]+]] {{.+}} local_count +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | |-IntegerLiteral {{.+}} 8 +// CHECK: | `-DependerDeclsAttr +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr {{.+}} 'int *__single __counted_by(len + 2)':'int *__single' 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && len + 2 <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= len + 2' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len + 2)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_4]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: `-OpaqueValueExpr [[ove_4]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-BinaryOperator {{.+}} 'int' '+' +// CHECK: |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: `-IntegerLiteral {{.+}} 2 + +void local_count_size(void) { + size_t nelems; + size_t size; + void *__sized_by(nelems * size) buf; + int arr[10]; + nelems = 10; + size = 4; + buf = arr; +} +// CHECK: -FunctionDecl [[func_local_count_size:0x[^ ]+]] {{.+}} local_count_size +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_nelems:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_size:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_buf_2:0x[^ ]+]] +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_2:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} 'size_t':'unsigned long' 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && 10 * 4 <= (char *)__builtin_get_pointer_upper_bound(arr) - (char *__bidi_indexable)arr' +// CHECK: | | |-BinaryOperator {{.+}} 'size_t':'unsigned long' '=' +// CHECK: | | | |-DeclRefExpr {{.+}} [[var_nelems]] +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-BinaryOperator {{.+}} 'size_t':'unsigned long' '*' +// CHECK: | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'size_t':'unsigned long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'size_t':'unsigned long' +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'size_t':'unsigned long' +// CHECK: | | `-IntegerLiteral {{.+}} 4 +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: |-BinaryOperator {{.+}} 'size_t':'unsigned long' '=' +// CHECK: | |-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'size_t':'unsigned long' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'void *__single __sized_by(nelems * size)':'void *__single' '=' +// CHECK: | |-DeclRefExpr {{.+}} [[var_buf_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(nelems * size)':'void *__single' +// CHECK: | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_5]] {{.*}} 'size_t':'unsigned long' +// CHECK: |-OpaqueValueExpr [[ove_7]] {{.*}} 'size_t':'unsigned long' +// CHECK: `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-calls.c b/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-calls.c new file mode 100644 index 0000000000000..e0e8f0dc92947 --- /dev/null +++ b/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-calls.c @@ -0,0 +1,120 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include +#include + +void param_with_count(int *__counted_by(len - 2) buf, int len); +// CHECK: |-FunctionDecl [[func_param_with_count:0x[^ ]+]] {{.+}} param_with_count +// CHECK: | |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr + + +void *__sized_by(count * size) return_count_size(size_t count, size_t size); +// CHECK: |-FunctionDecl [[func_return_count_size:0x[^ ]+]] {{.+}} return_count_size +// CHECK: | |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_size:0x[^ ]+]] + +void calls(void) { + int arr[10]; + param_with_count(arr, 12); + + int *buf = return_count_size(10, 13); +} +// CHECK: `-FunctionDecl [[func_calls:0x[^ ]+]] {{.+}} calls +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'void' 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && 12 - 2 <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= 12 - 2' +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len - 2), int)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_param_with_count]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len - 2)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-IntegerLiteral {{.+}} 12 +// CHECK: | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | | `-IntegerLiteral {{.+}} 2 +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'void *__single __sized_by(count * size)':'void *__single' +// CHECK: | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__single __sized_by(count * size)':'void *__single' +// CHECK: | | | `-AssumptionExpr +// CHECK: | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'size_t':'unsigned long' +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'size_t':'unsigned long' +// CHECK: | | `-IntegerLiteral {{.+}} 13 +// CHECK: | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(count * size)(*__single)(size_t, size_t)' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_return_count_size]] +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'size_t':'unsigned long' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'size_t':'unsigned long' +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-BinaryOperator {{.+}} 'size_t':'unsigned long' '*' +// CHECK: | |-OpaqueValueExpr [[ove_4]] {{.*}} 'size_t':'unsigned long' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'size_t':'unsigned long' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'size_t':'unsigned long' +// CHECK: |-OpaqueValueExpr [[ove_5]] {{.*}} 'size_t':'unsigned long' +// CHECK: |-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__single __sized_by(count * size)':'void *__single' +// CHECK: `-OpaqueValueExpr [[ove_6]] {{.*}} 'size_t':'unsigned long' + diff --git a/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-vars.c b/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-vars.c new file mode 100644 index 0000000000000..0045e4cd6840c --- /dev/null +++ b/clang/test/BoundsSafety/AST/arithmetic-ops-in-counted-by-vars.c @@ -0,0 +1,49 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include +#include + +int len = -1; +int *__counted_by(len + 1) buf; +// CHECK: |-VarDecl {{.*}} used len 'int' cinit +// CHECK-NEXT:| |-UnaryOperator {{.*}} 'int' prefix '-' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 1 +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} <> Implicit {{.*}} 0 +// CHECK-NEXT:|-VarDecl {{.*}} buf 'int *__single __counted_by(len + 1)':'int *__single' + +unsigned size; +unsigned count; +void *__sized_by(size * count) buf2; +// CHECK: |-VarDecl {{.*}} used size 'unsigned int' +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} <> Implicit {{.*}} 0 +// CHECK-NEXT:|-VarDecl {{.*}} used count 'unsigned int' +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} <> Implicit {{.*}} 0 +// CHECK-NEXT:|-VarDecl {{.*}} buf2 'void *__single __sized_by(size * count)':'void *__single' + +void f(void) { + int len3 = 10; + int *__counted_by(len3 - 10) buf3; +} +// CHECK-LABEL: f 'void (void)' +// CHECK-NEXT:| `-CompoundStmt {{.*}} +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} used len3 'int' cinit +// CHECK-NEXT:| | |-IntegerLiteral {{.*}} 'int' 10 +// CHECK-NEXT:| | `-DependerDeclsAttr {{.*}} <> Implicit {{.*}} 0 +// CHECK-NEXT:| `-DeclStmt {{.*}} +// CHECK-NEXT:| `-VarDecl {{.*}} buf3 'int *__single __counted_by(len3 - 10)':'int *__single' + +void f2(int *__counted_by(10 * order1 + order0) buf, int order1, unsigned order0); +// CHECK-LABEL: f2 'void (int *__single __counted_by(10 * order1 + order0), int, unsigned int)' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} buf 'int *__single __counted_by(10 * order1 + order0)':'int *__single' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} used order1 'int' +// CHECK-NEXT:| | `-DependerDeclsAttr {{.*}} <> Implicit {{.*}} 0 +// CHECK-NEXT:| `-ParmVarDecl {{.*}} used order0 'unsigned int' +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} <> Implicit {{.*}} 0 + +void *__sized_by(nitems * size) mycalloc(size_t nitems, size_t size); +// CHECK-LABEL: mycalloc 'void *__single __sized_by(nitems * size)(size_t, size_t)' +// CHECK-NEXT: |-ParmVarDecl {{.*}} used nitems 'size_t':'unsigned long' +// CHECK-NEXT: `-ParmVarDecl {{.*}} used size 'size_t':'unsigned long' diff --git a/clang/test/BoundsSafety/AST/array-to-pointer-decay.c b/clang/test/BoundsSafety/AST/array-to-pointer-decay.c new file mode 100644 index 0000000000000..6b3f164f919b9 --- /dev/null +++ b/clang/test/BoundsSafety/AST/array-to-pointer-decay.c @@ -0,0 +1,58 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +#include + +int glen; +int gArr[5]; + +void Test() { + int *__bidi_indexable ptrGArr = gArr; + // CHECK: VarDecl {{.+}} ptrGArr 'int *__bidi_indexable' cinit + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[5]' lvalue Var {{.+}} 'gArr' 'int[5]' + + int arrVLA[glen]; + int *__bidi_indexable ptrVLA = arrVLA; + ptrVLA = arrVLA; + + int arrLocal[7]; + + int *__indexable ptrArrayLocal = arrLocal; + // CHECK: VarDecl {{.+}} ptrArrayLocal 'int *__indexable' cinit + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[7]' lvalue Var {{.+}} 'arrLocal' 'int[7]' + ptrArrayLocal = arrLocal; + // CHECK-NEXT: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'ptrArrayLocal' 'int *__indexable' + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[7]' lvalue Var {{.+}} 'arrLocal' 'int[7]' + + int *__single ptrThinLocal = arrLocal; + // CHECK: VarDecl {{.+}} ptrThinLocal 'int *__single' cinit + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__single' + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[7]' lvalue Var {{.+}} 'arrLocal' 'int[7]' + ptrThinLocal = arrLocal; + // CHECK-NEXT: BinaryOperator {{.+}} 'int *__single' '=' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'ptrThinLocal' 'int *__single' + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__single' + // CHECK-NEXT: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[7]' lvalue Var {{.+}} 'arrLocal' 'int[7]' + + int *ptrUnspecifiedLocal = arrLocal; + // CHECK: VarDecl {{.+}} ptrUnspecifiedLocal 'int *__bidi_indexable' + // CHECK: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[7]' lvalue Var {{.+}} 'arrLocal' 'int[7]' + ptrUnspecifiedLocal = arrLocal; + // CHECK-NEXT: BinaryOperator {{.+}} 'int *__bidi_indexable'{{.*}} '=' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int *__bidi_indexable'{{.*}} 'ptrUnspecifiedLocal' 'int *__bidi_indexable' + // CHECK: ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: DeclRefExpr {{.+}} 'int[7]' lvalue Var {{.+}} 'arrLocal' 'int[7]' + + int *__bidi_indexable ptrFromArraySub = &arrLocal[0]; +} + diff --git a/clang/test/BoundsSafety/AST/atomic-ops-c11-casts.c b/clang/test/BoundsSafety/AST/atomic-ops-c11-casts.c new file mode 100644 index 0000000000000..f0f7ca3439901 --- /dev/null +++ b/clang/test/BoundsSafety/AST/atomic-ops-c11-casts.c @@ -0,0 +1,125 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// Make sure that BoundsSafetyPointerCast is emitted when an atomic op is used. + +void unsafe_indexable(void) { + // CHECK: DeclStmt {{.+}} + // CHECK: `-VarDecl {{.+}} p1 '_Atomic(int *__unsafe_indexable)' cinit + // CHECK: `-ImplicitCastExpr {{.+}} '_Atomic(int *__unsafe_indexable)' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q1' 'int *__bidi_indexable' + int *__bidi_indexable q1; + int *_Atomic __unsafe_indexable p1 = q1; + + // CHECK: AtomicExpr {{.+}} 'void' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__unsafe_indexable) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__unsafe_indexable)' lvalue Var {{.+}} 'p2' '_Atomic(int *__unsafe_indexable)' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q2' 'int *__bidi_indexable' + int *_Atomic __unsafe_indexable p2; + int *__bidi_indexable q2; + __c11_atomic_init(&p2, q2); + + // CHECK: AtomicExpr {{.+}} 'void' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__unsafe_indexable) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__unsafe_indexable)' lvalue Var {{.+}} 'p3' '_Atomic(int *__unsafe_indexable)' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q3' 'int *__bidi_indexable' + int *_Atomic __unsafe_indexable p3; + int *__bidi_indexable q3; + __c11_atomic_store(&p3, q3, __ATOMIC_SEQ_CST); + + // CHECK: AtomicExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__unsafe_indexable) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__unsafe_indexable)' lvalue Var {{.+}} 'p4' '_Atomic(int *__unsafe_indexable)' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q4' 'int *__bidi_indexable' + int *_Atomic __unsafe_indexable p4; + int *__bidi_indexable q4; + __c11_atomic_exchange(&p4, q4, __ATOMIC_SEQ_CST); + + // CHECK: AtomicExpr {{.+}} '_Bool' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__unsafe_indexable) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__unsafe_indexable)' lvalue Var {{.+}} 'p5' '_Atomic(int *__unsafe_indexable)' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable*' + // CHECK: | `-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue Var {{.+}} 'q5' 'int *__unsafe_indexable' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'r5' 'int *__bidi_indexable' + int *_Atomic __unsafe_indexable p5; + int *__unsafe_indexable q5; + int *__bidi_indexable r5; + __c11_atomic_compare_exchange_strong(&p5, &q5, r5, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} + +void single(void) { + // CHECK: DeclStmt {{.+}} + // CHECK: `-VarDecl {{.+}} p1 '_Atomic(int *__single)' cinit + // CHECK: `-ImplicitCastExpr {{.+}} '_Atomic(int *__single)' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q1' 'int *__bidi_indexable' + int *__bidi_indexable q1; + int *_Atomic __single p1 = q1; + + // CHECK: AtomicExpr {{.+}} 'void' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__single) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__single)' lvalue Var {{.+}} 'p2' '_Atomic(int *__single)' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q2' 'int *__bidi_indexable' + int *_Atomic __single p2; + int *__bidi_indexable q2; + __c11_atomic_init(&p2, q2); + + // CHECK: AtomicExpr {{.+}} 'void' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__single) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__single)' lvalue Var {{.+}} 'p3' '_Atomic(int *__single)' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q3' 'int *__bidi_indexable' + int *_Atomic __single p3; + int *__bidi_indexable q3; + __c11_atomic_store(&p3, q3, __ATOMIC_SEQ_CST); + + // CHECK: AtomicExpr {{.+}} 'int *__single' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__single) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__single)' lvalue Var {{.+}} 'p4' '_Atomic(int *__single)' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q4' 'int *__bidi_indexable' + int *_Atomic __single p4; + int *__bidi_indexable q4; + __c11_atomic_exchange(&p4, q4, __ATOMIC_SEQ_CST); + + // CHECK: AtomicExpr {{.+}} '_Bool' + // CHECK: |-UnaryOperator {{.+}} '_Atomic(int *__single) *__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} '_Atomic(int *__single)' lvalue Var {{.+}} 'p5' '_Atomic(int *__single)' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: |-ImplicitCastExpr {{.+}} 'int *__single*' + // CHECK: | `-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'q5' 'int *__single' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'r5' 'int *__bidi_indexable' + int *_Atomic __single p5; + int *__single q5; + int *__bidi_indexable r5; + __c11_atomic_compare_exchange_strong(&p5, &q5, r5, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/AST/atomic-ops-gnu-casts.c b/clang/test/BoundsSafety/AST/atomic-ops-gnu-casts.c new file mode 100644 index 0000000000000..c7a8709e3cead --- /dev/null +++ b/clang/test/BoundsSafety/AST/atomic-ops-gnu-casts.c @@ -0,0 +1,91 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// Make sure that BoundsSafetyPointerCast is emitted when an atomic op is used. + +void unsafe_indexable(void) { + // CHECK: AtomicExpr {{.+}} 'void' + // CHECK: |-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue Var {{.+}} 'p1' 'int *__unsafe_indexable' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q1' 'int *__bidi_indexable' + int *__unsafe_indexable p1; + int *__bidi_indexable q1; + __atomic_store_n(&p1, q1, __ATOMIC_SEQ_CST); + + // CHECK: AtomicExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: |-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue Var {{.+}} 'p2' 'int *__unsafe_indexable' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q2' 'int *__bidi_indexable' + int *__unsafe_indexable p2; + int *__bidi_indexable q2; + __atomic_exchange_n(&p2, q2, __ATOMIC_SEQ_CST); + + // CHECK: `-AtomicExpr {{.+}} '_Bool' + // CHECK: |-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue Var {{.+}} 'p3' 'int *__unsafe_indexable' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable*' + // CHECK: | `-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue Var {{.+}} 'q3' 'int *__unsafe_indexable' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'r3' 'int *__bidi_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} '_Bool' + // CHECK: `-IntegerLiteral {{.+}} 'int' 0 + int *__unsafe_indexable p3; + int *__unsafe_indexable q3; + int *__bidi_indexable r3; + __atomic_compare_exchange_n(&p3, &q3, r3, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} + +void single(void) { + // CHECK: AtomicExpr {{.+}} 'void' + // CHECK: |-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'p1' 'int *__single' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q1' 'int *__bidi_indexable' + int *__single p1; + int *__bidi_indexable q1; + __atomic_store_n(&p1, q1, __ATOMIC_SEQ_CST); + + // CHECK: AtomicExpr {{.+}} 'int *__single' + // CHECK: |-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'p2' 'int *__single' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'q2' 'int *__bidi_indexable' + int *__single p2; + int *__bidi_indexable q2; + __atomic_exchange_n(&p2, q2, __ATOMIC_SEQ_CST); + + // CHECK: `-AtomicExpr {{.+}} '_Bool' + // CHECK: |-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'p3' 'int *__single' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: |-ImplicitCastExpr {{.+}} 'int *__single*' + // CHECK: | `-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow + // CHECK: | `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'q3' 'int *__single' + // CHECK: |-IntegerLiteral {{.+}} 'int' 5 + // CHECK: |-ImplicitCastExpr {{.+}} 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'r3' 'int *__bidi_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} '_Bool' + // CHECK: `-IntegerLiteral {{.+}} 'int' 0 + int *__single p3; + int *__single q3; + int *__bidi_indexable r3; + __atomic_compare_exchange_n(&p3, &q3, r3, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/AST/atomic-types.c b/clang/test/BoundsSafety/AST/atomic-types.c new file mode 100644 index 0000000000000..eb74e14a01d1f --- /dev/null +++ b/clang/test/BoundsSafety/AST/atomic-types.c @@ -0,0 +1,61 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +void test(void) { + // CHECK: VarDecl {{.+}} p1 '_Atomic(int *__unsafe_indexable)' + int *_Atomic __unsafe_indexable p1; + // CHECK: VarDecl {{.+}} p2 '_Atomic(int *__unsafe_indexable)' + int *__unsafe_indexable _Atomic p2; + // CHECK: VarDecl {{.+}} p3 '_Atomic(int *__unsafe_indexable)' + _Atomic(int *__unsafe_indexable) p3; + // CHECK: VarDecl {{.+}} p4 '_Atomic(int *__unsafe_indexable)' + _Atomic(int *) __unsafe_indexable p4; + + // CHECK: VarDecl {{.+}} p5 '_Atomic(int *__single)' + int *_Atomic __single p5; + // CHECK: VarDecl {{.+}} p6 '_Atomic(int *__single)' + int *__single _Atomic p6; + // CHECK: VarDecl {{.+}} p7 '_Atomic(int *__single)' + _Atomic(int *__single) p7; + // CHECK: VarDecl {{.+}} p8 '_Atomic(int *__single)' + _Atomic(int *) __single p8; + + // CHECK: VarDecl {{.+}} p9 '_Atomic(_Atomic(int *__unsafe_indexable) *__unsafe_indexable)' + int *_Atomic __unsafe_indexable *_Atomic __unsafe_indexable p9; + // CHECK: VarDecl {{.+}} p10 '_Atomic(_Atomic(int *__unsafe_indexable) *__unsafe_indexable)' + int *__unsafe_indexable _Atomic *_Atomic __unsafe_indexable p10; + // CHECK: VarDecl {{.+}} p11 '_Atomic(_Atomic(int *__unsafe_indexable) *__unsafe_indexable)' + _Atomic(int *__unsafe_indexable) *_Atomic __unsafe_indexable p11; + // CHECK: VarDecl {{.+}} p12 '_Atomic(_Atomic(int *__unsafe_indexable) *__unsafe_indexable)' + _Atomic(int *) __unsafe_indexable *_Atomic __unsafe_indexable p12; + + // CHECK: VarDecl {{.+}} p13 '_Atomic(_Atomic(int *__single) *__unsafe_indexable)' + int *_Atomic __single *_Atomic __unsafe_indexable p13; + // CHECK: VarDecl {{.+}} p14 '_Atomic(_Atomic(int *__single) *__unsafe_indexable)' + int *__single _Atomic *_Atomic __unsafe_indexable p14; + // CHECK: VarDecl {{.+}} p15 '_Atomic(_Atomic(int *__single) *__unsafe_indexable)' + _Atomic(int *__single) *_Atomic __unsafe_indexable p15; + // CHECK: VarDecl {{.+}} p16 '_Atomic(_Atomic(int *__single) *__unsafe_indexable)' + _Atomic(int *) __single *_Atomic __unsafe_indexable p16; + + // CHECK: VarDecl {{.+}} p17 '_Atomic(_Atomic(int *__unsafe_indexable) *__single)' + int *_Atomic __unsafe_indexable *_Atomic __single p17; + // CHECK: VarDecl {{.+}} p18 '_Atomic(_Atomic(int *__unsafe_indexable) *__single)' + int *__unsafe_indexable _Atomic *_Atomic __single p18; + // CHECK: VarDecl {{.+}} p19 '_Atomic(_Atomic(int *__unsafe_indexable) *__single)' + _Atomic(int *__unsafe_indexable) *_Atomic __single p19; + // CHECK: VarDecl {{.+}} p20 '_Atomic(_Atomic(int *__unsafe_indexable) *__single)' + _Atomic(int *) __unsafe_indexable *_Atomic __single p20; + + // CHECK: VarDecl {{.+}} p21 '_Atomic(_Atomic(int *__single) *__single)' + int *_Atomic __single *_Atomic __single p21; + // CHECK: VarDecl {{.+}} p22 '_Atomic(_Atomic(int *__single) *__single)' + int *__single _Atomic *_Atomic __single p22; + // CHECK: VarDecl {{.+}} p23 '_Atomic(_Atomic(int *__single) *__single)' + _Atomic(int *__single) *_Atomic __single p23; + // CHECK: VarDecl {{.+}} p24 '_Atomic(_Atomic(int *__single) *__single)' + _Atomic(int *) __single *_Atomic __single p24; +} diff --git a/clang/test/BoundsSafety/AST/attr-applied-twice.c b/clang/test/BoundsSafety/AST/attr-applied-twice.c new file mode 100644 index 0000000000000..ba440feaf9b8c --- /dev/null +++ b/clang/test/BoundsSafety/AST/attr-applied-twice.c @@ -0,0 +1,32 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s + +// When processing the type for `b`, the types are processed from the beginning of the declaration, +// resulting in the type attribute being applied despite having been applied already + +#include + +typedef int * int_ptr_t; +void foo() { + int_ptr_t __single a, b; + int_ptr_t __bidi_indexable c, d; + int_ptr_t __unsafe_indexable e, f; + int_ptr_t __indexable g, h; +} + +// CHECK: -FunctionDecl {{.*}} foo 'void ()' +// CHECK-NEXT: `-CompoundStmt +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | |-VarDecl {{.*}} a 'int_ptr_t __single':'int *__single' +// CHECK-NEXT: | `-VarDecl {{.*}} b 'int_ptr_t __single':'int *__single' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | |-VarDecl {{.*}} c 'int_ptr_t __bidi_indexable':'int *__bidi_indexable' +// CHECK-NEXT: | `-VarDecl {{.*}} d 'int_ptr_t __bidi_indexable':'int *__bidi_indexable' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | |-VarDecl {{.*}} e 'int_ptr_t __unsafe_indexable':'int *__unsafe_indexable' +// CHECK-NEXT: | `-VarDecl {{.*}} f 'int_ptr_t __unsafe_indexable':'int *__unsafe_indexable' +// CHECK-NEXT: `-DeclStmt +// CHECK-NEXT: |-VarDecl {{.*}} g 'int_ptr_t __indexable':'int *__indexable' +// CHECK-NEXT: `-VarDecl {{.*}} h 'int_ptr_t __indexable':'int *__indexable' + diff --git a/clang/test/BoundsSafety/AST/auto-bound-array.c b/clang/test/BoundsSafety/AST/auto-bound-array.c new file mode 100644 index 0000000000000..30cb4caf2b4d1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-array.c @@ -0,0 +1,23 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +void foo(void) { + // CHECK: VarDecl {{.+}} array_of_ptrs 'int *__single[2]' + int *array_of_ptrs[2]; + + // CHECK: VarDecl {{.+}} ptr_to_array 'int (*__bidi_indexable)[3]' + int(*ptr_to_array)[3]; + + // CHECK: VarDecl {{.+}} array_of_ptrs_to_arrays 'int (*__single[2])[3]' + int(*array_of_ptrs_to_arrays[2])[3]; + + // CHECK: VarDecl {{.+}} ptr_to_ptr_to_array 'int (*__single*__bidi_indexable)[3]' + int(**ptr_to_ptr_to_array)[3]; + + // CHECK: VarDecl {{.+}} ptr_to_array_of_ptrs 'int *__single(*__bidi_indexable)[3]' + int *(*ptr_to_array_of_ptrs)[3]; + + // CHECK: VarDecl {{.+}} ptr_to_array_of_ptrs_to_arrays 'int (*__single(*__bidi_indexable)[4])[3]' + int(*(*ptr_to_array_of_ptrs_to_arrays)[4])[3]; +} diff --git a/clang/test/BoundsSafety/AST/auto-bound-atomic.c b/clang/test/BoundsSafety/AST/auto-bound-atomic.c new file mode 100644 index 0000000000000..79d6728a88a2a --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-atomic.c @@ -0,0 +1,34 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +struct field { + // CHECK: FieldDecl {{.+}} f1 '_Atomic(int *__single)' + int *_Atomic f1; + // CHECK: FieldDecl {{.+}} f2 '_Atomic(int *__single)' + _Atomic(int *) f2; +}; + +// CHECK: FunctionDecl {{.+}} ret1 '_Atomic(int *__single) (void)' +int *_Atomic ret1(void); +// CHECK: FunctionDecl {{.+}} ret2 '_Atomic(int *__single) (void)' +_Atomic(int *) ret2(void); + +// CHECK: ParmVarDecl {{.+}} p1 '_Atomic(int *__single)' +void parm1(int *_Atomic p1); +// CHECK: ParmVarDecl {{.+}} p2 '_Atomic(int *__single)' +void parm2(_Atomic(int *) p2); + +void locals(void) { + // CHECK: VarDecl {{.+}} l1 '_Atomic(_Atomic(int *__single) *__single)' + int *_Atomic *_Atomic __single l1; + // CHECK: VarDecl {{.+}} l2 '_Atomic(_Atomic(int *__single) *__single)' + _Atomic(int *) *_Atomic __single l2; + + // CHECK: VarDecl {{.+}} l3 '_Atomic(_Atomic(int *__single) *__unsafe_indexable)' + int *_Atomic *_Atomic __unsafe_indexable l3; + // CHECK: VarDecl {{.+}} l4 '_Atomic(_Atomic(int *__single) *__unsafe_indexable)' + _Atomic(int *) *_Atomic __unsafe_indexable l4; +} diff --git a/clang/test/BoundsSafety/AST/auto-bound-const-char-pointer-param-system.h b/clang/test/BoundsSafety/AST/auto-bound-const-char-pointer-param-system.h new file mode 100644 index 0000000000000..0866fc9d50f74 --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-const-char-pointer-param-system.h @@ -0,0 +1,3 @@ +#pragma clang system_header + +void system_func(const char *p); diff --git a/clang/test/BoundsSafety/AST/auto-bound-const-char-pointer-param.c b/clang/test/BoundsSafety/AST/auto-bound-const-char-pointer-param.c new file mode 100644 index 0000000000000..942082dab4a14 --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-const-char-pointer-param.c @@ -0,0 +1,142 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -ast-dump -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include +#include +#include + +/* + * The cases where __null_terminated should be added: + */ + +// CHECK: ParmVarDecl {{.+}} const_char_parm 'const char *__single __terminated_by(0)' +void const_char_parm(const char *const_char_parm); + +// CHECK: ParmVarDecl {{.+}} const_wchar_t_param 'const wchar_t *__single __terminated_by(0)' +void const_wchar_t_param(const wchar_t *const_wchar_t_param); + +// CHECK: ParmVarDecl {{.+}} out_const_char_parm 'const char *__single __terminated_by(0)*__single' +void out_const_char_parm(const char **out_const_char_parm); + +// CHECK: ParmVarDecl {{.+}} fptr 'const char *__single __terminated_by(0)(*__single)(const char *__single __terminated_by(0))' +void fptr_param(const char *(*fptr)(const char *p)); + +// CHECK: ParmVarDecl {{.+}} p1 'const char *__single __terminated_by(0)' +// CHECK: ParmVarDecl {{.+}} p2 'const char *__single __terminated_by(0)(*__single)(const char *__single __terminated_by(0), const char *__single __terminated_by(0))' +// CHECK: ParmVarDecl {{.+}} p3 'const char *__single __terminated_by(0)' +void multiple_parms(const char *p1, const char *(*p2)(const char *a, const char *b), const char *p3); + +// CHECK: FunctionDecl {{.+}} const_char_ret 'const char *__single __terminated_by(0)(void)' +const char *const_char_ret(void); + +// CHECK: FunctionDecl {{.+}} fptr_ret_proto 'const char *__single __terminated_by(0)(*__single(void))(const char *__single __terminated_by(0))' +const char *(*fptr_ret_proto(void))(const char *); + +// CHECK: FunctionDecl {{.+}} fptr_ret_no_proto 'const char *__single __terminated_by(0)(*__single())(const char *__single __terminated_by(0))' +const char *(*fptr_ret_no_proto())(const char *); + +void foo(void) { + // CHECK: VarDecl {{.+}} local_fptr 'const char *__single __terminated_by(0)(*__single)(const char *__single __terminated_by(0))' + const char *(*local_fptr)(const char *p); + + // CHECK: VarDecl {{.+}} ptr_const_char_local 'const char *__single __terminated_by(0)*__bidi_indexable' + const char **ptr_const_char_local; + + // CHECK: VarDecl {{.+}} local_fptr_array 'const char *__single __terminated_by(0)(*__single[42])(const char *__single __terminated_by(0))' + const char *(*local_fptr_array[42])(const char *p); + + // CHECK: VarDecl {{.+}} local_fptr_array_ptr 'const char *__single __terminated_by(0)(*__single(*__bidi_indexable)[42])(const char *__single __terminated_by(0))' + const char *(*(*local_fptr_array_ptr)[42])(const char *p); +} + +// CHECK: VarDecl {{.+}} const_char_global 'const char *__single __terminated_by(0)' +const char *const_char_global; + +// CHECK: VarDecl {{.+}} global_fptr 'const char *__single __terminated_by(0)(*__single)(const char *__single __terminated_by(0))' +const char *(*global_fptr)(const char *p); + +struct const_char_struct { + // CHECK: FieldDecl {{.+}} const_char_field 'const char *__single __terminated_by(0)' + const char *const_char_field; +}; + +typedef const char *my_func_t(const char *p); +typedef const char *(*my_func_ptr_t)(const char *p); + +// CHECK: FunctionDecl {{.+}} typedef_func 'const char *__single __terminated_by(0)(const char *__single __terminated_by(0))' +my_func_t typedef_func; + +// CHECK: VarDecl {{.+}} typedef_func_ptr 'const char *__single __terminated_by(0)(*__single)(const char *__single __terminated_by(0))' +my_func_ptr_t typedef_func_ptr; + +// CHECK: ParmVarDecl {{.+}} quals_c 'const char *__single __terminated_by(0)const':'const char *__singleconst' +void quals_c(const char *const quals_c); + +// CHECK: ParmVarDecl {{.+}} quals_cv 'const char *__single __terminated_by(0)const volatile':'const char *__singleconst volatile' +void quals_cv(const char *const volatile quals_cv); + +/* + * The cases where __null_terminated should NOT be added: + */ + +void bar(void) { + // CHECK: VarDecl {{.+}} const_char_local 'const char *__bidi_indexable' + const char *const_char_local; + + // CHECK: VarDecl {{.+}} local_fptr 'const char *__single(*__single)(const char *__single __counted_by(8))' + const char *__single (*local_fptr)(const char *__counted_by(8) p); +} + +// CHECK: ParmVarDecl {{.+}} const_int_parm 'const int *__single' +void const_int_parm(const int *const_int_parm); + +// CHECK: ParmVarDecl {{.+}} const_int8_t_param 'const int8_t *__single' +void const_int8_t_param(const int8_t *const_int8_t_param); + +// CHECK: ParmVarDecl {{.+}} char_parm 'char *__single' +void char_parm(char *char_parm); + +// CHECK: ParmVarDecl {{.+}} unsafe_parm 'const char *__unsafe_indexable' +void unsafe_parm(const char *__unsafe_indexable unsafe_parm); + +// CHECK: ParmVarDecl {{.+}} single_parm 'const char *__single' +void single_parm(const char *__single single_parm); + +// CHECK: ParmVarDecl {{.+}} bidi_parm 'const char *__bidi_indexable' +void bidi_parm(const char *__bidi_indexable bidi_parm); + +// CHECK: ParmVarDecl {{.+}} counted_parm 'const char *__single __counted_by(8)' +void counted_parm(const char *__counted_by(8) counted_parm); + +// CHECK: ParmVarDecl {{.+}} ended_parm 'const char *__single __ended_by(end)' +// CHECK: ParmVarDecl {{.+}} end 'const char *__single /* __started_by(ended_parm) */ ':'const char *__single' +void ended_parm_single(const char *__ended_by(end) ended_parm, const char *__single end); + +// CHECK: ParmVarDecl {{.+}} ended_parm 'const char *__single __ended_by(end)' +// CHECK: ParmVarDecl {{.+}} end 'const char *__single /* __started_by(ended_parm) */ ':'const char *__single' +void ended_parm_unspec(const char *__ended_by(end) ended_parm, const char *end); + +// CHECK: ParmVarDecl {{.+}} nt_parm 'const char *__single __terminated_by(0)' +void nt_parm(const char *__null_terminated nt_parm); + +// CHECK: ParmVarDecl {{.+}} tb_parm 'const char *__single __terminated_by('X')' +void tb_parm(const char *__terminated_by('X') tb_parm); + +// CHECK: ParmVarDecl {{.+}} out_single 'const char *__single*__single' +void out_single(const char *__single *out_single); + +// CHECK: ParmVarDecl {{.+}} out_counted 'const char *__single __counted_by(8)*__single' +void out_counted(const char *__counted_by(8) * out_counted); + +// CHECK: FunctionDecl {{.+}} system_func 'void (const char *)' +#include "auto-bound-const-char-pointer-param-system.h" + +__ptrcheck_abi_assume_unsafe_indexable(); + +// CHECK: FunctionDecl {{.+}} unsafe_abi 'void (const char *__unsafe_indexable)' +void unsafe_abi(const char *p); + +// Make sure we add __single to __terminated_by() even if the ABI is __unsafe_indexable. +// CHECK: FunctionDecl {{.+}} unsafe_abi_explicit 'void (const char *__single __terminated_by(0))' +void unsafe_abi_explicit(const char *__null_terminated p); diff --git a/clang/test/BoundsSafety/AST/auto-bound-function-pointer.c b/clang/test/BoundsSafety/AST/auto-bound-function-pointer.c new file mode 100644 index 0000000000000..5d697add375bf --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-function-pointer.c @@ -0,0 +1,63 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +// CHECK: VarDecl {{.+}} g_var_proto 'int *__single(*__single)(int *__single)' +int *(*g_var_proto)(int *p); + +// CHECK: VarDecl {{.+}} g_var_proto_va 'int *__single(*__single)(int *__single, ...)' +int *(*g_var_proto_va)(int *p, ...); + +// CHECK: VarDecl {{.+}} g_var_no_proto 'int *__single(*__single)()' +int *(*g_var_no_proto)(); + +void foo(void) { + // CHECK: VarDecl {{.+}} l_var_proto 'int *__single(*__single)(int *__single)' + int *(*l_var_proto)(int *p); + + // CHECK: VarDecl {{.+}} l_var_proto_va 'int *__single(*__single)(int *__single, ...)' + int *(*l_var_proto_va)(int *p, ...); + + // CHECK: VarDecl {{.+}} l_var_no_proto 'int *__single(*__single)()' + int *(*l_var_no_proto)(); +} + +// CHECK: ParmVarDecl {{.+}} param_proto 'int *__single(*__single)(int *__single)' +void f1(int *(*param_proto)(int *p)); + +// CHECK: ParmVarDecl {{.+}} param_proto_va 'int *__single(*__single)(int *__single, ...)' +void f2(int *(*param_proto_va)(int *p, ...)); + +// CHECK: ParmVarDecl {{.+}} param_no_proto 'int *__single(*__single)()' +void f3(int *(*param_no_proto)()); + +// CHECK: FunctionDecl {{.+}} ret_proto 'int *__single(*__single(void))(int *__single)' +int *(*ret_proto(void))(int *p); + +// CHECK: FunctionDecl {{.+}} ret_proto_va 'int *__single(*__single(void))(int *__single, ...)' +int *(*ret_proto_va(void))(int *p, ...); + +// CHECK: FunctionDecl {{.+}} ret_no_proto 'int *__single(*__single(void))()' +int *(*ret_no_proto(void))(); + +struct bar { + // CHECK: FieldDecl {{.+}} field_proto 'int *__single(*__single)(int *__single)' + int *(*field_proto)(int *p); + + // CHECK: FieldDecl {{.+}} field_proto_va 'int *__single(*__single)(int *__single, ...)' + int *(*field_proto_va)(int *p, ...); + + // CHECK: FieldDecl {{.+}} field_no_proto 'int *__single(*__single)()' + int *(*field_no_proto)(); +}; + +// CHECK: FunctionDecl {{.+}} nested 'int *__single(*__single(int *__single(*__single)(int *__single)))(int *__single(*__single)(int *__single))' +int *(*nested(int *(*)(int *)))(int *(*)(int *)); + +// CHECK: VarDecl {{.+}} array 'int *__single(*__single[42])(int *__single)' +int *(*array[42])(int *); + +struct digest { + // CHECK: FieldDecl {{.+}} di 'const struct ccdigest_info *__single(*__single)(void)' + const struct ccdigest_info *(*di)(void); +}; diff --git a/clang/test/BoundsSafety/AST/auto-bound-local.c b/clang/test/BoundsSafety/AST/auto-bound-local.c new file mode 100644 index 0000000000000..fcdbbb3113573 --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-local.c @@ -0,0 +1,66 @@ + + +// FIXME: rdar://69452444 +// RUN: not %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s --check-prefix=CHECK-M2 +// RUN: not %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s --check-prefix=CHECK-M2 +#include + + +void Test(void) { + int *localAddrTaken; + int **pp = &localAddrTaken; + int *localNoAddrTaken; + int *__bidi_indexable localBoundAddrTaken; + int *__bidi_indexable *boundPP = &localBoundAddrTaken; + void (*fptr)(void) = &Test; + void (*fptr2)(void) = Test; +} + +// FIXME: rdar://69452444 +// CHECK: `-FunctionDecl {{.+}} Test 'void (void)' +// CHECK-NEXT: `-CompoundStmt +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.+}} used localAddrTaken 'int *__single' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.+}} pp 'int *__single*__bidi_indexable' cinit +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single' lvalue Var {{.+}} 'localAddrTaken' 'int *__single' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.+}} localNoAddrTaken 'int *__bidi_indexable' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.+}} localBoundAddrTaken 'int *__bidi_indexable' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.+}} boundPP 'int *__bidi_indexable*__bidi_indexable' cinit +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'int *__bidi_indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{0x[a-z0-9]*}} 'localBoundAddrTaken' 'int *__bidi_indexable' +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.+}} fptr 'void (*__single)(void)':'void (*__single)(void)' cinit +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'void (*__single)(void)' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (void)' Function {{.+}} 'Test' 'void (void)' +// CHECK-NEXT: `-DeclStmt +// CHECK-NEXT: `-VarDecl {{.+}} fptr2 'void (*__single)(void)':'void (*__single)(void)' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'void (*__single)(void)' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'void (void)' Function {{.+}} 'Test' 'void (void)' + +// CHECK-M2: `-FunctionDecl +// CHECK-M2: `-CompoundStmt +// CHECK-M2: |-DeclStmt +// CHECK-M2: | `-VarDecl {{.+}} used localAddrTaken 'int *__bidi_indexable' +// CHECK-M2: |-DeclStmt +// CHECK-M2: | `-VarDecl {{.+}} pp 'int *__single*__bidi_indexable' +// CHECK-M2: |-DeclStmt +// CHECK-M2: | `-VarDecl {{.+}} localNoAddrTaken 'int *__bidi_indexable' +// CHECK-M2: |-DeclStmt +// CHECK-M2: | `-VarDecl {{.+}} used localBoundAddrTaken 'int *__bidi_indexable' +// CHECK-M2: |-DeclStmt +// CHECK-M2: | `-VarDecl {{.+}} boundPP 'int *__bidi_indexable*__bidi_indexable' +// CHECK-M2: | `-UnaryOperator {{.+}} 'int *__bidi_indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK-M2: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue Var {{.+}} 'localBoundAddrTaken' 'int *__bidi_indexable' +// CHECK-M2: |-DeclStmt +// CHECK-M2: | `-VarDecl {{.+}} fptr 'void (*__single)(void)' cinit +// CHECK-M2: | `-UnaryOperator {{.+}} 'void (*__single)(void)' prefix '&' cannot overflow +// CHECK-M2: | `-DeclRefExpr {{.+}} 'void (void)' Function {{.+}} 'Test' 'void (void)' +// CHECK-M2: `-DeclStmt +// CHECK-M2: `-VarDecl {{.+}} fptr2 'void (*__single)(void)' cinit +// CHECK-M2: `-ImplicitCastExpr {{.+}} 'void (*__single)(void)' +// CHECK-M2: `-DeclRefExpr {{.+}} 'void (void)' Function {{.+}} 'Test' 'void (void)' diff --git a/clang/test/BoundsSafety/AST/auto-bound-var-storage.c b/clang/test/BoundsSafety/AST/auto-bound-var-storage.c new file mode 100644 index 0000000000000..a672624efaa2b --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-bound-var-storage.c @@ -0,0 +1,52 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: VarDecl {{.+}} global 'int *__single' +int *global; + +// CHECK: VarDecl {{.+}} static_global 'int *__single' static +static int *static_global; + +// CHECK: VarDecl {{.+}} extern_global 'int *__single' extern +extern int *extern_global; + +// CHECK: VarDecl {{.+}} private_extern_global 'int *__single' __private_extern__ +__private_extern__ int *private_extern_global; + +// CHECK: VarDecl {{.+}} thread_global 'int *__single' tls +__thread int *thread_global; + +// CHECK: VarDecl {{.+}} static_thread_global 'int *__single' static tls +static __thread int *static_thread_global; + +// CHECK: VarDecl {{.+}} extern_thread_global 'int *__single' extern tls +extern __thread int *extern_thread_global; + +// CHECK: VarDecl {{.+}} private_extern_thread_global 'int *__single' __private_extern__ tls +__private_extern__ __thread int *private_extern_thread_global; + +void foo(void) { + // CHECK: VarDecl {{.+}} local 'int *__bidi_indexable' + int *local; + + // CHECK: VarDecl {{.+}} static_local 'int *__single' static + static int *static_local; + + // CHECK: VarDecl {{.+}} extern_local 'int *__single' extern + extern int *extern_local; + + // CHECK: VarDecl {{.+}} private_extern_local 'int *__single' __private_extern__ + __private_extern__ int *private_extern_local; + + // CHECK: VarDecl {{.+}} static_thread_local 'int *__single' static tls + static __thread int *static_thread_local; + + // CHECK: VarDecl {{.+}} extern_thread_local 'int *__single' extern tls + extern __thread int *extern_thread_local; + + // CHECK: VarDecl {{.+}} private_extern_thread_local 'int *__single' __private_extern__ tls + __private_extern__ __thread int *private_extern_thread_local; +} diff --git a/clang/test/BoundsSafety/AST/auto-type-from-decayed.c b/clang/test/BoundsSafety/AST/auto-type-from-decayed.c new file mode 100644 index 0000000000000..d1311d2e313c1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/auto-type-from-decayed.c @@ -0,0 +1,62 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s + +#include + +int i; +int arr[2]; +void Foo(int *__counted_by(len) ptr, unsigned len); + +__auto_type glob_from_addrof = &i; /* int *__bidi_indexable */ +__auto_type glob_from_arrdecay = arr; /* int *__bidi_indexable */ +__auto_type glob_from_addr_of_arr = &arr[1]; /* int *__bidi_indexable */ +__auto_type glob_from_fundecay = Foo; /* int *__single */ + +void Test() { + int *__single single_ptr; + int *__indexable index_ptr; + int *__unsafe_indexable unsafe_ptr; + + __auto_type local_from_arrdecay = arr; /* int *__bidi_indexable */ + __auto_type local_from_addrof = &i; /* int *__bidi_indexable */ + __auto_type local_from_addr_of_arr = &arr[0]; /* int *__bidi_indexable */ + __auto_type local_from_fundecay = Foo; /* void(*__single)(int *__counted_by(), unsigned) */ + __auto_type local_from_single = single_ptr; /* int *__single */ + __auto_type local_from_index = index_ptr; /* int *__indexable */ + __auto_type local_from_unsafe = unsafe_ptr; /* int *__unsafe_indexable */ +} + +// CHECK: `-FunctionDecl {{.*}} line:17:6 Test 'void ()' +// CHECK: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:15 local_from_arrdecay 'int *__bidi_indexable' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int[2]' lvalue Var {{.*}} 'arr' 'int[2]' +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:15 local_from_addrof 'int *__bidi_indexable' cinit +// CHECK-NEXT: | `-UnaryOperator {{.*}} 'int *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'i' 'int' +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:15 local_from_addr_of_arr 'int *__bidi_indexable' cinit +// CHECK-NEXT: | `-UnaryOperator {{.*}} 'int *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.*}} 'int' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'int[2]' lvalue Var {{.*}} 'arr' 'int[2]' +// CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 0 +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:15 local_from_fundecay 'void (*__single)(int *__single __counted_by(len), unsigned int)' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'void (*__single)(int *__single __counted_by(len), unsigned int)' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'void (int *__single __counted_by(len), unsigned int)' Function {{.*}} 'Foo' 'void (int *__single __counted_by(len), unsigned int)' +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:15 local_from_single 'int *__single' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'int *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'single_ptr' 'int *__single' +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:15 local_from_index 'int *__indexable' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'int *__indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'index_ptr' 'int *__indexable' +// CHECK-NEXT: `-DeclStmt {{.*}} +// CHECK-NEXT: `-VarDecl {{.*}} col:15 local_from_unsafe 'int *__unsafe_indexable' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'unsafe_ptr' 'int *__unsafe_indexable' diff --git a/clang/test/BoundsSafety/AST/bitcast-to-counted_by.c b/clang/test/BoundsSafety/AST/bitcast-to-counted_by.c new file mode 100644 index 0000000000000..f47410d893769 --- /dev/null +++ b/clang/test/BoundsSafety/AST/bitcast-to-counted_by.c @@ -0,0 +1,116 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump -Wno-bounds-safety-init-list %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -Wno-bounds-safety-init-list %s | FileCheck %s +#include + +void Test(void) { + int *__single iptr; + int len; + char *__counted_by(len) cptr = iptr; + int len2; + long *__counted_by(len2) lptr = cptr; + return; +} + +// CHECK-LABEL: Test +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_iptr:0x[^ ]+]] +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_len:0x[^ ]+]] +// CHECK: {{^}} | `-DependerDeclsAttr +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_cptr:0x[^ ]+]] +// CHECK: {{^}} | `-BoundsCheckExpr +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'long' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_iptr]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_len2:0x[^ ]+]] +// CHECK: {{^}} | `-DependerDeclsAttr +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_lptr:0x[^ ]+]] +// CHECK: {{^}} | `-BoundsCheckExpr +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'long *__single __counted_by(len2)':'long *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'long *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'long *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'long' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'long *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'long' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'long *__bidi_indexable' +// CHECK: {{^}} | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: {{^}} | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_cptr]] +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_len2]] diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-assign-null.c b/clang/test/BoundsSafety/AST/bounds-attributed-assign-null.c new file mode 100644 index 0000000000000..ea73a7a716e2f --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-assign-null.c @@ -0,0 +1,752 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump -verify %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -ast-dump -verify %s | FileCheck %s +#include + +// expected-no-diagnostics + +// CHECK: {{^}}|-FunctionDecl [[func_init_local:0x[^ ]+]] {{.+}} init_local +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} 'count == 0' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} 'count1 == 0' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count1)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count1]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} 'count2 == 0' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count2)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count2]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} 'count3 == 0' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count3)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-ParenExpr +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count3]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| `-DeclStmt +// CHECK-NEXT: {{^}}| `-VarDecl [[var_p4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'count4 == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count4)':'int *__single' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_count4]] +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 0 +void init_local(void) { + int count = 0; + int* __counted_by(count) p = (int*) 0; + + int count1 = 0; + int* __counted_by(count1) p1 = (void*) 0; + + int count2 = 0; + int* __counted_by(count2) p2 = (int*)(void*) 0; + + int count3 = 0; + int* __counted_by(count3) p3 = ((int*)(void*)(int*) 0); + + int count4 = 0; + int* __counted_by(count4) p4 = 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_assign_local:0x[^ ]+]] {{.+}} assign_local +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count1_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p1_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count2_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count3_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p3_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_count4_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p4_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-DeclRefExpr {{.+}} [[var_count_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-DeclRefExpr {{.+}} [[var_count1_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count1)':'int *__single' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count1)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-DeclRefExpr {{.+}} [[var_p1_1]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(count1)':'int *__single' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(count1)':'int *__single' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-DeclRefExpr {{.+}} [[var_count2_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count2)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-DeclRefExpr {{.+}} [[var_p2_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count2)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-DeclRefExpr {{.+}} [[var_count3_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count3)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-DeclRefExpr {{.+}} [[var_p3_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count3)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-DeclRefExpr {{.+}} [[var_count4_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count4)':'int *__single' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int *__single __counted_by(count4)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | |-DeclRefExpr {{.+}} [[var_p4_1]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__single __counted_by(count4)':'int *__single' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__single __counted_by(count4)':'int *__single' +// CHECK: {{^}}|-RecordDecl +// CHECK-NEXT: {{^}}| |-FieldDecl +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| `-FieldDecl +void assign_local(void) { + int count = 0; + int* __counted_by(count) p; + int count1 = 0; + int* __counted_by(count1) p1; + int count2 = 0; + int* __counted_by(count2) p2; + int count3 = 0; + int* __counted_by(count3) p3; + int count4 = 0; + int* __counted_by(count4) p4; + + count = 0; + p = (int*)0; + + count1 = 0; + p1 = (void*) 0; + + count2 = 0; + p2 = (int*)(void*) 0; + + count3 = 0; + p3 = ((int*)(void*)(int*) 0); + + count4 = 0; + p4 = 0; +} + +struct Cb { + int count; + int* __counted_by(count) ptr; +}; + +// CHECK: {{^}}|-FunctionDecl [[func_init_struct:0x[^ ]+]] {{.+}} init_struct +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | |-InitListExpr +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}}| | |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_10]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_10]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | |-InitListExpr +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_13]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | |-InitListExpr +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_14:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}}| | |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_14]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_14]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | |-InitListExpr +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}}| | |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_17]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-DeclStmt +// CHECK-NEXT: {{^}}| `-VarDecl [[var_c4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| |-InitListExpr +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_18:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_18]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_18]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_19]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 0 +void init_struct(void) { + struct Cb c = { 0, (int*)0}; + struct Cb c1 = { 0, (void*)0}; + struct Cb c2 = { 0, (int*)(void*)0}; + struct Cb c3 = { 0, ((int*)(void*)(int*)0)}; + struct Cb c4 = { 0, 0}; +} + +// CHECK: {{^}}|-FunctionDecl [[func_assign_struct:0x[^ ]+]] {{.+}} assign_struct +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c1_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c2_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c3_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_c4_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} .count +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_c_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_20]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_20]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_c_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} .count +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_c1_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_22]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_22]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_23:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_c1_1]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_22]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} .count +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_c2_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_24]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_24]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_25:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_c2_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int *' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int *' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} .count +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_c3_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_26]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_26]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_c3_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} .count +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_c4_1]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_28]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_28]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_c4_1]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_29]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_29]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +void assign_struct(void) { + struct Cb c; + struct Cb c1; + struct Cb c2; + struct Cb c3; + struct Cb c4; + + c.count = 0; + c.ptr = (int*)0; + + c1.count = 0; + c1.ptr = (void*)0; + + c2.count = 0; + c2.ptr = (int*)(void*)0; + + c3.count = 0; + c3.ptr = ((int*)(void*)(int*)0); + + c4.count = 0; + c4.ptr = 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_receive_cb:0x[^ ]+]] {{.+}} receive_cb +// CHECK-NEXT: {{^}}| |-ParmVarDecl +// CHECK-NEXT: {{^}}| `-ParmVarDecl [[var_count_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-DependerDeclsAttr +void receive_cb(int* __counted_by(count), int count); + +// CHECK: {{^}}`-FunctionDecl [[func_call_arg:0x[^ ]+]] {{.+}} call_arg +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_count_3:0x[^ ]+]] +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsCheckExpr {{.+}} '(int *)0 <= (int *)0 && (int *)0 <= (int *)0 && count <= (int *)0 - (int *)0 && 0 <= count' +// CHECK-NEXT: {{^}} | | | |-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_receive_cb]] +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_30:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_31:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | | |-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_30]] +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_31]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | |-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsCheckExpr {{.+}} '(void *)0 <= (void *)0 && (void *)0 <= (void *)0 && count <= (void *)0 - (void *)0 && 0 <= count' +// CHECK-NEXT: {{^}} | | | |-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_receive_cb]] +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_32:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}} | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_33:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | | |-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_32]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_33]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | |-OpaqueValueExpr [[ove_32]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int' +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsCheckExpr {{.+}} '(int *)(void *)0 <= (int *)(void *)0 && (int *)(void *)0 <= (int *)(void *)0 && count <= (int *)(void *)0 - (int *)(void *)0 && 0 <= count' +// CHECK-NEXT: {{^}} | | | |-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_receive_cb]] +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_34:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_35:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | | |-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_35]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_35]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_34]] +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_35]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | |-OpaqueValueExpr [[ove_34]] {{.*}} 'int *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_35]] {{.*}} 'int' +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsCheckExpr {{.+}} '((int *)(void *)(int *)0) <= ((int *)(void *)(int *)0) && ((int *)(void *)(int *)0) <= ((int *)(void *)(int *)0) && count <= ((int *)(void *)(int *)0) - ((int *)(void *)(int *)0) && 0 <= count' +// CHECK-NEXT: {{^}} | | | |-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_receive_cb]] +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_36:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_37:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | | |-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_37]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_37]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_36]] +// CHECK-NEXT: {{^}} | | | `-ParenExpr +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}} | | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_37]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | |-OpaqueValueExpr [[ove_36]] {{.*}} 'int *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_37]] {{.*}} 'int' +// CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BoundsCheckExpr {{.+}} '0 <= 0 && 0 <= 0 && count <= 0 - 0 && 0 <= count' +// CHECK-NEXT: {{^}} | | |-CallExpr +// CHECK-NEXT: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_receive_cb]] +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_38:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_39:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_38]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_39]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}} |-OpaqueValueExpr [[ove_38]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_39]] {{.*}} 'int' +void call_arg(int count) { + receive_cb((int*)0, count); + receive_cb((void*)0, count); + receive_cb((int*)(void*)0, count); + receive_cb(((int*)(void*)(int*)0), count); + receive_cb(0, count); +} + +// Assignment on returns and in compound-literals are handled elsewhere diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-in-return-disabled.c b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-disabled.c new file mode 100644 index 0000000000000..78c55972465d3 --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-disabled.c @@ -0,0 +1,125 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// TODO: Remove this test when return-size checks are enabled by default. + +// CHECK: FunctionDecl [[func_cb_in_from_bidi:0x[^ ]+]] {{.+}} cb_in_from_bidi +// CHECK: |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_p]] +int *__counted_by(count) cb_in_from_bidi(int count, int *__bidi_indexable p) { + return p; +} + +// CHECK: FunctionDecl [[func_cb_in_from_indexable:0x[^ ]+]] {{.+}} cb_in_from_indexable +// CHECK: |-ParmVarDecl [[var_count_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_1]] +int *__counted_by(count) cb_in_from_indexable(int count, int *__indexable p) { + return p; +} + +// CHECK: FunctionDecl [[func_cb_in_from_single:0x[^ ]+]] {{.+}} cb_in_from_single +// CHECK: |-ParmVarDecl [[var_count_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_2]] +int *__counted_by(count) cb_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_in_from_int_cast_null:0x[^ ]+]] {{.+}} cb_in_from_int_cast_null +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 0 +int *__counted_by(count) cb_in_from_int_cast_null(int count) { + return (int*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_in_from_void_cast_null:0x[^ ]+]] {{.+}} cb_in_from_void_cast_null +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 0 +int *__counted_by(count) cb_in_from_void_cast_null(int count) { + return (void*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_in_from_int_void_int_cast_null:0x[^ ]+]] {{.+}} cb_in_from_int_void_int_cast_null +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 0 +int *__counted_by(count) cb_in_from_int_void_int_cast_null(int count) { + return (int*)(void*)(int*)0; +} + +// CHECK: FunctionDecl [[func_cb_out_from_single:0x[^ ]+]] {{.+}} cb_out_from_single +// CHECK: |-ParmVarDecl [[var_count_3:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_3]] +int *__counted_by(*count) cb_out_from_single(int *__single count, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_sb_from_single:0x[^ ]+]] {{.+}} sb_from_single +// CHECK: |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_4]] +void *__sized_by(size) sb_from_single(int size, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_cbn_in_from_single:0x[^ ]+]] {{.+}} cbn_in_from_single +// CHECK: |-ParmVarDecl [[var_count_4:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_5:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_5]] +int *__counted_by_or_null(count) cbn_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_eb_in_from_single:0x[^ ]+]] {{.+}} eb_in_from_single +// CHECK: |-ParmVarDecl [[var_end:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_6:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_6]] +int *__ended_by(end) eb_in_from_single(int *__single end, int *__single p) { + return p; +} diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-in-return-invalid-expr.c b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-invalid-expr.c new file mode 100644 index 0000000000000..ad9e434e69176 --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-invalid-expr.c @@ -0,0 +1,20 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: not %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -ast-dump %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -ast-dump %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// Make sure we don't add bounds checks to RecoveryExprs +// CHECK-LABEL:`-FunctionDecl {{.+}} <{{.+}}, line:{{.+}}> line:{{.+}} no_bounds_check_expr_ret 'int *__single __sized_by(2)(int *__single __sized_by(3))' +// CHECK-NEXT: |-ParmVarDecl {{.+}} p 'int *__single __sized_by(3)':'int *__single' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-ReturnStmt {{.+}} +// CHECK-NEXT: `-RecoveryExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' contains-errors +// CHECK-NEXT: `-FloatingLiteral {{.+}} 'float' 0.000000e+00 +int* __sized_by(2) no_bounds_check_expr_ret(int* __sized_by(3) p) { + return 0.0f; +} + diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null-system-header.c b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null-system-header.c new file mode 100644 index 0000000000000..92f58c31c954e --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null-system-header.c @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -ast-dump -verify %s > %t.c.ast_dump.txt 2>&1 +// RUN: FileCheck --input-file=%t.c.ast_dump.txt %S/bounds-attributed-in-return-null-system-header.h +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety-bringup-missing-checks=return_size -ast-dump -verify %s > %t.objc.ast_dump.txt 2>&1 +// RUN: FileCheck --input-file=%t.objc.ast_dump.txt %S/bounds-attributed-in-return-null-system-header.h + +#include "bounds-attributed-in-return-null-system-header.h" + +// expected-no-diagnostics + +// AST CHECK lines are in the included header file +int* __counted_by(count) test_explicit_unspecified_cast_0(int count) { + return inline_header_ret_explicit_unspecified_cast_0(count); +} + +// AST CHECK lines are in the included header file +int* __counted_by(count) test_explicit_unsafe_indexable_cast_0(int count) { + return inline_header_ret_explicit_unsafe_indexable_cast_0(count); +} + +// AST CHECK lines are in the included header file +int* __counted_by(count) test_0(int count) { + return inline_header_ret_0(count); +} + +// AST CHECK lines are in the included header file +int* __counted_by(count) test_void_star_unspecified_0(int count) { + return inline_header_ret_void_star_unspecified_0(count); +} + +// AST CHECK lines are in the included header file +int* __counted_by(count) test_void_star_unsafe_indexable_0(int count) { + return inline_header_ret_void_star_unsafe_indexable_0(count); +} diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null-system-header.h b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null-system-header.h new file mode 100644 index 0000000000000..508e0dc25f0f2 --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null-system-header.h @@ -0,0 +1,530 @@ +// Pretend this is a system header +#pragma clang system_header +#include + +// FIXME: We might not want bounds checks here because this file is in something +// that hasn't fully adopted (rdar://139815437) + +// CHECK: {{^}}|-FunctionDecl [[func_inline_header_ret_explicit_unspecified_cast_0:0x[^ ]+]] {{.+}} inline_header_ret_explicit_unspecified_cast_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'count == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count]] +inline int* __counted_by(count) inline_header_ret_explicit_unspecified_cast_0(int count) { + // Outside of system headers this implicit conversion **is allowed** + return (int*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_inline_header_ret_explicit_unsafe_indexable_cast_0:0x[^ ]+]] {{.+}} inline_header_ret_explicit_unsafe_indexable_cast_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'count == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *__unsafe_indexable' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count_1]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *__unsafe_indexable' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_3]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_1]] +inline int* __counted_by(count) inline_header_ret_explicit_unsafe_indexable_cast_0(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. + return (int* __unsafe_indexable)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_inline_header_ret_0:0x[^ ]+]] {{.+}} inline_header_ret_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'count == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count_2]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_5]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_2]] +inline int* __counted_by(count) inline_header_ret_0(int count) { + // Outside of system headers this implicit conversion **is allowed** + return 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_inline_header_ret_void_star_unspecified_0:0x[^ ]+]] {{.+}} inline_header_ret_void_star_unspecified_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'count == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count_3]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_7]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_3]] +inline int* __counted_by(count) inline_header_ret_void_star_unspecified_0(int count) { + // Outside of system headers this implicit conversion **is allowed** + return (void*)0; +} + +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_inline_header_ret_void_star_unsafe_indexable_0:0x[^ ]+]] {{.+}} inline_header_ret_void_star_unsafe_indexable_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'count == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *__unsafe_indexable' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_count_4]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *__unsafe_indexable' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_9]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_4]] +inline int* __counted_by(count) inline_header_ret_void_star_unsafe_indexable_0(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. + return (void* __unsafe_indexable) 0; +} + +// These are the checks for the AST from the `bounds-attributed-in-return-null-system-header.c`. +// They have to be here because that's what FileCheck requires. + +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_test_explicit_unspecified_cast_0:0x[^ ]+]] {{.+}} test_explicit_unspecified_cast_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'inline_header_ret_explicit_unspecified_cast_0(count) <= __builtin_get_pointer_upper_bound(inline_header_ret_explicit_unspecified_cast_0(count)) && __builtin_get_pointer_lower_bound(inline_header_ret_explicit_unspecified_cast_0(count)) <= inline_header_ret_explicit_unspecified_cast_0(count) && count <= __builtin_get_pointer_upper_bound(inline_header_ret_explicit_unspecified_cast_0(count)) - inline_header_ret_explicit_unspecified_cast_0(count) && 0 <= count' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unspecified_cast_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_5]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unspecified_cast_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_10]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unspecified_cast_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_5]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unspecified_cast_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_13]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_5]] +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_test_explicit_unsafe_indexable_cast_0:0x[^ ]+]] {{.+}} test_explicit_unsafe_indexable_cast_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'inline_header_ret_explicit_unsafe_indexable_cast_0(count) <= __builtin_get_pointer_upper_bound(inline_header_ret_explicit_unsafe_indexable_cast_0(count)) && __builtin_get_pointer_lower_bound(inline_header_ret_explicit_unsafe_indexable_cast_0(count)) <= inline_header_ret_explicit_unsafe_indexable_cast_0(count) && count <= __builtin_get_pointer_upper_bound(inline_header_ret_explicit_unsafe_indexable_cast_0(count)) - inline_header_ret_explicit_unsafe_indexable_cast_0(count) && 0 <= count' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unsafe_indexable_cast_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_6]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unsafe_indexable_cast_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_14]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unsafe_indexable_cast_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_6]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_explicit_unsafe_indexable_cast_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_17]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_6]] +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_test_0:0x[^ ]+]] {{.+}} test_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_7:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'inline_header_ret_0(count) <= __builtin_get_pointer_upper_bound(inline_header_ret_0(count)) && __builtin_get_pointer_lower_bound(inline_header_ret_0(count)) <= inline_header_ret_0(count) && count <= __builtin_get_pointer_upper_bound(inline_header_ret_0(count)) - inline_header_ret_0(count) && 0 <= count' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_19:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_20]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_7]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_19]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_18]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_19]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_20]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_7]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_19]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_21]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_7]] +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_test_void_star_unspecified_0:0x[^ ]+]] {{.+}} test_void_star_unspecified_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_count_8:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'inline_header_ret_void_star_unspecified_0(count) <= __builtin_get_pointer_upper_bound(inline_header_ret_void_star_unspecified_0(count)) && __builtin_get_pointer_lower_bound(inline_header_ret_void_star_unspecified_0(count)) <= inline_header_ret_void_star_unspecified_0(count) && count <= __builtin_get_pointer_upper_bound(inline_header_ret_void_star_unspecified_0(count)) - inline_header_ret_void_star_unspecified_0(count) && 0 <= count' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_23:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unspecified_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_24]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_8]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_23]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unspecified_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_22]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_23]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unspecified_0]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_24]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_count_8]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_23]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unspecified_0]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_25]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_count_8]] +// CHECK-NEXT: {{^}}`-FunctionDecl [[func_test_void_star_unsafe_indexable_0:0x[^ ]+]] {{.+}} test_void_star_unsafe_indexable_0 +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_count_9:0x[^ ]+]] +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} `-ReturnStmt +// CHECK-NEXT: {{^}} `-BoundsCheckExpr {{.+}} 'inline_header_ret_void_star_unsafe_indexable_0(count) <= __builtin_get_pointer_upper_bound(inline_header_ret_void_star_unsafe_indexable_0(count)) && __builtin_get_pointer_lower_bound(inline_header_ret_void_star_unsafe_indexable_0(count)) <= inline_header_ret_void_star_unsafe_indexable_0(count) && count <= __builtin_get_pointer_upper_bound(inline_header_ret_void_star_unsafe_indexable_0(count)) - inline_header_ret_void_star_unsafe_indexable_0(count) && 0 <= count' +// CHECK-NEXT: {{^}} |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] +// CHECK-NEXT: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_27:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | | | `-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unsafe_indexable_0]] +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_28]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count_9]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_27]] +// CHECK-NEXT: {{^}} | | `-CallExpr +// CHECK-NEXT: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unsafe_indexable_0]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_29]] {{.*}} 'int' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_26]] +// CHECK-NEXT: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_27]] +// CHECK-NEXT: {{^}} | | | | `-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unsafe_indexable_0]] +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_28]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count_9]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_27]] +// CHECK-NEXT: {{^}} | | `-CallExpr +// CHECK-NEXT: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[func_inline_header_ret_void_star_unsafe_indexable_0]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_29]] +// CHECK-NEXT: {{^}} `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} `-DeclRefExpr {{.+}} [[var_count_9]] diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null.c b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null.c new file mode 100644 index 0000000000000..3472c95f7896e --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-in-return-null.c @@ -0,0 +1,421 @@ + + +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -ast-dump %s 2>&1 | FileCheck %s +#include + +// CHECK: {{^}}|-FunctionDecl [[func_cb_0:0x[^ ]+]] {{.+}} cb_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len]] +int *__counted_by(len) cb_0(int len) { + return 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_NULL:0x[^ ]+]] {{.+}} cb_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_3]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_1]] +int *__counted_by(len) cb_NULL(int len) { + return (void *)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_int_cast:0x[^ ]+]] {{.+}} cb_int_cast +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_5]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_2]] +int *__counted_by(len) cb_int_cast(int len) { + return (int *)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_int_cast_NULL:0x[^ ]+]] {{.+}} cb_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_7]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_3]] +int *__counted_by(len) cb_int_cast_NULL(int len) { + return (int *)(void*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cb_int_void_int_cast_NULL:0x[^ ]+]] {{.+}} cb_int_void_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_4]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_9]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_4]] +int *__counted_by(len) cb_int_void_int_cast_NULL(int len) { + return ((int*)(void*)(int*)0); +} + +// CHECK: {{^}}|-FunctionDecl [[func_sb_0:0x[^ ]+]] {{.+}} sb_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'size == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(size)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_10]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_size]] +int *__sized_by(size) sb_0(int size) { + return 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_sb_int_cast:0x[^ ]+]] {{.+}} sb_int_cast +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_5]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_13]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_5]] +int *__sized_by(len) sb_int_cast(int len) { + return (int *)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_sb_int_cast_NULL:0x[^ ]+]] {{.+}} sb_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_14]] +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_6]] +int *__sized_by(len) sb_int_cast_NULL(int len) { + return (int *)(void*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_sb_int_void_int_cast_NULL:0x[^ ]+]] {{.+}} sb_int_void_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_7:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-BoundsCheckExpr {{.+}} 'len == 0' +// CHECK-NEXT: {{^}}| |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '==' +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_17:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}}| | `-ParenExpr +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_17]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| `-DeclRefExpr {{.+}} [[var_len_7]] +int *__sized_by(len) sb_int_void_int_cast_NULL(int len) { + return ((int*)(void*)(int*)0); +} + +// CHECK: {{^}}|-FunctionDecl [[func_cbn_0:0x[^ ]+]] {{.+}} cbn_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_8:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_18]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_8]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +int *__counted_by_or_null(len) cbn_0(int len) { + return 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cbn_int_cast:0x[^ ]+]] {{.+}} cbn_int_cast +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_9:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_20]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_9]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_20]] {{.*}} 'int *' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_21]] {{.*}} 'int' +int *__counted_by_or_null(len) cbn_int_cast(int len) { + return (int*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cbn_int_cast_NULL:0x[^ ]+]] {{.+}} cbn_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_10:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_22]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_23:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_10]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_22]] {{.*}} 'int *' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_23]] {{.*}} 'int' +int *__counted_by_or_null(len) cbn_int_cast_NULL(int len) { + return (int*)(void*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_cbn_int_void_int_cast_NULL:0x[^ ]+]] {{.+}} cbn_int_void_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_11:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-ParenExpr +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_24]] +// CHECK-NEXT: {{^}}| | | `-ParenExpr +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_25:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_11]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_24]] {{.*}} 'int *' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_25]] {{.*}} 'int' +int *__counted_by_or_null(len) cbn_int_void_int_cast_NULL(int len) { + return ((int*)(void*)(int*)0); +} + +// CHECK: {{^}}|-FunctionDecl [[func_sbn_0:0x[^ ]+]] {{.+}} sbn_0 +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_12:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_26]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_12]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +int *__sized_by_or_null(len) sbn_0(int len) { + return 0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_sbn_int_cast:0x[^ ]+]] {{.+}} sbn_int_cast +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_13:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_28]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_13]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_28]] {{.*}} 'int *' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_29]] {{.*}} 'int' +int *__sized_by_or_null(len) sbn_int_cast(int len) { + return (int*)0; +} + +// CHECK: {{^}}|-FunctionDecl [[func_sbn_int_cast_NULL:0x[^ ]+]] {{.+}} sbn_int_cast_NULL +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_14:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_30:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | |-OpaqueValueExpr [[ove_30]] +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_31:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_14]] +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_30]] {{.*}} 'int *' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +int *__sized_by_or_null(len) sbn_int_cast_NULL(int len) { + return (int*)(void*)0; +} + +// CHECK: {{^}}`-FunctionDecl [[func_sbn_int_void_int_cast_NULL:0x[^ ]+]] {{.+}} sbn_int_void_int_cast_NULL +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_len_15:0x[^ ]+]] +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} `-ReturnStmt +// CHECK-NEXT: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_32:0x[^ ]+]] +// CHECK-NEXT: {{^}} | | `-ParenExpr +// CHECK-NEXT: {{^}} | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}} | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | |-OpaqueValueExpr [[ove_32]] +// CHECK-NEXT: {{^}} | | `-ParenExpr +// CHECK-NEXT: {{^}} | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: {{^}} | | `-CStyleCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_33:0x[^ ]+]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | `-DeclRefExpr {{.+}} [[var_len_15]] +// CHECK-NEXT: {{^}} |-OpaqueValueExpr [[ove_32]] {{.*}} 'int *' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_33]] {{.*}} 'int' +int *__sized_by_or_null(len) sbn_int_void_int_cast_NULL(int len) { + return ((int*)(void*)(int*)0); +} diff --git a/clang/test/BoundsSafety/AST/bounds-attributed-in-return.c b/clang/test/BoundsSafety/AST/bounds-attributed-in-return.c new file mode 100644 index 0000000000000..ac3819b9accd1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/bounds-attributed-in-return.c @@ -0,0 +1,298 @@ + + +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: FunctionDecl [[func_cb_in_from_bidi:0x[^ ]+]] {{.+}} cb_in_from_bidi +// CHECK: |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && count <= __builtin_get_pointer_upper_bound(p) - p && 0 <= count' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: `-OpaqueValueExpr [[ove_1]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_count]] +int *__counted_by(count) cb_in_from_bidi(int count, int *__bidi_indexable p) { + return p; +} + +// CHECK: FunctionDecl [[func_cb_in_from_indexable:0x[^ ]+]] {{.+}} cb_in_from_indexable +// CHECK: |-ParmVarDecl [[var_count_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && count <= __builtin_get_pointer_upper_bound(p) - p && 0 <= count' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK: `-OpaqueValueExpr [[ove_3]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_count_1]] +int *__counted_by(count) cb_in_from_indexable(int count, int *__indexable p) { + return p; +} + +// CHECK: FunctionDecl [[func_cb_in_from_single:0x[^ ]+]] {{.+}} cb_in_from_single +// CHECK: |-ParmVarDecl [[var_count_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && count <= __builtin_get_pointer_upper_bound(p) - p && 0 <= count' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_4]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: `-OpaqueValueExpr [[ove_5]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_count_2]] +int *__counted_by(count) cb_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_cb_out_from_single:0x[^ ]+]] {{.+}} cb_out_from_single +// CHECK: |-ParmVarDecl [[var_count_3:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && *count <= __builtin_get_pointer_upper_bound(p) - p && 0 <= *count' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*count)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_6]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_3]] +// CHECK: `-OpaqueValueExpr [[ove_7]] +// CHECK: `-UnaryOperator {{.+}} cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_count_3]] +int *__counted_by(*count) cb_out_from_single(int *__single count, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_sb_from_single:0x[^ ]+]] {{.+}} sb_from_single +// CHECK: |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && size <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__bidi_indexable)p && 0 <= size' +// CHECK: |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_8]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK: `-OpaqueValueExpr [[ove_9]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_size]] +void *__sized_by(size) sb_from_single(int size, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_cbn_in_from_single:0x[^ ]+]] {{.+}} cbn_in_from_single +// CHECK: |-ParmVarDecl [[var_count_4:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_5:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || count <= __builtin_get_pointer_upper_bound(p) - p && 0 <= count' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_10]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: `-OpaqueValueExpr [[ove_11]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_count_4]] +int *__counted_by_or_null(count) cbn_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK: FunctionDecl [[func_eb_in_from_single:0x[^ ]+]] {{.+}} eb_in_from_single +// CHECK: |-ParmVarDecl [[var_end:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_p_6:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-BoundsCheckExpr {{.+}} 'end <= __builtin_get_pointer_upper_bound(p) && p <= end' +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: | | `-GetBoundExpr {{.+}} upper +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single' +// CHECK: |-OpaqueValueExpr [[ove_12]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_6]] +// CHECK: `-OpaqueValueExpr [[ove_13]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_end]] +int *__ended_by(end) eb_in_from_single(int *__single end, int *__single p) { + return p; +} diff --git a/clang/test/BoundsSafety/AST/builtin-memcpy-count-annotation.c b/clang/test/BoundsSafety/AST/builtin-memcpy-count-annotation.c new file mode 100644 index 0000000000000..41eea51c25317 --- /dev/null +++ b/clang/test/BoundsSafety/AST/builtin-memcpy-count-annotation.c @@ -0,0 +1,110 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -Wcast-qual 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -Wcast-qual 2>&1 | FileCheck %s + +#include + +void foo(void) { + char *dst, *src; + __builtin_memcpy(dst, src, 10); +} + +// CHECK: {{^}}|-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | |-VarDecl [[var_dst:0x[^ ]+]] +// CHECK: {{^}}| | `-VarDecl [[var_src:0x[^ ]+]] +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: {{^}}| | | | `-AssumptionExpr +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_dst]] +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_src]] +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}}| | | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}}| | `-BoundsCheckExpr +// CHECK: {{^}}| | |-BoundsCheckExpr +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(function-parameter-0-2)(*)(void *__single __sized_by(function-parameter-0-2), const void *__single __sized_by(function-parameter-0-2), unsigned long)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'const void *__single __sized_by(function-parameter-0-2)':'const void *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'const void *' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'const void *' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-ImplicitCastExpr {{.+}} 'const void *' +// CHECK: {{^}}| | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'const void *' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: {{^}}| | | `-CStyleCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: {{^}}| | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: {{^}}| | `-CStyleCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_2]] {{.*}} 'const void *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' diff --git a/clang/test/BoundsSafety/AST/cast-inner-dimensions.c b/clang/test/BoundsSafety/AST/cast-inner-dimensions.c new file mode 100644 index 0000000000000..92d42de95685f --- /dev/null +++ b/clang/test/BoundsSafety/AST/cast-inner-dimensions.c @@ -0,0 +1,22 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +void foo(void) { + (void)(void *)(char *__single *)0; + (void)(char *__single *)(void *)0; +} + +// CHECK: TranslationUnitDecl +// CHECK: `-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: `-CompoundStmt +// CHECK: |-CStyleCastExpr {{.+}} 'void' +// CHECK: | `-CStyleCastExpr {{.+}} 'void *' +// CHECK: | `-CStyleCastExpr {{.+}} 'char *__single*' +// CHECK: | `-IntegerLiteral {{.+}} 0 +// CHECK: `-CStyleCastExpr {{.+}} 'void' +// CHECK: `-CStyleCastExpr {{.+}} 'char *__single*' +// CHECK: `-CStyleCastExpr {{.+}} 'void *' +// CHECK: `-IntegerLiteral {{.+}} 0 diff --git a/clang/test/BoundsSafety/AST/common-sugared-type.c b/clang/test/BoundsSafety/AST/common-sugared-type.c new file mode 100644 index 0000000000000..2cc4119da29bc --- /dev/null +++ b/clang/test/BoundsSafety/AST/common-sugared-type.c @@ -0,0 +1,41 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety %s -o /dev/null + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null + +struct s { + int dummy; +}; + +struct s *g; + +typedef struct s s_t; +s_t *f(void) { + s_t *l; + return g ? l : g; +} +// CHECK:TranslationUnitDecl {{.*}} <> +// CHECK:|-RecordDecl {{.*}} <{{.*}}common-sugared-type.c:9:1, line:11:1> line:9:8 struct s definition +// CHECK-NEXT:| `-FieldDecl {{.*}} col:6 dummy 'int' +// CHECK-NEXT:|-VarDecl {{.*}} col:11 used g 'struct s *__single' +// CHECK-NEXT:|-TypedefDecl {{.*}} col:18 referenced s_t 'struct s' +// CHECK-NEXT:| `-ElaboratedType {{.*}} 'struct s' sugar +// CHECK-NEXT:| `-RecordType {{.*}} 'struct s' +// CHECK-NEXT:| `-Record {{.*}} 's' +// CHECK-NEXT:`-FunctionDecl {{.*}} line:16:6 f 's_t *__single(void)' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} col:8 used l 's_t *__bidi_indexable' +// CHECK-NEXT: `-ReturnStmt {{.*}} +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 's_t *__single' +// CHECK-NEXT: `-ConditionalOperator {{.*}} 'struct s *__bidi_indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'struct s *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'struct s *__single' lvalue Var {{.*}} 'g' 'struct s *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 's_t *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 's_t *__bidi_indexable' lvalue Var {{.*}} 'l' 's_t *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'struct s *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'struct s *__single' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'struct s *__single' lvalue Var {{.*}} 'g' 'struct s *__single' diff --git a/clang/test/BoundsSafety/AST/complex-typespecs-with-bounds.c b/clang/test/BoundsSafety/AST/complex-typespecs-with-bounds.c new file mode 100644 index 0000000000000..efdbd2252145d --- /dev/null +++ b/clang/test/BoundsSafety/AST/complex-typespecs-with-bounds.c @@ -0,0 +1,190 @@ + +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s + +// Tests the correctness of applying bounds attributes to complex type specifiers +// as well as to what extent other attributes (represented by _Nullable) are retained. + +#include "complex-typespecs-with-bounds.h" +#include + +void typeoftypes() { + typeof((long * _Nullable) 0) __single p1; + typeof(typeof(bar) *) __single p2; +} + +struct S { + char * _Nullable f1; +}; + +void typeofexprs(struct S s) { + typeof(foo()) __single p1; + typeof(&foo) __single p2; + typeof(&foo) __unsafe_indexable p3; // error: pointer cannot have more than one bound attribute + + typeof(bar) __single p4; + typeof(&bar) __single p5; // error: pointer cannot have more than one bound attribute + typeof(bar) * __single p6; + typeof(bar[2]) * __single p7; + typeof(&bar[2]) __single p8; + typeof(&*bar) __single p9; + + typeof(s.f1) __bidi_indexable p10; + typeof(*s.f1) * __bidi_indexable p11; + typeof(&*s.f1) __unsafe_indexable p12; +} + +typedef typeof(*bar) my_t; +typedef typeof(bar) my_ptr_t; +typedef typeof(*bar) * my_manual_ptr_t; + +void typedefs_of_typeof() { + my_t * __single p1; + my_ptr_t __single p2; + my_manual_ptr_t __single p3; + my_manual_ptr_t __bidi_indexable p4; + my_manual_ptr_t __unsafe_indexable p5; +} + +void autotypes(char * _Nullable __single p) { + __auto_type * __unsafe_indexable p1 = p; + __auto_type * __unsafe_indexable p2 = &*p; +} + +void typeofexpr_typeofexpr() { + typeof(bar) p1; + typeof(p1) __single p2; +} + +void typeofexpr_typeoftype_typeofexpr() { + typeof(typeof(bar)) p1; + typeof(p1) __single p2; +} + +void typeof_autotype1() { + __auto_type p1 = bar; + typeof(p1) __single p2; +} + +void typeof_autotype2() { + __auto_type * p1 = bar; + typeof(p1) __single p2; +} + +// CHECK: TranslationUnitDecl +// CHECK: |-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: |-VarDecl [[var_bar:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_typeoftypes:0x[^ ]+]] {{.+}} typeoftypes +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p1:0x[^ ]+]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2:0x[^ ]+]] +// CHECK: |-RecordDecl +// CHECK: | `-FieldDecl +// CHECK: |-FunctionDecl [[func_typeofexprs:0x[^ ]+]] {{.+}} typeofexprs +// CHECK: | |-ParmVarDecl [[var_referenced:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p1:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p2:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p3:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p4:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p5:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p6:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p7:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p8:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p9:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p10:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p11:0x[^ ]+]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p12:0x[^ ]+]] +// CHECK: |-TypedefDecl +// CHECK: | `-TypeOfExprType +// CHECK: | |-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char * _Nullable':'char *' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-BuiltinType +// CHECK: |-TypedefDecl +// CHECK: | `-TypeOfExprType +// CHECK: | |-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-AttributedType +// CHECK: | `-AttributedType +// CHECK: | `-PointerType +// CHECK: | `-BuiltinType +// CHECK: |-TypedefDecl +// CHECK: | `-PointerType +// CHECK: | `-TypeOfExprType +// CHECK: | |-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char * _Nullable':'char *' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-BuiltinType +// CHECK: |-FunctionDecl [[func_typedefs_of_typeof:0x[^ ]+]] {{.+}} typedefs_of_typeof +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p1_1:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p2_1:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p3_1:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p4_1:0x[^ ]+]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p5_1:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_autotypes:0x[^ ]+]] {{.+}} autotypes +// CHECK: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_p1_2:0x[^ ]+]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__unsafe_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__single _Nullable':'char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_2:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__unsafe_indexable' +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__single _Nullable':'char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: |-FunctionDecl [[func_typeofexpr_typeofexpr:0x[^ ]+]] {{.+}} typeofexpr_typeofexpr +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_referenced_1:0x[^ ]+]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_3:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_typeofexpr_typeoftype_typeofexpr:0x[^ ]+]] {{.+}} typeofexpr_typeoftype_typeofexpr +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_referenced_2:0x[^ ]+]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_4:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_typeof_autotype1:0x[^ ]+]] {{.+}} typeof_autotype1 +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_referenced_3:0x[^ ]+]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char * _Nullable':'char *' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_5:0x[^ ]+]] +// CHECK: `-FunctionDecl [[func_typeof_autotype2:0x[^ ]+]] {{.+}} typeof_autotype2 +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_referenced_4:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char * _Nullable':'char *' +// CHECK: | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_6:0x[^ ]+]] + diff --git a/clang/test/BoundsSafety/AST/complex-typespecs-with-bounds.h b/clang/test/BoundsSafety/AST/complex-typespecs-with-bounds.h new file mode 100644 index 0000000000000..5659fcf83d793 --- /dev/null +++ b/clang/test/BoundsSafety/AST/complex-typespecs-with-bounds.h @@ -0,0 +1,4 @@ +#pragma clang system_header + +char * _Nullable foo(); +char * _Nullable bar; diff --git a/clang/test/BoundsSafety/AST/compound-assign-count.c b/clang/test/BoundsSafety/AST/compound-assign-count.c new file mode 100644 index 0000000000000..1299dcadfcbde --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-assign-count.c @@ -0,0 +1,110 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety %s | FileCheck %s +#include + +struct T { + int *__counted_by(len) ptr; + int len; +}; + +void Test(struct T *t, int amt) { + t->ptr += amt; + t->len -= amt; +} + +// CHECK-LABEL: Test 'void (struct T *__single, int)' +// CHECK: |-ParmVarDecl [[var_t:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_amt:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CompoundAssignOperator {{.+}} ComputeResultTy='int *__single +// CHECK: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_amt]] +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-MemberExpr {{.+}} ->ptr +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->len +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_amt]] +// CHECK: | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-MemberExpr {{.+}} ->len +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct T *__single' +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-CompoundAssignOperator {{.+}} 'int' '-=' +// CHECK: | |-OpaqueValueExpr [[ove_7]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove_7]] {{.*}} lvalue +// CHECK: `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/compound-literal-counted_by-disabled-checks.c b/clang/test/BoundsSafety/AST/compound-literal-counted_by-disabled-checks.c new file mode 100644 index 0000000000000..7b894bf5b9a0a --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-counted_by-disabled-checks.c @@ -0,0 +1,1183 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct cb { + int count; + char* __counted_by(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cb 'void (struct cb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cb' +void consume_cb(struct cb); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cb_arr 'void (struct cb (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cb (*__single)[]' +void consume_cb_arr(struct cb (*arr)[]); + +struct nested_cb { + struct cb nested; + int other; +}; + +struct nested_and_outer_cb { + struct cb nested; + int other; + int count; + char* __counted_by(count) buf; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_cb_with_other_data_arr 'void (struct cb_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cb_with_other_data (*__single)[]' +void consume_cb_with_other_data_arr(struct cb_with_other_data (*arr)[]); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct cb *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct cb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'new' 'struct cb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cb 'struct cb (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct cb return_cb(int new_count, char* __bidi_indexable new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct cb *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct cb* ptr, int new_count) { + *ptr = (struct cb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_cb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_cb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_cb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_and_outer_cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb (*__single)[])' Function {{.+}} 'consume_cb_arr' 'void (struct cb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb[2]' lvalue Var {{.+}} 'arr' 'struct cb[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __counted_by((struct cb[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__counted_by(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct cb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct cb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + // FIXME: The type that `0x0` gets implicitly casted to is weird because its + // using the CompoundLiteralExpr as a base. That base contains a side-effect + // which means its not a valid counted_by expression. So far this hasn't + // mattered. + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct cb *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +void assign_via_ptr_from_ptr(struct cb* ptr) { + *ptr = (struct cb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_cb 'void (struct cb *__single, int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_cb(struct cb* ptr, int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'new' 'struct cb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cb_from_cb 'struct cb (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct cb return_cb_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_cb 'void (struct nested_cb *__single, char *__single __counted_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_cb 'void (struct nested_cb *__single, char *__single __counted_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_cb 'void (char *__single __counted_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb (*__single)[])' Function {{.+}} 'consume_cb_arr' 'void (struct cb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb[2]' lvalue Var {{.+}} 'arr' 'struct cb[2]' +void array_of_struct_init_from_cb(char* __counted_by(new_count) new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_cb 'void (struct cb_with_other_data *__single, int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_cb 'void (struct cb_with_other_data *__single, int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + // FIXME: The type that `0x0` gets implicitly casted to is weird because its + // using the CompoundLiteralExpr as a base. That base contains a side-effect + // which means its not a valid counted_by expression. So far this hasn't + // mattered. + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_cb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_cb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: `-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-counted_by.c b/clang/test/BoundsSafety/AST/compound-literal-counted_by.c new file mode 100644 index 0000000000000..d5ea00d5edbb5 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-counted_by.c @@ -0,0 +1,4784 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct cb { + int count; + char* __counted_by(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cb 'void (struct cb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cb' +void consume_cb(struct cb); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cb_arr 'void (struct cb (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cb (*__single)[]' +void consume_cb_arr(struct cb (*arr)[]); + +struct nested_cb { + struct cb nested; + int other; +}; + +struct nested_and_outer_cb { + struct cb nested; + int other; + int count; + char* __counted_by(count) buf; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_cb_with_other_data_arr 'void (struct cb_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cb_with_other_data (*__single)[]' +void consume_cb_with_other_data_arr(struct cb_with_other_data (*arr)[]); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct cb *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct cb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'new' 'struct cb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cb 'struct cb (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct cb return_cb(int new_count, char* __bidi_indexable new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct cb *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct cb* ptr, int new_count) { + *ptr = (struct cb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_cb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_cb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_cb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct nested_and_outer_cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct nested_and_outer_cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' '0 == 0' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb (*__single)[])' Function {{.+}} 'consume_cb_arr' 'void (struct cb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb[2]' lvalue Var {{.+}} 'arr' 'struct cb[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __counted_by((struct cb[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__counted_by(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct cb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_other_data_side_effect(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct cb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct cb *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr->buf <= __builtin_get_pointer_upper_bound(ptr->buf) && __builtin_get_pointer_lower_bound(ptr->buf) <= ptr->buf && ptr->count <= __builtin_get_pointer_upper_bound(ptr->buf) - ptr->buf && 0 <= ptr->count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +void assign_via_ptr_from_ptr(struct cb* ptr) { + *ptr = (struct cb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_cb 'void (struct cb *__single, int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_cb(struct cb* ptr, int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'new' 'struct cb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cb_from_cb 'struct cb (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct cb return_cb_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_cb 'void (int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_cb 'void (struct nested_cb *__single, char *__single __counted_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_cb 'void (struct nested_cb *__single, char *__single __counted_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_cb 'void (char *__single __counted_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct cb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' '0 == 0' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb (*__single)[])' Function {{.+}} 'consume_cb_arr' 'void (struct cb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb[2]' lvalue Var {{.+}} 'arr' 'struct cb[2]' +void array_of_struct_init_from_cb(char* __counted_by(new_count) new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_cb 'void (struct cb_with_other_data *__single, int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_other_data_side_effect_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_cb 'void (struct cb_with_other_data *__single, int, char *__single __counted_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __counted_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_cb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_cb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cb' 'struct cb_with_other_data' +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_null_ptr_cb 'void (int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cb' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: `-BoundsCheckExpr {{.+}} 'struct cb' 'new_count == 0' +// CHECK-NEXT: |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | `-ParenExpr {{.+}} 'char *' +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: `-ParenExpr {{.+}} 'char *' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'void *' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void call_null_ptr_cb(int new_count) { + consume_cb((struct cb) { + .buf = (char*)0, + .count = new_count + }); + consume_cb((struct cb) { + .buf = (void*)0, + .count = new_count + }); + consume_cb((struct cb) { + .buf = ((char*)(void*)(char*)0), + .count = new_count + }); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-counted_by_or_null-disabled-checks.c b/clang/test/BoundsSafety/AST/compound-literal-counted_by_or_null-disabled-checks.c new file mode 100644 index 0000000000000..8f1f5ae2035e0 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-counted_by_or_null-disabled-checks.c @@ -0,0 +1,1168 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct cbon { + int count; + char* __counted_by_or_null(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cbon 'void (struct cbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cbon' +void consume_cbon(struct cbon); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cbon_arr 'void (struct cbon (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cbon (*__single)[]' +void consume_cbon_arr(struct cbon (*arr)[]); + +struct nested_cbon { + struct cbon nested; + int other; +}; + +struct nested_and_outer_cbon { + struct cbon nested; + int other; + int count; + char* __counted_by_or_null(count) buf; +}; + + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_cbon_with_other_data_arr 'void (struct cbon_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cbon_with_other_data (*__single)[]' +void consume_cbon_with_other_data_arr(struct cbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct cbon_with_other_data cbon; + struct no_attr_with_other_data no_cbon; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct cbon *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct cbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'new' 'struct cbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cbon 'struct cbon (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct cbon return_cbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct cbon *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct cbon* ptr, int new_count) { + *ptr = (struct cbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_cbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_cbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_cbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_and_outer_cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon (*__single)[])' Function {{.+}} 'consume_cbon_arr' 'void (struct cbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon[2]' lvalue Var {{.+}} 'arr' 'struct cbon[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct cbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct cbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by_or_null source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct cbon *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +void assign_via_ptr_from_ptr(struct cbon* ptr) { + *ptr = (struct cbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_cbon 'void (struct cbon *__single, int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_cbon(struct cbon* ptr, int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'new' 'struct cbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cbon_from_cbon 'struct cbon (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct cbon return_cbon_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_cbon 'void (struct nested_cbon *__single, char *__single __counted_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_cbon 'void (struct nested_cbon *__single, char *__single __counted_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_cbon 'void (char *__single __counted_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon (*__single)[])' Function {{.+}} 'consume_cbon_arr' 'void (struct cbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon[2]' lvalue Var {{.+}} 'arr' 'struct cbon[2]' +void array_of_struct_init_from_cbon(char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_cbon 'void (struct cbon_with_other_data *__single, int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon 'void (struct cbon_with_other_data *__single, int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_cbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_cbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: `-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-counted_by_or_null.c b/clang/test/BoundsSafety/AST/compound-literal-counted_by_or_null.c new file mode 100644 index 0000000000000..fb73820f56772 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-counted_by_or_null.c @@ -0,0 +1,5130 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct cbon { + int count; + char* __counted_by_or_null(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cbon 'void (struct cbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cbon' +void consume_cbon(struct cbon); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cbon_arr 'void (struct cbon (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cbon (*__single)[]' +void consume_cbon_arr(struct cbon (*arr)[]); + +struct nested_cbon { + struct cbon nested; + int other; +}; + +struct nested_and_outer_cbon { + struct cbon nested; + int other; + int count; + char* __counted_by_or_null(count) buf; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_cbon_with_other_data_arr 'void (struct cbon_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct cbon_with_other_data (*__single)[]' +void consume_cbon_with_other_data_arr(struct cbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct cbon_with_other_data cbon; + struct no_attr_with_other_data no_cbon; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct cbon *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct cbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'new' 'struct cbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cbon 'struct cbon (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct cbon return_cbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct cbon *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct cbon* ptr, int new_count) { + *ptr = (struct cbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_cbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_cbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_cbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct nested_and_outer_cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct nested_and_outer_cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon (*__single)[])' Function {{.+}} 'consume_cbon_arr' 'void (struct cbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon[2]' lvalue Var {{.+}} 'arr' 'struct cbon[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __counted_by_or_null((struct cbon[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__counted_by_or_null(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct cbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_other_data_side_effect(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct cbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by_or_null source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct cbon *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr->buf <= __builtin_get_pointer_upper_bound(ptr->buf) && __builtin_get_pointer_lower_bound(ptr->buf) <= ptr->buf && !ptr->buf || ptr->count <= __builtin_get_pointer_upper_bound(ptr->buf) - ptr->buf && 0 <= ptr->count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +void assign_via_ptr_from_ptr(struct cbon* ptr) { + *ptr = (struct cbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_cbon 'void (struct cbon *__single, int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_cbon(struct cbon* ptr, int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct cbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'new' 'struct cbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct cbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_cbon_from_cbon 'struct cbon (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct cbon return_cbon_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_cbon 'void (int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_cbon 'void (struct nested_cbon *__single, char *__single __counted_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_cbon 'void (struct nested_cbon *__single, char *__single __counted_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_cbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_cbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_cbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_cbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_cbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_cbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_cbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_cbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_cbon 'void (char *__single __counted_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct cbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct cbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon (*__single)[])' Function {{.+}} 'consume_cbon_arr' 'void (struct cbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct cbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon[2]' lvalue Var {{.+}} 'arr' 'struct cbon[2]' +void array_of_struct_init_from_cbon(char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_cbon 'void (struct cbon_with_other_data *__single, int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_other_data_side_effect_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon 'void (struct cbon_with_other_data *__single, int, char *__single __counted_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __counted_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct cbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct cbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct cbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct cbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_cbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct cbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct cbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_cbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'cbon' 'struct cbon_with_other_data' +// CHECK-NEXT: `-BoundsCheckExpr {{.+}} 'struct cbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= __builtin_get_pointer_upper_bound(new_ptr) - new_ptr && 0 <= new_count' +// CHECK-NEXT: |-InitListExpr {{.+}} 'struct cbon_with_other_data' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-ended_by-disabled-checks.c b/clang/test/BoundsSafety/AST/compound-literal-ended_by-disabled-checks.c new file mode 100644 index 0000000000000..7cb49c770f354 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-ended_by-disabled-checks.c @@ -0,0 +1,957 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct eb { + char* __ended_by(end) start; + char* end; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_eb 'void (struct eb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct eb' +void consume_eb(struct eb); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_eb_arr 'void (struct eb (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct eb (*__single)[]' +void consume_eb_arr(struct eb (*arr)[]); + +struct nested_eb { + struct eb nested; + int other; +}; + +struct nested_and_outer_eb { + struct eb nested; + int other; + char* __ended_by(end) start; + char* end; +}; + + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_eb_with_other_data_arr 'void (struct eb_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct eb_with_other_data (*__single)[]' +void consume_eb_with_other_data_arr(struct eb_with_other_data (*arr)[]); + +union TransparentUnion { + struct eb_with_other_data eb; + struct no_attr_with_other_data no_eb; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr(struct eb* ptr, + char* __bidi_indexable new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct eb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct eb' lvalue Var {{.+}} 'new' 'struct eb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_operator(char* __bidi_indexable new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct eb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void local_var_init(char* __bidi_indexable new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb)' Function {{.+}} 'consume_eb' 'void (struct eb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void call_arg(char* __bidi_indexable new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_eb 'struct eb (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +struct eb return_eb(char* __bidi_indexable new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void construct_not_used(char* __bidi_indexable new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct eb *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_nullptr(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_and_outer_eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_nested_v3(struct nested_and_outer_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_and_outer_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0, + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct eb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb (*__single)[])' Function {{.+}} 'consume_eb_arr' 'void (struct eb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct eb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct eb[2]' lvalue Var {{.+}} 'arr' 'struct eb[2]' +void array_of_struct_init(char* __bidi_indexable new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct eb_with_other_data *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect(struct eb_with_other_data* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct eb_with_other_data *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union(char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently( + char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __ended_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_eb 'void (struct eb *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void assign_via_ptr_from_eb(struct eb* ptr, + char* __ended_by(new_end) new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct eb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct eb' lvalue Var {{.+}} 'new' 'struct eb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void assign_operator_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct eb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void local_var_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb)' Function {{.+}} 'consume_eb' 'void (struct eb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void call_arg_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_eb_from_eb 'struct eb (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +struct eb return_eb_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void construct_not_used_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr_from_eb 'void (struct eb *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_nullptr_from_eb(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_eb 'void (struct nested_eb *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_eb 'void (struct nested_eb *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct eb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb (*__single)[])' Function {{.+}} 'consume_eb_arr' 'void (struct eb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct eb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct eb[2]' lvalue Var {{.+}} 'arr' 'struct eb[2]' +void array_of_struct_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_eb 'void (struct eb_with_other_data *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_from_eb(struct eb_with_other_data* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_eb 'void (struct eb_with_other_data *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr_from_eb(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_from_eb(char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: `-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently_from_eb( + char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-ended_by.c b/clang/test/BoundsSafety/AST/compound-literal-ended_by.c new file mode 100644 index 0000000000000..4a620780b88b2 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-ended_by.c @@ -0,0 +1,2532 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct eb { + char* __ended_by(end) start; + char* end; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_eb 'void (struct eb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct eb' +void consume_eb(struct eb); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_eb_arr 'void (struct eb (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct eb (*__single)[]' +void consume_eb_arr(struct eb (*arr)[]); + +struct nested_eb { + struct eb nested; + int other; +}; + +struct nested_and_outer_eb { + struct eb nested; + int other; + char* __ended_by(end) start; + char* end; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_eb_with_other_data_arr 'void (struct eb_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct eb_with_other_data (*__single)[]' +void consume_eb_with_other_data_arr(struct eb_with_other_data (*arr)[]); + +union TransparentUnion { + struct eb_with_other_data eb; + struct no_attr_with_other_data no_eb; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr(struct eb* ptr, + char* __bidi_indexable new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct eb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct eb' lvalue Var {{.+}} 'new' 'struct eb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_operator(char* __bidi_indexable new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct eb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void local_var_init(char* __bidi_indexable new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb)' Function {{.+}} 'consume_eb' 'void (struct eb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void call_arg(char* __bidi_indexable new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_eb 'struct eb (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +struct eb return_eb(char* __bidi_indexable new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void construct_not_used(char* __bidi_indexable new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct eb *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(0) && 0 <= new_end' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_nullptr(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_eb *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct nested_and_outer_eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct nested_and_outer_eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | | | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_nested_v3(struct nested_and_outer_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_and_outer_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0, + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct eb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct eb' '0 <= __builtin_get_pointer_upper_bound(0) && 0 <= 0' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb (*__single)[])' Function {{.+}} 'consume_eb_arr' 'void (struct eb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct eb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct eb[2]' lvalue Var {{.+}} 'arr' 'struct eb[2]' +void array_of_struct_init(char* __bidi_indexable new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + // Note the type of `.start` and `.end`'s assignments here looks odd in the + // AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single /* __started_by((struct eb[2]){{.start = new_start, .end = new_end}, {.start = 0, .end = 0}}[1UL].start) */ ':'char *__single' + // + // We could consider constant folding the expression + // to `__ended_by(0)` but this doesn't seem to be necessary. + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct eb_with_other_data *__single, char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_other_data_side_effect(struct eb_with_other_data* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct eb_with_other_data *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(0) && 0 <= new_end' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_other_data_side_effect_zero_ptr(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void call_arg_transparent_union(char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (char *__bidi_indexable, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_start' 'char *__bidi_indexable' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void call_arg_transparent_union_untransparently( + char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __ended_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_eb 'void (struct eb *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void assign_via_ptr_from_eb(struct eb* ptr, + char* __ended_by(new_end) new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct eb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct eb' lvalue Var {{.+}} 'new' 'struct eb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void assign_operator_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct eb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void local_var_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb)' Function {{.+}} 'consume_eb' 'void (struct eb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void call_arg_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_eb_from_eb 'struct eb (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +struct eb return_eb_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void construct_not_used_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr_from_eb 'void (struct eb *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(0) && 0 <= new_end' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_nullptr_from_eb(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_eb 'void (struct nested_eb *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_eb 'void (struct nested_eb *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_eb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_eb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_eb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_eb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_eb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_eb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_eb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_eb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct eb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct eb[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct eb' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct eb' '0 <= __builtin_get_pointer_upper_bound(0) && 0 <= 0' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct eb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct eb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct eb (*__single)[])' Function {{.+}} 'consume_eb_arr' 'void (struct eb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct eb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct eb[2]' lvalue Var {{.+}} 'arr' 'struct eb[2]' +void array_of_struct_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + // Note the type of `.start` and `.end`'s assignments here looks odd in the + // AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single /* __started_by((struct eb[2]){{.start = new_start, .end = new_end}, {.start = 0, .end = 0}}[1UL].start) */ ':'char *__single' + // + // We could consider constant folding the expression + // to `__ended_by(0)` but this doesn't seem to be necessary. + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_eb 'void (struct eb_with_other_data *__single, char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void assign_via_ptr_other_data_side_effect_from_eb(struct eb_with_other_data* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_eb 'void (struct eb_with_other_data *__single, char *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct eb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct eb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct eb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct eb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct eb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(0) && 0 <= new_end' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single' +void assign_via_ptr_other_data_side_effect_zero_ptr_from_eb(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct eb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void call_arg_transparent_union_from_eb(char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_eb 'void (char *__single __ended_by(new_end), char *__single /* __started_by(new_start) */ )' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_start 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_end 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'eb' 'struct eb_with_other_data' +// CHECK-NEXT: `-BoundsCheckExpr {{.+}} 'struct eb_with_other_data' 'new_end <= __builtin_get_pointer_upper_bound(new_start) && new_start <= new_end && __builtin_get_pointer_lower_bound(new_start) <= new_start' +// CHECK-NEXT: |-InitListExpr {{.+}} 'struct eb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: |-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__single /* __started_by(new_start) */ ':'char *__single' lvalue ParmVar {{.+}} 'new_end' 'char *__single /* __started_by(new_start) */ ':'char *__single' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' +// CHECK-NEXT: `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(new_end)':'char *__single' lvalue ParmVar {{.+}} 'new_start' 'char *__single __ended_by(new_end)':'char *__single' +void call_arg_transparent_union_untransparently_from_eb( + char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-pointer.c b/clang/test/BoundsSafety/AST/compound-literal-pointer.c new file mode 100644 index 0000000000000..64cba0e0f2775 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-pointer.c @@ -0,0 +1,42 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include +#include "compound-literal-pointer.h" + +// CHECK: |-FunctionDecl {{.+}} return_ptr_nonptr +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *__bidi_indexable' lvalue +int *return_ptr_nonptr(void) { + return (int *){ 0 }; +} + +// CHECK: |-FunctionDecl {{.+}} return_ptr_single +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *__single' lvalue +int *return_ptr_single(int *__single p) { + return (int *){ p }; +} + +// CHECK: |-FunctionDecl {{.+}} return_ptr_indexable +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *__indexable' lvalue +int *return_ptr_indexable(int *__indexable p) { + return (int *){ p }; +} + +// CHECK: |-FunctionDecl {{.+}} return_ptr_bidi_indexable +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *__bidi_indexable' lvalue +int *return_ptr_bidi_indexable(int *__bidi_indexable p) { + return (int *){ p }; +} + +// CHECK: |-FunctionDecl {{.+}} return_ptr_counted_by +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *__bidi_indexable' lvalue +int *return_ptr_counted_by(int *__counted_by(n) p, int n) { + return (int *){ p }; +} + +// CHECK: `-FunctionDecl {{.+}} return_ptr_unsafe_indexable +// CHECK: `-CompoundLiteralExpr {{.+}} 'int *__unsafe_indexable' lvalue +int *__unsafe_indexable return_ptr_unsafe_indexable(int *__unsafe_indexable p) { + return (int *){ p }; +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-pointer.h b/clang/test/BoundsSafety/AST/compound-literal-pointer.h new file mode 100644 index 0000000000000..d7de4fc9384c7 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-pointer.h @@ -0,0 +1,27 @@ +#pragma clang system_header + +// CHECK: |-FunctionDecl {{.+}} compound_from_argument +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *' lvalue +int *compound_from_argument(int *p) { + return (int *) { p }; +} + +// CHECK: |-FunctionDecl {{.+}} compound_from_addrof +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *' lvalue +int *compound_from_addrof(void) { + int x; + return (int *) { &x }; +} + +// CHECK: |-FunctionDecl {{.+}} compound_from_null +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *' lvalue +int *compound_from_null(void) { + int x; + return (int *) { 0 }; +} + +// CHECK: |-FunctionDecl {{.+}} compound_from_function_call +// CHECK: | `-CompoundLiteralExpr {{.+}} 'int *' lvalue +int *compound_from_function_call(void) { + return (int *) { compound_from_null() }; +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-sized_by-disabled-checks.c b/clang/test/BoundsSafety/AST/compound-literal-sized_by-disabled-checks.c new file mode 100644 index 0000000000000..de13ac83b2b66 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-sized_by-disabled-checks.c @@ -0,0 +1,1175 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct sb { + int count; + char* __sized_by(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sb 'void (struct sb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sb' +void consume_sb(struct sb); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sb_arr 'void (struct sb (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sb (*__single)[]' +void consume_sb_arr(struct sb (*arr)[]); + +struct nested_sb { + struct sb nested; + int other; +}; + +struct nested_and_outer_sb { + struct sb nested; + int other; + int count; + char* __sized_by(count) buf; +}; + + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_sb_with_other_data_arr 'void (struct sb_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sb_with_other_data (*__single)[]' +void consume_sb_with_other_data_arr(struct sb_with_other_data (*arr)[]); + +union TransparentUnion { + struct sb_with_other_data sb; + struct no_attr_with_other_data no_sb; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct sb *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct sb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'new' 'struct sb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sb 'struct sb (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct sb return_sb(int new_count, char* __bidi_indexable new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct sb *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct sb* ptr, int new_count) { + *ptr = (struct sb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_sb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_sb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_sb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_and_outer_sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb (*__single)[])' Function {{.+}} 'consume_sb_arr' 'void (struct sb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb[2]' lvalue Var {{.+}} 'arr' 'struct sb[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __sized_by((struct sb[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__sized_by(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct sb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct sb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct sb *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +void assign_via_ptr_from_ptr(struct sb* ptr) { + *ptr = (struct sb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_sb 'void (struct sb *__single, int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_sb(struct sb* ptr, int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'new' 'struct sb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sb_from_sb 'struct sb (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct sb return_sb_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_sb 'void (struct nested_sb *__single, char *__single __sized_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_sb 'void (struct nested_sb *__single, char *__single __sized_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_sb 'void (char *__single __sized_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb (*__single)[])' Function {{.+}} 'consume_sb_arr' 'void (struct sb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb[2]' lvalue Var {{.+}} 'arr' 'struct sb[2]' +void array_of_struct_init_from_sb(char* __sized_by(new_count) new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_sb 'void (struct sb_with_other_data *__single, int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_sb 'void (struct sb_with_other_data *__single, int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_sb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_sb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: `-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-sized_by.c b/clang/test/BoundsSafety/AST/compound-literal-sized_by.c new file mode 100644 index 0000000000000..d047919d335d6 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-sized_by.c @@ -0,0 +1,4737 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct sb { + int count; + char* __sized_by(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sb 'void (struct sb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sb' +void consume_sb(struct sb); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sb_arr 'void (struct sb (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sb (*__single)[]' +void consume_sb_arr(struct sb (*arr)[]); + +struct nested_sb { + struct sb nested; + int other; +}; + +struct nested_and_outer_sb { + struct sb nested; + int other; + int count; + char* __sized_by(count) buf; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_sb_with_other_data_arr 'void (struct sb_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sb_with_other_data (*__single)[]' +void consume_sb_with_other_data_arr(struct sb_with_other_data (*arr)[]); + +union TransparentUnion { + struct sb_with_other_data sb; + struct no_attr_with_other_data no_sb; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct sb *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct sb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'new' 'struct sb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sb 'struct sb (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct sb return_sb(int new_count, char* __bidi_indexable new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct sb *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct sb* ptr, int new_count) { + *ptr = (struct sb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_sb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_sb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_sb *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct nested_and_outer_sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct nested_and_outer_sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' '0 == 0' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb (*__single)[])' Function {{.+}} 'consume_sb_arr' 'void (struct sb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb[2]' lvalue Var {{.+}} 'arr' 'struct sb[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __sized_by((struct sb[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__sized_by(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct sb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_other_data_side_effect(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct sb_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct sb *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr->buf <= __builtin_get_pointer_upper_bound(ptr->buf) && __builtin_get_pointer_lower_bound(ptr->buf) <= ptr->buf && ptr->count <= (char *)__builtin_get_pointer_upper_bound(ptr->buf) - (char *__bidi_indexable)ptr->buf && 0 <= ptr->count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +void assign_via_ptr_from_ptr(struct sb* ptr) { + *ptr = (struct sb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_sb 'void (struct sb *__single, int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_sb(struct sb* ptr, int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sb' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'new' 'struct sb' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sb' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sb_from_sb 'struct sb (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct sb return_sb_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_sb 'void (int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_sb 'void (struct nested_sb *__single, char *__single __sized_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_sb 'void (struct nested_sb *__single, char *__single __sized_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sb *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sb' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sb' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sb *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sb *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sb *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sb' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sb' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_sb 'void (char *__single __sized_by(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sb[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct sb' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' '0 == 0' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb (*__single)[])' Function {{.+}} 'consume_sb_arr' 'void (struct sb (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sb (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb[2]' lvalue Var {{.+}} 'arr' 'struct sb[2]' +void array_of_struct_init_from_sb(char* __sized_by(new_count) new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_sb 'void (struct sb_with_other_data *__single, int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_other_data_side_effect_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_sb 'void (struct sb_with_other_data *__single, int, char *__single __sized_by(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sb_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __sized_by(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sb_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sb_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sb_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sb_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_count == 0' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} > 'int' '==' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_sb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sb_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_sb 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sb' 'struct sb_with_other_data' +// CHECK-NEXT: `-BoundsCheckExpr {{.+}} 'struct sb_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: |-InitListExpr {{.+}} 'struct sb_with_other_data' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-sized_by_or_null-disabled-checks.c b/clang/test/BoundsSafety/AST/compound-literal-sized_by_or_null-disabled-checks.c new file mode 100644 index 0000000000000..906cc27535fdc --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-sized_by_or_null-disabled-checks.c @@ -0,0 +1,1182 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct sbon { + int count; + char* __sized_by_or_null(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sbon 'void (struct sbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sbon' +void consume_sbon(struct sbon); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sbon_arr 'void (struct sbon (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sbon (*__single)[]' +void consume_sbon_arr(struct sbon (*arr)[]); + +struct nested_sbon { + struct sbon nested; + int other; +}; + +struct nested_and_outer_sbon { + struct sbon nested; + int other; + int count; + char* __sized_by_or_null(count) buf; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_sbon_with_other_data_arr 'void (struct sbon_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sbon_with_other_data (*__single)[]' +void consume_sbon_with_other_data_arr(struct sbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct sbon_with_other_data sbon; + struct no_attr_with_other_data no_sbon; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct sbon *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct sbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'new' 'struct sbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sbon 'struct sbon (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct sbon return_sbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct sbon *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct sbon* ptr, int new_count) { + *ptr = (struct sbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_sbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_sbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_sbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_and_outer_sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon (*__single)[])' Function {{.+}} 'consume_sbon_arr' 'void (struct sbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon[2]' lvalue Var {{.+}} 'arr' 'struct sbon[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __sized_by_or_null((struct sbon[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__sized_by_or_null(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct sbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct sbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + // FIXME: The type that `0x0` gets implicitly casted to is weird because its + // using the CompoundLiteralExpr as a base. That base contains a side-effect + // which means its not a valid counted_by expression. So far this hasn't + // mattered. + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by_or_null source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct sbon *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +void assign_via_ptr_from_ptr(struct sbon* ptr) { + *ptr = (struct sbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_sbon 'void (struct sbon *__single, int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_sbon(struct sbon* ptr, int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'new' 'struct sbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sbon_from_sbon 'struct sbon (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct sbon return_sbon_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_sbon 'void (struct nested_sbon *__single, char *__single __sized_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_sbon 'void (struct nested_sbon *__single, char *__single __sized_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_sbon 'void (char *__single __sized_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon (*__single)[])' Function {{.+}} 'consume_sbon_arr' 'void (struct sbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon[2]' lvalue Var {{.+}} 'arr' 'struct sbon[2]' +void array_of_struct_init_from_sbon(char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_sbon 'void (struct sbon_with_other_data *__single, int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon 'void (struct sbon_with_other_data *__single, int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + // FIXME: The type that `0x0` gets implicitly casted to is weird because its + // using the CompoundLiteralExpr as a base. That base contains a side-effect + // which means its not a valid counted_by expression. So far this hasn't + // mattered. + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_sbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_sbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: `-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void call_arg_transparent_union_untransparently_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound-literal-sized_by_or_null.c b/clang/test/BoundsSafety/AST/compound-literal-sized_by_or_null.c new file mode 100644 index 0000000000000..6eaa210459d15 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound-literal-sized_by_or_null.c @@ -0,0 +1,5185 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s +// RUN: %clang_cc1 -x objective-c -fbounds-attributes-objc-experimental -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -verify %s | FileCheck %s + +#include + +// expected-no-diagnostics +struct sbon { + int count; + char* __sized_by_or_null(count) buf; +}; +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sbon 'void (struct sbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sbon' +void consume_sbon(struct sbon); +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sbon_arr 'void (struct sbon (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sbon (*__single)[]' +void consume_sbon_arr(struct sbon (*arr)[]); + +struct nested_sbon { + struct sbon nested; + int other; +}; + +struct nested_and_outer_sbon { + struct sbon nested; + int other; + int count; + char* __sized_by_or_null(count) buf; +}; + + +// CHECK-LABEL:|-FunctionDecl {{.+}} used get_int 'int (void)' +int get_int(void); + +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +// CHECK-LABEL:|-FunctionDecl {{.+}} consume_sbon_with_other_data_arr 'void (struct sbon_with_other_data (*__single)[])' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} arr 'struct sbon_with_other_data (*__single)[]' +void consume_sbon_with_other_data_arr(struct sbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct sbon_with_other_data sbon; + struct no_attr_with_other_data no_sbon; +} __attribute__((__transparent_union__)); + +// CHECK-LABEL:|-FunctionDecl {{.+}} used receive_transparent_union 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'union TransparentUnion' +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr 'void (struct sbon *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr(struct sbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'new' 'struct sbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sbon 'struct sbon (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +struct sbon return_sbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nullptr 'void (struct sbon *__single, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nullptr(struct sbon* ptr, int new_count) { + *ptr = (struct sbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested 'void (struct nested_sbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2 'void (struct nested_sbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v3 'void (struct nested_and_outer_sbon *__single, char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_and_outer_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_and_outer_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_and_outer_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_and_outer_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_and_outer_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_and_outer_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_and_outer_sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct nested_and_outer_sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct nested_and_outer_sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_nested_v3(struct nested_and_outer_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init 'void (char *__bidi_indexable, int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon (*__single)[])' Function {{.+}} 'consume_sbon_arr' 'void (struct sbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon[2]' lvalue Var {{.+}} 'arr' 'struct sbon[2]' +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + // Note the type of `.buf`'s assignment here looks odd in the AST (child of + // BoundsCheckExpr that should be materialized) because the compound literal + // is used as the base. E.g.: + // 'char *__single __sized_by_or_null((struct sbon[2]){{.count = new_count, .buf = new_ptr}, {.count = 0, .buf = 0}}[1UL].count)':'char *__single' + // + // We could consider constant folding the expression + // to `__sized_by_or_null(0)` but this doesn't seem to be necessary. + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect 'void (struct sbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void assign_via_ptr_other_data_side_effect(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr 'void (struct sbon_with_other_data *__single, int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_untransparently 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by_or_null source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_ptr 'void (struct sbon *__single)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr->buf <= __builtin_get_pointer_upper_bound(ptr->buf) && __builtin_get_pointer_lower_bound(ptr->buf) <= ptr->buf && !ptr->buf || ptr->count <= (char *)__builtin_get_pointer_upper_bound(ptr->buf) - (char *__bidi_indexable)ptr->buf && 0 <= ptr->count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-MemberExpr {{.+}} 'int' lvalue ->count {{.+}} +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-MemberExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' lvalue ->buf {{.+}} +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +void assign_via_ptr_from_ptr(struct sbon* ptr) { + *ptr = (struct sbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_from_sbon 'void (struct sbon *__single, int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_from_sbon(struct sbon* ptr, int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_operator_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used new 'struct sbon' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'new' 'struct sbon' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_operator_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL:|-FunctionDecl {{.+}} local_var_init_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} new 'struct sbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void local_var_init_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void call_arg_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} return_sbon_from_sbon 'struct sbon (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-ReturnStmt {{.+}} +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +struct sbon return_sbon_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} construct_not_used_from_sbon 'void (int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'void' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' part_of_explicit_cast +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void construct_not_used_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_from_sbon 'void (struct nested_sbon *__single, char *__single __sized_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_nested_v2_from_sbon 'void (struct nested_sbon *__single, char *__single __sized_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct nested_sbon *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct nested_sbon' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct nested_sbon' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct nested_sbon *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct nested_sbon *__single' lvalue ParmVar {{.+}} 'ptr' 'struct nested_sbon *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct nested_sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct nested_sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_nested_v2_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} array_of_struct_init_from_sbon 'void (char *__single __sized_by_or_null(new_count), int)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used arr 'struct sbon[2]' cinit +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon[2]' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'struct sbon' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon (*__single)[])' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon (*__single)[])' Function {{.+}} 'consume_sbon_arr' 'void (struct sbon (*__single)[])' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__single)[]' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon (*__bidi_indexable)[]' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'struct sbon (*__bidi_indexable)[2]' prefix '&' cannot overflow +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon[2]' lvalue Var {{.+}} 'arr' 'struct sbon[2]' +void array_of_struct_init_from_sbon(char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_from_sbon 'void (struct sbon_with_other_data *__single, int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(new_count)':'char *__single' lvalue ParmVar {{.+}} 'new_ptr' 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +void assign_via_ptr_other_data_side_effect_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon 'void (struct sbon_with_other_data *__single, int, char *__single __sized_by_or_null(new_count))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} new_ptr 'char *__single __sized_by_or_null(new_count)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct sbon_with_other_data' '=' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'struct sbon_with_other_data' lvalue prefix '*' cannot overflow +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'struct sbon_with_other_data *__single' lvalue ParmVar {{.+}} 'ptr' 'struct sbon_with_other_data *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | | | `-CallExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)(void)' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int (void)' Function {{.+}} 'get_int' 'int (void)' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} call_arg_transparent_union_from_sbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: | `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon_with_other_data' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL:`-FunctionDecl {{.+}} call_arg_transparent_union_untransparently_from_sbon 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_count 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used new_ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(union TransparentUnion)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (union TransparentUnion)' Function {{.+}} 'receive_transparent_union' 'void (union TransparentUnion)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'union TransparentUnion' +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'union TransparentUnion' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'union TransparentUnion' field Field {{.+}} 'sbon' 'struct sbon_with_other_data' +// CHECK-NEXT: `-BoundsCheckExpr {{.+}} 'struct sbon_with_other_data' 'new_ptr <= __builtin_get_pointer_upper_bound(new_ptr) && __builtin_get_pointer_lower_bound(new_ptr) <= new_ptr && !new_ptr || new_count <= (char *)__builtin_get_pointer_upper_bound(new_ptr) - (char *__bidi_indexable)new_ptr && 0 <= new_count' +// CHECK-NEXT: |-InitListExpr {{.+}} 'struct sbon_with_other_data' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK-NEXT: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'new_count' 'int' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'new_ptr' 'char *__bidi_indexable' +void call_arg_transparent_union_untransparently_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} diff --git a/clang/test/BoundsSafety/AST/compound_expr_on_dbp.c b/clang/test/BoundsSafety/AST/compound_expr_on_dbp.c new file mode 100644 index 0000000000000..6d26c40678cd4 --- /dev/null +++ b/clang/test/BoundsSafety/AST/compound_expr_on_dbp.c @@ -0,0 +1,328 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +void TestCountedPtr(int *__counted_by(*len) *ptr, unsigned *len) { + *ptr += 4; + *len = *len - 4; +} + +// CHECK-LABEL: TestCountedPtr +// CHECK: | |-ParmVarDecl [[var_ptr:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CompoundAssignOperator {{.+}} ComputeResultTy='int *__single +// CHECK: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | | | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-IntegerLiteral {{.+}} 4 +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*len)*__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_ptr]] +// CHECK: | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned int' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-BinaryOperator {{.+}} 'unsigned int' '-' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned int *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-IntegerLiteral {{.+}} 4 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'unsigned int' '=' +// CHECK: | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned int *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned int' + +void TestCount(int *__counted_by(len) ptr, unsigned len) { + int len2 = len; + int *__counted_by(len2) ptr2 = ptr; + + ptr2 = ptr2; + len2 /= 4; +} +// CHECK-LABEL: TestCount +// CHECK: | |-ParmVarDecl [[var_ptr_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_len2:0x[^ ]+]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_ptr2:0x[^ ]+]] +// CHECK: | | `-BoundsCheckExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'long' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'unsigned int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_ptr_1]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'unsigned int' +// CHECK: | | `-OpaqueValueExpr [[ove_9]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len2]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len2)':'int *__single' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_ptr2]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_11]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_ptr2]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len2]] +// CHECK: | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len2)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_15]] +// CHECK: | | | `-IntegerLiteral {{.+}} 4 +// CHECK: | | |-OpaqueValueExpr [[ove_14]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len2]] +// CHECK: | | `-OpaqueValueExpr [[ove_13]] +// CHECK: | | `-BinaryOperator {{.+}} 'int' '/' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_14]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-CompoundAssignOperator {{.+}} 'int' '/=' +// CHECK: | | |-OpaqueValueExpr [[ove_14]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_14]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' + +struct EndedBy { + int *__ended_by(end) start; + char *end; +}; +void TestRange(struct EndedBy *e) { + e->end -= 4; + e->start = e->start; +} +// CHECK-LABEL: TestRange +// CHECK: |-ParmVarDecl [[var_e:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CompoundAssignOperator {{.+}} */ ':' +// CHECK: | | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'struct EndedBy *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_16]] {{.*}} lvalue +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_21]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-MemberExpr {{.+}} ->start +// CHECK: | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct EndedBy *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'struct EndedBy *__single' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_18]] +// CHECK: | | `-IntegerLiteral {{.+}} 4 +// CHECK: | |-OpaqueValueExpr [[ove_17]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct EndedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_e]] +// CHECK: | |-OpaqueValueExpr [[ove_16]] +// CHECK: | | `-MemberExpr {{.+}} ->end +// CHECK: | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct EndedBy *__single' +// CHECK: | |-OpaqueValueExpr [[ove_19]] +// CHECK: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_16]] {{.*}} lvalue +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_20]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-MemberExpr {{.+}} ->start +// CHECK: | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct EndedBy *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_22]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | |-MemberExpr {{.+}} ->start +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct EndedBy *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_e]] +// CHECK: | | | |-OpaqueValueExpr [[ove_23]] {{.*}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | `-MemberExpr {{.+}} ->start +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct EndedBy *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_e]] +// CHECK: | | |-OpaqueValueExpr [[ove_24]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct EndedBy *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_e]] +// CHECK: | | `-OpaqueValueExpr [[ove_23]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK: | | `-MemberExpr {{.+}} ->end +// CHECK: | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'struct EndedBy *__single' +// CHECK: | |-OpaqueValueExpr [[ove_24]] {{.*}} 'struct EndedBy *__single' +// CHECK: | `-OpaqueValueExpr [[ove_23]] {{.*}} 'char *__single /* __started_by(start) */ ':'char *__single' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single __ended_by(end)':'int *__single' '=' +// CHECK: | |-MemberExpr {{.+}} ->start +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct EndedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_e]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_17]] {{.*}} 'struct EndedBy *__single' +// CHECK: |-OpaqueValueExpr [[ove_16]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_19]] {{.*}} 'char *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/const-cast.c b/clang/test/BoundsSafety/AST/const-cast.c new file mode 100644 index 0000000000000..73b4667db8eec --- /dev/null +++ b/clang/test/BoundsSafety/AST/const-cast.c @@ -0,0 +1,223 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -Wno-incompatible-pointer-types %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O2 %s -o /dev/null + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-incompatible-pointer-types %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O2 %s -o /dev/null + +#include + +void implicit(void) { + int *__indexable i; + const int *__indexable ci; + const int *__bidi_indexable cbi; + const void *__bidi_indexable cbv; + + // CHECK: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__indexable' lvalue Var {{.+}} 'ci' 'const int *__indexable' + i = ci; + + // CHECK: BinaryOperator {{.+}} 'const int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__indexable' lvalue Var {{.+}} 'ci' 'const int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + ci = i; + + // CHECK: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__bidi_indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__bidi_indexable' + i = cbi; + + // CHECK: BinaryOperator {{.+}} 'const int *__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + cbi = i; + + // CHECK: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__bidi_indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const void *__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__bidi_indexable' + i = cbv; + + // CHECK: BinaryOperator {{.+}} 'const void *__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const void *__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + cbv = i; +} + +void implicit_nested(void) { + int **__indexable i; + const int **__indexable ci; + const int **__bidi_indexable cbi; + const void **__bidi_indexable cbv; + + // CHECK: BinaryOperator {{.+}} 'int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__single*__indexable' lvalue Var {{.+}} 'ci' 'const int *__single*__indexable' + i = ci; + + // CHECK: BinaryOperator {{.+}} 'const int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__single*__indexable' lvalue Var {{.+}} 'ci' 'const int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + ci = i; + + // CHECK: BinaryOperator {{.+}} 'int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__bidi_indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__single*__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__single*__bidi_indexable' + i = cbi; + + // CHECK: BinaryOperator {{.+}} 'const int *__single*__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__single*__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + cbi = i; + + // CHECK: BinaryOperator {{.+}} 'int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__single*__bidi_indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const void *__single*__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__single*__bidi_indexable' + i = cbv; + + // CHECK: BinaryOperator {{.+}} 'const void *__single*__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const void *__single*__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + cbv = i; +} + +void explicit(void) { + int *__indexable i; + const int *__indexable ci; + const int *__bidi_indexable cbi; + const void *__bidi_indexable cbv; + + // CHECK: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__indexable' lvalue Var {{.+}} 'ci' 'const int *__indexable' + i = (int *__indexable)ci; + + // CHECK: BinaryOperator {{.+}} 'const int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__indexable' lvalue Var {{.+}} 'ci' 'const int *__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'const int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + ci = (const int *__indexable)i; + + // CHECK: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__bidi_indexable' + i = (int *__indexable)cbi; + + // CHECK: BinaryOperator {{.+}} 'const int *__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__bidi_indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'const int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + cbi = (const int *__bidi_indexable)i; + + // CHECK: BinaryOperator {{.+}} 'int *__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const void *__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__bidi_indexable' + i = (int *__indexable)cbv; + + // CHECK: BinaryOperator {{.+}} 'const void *__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const void *__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__bidi_indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'const void *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue Var {{.+}} 'i' 'int *__indexable' + cbv = (const void *__bidi_indexable)i; +} + +void explicit_nested(void) { + int **__indexable i; + const int **__indexable ci; + const int **__bidi_indexable cbi; + const void **__bidi_indexable cbv; + + // CHECK: BinaryOperator {{.+}} 'int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__single*__indexable' lvalue Var {{.+}} 'ci' 'const int *__single*__indexable' + i = (int **__indexable)ci; + + // CHECK: BinaryOperator {{.+}} 'const int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__single*__indexable' lvalue Var {{.+}} 'ci' 'const int *__single*__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'const int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + ci = (const int **__indexable)i; + + // CHECK: BinaryOperator {{.+}} 'int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int *__single*__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__single*__bidi_indexable' + i = (int **__indexable)cbi; + + // CHECK: BinaryOperator {{.+}} 'const int *__single*__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const int *__single*__bidi_indexable' lvalue Var {{.+}} 'cbi' 'const int *__single*__bidi_indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'const int *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const int *__single*__indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + cbi = (const int **__bidi_indexable)i; + + // CHECK: BinaryOperator {{.+}} 'int *__single*__indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'int *__single*__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__single*__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'const void *__single*__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__single*__bidi_indexable' + i = (int **__indexable)cbv; + + // CHECK: BinaryOperator {{.+}} 'const void *__single*__bidi_indexable' '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'const void *__single*__bidi_indexable' lvalue Var {{.+}} 'cbv' 'const void *__single*__bidi_indexable' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'const void *__single*__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const void *__single*__indexable' part_of_explicit_cast + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__single*__indexable' part_of_explicit_cast + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__single*__indexable' lvalue Var {{.+}} 'i' 'int *__single*__indexable' + cbv = (const void **__bidi_indexable)i; +} diff --git a/clang/test/BoundsSafety/AST/const-fold-dynamic-count-expr.c b/clang/test/BoundsSafety/AST/const-fold-dynamic-count-expr.c new file mode 100644 index 0000000000000..4ad9817af1046 --- /dev/null +++ b/clang/test/BoundsSafety/AST/const-fold-dynamic-count-expr.c @@ -0,0 +1,78 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -verify %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s 2>&1 | FileCheck %s + +#include + +struct Foo { + void *__sized_by(sizeof(int) * num_elements) vptr; + unsigned int num_elements; + + unsigned long long number_of_bits; + const unsigned char *__sized_by((number_of_bits + 7) & -7) buffer; + + char *__sized_by(sizeof(char) * 100) cptr; +}; + +// CHECK: RecordDecl {{.*}} struct Foo definition +// CHECK: |-FieldDecl [[VPTR_PTR:0x[0-9a-z]*]] {{.*}} vptr 'void *__single __sized_by(4UL * num_elements)':'void *__single' +// CHECK: |-FieldDecl {{.*}} referenced num_elements 'unsigned int' +// CHECK: | `-DependerDeclsAttr {{.*}} Implicit [[VPTR_PTR]] 0 +// CHECK: |-FieldDecl {{.*}} referenced number_of_bits 'unsigned long long' +// CHECK: | `-DependerDeclsAttr {{.*}} Implicit [[BUFFER_PTR:0x[0-9a-z]*]] 0 +// CHECK: |-FieldDecl [[BUFFER_PTR]] {{.*}} buffer 'const unsigned char *__single __sized_by((number_of_bits + 7) & -7)':'const unsigned char *__single' +// CHECK: `-FieldDecl {{.*}} cptr 'char *__single __sized_by(100UL)':'char *__single' + +static int g_arr[] = { 0, 1, 2 }; // expected-note{{'g_arr' declared here}} +static char g_char[100]; +const unsigned char g_cchar[] = "Hello world!"; // expected-note{{'g_cchar' declared here}} +// confirms that the compiler treats this as compile-time constant. +struct Foo g_foo = {g_arr, sizeof(g_arr)/sizeof(int), 3, g_cchar, g_char}; +// expected-error@+1{{initializing 'g_foo_ovf.buffer' of type 'const unsigned char *__single __sized_by((number_of_bits + 7) & -7)' (aka 'const unsigned char *__single') and size value of 17 with array 'g_cchar' (which has 13 bytes) always fails}} +struct Foo g_foo_ovf = {g_arr, sizeof(g_arr)/sizeof(int), 10, g_cchar, g_char}; + +struct Bar { + int num; + int *__counted_by(*(&num) + *&(*&num) + (-num)) iptr; +}; + +const float cf = 1.0f; +struct Baz { + float f; + int *__counted_by((int)f) ptr1; + // FIXME: "*&" pattern with '&' wrapped in C-style casts is not supported yet. + int *__counted_by(*(int*)(&f)) ptr2; // expected-error{{invalid argument expression to bounds attribute}} + int *__counted_by(*(int*)(&cf)) ptr3; // expected-error{{invalid argument expression to bounds attribute}} +}; + +struct Bar g_bar = {3, g_arr}; +// expected-error@+1{{initializing 'g_bar_ovf.iptr' of type 'int *__single __counted_by(num + num + (-num))' (aka 'int *__single') and count value of 4 with array 'g_arr' (which has 3 elements) always fails}} +struct Bar g_bar_ovf = {4, g_arr}; + +void test() { + int arr[100]; + int *var; + void *__sized_by(sizeof(var) * 10) vptr = arr; + + const int num = 10; + int *__counted_by(*(&num) + *&(*&num)) iptr = arr; + int *__counted_by((int)10.0f) iptr2 = arr; + int *__counted_by(10.0f) iptr3 = arr; // expected-error{{'__counted_by' attribute requires an integer type argument}} + + iptr = iptr2; // this is not an error since 'num' is constant. +} + +// CHECK: DeclStmt +// CHECK: `-VarDecl {{.*}} referenced var 'int *__bidi_indexable' +// CHECK: DeclStmt +// CHECK: `-VarDecl {{.*}} vptr 'void *__single __sized_by(240UL)':'void *__single' +// CHECK: DeclStmt +// CHECK: `-VarDecl {{.*}} used num 'const int' cinit +// CHECK: `-IntegerLiteral {{.*}} 'int' 10 +// CHECK: DeclStmt +// CHECK: `-VarDecl {{.*}} iptr 'int *__single __counted_by(20)':'int *__single' +// CHECK: DeclStmt +// CHECK: `-VarDecl {{.*}} iptr2 'int *__single __counted_by(10)':'int *__single' +// CHECK: DeclStmt +// CHECK: `-VarDecl {{.*}} iptr3 'int *__single __counted_by(0)':'int *__single' diff --git a/clang/test/BoundsSafety/AST/constant-eval-bin-cond-op-in-bounds-check.c b/clang/test/BoundsSafety/AST/constant-eval-bin-cond-op-in-bounds-check.c new file mode 100644 index 0000000000000..3082874a463af --- /dev/null +++ b/clang/test/BoundsSafety/AST/constant-eval-bin-cond-op-in-bounds-check.c @@ -0,0 +1,203 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +// Check the GNU extension to the conditional operator. + +// CHECK: FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: `-ParmVarDecl [[var_p:0x[^ ]+]] {{.+}} p 'void *__single __sized_by(16)':'void *__single' +void foo(void *__sized_by(16) p); + +// CHECK: FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_buf:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(void *__single __sized_by(16))' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(16)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'void *' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryConditionalOperator {{.+}} 'unsigned char *__bidi_indexable' +// CHECK: | | |-CStyleCastExpr {{.+}} 'void *' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'void *' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_buf]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | `-IntegerLiteral {{.+}} 16 +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] {{.*}} 'long' +void bar(void) { + unsigned char buf[16]; + foo((void *)0 ?: buf); +} + +// CHECK: FunctionDecl [[func_returns_a_pointer:0x[^ ]+]] {{.+}} returns_a_pointer +void *__bidi_indexable returns_a_pointer(void) { + return 0; +} + +// CHECK: FunctionDecl [[func_baz:0x[^ ]+]] {{.+}} baz +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(void *__single __sized_by(16))' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(16)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'long' +// CHECK: | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-BinaryConditionalOperator {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable(*__single)(void)' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_returns_a_pointer]] +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | `-IntegerLiteral {{.+}} 16 +// CHECK: |-OpaqueValueExpr [[ove_3]] {{.*}} 'void *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'long' +void baz(void) { + foo(returns_a_pointer() ?: 0); +} + +int bounds_safety_func(int * __counted_by(b) a, int b); +// CHECK: FunctionDecl [[func_bounds_safety_func:0x[^ ]+]] {{.+}} bounds_safety_func +// CHECK: |-ParmVarDecl [[var_a:0x[^ ]+]] +// CHECK: `-ParmVarDecl [[var_b:0x[^ ]+]] +// CHECK: `-DependerDeclsAttr + +int eval_count_arg(int * __bidi_indexable a, int b) { + return bounds_safety_func(a, b ?: 0); +} + +// CHECK: FunctionDecl [[func_eval_count_arg:0x[^ ]+]] {{.+}} eval_count_arg +// CHECK: |-ParmVarDecl [[var_a_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_b_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} 'a <= __builtin_get_pointer_upper_bound(a) && __builtin_get_pointer_lower_bound(a) <= a && b ?: 0 <= __builtin_get_pointer_upper_bound(a) - a && 0 <= b ?: 0' +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int (*__single)(int *__single __counted_by(b), int)' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_bounds_safety_func]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(b)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_a_1]] +// CHECK: | `-OpaqueValueExpr [[ove_7]] +// CHECK: | `-BinaryConditionalOperator {{.+}} 'int' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_b_1]] +// CHECK: | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | `-IntegerLiteral {{.+}} 0 +// CHECK: |-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' + diff --git a/clang/test/BoundsSafety/AST/constant-eval-cast-to-single.c b/clang/test/BoundsSafety/AST/constant-eval-cast-to-single.c new file mode 100644 index 0000000000000..b2f690a3f0ce1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/constant-eval-cast-to-single.c @@ -0,0 +1,87 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -triple arm64-apple-ios -target-feature +sve 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm64-apple-ios -target-feature +sve %s 2>&1 | FileCheck %s + +typedef struct opaque_s *opaque_t; + +// CHECK: VarDecl {{.+}} g_void 'void *__single' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1] +void *g_void = (void *)(unsigned char[1]){}; + +// CHECK: VarDecl {{.+}} g_opaque 'struct opaque_s *__single' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct opaque_s *__single' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'struct opaque_s *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' +opaque_t g_opaque = (opaque_t)(unsigned char[1]){}; + +// CHECK: VarDecl {{.+}} g_func 'void (*__single)(int)' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'void (*__single)(int)' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'void (*__bidi_indexable)(int)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' +void (*g_func)(int) = (void (*)(int))(unsigned char[1]){}; + +// CHECK: VarDecl {{.+}} g_sizeless '__SVInt8_t *__single' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} '__SVInt8_t *__single' +// CHECK-NEXT: `-CStyleCastExpr {{.+}} '__SVInt8_t *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast +// CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue +// CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' +__SVInt8_t *g_sizeless = (__SVInt8_t *)(unsigned char[1]){}; + +int f_void(void *); +int f_opaque(opaque_t); +int f_func(void (*)(int)); +int f_sizeless(__SVInt8_t *); + +void foo(void) { + int result; + + // CHECK: CallExpr {{.+}} 'int' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int (*__single)(void *__single)' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void *__single)' Function {{.+}} 'f_void' 'int (void *__single)' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'void *__single' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'void *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue + // CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' + result = f_void((void *)(unsigned char[1]){}); + + // CHECK: CallExpr {{.+}} 'int' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int (*__single)(struct opaque_s *__single)' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (struct opaque_s *__single)' Function {{.+}} 'f_opaque' 'int (struct opaque_s *__single)' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct opaque_s *__single' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'struct opaque_s *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue + // CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' + result = f_opaque((opaque_t)(unsigned char[1]){}); + + // CHECK: CallExpr {{.+}} 'int' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int (*__single)(void (*__single)(int))' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (void (*__single)(int))' Function {{.+}} 'f_func' 'int (void (*__single)(int))' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'void (*__single)(int)' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} 'void (*__bidi_indexable)(int)' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue + // CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' + result = f_func((void (*)(int))(unsigned char[1]){}); + + // CHECK: CallExpr {{.+}} 'int' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int (*__single)(__SVInt8_t *__single)' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int (__SVInt8_t *__single)' Function {{.+}} 'f_sizeless' 'int (__SVInt8_t *__single)' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} '__SVInt8_t *__single' + // CHECK-NEXT: `-CStyleCastExpr {{.+}} '__SVInt8_t *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'unsigned char *__bidi_indexable' part_of_explicit_cast + // CHECK-NEXT: `-CompoundLiteralExpr {{.+}} 'unsigned char[1]' lvalue + // CHECK-NEXT: `-InitListExpr {{.+}} 'unsigned char[1]' + result = f_sizeless((__SVInt8_t *)(unsigned char[1]){}); +} diff --git a/clang/test/BoundsSafety/AST/count-attributed-type-attribute-only-mode.c b/clang/test/BoundsSafety/AST/count-attributed-type-attribute-only-mode.c new file mode 100644 index 0000000000000..4b034182e7490 --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-attributed-type-attribute-only-mode.c @@ -0,0 +1,90 @@ + + +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK-NOT: BoundsCheckExpr +// CHECK-NOT: BoundsSafetyPointerPromotionExpr +// CHECK-NOT: MaterializeSequenceExpr + +// CHECK: FunctionDecl {{.+}} foo 'void (int * __counted_by(count), int)' +void foo(int *__counted_by(count) p, int count) { + // CHECK: UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int * __counted_by(count)':'int *' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int * __counted_by(count)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __counted_by(count)':'int *' + (void)*p; + + // CHECK: ArraySubscriptExpr {{.+}} 'int' lvalue + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __counted_by(count)':'int *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int * __counted_by(count)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __counted_by(count)':'int *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + (void)p[42]; + + // CHECK: BinaryOperator {{.+}} 'int * __counted_by(count)':'int *'{{.*}} '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int * __counted_by(count)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __counted_by(count)':'int *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int * __counted_by(count)':'int *' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __counted_by(count)':'int *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int * __counted_by(count)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __counted_by(count)':'int *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + // CHECK-NEXT: BinaryOperator {{.+}} 'int'{{.*}} '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int' '-' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + p = p + 42; + count = count - 42; +} + +// CHECK: RecordDecl {{.+}} struct bar definition +// CHECK: |-FieldDecl {{.+}} q 'int * __sized_by(size)':'int *' +// CHECK-NEXT: `-FieldDecl {{.+}} referenced size 'int' +struct bar { + int *__sized_by(size) q; + int size; +}; + +// CHECK: FunctionDecl {{.+}} baz 'void (struct bar *)' +void baz(struct bar *b) { + // CHECK: UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int * __sized_by(size)':'int *' + // CHECK-NEXT: `-MemberExpr {{.+}} 'int * __sized_by(size)':'int *' lvalue ->q + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + (void)*b->q; + + // CHECK: ArraySubscriptExpr {{.+}} 'int' lvalue + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __sized_by(size)':'int *' + // CHECK-NEXT: | `-MemberExpr {{.+}} 'int * __sized_by(size)':'int *' lvalue ->q + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + (void)b->q[42]; + + // CHECK: BinaryOperator {{.+}} 'int * __sized_by(size)':'int *'{{.*}} '=' + // CHECK-NEXT: |-MemberExpr {{.+}} 'int * __sized_by(size)':'int *' lvalue ->q + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int * __sized_by(size)':'int *' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __sized_by(size)':'int *' + // CHECK-NEXT: | `-MemberExpr {{.+}} 'int * __sized_by(size)':'int *' lvalue ->q + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + // CHECK-NEXT: BinaryOperator {{.+}} 'int'{{.*}} '=' + // CHECK-NEXT: |-MemberExpr {{.+}} 'int' lvalue ->size + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int' '-' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: | `-MemberExpr {{.+}} 'int' lvalue ->size + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + b->q = b->q + 42; + b->size = b->size - 42; +} diff --git a/clang/test/BoundsSafety/AST/count-attributed-type-func.c b/clang/test/BoundsSafety/AST/count-attributed-type-func.c new file mode 100644 index 0000000000000..132fd4b1b019b --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-attributed-type-func.c @@ -0,0 +1,69 @@ + + +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -x c++ -fbounds-attributes-cxx-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: FunctionDecl {{.+}} cb_in_in 'void (int *{{.*}}__counted_by(count), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'int *{{.*}}__counted_by(count)':'int *{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +void cb_in_in(int *__counted_by(count) ptr, int count); + +// CHECK: FunctionDecl {{.+}} cb_in_out 'void (int *{{.*}}__counted_by(*count), int *__single)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'int *{{.*}}__counted_by(*count)':'int *{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int *__single' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit IsDeref {{.+}} 0 +void cb_in_out(int *__counted_by(*count) ptr, int *__single count); + +// CHECK: FunctionDecl {{.+}} cb_out_in 'void (int *{{.*}}__counted_by(count)*__single, int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'int *{{.*}}__counted_by(count)*__single' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 1 +void cb_out_in(int *__counted_by(count) *__single ptr, int count); + +// CHECK: FunctionDecl {{.+}} cb_out_out 'void (int *{{.*}}__counted_by(*count)*__single, int *__single)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'int *{{.*}}__counted_by(*count)*__single' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int *__single' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit IsDeref {{.+}} 1 +void cb_out_out(int *__counted_by(*count) *__single ptr, int *__single count); + +// CHECK: FunctionDecl {{.+}} cbn 'void (int *{{.*}}__counted_by_or_null(count), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'int *{{.*}}__counted_by_or_null(count)':'int *{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +void cbn(int *__counted_by_or_null(count) ptr, int count); + +// CHECK: FunctionDecl {{.+}} sb 'void (void *{{.*}}__sized_by(size), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'void *{{.*}}__sized_by(size)':'void *{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used size 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +void sb(void *__sized_by(size) ptr, int size); + +// CHECK: FunctionDecl {{.+}} sbn 'void (void *{{.*}}__sized_by_or_null(size), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} ptr 'void *{{.*}}__sized_by_or_null(size)':'void *{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used size 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +void sbn(void *__sized_by_or_null(size) ptr, int size); + +// CHECK: FunctionDecl {{.+}} rcb 'int *{{.*}}__counted_by(count)(int)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int' +int *__counted_by(count) rcb(int count); + +// CHECK: FunctionDecl {{.+}} rcbn 'int *{{.*}}__counted_by_or_null(count)(int)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used count 'int' +int *__counted_by_or_null(count) rcbn(int count); + +// CHECK: FunctionDecl {{.+}} rsb 'void *{{.*}}__sized_by(size)(int)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used size 'int' +void *__sized_by(size) rsb(int size); + +// CHECK: FunctionDecl {{.+}} rsbn 'void *{{.*}}__sized_by_or_null(size)(int)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used size 'int' +void *__sized_by_or_null(size) rsbn(int size); diff --git a/clang/test/BoundsSafety/AST/count-attributed-type-struct-complete-type.c b/clang/test/BoundsSafety/AST/count-attributed-type-struct-complete-type.c new file mode 100644 index 0000000000000..05884401a0ddb --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-attributed-type-struct-complete-type.c @@ -0,0 +1,35 @@ + + +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -x c++ -fbounds-attributes-cxx-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include +#include + +// This test ensures that the parsing is done late enough to have a complete +// struct type and be able to use offsetof()/sizeof(). + +// CHECK: RecordDecl {{.+}} struct foo +// CHECK: FieldDecl {{.+}} dummy 'int' +// CHECK-NEXT: FieldDecl {{.+}} ptr 'int *{{.*}}__counted_by(0UL)':'int *{{.*}}' +// CHECK-NEXT: FieldDecl {{.+}} ptr2 'int *{{.*}}__counted_by(24UL)':'int *{{.*}}' +// CHECK-NEXT: FieldDecl {{.+}} dummy2 'int' +struct foo { + int dummy; + int *__counted_by(offsetof(struct foo, dummy)) ptr; + int *__counted_by(offsetof(struct foo, dummy2)) ptr2; + int dummy2; +}; + +// CHECK: RecordDecl {{.+}} struct bar +// CHECK: FieldDecl {{.+}} dummy 'int' +// CHECK-NEXT: FieldDecl {{.+}} ptr 'int *{{.*}}__counted_by(16UL)':'int *{{.*}}' +struct bar { + int dummy; + int *__counted_by(sizeof(struct bar)) ptr; +}; diff --git a/clang/test/BoundsSafety/AST/count-attributed-type-struct.c b/clang/test/BoundsSafety/AST/count-attributed-type-struct.c new file mode 100644 index 0000000000000..4c2889ca479a0 --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-attributed-type-struct.c @@ -0,0 +1,43 @@ + + +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -x c++ -fbounds-attributes-cxx-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: FieldDecl {{.+}} ptr 'int *{{.*}}__counted_by(count)':'int *{{.*}}' +// CHECK-NEXT: FieldDecl {{.+}} referenced count 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +struct cb { + int *__counted_by(count) ptr; + int count; +}; + +// CHECK: FieldDecl {{.+}} ptr 'int *{{.*}}__counted_by_or_null(count)':'int *{{.*}}' +// CHECK-NEXT: FieldDecl {{.+}} referenced count 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +struct cbn { + int *__counted_by_or_null(count) ptr; + int count; +}; + +// CHECK: FieldDecl {{.+}} ptr 'void *{{.*}}__sized_by(size)':'void *{{.*}}' +// CHECK-NEXT: FieldDecl {{.+}} referenced size 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +struct sb { + void *__sized_by(size) ptr; + int size; +}; + +// CHECK: FieldDecl {{.+}} ptr 'void *{{.*}}__sized_by_or_null(size)':'void *{{.*}}' +// CHECK-NEXT: FieldDecl {{.+}} referenced size 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +struct sbn { + void *__sized_by_or_null(size) ptr; + int size; +}; diff --git a/clang/test/BoundsSafety/AST/count-attrs.c b/clang/test/BoundsSafety/AST/count-attrs.c new file mode 100644 index 0000000000000..321453c08a7ec --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-attrs.c @@ -0,0 +1,118 @@ + + +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +int *__counted_by(len) frob(int len); + +int *__sized_by(len) byte_frob(int len); +void *alloc_bytes(int byte_count) __attribute__((alloc_size(1))); +// +// void *alloc_items(int byte_count, int size) __attribute__((alloc_size(1, 2))); + +typedef int (__array_decay_discards_count_in_parameters int_array_t)[10]; +void count_attr_in_bracket(int buf[__counted_by(len)], int len); +void count_ignored_from_array(int (__array_decay_discards_count_in_parameters buf)[10]); +void count_ignored_and_attr(int_array_t __counted_by(count) buf, int count); + +struct s { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + unsigned char *bp3 __sized_by(l); + unsigned char *bp4 __sized_by(l+1); + int l; +}; + +void test(void) { + int n = 0; + int *__counted_by(n) buf1; + int n2 = 0; + int *buf2 __counted_by(n2); + int n3 = sizeof(int) * n; + unsigned char *__sized_by(n3) byte_buf1; + int n4 = sizeof(int) * n2; + unsigned char *byte_buf2 __sized_by(n4); +} + +int *__counted_by(len) frob_body(int len) { return 0; } +int *__sized_by(len) byte_frob_body(int len) { return 0; } +// CHECK:TranslationUnitDecl {{.*}} +// CHECK:|-FunctionDecl {{.*}} frob 'int *__single __counted_by(len)(int)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} used len 'int' +// CHECK-NEXT:|-FunctionDecl {{.*}} byte_frob 'int *__single __sized_by(len)(int)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} used len 'int' +// CHECK-NEXT:|-FunctionDecl {{.*}} alloc_bytes 'void *__single(int)' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} byte_count 'int' +// CHECK-NEXT:| `-AllocSizeAttr {{.*}} 1 +// CHECK-NEXT:|-TypedefDecl {{.*}} referenced int_array_t '__array_decay_discards_count_in_parameters int[10]':'int[10]' +// CHECK-NEXT:| `-MacroQualifiedType {{.*}} '__array_decay_discards_count_in_parameters int[10]' sugar +// CHECK-NEXT:| `-AttributedType {{.*}} 'int[10] __attribute__((decay_discards_count_in_parameters))' sugar +// CHECK-NEXT:| `-ParenType {{.*}} 'int[10]' sugar +// CHECK-NEXT:| `-ConstantArrayType {{.*}} 'int[10]' 10 +// CHECK-NEXT:| `-BuiltinType {{.*}} 'int' +// CHECK-NEXT:|-FunctionDecl {{.*}} count_attr_in_bracket 'void (int *__single __counted_by(len), int)' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} buf 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} used len 'int' +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT:|-FunctionDecl {{.*}} count_ignored_from_array 'void (int *__single)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} buf 'int *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} count_ignored_and_attr 'void (int *__single __counted_by(count), int)' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} buf 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} used count 'int' +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT:|-RecordDecl {{.*}} struct s definition +// CHECK-NEXT:| |-FieldDecl {{.*}} bp 'int *__single __counted_by(l)':'int *__single' +// CHECK-NEXT:| |-FieldDecl {{.*}} bp2 'int *__single __counted_by(l + 1)':'int *__single' +// CHECK-NEXT:| |-FieldDecl {{.*}} bp3 'unsigned char *__single __sized_by(l)':'unsigned char *__single' +// CHECK-NEXT:| |-FieldDecl {{.*}} bp4 'unsigned char *__single __sized_by(l + 1)':'unsigned char *__single' +// CHECK-NEXT:| `-FieldDecl {{.*}} referenced l 'int' +// CHECK-NEXT:| `-DependerDeclsAttr {{.*}} Implicit {{.*}} {{.*}} {{.*}} {{.*}} 0 0 0 0 +// CHECK-NEXT:|-FunctionDecl {{.*}} test 'void (void)' +// CHECK-NEXT:| `-CompoundStmt {{.*}} +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} used n 'int' cinit +// CHECK-NEXT:| | |-IntegerLiteral {{.*}} 'int' 0 +// CHECK-NEXT:| | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} buf1 'int *__single __counted_by(n)':'int *__single' +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} used n2 'int' cinit +// CHECK-NEXT:| | |-IntegerLiteral {{.*}} 'int' 0 +// CHECK-NEXT:| | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} buf2 'int *__single __counted_by(n2)':'int *__single' +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} used n3 'int' cinit +// CHECK-NEXT:| | |-ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT:| | | `-BinaryOperator {{.*}} 'unsigned long' '*' +// CHECK-NEXT:| | | |-UnaryExprOrTypeTraitExpr {{.*}} 'unsigned long' sizeof 'int' +// CHECK-NEXT:| | | `-ImplicitCastExpr {{.*}} 'unsigned long' +// CHECK-NEXT:| | | `-ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT:| | | `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'n' 'int' +// CHECK-NEXT:| | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} byte_buf1 'unsigned char *__single __sized_by(n3)':'unsigned char *__single' +// CHECK-NEXT:| |-DeclStmt {{.*}} +// CHECK-NEXT:| | `-VarDecl {{.*}} used n4 'int' cinit +// CHECK-NEXT:| | |-ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT:| | | `-BinaryOperator {{.*}} 'unsigned long' '*' +// CHECK-NEXT:| | | |-UnaryExprOrTypeTraitExpr {{.*}} 'unsigned long' sizeof 'int' +// CHECK-NEXT:| | | `-ImplicitCastExpr {{.*}} 'unsigned long' +// CHECK-NEXT:| | | `-ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT:| | | `-DeclRefExpr {{.*}} 'int' lvalue Var {{.*}} 'n2' 'int' +// CHECK-NEXT:| | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT:| `-DeclStmt {{.*}} +// CHECK-NEXT:| `-VarDecl {{.*}} byte_buf2 'unsigned char *__single __sized_by(n4)':'unsigned char *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} frob_body 'int *__single __counted_by(len)(int)' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} used len 'int' +// CHECK-NEXT:| `-CompoundStmt {{.*}} +// CHECK-NEXT:| `-ReturnStmt {{.*}} +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT:| `-IntegerLiteral {{.*}} 'int' 0 +// CHECK-NEXT:`-FunctionDecl {{.*}} byte_frob_body 'int *__single __sized_by(len)(int)' +// CHECK-NEXT: |-ParmVarDecl {{.*}} used len 'int' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: `-ReturnStmt {{.*}} +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 0 diff --git a/clang/test/BoundsSafety/AST/count-init-list.c b/clang/test/BoundsSafety/AST/count-init-list.c new file mode 100644 index 0000000000000..a977fbdf4f66e --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-init-list.c @@ -0,0 +1,57 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct Foo { + int len; + int *__counted_by(len) ptr; +}; + +void Test(void) { + int *p; + struct Foo f = { 10, p }; +} + +// CHECK-LABEL: Test 'void (void)' +// CHECK: CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_p:0x[^ ]+]] +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_f:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr +// CHECK: |-InitListExpr +// CHECK: | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-OpaqueValueExpr [[ove_1]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_p]] diff --git a/clang/test/BoundsSafety/AST/count-return.c b/clang/test/BoundsSafety/AST/count-return.c new file mode 100644 index 0000000000000..d585388448b08 --- /dev/null +++ b/clang/test/BoundsSafety/AST/count-return.c @@ -0,0 +1,105 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +int *__sized_by(len) alloc(int len); +// CHECK: FunctionDecl [[func_alloc:0x[^ ]+]] {{.+}} alloc + +int *__counted_by(4) noproto(); // non-prototype function +// CHECK: FunctionDecl [[func_noproto:0x[^ ]+]] {{.+}} noproto 'int *__single __counted_by(4)()' + +int Test() { + int len = 16; +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_len_1:0x[^ ]+]] + + int *bufAuto = alloc(len); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_bufAuto:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | | `-CallExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)(*__single)(int)' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[func_alloc]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(len)':'int *__single' + + int *__bidi_indexable bufBound = alloc(sizeof(int) * 10); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_bufBound:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: {{^}} | | | |-UnaryExprOrTypeTraitExpr +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}} | | `-CallExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)(*__single)(int)' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[func_alloc]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(len)':'int *__single' + + int *buf4 = noproto(); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_buf4:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __counted_by(4)':'int *__single' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(4)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}} | | | `-CallExpr +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(4)(*__single)()' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[func_noproto]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}} | | `-IntegerLiteral {{.+}} 4 +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(4)':'int *__single' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + + return buf4[3] + bufBound[9]; +// CHECK: {{^}} `-ReturnStmt +// CHECK: {{^}} `-BinaryOperator {{.+}} 'int' '+' +// CHECK: {{^}} |-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-ArraySubscriptExpr +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_buf4]] +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 3 +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} `-ArraySubscriptExpr +// CHECK: {{^}} |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_bufBound]] +// CHECK: {{^}} `-IntegerLiteral {{.+}} 9 +} diff --git a/clang/test/BoundsSafety/AST/counted-by-nested-assignments.c b/clang/test/BoundsSafety/AST/counted-by-nested-assignments.c new file mode 100644 index 0000000000000..4ffe310d0ebbe --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted-by-nested-assignments.c @@ -0,0 +1,210 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 5 + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump -fbounds-safety-bringup-missing-checks=indirect_count_update -DWITH %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -ast-dump -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s 2>&1 | FileCheck --check-prefix WITHOUT %s +#include + +// CHECK: |-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: | |-ParmVarDecl [[var_x:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'x + 1UL <= __builtin_get_pointer_upper_bound(x + 1UL) && __builtin_get_pointer_lower_bound(x + 1UL) <= x + 1UL && count - 1U <= __builtin_get_pointer_upper_bound(x + 1UL) - x + 1UL' +// CHECK: | | | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | | | | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_x]] +// CHECK: | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned int' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned int' +// CHECK: | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned int' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned int' + +// WITHOUT: |-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// WITHOUT-NEXT: | |-ParmVarDecl [[var_x:0x[^ ]+]] +// WITHOUT-NEXT: | |-ParmVarDecl [[var_count:0x[^ ]+]] +// WITHOUT-NEXT: | | `-DependerDeclsAttr +// WITHOUT-NEXT: | `-CompoundStmt +// WITHOUT-NEXT: | `-BinaryOperator {{.+}} 'int' '=' +// WITHOUT-NEXT: | |-UnaryOperator {{.+}} cannot overflow +// WITHOUT-NEXT: | | `-UnaryOperator {{.+}} postfix '++' +// WITHOUT-NEXT: | | `-DeclRefExpr {{.+}} [[var_x]] +// WITHOUT-NEXT: | `-IntegerLiteral {{.+}} 0 +void foo(int *__counted_by(count) x, unsigned count) { + *x++ = 0; + #ifdef WITH + // NOTE: It's necessary to exclude the count update with + // indirect_count_update disabled otherwise we'll get diagnostics about + // the a missing assignment to `x`. + count--; + #endif +} + +// CHECK: {{^}}`-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_x_1:0x[^ ]+]] +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_count_1:0x[^ ]+]] +// CHECK-NEXT: {{^}} | `-DependerDeclsAttr +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BoundsCheckExpr {{.+}} 'x + 1 <= __builtin_get_pointer_upper_bound(x + 1) && __builtin_get_pointer_lower_bound(x + 1) <= x + 1 && count - 1 <= __builtin_get_pointer_upper_bound(x + 1) - x + 1 && 0 <= count - 1' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}} | | | |-DeclRefExpr {{.+}} [[var_count_1]] +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}} | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK-NEXT: {{^}} | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count_1]] +// CHECK-NEXT: {{^}} | | `-IntegerLiteral {{.+}} 1 +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_7]] +// CHECK-NEXT: {{^}} | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_x_1]] +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_9]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count_1]] +// CHECK-NEXT: {{^}} | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 1 +// CHECK-NEXT: {{^}} `-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}} |-UnaryOperator {{.+}} cannot overflow +// CHECK-NEXT: {{^}} | `-ParenExpr +// CHECK-NEXT: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// CHECK-NEXT: {{^}} | | |-DeclRefExpr {{.+}} [[var_x_1]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} `-IntegerLiteral {{.+}} 0 + +// WITHOUT-NEXT: {{^}}`-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// WITHOUT-NEXT: {{^}} |-ParmVarDecl [[var_x_1:0x[^ ]+]] +// WITHOUT-NEXT: {{^}} |-ParmVarDecl [[var_count_1:0x[^ ]+]] +// WITHOUT-NEXT: {{^}} | `-DependerDeclsAttr +// WITHOUT-NEXT: {{^}} `-CompoundStmt +// WITHOUT-NEXT: {{^}} `-BinaryOperator {{.+}} 'int' '=' +// WITHOUT-NEXT: {{^}} |-UnaryOperator {{.+}} cannot overflow +// WITHOUT-NEXT: {{^}} | `-ParenExpr +// WITHOUT-NEXT: {{^}} | `-BinaryOperator {{.+}} 'int *__single __counted_by(count)':'int *__single' '=' +// WITHOUT-NEXT: {{^}} | |-DeclRefExpr {{.+}} [[var_x_1]] +// WITHOUT-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// WITHOUT-NEXT: {{^}} | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// WITHOUT-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// WITHOUT-NEXT: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// WITHOUT-NEXT: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// WITHOUT-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// WITHOUT: {{^}} | | | | |-BinaryOperator {{.+}} 'int *' '+' +// WITHOUT-NEXT: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// WITHOUT-NEXT: {{^}} | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// WITHOUT: {{^}} | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// WITHOUT: {{^}} | | | |-OpaqueValueExpr [[ove]] +// WITHOUT-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// WITHOUT-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_x_1]] +// WITHOUT-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_1]] +// WITHOUT-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// WITHOUT-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count_1]] +// WITHOUT-NEXT: {{^}} | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// WITHOUT: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// WITHOUT: {{^}} | `-IntegerLiteral {{.+}} 1 +// WITHOUT-NEXT: {{^}} `-IntegerLiteral {{.+}} 0 + +void bar(int *__counted_by(count) x, int count) { + #ifdef WITH + // NOTE: It's necessary to exclude the count update with + // indirect_count_update disabled otherwise we'll get diagnostics about + // the a missing assignment to `x`. + count = count - 1; + #endif + *(x = x+1) = 0; +} diff --git a/clang/test/BoundsSafety/AST/counted-by-offset-of-inside-struct.c b/clang/test/BoundsSafety/AST/counted-by-offset-of-inside-struct.c new file mode 100644 index 0000000000000..e48f7d65d3c43 --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted-by-offset-of-inside-struct.c @@ -0,0 +1,26 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include +#include + +struct Foo { + int len; + int fam[__counted_by((len - offsetof(struct Foo, fam)) / sizeof(int))]; +}; + +// CHECK: -RecordDecl {{.*}} struct Foo definition +// CHECK-NEXT: |-FieldDecl {{.*}} referenced len 'int' +// CHECK-NEXT: | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT: `-FieldDecl {{.*}} fam 'int[__counted_by((len - 4UL) / 4UL)]':'int[]' + +struct Bar { + int len; + int fam[__counted_by((len - sizeof(struct Foo)) / sizeof(int))]; +}; + +// CHECK: -RecordDecl {{.*}} struct Bar definition +// CHECK-NEXT: |-FieldDecl {{.*}} referenced len 'int' +// CHECK-NEXT: | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK-NEXT: `-FieldDecl {{.*}} fam 'int[__counted_by((len - 4UL) / 4UL)]':'int[]' diff --git a/clang/test/BoundsSafety/AST/counted_by_const_call.c b/clang/test/BoundsSafety/AST/counted_by_const_call.c new file mode 100644 index 0000000000000..9f8bdf668b48e --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_const_call.c @@ -0,0 +1,152 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +int data_const_count __unsafe_late_const; + +// CHECK: |-FunctionDecl [[func_fun_const_count:0x[^ ]+]] {{.+}} fun_const_count +__attribute__((const)) int fun_const_count() { + return data_const_count; +} + +struct struct_const_count_call { + int *__counted_by(fun_const_count()) ptr; +}; + +// CHECK-LABEL: fun_pointer_access +void fun_pointer_access(struct struct_const_count_call *sp) { + *(sp->ptr) = 0; +} +// CHECK: | |-ParmVarDecl [[var_sp:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(fun_const_count())':'int *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(fun_const_count())':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(fun_const_count())':'int *__single' +// CHECK: | | | | `-ParenExpr +// CHECK: | | | | `-MemberExpr {{.+}} ->ptr +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct struct_const_count_call *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_sp]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)()' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_fun_const_count]] +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(fun_const_count())':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | `-IntegerLiteral {{.+}} 0 + +// CHECK-LABEL: fun_pointer_assignment +void fun_pointer_assignment(struct struct_const_count_call *sp, void *__bidi_indexable buf) { + sp->ptr = buf; +} +// CHECK: | |-ParmVarDecl [[var_sp_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __counted_by(fun_const_count())':'int *__single' '=' +// CHECK: | | | | |-MemberExpr {{.+}} ->ptr +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct struct_const_count_call *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_sp_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(fun_const_count())':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)()' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_fun_const_count]] +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf]] + +// FIXME: rdar://85158790 +// CHECK-LABEL: fun_struct_noinit +void fun_struct_noinit() { + struct struct_const_count_call s; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_s:0x[^ ]+]] + +// CHECK-LABEL: fun_struct_init +void fun_struct_init(int *__bidi_indexable buf) { + struct struct_const_count_call s = { buf }; +} +// CHECK: |-ParmVarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s_1:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr +// CHECK: |-InitListExpr +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(fun_const_count())':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove_4]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf_1]] +// CHECK: `-OpaqueValueExpr [[ove_5]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-CallExpr +// CHECK: `-ImplicitCastExpr {{.+}} 'int (*__single)()' +// CHECK: `-DeclRefExpr {{.+}} [[func_fun_const_count]] + diff --git a/clang/test/BoundsSafety/AST/counted_by_const_count_init.c b/clang/test/BoundsSafety/AST/counted_by_const_count_init.c new file mode 100644 index 0000000000000..cbd8bd1c0c540 --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_const_count_init.c @@ -0,0 +1,105 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +struct struct_const_count { + unsigned const const_count; + int *__counted_by(const_count) ptr; +}; + +enum enum_count { en_count = 10 }; +// CHECK-LABEL: fun_enum_count +void fun_enum_count(int *__sized_by(en_count) ptr, int *__bidi_indexable buf) { + ptr = buf; +} +// CHECK: | |-ParmVarDecl {{.*}} 'int *__single __sized_by(10)':'int *__single' +// CHECK: | |-ParmVarDecl {{.*}} 'int *__bidi_indexable' + +// CHECK-LABEL: fun_pointer_access +void fun_pointer_access(struct struct_const_count *sp) { + *(sp->ptr) = 0; +} +// CHECK: | |-ParmVarDecl [[var_sp:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(const_count)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct struct_const_count *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(const_count)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'const unsigned int' +// CHECK: | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct struct_const_count *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_sp]] +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'const unsigned int' +// CHECK: | | | | `-MemberExpr {{.+}} ->const_count +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct struct_const_count *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(const_count)':'int *__single' +// CHECK: | | | `-MemberExpr {{.+}} ->ptr +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct struct_const_count *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct struct_const_count *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'const unsigned int' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(const_count)':'int *__single' +// CHECK: | `-IntegerLiteral {{.+}} 0 + +// CHECK-LABEL: fun_pointer_assignment +void fun_pointer_assignment(struct struct_const_count *sp, void *__bidi_indexable buf) { + sp->ptr = buf; +} +// CHECK: | |-ParmVarDecl [[var_sp_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __counted_by(const_count)':'int *__single' '=' +// CHECK: | | | | |-MemberExpr {{.+}} ->ptr +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct struct_const_count *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_sp_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(const_count)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | `-MemberExpr {{.+}} ->const_count +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct struct_const_count *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_sp_1]] +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf]] + +// FIXME: rdar://85158790 +// CHECK-LABEL: fun_struct_noinit +void fun_struct_noinit() { + struct struct_const_count s; +} +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s:0x[^ ]+]] diff --git a/clang/test/BoundsSafety/AST/counted_by_data_const_count_init.c b/clang/test/BoundsSafety/AST/counted_by_data_const_count_init.c new file mode 100644 index 0000000000000..b58ff09602848 --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_data_const_count_init.c @@ -0,0 +1,198 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +// CHECK: {{^}}|-VarDecl [[var_data_const_count:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-UnsafeLateConstAttr +// CHECK-NEXT: {{^}}| `-DependerDeclsAttr +unsigned data_const_count __unsafe_late_const; + +// CHECK: {{^}}|-RecordDecl +// CHECK-NEXT: {{^}}| `-FieldDecl +struct struct_data_const_count { + int *__counted_by(data_const_count) ptr; +}; + +// CHECK: {{^}}|-VarDecl [[var_data_const_count_flex:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-UnsafeLateConstAttr +// CHECK-NEXT: {{^}}| `-DependerDeclsAttr +unsigned data_const_count_flex __unsafe_late_const; + +// CHECK: {{^}}|-RecordDecl +// CHECK-NEXT: {{^}}| |-FieldDecl +// CHECK-NEXT: {{^}}| `-FieldDecl +struct struct_data_const_count_flex { + int count; + int fam[__counted_by(data_const_count_flex)]; +}; + +// CHECK-LABEL: fun_pointer_assignment +void fun_pointer_assignment(struct struct_data_const_count *sp, void *__bidi_indexable buf) { + sp->ptr = buf; +} +// CHECK: | |-ParmVarDecl [[var_sp:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __counted_by(data_const_count)':'int *__single' '=' +// CHECK: | | | | |-MemberExpr {{.+}} ->ptr +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_sp]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(data_const_count)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_data_const_count]] +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf]] + +// CHECK-LABEL: fun_pointer_assignment2 +void fun_pointer_assignment2(struct struct_data_const_count *sp, void *__bidi_indexable buf) { + sp->ptr = buf; + data_const_count = 10; // XXX: The assignment precheck at `buf` won't take into account this new count assignment. +} +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_sp_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_buf_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsCheckExpr {{.+}} 'buf <= __builtin_get_pointer_upper_bound(buf) && __builtin_get_pointer_lower_bound(buf) <= buf && data_const_count <= __builtin_get_pointer_upper_bound(buf) - buf' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int *__single __counted_by(data_const_count)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | | | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | | | | `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count *__single' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[var_sp_1]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(data_const_count)':'int *__single' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_data_const_count]] +// CHECK-NEXT: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_buf_1]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_buf_1]] +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'unsigned int' '=' +// CHECK-NEXT: {{^}}| | | |-DeclRefExpr {{.+}} [[var_data_const_count]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 10 +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 10 + + +// CHECK-LABEL: fun_flex_pointer_assignment +void fun_flex_pointer_assignment(struct struct_data_const_count_flex *sp, void *__bidi_indexable buf) { + sp = buf; +} +// CHECK: |-ParmVarDecl [[var_sp_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_buf_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'struct struct_data_const_count_flex *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_sp_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count_flex *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_data_const_count_flex]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] +// CHECK: `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_buf_2]] + +// CHECK-LABEL: fun_flex_pointer_assignment2 +void fun_flex_pointer_assignment2(struct struct_data_const_count_flex *sp, void *__bidi_indexable buf) { + sp = buf; + data_const_count_flex = 100; // XXX: The assignment precheck at `buf` won't take into account this new count assignment. +} +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_sp_3:0x[^ ]+]] +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_buf_3:0x[^ ]+]] +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'struct struct_data_const_count_flex *__single' '=' +// CHECK-NEXT: {{^}} | | | |-DeclRefExpr {{.+}} [[var_sp_3]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count_flex *__single' +// CHECK-NEXT: {{^}} | | | `-PredefinedBoundsCheckExpr {{.+}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | `-MemberExpr {{.+}} ->fam +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_data_const_count_flex]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_buf_3]] +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'struct struct_data_const_count_flex *__bidi_indexable' +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK-NEXT: {{^}} | `-DeclRefExpr {{.+}} [[var_buf_3]] +// CHECK-NEXT: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BinaryOperator {{.+}} 'unsigned int' '=' +// CHECK-NEXT: {{^}} | | |-DeclRefExpr {{.+}} [[var_data_const_count_flex]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_5]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}} | `-IntegerLiteral {{.+}} 100 +// CHECK-NEXT: {{^}} `-OpaqueValueExpr [[ove_5]] +// CHECK-NEXT: {{^}} `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK-NEXT: {{^}} `-IntegerLiteral {{.+}} 100 diff --git a/clang/test/BoundsSafety/AST/counted_by_global_assign.c b/clang/test/BoundsSafety/AST/counted_by_global_assign.c new file mode 100644 index 0000000000000..274c81cc95fbb --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_global_assign.c @@ -0,0 +1,63 @@ + + +// FileCheck doesn't seem to be able to handle too many slashes in a line +// XFAIL: * + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +int glen; +int *__counted_by(glen) gptr; +// CHECK: |-VarDecl [[var_len:0x[^ ]+]] +// CHECK: {{^ *}}DependerDeclsAttr +// CHECK: |-VarDecl [[var_ptr:0x[^ ]+]] + +// CHECK-LABEL: test +void test(int *__bidi_indexable arg) { + glen = 0; + gptr = arg; +} +// CHECK: |-ParmVarDecl [[var_arg:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | |-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_arg]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK: | |-DeclRefExpr {{.+}} [[var_ptr]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/counted_by_inc_constant_count_valid_ast.c b/clang/test/BoundsSafety/AST/counted_by_inc_constant_count_valid_ast.c new file mode 100644 index 0000000000000..84853a7fb159d --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_inc_constant_count_valid_ast.c @@ -0,0 +1,616 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// First make sure the previously buggy diagnostic is present +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -verify -Wno-error=bounds-safety-externally-counted-ptr-arith-constant-count %s +// RUN: %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -verify -Wno-error=bounds-safety-externally-counted-ptr-arith-constant-count %s + + +// Now make sure the AST is as expected (no errors) and identical with and without the new bounds checks +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -Wno-bounds-safety-externally-counted-ptr-arith-constant-count -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -Wno-bounds-safety-externally-counted-ptr-arith-constant-count -ast-dump %s 2>&1 | FileCheck %s + + +// Verify we generate the same AST even when the warning is treated as an error, with and without the new bounds checks +// Note: `-verify=default` is needed here, otherwise clang will return a non-zero exit because of the error. +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -verify=default -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -verify=default -ast-dump %s 2>&1 | FileCheck %s + +#include +// CHECK-LABEL:|-FunctionDecl {{.+}} <{{.+}}:{{.+}}, line:{{.+}}> line:{{.+}} test_cb_unary 'void (int *__single __counted_by(3))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used p 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' 'p + 1UL <= __builtin_get_pointer_upper_bound(p + 1UL) && __builtin_get_pointer_lower_bound(p + 1UL) <= p + 1UL && 3 <= __builtin_get_pointer_upper_bound(p + 1UL) - p + 1UL && 0 <= 3' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int *__single __counted_by(3)':'int *__single' prefix '++' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} <> 'long' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'unsigned long' 1 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'unsigned long' 1 +void test_cb_unary(int* __counted_by(3) p) { // expected-note{{__counted_by attribute is here}} + // default-note@-1{{__counted_by attribute is here}} + // default-error@+1{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + ++p; // expected-warning{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} test_cb_binary 'void (int *__single __counted_by(3))' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used p 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-MaterializeSequenceExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: |-MaterializeSequenceExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' 'p + 1 <= __builtin_get_pointer_upper_bound(p + 1) && __builtin_get_pointer_lower_bound(p + 1) <= p + 1 && 3 <= __builtin_get_pointer_upper_bound(p + 1) - p + 1 && 0 <= 3' +// CHECK-NEXT: | | |-CompoundAssignOperator {{.+}} 'int *__single __counted_by(3)':'int *__single' '+=' ComputeLHSTy='int *__single __counted_by(3)':'int *__single' ComputeResultTy='int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} <> 'long' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'long' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 1 +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(3)':'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single __counted_by(3)':'int *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 1 +void test_cb_binary(int* __counted_by(3) p) { // expected-note{{__counted_by attribute is here}} + // default-note@-1{{__counted_by attribute is here}} + // default-error@+1{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 3 always traps}} + p += 1; // expected-warning{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 3 always traps}} +} + diff --git a/clang/test/BoundsSafety/AST/counted_by_incdec.c b/clang/test/BoundsSafety/AST/counted_by_incdec.c new file mode 100644 index 0000000000000..c356e42e7aa8c --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_incdec.c @@ -0,0 +1,186 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +struct T { + int *__counted_by(len) ptr; + int len; +}; + +void Test(struct T *t) { + t->ptr++; + t->len--; +} + +// CHECK-LABEL: Test 'void (struct T *__single)' +// CHECK: | |-ParmVarDecl [[var_t:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-MemberExpr {{.+}} ->ptr +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->len +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-MemberExpr {{.+}} ->len +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct T *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} lvalue +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_7]] {{.*}} 'struct T *__single' +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + +void TestReverse(struct T *t) { + t->len--; + t->ptr++; +} + +// CHECK-LABEL: TestReverse 'void (struct T *__single)' +// CHECK: |-ParmVarDecl [[var_t_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t_1]] +// CHECK: | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | `-MemberExpr {{.+}} ->len +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct T *__single' +// CHECK: | |-OpaqueValueExpr [[ove_15]] +// CHECK: | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} lvalue +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | |-OpaqueValueExpr [[ove_13]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t_1]] +// CHECK: | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | `-MemberExpr {{.+}} ->ptr +// CHECK: | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'struct T *__single' +// CHECK: | `-OpaqueValueExpr [[ove_10]] +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_14]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} ->len +// CHECK: | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'struct T *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | `-IntegerLiteral {{.+}} 1 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-UnaryOperator {{.+}} postfix '++' +// CHECK: | `-OpaqueValueExpr [[ove_12]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove_8]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_13]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove_12]] {{.*}} lvalue +// CHECK: `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/counted_by_or_null_call.c b/clang/test/BoundsSafety/AST/counted_by_or_null_call.c new file mode 100644 index 0000000000000..43f06174950a1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_or_null_call.c @@ -0,0 +1,611 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -Wno-bounds-safety-init-list %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-bounds-safety-init-list %s | FileCheck %s + +#include + +// CHECK: {{^}}|-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: {{^}}| |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +void foo(int *__counted_by_or_null(len) p, int len) {} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_1:0x[^ ]+]] {{.+}} caller_1 +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 <= 0 && 0 <= 0 && !0 || 2 <= 0 - 0 && 0 <= 2' +// CHECK-NEXT: {{^}}| | | |-CallExpr +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 2 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 2 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +void caller_1() { + foo(0, 2); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_2:0x[^ ]+]] {{.+}} caller_2 +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 <= 0 && 0 <= 0 && !0 || 0 <= 0 - 0 && 0 <= 0' +// CHECK-NEXT: {{^}}| | | |-CallExpr +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_3]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +void caller_2() { + foo(0, 0); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_3:0x[^ ]+]] {{.+}} caller_3 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= __builtin_get_pointer_upper_bound(p) - p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +void caller_3(int *__counted_by_or_null(len) p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_4:0x[^ ]+]] {{.+}} caller_4 +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_i:0x[^ ]+]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} '&i <= __builtin_get_pointer_upper_bound(&i) && __builtin_get_pointer_lower_bound(&i) <= &i && !&i || -1 <= __builtin_get_pointer_upper_bound(&i) - &i && 0 <= -1' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_8]] +// CHECK: {{^}}| | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_i]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_9]] +// CHECK: {{^}}| | `-UnaryOperator {{.+}} prefix '-' +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +void caller_4() { + int i = 0; + foo(&i, -1); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_5:0x[^ ]+]] {{.+}} caller_5 +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_i_1:0x[^ ]+]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} '&i <= __builtin_get_pointer_upper_bound(&i) && __builtin_get_pointer_lower_bound(&i) <= &i && !&i || 2 <= __builtin_get_pointer_upper_bound(&i) - &i && 0 <= 2' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_10]] +// CHECK: {{^}}| | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_i_1]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_11]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 2 +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +void caller_5() { + int i = 0; + foo(&i, 2); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_6:0x[^ ]+]] {{.+}} caller_6 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= __builtin_get_pointer_upper_bound(p) - p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_12]] +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_13]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_14]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_15]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +void caller_6(int *__counted_by(len) p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_7:0x[^ ]+]] {{.+}} caller_7 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_3:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= __builtin_get_pointer_upper_bound(p) - p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_16]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_3]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_17]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +void caller_7(int *__bidi_indexable p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_8:0x[^ ]+]] {{.+}} caller_8 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_4:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= __builtin_get_pointer_upper_bound(p) - p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_18]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_19]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_4]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +void caller_8(int *__single p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: {{^}}| |-ParmVarDecl [[var_out:0x[^ ]+]] +// CHECK: {{^}}| `-ParmVarDecl [[var_len_5:0x[^ ]+]] +// CHECK: {{^}}| `-DependerDeclsAttr +void bar(int *__counted_by(*len) *out, int *len); + +// CHECK: {{^}}|-FunctionDecl [[func_caller_9:0x[^ ]+]] {{.+}} caller_9 +// CHECK: {{^}}| |-ParmVarDecl [[var_out_1:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_6:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-CallExpr +// CHECK: {{^}}| | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(*len)*__single, int *__single)' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int *__single __counted_by(*len)*__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_20]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*len)*__single' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_out_1]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_21]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_20]] {{.*}} 'int *__single __counted_by(*len)*__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__single' +void caller_9(int *__counted_by(*len) *out, int *len){ + bar(out, len); +} + +// CHECK: {{^}}`-FunctionDecl [[func_caller_10:0x[^ ]+]] {{.+}} caller_10 +// CHECK: {{^}} |-ParmVarDecl [[var_len_7:0x[^ ]+]] +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_count:0x[^ ]+]] +// CHECK: {{^}} | `-DependerDeclsAttr +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_p_5:0x[^ ]+]] +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-CallExpr +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(*len)*__single, int *__single)' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*len)*__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(count)*__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_22]] +// CHECK: {{^}} | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_23]] +// CHECK: {{^}} | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__single __counted_by_or_null(count)*__bidi_indexable' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= __builtin_get_pointer_upper_bound(p) - p && 0 <= len' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' '=' +// CHECK: {{^}} | | | |-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | | |-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}} | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_24]] +// CHECK: {{^}} | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_25]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_25]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_26]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_25]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_27]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: {{^}} | | |-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: {{^}} `-ReturnStmt +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_28]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_29]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} |-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_29]] {{.*}} 'int' +int *__counted_by_or_null(len) caller_10(int len) { + int count; + int *__counted_by_or_null(count) p; + bar(&p, &count); + p = p; // workaround for missing return bounds check + count = len; + return p; +} diff --git a/clang/test/BoundsSafety/AST/counted_by_to_counted_by.c b/clang/test/BoundsSafety/AST/counted_by_to_counted_by.c new file mode 100644 index 0000000000000..07d62092e5734 --- /dev/null +++ b/clang/test/BoundsSafety/AST/counted_by_to_counted_by.c @@ -0,0 +1,266 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null + +#include + +struct Packet { + int *__counted_by(len) buf; + int len; +}; + +void *__sized_by(siz) my_alloc(int siz); +int *__sized_by(cnt) my_alloc_int(int cnt); + +// CHECK: |-FunctionDecl [[func_my_alloc:0x[^ ]+]] {{.+}} my_alloc +// CHECK: |-FunctionDecl [[func_my_alloc_int:0x[^ ]+]] {{.+}} my_alloc_int + +void Foo(void) { + struct Packet p; + int siz = 10 * sizeof(int); + p.buf = my_alloc_int(siz); + p.len = 10; +} +// CHECK-LABEL: |-FunctionDecl {{.+}} Foo +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_p:0x[^ ]+]] +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_siz_1:0x[^ ]+]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: {{^}}| | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}}| | | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}}| | `-UnaryExprOrTypeTraitExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK: {{^}}| | | | |-MemberExpr {{.+}} .buf +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__single __sized_by(cnt)':'int *__single' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove]] +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __sized_by(cnt)':'int *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __sized_by(cnt)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_siz_1]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}}| | | | `-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(cnt)(*__single)(int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_my_alloc_int]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __sized_by(cnt)':'int *__single' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK: {{^}}| | |-MemberExpr {{.+}} .len +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' + + +void FooBitCast(void) { + struct Packet p; + int siz = 10 * sizeof(int); + p.buf = my_alloc(siz); + p.len = 10; +} + +// CHECK-LABEL: |-FunctionDecl {{.+}} FooBitCast +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_p_1:0x[^ ]+]] +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_siz_2:0x[^ ]+]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: {{^}}| | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}}| | | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}}| | `-UnaryExprOrTypeTraitExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK: {{^}}| | | | |-MemberExpr {{.+}} .buf +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_siz_2]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}}| | | | `-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(siz)(*__single)(int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_my_alloc]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK: {{^}}| | |-MemberExpr {{.+}} .len +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +void FooCCast(void) { + struct Packet p; + int siz = 10 * sizeof(int); + p.buf = (int*)my_alloc(siz); + p.len = 10; +} + +// CHECK-LABEL: `-FunctionDecl {{.+}} FooCCast +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_p_2:0x[^ ]+]] +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_siz_3:0x[^ ]+]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}} | | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}} | `-UnaryExprOrTypeTraitExpr +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsCheckExpr +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK: {{^}} | | | |-MemberExpr {{.+}} .buf +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_8]] +// CHECK: {{^}} | | `-CStyleCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_10]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_siz_3]] +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_9]] +// CHECK: {{^}} | | | `-CallExpr +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(siz)(*__single)(int)' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_my_alloc]] +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'void *__single __sized_by(siz)':'void *__single' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_11]] +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} |-BinaryOperator {{.+}} 'int' '=' +// CHECK: {{^}} | |-MemberExpr {{.+}} .len +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/dbpt-nullable.c b/clang/test/BoundsSafety/AST/dbpt-nullable.c new file mode 100644 index 0000000000000..771d9db2f2fa9 --- /dev/null +++ b/clang/test/BoundsSafety/AST/dbpt-nullable.c @@ -0,0 +1,83 @@ + +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety %s | FileCheck %s --check-prefix=CHECK +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s --check-prefix=CHECK + +#include + +void counted(int *_Nullable __counted_by(count) array, int count) { + array[10]; +} + +// CHECK: |-FunctionDecl [[func_counted:0x[^ ]+]] {{.+}} counted +// CHECK: | |-ParmVarDecl [[var_array:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_count:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count) _Nullable':'int *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count) _Nullable':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count) _Nullable':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_array]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count) _Nullable':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | `-IntegerLiteral {{.+}} 10 + +void sized(int *_Nullable __sized_by(count) array, int count) { + array[10]; +} + +// CHECK: |-FunctionDecl [[func_sized:0x[^ ]+]] {{.+}} sized +// CHECK: | |-ParmVarDecl [[var_array_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_count_1:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __sized_by(count) _Nullable':'int *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(count) _Nullable':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(count) _Nullable':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_array_1]] +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_count_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(count) _Nullable':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | `-IntegerLiteral {{.+}} 10 + +void ended(int *_Nullable __ended_by(end) array, int *end) { + array[10]; +} + +// CHECK: `-FunctionDecl [[func_ended:0x[^ ]+]] {{.+}} ended +// CHECK: |-ParmVarDecl [[var_array_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_end:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-ArraySubscriptExpr +// CHECK: |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | |-DeclRefExpr {{.+}} [[var_array_2]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(array) */ ':'int *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_end]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end) _Nullable':'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_array_2]] +// CHECK: `-IntegerLiteral {{.+}} 10 diff --git a/clang/test/BoundsSafety/AST/default-attributes-in-preprocessed.c b/clang/test/BoundsSafety/AST/default-attributes-in-preprocessed.c new file mode 100644 index 0000000000000..db33958b17a5d --- /dev/null +++ b/clang/test/BoundsSafety/AST/default-attributes-in-preprocessed.c @@ -0,0 +1,27 @@ + + +// RUN: %clang_cc1 -ast-dump -verify -fbounds-safety -isystem %S/SystemHeaders/include %s | FileCheck %s +// RUN: %clang_cc1 -E -verify -fbounds-safety -isystem %S/SystemHeaders/include -o %t.pp.c %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety %t.pp.c | FileCheck %s + +// expected-no-diagnostics + +#include +#include + +int *__single return_to_single_p(int *p) { + return p; +} + +// CHECK-LABEL: increment_unsafe_p 'void (int *)' inline +// CHECK-NEXT: | |-ParmVarDecl {{.*}} used p 'int *' +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: | `-UnaryOperator {{.*}} 'int *' postfix '++' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int *' lvalue ParmVar {{.*}} 'p' 'int *' + +// CHECK-LABEL: return_to_single_p 'int *__single(int *__single)' +// CHECK-NEXT: |-ParmVarDecl {{.*}} used p 'int *__single' +// CHECK-NEXT: `-CompoundStmt +// CHECK-NEXT: `-ReturnStmt +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__single' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int *__single' lvalue ParmVar {{.*}} 'p' 'int *__single' diff --git a/clang/test/BoundsSafety/AST/dispatch-incomplete-addrof.c b/clang/test/BoundsSafety/AST/dispatch-incomplete-addrof.c new file mode 100644 index 0000000000000..5113a80d38b47 --- /dev/null +++ b/clang/test/BoundsSafety/AST/dispatch-incomplete-addrof.c @@ -0,0 +1,27 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s + +#define DISPATCH_GLOBAL_OBJECT(type, object) ((type)&(object)) +#define DISPATCH_DECL(name) typedef struct name##_s *name##_t +DISPATCH_DECL(dispatch_data); + +#define dispatch_data_empty \ + DISPATCH_GLOBAL_OBJECT(dispatch_data_t, _dispatch_data_empty) +extern struct dispatch_data_s _dispatch_data_empty; + +void foo(dispatch_data_t _Nonnull arg); + +// CHECK-LABEL: test 'void ()' +void test() { + foo(dispatch_data_empty); +} +// CHECK: `-CompoundStmt +// CHECK: `-CallExpr {{.*}} 'void' +// CHECK: |-ImplicitCastExpr {{.*}} 'void (*__single)(struct dispatch_data_s *__single _Nonnull)' +// CHECK: | `-DeclRefExpr {{.*}} 'void (struct dispatch_data_s *__single _Nonnull)' Function {{.*}} 'foo' 'void (struct dispatch_data_s *__single _Nonnull)' +// CHECK: `-ParenExpr {{.*}} 'struct dispatch_data_s *__single' +// CHECK: `-CStyleCastExpr {{.*}} 'struct dispatch_data_s *__single' +// CHECK: `-UnaryOperator {{.*}} 'struct dispatch_data_s *__single' prefix '&' cannot overflow +// CHECK: `-ParenExpr {{.*}} 'struct dispatch_data_s' lvalue +// CHECK: `-DeclRefExpr {{.*}} 'struct dispatch_data_s' lvalue Var {{.*}} '_dispatch_data_empty' 'struct dispatch_data_s' diff --git a/clang/test/BoundsSafety/AST/dynamic-count-assignment-in-comma-op.c b/clang/test/BoundsSafety/AST/dynamic-count-assignment-in-comma-op.c new file mode 100644 index 0000000000000..009e1384f3756 --- /dev/null +++ b/clang/test/BoundsSafety/AST/dynamic-count-assignment-in-comma-op.c @@ -0,0 +1,826 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +// CHECK-LABEL: preincdec_init +void preincdec_init(char *__sized_by(len) p, unsigned long long len) { + char *lp = (--len, ++p); +} +// CHECK: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_lp:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'char *__single __sized_by(len)':'char *__single' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'p + 1UL <= __builtin_get_pointer_upper_bound(p + 1UL) && __builtin_get_pointer_lower_bound(p + 1UL) <= p + 1UL && len - 1ULL <= (char *)__builtin_get_pointer_upper_bound(p + 1UL) - (char *__bidi_indexable)p + 1UL' +// CHECK: | | | |-UnaryOperator {{.+}} prefix '--' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | `-AssumptionExpr +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long long' +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-UnaryOperator {{.+}} prefix '++' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' + +// CHECK-LABEL: postincdec +void postincdec(char *__sized_by(len) p, unsigned long long len) { + p++, len--; +} +// CHECK: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'p + 1UL <= __builtin_get_pointer_upper_bound(p + 1UL) && __builtin_get_pointer_lower_bound(p + 1UL) <= p + 1UL && len - 1ULL <= (char *)__builtin_get_pointer_upper_bound(p + 1UL) - (char *__bidi_indexable)p + 1UL' +// CHECK: | | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | `-AssumptionExpr +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: postincdec_init +void postincdec_init(char *__sized_by(len) p, unsigned long long len) { + char *lp = (len--, p++); +} +// CHECK: | |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_lp:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'char *__single __sized_by(len)':'char *__single' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'p + 1UL <= __builtin_get_pointer_upper_bound(p + 1UL) && __builtin_get_pointer_lower_bound(p + 1UL) <= p + 1UL && len - 1ULL <= (char *)__builtin_get_pointer_upper_bound(p + 1UL) - (char *__bidi_indexable)p + 1UL' +// CHECK: | | | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_11]] +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} lvalue +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK: | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | `-AssumptionExpr +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'unsigned long long' +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__bidi_indexable' + +// CHECK-LABEL: compound_assign_init +void compound_assign_init(char *__sized_by(len) p, unsigned long long len) { + unsigned long long l = (p+=1, len-=1); +} +// CHECK: | |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_l:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'p + 1 <= __builtin_get_pointer_upper_bound(p + 1) && __builtin_get_pointer_lower_bound(p + 1) <= p + 1 && len - 1 <= (char *)__builtin_get_pointer_upper_bound(p + 1) - (char *__bidi_indexable)p + 1' +// CHECK: | | | |-CompoundAssignOperator {{.+}} ComputeResultTy='char *__single +// CHECK: | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_13]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: | | |-OpaqueValueExpr [[ove_14]] +// CHECK: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_15]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | `-AssumptionExpr +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_16]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | | |-OpaqueValueExpr [[ove_15]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_16]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_15]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'unsigned long long' +// CHECK: | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_19]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_18]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: | | `-OpaqueValueExpr [[ove_17]] +// CHECK: | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'unsigned long long' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-CompoundAssignOperator {{.+}} long' ComputeResultTy='unsigned +// CHECK: | | |-OpaqueValueExpr [[ove_18]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_12]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_19]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_18]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_17]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: compound_assign_assign +void compound_assign_assign(char *__sized_by(len) p, unsigned long long len) { + unsigned long long l; + l = (p+=1, len-=1); +} +// CHECK: | |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_3:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_l_1:0x[^ ]+]] +// CHECK: | `-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | |-DeclRefExpr {{.+}} [[var_l_1]] +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'p + 1 <= __builtin_get_pointer_upper_bound(p + 1) && __builtin_get_pointer_lower_bound(p + 1) <= p + 1 && len - 1 <= (char *)__builtin_get_pointer_upper_bound(p + 1) - (char *__bidi_indexable)p + 1' +// CHECK: | | | |-CompoundAssignOperator {{.+}} ComputeResultTy='char *__single +// CHECK: | | | | |-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_21]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_20]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_3]] +// CHECK: | | |-OpaqueValueExpr [[ove_22]] +// CHECK: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_23]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | `-AssumptionExpr +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_24]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | | |-OpaqueValueExpr [[ove_23]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_24]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_23]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'unsigned long long' +// CHECK: | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_27]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_26]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK: | | `-OpaqueValueExpr [[ove_25]] +// CHECK: | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-OpaqueValueExpr [[ove_26]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'unsigned long long' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-CompoundAssignOperator {{.+}} long' ComputeResultTy='unsigned +// CHECK: | | |-OpaqueValueExpr [[ove_26]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_21]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_20]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_22]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_27]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_26]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_25]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: assign_init_zero +void assign_init_zero(char *__sized_by(len) p, unsigned long long len) { + unsigned long long l = (p=0, len=0); +} +// CHECK: | |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_4:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_l_2:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} '0 == 0' +// CHECK: | | | |-BinaryOperator {{.+}} 'char *__single __sized_by(len)':'char *__single' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK: | | | |-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_28]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_29]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_len_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_28]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | `-OpaqueValueExpr [[ove_29]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: assign_init +void assign_init(char *__sized_by(len) p, unsigned long long len, + char *__bidi_indexable ip, unsigned long long ilen) { + unsigned long long l = (p=ip, len=ilen); +} +// CHECK: | |-ParmVarDecl [[var_p_5:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_5:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_ip:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_ilen:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_l_3:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'ip <= __builtin_get_pointer_upper_bound(ip) && __builtin_get_pointer_lower_bound(ip) <= ip && ilen <= (char *)__builtin_get_pointer_upper_bound(ip) - (char *__bidi_indexable)ip' +// CHECK: | | | |-BinaryOperator {{.+}} 'char *__single __sized_by(len)':'char *__single' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_30:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_31:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_30]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_ip]] +// CHECK: | | `-OpaqueValueExpr [[ove_31]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_ilen]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_len_5]] +// CHECK: | | `-OpaqueValueExpr [[ove_31]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_30]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_31]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: assign_assign +void assign_assign(char *__sized_by(len) p, unsigned long long len, + char *__bidi_indexable ip, unsigned long long ilen) { + unsigned long long llen = 0; + char *__sized_by(llen) lp = 0; + lp = (len = ilen, p=ip); + llen = 0; +} +// CHECK: | |-ParmVarDecl [[var_p_6:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_6:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_ip_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_ilen_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_llen:0x[^ ]+]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_lp_1:0x[^ ]+]] +// CHECK: | | `-BoundsCheckExpr {{.+}} 'llen == 0' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'char *__single __sized_by(llen)':'char *__single' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-BinaryOperator {{.+}} 'int' '==' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_llen]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} '(len = ilen , p = ip) <= __builtin_get_pointer_upper_bound((len = ilen , p = ip)) && __builtin_get_pointer_lower_bound((len = ilen , p = ip)) <= (len = ilen , p = ip) && 0 <= (char *)__builtin_get_pointer_upper_bound((len = ilen , p = ip)) - (char *__single)(len = ilen , p = ip)' +// CHECK: | | | |-BinaryOperator {{.+}} 'char *__single __sized_by(llen)':'char *__single' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_lp_1]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_32:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_33:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_34:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_32]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_35:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_32]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-BinaryOperator {{.+}} 'char *__single __sized_by(len)':'char *__single' ',' +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsCheckExpr {{.+}} 'ip <= __builtin_get_pointer_upper_bound(ip) && __builtin_get_pointer_lower_bound(ip) <= ip && ilen <= (char *)__builtin_get_pointer_upper_bound(ip) - (char *__bidi_indexable)ip' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | | | | | | |-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_33]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_33]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_ilen_1]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_34]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_ip_1]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BinaryOperator {{.+}} 'char *__single __sized_by(len)':'char *__single' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_p_6]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_33]] {{.*}} 'unsigned long long' +// CHECK: | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_llen]] +// CHECK: | | `-OpaqueValueExpr [[ove_35]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: for_cond_inc +char for_cond_inc(char *__sized_by(len) p, unsigned long long len) { + char val = *p; + for (p++, len--; len > 0; p++, len--) { + val += *p; + } + return val; +} +// CHECK: |-ParmVarDecl [[var_p_7:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_7:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_val:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char' +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_36:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | `-AssumptionExpr +// CHECK: | | | | |-OpaqueValueExpr [[ove_37:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_37]] {{.*}} 'unsigned long long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_36]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK: | | `-OpaqueValueExpr [[ove_37]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | |-OpaqueValueExpr [[ove_36]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | `-OpaqueValueExpr [[ove_37]] {{.*}} 'unsigned long long' +// CHECK: |-ForStmt +// CHECK: | |-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'p + 1UL <= __builtin_get_pointer_upper_bound(p + 1UL) && __builtin_get_pointer_lower_bound(p + 1UL) <= p + 1UL && len - 1ULL <= (char *)__builtin_get_pointer_upper_bound(p + 1UL) - (char *__bidi_indexable)p + 1UL' +// CHECK: | | | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_38:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_39:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_40:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | | |-OpaqueValueExpr [[ove_41:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_42:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_43:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_39]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_38]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK: | | | |-OpaqueValueExpr [[ove_39]] +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_40]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_40]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | `-AssumptionExpr +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_41]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_41]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_40]] +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_38]] {{.*}} lvalue +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_41]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_40]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_41]] {{.*}} 'unsigned long long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | |-OpaqueValueExpr [[ove_43]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | | | `-OpaqueValueExpr [[ove_42]] +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_43]] {{.*}} lvalue +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | | `-OpaqueValueExpr [[ove_43]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove_38]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove_39]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_43]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_42]] {{.*}} 'unsigned long long' +// CHECK: | |-BinaryOperator {{.+}} 'int' '>' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-BinaryOperator {{.+}} 'unsigned long long' ',' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'p + 1UL <= __builtin_get_pointer_upper_bound(p + 1UL) && __builtin_get_pointer_lower_bound(p + 1UL) <= p + 1UL && len - 1ULL <= (char *)__builtin_get_pointer_upper_bound(p + 1UL) - (char *__bidi_indexable)p + 1UL' +// CHECK: | | | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_44:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_45:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_46:0x[^ ]+]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | | | | |-OpaqueValueExpr [[ove_47:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_45]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_45]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_45]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_48:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_49:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_45]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_45]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_44]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK: | | | |-OpaqueValueExpr [[ove_45]] +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_46]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_46]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | | `-AssumptionExpr +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_47]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_47]] {{.*}} 'unsigned long long' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_46]] +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_44]] {{.*}} lvalue +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_47]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_46]] {{.*}} 'char *__single __sized_by(len)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_47]] {{.*}} 'unsigned long long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | |-OpaqueValueExpr [[ove_49]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | | | `-OpaqueValueExpr [[ove_48]] +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_49]] {{.*}} lvalue +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | | `-OpaqueValueExpr [[ove_49]] {{.*}} lvalue +// CHECK: | `-CompoundStmt + diff --git a/clang/test/BoundsSafety/AST/dynamic-inout-count.c b/clang/test/BoundsSafety/AST/dynamic-inout-count.c new file mode 100644 index 0000000000000..8ee4b6c900f4b --- /dev/null +++ b/clang/test/BoundsSafety/AST/dynamic-inout-count.c @@ -0,0 +1,135 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_1]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_buf]] +// CHECK: | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove]] +// CHECK: `-IntegerLiteral {{.+}} 42 +void foo(int *__counted_by(*len) buf, int *len) { + *len = 42; +} + +// CHECK: FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | `-IntegerLiteral {{.+}} 11 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(*len), int *__single)' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(*len)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'long' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_6]] {{.*}} 'long' +void bar() { + int arr[10]; + int len = 11; + foo(arr, &len); +} diff --git a/clang/test/BoundsSafety/AST/dynamic-range-init-list.c b/clang/test/BoundsSafety/AST/dynamic-range-init-list.c new file mode 100644 index 0000000000000..317d85ed3770d --- /dev/null +++ b/clang/test/BoundsSafety/AST/dynamic-range-init-list.c @@ -0,0 +1,86 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple x86_64 %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple x86_64 %s | FileCheck %s + +#include + +struct RangePtrs { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + void *end; +}; + +void Test(void) { + int arr[10]; + struct RangePtrs rptrs = { arr + 1, arr + 2, arr + 3 }; +} + +// CHECK: {{^}}`-FunctionDecl [[func_Test:0x[^ ]+]] {{.+}} Test +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: {{^}} `-DeclStmt +// CHECK: {{^}} `-VarDecl [[var_rptrs:0x[^ ]+]] +// CHECK: {{^}} `-BoundsCheckExpr {{.+}} 'arr + 2 <= __builtin_get_pointer_upper_bound(arr + 1) && arr + 1 <= arr + 2 && __builtin_get_pointer_lower_bound(arr + 1) <= arr + 1' +// CHECK: {{^}} |-BoundsCheckExpr {{.+}} 'arr + 3 <= __builtin_get_pointer_upper_bound(arr + 2) && arr + 2 <= arr + 3 && __builtin_get_pointer_lower_bound(arr + 2) <= arr + 2' +// CHECK: {{^}} | |-InitListExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __ended_by(iter)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'void *__single /* __started_by(iter) */ ':'void *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} |-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}} |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 2 +// CHECK: {{^}} `-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}} |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: {{^}} `-IntegerLiteral {{.+}} 3 + diff --git a/clang/test/BoundsSafety/AST/ended-by-attribute-only-mode.c b/clang/test/BoundsSafety/AST/ended-by-attribute-only-mode.c new file mode 100644 index 0000000000000..0745c5054a372 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended-by-attribute-only-mode.c @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK-NOT: BoundsCheckExpr +// CHECK-NOT: BoundsSafetyPointerPromotionExpr +// CHECK-NOT: MaterializeSequenceExpr + +// CHECK: FunctionDecl {{.+}} foo 'void (int * __ended_by(end), int * /* __started_by(p) */ )' +void foo(int *__ended_by(end) p, int *end) { + // CHECK: UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int * __ended_by(end)':'int *' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __ended_by(end)':'int *' + (void)*p; + + // CHECK: ArraySubscriptExpr {{.+}} 'int' lvalue + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __ended_by(end)':'int *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __ended_by(end)':'int *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + (void)p[42]; + + // CHECK: BinaryOperator {{.+}} 'int * __ended_by(end)':'int *'{{.*}} '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __ended_by(end)':'int *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int * __ended_by(end)':'int *' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __ended_by(end)':'int *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ParmVar {{.+}} 'p' 'int * __ended_by(end)':'int *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + // CHECK-NEXT: BinaryOperator {{.+}} 'int * /* __started_by(p) */ ':'int *'{{.*}} '=' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int * /* __started_by(p) */ ':'int *' lvalue ParmVar {{.+}} 'end' 'int * /* __started_by(p) */ ':'int *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int * /* __started_by(p) */ ':'int *' '-' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * /* __started_by(p) */ ':'int *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int * /* __started_by(p) */ ':'int *' lvalue ParmVar {{.+}} 'end' 'int * /* __started_by(p) */ ':'int *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + p = p + 42; + end = end - 42; +} + +// CHECK: RecordDecl {{.+}} struct bar definition +// CHECK: |-FieldDecl {{.+}} q 'int * __ended_by(end)':'int *' +// CHECK-NEXT: `-FieldDecl {{.+}} end 'int * /* __started_by(q) */ ':'int *' +struct bar { + int *__ended_by(end) q; + int *end; +}; + +// CHECK: FunctionDecl {{.+}} baz 'void (struct bar *)' +void baz(struct bar *b) { + // CHECK: UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int * __ended_by(end)':'int *' + // CHECK-NEXT: `-MemberExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ->q + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + (void)*b->q; + + // CHECK: ArraySubscriptExpr {{.+}} 'int' lvalue + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __ended_by(end)':'int *' + // CHECK-NEXT: | `-MemberExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ->q + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + (void)b->q[42]; + + // CHECK: BinaryOperator {{.+}} 'int * __ended_by(end)':'int *'{{.*}} '=' + // CHECK-NEXT: |-MemberExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ->q + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int * __ended_by(end)':'int *' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * __ended_by(end)':'int *' + // CHECK-NEXT: | `-MemberExpr {{.+}} 'int * __ended_by(end)':'int *' lvalue ->q + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + // CHECK-NEXT: BinaryOperator {{.+}} 'int * /* __started_by(q) */ ':'int *'{{.*}} '=' + // CHECK-NEXT: |-MemberExpr {{.+}} 'int * /* __started_by(q) */ ':'int *' lvalue ->end + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-BinaryOperator {{.+}} 'int * /* __started_by(q) */ ':'int *' '-' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int * /* __started_by(q) */ ':'int *' + // CHECK-NEXT: | `-MemberExpr {{.+}} 'int * /* __started_by(q) */ ':'int *' lvalue ->end + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct bar *' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct bar *' lvalue ParmVar {{.+}} 'b' 'struct bar *' + // CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 42 + b->q = b->q + 42; + b->end = b->end - 42; +} diff --git a/clang/test/BoundsSafety/AST/ended-by-nested-assignments.c b/clang/test/BoundsSafety/AST/ended-by-nested-assignments.c new file mode 100644 index 0000000000000..bb5224ac8c905 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended-by-nested-assignments.c @@ -0,0 +1,188 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump -fbounds-safety-bringup-missing-checks=indirect_count_update %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -ast-dump -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s 2>&1 | FileCheck --check-prefix WITHOUT %s +#include + +// CHECK: |-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: | |-ParmVarDecl [[var_start:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_end:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'end - 1UL <= __builtin_get_pointer_upper_bound(start + 1UL) && start + 1UL <= end - 1UL && __builtin_get_pointer_lower_bound(start + 1UL) <= start + 1UL' +// CHECK: | | | | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_end]] +// CHECK: | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '-' +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_start]] +// CHECK: | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_start]] +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_end]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-IntegerLiteral {{.+}} 0 + +// WITHOUT: |-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// WITHOUT: | |-ParmVarDecl [[var_start:0x[^ ]+]] +// WITHOUT: | |-ParmVarDecl [[var_end:0x[^ ]+]] +// WITHOUT: | `-CompoundStmt +// WITHOUT: | |-BinaryOperator {{.+}} 'int' '=' +// WITHOUT: | | |-UnaryOperator {{.+}} cannot overflow +// WITHOUT: | | | `-UnaryOperator {{.+}} postfix '--' +// WITHOUT: | | | `-DeclRefExpr {{.+}} [[var_end]] +// WITHOUT: | | `-IntegerLiteral {{.+}} 0 +// WITHOUT: | `-BinaryOperator {{.+}} 'int' '=' +// WITHOUT: | |-UnaryOperator {{.+}} cannot overflow +// WITHOUT: | | `-UnaryOperator {{.+}} postfix '++' +// WITHOUT: | | `-DeclRefExpr {{.+}} [[var_start]] +// WITHOUT: | `-IntegerLiteral {{.+}} 0 + +void foo(int *__ended_by(end) start, int * end) { + *end-- = 0; + *start++ = 0; +} + +// CHECK: `-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: |-ParmVarDecl [[var_start_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_end_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ParenExpr +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'end - 1 <= __builtin_get_pointer_upper_bound(start + 1) && start + 1 <= end - 1 && __builtin_get_pointer_lower_bound(start + 1) <= start + 1' +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __ended_by(end)':'int *__single' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_start_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_start_1]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_end_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_start_1]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '-' +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | |-DeclRefExpr {{.+}} [[var_end_1]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_end_1]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_start_1]] +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | `-IntegerLiteral {{.+}} 0 +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-ParenExpr +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_end_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: `-IntegerLiteral {{.+}} 0 + +// WITHOUT: `-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// WITHOUT: |-ParmVarDecl [[var_start_1:0x[^ ]+]] +// WITHOUT: |-ParmVarDecl [[var_end_1:0x[^ ]+]] +// WITHOUT: `-CompoundStmt +// WITHOUT: |-BinaryOperator {{.+}} 'int' '=' +// WITHOUT: | |-UnaryOperator {{.+}} cannot overflow +// WITHOUT: | | `-ParenExpr +// WITHOUT: | | `-BinaryOperator {{.+}} 'int *__single __ended_by(end)':'int *__single' '=' +// WITHOUT: | | |-DeclRefExpr {{.+}} [[var_start_1]] +// WITHOUT: | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// WITHOUT: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// WITHOUT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// WITHOUT: | | | |-DeclRefExpr {{.+}} [[var_start_1]] +// WITHOUT: | | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// WITHOUT: | | | | `-DeclRefExpr {{.+}} [[var_end_1]] +// WITHOUT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// WITHOUT: | | | `-DeclRefExpr {{.+}} [[var_start_1]] +// WITHOUT: | | `-IntegerLiteral {{.+}} 1 +// WITHOUT: | `-IntegerLiteral {{.+}} 0 +// WITHOUT: `-BinaryOperator {{.+}} 'int' '=' +// WITHOUT: |-UnaryOperator {{.+}} cannot overflow +// WITHOUT: | `-ParenExpr +// WITHOUT: | `-BinaryOperator {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' '=' +// WITHOUT: | |-DeclRefExpr {{.+}} [[var_end_1]] +// WITHOUT: | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// WITHOUT: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '-' +// WITHOUT: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// WITHOUT: | | |-DeclRefExpr {{.+}} [[var_end_1]] +// WITHOUT: | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// WITHOUT: | | | `-DeclRefExpr {{.+}} [[var_end_1]] +// WITHOUT: | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// WITHOUT: | | `-DeclRefExpr {{.+}} [[var_start_1]] +// WITHOUT: | `-IntegerLiteral {{.+}} 1 +// WITHOUT: `-IntegerLiteral {{.+}} 0 + +void bar(int *__ended_by(end) start, int * end) { + *(start = start+1) = 0; + *(end = end-1) = 0; +} diff --git a/clang/test/BoundsSafety/AST/ended_by_assign_checks.c b/clang/test/BoundsSafety/AST/ended_by_assign_checks.c new file mode 100644 index 0000000000000..59bb18b389358 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_assign_checks.c @@ -0,0 +1,176 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2> /dev/null | FileCheck %s + +#include + +struct DataWithEndedBy { + int *__ended_by(fend) fbegin; + int *fend; +}; + +// CHECK-LABEL: test +void test(struct DataWithEndedBy *data, int len) { + int arr[10]; + data->fbegin = arr; + data->fend = arr + 10; +} +// CHECK: | |-ParmVarDecl [[var_data:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __ended_by(fend)':'int *__single' '=' +// CHECK: | | | | |-MemberExpr {{.+}} ->fbegin +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_data]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(fend)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' '=' +// CHECK: | | |-MemberExpr {{.+}} ->fend +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_data]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' + +// CHECK-LABEL: test_bitcast +void test_bitcast(struct DataWithEndedBy *data, int len) { + char arr[10]; + data->fbegin = arr; + data->fend = arr + 10; +} +// CHECK: | |-ParmVarDecl [[var_data_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __ended_by(fend)':'int *__single' '=' +// CHECK: | | | | |-MemberExpr {{.+}} ->fbegin +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_data_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(fend)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' '=' +// CHECK: | | |-MemberExpr {{.+}} ->fend +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_data_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' + +// CHECK-LABEL: test_ext_bitcast +void test_ext_bitcast(struct DataWithEndedBy *data, int len) { + char arr[10]; + data->fbegin = (int *)arr; + data->fend = (int *)(arr + 10); +} +// CHECK: |-ParmVarDecl [[var_data_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_2:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'int *__single __ended_by(fend)':'int *__single' '=' +// CHECK: | | | |-MemberExpr {{.+}} ->fbegin +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_data_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(fend)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-CStyleCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-CStyleCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ParenExpr +// CHECK: | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' '=' +// CHECK: | |-MemberExpr {{.+}} ->fend +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_data_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/ended_by_const_param.c b/clang/test/BoundsSafety/AST/ended_by_const_param.c new file mode 100644 index 0000000000000..d0a5d1b4ab7e0 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_const_param.c @@ -0,0 +1,91 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +struct S { + int *end; + int *__ended_by(end) iter; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used foo 'void (int *__single __ended_by(end)const, int *__single /* __started_by(start) */ const)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used start 'int *__single __ended_by(end)const':'int *__singleconst' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used end 'int *__single /* __started_by(start) */ const':'int *__singleconst' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} local 'int *__bidi_indexable' cinit +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'int *__single /* __started_by(start) */ const':'int *__singleconst' lvalue ParmVar {{.+}} 'end' 'int *__single /* __started_by(start) */ const':'int *__singleconst' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ const':'int *__singleconst' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__single /* __started_by(start) */ const':'int *__singleconst' lvalue ParmVar {{.+}} 'end' 'int *__single /* __started_by(start) */ const':'int *__singleconst' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'int *__single __ended_by(end)const':'int *__singleconst' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'int *__single __ended_by(end)const':'int *__singleconst' lvalue ParmVar {{.+}} 'start' 'int *__single __ended_by(end)const':'int *__singleconst' +void foo(int * const __ended_by(end) start, int* const end) { + int *local = end; +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} bar 'void (void)' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: |-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} used arr 'int[40]' +// CHECK-NEXT: `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'void' 'arr + 40 <= __builtin_get_pointer_upper_bound(arr) && arr <= arr + 40' +// CHECK-NEXT: | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __ended_by(end)const, int *__single /* __started_by(start) */ const)' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'void (int *__single __ended_by(end)const, int *__single /* __started_by(start) */ const)' Function {{.+}} 'foo' 'void (int *__single __ended_by(end)const, int *__single /* __started_by(start) */ const)' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int[40]' lvalue Var {{.+}} 'arr' 'int[40]' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int[40]' lvalue Var {{.+}} 'arr' 'int[40]' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 40 +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int[40]' lvalue Var {{.+}} 'arr' 'int[40]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 40 +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int[40]' lvalue Var {{.+}} 'arr' 'int[40]' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int[40]' lvalue Var {{.+}} 'arr' 'int[40]' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int[40]' lvalue Var {{.+}} 'arr' 'int[40]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 40 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int[40]' lvalue Var {{.+}} 'arr' 'int[40]' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int[40]' lvalue Var {{.+}} 'arr' 'int[40]' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 40 +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int[40]' lvalue Var {{.+}} 'arr' 'int[40]' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int[40]' lvalue Var {{.+}} 'arr' 'int[40]' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 40 +void bar(void) { + int arr[40]; + foo(arr, arr + 40); +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/AST/ended_by_decls.c b/clang/test/BoundsSafety/AST/ended_by_decls.c new file mode 100644 index 0000000000000..30a229b0f86df --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_decls.c @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fexperimental-bounds-safety-attributes -x c %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fexperimental-bounds-safety-attributes -x c++ %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fexperimental-bounds-safety-attributes -x objective-c %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-mac -ast-dump -fexperimental-bounds-safety-attributes -x objective-c++ %s 2>&1 | FileCheck %s + +#include + +// CHECK: RecordDecl {{.*}} struct S definition +// CHECK: |-FieldDecl {{.*}} referenced end 'int *{{.*}}/* __started_by(iter) */ ' +// CHECK: |-FieldDecl {{.*}} referenced start 'int *{{.*}}__ended_by(iter)' +// CHECK: `-FieldDecl {{.*}} referenced iter 'int *{{.*}}__ended_by(end) /* __started_by(start) */ ' +struct S { + int *end; + int *__ended_by(iter) start; + int *__ended_by(end) iter; +}; + +// CHECK: FunctionDecl {{.*}} foo 'void (int *{{.*}}__ended_by(end), int *{{.*}}/* __started_by(start) */ )' +// CHECK: |-ParmVarDecl {{.*}} used start 'int *{{.*}}__ended_by(end)' +// CHECK: `-ParmVarDecl {{.*}} used end 'int *{{.*}}/* __started_by(start) */ ' +void foo(int *__ended_by(end) start, int* end); + +// CHECK: FunctionDecl {{.*}} foo_cptr_end 'void (int *{{.*}}__ended_by(end), char *{{.*}}/* __started_by(start) */ )' +// CHECK: |-ParmVarDecl {{.*}} used start 'int *{{.*}}__ended_by(end)' +// CHECK: `-ParmVarDecl {{.*}} used end 'char *{{.*}}/* __started_by(start) */ ' +void foo_cptr_end(int *__ended_by(end) start, char* end); + +// CHECK: FunctionDecl {{.*}} foo_seq 'void (int *{{.*}}__ended_by(next), int *{{.*}}__ended_by(end) /* __started_by(start) */ , char *{{.*}}/* __started_by(next) */ )' +// CHECK: |-ParmVarDecl {{.*}} used start 'int *{{.*}}__ended_by(next)' +// CHECK: |-ParmVarDecl {{.*}} used next 'int *{{.*}}__ended_by(end) /* __started_by(start) */ ' +// CHECK: `-ParmVarDecl {{.*}} used end 'char *{{.*}}/* __started_by(next) */ ' +void foo_seq(int *__ended_by(next) start, int *__ended_by(end) next, char* end); + +// CHECK: FunctionDecl {{.*}} foo_out_start_out_end 'void (int *{{.*}}__ended_by(*out_end)*{{.*}}, int *{{.*}}/* __started_by(*out_start) */ *{{.*}})' +// CHECK: |-ParmVarDecl {{.*}} used out_start 'int *{{.*}}__ended_by(*out_end)*{{.*}}' +// CHECK: `-ParmVarDecl {{.*}} used out_end 'int *{{.*}}/* __started_by(*out_start) */ *{{.*}}' +void foo_out_start_out_end(int *__ended_by(*out_end) *out_start, int **out_end); + +// CHECK: FunctionDecl {{.*}} foo_out_end 'void (int *{{.*}}__ended_by(*out_end), int *{{.*}}/* __started_by(start) */ *{{.*}})' +// CHECK: |-ParmVarDecl {{.*}} used start 'int *{{.*}}__ended_by(*out_end)':'int *{{.*}}' +// CHECK: `-ParmVarDecl {{.*}} used out_end 'int *{{.*}}/* __started_by(start) */ *{{.*}}' +void foo_out_end(int *__ended_by(*out_end) start, int **out_end); + +// CHECK: FunctionDecl {{.*}} foo_ret_end 'int *{{.*}}__ended_by(end)(int *{{.*}})' +// CHECK: `-ParmVarDecl {{.*}} used end 'int *{{.*}}' +int *__ended_by(end) foo_ret_end(int *end); + +// CHECK: FunctionDecl {{.*}} foo_local_ended_by 'void ({{.*}})' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used end 'int *{{.*}}/* __started_by(start) */ ' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} used start 'int *{{.*}}__ended_by(end)' +void foo_local_ended_by(void) { + int *end; + int *__ended_by(end) start; +} diff --git a/clang/test/BoundsSafety/AST/ended_by_incdec.c b/clang/test/BoundsSafety/AST/ended_by_incdec.c new file mode 100644 index 0000000000000..6f036e0ba49a4 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_incdec.c @@ -0,0 +1,151 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +struct T { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + int *end; +}; + +void Test(struct T *t) { + t->start++; + --t->iter; + t->end--; +} + +// CHECK-LABEL: Test 'void (struct T *__single)' +// CHECK: |-ParmVarDecl [[var_t:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} 't->end - 1UL <= __builtin_get_pointer_upper_bound(t->iter - 1UL) && t->iter - 1UL <= t->end - 1UL && __builtin_get_pointer_lower_bound(t->iter - 1UL) <= t->iter - 1UL' +// CHECK: | | |-BoundsCheckExpr {{.+}} 't->iter - 1UL <= __builtin_get_pointer_upper_bound(t->start + 1UL) && t->start + 1UL <= t->iter - 1UL && __builtin_get_pointer_lower_bound(t->start + 1UL) <= t->start + 1UL' +// CHECK: | | | |-UnaryOperator {{.+}} postfix '++' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__single /* __started_by(iter) */ ':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int *__single __ended_by(iter)':'int *__single' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'struct T *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-MemberExpr {{.+}} ->start +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(iter)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | | | `-MemberExpr {{.+}} ->iter +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-MemberExpr {{.+}} ->iter +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct T *__single' +// CHECK: | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '-' +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single /* __started_by(iter) */ ':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single __ended_by(iter)':'int *__single' +// CHECK: | | | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(iter)':'int *__single' +// CHECK: | | | | | `-MemberExpr {{.+}} ->start +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct T *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(iter) */ ':'int *__single' +// CHECK: | | | | `-MemberExpr {{.+}} ->end +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct T *__single' +// CHECK: | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__single __ended_by(iter)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single /* __started_by(iter) */ ':'int *__single' +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | |-OpaqueValueExpr [[ove_11]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct T *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_t]] +// CHECK: | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | `-MemberExpr {{.+}} ->end +// CHECK: | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'struct T *__single' +// CHECK: | `-OpaqueValueExpr [[ove_9]] +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '-' +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} lvalue +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(iter) */ ':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | | | `-MemberExpr {{.+}} ->iter +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'struct T *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __ended_by(end) /* __started_by(start) */ ':'int *__single' +// CHECK: | `-IntegerLiteral {{.+}} 1 +// CHECK: |-UnaryOperator {{.+}} prefix '--' +// CHECK: | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-UnaryOperator {{.+}} postfix '--' +// CHECK: | `-OpaqueValueExpr [[ove_10]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_11]] {{.*}} 'struct T *__single' +// CHECK: |-OpaqueValueExpr [[ove_10]] {{.*}} lvalue +// CHECK: `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' + diff --git a/clang/test/BoundsSafety/AST/ended_by_locals.c b/clang/test/BoundsSafety/AST/ended_by_locals.c new file mode 100644 index 0000000000000..46fa394cdce98 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_locals.c @@ -0,0 +1,76 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +void foo(int * __bidi_indexable asdf, int asdf_len) { + const int *myEndPtr = asdf + asdf_len; + const int * __ended_by(myEndPtr) myEndedByPtr = asdf; +} + +// CHECK:FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK:|-ParmVarDecl [[var_asdf:0x[^ ]+]] +// CHECK:|-ParmVarDecl [[var_asdf_len:0x[^ ]+]] +// CHECK:`-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_myEndPtr:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'const int *__single /* __started_by(myEndedByPtr) */ ':'const int *__single' +// CHECK: | `-BoundsCheckExpr {{.+}} 'asdf + asdf_len <= __builtin_get_pointer_upper_bound(asdf + asdf_len) && __builtin_get_pointer_lower_bound(asdf + asdf_len) <= asdf + asdf_len' +// CHECK: | |-ImplicitCastExpr {{.+}} 'const int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_asdf]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_asdf_len]] +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const int *' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'const int *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_asdf]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_asdf_len]] +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_myEndedByPtr:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr {{.+}} 'myEndPtr <= __builtin_get_pointer_upper_bound(asdf) && asdf <= myEndPtr && __builtin_get_pointer_lower_bound(asdf) <= asdf' +// CHECK: |-ImplicitCastExpr {{.+}} 'const int *__single __ended_by(myEndPtr)':'const int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'const int *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'const int *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'const int *' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-GetBoundExpr {{.+}} lower +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'const int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'const int *' +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'const int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'const int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_asdf]] +// CHECK: `-OpaqueValueExpr [[ove_2]] +// CHECK: `-ImplicitCastExpr {{.+}} 'const int *' +// CHECK: `-BoundsSafetyPointerPromotionExpr {{.+}} 'const int *__bidi_indexable' +// CHECK: |-DeclRefExpr {{.+}} [[var_myEndPtr]] +// CHECK: |-ImplicitCastExpr {{.+}} 'const int *__single /* __started_by(myEndedByPtr) */ ':'const int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_myEndPtr]] +// CHECK: `-ImplicitCastExpr {{.+}} 'const int *__single __ended_by(myEndPtr)':'const int *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_myEndedByPtr]] + diff --git a/clang/test/BoundsSafety/AST/ended_by_param_in_call-disable-lb-check.c b/clang/test/BoundsSafety/AST/ended_by_param_in_call-disable-lb-check.c new file mode 100644 index 0000000000000..dbbe86115802a --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_param_in_call-disable-lb-check.c @@ -0,0 +1,863 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -verify %s 2> /dev/null | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -verify -x objective-c -fbounds-attributes-objc-experimental %s 2> /dev/null | FileCheck %s +#include + +// expected-no-diagnostics + +// CHECK-LABEL:|-FunctionDecl {{.+}} <{{.+}}:12:1, col:{{.+}}> col:{{.+}} used ended_by 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used start 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} used end 'const char *__single /* __started_by(start) */ ':'const char *__single' +void ended_by(const char *__ended_by(end) start, const char *end); + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_const_size_arr_in_bounds 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&local[10] <= __builtin_get_pointer_upper_bound(local) && local <= &local[10]' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_const_size_arr_in_bounds(void) { + char local[10]; + ended_by(local, &local[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_const_size_arr_start_oob 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&local[10] <= __builtin_get_pointer_upper_bound(local - 2) && local - 2 <= &local[10]' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_const_size_arr_start_oob(void) { + char local[10]; + ended_by(local - 2, &local[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_const_size_arr_end_oob 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' 'local + 11 <= __builtin_get_pointer_upper_bound(local) && local <= local + 11' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 11 +void pass_const_size_arr_end_oob(void) { + char local[10]; + ended_by(local, local + 11); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_explicit_indexable 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used ilocal 'char *__indexable' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&ilocal[10] <= __builtin_get_pointer_upper_bound(ilocal) && ilocal <= &ilocal[10]' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_explicit_indexable(void) { + char local[10]; + char* __indexable ilocal = local; + ended_by(ilocal, &ilocal[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_explict_bidi_indexable 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used bilocal 'char *__bidi_indexable' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&bilocal[10] <= __builtin_get_pointer_upper_bound(bilocal) && bilocal <= &bilocal[10]' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_explict_bidi_indexable(void) { + char local[10]; + char* __bidi_indexable bilocal = local; + ended_by(bilocal, &bilocal[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_ended_by 'void (char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used start 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used end 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' 'end <= __builtin_get_pointer_upper_bound(start) && start <= end' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +void pass_ended_by(char* __ended_by(end) start, const char* end) { + ended_by(start, end); +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} pass_counted_by 'void (char *__single __counted_by(count), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used start 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used count 'int' +// CHECK-NEXT: | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'void' 'start + count <= __builtin_get_pointer_upper_bound(start) && start <= start + count' +// CHECK-NEXT: | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +void pass_counted_by(char* __counted_by(count) start, int count) { + ended_by(start, start + count); +} diff --git a/clang/test/BoundsSafety/AST/ended_by_param_in_call.c b/clang/test/BoundsSafety/AST/ended_by_param_in_call.c new file mode 100644 index 0000000000000..1e463b55944de --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_param_in_call.c @@ -0,0 +1,1017 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -verify %s 2> /dev/null | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -verify -x objective-c -fbounds-attributes-objc-experimental %s 2> /dev/null | FileCheck %s +#include + +// expected-no-diagnostics + +// CHECK-LABEL:|-FunctionDecl {{.+}} <{{.+}}:12:1, col:{{.+}}> col:{{.+}} used ended_by 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used start 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} used end 'const char *__single /* __started_by(start) */ ':'const char *__single' +void ended_by(const char *__ended_by(end) start, const char *end); + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_const_size_arr_in_bounds 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&local[10] <= __builtin_get_pointer_upper_bound(local) && local <= &local[10] && __builtin_get_pointer_lower_bound(local) <= local' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_const_size_arr_in_bounds(void) { + char local[10]; + ended_by(local, &local[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_const_size_arr_start_oob 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&local[10] <= __builtin_get_pointer_upper_bound(local - 2) && local - 2 <= &local[10] && __builtin_get_pointer_lower_bound(local - 2) <= local - 2' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '-' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 2 +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_const_size_arr_start_oob(void) { + char local[10]; + ended_by(local - 2, &local[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_const_size_arr_end_oob 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' 'local + 11 <= __builtin_get_pointer_upper_bound(local) && local <= local + 11 && __builtin_get_pointer_lower_bound(local) <= local' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 11 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 11 +void pass_const_size_arr_end_oob(void) { + char local[10]; + ended_by(local, local + 11); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_explicit_indexable 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used ilocal 'char *__indexable' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&ilocal[10] <= __builtin_get_pointer_upper_bound(ilocal) && ilocal <= &ilocal[10] && __builtin_get_pointer_lower_bound(ilocal) <= ilocal' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__indexable' lvalue Var {{.+}} 'ilocal' 'char *__indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_explicit_indexable(void) { + char local[10]; + char* __indexable ilocal = local; + ended_by(ilocal, &ilocal[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_explict_bidi_indexable 'void (void)' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used local 'char[10]' +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used bilocal 'char *__bidi_indexable' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char[10]' lvalue Var {{.+}} 'local' 'char[10]' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' '&bilocal[10] <= __builtin_get_pointer_upper_bound(bilocal) && bilocal <= &bilocal[10] && __builtin_get_pointer_lower_bound(bilocal) <= bilocal' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-IntegerLiteral {{.+}} 'int' 10 +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-UnaryOperator {{.+}} 'char *__bidi_indexable' prefix '&' cannot overflow +// CHECK-NEXT: | `-ArraySubscriptExpr {{.+}} 'char' lvalue +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue Var {{.+}} 'bilocal' 'char *__bidi_indexable' +// CHECK-NEXT: | `-IntegerLiteral {{.+}} 'int' 10 +void pass_explict_bidi_indexable(void) { + char local[10]; + char* __bidi_indexable bilocal = local; + ended_by(bilocal, &bilocal[10]); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} pass_ended_by 'void (char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used start 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used end 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | | |-BoundsCheckExpr {{.+}} 'void' 'end <= __builtin_get_pointer_upper_bound(start) && start <= end && __builtin_get_pointer_lower_bound(start) <= start' +// CHECK-NEXT: | | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' lvalue ParmVar {{.+}} 'end' 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} <> 'char *__single __ended_by(end)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __ended_by(end)':'char *__single' +void pass_ended_by(char* __ended_by(end) start, const char* end) { + ended_by(start, end); +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} pass_counted_by 'void (char *__single __counted_by(count), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used start 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used count 'int' +// CHECK-NEXT: | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: `-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: |-MaterializeSequenceExpr {{.+}} 'void' +// CHECK-NEXT: | |-BoundsCheckExpr {{.+}} 'void' 'start + count <= __builtin_get_pointer_upper_bound(start) && start <= start + count && __builtin_get_pointer_lower_bound(start) <= start' +// CHECK-NEXT: | | |-CallExpr {{.+}} 'void' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' Function {{.+}} 'ended_by' 'void (const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | | `-GetBoundExpr {{.+}} 'const char *__bidi_indexable' lower +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: |-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: `-OpaqueValueExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK-NEXT: `-BinaryOperator {{.+}} 'char *__bidi_indexable' '+' +// CHECK-NEXT: |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | | | `-<<>> +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__single __counted_by(count)':'char *__single' lvalue ParmVar {{.+}} 'start' 'char *__single __counted_by(count)':'char *__single' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count' 'int' +void pass_counted_by(char* __counted_by(count) start, int count) { + ended_by(start, start + count); +} diff --git a/clang/test/BoundsSafety/AST/ended_by_returns.c b/clang/test/BoundsSafety/AST/ended_by_returns.c new file mode 100644 index 0000000000000..f890139dc74ae --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_returns.c @@ -0,0 +1,493 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2> /dev/null | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2> /dev/null | FileCheck %s + +#include + +char *__ended_by(end) chunk(char *__ended_by(end) begin, char *end) { + return begin + 1; +} +//CHECK: FunctionDecl [[func_chunk:0x[^ ]+]] {{.*}} used chunk 'char *__single __ended_by(end)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +//CHECK: |-ParmVarDecl [[var_begin:0x[^ ]+]] {{.*}} 'char *__single __ended_by(end)':'char *__single' +//CHECK: |-ParmVarDecl [[var_end:0x[^ ]+]] {{.*}} 'char *__single /* __started_by(begin) */ ':'char *__single' +//CHECK: `-CompoundStmt +//CHECK: `-ReturnStmt +//CHECK: `-ImplicitCastExpr {{.*}} 'char *__single __ended_by(end)':'char *__single' +//CHECK: `-BinaryOperator {{.*}} 'char *__bidi_indexable' '+' +//CHECK: |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +//CHECK: | |-DeclRefExpr {{.+}} [[var_begin]] +//CHECK: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +//CHECK: | | `-DeclRefExpr {{.+}} [[var_end]] +//CHECK: | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +//CHECK: | `-DeclRefExpr {{.+}} [[var_begin]] +//CHECK: `-IntegerLiteral {{.*}} 'int' 1 + +// CHECK-LABEL: foo +void foo(void) { + int arr[10]; + int *p = chunk(arr, arr+10); +} +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)(*__single)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_chunk]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | `-OpaqueValueExpr [[ove_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__bidi_indexable' + +// CHECK-LABEL: fooCast +void fooCast(void) { + int arr[10]; + int *p = (int*)chunk(arr, arr+10); +} +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p_1:0x[^ ]+]] +// CHECK: `-CStyleCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)(*__single)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_chunk]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: |-OpaqueValueExpr [[ove_2]] {{.*}} 'char *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_3]] {{.*}} 'char *__bidi_indexable' + +struct DataWithEndedBy { + int *__ended_by(fend) fbegin; + int *fend; +}; + +// CHECK-LABEL: bar +void bar(struct DataWithEndedBy *data) { + int arr[10]; + data->fbegin = chunk(arr, arr+10); + data->fend = arr+10; +} +// CHECK: |-ParmVarDecl [[var_data:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_2:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'int *__single __ended_by(fend)':'int *__single' '=' +// CHECK: | | | |-MemberExpr {{.+}} ->fbegin +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_data]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(fend)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-BoundsCheckExpr +// CHECK: | | | | | |-CallExpr +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)(*__single)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[func_chunk]] +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_7]] +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' '=' +// CHECK: | |-MemberExpr {{.+}} ->fend +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_data]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' + +// CHECK-LABEL: barCast +void barCast(struct DataWithEndedBy *data) { + int arr[10]; + data->fbegin = (int*)chunk(arr, arr+10); + data->fend = arr+10; +} +// CHECK: |-ParmVarDecl [[var_data_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_3:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'int *__single __ended_by(fend)':'int *__single' '=' +// CHECK: | | | |-MemberExpr {{.+}} ->fbegin +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_data_1]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(fend)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | `-CStyleCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-BoundsCheckExpr +// CHECK: | | | | | |-CallExpr +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)(*__single)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[func_chunk]] +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_11]] +// CHECK: | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' '=' +// CHECK: | |-MemberExpr {{.+}} ->fend +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct DataWithEndedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_data_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(fbegin) */ ':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__bidi_indexable' + +struct DataWithCountedBy { + long *__counted_by(count) ptr; + unsigned long long count; +}; + +// CHECK-LABEL: baz 'void (struct DataWithCountedBy *__single, int)' +void baz(struct DataWithCountedBy *data, int len) { + int arr[10]; + data->ptr = chunk(arr, arr+10); + data->count = len; +} +// CHECK: |-ParmVarDecl [[var_data_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_4:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'long *__single __counted_by(count)':'long *__single' '=' +// CHECK: | | | |-MemberExpr {{.+}} ->ptr +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithCountedBy *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_data_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long *__single __counted_by(count)':'long *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long *' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long *' +// CHECK: | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-BoundsCheckExpr +// CHECK: | | | | | |-CallExpr +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)(*__single)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[func_chunk]] +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_13]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_4]] +// CHECK: | | | `-OpaqueValueExpr [[ove_14]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_4]] +// CHECK: | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_15]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | |-MemberExpr {{.+}} ->count +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct DataWithCountedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_data_2]] +// CHECK: | `-OpaqueValueExpr [[ove_15]] {{.*}} 'unsigned long long' +// CHECK: |-OpaqueValueExpr [[ove_12]] {{.*}} 'long *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_15]] {{.*}} 'unsigned long long' + +// CHECK-LABEL: bazCast 'void (struct DataWithCountedBy *__single, int)' +void bazCast(struct DataWithCountedBy *data, int len) { + int arr[10]; + data->ptr = (long*)chunk(arr, arr+10); + data->count = len; +} +// CHECK: |-ParmVarDecl [[var_data_3:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_5:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'long *__single __counted_by(count)':'long *__single' '=' +// CHECK: | | | |-MemberExpr {{.+}} ->ptr +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct DataWithCountedBy *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_data_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long *__single __counted_by(count)':'long *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long *' +// CHECK: | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long *' +// CHECK: | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_16]] +// CHECK: | | `-CStyleCastExpr {{.+}} 'long *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-BoundsCheckExpr +// CHECK: | | | | | |-CallExpr +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)(*__single)(char *__single __ended_by(end), char *__single /* __started_by(begin) */ )' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[func_chunk]] +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __ended_by(end)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(begin) */ ':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_17]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_5]] +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_5]] +// CHECK: | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | |-OpaqueValueExpr [[ove_17]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_19]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | |-MemberExpr {{.+}} ->count +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct DataWithCountedBy *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_data_3]] +// CHECK: | `-OpaqueValueExpr [[ove_19]] {{.*}} 'unsigned long long' +// CHECK: |-OpaqueValueExpr [[ove_16]] {{.*}} 'long *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_19]] {{.*}} 'unsigned long long' diff --git a/clang/test/BoundsSafety/AST/ended_by_type.c b/clang/test/BoundsSafety/AST/ended_by_type.c new file mode 100644 index 0000000000000..d5d9647bca947 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ended_by_type.c @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck --check-prefix=BS %s +// RUN: %clang_cc1 -fbounds-safety -fbounds-attributes-objc-experimental -x objective-c -ast-dump %s 2>&1 | FileCheck --check-prefix=BS %s +// RUN: %clang_cc1 -fbounds-safety -fbounds-attributes-cxx-experimental -x c++ -ast-dump %s 2>&1 | FileCheck --check-prefix=BS %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck --check-prefix=BSA %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck --check-prefix=BSA %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck --check-prefix=BSA %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck --check-prefix=BSA %s + +#include + +// BS: VarDecl {{.+}} func_ptr_dd 'void (*__single)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// BSA: VarDecl {{.+}} func_ptr_dd 'void (*)(void * __ended_by(end), void * /* __started_by(start) */ )' +void (*func_ptr_dd)(void *__ended_by(end) start, void *end); + +// BS: VarDecl {{.+}} func_ptr_di 'void (*__single)(void *__single __ended_by(*end), void *__single /* __started_by(start) */ *__single)' +// BSA: VarDecl {{.+}} func_ptr_di 'void (*)(void * __ended_by(*end), void * /* __started_by(start) */ *)' +void (*func_ptr_di)(void *__ended_by(*end) start, void **end); + +// BS: VarDecl {{.+}} func_ptr_id 'void (*__single)(void *__single __ended_by(end)*__single, void *__single /* __started_by(*start) */ )' +// BSA: VarDecl {{.+}} func_ptr_id 'void (*)(void * __ended_by(end)*, void * /* __started_by(*start) */ )' +void (*func_ptr_id)(void *__ended_by(end) *start, void *end); + +// BS: VarDecl {{.+}} func_ptr_ii 'void (*__single)(void *__single __ended_by(*end)*__single, void *__single /* __started_by(*start) */ *__single)' +// BSA: VarDecl {{.+}} func_ptr_ii 'void (*)(void * __ended_by(*end)*, void * /* __started_by(*start) */ *)' +void (*func_ptr_ii)(void *__ended_by(*end) *start, void **end); + +void foo(void) { + // BS: CStyleCastExpr {{.+}} 'void (*)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' + // BSA: CStyleCastExpr {{.+}} 'void (*)(void * __ended_by(end), void * /* __started_by(start) */ )' + (void (*)(void *__ended_by(end) start, void *end))0; +} diff --git a/clang/test/BoundsSafety/AST/explicit-cast-from-bounds-safety-pointer.c b/clang/test/BoundsSafety/AST/explicit-cast-from-bounds-safety-pointer.c new file mode 100644 index 0000000000000..4078f3826ebc6 --- /dev/null +++ b/clang/test/BoundsSafety/AST/explicit-cast-from-bounds-safety-pointer.c @@ -0,0 +1,17 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +int main() { + int *ip = 0; + char *cp = (char*)ip; + + return 0; +} + +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} cp 'char *__bidi_indexable'{{.*}} cinit +// CHECK: | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable'{{.*}} part_of_explicit_cast +// CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable'{{.*}} lvalue Var {{.+}} 'ip' 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-assign-null.c b/clang/test/BoundsSafety/AST/flexible-array-member-assign-null.c new file mode 100644 index 0000000000000..4e9ad34f05732 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-assign-null.c @@ -0,0 +1,62 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +typedef struct { + int count; + int elems[]; +} flex_t; + +// CHECK-LABEL: init_null 'void (void)' +void init_null(void) { + flex_t *__single s = 0; +} +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: `-DeclStmt +// CHECK-NEXT: `-VarDecl {{.*}} s 'flex_t *__single' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'flex_t *__single' +// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 0 + + +// CHECK-LABEL: init_casted_null 'void (void)' +void init_casted_null(void) { + flex_t *__single s = (flex_t *)0; +} +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: `-DeclStmt +// CHECK-NEXT: `-VarDecl {{.*}} s 'flex_t *__single' cinit +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'flex_t *__single' +// CHECK-NEXT: `-CStyleCastExpr {{.*}} 'flex_t *' +// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 0 + + +// CHECK-LABEL: assign_null 'void (void)' +void assign_null(void) { + flex_t *__single s; + s = 0; +} +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.*}} used s 'flex_t *__single' +// CHECK-NEXT: `-BinaryOperator {{.*}} 'flex_t *__single' '=' +// CHECK-NEXT: |-DeclRefExpr {{.*}} 'flex_t *__single' lvalue Var {{.*}} 's' 'flex_t *__single' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'flex_t *__single' +// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 0 + + +// CHECK-LABEL: assign_casted_null 'void (void)' +void assign_casted_null(void) { + flex_t *__single s; + s = (flex_t *)0; +} +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl {{.*}} used s 'flex_t *__single' +// CHECK-NEXT: `-BinaryOperator {{.*}} 'flex_t *__single' '=' +// CHECK-NEXT: |-DeclRefExpr {{.*}} 'flex_t *__single' lvalue Var {{.*}} 's' 'flex_t *__single' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'flex_t *__single' +// CHECK-NEXT: `-CStyleCastExpr {{.*}} 'flex_t *' +// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 0 diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-assign-with-single.c b/clang/test/BoundsSafety/AST/flexible-array-member-assign-with-single.c new file mode 100644 index 0000000000000..e829f0be0ca62 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-assign-with-single.c @@ -0,0 +1,65 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +// CHECK-LABEL: init_single +void init_single(void *p) { + struct flexible *__single s = p; +} +// CHECK-NEXT: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: | `-DeclStmt +// CHECK-NEXT: | `-VarDecl [[var_s:0x[^ ]+]] +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} [[var_p]] + +// CHECK-LABEL: init_casted_single +void init_casted_single(void *p) { + struct flexible *__single s = (struct flexible *)p; +} +// CHECK-NEXT: | |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: | `-DeclStmt +// CHECK-NEXT: | `-VarDecl [[var_s_1:0x[^ ]+]] +// CHECK-NEXT: | `-CStyleCastExpr {{.+}} 'struct flexible *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} [[var_p_1]] + +// CHECK-LABEL: assign_single +void assign_single(void *p) { + struct flexible *__single s; + s = p; +} +// CHECK-NEXT: | |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK-NEXT: | `-CompoundStmt +// CHECK-NEXT: | |-DeclStmt +// CHECK-NEXT: | | `-VarDecl [[var_s_2:0x[^ ]+]] +// CHECK-NEXT: | `-BinaryOperator {{.+}} 'struct flexible *__single' '=' +// CHECK-NEXT: | |-DeclRefExpr {{.+}} [[var_s_2]] +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} [[var_p_2]] + +// CHECK-LABEL: assign_casted_single +void assign_casted_single(void *p) { + struct flexible *__single s; + s = (struct flexible *)p; +} +// CHECK-NEXT: |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK-NEXT: `-CompoundStmt +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl [[var_s_3:0x[^ ]+]] +// CHECK-NEXT: `-BinaryOperator {{.+}} 'struct flexible *__single' '=' +// CHECK-NEXT: |-DeclRefExpr {{.+}} [[var_s_3]] +// CHECK-NEXT: `-CStyleCastExpr {{.+}} 'struct flexible *__single' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK-NEXT: `-DeclRefExpr {{.+}} [[var_p_3]] diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-assignments-no-count.c b/clang/test/BoundsSafety/AST/flexible-array-member-assignments-no-count.c new file mode 100644 index 0000000000000..dc3877ad66d00 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-assignments-no-count.c @@ -0,0 +1,126 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +typedef struct { + int count; + int elems[]; +} flex_inner_t; + +typedef struct { + unsigned dummy; + flex_inner_t flex; +} flex_t; + + +// CHECK-LABEL: test_fam_base +void test_fam_base(flex_t *f, void *__bidi_indexable buf) { + f = buf; +} + +// CHECK: |-ParmVarDecl [[var_f:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-BinaryOperator {{.+}} 'flex_t *__single' '=' +// CHECK: |-DeclRefExpr {{.+}} [[var_f]] +// CHECK: `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_buf]] + + +// CHECK-LABEL: test_fam_base_with_count +void test_fam_base_with_count(flex_t *f, void *__bidi_indexable buf) { + f = buf; + f->flex.count = 10; +} + +// CHECK: |-ParmVarDecl [[var_f_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-BinaryOperator {{.+}} 'flex_t *__single' '=' +// CHECK: | |-DeclRefExpr {{.+}} [[var_f_1]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf_1]] +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-MemberExpr {{.+}} .count +// CHECK: | `-MemberExpr {{.+}} ->flex +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_f_1]] +// CHECK: `-IntegerLiteral {{.+}} 10 + +// CHECK-LABEL: test_fam_base_init +void test_fam_base_init(void *__bidi_indexable buf) { + flex_t *__single f = buf; +} +// CHECK: |-ParmVarDecl [[var_buf_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_f_2:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_buf_2]] + + +// CHECK-LABEL: test_fam_base_init_with_count +void test_fam_base_init_with_count(void *__bidi_indexable buf) { + flex_t *__single f = buf; + f->flex.count = 10; +} +// CHECK: |-ParmVarDecl [[var_buf_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_f_3:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf_3]] +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-MemberExpr {{.+}} .count +// CHECK: | `-MemberExpr {{.+}} ->flex +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_f_3]] +// CHECK: `-IntegerLiteral {{.+}} 10 + +// CHECK: VarDecl [[var_g_flex:0x[^ ]+]] +flex_inner_t g_flex; + +// CHECK-LABEL: test_fam_lvalue_base_count_assign +void test_fam_lvalue_base_count_assign(unsigned arg) { + g_flex.count = arg; +} +// CHECK: |-ParmVarDecl [[var_arg:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-MemberExpr {{.+}} .count +// CHECK: | `-DeclRefExpr {{.+}} [[var_g_flex]] +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: `-DeclRefExpr {{.+}} [[var_arg]] + +// CHECK-LABEL: test_fam_lvalue_base_count_decrement +void test_fam_lvalue_base_count_decrement() { + g_flex.count--; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-UnaryOperator {{.+}} postfix '--' +// CHECK: | `-MemberExpr {{.+}} .count +// CHECK: | `-DeclRefExpr {{.+}} [[var_g_flex]] + +// CHECK-LABEL: test_fam_lvalue_base_count_compound +void test_fam_lvalue_base_count_compound(unsigned arg) { + g_flex.count -= arg; +} +// CHECK: |-ParmVarDecl [[var_arg_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-CompoundAssignOperator {{.+}} ComputeLHSTy='unsigned int' +// CHECK: |-MemberExpr {{.+}} .count +// CHECK: | `-DeclRefExpr {{.+}} [[var_g_flex]] +// CHECK: `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: `-DeclRefExpr {{.+}} [[var_arg_1]] diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-bidi.c b/clang/test/BoundsSafety/AST/flexible-array-member-bidi.c new file mode 100644 index 0000000000000..dca5a785ac8c2 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-bidi.c @@ -0,0 +1,761 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +struct Simple { + int len; + int fam[__counted_by(len)]; +}; + +// rdar://132731845 the flexible arrays are not bounds checked + +void simple_no_flexbase_update(struct Simple * __bidi_indexable p) { + p->len = 11; +} +// CHECK: {{^}}|-FunctionDecl [[func_simple_no_flexbase_update:0x[^ ]+]] {{.+}} simple_no_flexbase_update +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'struct Simple *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 11 + +// rdar://132731845 +void simple_flexbase_update(struct Simple * __bidi_indexable p) { + struct Simple * __bidi_indexable p2 = p; + p2->len = 11; +} +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_simple_flexbase_update:0x[^ ]+]] {{.+}} simple_flexbase_update +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Simple *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'struct Simple *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_p2]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 11 + +// rdar://132731845 +void simple_flexbase_self_assign(struct Simple * __bidi_indexable p) { + p = p; + p->len = 11; +} +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_simple_flexbase_self_assign:0x[^ ]+]] {{.+}} simple_flexbase_self_assign +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'struct Simple *__bidi_indexable' '=' +// CHECK-NEXT: {{^}}| | |-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Simple *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'struct Simple *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}|-RecordDecl +// CHECK-NEXT: {{^}}| |-FieldDecl +// CHECK-NEXT: {{^}}| | `-DependerDeclsAttr +// CHECK-NEXT: {{^}}| |-FieldDecl +// CHECK-NEXT: {{^}}| `-FieldDecl + +struct Shared { + int len; + int * __counted_by(len) ptr; + int fam[__counted_by(len)]; +}; +int * __counted_by(len) baz(int len); +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_baz:0x[^ ]+]] {{.+}} baz +// CHECK-NEXT: {{^}}| `-ParmVarDecl [[var_len:0x[^ ]+]] + +void shared_no_flexbase_update(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p->len = 11; + p->ptr = p2; +} +// CHECK-NEXT: {{^}}|-FunctionDecl [[func_shared_no_flexbase_update:0x[^ ]+]] {{.+}} shared_no_flexbase_update +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_3]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p2 <= __builtin_get_pointer_upper_bound(p2) && __builtin_get_pointer_lower_bound(p2) <= p2 && 11 <= __builtin_get_pointer_upper_bound(p2) - p2 && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_3]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_5]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p2_1]] +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_3]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_6]] {{.*}} 'int *__bidi_indexable' + +void shared_no_flexbase_update_reverse(struct Shared * __bidi_indexable p) { + p->ptr = baz(11); + p->len = 11; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_no_flexbase_update_reverse:0x[^ ]+]] {{.+}} shared_no_flexbase_update_reverse +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'baz(11) <= __builtin_get_pointer_upper_bound(baz(11)) && __builtin_get_pointer_lower_bound(baz(11)) <= baz(11) && 11 <= __builtin_get_pointer_upper_bound(baz(11)) - baz(11) && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_7]] +// CHECK-NEXT: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_9]] +// CHECK-NEXT: {{^}}| | | | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}}| | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_10]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' + +void shared_flexbase_update(struct Shared * __bidi_indexable p) { + int * p3 = baz(11); + struct Shared * __bidi_indexable p2 = p; + p2->ptr = p3; + p2->len = 11; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_flexbase_update:0x[^ ]+]] {{.+}} shared_flexbase_update +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p3 <= __builtin_get_pointer_upper_bound(p3) && __builtin_get_pointer_lower_bound(p3) <= p3 && 11 <= __builtin_get_pointer_upper_bound(p3) - p3 && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p2_2]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_13]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p3]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_14]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p2_2]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' + +void shared_flexbase_update_reverse(struct Shared * __bidi_indexable p) { + int * p3 = baz(11); + struct Shared * __bidi_indexable p2 = p; + p2->len = 11; + p2->ptr = p3; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_flexbase_update_reverse:0x[^ ]+]] {{.+}} shared_flexbase_update_reverse +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_6:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p3_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_6]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p3 <= __builtin_get_pointer_upper_bound(p3) && __builtin_get_pointer_lower_bound(p3) <= p3 && 11 <= __builtin_get_pointer_upper_bound(p3) - p3 && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p2_3]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_17]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_18]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p3_1]] +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p2_3]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__bidi_indexable' + +void shared_flexbase_self_assign(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p = p; + p->ptr = p2; + p->len = 11; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_flexbase_self_assign:0x[^ ]+]] {{.+}} shared_flexbase_self_assign +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_7:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2_4:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_20]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_19]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'struct Shared *__bidi_indexable' '=' +// CHECK-NEXT: {{^}}| | |-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p2 <= __builtin_get_pointer_upper_bound(p2) && __builtin_get_pointer_lower_bound(p2) <= p2 && 11 <= __builtin_get_pointer_upper_bound(p2) - p2 && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_21]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p2_4]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_22]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_22]] {{.*}} 'int' + +void shared_flexbase_self_assign_reverse(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p = p; + p->len = 11; + p->ptr = p2; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_flexbase_self_assign_reverse:0x[^ ]+]] {{.+}} shared_flexbase_self_assign_reverse +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_8:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2_5:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_24]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_23]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'struct Shared *__bidi_indexable' '=' +// CHECK-NEXT: {{^}}| | |-DeclRefExpr {{.+}} [[var_p_8]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_8]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p2 <= __builtin_get_pointer_upper_bound(p2) && __builtin_get_pointer_lower_bound(p2) <= p2 && 11 <= __builtin_get_pointer_upper_bound(p2) - p2 && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_8]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_25]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_26]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p2_5]] +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_8]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_25]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_26]] {{.*}} 'int *__bidi_indexable' + +void shared_flexbase_self_assign_fr(struct Shared * __bidi_indexable p) { + p = p; + p->ptr = p->ptr; + p->len = 11; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_flexbase_self_assign_fr:0x[^ ]+]] {{.+}} shared_flexbase_self_assign_fr +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_9:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'struct Shared *__bidi_indexable' '=' +// CHECK-NEXT: {{^}}| | |-DeclRefExpr {{.+}} [[var_p_9]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_9]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p->ptr <= __builtin_get_pointer_upper_bound(p->ptr) && __builtin_get_pointer_lower_bound(p->ptr) <= p->ptr && 11 <= __builtin_get_pointer_upper_bound(p->ptr) - p->ptr && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_9]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | |-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_30:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_31:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_27]] +// CHECK-NEXT: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_29]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_9]] +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_30]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | | `-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_28]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | `-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_29]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_30]] {{.*}} 'int' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_31]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_9]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_27]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' + +void shared_flexbase_self_assign_fr_reverse(struct Shared * __bidi_indexable p) { + p = p; + p->len = 11; + p->ptr = p->ptr; +} +// CHECK: {{^}}|-FunctionDecl [[func_shared_flexbase_self_assign_fr_reverse:0x[^ ]+]] {{.+}} shared_flexbase_self_assign_fr_reverse +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_10:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'struct Shared *__bidi_indexable' '=' +// CHECK-NEXT: {{^}}| | |-DeclRefExpr {{.+}} [[var_p_10]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p_10]] +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p->ptr <= __builtin_get_pointer_upper_bound(p->ptr) && __builtin_get_pointer_lower_bound(p->ptr) <= p->ptr && 11 <= __builtin_get_pointer_upper_bound(p->ptr) - p->ptr && 0 <= 11' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_10]] +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_32:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_33:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | | | |-OpaqueValueExpr [[ove_34:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | | | | `-OpaqueValueExpr [[ove_35:0x[^ ]+]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | | | | | | | | `-OpaqueValueExpr [[ove_36:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_32]] +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_33]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_34]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_35]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_p_10]] +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_36]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_35]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_34]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_35]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_35]] {{.*}} 'struct Shared *__bidi_indexable' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_36]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_34]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} ->ptr +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Shared *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_10]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_32]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_33]] {{.*}} 'int *__bidi_indexable' + +struct Double { + int len; + int len2; + int fam[__counted_by(len + len2)]; +}; + +void double_no_flexbase_update_once(struct Double * __bidi_indexable p) { + p->len = 11; +} +// CHECK: {{^}}|-FunctionDecl [[func_double_no_flexbase_update_once:0x[^ ]+]] {{.+}} double_no_flexbase_update_once +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_p_11:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'struct Double *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_p_11]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_37:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_37]] {{.*}} 'int' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_37]] +// CHECK-NEXT: {{^}}| `-IntegerLiteral {{.+}} 11 + +void double_no_flexbase_update_both(struct Double * __bidi_indexable p) { + p->len = 11; + p->len2 = 11; +} +// CHECK-NEXT: {{^}}`-FunctionDecl [[func_double_no_flexbase_update_both:0x[^ ]+]] {{.+}} double_no_flexbase_update_both +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_p_12:0x[^ ]+]] +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}} | | |-MemberExpr {{.+}} ->len +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'struct Double *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_p_12]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_38:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_38]] +// CHECK-NEXT: {{^}} | | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_39:0x[^ ]+]] +// CHECK-NEXT: {{^}} | `-IntegerLiteral {{.+}} 11 +// CHECK-NEXT: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}} | |-MemberExpr {{.+}} ->len2 +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'struct Double *__bidi_indexable' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_p_12]] +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_39]] {{.*}} 'int' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_38]] {{.*}} 'int' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_39]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-checks-assignments.c b/clang/test/BoundsSafety/AST/flexible-array-member-checks-assignments.c new file mode 100644 index 0000000000000..6c7570f2b8d85 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-checks-assignments.c @@ -0,0 +1,354 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_inner_t; + +typedef struct { + unsigned dummy; + flex_inner_t flex; +} flex_t; + + +// CHECK-LABEL: test_fam_base +void test_fam_base(flex_t *f, void *__bidi_indexable buf) { + f = buf; +} +// CHECK: |-ParmVarDecl [[var_f:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'flex_t *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_f]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | `-MemberExpr {{.+}} ->flex +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-MemberExpr {{.+}} .count +// CHECK: | | `-MemberExpr {{.+}} ->flex +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove]] +// CHECK: `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_buf]] + +// CHECK-LABEL: test_fam_base_with_count +void test_fam_base_with_count(flex_t *f, void *__bidi_indexable buf) { + f = buf; + f->flex.count = 10; +} + +// CHECK: |-ParmVarDecl [[var_f_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'flex_t *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_f_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | `-MemberExpr {{.+}} ->flex +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_buf_1]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} .count +// CHECK: | | `-MemberExpr {{.+}} ->flex +// CHECK: | | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f_1]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' + + +// CHECK-LABEL: test_fam_base_init +void test_fam_base_init(void *__bidi_indexable buf) { + flex_t *__single f = buf; +} +// CHECK: |-ParmVarDecl [[var_buf_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_f_2:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | `-MemberExpr {{.+}} ->flex +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-MemberExpr {{.+}} .count +// CHECK: | | `-MemberExpr {{.+}} ->flex +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_3]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_3]] +// CHECK: `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_buf_2]] + +// CHECK-LABEL: test_fam_base_init_with_count +void test_fam_base_init_with_count(void *__bidi_indexable buf) { + flex_t *__single f = buf; + f->flex.count = 10; +} +// CHECK: |-ParmVarDecl [[var_buf_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_f_3:0x[^ ]+]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | `-MemberExpr {{.+}} ->flex +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_buf_3]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} .count +// CHECK: | | `-MemberExpr {{.+}} ->flex +// CHECK: | | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f_3]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + + +// FIXME: rdar://84810920 +// CHECK-LABEL: test_fam_base_init_deref_with_count +void test_fam_base_init_deref_with_count(void *__bidi_indexable buf) { + flex_t *__single f = buf; + (*f).flex.count = 10; +} +// CHECK: |-ParmVarDecl [[var_buf_4:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_f_4:0x[^ ]+]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | | `-PredefinedBoundsCheckExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | `-MemberExpr {{.+}} ->flex +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} .count +// CHECK: | | | `-MemberExpr {{.+}} ->flex +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'flex_t *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_buf_4]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} '10 <= (*f).flex.count && 0 <= 10' +// FIXME: Ignoring the rest of AST for now. It seems file check doesn't like +// the dump produced by our ast simplfier for this function. rdar://103050286 + +// CHECK: VarDecl [[var_g_flex:0x[^ ]+]] +flex_inner_t g_flex = {4, {1, 2, 3, 4}}; + +// CHECK-LABEL: test_fam_lvalue_base_count_assign +void test_fam_lvalue_base_count_assign(unsigned arg) { + g_flex.count = arg; +} +// CHECK: | |-ParmVarDecl [[var_arg:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'arg <= g_flex.count && 0 <= arg' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | | |-MemberExpr {{.+}} .count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} .count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} lvalue +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_17]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_g_flex]] +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_18]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_arg]] + +// CHECK-LABEL: test_fam_lvalue_base_count_decrement +void test_fam_lvalue_base_count_decrement() { + g_flex.count--; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-UnaryOperator {{.+}} postfix '--' +// CHECK: | | | | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} .count +// CHECK: | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_20]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove_19]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_20]] +// CHECK: | | `-DeclRefExpr {{.+}} [[var_g_flex]] +// CHECK: | |-OpaqueValueExpr [[ove_19]] +// CHECK: | | `-MemberExpr {{.+}} .count +// CHECK: | | `-OpaqueValueExpr [[ove_20]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_21]] +// CHECK: | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_19]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 1 + +// CHECK-LABEL: test_fam_lvalue_base_count_compound +void test_fam_lvalue_base_count_compound(unsigned arg) { + g_flex.count -= arg; +} +// CHECK: | |-ParmVarDecl [[var_arg_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CompoundAssignOperator {{.+}} ComputeLHSTy='unsigned int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} .count +// CHECK: | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'unsigned int' +// CHECK: | | |-OpaqueValueExpr [[ove_24]] {{.*}} 'unsigned int' +// CHECK: | | |-OpaqueValueExpr [[ove_23]] {{.*}} lvalue +// CHECK: | | |-OpaqueValueExpr [[ove_22]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'unsigned int' +// CHECK: | |-OpaqueValueExpr [[ove_24]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arg_1]] +// CHECK: | |-OpaqueValueExpr [[ove_23]] +// CHECK: | | `-DeclRefExpr {{.+}} [[var_g_flex]] +// CHECK: | |-OpaqueValueExpr [[ove_22]] +// CHECK: | | `-MemberExpr {{.+}} .count +// CHECK: | | `-OpaqueValueExpr [[ove_23]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_25]] +// CHECK: | `-BinaryOperator {{.+}} 'unsigned int' '-' +// CHECK: | |-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_22]] {{.*}} lvalue +// CHECK: | `-OpaqueValueExpr [[ove_24]] {{.*}} 'unsigned int' + +typedef struct { + unsigned char count; + int elems[__counted_by(count - 1)]; +} flex_uchar_t; + +void test_flex_uchar_count_conversion(flex_uchar_t *flex, int arg) { + flex = flex; + flex->count = arg; +} +// CHECK: |-FunctionDecl [[func_test_flex_uchar_count_conversion:0x[^ ]+]] {{.+}} test_flex_uchar_count_conversion +// CHECK: | |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_arg_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BinaryOperator {{.+}} 'flex_uchar_t *__single' '=' +// CHECK: | | | |-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'flex_uchar_t *__single' +// CHECK: | | | `-PredefinedBoundsCheckExpr {{.+}} 'flex_uchar_t *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'flex_uchar_t *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} 'flex_uchar_t *__single' +// CHECK: | | | |-OpaqueValueExpr [[ove_25]] {{.*}} 'flex_uchar_t *__bidi_indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'flex_uchar_t *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'unsigned char' +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_25]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'flex_uchar_t *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_26]] {{.*}} 'flex_uchar_t *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'flex_uchar_t *__single' +// CHECK: | | | | | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'unsigned char' +// CHECK: | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'flex_uchar_t *__single' +// CHECK: | | | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | | `-OpaqueValueExpr [[ove_26]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'flex_uchar_t *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'flex_uchar_t *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_27]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned char' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arg_2]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'unsigned char' '=' +// CHECK: | | |-MemberExpr {{.+}} ->count +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'flex_uchar_t *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'unsigned char' +// CHECK: | |-OpaqueValueExpr [[ove_25]] {{.*}} 'flex_uchar_t *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_27]] {{.*}} 'unsigned char' +; diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-checks-deref-no-count.c b/clang/test/BoundsSafety/AST/flexible-array-member-checks-deref-no-count.c new file mode 100644 index 0000000000000..161240d216e58 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-checks-deref-no-count.c @@ -0,0 +1,114 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[]; +}; + +// CHECK-LABEL: not_checking_count_single +int not_checking_count_single(struct flexible *__single flex) { + return (*flex).elems[12]; +} +// CHECK: | |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} .elems +// CHECK: | | `-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | `-IntegerLiteral {{.+}} 12 + +// CHECK-LABEL: not_checking_count_unsafe +int not_checking_count_unsafe(struct flexible *__unsafe_indexable flex) { + return (*flex).elems[12]; +} +// CHECK: | |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} .elems +// CHECK: | | `-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__unsafe_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | `-IntegerLiteral {{.+}} 12 + +// CHECK-LABEL: checking_count_indexable +int checking_count_indexable(struct flexible *__indexable flex) { + return (*flex).elems[12]; +} +// CHECK: | |-ParmVarDecl [[var_flex_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} .elems +// CHECK: | | `-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | `-IntegerLiteral {{.+}} 12 + +// CHECK-LABEL: checking_count_bidi_indexable +int checking_count_bidi_indexable(struct flexible *__indexable flex) { + return (*flex).elems[12]; +} +// CHECK: | |-ParmVarDecl [[var_flex_3:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} .elems +// CHECK: | | `-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | `-IntegerLiteral {{.+}} 12 + +// CHECK-LABEL: checking_count_sized_by +int checking_count_sized_by(struct flexible *__sized_by(size) flex, int size) { + return (*flex).elems[12]; +} +// CHECK: |-ParmVarDecl [[var_flex_4:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-ArraySubscriptExpr +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-MemberExpr {{.+}} .elems +// CHECK: | `-ParenExpr +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: `-IntegerLiteral {{.+}} 12 diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-checks-deref.c b/clang/test/BoundsSafety/AST/flexible-array-member-checks-deref.c new file mode 100644 index 0000000000000..546473484643f --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-checks-deref.c @@ -0,0 +1,284 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +// CHECK-LABEL: not_checking_count_single +int not_checking_count_single(struct flexible *__single flex) { +// CHECK: | |-ParmVarDecl [[var_flex:0x[^ ]+]] + return (*flex).elems[12]; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 12 + + +// CHECK-LABEL: not_checking_count_unsafe +int not_checking_count_unsafe(struct flexible *__unsafe_indexable flex) { +// CHECK: | |-ParmVarDecl [[var_flex_1:0x[^ ]+]] + return (*flex).elems[12]; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 12 + +// CHECK-LABEL: checking_count_indexable +int checking_count_indexable(struct flexible *__indexable flex) { +// CHECK: | |-ParmVarDecl [[var_flex_2:0x[^ ]+]] + return (*flex).elems[12]; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 12 + +// CHECK-LABEL: checking_count_bidi_indexable +int checking_count_bidi_indexable(struct flexible *__indexable flex) { +// CHECK: | |-ParmVarDecl [[var_flex_3:0x[^ ]+]] + return (*flex).elems[12]; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 12 + + +// CHECK-LABEL: checking_count_sized_by +int checking_count_sized_by(struct flexible *__sized_by(size) flex, int size) { +// CHECK: | |-ParmVarDecl [[var_flex_4:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size:0x[^ ]+]] + return (*flex).elems[12]; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | | | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_15]] +// CHECK: | | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_12]] +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_13]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_11]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 12 +; diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-checks-to-single.c b/clang/test/BoundsSafety/AST/flexible-array-member-checks-to-single.c new file mode 100644 index 0000000000000..b6829d12b9ab0 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-checks-to-single.c @@ -0,0 +1,339 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +void sink(struct flexible *__single flex); +// CHECK: |-FunctionDecl [[func_sink:0x[^ ]+]] {{.+}} sink + +// CHECK-LABEL: checking_count_single +void checking_count_single(struct flexible *__single flex) { +// CHECK: | |-ParmVarDecl [[var_flex_1:0x[^ ]+]] + sink(flex); +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct flexible *__single)' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_sink]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' + + (void)(struct flexible *__single)flex; +// CHECK: | `-CStyleCastExpr {{.+}} 'void' +// CHECK: | `-CStyleCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single' + +} + +// CHECK-LABEL: checking_count_indexable +void checking_count_indexable(struct flexible *__indexable flex) { +// CHECK: | |-ParmVarDecl [[var_flex_2:0x[^ ]+]] + sink(flex); +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct flexible *__single)' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_sink]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__indexable' + + (void)(struct flexible *__single)flex; +// CHECK: | `-CStyleCastExpr {{.+}} 'void' +// CHECK: | `-CStyleCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex_2]] +} + +// CHECK-LABEL: checking_count_bidi_indexable +void checking_count_bidi_indexable(struct flexible *__indexable flex) { +// CHECK: {{^}}| |-ParmVarDecl [[var_flex_3:0x[^ ]+]] + sink(flex); +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct flexible *__single)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_sink]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct flexible *__indexable' + + (void)(struct flexible *__single)flex; +// CHECK: | `-CStyleCastExpr {{.+}} 'void' +// CHECK: | `-CStyleCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex_3]] +} + +// CHECK-LABEL: checking_count_sized_by +void checking_count_sized_by(struct flexible *__sized_by(size) flex, int size) { +// CHECK: {{^}}| |-ParmVarDecl [[var_flex_4:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_size:0x[^ ]+]] + sink(flex); +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct flexible *__single)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_sink]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' + + + (void)(struct flexible *__single)flex; +// CHECK: | `-CStyleCastExpr {{.+}} 'void' +// CHECK: | `-CStyleCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_11]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_12]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | |-OpaqueValueExpr [[ove_11]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' + +} + +// FIXME: Unnecessary promotion +// CHECK-LABEL: checking_count_single_return +struct flexible *checking_count_single_return(struct flexible *__single flex) { +// CHECK: | |-ParmVarDecl [[var_flex_5:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_13]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_5]] +// CHECK: | `-OpaqueValueExpr [[ove_13]] {{.*}} 'struct flexible *__single' + return flex; +} + +// CHECK-LABEL: checking_count_indexable_return +struct flexible *checking_count_indexable_return(struct flexible *__indexable flex) { +// CHECK: | |-ParmVarDecl [[var_flex_6:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_14]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'struct flexible *__indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_14]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_6]] +// CHECK: | `-OpaqueValueExpr [[ove_14]] {{.*}} 'struct flexible *__indexable' + return flex; +} + +// CHECK-LABEL: checking_count_bidi_indexable_return +struct flexible *checking_count_bidi_indexable_return(struct flexible *__bidi_indexable flex) { +// CHECK: | |-ParmVarDecl [[var_flex_7:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_15]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_7]] +// CHECK: | `-OpaqueValueExpr [[ove_15]] {{.*}} 'struct flexible *__bidi_indexable' + return flex; +} + +// CHECK-LABEL: checking_count_sized_by_return +struct flexible *checking_count_sized_by_return(struct flexible *__sized_by(size) flex, unsigned long long size) { +// CHECK: | |-ParmVarDecl [[var_flex_8:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | |-OpaqueValueExpr [[ove_16]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_16]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_17]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | `-AssumptionExpr +// CHECK: | | | | | |-OpaqueValueExpr [[ove_18]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'unsigned long long' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | |-OpaqueValueExpr [[ove_17]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_8]] +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_17]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'unsigned long long' +// CHECK: | `-OpaqueValueExpr [[ove_16]] {{.*}} 'struct flexible *__bidi_indexable' + + return flex; +} + +; diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-global-init.c b/clang/test/BoundsSafety/AST/flexible-array-member-global-init.c new file mode 100644 index 0000000000000..ce697100fafbc --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-global-init.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct flex_uchar { + unsigned char len; + unsigned char data[__counted_by(len - 1)]; +}; + +struct flex_uchar init = { 3, {0, 1} }; +// CHECK: `-VarDecl {{.+}} init 'struct flex_uchar' cinit +// CHECK: `-InitListExpr {{.+}} 'struct flex_uchar' +// CHECK: |-ImplicitCastExpr {{.+}} 'unsigned char' +// CHECK: | `-IntegerLiteral {{.+}} 'int' 3 +// CHECK: `-InitListExpr {{.+}} 'unsigned char[2]' +// CHECK: |-ImplicitCastExpr {{.+}} 'unsigned char' +// CHECK: | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK: `-ImplicitCastExpr {{.+}} 'unsigned char' +// CHECK: `-IntegerLiteral {{.+}} 'int' 1 diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-multiple-decls.c b/clang/test/BoundsSafety/AST/flexible-array-member-multiple-decls.c new file mode 100644 index 0000000000000..6e21fb4fdb359 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-multiple-decls.c @@ -0,0 +1,65 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +typedef struct { + int len; + int offs; + int fam[__counted_by(len - offs)]; +} S; + +void f(S *s) { + int arr[10] = {0}; + s = (S *)&arr[5]; + s->offs = 5; + s->len = 10; +} +// CHECK: `-FunctionDecl [[func_f:0x[^ ]+]] {{.+}} f +// CHECK: |-ParmVarDecl [[var_s:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: | `-InitListExpr +// CHECK: | |-array_filler: ImplicitValueInitExpr +// CHECK: | `-IntegerLiteral {{.+}} 0 +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'S *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'S *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'S *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'S *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'S *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'S *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '-' +// CHECK: | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-CStyleCastExpr {{.+}} 'S *__bidi_indexable' +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ArraySubscriptExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | | `-IntegerLiteral {{.+}} 5 +// CHECK: | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-IntegerLiteral {{.+}} 5 +// CHECK: | `-OpaqueValueExpr [[ove_1]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} ->offs +// CHECK: | | `-ImplicitCastExpr {{.+}} 'S *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} ->len +// CHECK: | | `-ImplicitCastExpr {{.+}} 'S *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'S *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion-assignment-uint64.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-assignment-uint64.c new file mode 100644 index 0000000000000..bef8bae613fff --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-assignment-uint64.c @@ -0,0 +1,285 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + unsigned long long count; + int elems[__counted_by(count)]; +}; + +// CHECK-LABEL: promote_to_bidi_indexable +void promote_to_bidi_indexable(struct flexible *flex) { + struct flexible *b = flex; +} +// CHECK: |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-AssumptionExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' + + +// CHECK-LABEL: promote_null_to_bidi_indexable +void promote_null_to_bidi_indexable(void) { + struct flexible *b = (struct flexible *)0; +} +// CHECK: CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b_1:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: `-CStyleCastExpr {{.+}} 'struct flexible *' +// CHECK: `-IntegerLiteral {{.+}} 0 + +// CHECK-LABEL: promote_null_to_single +void promote_null_to_single() { + struct flexible *__single b = (struct flexible *)0; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_b_2:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-CStyleCastExpr {{.+}} 'struct flexible *' +// CHECK: | `-IntegerLiteral {{.+}} 0 + +// CHECK-LABEL: promote_to_single +void promote_to_single(struct flexible *flex) { + struct flexible *__single s = flex; +} +// CHECK: |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | `-MemberExpr {{.+}} ->count +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-AssumptionExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' + +// CHECK-LABEL: double_promote_to_bidi_indexable +void double_promote_to_bidi_indexable(struct flexible *__sized_by(size) flex, int size) { + struct flexible *b = flex; +} +// CHECK: |-ParmVarDecl [[var_flex_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + +// CHECK-LABEL: promote_to_sized_by +void promote_to_sized_by(struct flexible *flex) { + unsigned long long siz; + struct flexible *__sized_by(siz) s; + + siz = sizeof(struct flexible) + sizeof(int) * flex->count; + s = flex; +} +// CHECK: |-ParmVarDecl [[var_flex_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_siz:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s_1:0x[^ ]+]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'flex <= __builtin_get_pointer_upper_bound(flex) && __builtin_get_pointer_lower_bound(flex) <= flex && sizeof(struct flexible) + sizeof(int) * flex->count <= (char *)__builtin_get_pointer_upper_bound(flex) - (char *__bidi_indexable)flex' +// CHECK: | | | |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long long' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-UnaryExprOrTypeTraitExpr +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long long' '*' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | `-UnaryExprOrTypeTraitExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-AssumptionExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'struct flexible *__single __sized_by(siz)':'struct flexible *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_s_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(siz)':'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} 'unsigned long long' +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' + +// CHECK-LABEL: promote_to_single_assign +void promote_to_single_assign(struct flexible *flex) { + struct flexible *__single s = flex; + s->count = flex->count; +} +// CHECK: |-ParmVarDecl [[var_flex_4:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s_2:0x[^ ]+]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-AssumptionExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_11]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | `-MemberExpr {{.+}} ->count +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | |-MemberExpr {{.+}} ->count +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_s_2]] +// CHECK: | `-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long long' +// CHECK: |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_11]] {{.*}} 'unsigned long long' diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion-assignment.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-assignment.c new file mode 100644 index 0000000000000..c4c229187f35b --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-assignment.c @@ -0,0 +1,252 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +// CHECK-LABEL: promote_to_bidi_indexable +void promote_to_bidi_indexable(struct flexible *flex) { + struct flexible *b = flex; +} +// CHECK: |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single' + +// CHECK-LABEL: promote_null_to_bidi_indexable +void promote_null_to_bidi_indexable(void) { + struct flexible *b = (struct flexible *)0; +} +// CHECK: CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b_1:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: `-CStyleCastExpr {{.+}} 'struct flexible *' +// CHECK: `-IntegerLiteral {{.+}} 0 + +// CHECK-LABEL: promote_null_to_single +void promote_null_to_single() { + struct flexible *__single b = (struct flexible *)0; +} +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_b_2:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-CStyleCastExpr {{.+}} 'struct flexible *' +// CHECK: | `-IntegerLiteral {{.+}} 0 + +// CHECK-LABEL: promote_to_single +void promote_to_single(struct flexible *flex) { + struct flexible *__single s = flex; +} +// CHECK: |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-MemberExpr {{.+}} ->count +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' + +// CHECK-LABEL: double_promote_to_bidi_indexable +void double_promote_to_bidi_indexable(struct flexible *__sized_by(size) flex, int size) { + struct flexible *b = flex; +} +// CHECK: |-ParmVarDecl [[var_flex_2:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + +// CHECK-LABEL: promote_to_sized_by +void promote_to_sized_by(struct flexible *flex) { + unsigned long long siz; + struct flexible *__sized_by(siz) s; + + siz = sizeof(struct flexible) + sizeof(int) * flex->count; + s = flex; +} +// CHECK: |-ParmVarDecl [[var_flex_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_siz:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s_1:0x[^ ]+]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr {{.+}} 'flex <= __builtin_get_pointer_upper_bound(flex) && __builtin_get_pointer_lower_bound(flex) <= flex && sizeof(struct flexible) + sizeof(int) * flex->count <= (char *)__builtin_get_pointer_upper_bound(flex) - (char *__bidi_indexable)flex' +// CHECK: | | | |-BinaryOperator {{.+}} 'unsigned long long' '=' +// CHECK: | | | | |-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_6]] {{.*}} 'unsigned long long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long' '+' +// CHECK: | | | |-UnaryExprOrTypeTraitExpr +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: | | | |-UnaryExprOrTypeTraitExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'struct flexible *__single __sized_by(siz)':'struct flexible *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_s_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(siz)':'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} 'unsigned long long' +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__bidi_indexable' + +// CHECK-LABEL: promote_to_single_assign +void promote_to_single_assign(struct flexible *flex) { + struct flexible *__single s = flex; + s->count = flex->count; +} +// CHECK: |-ParmVarDecl [[var_flex_4:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s_2:0x[^ ]+]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_11]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-MemberExpr {{.+}} ->count +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_flex_4]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} ->count +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_s_2]] +// CHECK: | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_9]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion-call-builtin.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-call-builtin.c new file mode 100644 index 0000000000000..930293fe309e7 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-call-builtin.c @@ -0,0 +1,192 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +// Make sure that calling a builtin and function has the same checks. + +// CHECK: FunctionDecl [[func_my_memset:0x[^ ]+]] {{.+}} my_memset +void *__sized_by(len) my_memset(void *__sized_by(len) b, int c, unsigned long len); + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK: FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'flex_t *__single' +// CHECK: | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: | | | `-AssumptionExpr +// CHECK: | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'flex_t *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'flex_t *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'flex_t *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'flex_t *__single' +// CHECK: | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-BoundsCheckExpr {{.+}} 'flex <= __builtin_get_pointer_upper_bound(flex) && __builtin_get_pointer_lower_bound(flex) <= flex && size <= (char *)__builtin_get_pointer_upper_bound(flex) - (char *)flex' +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(function-parameter-0-2)(*)(void *__single __sized_by(function-parameter-0-2), int, unsigned long)' +// CHECK: | | | `-DeclRefExpr {{.+}} +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-GetBoundExpr {{.+}} upper +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'void *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'unsigned long' +// CHECK: `-OpaqueValueExpr [[ove]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +void foo(flex_t *flex, unsigned size) { + __builtin_memset(flex, 0, size); +} + +// CHECK: FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'flex_t *__single' +// CHECK: | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | `-AssumptionExpr +// CHECK: | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'unsigned long' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'unsigned long' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'flex_t *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'flex_t *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'flex_t *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'flex_t *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'flex_t *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'flex_t *__single' +// CHECK: | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size_1]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-BoundsCheckExpr {{.+}} 'flex <= __builtin_get_pointer_upper_bound(flex) && __builtin_get_pointer_lower_bound(flex) <= flex && size <= (char *)__builtin_get_pointer_upper_bound(flex) - (char *)flex' +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(len)(*__single)(void *__single __sized_by(len), int, unsigned long)' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_my_memset]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'unsigned long' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-OpaqueValueExpr [[ove_9]] {{.*}} 'unsigned long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-GetBoundExpr {{.+}} upper +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_9]] {{.*}} 'unsigned long' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +void bar(flex_t *flex, unsigned size) { + my_memset(flex, 0, size); +} diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion-no-count.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-no-count.c new file mode 100644 index 0000000000000..46793fe1b1224 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-no-count.c @@ -0,0 +1,145 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[]; +}; + +// CHECK-LABEL: flex_no_bounds_promotion +int flex_no_bounds_promotion(struct flexible *__bidi_indexable flex) { + return flex->elems[2]; +} +// CHECK: | |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} ->elems +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | `-IntegerLiteral {{.+}} 2 + +// CHECK-LABEL: flex_promote_from_sized_by +int flex_promote_from_sized_by(struct flexible *__sized_by(size) flex, long size) { + return flex->elems[2]; +} +// CHECK: | |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} ->elems +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// CHECK: | `-IntegerLiteral {{.+}} 2 + +// CHECK-LABEL: flex_promote_from_single +int flex_promote_from_single(struct flexible *flex) { + return flex->elems[2]; +} +// CHECK: | |-ParmVarDecl [[var_flex_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} ->elems +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | `-IntegerLiteral {{.+}} 2 + +struct nested_flexible { + int blah; + struct flexible flex; +}; + +// CHECK-LABEL: nested_flex_no_bounds_promotion +int nested_flex_no_bounds_promotion(struct nested_flexible *__bidi_indexable nested) { + return nested->flex.elems[2]; +} +// CHECK: | |-ParmVarDecl [[var_nested:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} .elems +// CHECK: | | `-MemberExpr {{.+}} ->flex +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_nested]] +// CHECK: | `-IntegerLiteral {{.+}} 2 + +// CHECK-LABEL: nested_flex_promote_from_sized_by +int nested_flex_promote_from_sized_by(struct nested_flexible *__sized_by(size) nested, long size) { + return nested->flex.elems[2]; +} +// CHECK: | |-ParmVarDecl [[var_nested_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-MemberExpr {{.+}} .elems +// CHECK: | | `-MemberExpr {{.+}} ->flex +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct nested_flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct nested_flexible *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_nested_1]] +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'long' +// CHECK: | `-IntegerLiteral {{.+}} 2 + +// CHECK-LABEL: nested_flex_promote_from_single +int nested_flex_promote_from_single(struct nested_flexible *nested) { + return nested->flex.elems[2]; +} +// CHECK: |-ParmVarDecl [[var_nested_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-ArraySubscriptExpr +// CHECK: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-MemberExpr {{.+}} .elems +// CHECK: | `-MemberExpr {{.+}} ->flex +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_nested_2]] +// CHECK: `-IntegerLiteral {{.+}} 2 diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion-returns.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-returns.c new file mode 100644 index 0000000000000..398b7fd41dfd7 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-returns.c @@ -0,0 +1,196 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +// CHECK: FunctionDecl [[func_return_flex:0x[^ ]+]] {{.+}} return_flex +struct flexible *return_flex(); + +// CHECK-LABEL: single_init +void single_init() { + struct flexible *__single s = return_flex(); +} +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-MemberExpr {{.+}} ->count +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_1]] +// CHECK: | `-CallExpr +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single(*__single)()' +// CHECK: | `-DeclRefExpr {{.+}} [[func_return_flex]] +// CHECK: `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__single' + + +// CHECK-LABEL: single_assign +void single_assign() { + struct flexible *__single s; + s = return_flex(); +} +// CHECK: CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'struct flexible *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_s_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-MemberExpr {{.+}} ->count +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-CallExpr +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single(*__single)()' +// CHECK: | `-DeclRefExpr {{.+}} [[func_return_flex]] +// CHECK: `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' + +// CHECK-LABEL: single_assign_with_count +void single_assign_with_count() { + struct flexible *__single s; + s = return_flex(); + s->count = 10; +} +// CHECK: CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s_2:0x[^ ]+]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'struct flexible *__single' '=' +// CHECK: | | |-DeclRefExpr {{.+}} [[var_s_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single(*__single)()' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_return_flex]] +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} ->count +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_s_2]] +// CHECK: | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' + +// CHECK-LABEL: bidi_init +void bidi_init() { + struct flexible *b = return_flex(); +} +// CHECK: CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_b:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_7]] +// CHECK: | `-CallExpr +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single(*__single)()' +// CHECK: | `-DeclRefExpr {{.+}} [[func_return_flex]] +// CHECK: `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct flexible *__single' + +// CHECK-LABEL: bidi_assign +void bidi_assign() { + struct flexible *b; + b = return_flex(); +} +// CHECK: CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_b_1:0x[^ ]+]] +// CHECK: `-BinaryOperator {{.+}} 'struct flexible *__bidi_indexable' '=' +// CHECK: |-DeclRefExpr {{.+}} [[var_b_1]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' +// CHECK: | `-OpaqueValueExpr [[ove_8]] +// CHECK: | `-CallExpr +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct flexible *__single(*__single)()' +// CHECK: | `-DeclRefExpr {{.+}} [[func_return_flex]] +// CHECK: `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct flexible *__single' diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion-unsafe.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-unsafe.c new file mode 100644 index 0000000000000..0e57cde03e8fd --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion-unsafe.c @@ -0,0 +1,132 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +// CHECK-LABEL: elem_access +void elem_access(struct flexible *__unsafe_indexable flex) { + flex->elems[2] = 0; // array subscription check is currently done directly in clang CodeGen. +} +// CHECK: |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__unsafe_indexable' +// CHECK: | `-IntegerLiteral {{.+}} 2 +// CHECK: `-IntegerLiteral {{.+}} 0 + + +// CHECK-LABEL: count_access +void count_access(struct flexible *__unsafe_indexable flex) { + flex->count = 10; +} +// CHECK: |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | |-MemberExpr {{.+}} ->count +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_2]] +// CHECK: `-IntegerLiteral {{.+}} 10 + + +// CHECK-LABEL: elem_access_deref +void elem_access_deref(struct flexible *__unsafe_indexable flex) { + (*flex).elems[2] = 0; +} +// CHECK: |-ParmVarDecl [[var_flex_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-MemberExpr {{.+}} .elems +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct flexible *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-ParenExpr +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__unsafe_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} lvalue +// CHECK: | `-IntegerLiteral {{.+}} 2 +// CHECK: `-IntegerLiteral {{.+}} 0 + + +// CHECK-LABEL: count_access_deref +void count_access_deref(struct flexible *__unsafe_indexable flex) { + (*flex).count = 10; +} +// CHECK: |-ParmVarDecl [[var_flex_3:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} '10 <= (*flex).count && 0 <= 10' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | |-MemberExpr {{.+}} .count +// CHECK: | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} .count +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} lvalue +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] +// CHECK: | | `-ParenExpr +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct flexible *__unsafe_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_flex_3]] +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_7]] +// CHECK: `-IntegerLiteral {{.+}} 10 diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-promotion.c b/clang/test/BoundsSafety/AST/flexible-array-member-promotion.c new file mode 100644 index 0000000000000..283971d252d68 --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-promotion.c @@ -0,0 +1,248 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +int flex_no_bounds_promotion(struct flexible *__bidi_indexable flex) { + return flex->elems[2]; +} +// CHECK-LABEL: flex_no_bounds_promotion +// CHECK: {{^}}| |-ParmVarDecl [[var_flex:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-ArraySubscriptExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_flex]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| `-IntegerLiteral {{.+}} 2 + +int flex_promote_from_sized_by(struct flexible *__sized_by(size) flex, long size) { + return flex->elems[2]; +} +// CHECK-LABEL: flex_promote_from_sized_by +// CHECK: {{^}}| |-ParmVarDecl [[var_flex_1:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-ArraySubscriptExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'long' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'long' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_flex_1]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_size]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct flexible *__single __sized_by(size)':'struct flexible *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'long' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| `-IntegerLiteral {{.+}} 2 + +int flex_promote_from_single(struct flexible *flex) { + return flex->elems[2]; +} +// CHECK-LABEL: flex_promote_from_single +// CHECK: {{^}}| |-ParmVarDecl [[var_flex_2:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-ArraySubscriptExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | | `-MemberExpr {{.+}} ->count +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct flexible *__single' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_flex_2]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_4]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| `-IntegerLiteral {{.+}} 2 + +struct nested_flexible { + int blah; + struct flexible flex; +}; + +int nested_flex_no_bounds_promotion(struct nested_flexible *__bidi_indexable nested) { + return nested->flex.elems[2]; +} +// CHECK-LABEL: nested_flex_no_bounds_promotion +// CHECK: {{^}}| |-ParmVarDecl [[var_nested:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-ArraySubscriptExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} .elems +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} lvalue +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}}| | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}}| | | `-MemberExpr {{.+}} ->flex +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *__bidi_indexable' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_nested]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: {{^}}| `-IntegerLiteral {{.+}} 2 + +int nested_flex_promote_from_sized_by(struct nested_flexible *__sized_by(size) nested, long size) { + return nested->flex.elems[2]; +} +// CHECK-LABEL: nested_flex_promote_from_sized_by +// CHECK: {{^}}| |-ParmVarDecl [[var_nested_1:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-ArraySubscriptExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} .elems +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} lvalue +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'long' +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_10]] +// CHECK: {{^}}| | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} lvalue +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}}| | | `-MemberExpr {{.+}} ->flex +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct nested_flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'struct nested_flexible *' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'long' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_8]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_nested_1]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_9]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_size_1]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct nested_flexible *__single __sized_by(size)':'struct nested_flexible *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'long' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] {{.*}} lvalue +// CHECK: {{^}}| `-IntegerLiteral {{.+}} 2 + + +int nested_flex_promote_from_single(struct nested_flexible *nested) { + return nested->flex.elems[2]; +} +// CHECK-LABEL: nested_flex_promote_from_single +// CHECK: {{^}}| |-ParmVarDecl [[var_nested_2:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-ArraySubscriptExpr +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} .elems +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} lvalue +// CHECK: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct flexible *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | | | `-MemberExpr {{.+}} ->elems +// CHECK: {{^}}| | | | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_12]] +// CHECK: {{^}}| | | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} lvalue +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct flexible *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_11]] +// CHECK: {{^}}| | | `-MemberExpr {{.+}} ->flex +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct nested_flexible *__single' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_nested_2]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_11]] {{.*}} lvalue +// CHECK: {{^}}| `-IntegerLiteral {{.+}} 2 + +; diff --git a/clang/test/BoundsSafety/AST/flexible-array-member-shared-decls.c b/clang/test/BoundsSafety/AST/flexible-array-member-shared-decls.c new file mode 100644 index 0000000000000..625a78a19e84d --- /dev/null +++ b/clang/test/BoundsSafety/AST/flexible-array-member-shared-decls.c @@ -0,0 +1,318 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +struct Inner { + int * __counted_by(len) ptr; + int len; +}; +struct Outer { + struct Inner hdr; + int fam[__counted_by(hdr.len)]; +}; + +struct Outer * __sized_by(sizeof(struct Outer) + sizeof(int) * len) bar(int len); +int * __counted_by(len) baz(int len); + +// CHECK: |-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: |-FunctionDecl [[func_baz:0x[^ ]+]] {{.+}} baz + +struct Outer *foo(int len) { + int * p2 = baz(len); + struct Outer * __single p = bar(len); + p->hdr.len = len; + p->hdr.ptr = p2; + return p; +} + +// CHECK-LABEL: foo +// CHECK-NEXT: {{^}}| |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | | `-CallExpr +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| |-DeclStmt +// CHECK-NEXT: {{^}}| | `-VarDecl [[var_p:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} 'p2 <= __builtin_get_pointer_upper_bound(p2) && __builtin_get_pointer_lower_bound(p2) <= p2 && len <= __builtin_get_pointer_upper_bound(p2) - p2 && 0 <= len' +// CHECK-NEXT: {{^}}| | | |-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}}| | | | `-PredefinedBoundsCheckExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-MemberExpr {{.+}} ->fam +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'struct Outer *' +// CHECK-NEXT: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'struct Outer *' +// CHECK-NEXT: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}}| | | | | | `-AssumptionExpr +// CHECK-NEXT: {{^}}| | | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK-NEXT: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_4]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_3]] +// CHECK-NEXT: {{^}}| | | | | `-CallExpr +// CHECK-NEXT: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'struct Outer *__single __sized_by(16UL + 4UL * len)(*__single)(int)' +// CHECK-NEXT: {{^}}| | | | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_5]] +// CHECK-NEXT: {{^}}| | | | `-BinaryOperator {{.+}} 'unsigned long' '+' +// CHECK-NEXT: {{^}}| | | | |-IntegerLiteral {{.+}} 16 +// CHECK-NEXT: {{^}}| | | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK-NEXT: {{^}}| | | | |-IntegerLiteral {{.+}} 4 +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned long' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_6]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_7]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p2]] +// CHECK-NEXT: {{^}}| |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}}| | |-MemberExpr {{.+}} .len +// CHECK-NEXT: {{^}}| | | `-MemberExpr {{.+}} ->hdr +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}}| | | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}}| | | | `-MemberExpr {{.+}} ->hdr +// CHECK-NEXT: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_2]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-ReturnStmt +// CHECK-NEXT: {{^}}| `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK-NEXT: {{^}}| | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct Outer *__single' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}}| | | | | `-MemberExpr {{.+}} ->fam +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct Outer *__single' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}}| | | | `-MemberExpr {{.+}} .len +// CHECK-NEXT: {{^}}| | | | `-MemberExpr {{.+}} ->hdr +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct Outer *__single' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_8]] +// CHECK-NEXT: {{^}}| | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}}| | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK-NEXT: {{^}}| `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct Outer *__single' + +struct Outer *foo2(int len) { + int * p2 = baz(len); + struct Outer * __single p = bar(len); + p->hdr.ptr = p2; + p->hdr.len = len; + return p; +} + +// CHECK-LABEL: foo2 +// CHECK-NEXT: {{^}} |-ParmVarDecl [[var_len_3:0x[^ ]+]] +// CHECK-NEXT: {{^}} `-CompoundStmt +// CHECK-NEXT: {{^}} |-DeclStmt +// CHECK-NEXT: {{^}} | `-VarDecl [[var_p2_1:0x[^ ]+]] +// CHECK-NEXT: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_10]] +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_9]] +// CHECK-NEXT: {{^}} | | `-CallExpr +// CHECK-NEXT: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)(*__single)(int)' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[func_baz]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}} |-DeclStmt +// CHECK-NEXT: {{^}} | `-VarDecl [[var_p_1:0x[^ ]+]] +// CHECK-NEXT: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BoundsCheckExpr {{.+}} 'p2 <= __builtin_get_pointer_upper_bound(p2) && __builtin_get_pointer_lower_bound(p2) <= p2 && len <= __builtin_get_pointer_upper_bound(p2) - p2 && 0 <= len' +// CHECK-NEXT: {{^}} | | |-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}} | | | `-PredefinedBoundsCheckExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}} | | | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}} | | | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | | | | | |-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'unsigned long' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | `-MemberExpr {{.+}} ->fam +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}} | | | |-GetBoundExpr {{.+}} upper +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}} | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_11]] +// CHECK-NEXT: {{^}} | | `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK-NEXT: {{^}} | | | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'struct Outer *' +// CHECK-NEXT: {{^}} | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: {{^}} | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: {{^}} | | | | | | `-ImplicitCastExpr {{.+}} 'struct Outer *' +// CHECK-NEXT: {{^}} | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}} | | | | | `-AssumptionExpr +// CHECK-NEXT: {{^}} | | | | | |-OpaqueValueExpr [[ove_14]] {{.*}} 'unsigned long' +// CHECK: {{^}} | | | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK-NEXT: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'unsigned long' +// CHECK: {{^}} | | | | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}} | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_13]] +// CHECK-NEXT: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK-NEXT: {{^}} | | | |-OpaqueValueExpr [[ove_12]] +// CHECK-NEXT: {{^}} | | | | `-CallExpr +// CHECK-NEXT: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'struct Outer *__single __sized_by(16UL + 4UL * len)(*__single)(int)' +// CHECK-NEXT: {{^}} | | | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_14]] +// CHECK-NEXT: {{^}} | | | `-BinaryOperator {{.+}} 'unsigned long' '+' +// CHECK-NEXT: {{^}} | | | |-IntegerLiteral {{.+}} 16 +// CHECK-NEXT: {{^}} | | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK-NEXT: {{^}} | | | |-IntegerLiteral {{.+}} 4 +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'struct Outer *__single __sized_by(16UL + 4UL * len)':'struct Outer *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'unsigned long' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_16]] +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_p2_1]] +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_15]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK-NEXT: {{^}} |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK-NEXT: {{^}} | |-MemberExpr {{.+}} .ptr +// CHECK-NEXT: {{^}} | | `-MemberExpr {{.+}} ->hdr +// CHECK-NEXT: {{^}} | | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}} | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK-NEXT: {{^}} | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BinaryOperator {{.+}} 'int' '=' +// CHECK-NEXT: {{^}} | | |-MemberExpr {{.+}} .len +// CHECK-NEXT: {{^}} | | | `-MemberExpr {{.+}} ->hdr +// CHECK-NEXT: {{^}} | | | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}} | | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK-NEXT: {{^}} | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_11]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}} `-ReturnStmt +// CHECK-NEXT: {{^}} `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}} | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK-NEXT: {{^}} | | |-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'struct Outer *__single' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: {{^}} | | | | `-MemberExpr {{.+}} ->fam +// CHECK-NEXT: {{^}} | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct Outer *__single' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: {{^}} | | | `-MemberExpr {{.+}} .len +// CHECK-NEXT: {{^}} | | | `-MemberExpr {{.+}} ->hdr +// CHECK-NEXT: {{^}} | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct Outer *__single' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_17]] +// CHECK-NEXT: {{^}} | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK-NEXT: {{^}} | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK-NEXT: {{^}} `-OpaqueValueExpr [[ove_17]] {{.*}} 'struct Outer *__single' + diff --git a/clang/test/BoundsSafety/AST/forward-declared-type.c b/clang/test/BoundsSafety/AST/forward-declared-type.c new file mode 100644 index 0000000000000..2abbea16c00f0 --- /dev/null +++ b/clang/test/BoundsSafety/AST/forward-declared-type.c @@ -0,0 +1,790 @@ + + +// RUN: %clang_cc1 -ast-dump -verify -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -x objective-c -ast-dump -verify -fbounds-safety -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +// CHECK: |-RecordDecl +struct unsized; + +// CHECK: |-FunctionDecl [[func_unsizedSizedByToSizedBy:0x[^ ]+]] {{.+}} unsizedSizedByToSizedBy +// CHECK: | |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +void unsizedSizedByToSizedBy(struct unsized * __sized_by(len) p, int len) { +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_size:0x[^ ]+]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | `-DependerDeclsAttr + int size = len; +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2:0x[^ ]+]] +// CHECK: | `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && size <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__bidi_indexable)p && 0 <= size' +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'long' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_size]] + struct unsized * __single __sized_by(size) p2 = p; +} + +// CHECK: |-RecordDecl +struct Bar { +// CHECK: | |-FieldDecl +// CHECK: | | `-DependerDeclsAttr + int size; +// CHECK: | `-FieldDecl + struct unsized * __single __sized_by(size) p; +}; + +// CHECK: |-FunctionDecl [[func_structMemberUnsizedSizedBy:0x[^ ]+]] {{.+}} structMemberUnsizedSizedBy +// CHECK: |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// expected-note@+2{{consider adding '__sized_by(len)' to 'p'}} +// expected-note@+1{{consider adding '__sized_by(10)' to 'p'}} +void structMemberUnsizedSizedBy(struct unsized * p, int len) { +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_bar:0x[^ ]+]] +// CHECK: | `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && 10 <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__single)p && 0 <= 10' +// CHECK: | |-InitListExpr +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'struct unsized *__single' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'struct unsized *__single' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct unsized *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct unsized *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct unsized *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct unsized *__single' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'struct unsized *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct unsized *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_1]] + struct Bar bar = { .size = 10, .p = p }; // expected-error{{initializing 'bar.p' of type 'struct unsized *__single __sized_by(size)' (aka 'struct unsized *__single') and size value of 10 with 'struct unsized *__single' and pointee of size 0 always fails}} +// CHECK: |-BinaryOperator {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' '=' +// CHECK: | |-MemberExpr {{.+}} .p +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'struct unsized *__single' + bar.p = p; // expected-warning{{size value is not statically known: assigning to 'struct unsized *__single __sized_by(size)' (aka 'struct unsized *__single') from 'struct unsized *__single' is invalid for any size greater than 0}} +// CHECK: |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | |-MemberExpr {{.+}} .size +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' + bar.size = len; // expected-note{{size assigned here}} + +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_bar2:0x[^ ]+]] +// CHECK: | `-BoundsCheckExpr {{.+}} 'bar.p <= __builtin_get_pointer_upper_bound(bar.p) && __builtin_get_pointer_lower_bound(bar.p) <= bar.p && len <= (char *)__builtin_get_pointer_upper_bound(bar.p) - (char *__bidi_indexable)bar.p && 0 <= len' +// CHECK: | |-InitListExpr +// CHECK: | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | `-OpaqueValueExpr [[ove_7]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} .size +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | `-MemberExpr {{.+}} .p +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_8]] {{.*}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' + struct Bar bar2 = { .size = len, .p = bar.p }; +// CHECK: |-BinaryOperator {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' '=' +// CHECK: | |-MemberExpr {{.+}} .p +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int' + bar2.p = bar.p; +// CHECK: `-BinaryOperator {{.+}} 'int' '=' +// CHECK: |-MemberExpr {{.+}} .size +// CHECK: | `-DeclRefExpr {{.+}} [[var_bar2]] +// CHECK: `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' + bar2.size = 10; +} + +// CHECK: |-FunctionDecl [[func_unsizedSizedByToSingle:0x[^ ]+]] {{.+}} unsizedSizedByToSingle +// CHECK: | |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +void unsizedSizedByToSingle(struct unsized * __sized_by(len) p, int len) { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_1:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct unsized *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_16]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: | | `-OpaqueValueExpr [[ove_17]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: | |-OpaqueValueExpr [[ove_16]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' + struct unsized * __single p2 = p; +} +// CHECK: |-RecordDecl +struct other; +// CHECK: |-FunctionDecl [[func_unsizedSingleToSingleTypecast:0x[^ ]+]] {{.+}} unsizedSingleToSingleTypecast +// CHECK: |-ParmVarDecl [[var_p_3:0x[^ ]+]] +void unsizedSingleToSingleTypecast(struct unsized * p) { +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_2:0x[^ ]+]] +// CHECK: `-ImplicitCastExpr {{.+}} 'struct other *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'struct unsized *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_p_3]] + struct other * __single p2 = p; // expected-warning{{incompatible pointer types initializing 'struct other *__single' with an expression of type 'struct unsized *__single'}} +} + +// CHECK: |-FunctionDecl [[func_unsizedSizedByToSizedByTypecast:0x[^ ]+]] {{.+}} unsizedSizedByToSizedByTypecast +// CHECK: |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_3:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +void unsizedSizedByToSizedByTypecast(struct unsized * __sized_by(len) p, int len) { +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK: | `-DependerDeclsAttr + int size = len; +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_3:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && size <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__bidi_indexable)p && 0 <= size' +// CHECK: |-ImplicitCastExpr {{.+}} 'struct other *__single __sized_by(size)':'struct other *__single' +// CHECK: | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct other *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct other *' +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'struct other *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_21]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove_18]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct other *__bidi_indexable' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_19]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_19]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_20]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK: | |-OpaqueValueExpr [[ove_19]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_21]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_size_1]] + struct other * __single __sized_by(size) p2 = p; // expected-warning{{incompatible pointer types initializing 'struct other *__single' with an expression of type 'struct unsized *__single __sized_by(len)' (aka 'struct unsized *__single')}} (This warning is redundant and missing __sized_by annotation rdar://112409995) + // expected-warning@-1{{incompatible pointer types initializing 'struct other *__single __sized_by(size)' (aka 'struct other *__single') with an expression of type 'struct unsized *__single __sized_by(len)' (aka 'struct unsized *__single')}} +} + +// CHECK: |-FunctionDecl [[func_voidSizedByToVoidSizedBy:0x[^ ]+]] {{.+}} voidSizedByToVoidSizedBy +// CHECK: |-ParmVarDecl [[var_p_5:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_4:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +void voidSizedByToVoidSizedBy(void * __sized_by(len) p, int len) { + int size = len; + void * __single __sized_by(size) p2 = p; +} + +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_size_2:0x[^ ]+]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_4]] +// CHECK: | `-DependerDeclsAttr +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_4:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && size <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__bidi_indexable)p && 0 <= size' +// CHECK: |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'int' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_25]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove_22]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_23]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_23]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: | | `-OpaqueValueExpr [[ove_24]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_4]] +// CHECK: | |-OpaqueValueExpr [[ove_23]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_25]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_size_2]] + +// CHECK: |-FunctionDecl [[func_unsizedBidiForgedNull:0x[^ ]+]] {{.+}} unsizedBidiForgedNull +void unsizedBidiForgedNull() { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_5:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 10 + struct unsized * __bidi_indexable p2 = __unsafe_forge_bidi_indexable(struct unsized *, 0, 10); +} + +// CHECK: |-FunctionDecl [[func_unsizedBidiForgedDyn:0x[^ ]+]] {{.+}} unsizedBidiForgedDyn +// CHECK: | |-ParmVarDecl [[var_p_6:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_5:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +void unsizedBidiForgedDyn(struct unsized * __sized_by(len) p, int len) { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_6:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_26]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | `-ParenExpr +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p_6]] +// CHECK: | | | `-OpaqueValueExpr [[ove_27]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_5]] +// CHECK: | | |-OpaqueValueExpr [[ove_26]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_5]] + struct unsized * __bidi_indexable p2 = __unsafe_forge_bidi_indexable(struct unsized *, p, len); +} + +// CHECK: |-FunctionDecl [[func_unsizedBidiForgedTypecast:0x[^ ]+]] {{.+}} unsizedBidiForgedTypecast +void unsizedBidiForgedTypecast() { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_7:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct other *__bidi_indexable' +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 10 + struct other * __bidi_indexable p2 = __unsafe_forge_bidi_indexable(struct unsized *, 0, 10); // expected-warning{{incompatible pointer types initializing 'struct other *__bidi_indexable' with an expression of type 'struct unsized *__bidi_indexable'}} +} + +// CHECK: |-FunctionDecl [[func_unsizedBidiForgedTypecastToInt:0x[^ ]+]] {{.+}} unsizedBidiForgedTypecastToInt +void unsizedBidiForgedTypecastToInt() { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_8:0x[^ ]+]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 10 + int * __bidi_indexable p2 = __unsafe_forge_bidi_indexable(struct unsized *, 0, 10); // expected-warning{{incompatible pointer types initializing 'int *__bidi_indexable' with an expression of type 'struct unsized *__bidi_indexable'}} +} + +// CHECK: |-FunctionDecl [[func_unsizedBidiForgedToSizedBy:0x[^ ]+]] {{.+}} unsizedBidiForgedToSizedBy +// CHECK: |-ParmVarDecl [[var_p_7:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_6:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +void unsizedBidiForgedToSizedBy(struct unsized * __sized_by(len) p, int len) { +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_size_3:0x[^ ]+]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK: | `-DependerDeclsAttr + int size = len; +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_9:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr {{.+}} '((struct unsized *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((p), (len))) <= __builtin_get_pointer_upper_bound(((struct unsized *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((p), (len)))) && __builtin_get_pointer_lower_bound(((struct unsized *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((p), (len)))) <= ((struct unsized *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((p), (len))) && size <= (char *)__builtin_get_pointer_upper_bound(((struct unsized *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((p), (len)))) - (char *__bidi_indexable)((struct unsized *__bidi_indexable)__builtin_unsafe_forge_bidi_indexable((p), (len))) && 0 <= size' +// CHECK: |-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(size)':'struct unsized *__single' +// CHECK: | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_30:0x[^ ]+]] {{.*}} 'int' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_31:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'struct unsized *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_31]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove_28]] +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_29]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_29]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | | | `-ParenExpr +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p_7]] +// CHECK: | | | `-OpaqueValueExpr [[ove_30]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK: | | |-OpaqueValueExpr [[ove_29]] {{.*}} 'struct unsized *__single __sized_by(len)':'struct unsized *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_30]] {{.*}} 'int' +// CHECK: | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK: `-OpaqueValueExpr [[ove_31]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_size_3]] + struct unsized * __single __sized_by(size) p2 = __unsafe_forge_bidi_indexable(struct unsized *, p, len); +} + +// CHECK: |-FunctionDecl [[func_unsizedSingleForgedNull:0x[^ ]+]] {{.+}} unsizedSingleForgedNull +void unsizedSingleForgedNull() { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_10:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__single' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ParenExpr +// CHECK: | | `-IntegerLiteral {{.+}} 0 + struct unsized * __single p2 = __unsafe_forge_single(struct unsized *, 0); +} + +// CHECK: |-FunctionDecl [[func_unsizedSingleForgedDyn:0x[^ ]+]] {{.+}} unsizedSingleForgedDyn +// CHECK: | |-ParmVarDecl [[var_p_8:0x[^ ]+]] +void unsizedSingleForgedDyn(int p) { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_11:0x[^ ]+]] +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__single' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p_8]] + struct unsized * __single p2 = __unsafe_forge_single(struct unsized *, p); +} + +// CHECK: |-FunctionDecl [[func_unsizedSingleForgedToBidi:0x[^ ]+]] {{.+}} unsizedSingleForgedToBidi +// CHECK: |-ParmVarDecl [[var_p_9:0x[^ ]+]] +void unsizedSingleForgedToBidi(int p) { +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_12:0x[^ ]+]] +// CHECK: `-RecoveryExpr +// CHECK: `-ParenExpr +// CHECK: `-CStyleCastExpr {{.+}} 'struct unsized *__single' +// CHECK: `-ForgePtrExpr +// CHECK: |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-ParenExpr +// CHECK: | `-DeclRefExpr {{.+}} [[var_p_9]] + // expected-note@+1{{pointer 'p2' declared here}} + struct unsized * __bidi_indexable p2 = __unsafe_forge_single(struct unsized *, p); // expected-error{{cannot initialize indexable pointer with type 'struct unsized *__bidi_indexable' from __single pointer to incomplete type 'struct unsized *__single'; consider declaring pointer 'p2' as '__single'}} +} + +// CHECK: |-FunctionDecl [[func_unsizedSingleForgedToSizedBy:0x[^ ]+]] {{.+}} unsizedSingleForgedToSizedBy +// CHECK: |-ParmVarDecl [[var_p_10:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_7:0x[^ ]+]] +void unsizedSingleForgedToSizedBy(int p, int len) { +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_size_4:0x[^ ]+]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: | `-DependerDeclsAttr + int size = len; // expected-note{{size initialized here}} +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p2_13:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr {{.+}} '(struct unsized *__single)__builtin_unsafe_forge_single((p)) <= __builtin_get_pointer_upper_bound((struct unsized *__single)__builtin_unsafe_forge_single((p))) && __builtin_get_pointer_lower_bound((struct unsized *__single)__builtin_unsafe_forge_single((p))) <= (struct unsized *__single)__builtin_unsafe_forge_single((p)) && size <= (char *)__builtin_get_pointer_upper_bound((struct unsized *__single)__builtin_unsafe_forge_single((p))) - (char *__single)(struct unsized *__single)__builtin_unsafe_forge_single((p)) && 0 <= size' +// CHECK: |-ParenExpr +// CHECK: | `-OpaqueValueExpr [[ove_32:0x[^ ]+]] {{.*}} 'struct unsized *__single' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_32]] {{.*}} 'struct unsized *__single' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'struct unsized *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'struct unsized *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'struct unsized *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_33:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct unsized *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'struct unsized *__single' +// CHECK: | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'struct unsized *__single' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_33]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove_32]] +// CHECK: | `-CStyleCastExpr {{.+}} 'struct unsized *__single' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p_10]] +// CHECK: `-OpaqueValueExpr [[ove_33]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_size_4]] + struct unsized * __single __sized_by(size) p2 = __unsafe_forge_single(struct unsized *, p); // expected-warning{{size value is not statically known: initializing 'p2' of type 'struct unsized *__single __sized_by(size)' (aka 'struct unsized *__single') with 'struct unsized *__single' is invalid for any size greater than 0}} +} + +// CHECK: |-FunctionDecl [[func_unsizedSingleForgedToBidiVoid:0x[^ ]+]] {{.+}} unsizedSingleForgedToBidiVoid +// CHECK: | |-ParmVarDecl [[var_p_11:0x[^ ]+]] +void unsizedSingleForgedToBidiVoid(int p) { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_14:0x[^ ]+]] +// CHECK: | `-RecoveryExpr +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'void *__single' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p_11]] + // expected-note@+1{{pointer 'p2' declared here}} + void * __bidi_indexable p2 = __unsafe_forge_single(void *, p); // expected-error{{cannot initialize indexable pointer with type 'void *__bidi_indexable' from __single pointer to incomplete type 'void *__single'; consider declaring pointer 'p2' as '__single'}} +} + +// CHECK: |-FunctionDecl [[func_unsizedSizedByToBidiVoid:0x[^ ]+]] {{.+}} unsizedSizedByToBidiVoid +// CHECK: | |-ParmVarDecl [[var_p_12:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_8:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +void unsizedSizedByToBidiVoid(void * __sized_by(len) p, int len) { +// CHECK: | `-CompoundStmt +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_p2_15:0x[^ ]+]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_34:0x[^ ]+]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_34]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_35:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_34]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_p_12]] +// CHECK: | | `-OpaqueValueExpr [[ove_35]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_len_8]] +// CHECK: | |-OpaqueValueExpr [[ove_34]] {{.*}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | `-OpaqueValueExpr [[ove_35]] {{.*}} 'int' + void * __bidi_indexable p2 = p; +} + +// CHECK: -FunctionDecl [[func_unsizedSingleToSizedByToBidiVoid:0x[^ ]+]] {{.+}} unsizedSingleToSizedByToBidiVoid +// CHECK: |-ParmVarDecl [[var_p_13:0x[^ ]+]] +void unsizedSingleToSizedByToBidiVoid(void * p) { // rdar://112462891 +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_p2_16:0x[^ ]+]] +// CHECK: | `-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && 0 <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__single)p && 0 <= 0' +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p_13]] +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_36:0x[^ ]+]] {{.*}} 'void *__single' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'void *__single' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'void *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-OpaqueValueExpr [[ove_37:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'void *__single' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_36]] {{.*}} 'void *__single' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_37]] {{.*}} 'long' +// CHECK: | |-OpaqueValueExpr [[ove_36]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p_13]] +// CHECK: | `-OpaqueValueExpr [[ove_37]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | `-IntegerLiteral {{.+}} 0 + void * __single __sized_by(0) p2 = p; +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p3:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_38:0x[^ ]+]] {{.*}} 'void *__single __sized_by(0)':'void *__single' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_38]] {{.*}} 'void *__single __sized_by(0)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_39:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_38]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__single __sized_by(0)':'void *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_p2_16]] +// CHECK: | `-OpaqueValueExpr [[ove_39]] +// CHECK: | `-IntegerLiteral {{.+}} 0 +// CHECK: |-OpaqueValueExpr [[ove_38]] {{.*}} 'void *__single __sized_by(0)':'void *__single' +// CHECK: `-OpaqueValueExpr [[ove_39]] {{.*}} 'int' + void * __bidi_indexable p3 = p2; +} + diff --git a/clang/test/BoundsSafety/AST/func-attributes.c b/clang/test/BoundsSafety/AST/func-attributes.c new file mode 100644 index 0000000000000..c7832239293d7 --- /dev/null +++ b/clang/test/BoundsSafety/AST/func-attributes.c @@ -0,0 +1,58 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// Functions + +// CHECK: FunctionDecl {{.+}} func_proto_void_cdecl 'void (void) __attribute__((cdecl))':'void (void)' +__attribute__((cdecl)) void func_proto_void_cdecl(void); + +// CHECK: FunctionDecl {{.+}} func_proto_void_noreturn 'void (void) __attribute__((noreturn))' +__attribute__((noreturn)) void func_proto_void_noreturn(void); + +// CHECK: FunctionDecl {{.+}} func_noproto_void_cdecl 'void () __attribute__((cdecl))':'void ()' +__attribute__((cdecl)) void func_noproto_void_cdecl(); + +// CHECK: FunctionDecl {{.+}} func_noproto_void_noreturn 'void () __attribute__((noreturn))' +__attribute__((noreturn)) void func_noproto_void_noreturn(); + +// CHECK: FunctionDecl {{.+}} func_proto_ret_cdecl 'void *__single(void) __attribute__((cdecl))':'void *__single(void)' +__attribute__((cdecl)) void *func_proto_ret_cdecl(void); + +// CHECK: FunctionDecl {{.+}} func_proto_ret_noreturn 'void *__single(void) __attribute__((noreturn))' +__attribute__((noreturn)) void *func_proto_ret_noreturn(void); + +// CHECK: FunctionDecl {{.+}} func_noproto_ret_cdecl 'void *__single() __attribute__((cdecl))':'void *__single()' +__attribute__((cdecl)) void *func_noproto_ret_cdecl(); + +// CHECK: FunctionDecl {{.+}} func_noproto_ret_noreturn 'void *__single() __attribute__((noreturn))' +__attribute__((noreturn)) void *func_noproto_ret_noreturn(); + +// Function pointers + +// CHECK: VarDecl {{.+}} fptr_proto_void_cdecl 'void ((*__single))(void) __attribute__((cdecl))' +__attribute__((cdecl)) void (*fptr_proto_void_cdecl)(void); + +// CHECK: VarDecl {{.+}} fptr_proto_void_noreturn 'void (*__single)(void) __attribute__((noreturn))' +__attribute__((noreturn)) void (*fptr_proto_void_noreturn)(void); + +// CHECK: VarDecl {{.+}} fptr_noproto_void_cdecl 'void ((*__single))() __attribute__((cdecl))' +__attribute__((cdecl)) void (*fptr_noproto_void_cdecl)(); + +// CHECK: VarDecl {{.+}} fptr_noproto_void_noreturn 'void (*__single)() __attribute__((noreturn))' +__attribute__((noreturn)) void (*fptr_noproto_void_noreturn)(); + +// CHECK: VarDecl {{.+}} fptr_proto_ret_cdecl 'void *__single((*__single))(void) __attribute__((cdecl))' +__attribute__((cdecl)) void *(*fptr_proto_ret_cdecl)(void); + +// CHECK: VarDecl {{.+}} fptr_proto_ret_noreturn 'void *__single(*__single)(void) __attribute__((noreturn))' +__attribute__((noreturn)) void *(*fptr_proto_ret_noreturn)(void); + +// CHECK: VarDecl {{.+}} fptr_noproto_ret_cdecl 'void *__single((*__single))() __attribute__((cdecl))' +__attribute__((cdecl)) void *(*fptr_noproto_ret_cdecl)(); + +// CHECK: VarDecl {{.+}} fptr_noproto_ret_noreturn 'void *__single(*__single)() __attribute__((noreturn))' +__attribute__((noreturn)) void *(*fptr_noproto_ret_noreturn)(); diff --git a/clang/test/BoundsSafety/AST/func-no-prototype-implicit-decl-unsafe.c b/clang/test/BoundsSafety/AST/func-no-prototype-implicit-decl-unsafe.c new file mode 100644 index 0000000000000..08eb4ce4ca8a1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/func-no-prototype-implicit-decl-unsafe.c @@ -0,0 +1,19 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -Wno-error=implicit-function-declaration %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -Wno-error=implicit-function-declaration -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +void call_undeclared_function(int *__counted_by(count) p, int count) { + int *a = p; + undeclared_function(a); +} +// CHECK:{{.*}}warning: call to undeclared function 'undeclared_function'; ISO C99 and later do not support implicit function declarations + +// CHECK-LABEL:`-FunctionDecl {{.*}} call_undeclared_function 'void (int *__single __counted_by(count), int)' +// CHECK: `-CallExpr {{.*}} 'int' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'int (*__single)()' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int ()' Function {{.*}} 'undeclared_function' 'int ()' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'a' 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/func-no-prototype-k-and-r-decl-unsafe.c b/clang/test/BoundsSafety/AST/func-no-prototype-k-and-r-decl-unsafe.c new file mode 100644 index 0000000000000..d286f11f74d8c --- /dev/null +++ b/clang/test/BoundsSafety/AST/func-no-prototype-k-and-r-decl-unsafe.c @@ -0,0 +1,43 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +// Deliberately use `__indexable` as an attribute +// because nothing else in this program uses it. +// That means when check for a cast to this pointer +// attribute in `call_has_no_prototype_k_and_r` that +// we can be sure it is being done because of the attribute +// in this function declaration and nothing else. +int *has_no_prototype_k_and_r(a) +int *__indexable a; +{ + return a; +} + +void call_has_no_prototype_k_and_r(int *__counted_by(count) p, int count) { + int *a = p; + has_no_prototype_k_and_r(a); +} + +// CHECK-LABEL:|-FunctionDecl {{.*}} has_no_prototype_k_and_r 'int *__single(int *__indexable)' +// CHECK-NEXT:| |-ParmVarDecl {{.*}} a 'int *__indexable' + +// This behavior is a little surprising. `has_no_prototype_k_and_r` does not +// have a prototype so one might expect the default argument promotion to apply. +// However, that's not what clang does because in `Sema::BuildResolvedCallExpr` +// although it figures out there's no prototype it tries looking for it +// elsewhere and thinks it finds one so it doesn't do default argument promotion. +// While this might be a violation of the C standard it means we end up casting +// to the actual types on the function which means we use the correct -fbounds-safety +// attributes. So this likely bug in Clang is actually good for BoundsSafety +// because it means there won't be an ABI mismatch. + +// CHECK-LABEL:`-FunctionDecl {{.*}} call_has_no_prototype_k_and_r 'void (int *__single __counted_by(count), int)' +// CHECK: `-CallExpr {{.*}} 'int *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'int *__single(*__single)()' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int *__single()' Function {{.*}} 'has_no_prototype_k_and_r' 'int *__single(int *__indexable)' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'a' 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/func-no-prototype-unsafe-array-ptr-decay.c b/clang/test/BoundsSafety/AST/func-no-prototype-unsafe-array-ptr-decay.c new file mode 100644 index 0000000000000..b1ee6fccfd475 --- /dev/null +++ b/clang/test/BoundsSafety/AST/func-no-prototype-unsafe-array-ptr-decay.c @@ -0,0 +1,26 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +void has_no_prototype(); + +void call_has_no_prototype() { + int buffer[5] = {0}; + return has_no_prototype(buffer); +} + +// CHECK-LABEL:|-FunctionDecl {{.*}} used has_no_prototype 'void ()' +// CHECK-LABEL:`-FunctionDecl {{.*}} call_has_no_prototype 'void ()' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} used buffer 'int[5]' cinit +// ... +// CHECK: `-ReturnStmt {{.*}} +// CHECK-NEXT: `-CallExpr {{.*}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'void (*__single)()' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'void ()' Function {{.*}} 'has_no_prototype' 'void ()' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}}'int *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int[5]' lvalue Var {{.*}} 'buffer' 'int[5]' diff --git a/clang/test/BoundsSafety/AST/func-no-prototype-unsafe.c b/clang/test/BoundsSafety/AST/func-no-prototype-unsafe.c new file mode 100644 index 0000000000000..85f6d3f0193fe --- /dev/null +++ b/clang/test/BoundsSafety/AST/func-no-prototype-unsafe.c @@ -0,0 +1,22 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +void has_no_prototype(); + +void call_has_no_prototype(int *__counted_by(count) p, int count) { + int *a = p; + has_no_prototype(a); +} + +// CHECK:|-FunctionDecl {{.*}} has_no_prototype 'void ()' + +// CHECK-LABEL:`-FunctionDecl {{.*}} call_has_no_prototype 'void (int *__single __counted_by(count), int)' +// CHECK: `-CallExpr {{.*}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'void (*__single)()' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'void ()' Function {{.*}} 'has_no_prototype' 'void ()' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'a' 'int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/get-pointer-bound.c b/clang/test/BoundsSafety/AST/get-pointer-bound.c new file mode 100644 index 0000000000000..1ace6e88c0f57 --- /dev/null +++ b/clang/test/BoundsSafety/AST/get-pointer-bound.c @@ -0,0 +1,80 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +void bidi_indexable(int *__bidi_indexable p) { + int *q = __builtin_get_pointer_lower_bound(p); + int *r = __builtin_get_pointer_upper_bound(p); +} + +// CHECK: | |-DeclStmt {{.+}} +// CHECK: | | `-VarDecl {{.+}} q 'int *__bidi_indexable' cinit +// CHECK: | | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' lower +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'p' 'int *__bidi_indexable' +// CHECK: | `-DeclStmt {{.+}} +// CHECK: | `-VarDecl {{.+}} r 'int *__bidi_indexable' cinit +// CHECK: | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' upper +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'p' 'int *__bidi_indexable' + +void fwd_indexable(int *__indexable p) { + int *q = __builtin_get_pointer_lower_bound(p); + int *r = __builtin_get_pointer_upper_bound(p); +} + +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} q 'int *__bidi_indexable' cinit +// CHECK: | | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' lower +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK: | | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl {{.+}} r 'int *__bidi_indexable' cinit +// CHECK: | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' upper +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + +void single(int *__single p) { + int *q = __builtin_get_pointer_lower_bound(p); + int *r = __builtin_get_pointer_upper_bound(p); +} + +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} q 'int *__bidi_indexable' cinit +// CHECK: | | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' lower +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | | `-DeclRefExpr {{.+}} 'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single' +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl {{.+}} r 'int *__bidi_indexable' cinit +// CHECK: | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' upper +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__single' lvalue ParmVar {{.+}} 'p' 'int *__single' + +void array(void) { + int array[10]; + int *q = __builtin_get_pointer_lower_bound(array); + int *r = __builtin_get_pointer_upper_bound(array); +} + +// CHECK: |-FunctionDecl {{.+}} array 'void (void)' +// CHECK: | `-CompoundStmt {{.+}} +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} used array 'int[10]' +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} q 'int *__bidi_indexable' cinit +// CHECK: | | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' lower +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} 'int[10]' lvalue Var {{.+}} 'array' 'int[10]' +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl {{.+}} col:10 r 'int *__bidi_indexable' cinit +// CHECK: | `-GetBoundExpr {{.+}} 'int *__bidi_indexable' upper +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'int[10]' lvalue Var {{.+}} 'array' 'int[10]' + +int endOfFile; diff --git a/clang/test/BoundsSafety/AST/implicit-cast-attributes-and-storage-type.c b/clang/test/BoundsSafety/AST/implicit-cast-attributes-and-storage-type.c new file mode 100644 index 0000000000000..ed1b68b3b2b87 --- /dev/null +++ b/clang/test/BoundsSafety/AST/implicit-cast-attributes-and-storage-type.c @@ -0,0 +1,34 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +#include + +extern int *__single extern_ptr; + +void foo(void) { + // Silence these expected warnings so they don't show up in AST dump. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincompatible-pointer-types" +#pragma clang diagnostic ignored "-Wbounds-attributes-implicit-conversion-single-to-explicit-indexable" + char *__bidi_indexable b = extern_ptr; +#pragma clang diagnostic pop +} + +// This test exists to make sure that the implementation of the +// "-Wbounds-attributes-implicit-conversion-single-to-explicit-indexable" +// warning doesn't break the requirement that the above implicit conversion is +// split into a BitCast then BoundsSafetyPointerCast. + +// CHECK:|-VarDecl {{.*}} used extern_ptr 'int *__single' extern +// CHECK-NEXT:`-FunctionDecl {{.*}} foo 'void (void)' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: `-DeclStmt {{.*}} +// CHECK-NEXT: `-VarDecl {{.*}} b 'char *__bidi_indexable' cinit + +// Make sure the `(int* __single) -> (char* __bidi_indexable) gets split into two +// separate implicit casts. +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'char *__bidi_indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'char *__single' + +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int *__single' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'extern_ptr' 'int *__single' diff --git a/clang/test/BoundsSafety/AST/implicit-thin-decls.c b/clang/test/BoundsSafety/AST/implicit-thin-decls.c new file mode 100644 index 0000000000000..95d0938490284 --- /dev/null +++ b/clang/test/BoundsSafety/AST/implicit-thin-decls.c @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +int* foo(int *); +int* bar(int *parm); +int* baz(int *param) {} + +struct S { + int *i; + long *l; +}; + +typedef struct { + long *l; + void *v; +} T; + +// CHECK: |-FunctionDecl {{.+}} foo 'int *__single(int *__single)' +// CHECK: | `-ParmVarDecl {{.+}} 'int *__single' +// CHECK: |-FunctionDecl {{.+}} bar 'int *__single(int *__single)' +// CHECK: | `-ParmVarDecl {{.+}} parm 'int *__single' +// CHECK: |-FunctionDecl {{.+}} baz 'int *__single(int *__single)' +// CHECK: | |-ParmVarDecl {{.+}} param 'int *__single' +// CHECK: | `-CompoundStmt +// CHECK: |-RecordDecl {{.+}} struct S definition +// CHECK: | |-FieldDecl {{.+}} i 'int *__single' +// CHECK: | `-FieldDecl {{.+}} l 'long *__single' +// CHECK: |-RecordDecl [[ADDR:0x[a-z0-9]+]] {{.+}} struct definition +// CHECK: | |-FieldDecl {{.+}} l 'long *__single' +// CHECK: | `-FieldDecl {{.+}} v 'void *__single' +// CHECK: `-TypedefDecl {{.+}} T 'struct T':'T' +// CHECK: `-ElaboratedType {{.+}} 'struct T' sugar +// CHECK: `-RecordType {{.+}} 'T' +// CHECK: `-Record [[ADDR]] {{.+}} diff --git a/clang/test/BoundsSafety/AST/incomplete-single-to-indexable-bitcast.c b/clang/test/BoundsSafety/AST/incomplete-single-to-indexable-bitcast.c new file mode 100644 index 0000000000000..bcc4a5cf7eaa5 --- /dev/null +++ b/clang/test/BoundsSafety/AST/incomplete-single-to-indexable-bitcast.c @@ -0,0 +1,30 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s + +#include + +#define NULL ((void *__single)0) + +// CHECK-LABEL: test_null_to_bidi 'void ()' +// CHECK: CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} impl_bidi_ptr 'int *__bidi_indexable' cinit +// CHECK: | `-CStyleCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.*}} 'int *__single' part_of_explicit_cast +// CHECK: | `-ParenExpr {{.*}} 'void *__single' +// CHECK: | `-CStyleCastExpr {{.*}} 'void *__single' +// CHECK: | `-IntegerLiteral {{.*}} 'int' 0 +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} impl_bidi_ptr2 'int *__bidi_indexable' cinit +// CHECK: `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK: `-ImplicitCastExpr {{.*}} 'int *__single' +// CHECK: `-CStyleCastExpr {{.*}} 'void *__single' +// CHECK: `-CStyleCastExpr {{.*}} 'char *__single' +// CHECK: `-ParenExpr {{.*}} 'void *__single' +// CHECK: `-CStyleCastExpr {{.*}} 'void *__single' +// CHECK: `-IntegerLiteral {{.*}} 'int' 0 +void test_null_to_bidi() { + int *impl_bidi_ptr = (int *__bidi_indexable)NULL; + int *impl_bidi_ptr2 = (void *)(char *)NULL; +} diff --git a/clang/test/BoundsSafety/AST/indexable-casts.c b/clang/test/BoundsSafety/AST/indexable-casts.c new file mode 100644 index 0000000000000..e5ada2531e5ee --- /dev/null +++ b/clang/test/BoundsSafety/AST/indexable-casts.c @@ -0,0 +1,33 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O2 %s -o /dev/null + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O2 %s -o /dev/null + +#include + +int foo() { + int *__indexable p; + p = (int *__indexable)(int *__bidi_indexable)p; + char *__bidi_indexable cp = (char *__bidi_indexable)p; + return 0; +} + +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used p 'int *__indexable' +// CHECK: |-BinaryOperator {{.*}} 'int *__indexable' '=' +// CHECK: | |-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'p' 'int *__indexable' +// CHECK: | `-CStyleCastExpr {{.*}} 'int *__indexable' +// CHECK: | `-CStyleCastExpr {{.*}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' part_of_explicit_cast +// CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'p' 'int *__indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} cp 'char *__bidi_indexable' cinit +// CHECK: | `-CStyleCastExpr {{.*}} 'char *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.*}} 'char *__indexable' part_of_explicit_cast +// CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' part_of_explicit_cast +// CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'p' 'int *__indexable' diff --git a/clang/test/BoundsSafety/AST/indexable-ptr-arith.c b/clang/test/BoundsSafety/AST/indexable-ptr-arith.c new file mode 100644 index 0000000000000..c3fb3655cd0fc --- /dev/null +++ b/clang/test/BoundsSafety/AST/indexable-ptr-arith.c @@ -0,0 +1,90 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// Unary operator should NOT convert __indexable to __bidi_indexable. +void unop(int *__indexable p) { + // CHECK: UnaryOperator {{.+}} 'int *__indexable' postfix '++' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + p++; + + // CHECK: UnaryOperator {{.+}} 'int *__indexable' prefix '++' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + ++p; +} + +// Binary operator should convert __indexable to __bidi_indexable. +void binop(int *__indexable p, int index) { + // CHECK: BinaryOperator {{.+}} 'int *__bidi_indexable' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + (void)(p + index); + + // CHECK: BinaryOperator {{.+}} 'int *__bidi_indexable' '+' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + (void)(index + p); + + // CHECK: BinaryOperator {{.+}} 'int *__bidi_indexable' '-' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + (void)(p - index); + + // CHECK: BinaryOperator {{.+}} 'long' '-' + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *' + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + (void)(p - p); +} + +// Pointer arithmetic with assignment should NOT convert __indexable to +// __bidi_indexable. +void assign(int *__indexable p, int index) { + (void)p; + + // CHECK: CompoundAssignOperator {{.+}} 'int *__indexable' '+=' ComputeLHSTy='int *__indexable' ComputeResultTy='int *__indexable' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + p += index; + + // CHECK: CompoundAssignOperator {{.+}} 'int *__indexable' '-=' ComputeLHSTy='int *__indexable' ComputeResultTy='int *__indexable' + // CHECK-NEXT: |-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + p -= index; +} + +// Array subscript should convert __indexable to __bidi_indexable. +void array_subscript(int *__indexable p, int index) { + // CHECK: ArraySubscriptExpr {{.+}} 'int' lvalue + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + (void)&p[index]; + + // CHECK: ArraySubscriptExpr {{.+}} 'int' lvalue + // CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int' + // CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'index' 'int' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'p' 'int *__indexable' + (void)&index[p]; +} diff --git a/clang/test/BoundsSafety/AST/init-struct-const-count-new-checks-disabled.c b/clang/test/BoundsSafety/AST/init-struct-const-count-new-checks-disabled.c new file mode 100644 index 0000000000000..5d299ea487990 --- /dev/null +++ b/clang/test/BoundsSafety/AST/init-struct-const-count-new-checks-disabled.c @@ -0,0 +1,1803 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py +// RUN: %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -ast-dump %s 2>&1 | FileCheck %s +#include + +// expected-no-diagnostics + +// ============================================================================= +// __counted_by +// ============================================================================= + +struct cb { + const int count; + int* __counted_by(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cb 'void (struct cb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cb' +void consume_cb(struct cb); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cb 'void (int, int *__single __counted_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void init_list_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cb_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void init_list_cb_bidi(int count_param, int* __bidi_indexable ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cb 'void (int, int *__single __counted_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void compound_literal_init_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cb_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void compound_literal_init_cb_bidi(int count_param, int*__bidi_indexable ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// ============================================================================= +// __counted_by_or_null +// ============================================================================= + +struct cbon { + const int count; + int* __counted_by_or_null(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cbon 'void (struct cbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cbon' +void consume_cbon(struct cbon); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cbon 'void (int, int *__single __counted_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void init_list_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cbon_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void init_list_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cbon 'void (int, int *__single __counted_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void compound_literal_init_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cbon_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void compound_literal_init_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// ============================================================================= +// __sized_by +// ============================================================================= + +struct sb { + const int count; + char* __sized_by(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sb 'void (struct sb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sb' +void consume_sb(struct sb); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_sb 'void (int, char *__single __sized_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void init_list_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void init_list_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_sb 'void (int, char *__single __sized_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void compound_literal_init_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_sb_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void compound_literal_init_sb_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// ============================================================================= +// __sized_by_or_null +// ============================================================================= + +struct sbon { + const int count; + char* __sized_by_or_null(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sbon 'void (struct sbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sbon' +void consume_sbon(struct sbon); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_sbon 'void (int, char *__single __sized_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void init_list_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_sbon_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void init_list_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_sbon 'void (int, char *__single __sized_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void compound_literal_init_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} compound_literal_init_sbon_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: |-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void compound_literal_init_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} diff --git a/clang/test/BoundsSafety/AST/init-struct-const-count-new-checks-enabled.c b/clang/test/BoundsSafety/AST/init-struct-const-count-new-checks-enabled.c new file mode 100644 index 0000000000000..8d2b695835002 --- /dev/null +++ b/clang/test/BoundsSafety/AST/init-struct-const-count-new-checks-enabled.c @@ -0,0 +1,2973 @@ +// FileCheck lines automatically generated using make-ast-dump-check-v2.py +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -ast-dump %s 2>&1 | FileCheck %s +#include + +// expected-no-diagnostics + +// ============================================================================= +// __counted_by +// ============================================================================= + +struct cb { + const int count; + int* __counted_by(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cb 'void (struct cb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cb' +void consume_cb(struct cb); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cb 'void (int, int *__single __counted_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void init_list_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cb_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void init_list_cb_bidi(int count_param, int* __bidi_indexable ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cb 'void (int, int *__single __counted_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void compound_literal_init_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cb_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cb)' Function {{.+}} 'consume_cb' 'void (struct cb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cb' lvalue Var {{.+}} 'c' 'struct cb' +void compound_literal_init_cb_bidi(int count_param, int*__bidi_indexable ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// ============================================================================= +// __counted_by_or_null +// ============================================================================= + +struct cbon { + const int count; + int* __counted_by_or_null(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_cbon 'void (struct cbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct cbon' +void consume_cbon(struct cbon); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cbon 'void (int, int *__single __counted_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void init_list_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_cbon_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void init_list_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cbon 'void (int, int *__single __counted_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__single __counted_by_or_null(count_param)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __counted_by_or_null(count_param)':'int *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void compound_literal_init_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_cbon_bidi 'void (int, int *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'int *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct cbon' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct cbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct cbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= __builtin_get_pointer_upper_bound(ptr) - ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(count)':'int *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'int *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct cbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct cbon)' Function {{.+}} 'consume_cbon' 'void (struct cbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct cbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct cbon' lvalue Var {{.+}} 'c' 'struct cbon' +void compound_literal_init_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// ============================================================================= +// __sized_by +// ============================================================================= + +struct sb { + const int count; + char* __sized_by(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sb 'void (struct sb)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sb' +void consume_sb(struct sb); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_sb 'void (int, char *__single __sized_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void init_list_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void init_list_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_sb 'void (int, char *__single __sized_by(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void compound_literal_init_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_sb_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sb' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sb' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sb' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sb' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sb)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sb)' Function {{.+}} 'consume_sb' 'void (struct sb)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sb' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sb' lvalue Var {{.+}} 'c' 'struct sb' +void compound_literal_init_sb_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// ============================================================================= +// __sized_by_or_null +// ============================================================================= + +struct sbon { + const int count; + char* __sized_by_or_null(count) ptr; +}; + +// CHECK-LABEL:|-FunctionDecl {{.+}} used consume_sbon 'void (struct sbon)' +// CHECK-NEXT: | `-ParmVarDecl {{.+}} 'struct sbon' +void consume_sbon(struct sbon); + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_sbon 'void (int, char *__single __sized_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void init_list_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} init_list_sbon_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void init_list_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// CHECK-LABEL:|-FunctionDecl {{.+}} compound_literal_init_sbon 'void (int, char *__single __sized_by_or_null(count_param))' +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: | | `-DependerDeclsAttr {{.+}} <> Implicit {{.+}} 0 +// CHECK-NEXT: | |-ParmVarDecl {{.+}} used ptr 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | `-CompoundStmt {{.+}} +// CHECK-NEXT: | |-DeclStmt {{.+}} +// CHECK-NEXT: | | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-<<>> +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | | `-<<>> +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | | | `-<<>> +// CHECK-NEXT: | | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} <, line:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | |-MaterializeSequenceExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK-NEXT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | | `-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | | `-<<>> +// CHECK-NEXT: | | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__single __sized_by_or_null(count_param)':'char *__single' lvalue ParmVar {{.+}} 'ptr' 'char *__single __sized_by_or_null(count_param)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-CallExpr {{.+}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void compound_literal_init_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// CHECK-LABEL:`-FunctionDecl {{.+}} compound_literal_init_sbon_bidi 'void (int, char *__bidi_indexable)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used count_param 'int' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used ptr 'char *__bidi_indexable' +// CHECK-NEXT: `-CompoundStmt {{.+}} +// CHECK-NEXT: |-DeclStmt {{.+}} +// CHECK-NEXT: | `-VarDecl {{.+}} used c 'struct sbon' cinit +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | `-CompoundLiteralExpr {{.+}} 'struct sbon' lvalue +// CHECK-NEXT: | `-BoundsCheckExpr {{.+}} 'struct sbon' 'ptr <= __builtin_get_pointer_upper_bound(ptr) && __builtin_get_pointer_lower_bound(ptr) <= ptr && !ptr || count_param <= (char *)__builtin_get_pointer_upper_bound(ptr) - (char *__bidi_indexable)ptr && 0 <= count_param' +// CHECK-NEXT: | |-InitListExpr {{.+}} 'struct sbon' +// CHECK-NEXT: | | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__single __sized_by_or_null(count)':'char *__single' +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-GetBoundExpr {{.+}} 'char *' lower +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: | | |-UnaryOperator {{.+}} 'int' prefix '!' cannot overflow +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | | `-GetBoundExpr {{.+}} 'char *' upper +// CHECK-NEXT: | | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK-NEXT: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | | | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: | | `-BinaryOperator {{.+}} <, col:{{.+}}> 'int' '<=' +// CHECK-NEXT: | | |-IntegerLiteral {{.+}} <> 'int' 0 +// CHECK-NEXT: | | `-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | |-OpaqueValueExpr {{.+}} 'int' +// CHECK-NEXT: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK-NEXT: | | `-DeclRefExpr {{.+}} 'int' lvalue ParmVar {{.+}} 'count_param' 'int' +// CHECK-NEXT: | `-OpaqueValueExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'char *__bidi_indexable' lvalue ParmVar {{.+}} 'ptr' 'char *__bidi_indexable' +// CHECK-NEXT: `-CallExpr {{.+}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'void (*__single)(struct sbon)' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'void (struct sbon)' Function {{.+}} 'consume_sbon' 'void (struct sbon)' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'struct sbon' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'struct sbon' lvalue Var {{.+}} 'c' 'struct sbon' +void compound_literal_init_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} diff --git a/clang/test/BoundsSafety/AST/inline-asm-external-bounds.c b/clang/test/BoundsSafety/AST/inline-asm-external-bounds.c new file mode 100644 index 0000000000000..0fbaaec7d4007 --- /dev/null +++ b/clang/test/BoundsSafety/AST/inline-asm-external-bounds.c @@ -0,0 +1,17 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +void Test() { + __asm__ ("test %0" ::"r" (("beef"))); +} +// CHECK-LABEL: Test 'void ()' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: `-GCCAsmStmt {{.*}} +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'char *' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'char *__bidi_indexable' +// CHECK-NEXT: `-ParenExpr {{.*}} 'char[5]' lvalue +// CHECK-NEXT: `-StringLiteral {{.*}} 'char[5]' lvalue "beef" diff --git a/clang/test/BoundsSafety/AST/macro-qualified-type.c b/clang/test/BoundsSafety/AST/macro-qualified-type.c new file mode 100644 index 0000000000000..5304b20e10059 --- /dev/null +++ b/clang/test/BoundsSafety/AST/macro-qualified-type.c @@ -0,0 +1,19 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +#define my_unused __attribute__((__unused__)) + +// CHECK: FunctionDecl {{.+}} foo 'void (my_unused int *__single)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} p 'my_unused int *__single':'int *__single' +// CHECK-NEXT: | `-UnusedAttr {{.+}} unused +// CHECK-NEXT: `-CompoundStmt +// CHECK-NEXT: `-DeclStmt +// CHECK-NEXT: `-VarDecl {{.+}} local 'my_unused int *__bidi_indexable':'int *__bidi_indexable' +// CHECK-NEXT: `-UnusedAttr {{.+}} unused +void foo(int *_Nullable p my_unused) { + int *_Nullable local my_unused; +} diff --git a/clang/test/BoundsSafety/AST/materialize-unsafe-forge-ptr.c b/clang/test/BoundsSafety/AST/materialize-unsafe-forge-ptr.c new file mode 100644 index 0000000000000..6ff47359f43f4 --- /dev/null +++ b/clang/test/BoundsSafety/AST/materialize-unsafe-forge-ptr.c @@ -0,0 +1,59 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +void *g; + +void test() { + const void *__sized_by(4) ptr = __unsafe_forge_bidi_indexable(const void *, g, 4); +} + +// CHECK: TranslationUnitDecl +// CHECK: |-VarDecl [[var_g:0x[^ ]+]] +// CHECK: `-FunctionDecl [[func_test:0x[^ ]+]] {{.+}} test +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_ptr:0x[^ ]+]] +// CHECK: `-BoundsCheckExpr +// CHECK: |-ImplicitCastExpr {{.+}} 'const void *__single __sized_by(4)':'const void *__single' +// CHECK: | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'const void *__bidi_indexable' +// CHECK: |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'const void *__bidi_indexable' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'const void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'const void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const void *' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'const void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | |-CStyleCastExpr {{.+}} 'const char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'const void *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | `-CStyleCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'const void *__bidi_indexable' +// CHECK: | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 0 +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// CHECK: |-OpaqueValueExpr [[ove]] +// CHECK: | `-ParenExpr +// CHECK: | `-CStyleCastExpr {{.+}} 'const void *__bidi_indexable' +// CHECK: | `-ForgePtrExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *__single' +// CHECK: | | `-ParenExpr +// CHECK: | | `-DeclRefExpr {{.+}} [[var_g]] +// CHECK: | `-ParenExpr +// CHECK: | `-IntegerLiteral {{.+}} 4 +// CHECK: `-OpaqueValueExpr [[ove_1]] +// CHECK: `-ImplicitCastExpr {{.+}} 'long' +// CHECK: `-IntegerLiteral {{.+}} 4 diff --git a/clang/test/BoundsSafety/AST/member-access-with-base-side-effects.c b/clang/test/BoundsSafety/AST/member-access-with-base-side-effects.c new file mode 100644 index 0000000000000..ee718e6f8aef0 --- /dev/null +++ b/clang/test/BoundsSafety/AST/member-access-with-base-side-effects.c @@ -0,0 +1,46 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct foo { + int *__counted_by(count) p; + int count; +}; + +struct foo *bar(void); +// CHECK: |-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar + +void baz(void) { + (void)bar()->p; +} + +// CHECK-LABEL: baz +// CHECK: `-CompoundStmt +// CHECK: `-CStyleCastExpr {{.+}} 'void' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct foo *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-CallExpr +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct foo *__single(*__single)(void)' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK: | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-MemberExpr {{.+}} ->count +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct foo *__single' +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | `-MemberExpr {{.+}} ->p +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct foo *__single' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct foo *__single' +// CHECK: |-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' diff --git a/clang/test/BoundsSafety/AST/mock-typedef-header.h b/clang/test/BoundsSafety/AST/mock-typedef-header.h new file mode 100644 index 0000000000000..c4fa48d5190ae --- /dev/null +++ b/clang/test/BoundsSafety/AST/mock-typedef-header.h @@ -0,0 +1,8 @@ +#ifndef __MOCK_TYPEDEF_HEADER_H__ +#define __MOCK_TYPEDEF_HEADER_H__ +#pragma clang system_header + +typedef int * siptr_t; +typedef int ** siptr_ptr_t; + +#endif diff --git a/clang/test/BoundsSafety/AST/multi-level-attr-decls.c b/clang/test/BoundsSafety/AST/multi-level-attr-decls.c new file mode 100644 index 0000000000000..eee6bc9bf686a --- /dev/null +++ b/clang/test/BoundsSafety/AST/multi-level-attr-decls.c @@ -0,0 +1,22 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct Foo { + int *__bidi_indexable *__single ptrBoundPtrThin; + // CHECK: FieldDecl {{.+}} ptrBoundPtrThin 'int *__bidi_indexable*__single' +}; + +typedef struct Foo Foo; + +Foo *__bidi_indexable *__single Test (Foo *__single *__bidi_indexable argFooPtrThinPtrBound) { + Foo *__single *__bidi_indexable localFooPtrThinPtrBound = argFooPtrThinPtrBound; + Foo *__bidi_indexable *__single Res; + return Res; +} +// CHECK: FunctionDecl {{.+}} Test 'Foo *__bidi_indexable*__single(Foo *__single*__bidi_indexable)' +// CHECK: VarDecl {{.+}} localFooPtrThinPtrBound 'Foo *__single*__bidi_indexable' cinit +// CHECK: VarDecl {{.+}} Res 'Foo *__bidi_indexable*__single' diff --git a/clang/test/BoundsSafety/AST/multiple-dependees.c b/clang/test/BoundsSafety/AST/multiple-dependees.c new file mode 100644 index 0000000000000..7131d16ad0916 --- /dev/null +++ b/clang/test/BoundsSafety/AST/multiple-dependees.c @@ -0,0 +1,23 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fexperimental-bounds-safety-attributes -x c %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fexperimental-bounds-safety-attributes -x c++ %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fexperimental-bounds-safety-attributes -x objective-c %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fexperimental-bounds-safety-attributes -x objective-c++ %s 2>&1 | FileCheck %s + +#include + +struct T { + int cnt1; + int cnt2; + int *__counted_by(cnt1 * 3 + cnt2 + 2) ptr; +}; + +// CHECK: RecordDecl {{.*}} struct T definition +// CHECK: |-FieldDecl {{.*}} referenced cnt1 'int' +// CHECK: | `-DependerDeclsAttr {{.*}} Implicit [[FIELD_PTR:0x[0-9a-f]+]] 0 +// CHECK: |-FieldDecl {{.*}} referenced cnt2 'int' +// CHECK: | `-DependerDeclsAttr {{.*}} Implicit [[FIELD_PTR]] 0 +// CHECK: `-FieldDecl [[FIELD_PTR]] {{.*}} ptr 'int *{{.*}}__counted_by(cnt1 * 3 + cnt2 + 2)':'int *{{.*}}' diff --git a/clang/test/BoundsSafety/AST/neon-builtin-bounds-safety-cast.c b/clang/test/BoundsSafety/AST/neon-builtin-bounds-safety-cast.c new file mode 100644 index 0000000000000..d59142a147613 --- /dev/null +++ b/clang/test/BoundsSafety/AST/neon-builtin-bounds-safety-cast.c @@ -0,0 +1,30 @@ + + +// RUN: %clang_cc1 -triple armv7k -target-feature +neon -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -triple armv7k -target-feature +neon -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s +#include + +// CHECK-LABEL: test +void test() { + float sign[64]; + float32x4x4_t r; + __builtin_neon_vld4q_v(&r, sign, 41); +} + +// CHECK: CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used sign 'float[64]' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} used r 'float32x4x4_t':'struct float32x4x4_t' +// CHECK: `-CallExpr {{.*}} 'void' +// CHECK: |-ImplicitCastExpr {{.*}} 'void (*)(void *, const void *, int)' +// CHECK: | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_neon_vld4q_v' 'void (void *, const void *, int)' +// CHECK: |-ImplicitCastExpr {{.*}} 'void *' +// CHECK: | `-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' +// CHECK: | `-UnaryOperator {{.*}} 'float32x4x4_t *__bidi_indexable' prefix '&' cannot overflow +// CHECK: | `-DeclRefExpr {{.*}} 'float32x4x4_t':'struct float32x4x4_t' lvalue Var {{.*}} 'r' 'float32x4x4_t':'struct float32x4x4_t' +// CHECK: |-ImplicitCastExpr {{.*}} 'const void *' +// CHECK: | `-ImplicitCastExpr {{.*}} 'const void *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.*}} 'float[64]' lvalue Var {{.*}} 'sign' 'float[64]' +// CHECK: `-IntegerLiteral {{.*}} 'int' 41 diff --git a/clang/test/BoundsSafety/AST/nested-struct-member-count.c b/clang/test/BoundsSafety/AST/nested-struct-member-count.c new file mode 100644 index 0000000000000..59b05d80d64b1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/nested-struct-member-count.c @@ -0,0 +1,107 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +struct Inner { + int dummy; + int len; +}; + +struct Outer { + struct Inner hdr; + char fam[__counted_by(hdr.len)]; +}; + +char access(struct Outer *bar, int index) { + return bar->fam[index]; +} + +// CHECK: -FunctionDecl [[func_access:0x[^ ]+]] {{.+}} access +// CHECK: |-ParmVarDecl [[var_bar:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_index:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'char' +// CHECK: `-ArraySubscriptExpr +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct Outer *__single' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'struct Outer *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct Outer *__single' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | | `-MemberExpr {{.+}} ->hdr +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct Outer *__single' +// CHECK: | | `-OpaqueValueExpr [[ove]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-OpaqueValueExpr [[ove]] {{.*}} 'struct Outer *__single' +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} [[var_index]] + +struct Outer * assign(void * __bidi_indexable bar, int len) { + struct Outer * __single s = (struct Outer *) bar; + s->hdr.len = len; + return s; +} + +// CHECK: -FunctionDecl [[func_assign:0x[^ ]+]] {{.+}} assign +// CHECK: |-ParmVarDecl [[var_bar_1:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s:0x[^ ]+]] +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK: | | `-PredefinedBoundsCheckExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-CStyleCastExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar_1]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | |-MemberExpr {{.+}} .len +// CHECK: | | | `-MemberExpr {{.+}} ->hdr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] {{.*}} 'struct Outer *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'struct Outer *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'struct Outer *__single' +// CHECK: | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct Outer *__single' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-MemberExpr {{.+}} .len +// CHECK: | | | `-MemberExpr {{.+}} ->hdr +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct Outer *__single' +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'struct Outer *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: `-OpaqueValueExpr [[ove_3]] {{.*}} 'struct Outer *__single' + diff --git a/clang/test/BoundsSafety/AST/null-terminated-predefined-string.c b/clang/test/BoundsSafety/AST/null-terminated-predefined-string.c new file mode 100644 index 0000000000000..b7c35303f0214 --- /dev/null +++ b/clang/test/BoundsSafety/AST/null-terminated-predefined-string.c @@ -0,0 +1,38 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s + +#include + +void foo(const char *); +void bar(void) { + foo(__func__); + foo(__FUNCTION__); + foo(__PRETTY_FUNCTION__); +} +// CHECK:TranslationUnitDecl {{.*}} <> +// CHECK:|-FunctionDecl {{.*}} used foo 'void (const char *__single __terminated_by(0))' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK-NEXT:`-FunctionDecl {{.*}} bar 'void (void)' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: |-CallExpr {{.*}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.*}} 'void (*__single)(const char *__single __terminated_by(0))' +// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'void (const char *__single __terminated_by(0))' Function {{.*}} 'foo' 'void (const char *__single __terminated_by(0))' +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-PredefinedExpr {{.*}} 'const char[4]' lvalue __func__ +// CHECK-NEXT: | `-StringLiteral {{.*}} 'const char[4]' lvalue "bar" +// CHECK-NEXT: |-CallExpr {{.*}} 'void' +// CHECK-NEXT: | |-ImplicitCastExpr {{.*}} 'void (*__single)(const char *__single __terminated_by(0))' +// CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'void (const char *__single __terminated_by(0))' Function {{.*}} 'foo' 'void (const char *__single __terminated_by(0))' +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const char *__bidi_indexable' +// CHECK-NEXT: | `-PredefinedExpr {{.*}} 'const char[4]' lvalue __FUNCTION__ +// CHECK-NEXT: | `-StringLiteral {{.*}} 'const char[4]' lvalue "bar" +// CHECK-NEXT: `-CallExpr {{.*}} 'void' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'void (*__single)(const char *__single __terminated_by(0))' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'void (const char *__single __terminated_by(0))' Function {{.*}} 'foo' 'void (const char *__single __terminated_by(0))' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'const char *__bidi_indexable' +// CHECK-NEXT: `-PredefinedExpr {{.*}} 'const char[15]' lvalue __PRETTY_FUNCTION__ +// CHECK-NEXT: `-StringLiteral {{.*}} 'const char[15]' lvalue "void bar(void)" diff --git a/clang/test/BoundsSafety/AST/pass-addr-of-array-subscript.c b/clang/test/BoundsSafety/AST/pass-addr-of-array-subscript.c new file mode 100644 index 0000000000000..9daa5614c1b9c --- /dev/null +++ b/clang/test/BoundsSafety/AST/pass-addr-of-array-subscript.c @@ -0,0 +1,128 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +// CHECK: FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +void bar(void *__sized_by(len) buf, int len); + +// CHECK: FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +void foo(int *__counted_by(len) elems, int len, int idx) { + bar(&elems[idx], sizeof(elems[idx])); +} + +// CHECK: |-ParmVarDecl [[var_elems:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-ParmVarDecl [[var_idx:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr {{.+}} '&elems[idx] <= __builtin_get_pointer_upper_bound(&elems[idx]) && __builtin_get_pointer_lower_bound(&elems[idx]) <= &elems[idx] && sizeof (elems[idx]) <= (char *)__builtin_get_pointer_upper_bound(&elems[idx]) - (char *)&elems[idx] && 0 <= sizeof (elems[idx])' +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(void *__single __sized_by(len), int)' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(len)':'void *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | | `-ArraySubscriptExpr +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[var_elems]] +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_idx]] +// CHECK: | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ArraySubscriptExpr +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_elems]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_idx]] +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-UnaryExprOrTypeTraitExpr +// CHECK: | `-ParenExpr +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_elems]] +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} [[var_idx]] +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'void *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' diff --git a/clang/test/BoundsSafety/AST/promote-fam-align.c b/clang/test/BoundsSafety/AST/promote-fam-align.c new file mode 100644 index 0000000000000..419d4d9638b83 --- /dev/null +++ b/clang/test/BoundsSafety/AST/promote-fam-align.c @@ -0,0 +1,50 @@ + + +// RUN: %clang_cc1 -ast-dump -verify -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -verify -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +// expected-no-diagnostics + +#include +typedef unsigned char uuid_t[16]; +struct s { + int count; + uuid_t fam[__counted_by(count)]; +}; + +void promote(struct s *info) { + uuid_t *uuids = &info->fam[0]; + (void)uuids; +} + +// CHECK: `-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} promote +// CHECK: |-ParmVarDecl [[var_info:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_uuids:0x[^ ]+]] +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'uuid_t *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'uuid_t *' +// CHECK: | | | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'struct s *__single' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-BoundsSafetyPointerPromotionExpr {{.+}} 'struct s *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'struct s *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'uuid_t *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'uuid_t *' +// CHECK: | | | | | | | `-MemberExpr {{.+}} ->fam +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct s *__single' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} ->count +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct s *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'struct s *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_info]] +// CHECK: | | `-OpaqueValueExpr [[ove]] {{.*}} 'struct s *__single' +// CHECK: | `-IntegerLiteral {{.+}} 0 +// CHECK: `-CStyleCastExpr {{.+}} 'void' +// CHECK: `-ImplicitCastExpr {{.+}} 'uuid_t *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_uuids]] \ No newline at end of file diff --git a/clang/test/BoundsSafety/AST/ptrs-with-count-arithmetic.c b/clang/test/BoundsSafety/AST/ptrs-with-count-arithmetic.c new file mode 100644 index 0000000000000..751d7a84eef06 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ptrs-with-count-arithmetic.c @@ -0,0 +1,190 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s +#include + +struct S { + int *__counted_by(len + 1) buf; + int len; +}; + +int foo(int *__counted_by(len) buf, int len) { +// CHECK-LABEL: FunctionDecl {{.+}} foo +// CHECK: {{^}}| |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len:0x[^ ]+]] + + struct S s = {0, -1}; +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_s:0x[^ ]+]] +// CHECK: {{^}}| | `-BoundsCheckExpr {{.+}} +// CHECK: {{^}}| | |-InitListExpr +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len + 1)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | |-BinaryOperator {{.+}} 'int' '==' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '+' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len + 1)':'int *__single' +// CHECK: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}}| | `-UnaryOperator {{.+}} prefix '-' +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 1 + + int *ptr = buf + 1; +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_ptr:0x[^ ]+]] +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_buf]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_len]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 1 + + ptr = s.buf + 2; +// CHECK: {{^}}| |-BinaryOperator {{.+}} 'int *__bidi_indexable' '=' +// CHECK: {{^}}| | |-DeclRefExpr {{.+}} [[var_ptr]] +// CHECK: {{^}}| | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}}| | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len + 1)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} lvalue +// CHECK: {{^}}| | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}}| | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len + 1)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | | | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} .len +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len + 1)':'int *__single' +// CHECK: {{^}}| | | | | `-MemberExpr {{.+}} .buf +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '+' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_5]] {{.*}} lvalue +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(len + 1)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 2 + + return *ptr; +// CHECK: {{^}}| `-ReturnStmt +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| `-UnaryOperator {{.+}} prefix '*' +// CHECK: {{^}}| `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-DeclRefExpr {{.+}} [[var_ptr]] +} + +struct S_Nullable { + int *__counted_by_or_null(len + 1) buf; + int len; +}; + +int bar(int *__counted_by_or_null(len) buf, int len) { +// CHECK-LABEL: FunctionDecl {{.+}} bar +// CHECK: {{^}} |-ParmVarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: {{^}} |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: {{^}} | `-DependerDeclsAttr + + struct S_Nullable s = {0, -1}; +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_s_1:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-InitListExpr +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_8]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_9]] +// CHECK: {{^}} | | `-UnaryOperator {{.+}} prefix '-' +// CHECK: {{^}} | | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' + + int *ptr = buf + 1; +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_ptr_1:0x[^ ]+]] +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}} | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_10]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_buf_1]] +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_11]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by_or_null(len)':'int *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 1 + + ptr = s.buf + 2; +// CHECK: {{^}} |-BinaryOperator {{.+}} 'int *__bidi_indexable' '=' +// CHECK: {{^}} | |-DeclRefExpr {{.+}} [[var_ptr_1]] +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} lvalue +// CHECK: {{^}} | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: {{^}} | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_13]] +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_s_1]] +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_15]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | | `-MemberExpr {{.+}} .len +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} lvalue +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_12]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | | | | `-MemberExpr {{.+}} .buf +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} lvalue +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_14]] +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '+' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_13]] {{.*}} lvalue +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __counted_by_or_null(len + 1)':'int *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 2 + + return *ptr; +// CHECK: {{^}} `-ReturnStmt +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} `-DeclRefExpr {{.+}} [[var_ptr_1]] +} diff --git a/clang/test/BoundsSafety/AST/rebuild-auto-bound-in-primitive-init.c b/clang/test/BoundsSafety/AST/rebuild-auto-bound-in-primitive-init.c new file mode 100644 index 0000000000000..c5b015db9e737 --- /dev/null +++ b/clang/test/BoundsSafety/AST/rebuild-auto-bound-in-primitive-init.c @@ -0,0 +1,30 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +#include + +int main() { + int *local; + int *copy = local; + int primitive = *local; + return primitive; +} + +// CHECK: `-FunctionDecl {{.+}} main 'int ()' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} local 'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} copy 'int *__bidi_indexable'{{.*}} cinit +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable'{{.*}} +// CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable'{{.*}} lvalue Var {{.+}} 'local' 'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used primitive 'int' cinit +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-UnaryOperator {{.+}} 'int' lvalue prefix '*' cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable'{{.*}} +// CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable'{{.*}} lvalue Var {{.+}} 'local' 'int *__bidi_indexable' +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int' +// CHECK: `-DeclRefExpr {{.+}} 'int' lvalue Var {{.+}} 'primitive' 'int' \ No newline at end of file diff --git a/clang/test/BoundsSafety/AST/redundant-attrs.c b/clang/test/BoundsSafety/AST/redundant-attrs.c new file mode 100644 index 0000000000000..48e0b96f5d8b7 --- /dev/null +++ b/clang/test/BoundsSafety/AST/redundant-attrs.c @@ -0,0 +1,25 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s +#include + +typedef int *__bidi_indexable bidiPtr; +bidiPtr __bidi_indexable ptrBoundBound; + +#define bidiPtr2 int *__bidi_indexable +bidiPtr2 __bidi_indexable ptrBoundBound2; + +// CHECK: TypedefDecl {{.*}} referenced bidiPtr 'int *__bidi_indexable' +// CHECK-NEXT: PointerType {{.*}} 'int *__bidi_indexable' +// CHECK-NEXT: BuiltinType {{.*}} 'int' +// CHECK: VarDecl {{.*}} ptrBoundBound 'bidiPtr':'int *__bidi_indexable' +// CHECK-NEXT: VarDecl {{.*}} ptrBoundBound2 'int *__bidi_indexable' + +typedef const int * _Nullable __bidi_indexable my_c_ptr_nullable_bidi_t; +my_c_ptr_nullable_bidi_t __bidi_indexable def_c_nullable_bidi_ptr; +// CHECK: TypedefDecl {{.*}} referenced my_c_ptr_nullable_bidi_t 'const int *__bidi_indexable _Nullable':'const int *__bidi_indexable' +// CHECK-NEXT: AttributedType {{.*}} 'const int *__bidi_indexable _Nullable' sugar +// CHECK-NEXT: PointerType {{.*}} 'const int *__bidi_indexable' +// CHECK-NEXT: QualType {{.*}} 'const int' const +// CHECK-NEXT: BuiltinType {{.*}} 'int' +// CHECK-NEXT: VarDecl {{.*}} def_c_nullable_bidi_ptr 'const int *__bidi_indexable _Nullable':'const int *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/reproduce-amr-dec-crash.c b/clang/test/BoundsSafety/AST/reproduce-amr-dec-crash.c new file mode 100644 index 0000000000000..9c66e5b32a3b2 --- /dev/null +++ b/clang/test/BoundsSafety/AST/reproduce-amr-dec-crash.c @@ -0,0 +1,100 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +// CHECK: FunctionDecl [[func_conv:0x[^ ]+]] {{.+}} conv +void conv(short x[__counted_by(m)], short y[__counted_by(m)], short m); + +void test() { + short x[16]; + short y[16]; + conv(x, y, 16); +} +// CHECK-LABEL: test 'void ()' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_x_1:0x[^ ]+]] +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_y_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(short *__single __counted_by(m), short *__single __counted_by(m), short)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_conv]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'short *__single __counted_by(m)':'short *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'short *__single __counted_by(m)':'short *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'short' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'short' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'short' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'short' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'short *' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'short' +// CHECK: | |-OpaqueValueExpr [[ove]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'short *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_x_1]] +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'short *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_y_1]] +// CHECK: | `-OpaqueValueExpr [[ove_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'short' +// CHECK: | `-IntegerLiteral {{.+}} 16 +// CHECK: |-OpaqueValueExpr [[ove]] {{.*}} 'short *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'short *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_2]] {{.*}} 'short' diff --git a/clang/test/BoundsSafety/AST/sized_by_or_null_call.c b/clang/test/BoundsSafety/AST/sized_by_or_null_call.c new file mode 100644 index 0000000000000..0cc509903787a --- /dev/null +++ b/clang/test/BoundsSafety/AST/sized_by_or_null_call.c @@ -0,0 +1,638 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -Wno-bounds-safety-init-list %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-bounds-safety-init-list %s | FileCheck %s + +#include + +// CHECK: {{^}}|-FunctionDecl [[func_foo:0x[^ ]+]] {{.+}} foo +// CHECK: {{^}}| |-ParmVarDecl [[var_p:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +void foo(int *__sized_by_or_null(len) p, int len) {} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_1:0x[^ ]+]] {{.+}} caller_1 +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 <= 0 && 0 <= 0 && !0 || 2 <= (char *)0 - (char *)0 && 0 <= 2' +// CHECK-NEXT: {{^}}| | | |-CallExpr +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 2 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-CStyleCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_1]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 2 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +void caller_1() { + foo(0, 2); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_2:0x[^ ]+]] {{.+}} caller_2 +// CHECK-NEXT: {{^}}| `-CompoundStmt +// CHECK-NEXT: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK-NEXT: {{^}}| | |-BoundsCheckExpr {{.+}} '0 <= 0 && 0 <= 0 && !0 || 0 <= (char *)0 - (char *)0 && 0 <= 0' +// CHECK-NEXT: {{^}}| | | |-CallExpr +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK-NEXT: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] +// CHECK-NEXT: {{^}}| | | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK-NEXT: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK-NEXT: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK-NEXT: {{^}}| | | | |-CStyleCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: {{^}}| | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK-NEXT: {{^}}| | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK-NEXT: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_2]] +// CHECK-NEXT: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK-NEXT: {{^}}| | | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| | `-OpaqueValueExpr [[ove_3]] +// CHECK-NEXT: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK-NEXT: {{^}}| |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +void caller_2() { + foo(0, 0); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_3:0x[^ ]+]] {{.+}} caller_3 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_1:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= (char *)__builtin_get_pointer_upper_bound(p) - (char *)p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_1]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_1]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_7]] {{.*}} 'int' +void caller_3(int *__sized_by_or_null(len) p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_4:0x[^ ]+]] {{.+}} caller_4 +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_i:0x[^ ]+]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} '&i <= __builtin_get_pointer_upper_bound(&i) && __builtin_get_pointer_lower_bound(&i) <= &i && !&i || -1 <= (char *)__builtin_get_pointer_upper_bound(&i) - (char *)&i && 0 <= -1' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_8]] +// CHECK: {{^}}| | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_i]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_9]] +// CHECK: {{^}}| | `-UnaryOperator {{.+}} prefix '-' +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 1 +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +void caller_4() { + int i = 0; + foo(&i, -1); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_5:0x[^ ]+]] {{.+}} caller_5 +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| |-DeclStmt +// CHECK: {{^}}| | `-VarDecl [[var_i_1:0x[^ ]+]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} '&i <= __builtin_get_pointer_upper_bound(&i) && __builtin_get_pointer_lower_bound(&i) <= &i && !&i || 2 <= (char *)__builtin_get_pointer_upper_bound(&i) - (char *)&i && 0 <= 2' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_10]] +// CHECK: {{^}}| | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_i_1]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_11]] +// CHECK: {{^}}| | `-IntegerLiteral {{.+}} 2 +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +void caller_5() { + int i = 0; + foo(&i, 2); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_6:0x[^ ]+]] {{.+}} caller_6 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_2:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_2:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= (char *)__builtin_get_pointer_upper_bound(p) - (char *)p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_12]] +// CHECK: {{^}}| | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}}| | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_13]] +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[var_p_2]] +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_14]] +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_15]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +void caller_6(int *__sized_by(len) p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_7:0x[^ ]+]] {{.+}} caller_7 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_3:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_3:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= (char *)__builtin_get_pointer_upper_bound(p) - (char *)p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_16]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_3]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_17]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_3]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_16]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +void caller_7(int *__bidi_indexable p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_caller_8:0x[^ ]+]] {{.+}} caller_8 +// CHECK: {{^}}| |-ParmVarDecl [[var_p_4:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_4:0x[^ ]+]] +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= (char *)__builtin_get_pointer_upper_bound(p) - (char *)p && 0 <= len' +// CHECK: {{^}}| | | |-CallExpr +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by_or_null(len), int)' +// CHECK: {{^}}| | | | | `-DeclRefExpr {{.+}} [[func_foo]] +// CHECK: {{^}}| | | | |-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | | |-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}}| | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}}| | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +// CHECK: {{^}}| | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}}| | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}}| | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}}| | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}}| | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}}| | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | | `-CStyleCastExpr {{.+}} 'char *__single' +// CHECK: {{^}}| | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}}| | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_18]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_p_4]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_19]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_4]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_18]] {{.*}} 'int *__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_19]] {{.*}} 'int' +void caller_8(int *__single p, int len) { + foo(p, len); +} + +// CHECK: {{^}}|-FunctionDecl [[func_bar:0x[^ ]+]] {{.+}} bar +// CHECK: {{^}}| |-ParmVarDecl [[var_out:0x[^ ]+]] +// CHECK: {{^}}| `-ParmVarDecl [[var_len_5:0x[^ ]+]] +// CHECK: {{^}}| `-DependerDeclsAttr +void bar(int *__sized_by(*len) *out, int *len); + +// CHECK: {{^}}|-FunctionDecl [[func_caller_9:0x[^ ]+]] {{.+}} caller_9 +// CHECK: {{^}}| |-ParmVarDecl [[var_out_1:0x[^ ]+]] +// CHECK: {{^}}| |-ParmVarDecl [[var_len_6:0x[^ ]+]] +// CHECK: {{^}}| | `-DependerDeclsAttr +// CHECK: {{^}}| `-CompoundStmt +// CHECK: {{^}}| `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}}| | |-CallExpr +// CHECK: {{^}}| | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(*len)*__single, int *__single)' +// CHECK: {{^}}| | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK: {{^}}| | | |-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int *__single __sized_by(*len)*__single' +// CHECK: {{^}}| | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: {{^}}| | |-OpaqueValueExpr [[ove_20]] +// CHECK: {{^}}| | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(*len)*__single' +// CHECK: {{^}}| | | `-DeclRefExpr {{.+}} [[var_out_1]] +// CHECK: {{^}}| | `-OpaqueValueExpr [[ove_21]] +// CHECK: {{^}}| | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}}| | `-DeclRefExpr {{.+}} [[var_len_6]] +// CHECK: {{^}}| |-OpaqueValueExpr [[ove_20]] {{.*}} 'int *__single __sized_by(*len)*__single' +// CHECK: {{^}}| `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__single' +void caller_9(int *__sized_by(*len) *out, int *len){ + bar(out, len); +} + +// CHECK: {{^}}`-FunctionDecl [[func_caller_10:0x[^ ]+]] {{.+}} caller_10 +// CHECK: {{^}} |-ParmVarDecl [[var_len_7:0x[^ ]+]] +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_count:0x[^ ]+]] +// CHECK: {{^}} | `-DependerDeclsAttr +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_p_5:0x[^ ]+]] +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-CallExpr +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(*len)*__single, int *__single)' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[func_bar]] +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(*len)*__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'int *__single __sized_by_or_null(count)*__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_22]] +// CHECK: {{^}} | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_23]] +// CHECK: {{^}} | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_22]] {{.*}} 'int *__single __sized_by_or_null(count)*__bidi_indexable' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsCheckExpr {{.+}} 'p <= __builtin_get_pointer_upper_bound(p) && __builtin_get_pointer_lower_bound(p) <= p && !p || len <= (char *)__builtin_get_pointer_upper_bound(p) - (char *__bidi_indexable)p && 0 <= len' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int *__single __sized_by_or_null(count)':'int *__single' '=' +// CHECK: {{^}} | | | |-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | | |-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '||' +// CHECK: {{^}} | | |-UnaryOperator {{.+}} cannot overflow +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_24]] +// CHECK: {{^}} | | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | | |-OpaqueValueExpr [[ove_25]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_25]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_25]] +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_26]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_25]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_27]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_len_7]] +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: {{^}} | | |-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__bidi_indexable' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: {{^}} `-ReturnStmt +// CHECK: {{^}} `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(len)':'int *__single' +// CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_28]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_p_5]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_29]] +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | `-DeclRefExpr {{.+}} [[var_count]] +// CHECK: {{^}} |-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__single __sized_by_or_null(count)':'int *__single' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_29]] {{.*}} 'int' +int *__sized_by_or_null(len) caller_10(int len) { + int count; + int *__sized_by_or_null(count) p; + bar(&p, &count); + p = p; // workaround for missing return bounds check + count = len; + return p; +} + diff --git a/clang/test/BoundsSafety/AST/struct-init-with-side-effects.c b/clang/test/BoundsSafety/AST/struct-init-with-side-effects.c new file mode 100644 index 0000000000000..9d4c120279650 --- /dev/null +++ b/clang/test/BoundsSafety/AST/struct-init-with-side-effects.c @@ -0,0 +1,164 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -verify %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s 2>&1 | FileCheck %s + +#include + +struct s_no_annot { + int len; + int *ptr; +}; + +int getlen(); +int *getptr(); + +// CHECK: |-FunctionDecl [[func_getlen:0x[^ ]+]] {{.+}} getlen +// CHECK: |-FunctionDecl [[func_getptr:0x[^ ]+]] {{.+}} getptr + +// CHECK: |-FunctionDecl [[func_test_no_annot:0x[^ ]+]] {{.+}} test_no_annot +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_s1:0x[^ ]+]] +// CHECK: | | `-InitListExpr +// CHECK: | | |-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)()' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_getlen]] +// CHECK: | | `-CallExpr +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single(*__single)()' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_getptr]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_s2:0x[^ ]+]] +void test_no_annot() { + struct s_no_annot s1 = { + .len = getlen(), + .ptr = getptr() + }; + struct s_no_annot s2; +} + + +struct s_count_annot { + int len; + int dummy; + int *__counted_by(len) ptr; +}; + +int *__counted_by(len) getcountptr(int len); + +// CHECK: |-FunctionDecl [[func_getcountptr:0x[^ ]+]] {{.+}} getcountptr +// CHECK: | `-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_test_count_annot:0x[^ ]+]] {{.+}} test_count_annot +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_arr:0x[^ ]+]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_s1_1:0x[^ ]+]] +// CHECK: | | `-BoundsCheckExpr +// CHECK: | | |-InitListExpr +// CHECK: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-CallExpr +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int (*__single)()' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_getlen]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_arr]] +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[var_s2_1:0x[^ ]+]] +void test_count_annot() { + int arr[10]; + struct s_count_annot s1 = { + .len = 10, + .ptr = arr, + // expected-warning@+1{{initializer 'getlen()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .dummy = getlen() + }; + struct s_count_annot s2; +} + +struct s_range_annot { + int *end; + int dummy; + int *__ended_by(end) start; +}; + +// CHECK: FunctionDecl [[func_test_range_annot:0x[^ ]+]] {{.+}} test_range_annot +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_s1_2:0x[^ ]+]] +// CHECK: | `-BoundsCheckExpr +// CHECK: | |-InitListExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *__single /* __started_by(start) */ ':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | |-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int (*__single)()' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_getlen]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | `-BinaryOperator {{.+}} 'int *__bidi_indexable' '+' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | `-OpaqueValueExpr [[ove_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_arr_1]] +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s2_2:0x[^ ]+]] +void test_range_annot() { + int arr[10]; + struct s_range_annot s1 = { + .end = arr + 10, + .start = arr, + // expected-warning@+1{{initializer 'getlen()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .dummy = getlen() + }; + struct s_count_annot s2; +} diff --git a/clang/test/BoundsSafety/AST/system-count-return.c b/clang/test/BoundsSafety/AST/system-count-return.c new file mode 100644 index 0000000000000..4e867520e0180 --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-count-return.c @@ -0,0 +1,67 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -isystem %S/Inputs/system-count-return %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -isystem %S/Inputs/system-count-return %s 2>&1 | FileCheck %s + +#include +// CHECK: FunctionDecl [[func_alloc_sized_by:0x[^ ]+]] {{.+}} alloc_sized_by +// CHECK: FunctionDecl [[func_alloc_attributed:0x[^ ]+]] {{.+}} alloc_attributed + +int Test() { + int len = 16; +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_len_2:0x[^ ]+]] +// CHECK: {{^}} | `-IntegerLiteral {{.+}} 16 + + int *bufAuto = alloc_sized_by(len); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_bufAuto:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(len)':'int *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_len_2]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | | `-CallExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(len)(*__single)(int)' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[func_alloc_sized_by]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(len)':'int *__single' + + int *__bidi_indexable bufBound = alloc_attributed(sizeof(int) * 10); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_bufBound:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: {{^}} | | | |-UnaryExprOrTypeTraitExpr +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 10 +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}} | | `-CallExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'int *(*__single)(int)' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[func_alloc_attributed]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *' + + return bufBound[10]; +} diff --git a/clang/test/BoundsSafety/AST/system-header-merge-bounds-attributed.c b/clang/test/BoundsSafety/AST/system-header-merge-bounds-attributed.c new file mode 100644 index 0000000000000..eed32ffe1f8aa --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-header-merge-bounds-attributed.c @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + + +// Decls in the system header don't have any attributes. + +#include "system-header-merge-bounds-attributed.h" + +// CHECK: FunctionDecl {{.+}} cb_in +// CHECK: |-ParmVarDecl {{.+}} cb_in_p 'int *' +// CHECK: `-ParmVarDecl {{.+}} len 'int' +// CHECK: FunctionDecl {{.+}} cb_out +// CHECK: |-ParmVarDecl {{.+}} cb_out_p 'int **' +// CHECK: `-ParmVarDecl {{.+}} len 'int' +// CHECK: FunctionDecl {{.+}} cb_out_count +// CHECK: |-ParmVarDecl {{.+}} cb_out_len_p 'int *' +// CHECK: `-ParmVarDecl {{.+}} len 'int *' +// CHECK: FunctionDecl {{.+}} cbn +// CHECK: |-ParmVarDecl {{.+}} cbn_p 'int *' +// CHECK: `-ParmVarDecl {{.+}} len 'int' +// CHECK: FunctionDecl {{.+}} sb +// CHECK: |-ParmVarDecl {{.+}} sb_p 'void *' +// CHECK: `-ParmVarDecl {{.+}} size 'int' +// CHECK: FunctionDecl {{.+}} eb +// CHECK: |-ParmVarDecl {{.+}} eb_p 'void *' +// CHECK: `-ParmVarDecl {{.+}} end 'void *' + + +// Check if we can override them. + +void cb_in(int *__counted_by(len) cb_in_p, int len); +void cb_out(int *__counted_by(len) *cb_out_p, int len); +void cb_out_count(int *__counted_by(*len) cb_out_len_p, int *len); +void cbn(int *__counted_by_or_null(len) cbn_p, int len); +void sb(void *__sized_by(size) sb_p, int size); +void eb(void *__ended_by(end) eb_p, void *end); + +// CHECK: FunctionDecl {{.+}} prev {{.+}} cb_in +// CHECK: |-ParmVarDecl {{.+}} cb_in_p 'int *{{.*}} __counted_by(len)' +// CHECK: `-ParmVarDecl {{.+}} used len 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} cb_out +// CHECK: |-ParmVarDecl {{.+}} cb_out_p 'int *{{.*}} __counted_by(len)*{{.*}}' +// CHECK: `-ParmVarDecl {{.+}} used len 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} cb_out_count +// CHECK: |-ParmVarDecl {{.+}} cb_out_len_p 'int *{{.*}} __counted_by(*len)' +// CHECK: `-ParmVarDecl {{.+}} used len 'int *{{.*}}' +// CHECK: FunctionDecl {{.+}} prev {{.+}} cbn +// CHECK: |-ParmVarDecl {{.+}} cbn_p 'int *{{.*}} __counted_by_or_null(len)' +// CHECK: `-ParmVarDecl {{.+}} used len 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} sb +// CHECK: |-ParmVarDecl {{.+}} sb_p 'void *{{.*}} __sized_by(size)' +// CHECK: `-ParmVarDecl {{.+}} used size 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} eb +// CHECK: |-ParmVarDecl {{.+}} used eb_p 'void *{{.*}} __ended_by(end)' +// CHECK: `-ParmVarDecl {{.+}} used end 'void *{{.*}} /* __started_by(eb_p) */ ' + + +// Check if the attributes are merged. + +#include "system-header-merge-bounds-attributed.h" + +// CHECK: FunctionDecl {{.+}} prev {{.+}} cb_in +// CHECK: |-ParmVarDecl {{.+}} cb_in_p 'int *{{.*}} __counted_by(len)' +// CHECK: `-ParmVarDecl {{.+}} used len 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} cb_out +// CHECK: |-ParmVarDecl {{.+}} cb_out_p 'int *{{.*}} __counted_by(len)*{{.*}}' +// CHECK: `-ParmVarDecl {{.+}} used len 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} cb_out_count +// CHECK: |-ParmVarDecl {{.+}} cb_out_len_p 'int *{{.*}} __counted_by(*len)' +// CHECK: `-ParmVarDecl {{.+}} used len 'int *{{.*}}' +// CHECK: FunctionDecl {{.+}} prev {{.+}} cbn +// CHECK: |-ParmVarDecl {{.+}} cbn_p 'int *{{.*}} __counted_by_or_null(len)' +// CHECK: `-ParmVarDecl {{.+}} used len 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} sb +// CHECK: |-ParmVarDecl {{.+}} sb_p 'void *{{.*}} __sized_by(size)' +// CHECK: `-ParmVarDecl {{.+}} used size 'int' +// CHECK: FunctionDecl {{.+}} prev {{.+}} eb +// CHECK: |-ParmVarDecl {{.+}} used eb_p 'void *{{.*}} __ended_by(end)' +// CHECK: `-ParmVarDecl {{.+}} used end 'void *{{.*}} /* __started_by(eb_p) */ ' diff --git a/clang/test/BoundsSafety/AST/system-header-merge-bounds-attributed.h b/clang/test/BoundsSafety/AST/system-header-merge-bounds-attributed.h new file mode 100644 index 0000000000000..b3d0fcb78790e --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-header-merge-bounds-attributed.h @@ -0,0 +1,8 @@ +#pragma clang system_header + +void cb_in(int *cb_in_p, int len); +void cb_out(int **cb_out_p, int len); +void cb_out_count(int *cb_out_len_p, int *len); +void cbn(int *cbn_p, int len); +void sb(void *sb_p, int size); +void eb(void *eb_p, void *end); diff --git a/clang/test/BoundsSafety/AST/system-header-terminated-by.c b/clang/test/BoundsSafety/AST/system-header-terminated-by.c new file mode 100644 index 0000000000000..9dfbcf52a998c --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-header-terminated-by.c @@ -0,0 +1,10 @@ + + +// RUN: %clang_cc1 -fsyntax-only -ast-dump -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include +#include "system-header-terminated-by.h" + +// CHECK: FunctionDecl {{.+}} foo 'void (int *__single __terminated_by(0))' +// CHECK-NEXT: `-ParmVarDecl {{.+}} 'int *__single __terminated_by(0)':'int *__single' diff --git a/clang/test/BoundsSafety/AST/system-header-terminated-by.h b/clang/test/BoundsSafety/AST/system-header-terminated-by.h new file mode 100644 index 0000000000000..f0d9b987efe1f --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-header-terminated-by.h @@ -0,0 +1,3 @@ +#pragma clang system_header + +void foo(int *__null_terminated); diff --git a/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr-2.c b/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr-2.c new file mode 100644 index 0000000000000..a4775b62b6b9f --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr-2.c @@ -0,0 +1,40 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -isystem %S/Inputs/system-merge-dynamic-bound-attr %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -isystem %S/Inputs/system-merge-dynamic-bound-attr %s 2>&1 | FileCheck %s + +#include +#include + +// CHECK: FunctionDecl {{.+}} myalloc +// CHECK: FunctionDecl {{.+}} myalloc +// CHECK: -AllocSizeAttr + +void Test(unsigned siz) { + void *src = myalloc(siz); +} + +// CHECK: {{^}}`-FunctionDecl [[func_Test:0x[^ ]+]] {{.+}} Test +// CHECK: {{^}} |-ParmVarDecl [[var_siz:0x[^ ]+]] +// CHECK: {{^}} `-CompoundStmt +// CHECK: {{^}} `-DeclStmt +// CHECK: {{^}} `-VarDecl [[var_src:0x[^ ]+]] +// CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | `-CallExpr +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'void *(*__single)(unsigned int)' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} 'myalloc' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' +// CHECK: {{^}} `-OpaqueValueExpr [[ove]] {{.*}} 'void *' diff --git a/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr.c b/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr.c new file mode 100644 index 0000000000000..d7e8a7beb7699 --- /dev/null +++ b/clang/test/BoundsSafety/AST/system-merge-dynamic-bound-attr.c @@ -0,0 +1,166 @@ +// RUN: %clang_cc1 -ast-dump -fbounds-safety -isystem %S/Inputs/system-merge-dynamic-bound-attr %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -isystem %S/Inputs/system-merge-dynamic-bound-attr %s 2>&1 | FileCheck %s + +#include +#include + +// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc +// CHECK: {{^}}| |-ParmVarDecl +// CHECK: {{^}}| `-AllocSizeAttr +// CHECK: {{^}}|-FunctionDecl {{.+}} myalloc +// CHECK: {{^}}| |-ParmVarDecl +// CHECK: {{^}}| `-AllocSizeAttr +// CHECK: {{^}}|-FunctionDecl {{.+}} memcpy +// CHECK: {{^}}| |-ParmVarDecl +// CHECK: {{^}}| |-ParmVarDecl +// CHECK: {{^}}| `-ParmVarDecl +// CHECK: {{^}}| `-DependerDeclsAttr + +void Test(unsigned siz) { +// CHECK: {{^}}`-FunctionDecl [[func_Test:0x[^ ]+]] {{.+}} Test +// CHECK: {{^}} |-ParmVarDecl [[var_siz:0x[^ ]+]] + + void *src = myalloc(siz); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_src:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_1]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove]] +// CHECK: {{^}} | | `-CallExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *(*__single)(unsigned int)' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} 'myalloc' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_1]] {{.*}} 'unsigned int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove]] {{.*}} 'void *' + + void *dst = myalloc(siz); +// CHECK: {{^}} |-DeclStmt +// CHECK: {{^}} | `-VarDecl [[var_dst:0x[^ ]+]] +// CHECK: {{^}} | `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned int' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_3]] +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_2]] +// CHECK: {{^}} | | `-CallExpr +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *(*__single)(unsigned int)' +// CHECK: {{^}} | | | `-DeclRefExpr {{.+}} 'myalloc' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned int' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_3]] {{.*}} 'unsigned int' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_2]] {{.*}} 'void *' + + memcpy(dst, src, siz); +// CHECK: {{^}} `-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} |-MaterializeSequenceExpr {{.+}} +// CHECK: {{^}} | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'unsigned long long' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: {{^}} | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: {{^}} | | | `-AssumptionExpr +// CHECK: {{^}} | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long long' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '>=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'long long' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long long' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'long long' +// CHECK: {{^}} | | | `-IntegerLiteral {{.+}} 0 +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_5]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_dst]] +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_6]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_src]] +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_7]] +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: {{^}} | | `-DeclRefExpr {{.+}} [[var_siz]] +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_4]] +// CHECK: {{^}} | `-BoundsCheckExpr +// CHECK: {{^}} | |-BoundsCheckExpr +// CHECK: {{^}} | | |-CallExpr +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(function-parameter-0-2)(*__single)(void *__single __sized_by(function-parameter-0-2), void *__single __sized_by(function-parameter-0-2), unsigned long long)' +// CHECK: {{^}} | | | | `-DeclRefExpr {{.+}} +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long long' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long long' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: {{^}} | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | | `-GetBoundExpr {{.+}} lower +// CHECK: {{^}} | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: {{^}} | |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long long' +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'unsigned long long' +// CHECK: {{^}} | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: {{^}} | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | | `-GetBoundExpr {{.+}} upper +// CHECK: {{^}} | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: {{^}} | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: {{^}} | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_5]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__bidi_indexable' +// CHECK: {{^}} |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned long long' +// CHECK: {{^}} `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__single __sized_by(function-parameter-0-2)':'void *__single' +} diff --git a/clang/test/BoundsSafety/AST/terminated-by-attr.c b/clang/test/BoundsSafety/AST/terminated-by-attr.c new file mode 100644 index 0000000000000..56dd05f50e23f --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-attr.c @@ -0,0 +1,108 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: VarDecl {{.+}} array1 'int[__terminated_by(0) 3]':'int[3]' +int array1[__null_terminated 3] = {1, 2, 0}; + +// CHECK: VarDecl {{.+}} array2 'int[__terminated_by(42) 3]':'int[3]' +int array2[__terminated_by(42) 3] = {1, 2, 42}; + +// CHECK: VarDecl {{.+}} incomplete_array1 'int[__terminated_by(0) 3]':'int[3]' +int incomplete_array1[__null_terminated] = {1, 2, 0}; + +// CHECK: VarDecl {{.+}} incomplete_array2 'int[__terminated_by(42) 3]':'int[3]' +int incomplete_array2[__terminated_by(42)] = {1, 2, 42}; + +// CHECK: VarDecl {{.+}} ptr1 'int *__single __terminated_by(0)':'int *__single' +int *__null_terminated ptr1 = array1; + +// CHECK: VarDecl {{.+}} ptr2 'int *__single __terminated_by(42)':'int *__single' +int *__terminated_by(42) ptr2 = array2; + +// CHECK: VarDecl {{.+}} ptr_array1 'int *__single __terminated_by(42)[__terminated_by(0) 3]':'int *__single __terminated_by(42)[3]' +int *__terminated_by(42) ptr_array1[__null_terminated 3] = {array2, array2, 0}; + +// CHECK: VarDecl {{.+}} ptr_ptr1 'int *__single __terminated_by(42)*__single __terminated_by(0)':'int *__single __terminated_by(42)*__single' +int *__terminated_by(42) *__null_terminated ptr_ptr1 = ptr_array1; + +// CHECK: RecordDecl {{.+}} foo +// CHECK-NEXT: FieldDecl {{.+}} array3 'int[__terminated_by(0) 2]':'int[2]' +// CHECK-NEXT: FieldDecl {{.+}} ptr3 'int *__single __terminated_by(0)':'int *__single' +struct foo { + int array3[__null_terminated 2]; + int *__null_terminated ptr3; +}; + +// CHECK: ParmVarDecl {{.+}} ptr4 'int *__single __terminated_by(0)':'int *__single' +void foo(int *__null_terminated ptr4); + +// CHECK: ParmVarDecl {{.+}} ptr_cv_nt 'int *__single __terminated_by(0)const volatile':'int *__singleconst volatile' +void quals_cv_nt(int *const volatile __null_terminated ptr_cv_nt); + +// CHECK: ParmVarDecl {{.+}} ptr_nt_cv 'int *__single __terminated_by(0)const volatile':'int *__singleconst volatile' +void quals_nt_cv(int *__null_terminated const volatile ptr_nt_cv); + +// CHECK: ParmVarDecl {{.+}} nested_ptr_cv_nt 'int *__single __terminated_by(0)const volatile *__single __terminated_by(0)const volatile':'int *__single __terminated_by(0)const volatile *__singleconst volatile' +void nested_quals_cv_nt(int *__null_terminated const volatile *const volatile __null_terminated nested_ptr_cv_nt); + +// CHECK: ParmVarDecl {{.+}} nested_ptr_nt_cv 'int *__single __terminated_by(0)const volatile *__single __terminated_by(0)const volatile':'int *__single __terminated_by(0)const volatile *__singleconst volatile' +void nested_quals_nt_cv(int *__null_terminated const volatile *__null_terminated const volatile nested_ptr_nt_cv); + +// CHECK: TypedefDecl {{.+}} my_int_t 'int *' +typedef int *my_int_t; + +// CHECK: ParmVarDecl {{.+}} typedef_nt 'int *__single __terminated_by(0)':'int *__single' +void typedef_nt(my_int_t __null_terminated typedef_nt); + +// CHECK: ParmVarDecl {{.+}} typedef_nt_c 'int *__single __terminated_by(0)const':'int *__singleconst' +void typedef_nt_c(my_int_t __null_terminated const typedef_nt_c); + +// CHECK: ParmVarDecl {{.+}} typedef_c_nt 'int *__single __terminated_by(0)const':'int *__singleconst' +void typedef_c_nt(my_int_t const __null_terminated typedef_c_nt); + +// CHECK: TypedefDecl {{.+}} my_cint_t 'int *const' +typedef int *const my_cint_t; + +// CHECK: ParmVarDecl {{.+}} ctypedef_nt 'int *__single __terminated_by(0)const':'int *__singleconst' +void ctypedef_nt(my_cint_t __null_terminated ctypedef_nt); + +// CHECK: ParmVarDecl {{.+}} ctypedef_nt_c 'int *__single __terminated_by(0)const':'int *__singleconst' +void ctypedef_nt_c(my_cint_t __null_terminated const ctypedef_nt_c); + +// CHECK: ParmVarDecl {{.+}} ctypedef_c_nt 'int *__single __terminated_by(0)const':'int *__singleconst' +void ctypedef_c_nt(my_cint_t const __null_terminated ctypedef_c_nt); + +#define my_ptr_c_nt_t int *const __null_terminated +// CHECK: VarDecl {{.+}} def_c_nt_nt 'int *__single __terminated_by(0)const':'int *__singleconst' +my_ptr_c_nt_t __null_terminated def_c_nt_nt; + +#define my_ptr_nt_nullable_t int *__null_terminated _Nullable +// CHECK: VarDecl {{.+}} def_nt_nullable_nt 'int *__single __terminated_by(0) _Nullable':'int *__single' +my_ptr_nt_nullable_t __null_terminated def_nt_nullable_nt; + +#define my_ptr_nullable_nt_t int *_Nullable __null_terminated +// CHECK: VarDecl {{.+}} def_nullable_nt_nt 'int *__single __terminated_by(0) _Nullable':'int *__single' +my_ptr_nullable_nt_t __null_terminated def_nullable_nt_nt; + +#define my_c_a_int_t const int __attribute__((aligned(64))) +typedef my_c_a_int_t * __attribute__((align_value(64))) _Nullable __null_terminated my_c_ptr_nullable_nt_t; +// CHECK: TypedefDecl {{.*}} referenced my_c_ptr_nullable_nt_t 'const int * __terminated_by(0) _Nullable':'const int *' +// CHECK-NEXT: |-AttributedType {{.*}} 'const int * __terminated_by(0) _Nullable' sugar +// CHECK-NEXT: | `-ValueTerminatedType {{.*}} 'const int * __terminated_by(0)' sugar +// CHECK-NEXT: | `-PointerType {{.*}} 'const int *' +// CHECK-NEXT: | `-QualType {{.*}} 'const int' const +// CHECK-NEXT: | `-BuiltinType {{.*}} 'int' +// CHECK-NEXT: |-AlignedAttr {{.*}} aligned +// CHECK-NEXT: | `-ConstantExpr {{.*}} 'int' +// CHECK-NEXT: | |-value: Int 64 +// CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 64 +// CHECK-NEXT: `-AlignValueAttr {{.*}} +// CHECK-NEXT: `-ConstantExpr {{.*}} 'int' +// CHECK-NEXT: |-value: Int 64 +// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 64 +// CHECK: VarDecl {{.*}} def_c_nullable_nt_nt 'const int *__single __terminated_by(0) _Nullable':'const int *__single' +my_c_ptr_nullable_nt_t __null_terminated def_c_nullable_nt_nt; + diff --git a/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.c b/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.c new file mode 100644 index 0000000000000..47b70518f83a8 --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.c @@ -0,0 +1,31 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include + +#include "terminated-by-conflicting-attrs.h" + +/* terminated-by-conflicting-attrs.h: + + void foo(int *__counted_by(len) p, int len); + void foo(int *__null_terminated p, int len); + + void bar(int *__null_terminated p, int len); + void bar(int *__counted_by(len) p, int len); + +*/ + +// CHECK: FunctionDecl {{.+}} foo 'void (int *__single __counted_by(len), int)' +// CHECK: FunctionDecl {{.+}} foo 'void (int *__single __counted_by(len), int)' +// CHECK: FunctionDecl {{.+}} bar 'void (int *__single __terminated_by(0), int)' +// XXX: rdar://127827450 +// CHECK: FunctionDecl {{.+}} bar 'void (int *__single __terminated_by(0), int)' + +void test() { + int arr[10]; + foo(arr, 10); + + int *__null_terminated ptr = 0; + bar(ptr, 0); +} diff --git a/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.h b/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.h new file mode 100644 index 0000000000000..776a970d4260a --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-conflicting-attrs.h @@ -0,0 +1,7 @@ +#pragma clang system_header + +void foo(int *__counted_by(len) p, int len); +void foo(int *__null_terminated p, int len); + +void bar(int *__null_terminated p, int len); +void bar(int *__counted_by(len) p, int len); diff --git a/clang/test/BoundsSafety/AST/terminated-by-decay.c b/clang/test/BoundsSafety/AST/terminated-by-decay.c new file mode 100644 index 0000000000000..d22f1708bb3f1 --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-decay.c @@ -0,0 +1,28 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// __terminated_by arrays should decay to __terminated_by __single pointers. + +// CHECK: ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int[__terminated_by(0) 3]':'int[3]' lvalue Var {{.+}} 'array' 'int[__terminated_by(0) 3]':'int[3]' +void null(void) { + int array[__null_terminated 3] = {1, 2, 0}; + (void)array; +} + +// CHECK: ImplicitCastExpr {{.+}} 'int *__single __terminated_by(42)':'int *__single' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int[__terminated_by(42) 3]':'int[3]' lvalue Var {{.+}} 'array2' 'int[__terminated_by(42) 3]':'int[3]' +void _42(void) { + int array2[__terminated_by(42) 3] = {1, 2, 42}; + (void)array2; +} + +// CHECK: ImplicitCastExpr {{.+}} 'const int *__single __terminated_by(0)':'const int *__single' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'const int[__terminated_by(0) 3]':'const int[3]' lvalue Var {{.+}} 'array' 'const int[__terminated_by(0) 3]':'const int[3]' +void quals(void) { + const int array[__null_terminated 3] = {1, 2, 0}; + (void)array; +} diff --git a/clang/test/BoundsSafety/AST/terminated-by-from-indexable.c b/clang/test/BoundsSafety/AST/terminated-by-from-indexable.c new file mode 100644 index 0000000000000..1653b4c46be3e --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-from-indexable.c @@ -0,0 +1,49 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: TerminatedByFromIndexableExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__indexable' +// CHECK-NEXT: `-<<>> +void null(int *__indexable ptr) { + __unsafe_null_terminated_from_indexable(ptr); +} + +// CHECK: TerminatedByFromIndexableExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__indexable' +// CHECK-NEXT: `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'ptr_to_term' 'int *__indexable' +void null_ptr_to_term(int *__indexable ptr, int *__indexable ptr_to_term) { + __unsafe_null_terminated_from_indexable(ptr, ptr_to_term); +} + +// CHECK: TerminatedByFromIndexableExpr {{.+}} 'int *__single __terminated_by((42))':'int *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'ptr' 'int *__indexable' +// CHECK-NEXT: `-<<>> +void _42(int *__indexable ptr) { + __unsafe_terminated_by_from_indexable(42, ptr); +} + +static int array[42]; + +// CHECK: TerminatedByFromIndexableExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int[42]' lvalue Var {{.+}} 'array' 'int[42]' +// CHECK-NEXT: `-<<>> +void decay(void) { + __unsafe_null_terminated_from_indexable(array); +} + +// CHECK: TerminatedByFromIndexableExpr {{.+}} 'const int *__single __terminated_by(0)':'const int *__single' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'const int *__indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'const int *__indexable' lvalue ParmVar {{.+}} 'ptr' 'const int *__indexable' +// CHECK-NEXT: `-<<>> +void quals(const int *__indexable ptr) { + __unsafe_null_terminated_from_indexable(ptr); +} diff --git a/clang/test/BoundsSafety/AST/terminated-by-redecl.c b/clang/test/BoundsSafety/AST/terminated-by-redecl.c new file mode 100644 index 0000000000000..0486a64ebbc96 --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-redecl.c @@ -0,0 +1,57 @@ + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s 2>&1 | FileCheck %s + +#include +#include "terminated-by-redecl.h" +// CHECK:|-FunctionDecl {{.*}} test_system_no_annot_argument 'void (int *)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'int *' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_argument 'void (int *__single __terminated_by(0))' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_argument_implicit_1 'void (const char *)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'const char *' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_argument_implicit_2 'void (const char *)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'const char *' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_argument_implicit_3 'void (const char *)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'const char *' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_no_annot_return 'int *()' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_return 'int *__single __terminated_by(0)()' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_return_implicit_1 'const char *()' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_return_implicit_2 'const char *()' +// CHECK-NEXT:|-FunctionDecl {{.*}} test_system_nt_return_implicit_3 'const char *()' + + +const char *test(); +const char *__null_terminated test(); +// CHECK-NEXT:|-FunctionDecl {{.*}} test 'const char *__single __terminated_by(0)()' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test 'const char *__single __terminated_by(0)()' + + +void test_system_no_annot_argument(int *__null_terminated p); +void test_system_nt_argument(int *__null_terminated p); +void test_system_nt_argument_implicit_1(const char *p); +void test_system_nt_argument_implicit_2(const char *__single p); +void test_system_nt_argument_implicit_3(const char *__null_terminated p); +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_no_annot_argument 'void (int *__single __terminated_by(0))' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_argument 'void (int *__single __terminated_by(0))' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_argument_implicit_1 'void (const char *__single __terminated_by(0))' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_argument_implicit_2 'void (const char *__single)' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'const char *__single' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_argument_implicit_3 'void (const char *__single __terminated_by(0))' +// CHECK-NEXT:| `-ParmVarDecl {{.*}} p 'const char *__single __terminated_by(0)':'const char *__single' + + +int *__null_terminated test_system_no_annot_return(); +int *__null_terminated test_system_nt_return(); +const char *test_system_nt_return_implicit_1(); +const char *__unsafe_indexable test_system_nt_return_implicit_2(); +const char *__null_terminated test_system_nt_return_implicit_3(); + +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_no_annot_return 'int *__single __terminated_by(0)()' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_return 'int *__single __terminated_by(0)()' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_return_implicit_1 'const char *__single __terminated_by(0)()' +// CHECK-NEXT:|-FunctionDecl {{.*}} prev {{.*}} test_system_nt_return_implicit_2 'const char *__unsafe_indexable()' +// CHECK-NEXT:`-FunctionDecl {{.*}} prev {{.*}} test_system_nt_return_implicit_3 'const char *__single __terminated_by(0)()' diff --git a/clang/test/BoundsSafety/AST/terminated-by-redecl.h b/clang/test/BoundsSafety/AST/terminated-by-redecl.h new file mode 100644 index 0000000000000..d77a062761f65 --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-redecl.h @@ -0,0 +1,15 @@ +#pragma clang system_header + +#include + +void test_system_no_annot_argument(int *p); +void test_system_nt_argument(int *__null_terminated p); +void test_system_nt_argument_implicit_1(const char *p); +void test_system_nt_argument_implicit_2(const char *p); +void test_system_nt_argument_implicit_3(const char *p); + +int *test_system_no_annot_return(); +int *__null_terminated test_system_nt_return(); +const char *test_system_nt_return_implicit_1(); +const char *test_system_nt_return_implicit_2(); +const char *test_system_nt_return_implicit_3(); diff --git a/clang/test/BoundsSafety/AST/terminated-by-to-indexable.c b/clang/test/BoundsSafety/AST/terminated-by-to-indexable.c new file mode 100644 index 0000000000000..294ce33b24abb --- /dev/null +++ b/clang/test/BoundsSafety/AST/terminated-by-to-indexable.c @@ -0,0 +1,55 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// CHECK: TerminatedByToIndexableExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-<<>> +void null(int *__null_terminated ptr) { + __terminated_by_to_indexable(ptr); +} + +// CHECK: TerminatedByToIndexableExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(42)':'int *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __terminated_by(42)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __terminated_by(42)':'int *__single' +// CHECK-NEXT: `-<<>> +void _42(int *__terminated_by(42) ptr) { + __terminated_by_to_indexable(ptr); +} + +// CHECK: TerminatedByToIndexableExpr {{.+}} 'const int *__indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'const int *__single __terminated_by(0)':'const int *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'const int *__single __terminated_by(0)':'const int *__single' lvalue ParmVar {{.+}} 'ptr' 'const int *__single __terminated_by(0)':'const int *__single' +// CHECK-NEXT: `-<<>> +void quals(const int *__null_terminated ptr) { + __terminated_by_to_indexable(ptr); +} + +// CHECK: TerminatedByToIndexableExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-<<>> +void null_unsafe(int *__null_terminated ptr) { + __unsafe_terminated_by_to_indexable(ptr); +} + +// CHECK: TerminatedByToIndexableExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: | `-ParenExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void explicit_null(int *__null_terminated ptr) { + __null_terminated_to_indexable(ptr); +} + +// CHECK: TerminatedByToIndexableExpr {{.+}} 'int *__indexable' +// CHECK-NEXT: |-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: | `-ParenExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue +// CHECK-NEXT: | `-DeclRefExpr {{.+}} 'int *__single __terminated_by(0)':'int *__single' lvalue ParmVar {{.+}} 'ptr' 'int *__single __terminated_by(0)':'int *__single' +// CHECK-NEXT: `-IntegerLiteral {{.+}} 'int' 0 +void explicit_null_unsafe(int *__null_terminated ptr) { + __unsafe_null_terminated_to_indexable(ptr); +} diff --git a/clang/test/BoundsSafety/AST/ternary-on-indexables.c b/clang/test/BoundsSafety/AST/ternary-on-indexables.c new file mode 100644 index 0000000000000..e41f683f06db0 --- /dev/null +++ b/clang/test/BoundsSafety/AST/ternary-on-indexables.c @@ -0,0 +1,362 @@ + +// RUN: %clang_cc1 -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +#include + +void Test(int sel) { + int a; + int *x = &a; + int *y = &a; + int *z = sel ? x : y; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} used z 'int *__bidi_indexable' cinit + // CHECK: | `-ConditionalOperator {{.*}} 'int *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'y' 'int *__bidi_indexable' + + + char c; + char *x_char = &c; + char *y_char = &c; + char *z_char = sel ? x_char : y_char; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} used z_char 'char *__bidi_indexable' cinit + // CHECK: | `-ConditionalOperator {{.*}} 'char *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'char *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'x_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'char *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'y_char' 'char *__bidi_indexable' + + + void *__indexable x_ix_void = &a; + void *__indexable y_ix_void = &a; + void *__indexable z_ix_void = x_ix_void ?: y_ix_void; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} used z_ix_void 'void *__indexable' cinit + // CHECK: | `-BinaryConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' + + int *__indexable x_ix = &a; + int *__indexable y_ix = &a; + int *__indexable z_ix = sel ? x_ix : y_ix; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} used z_ix 'int *__indexable' cinit + // CHECK: | `-ConditionalOperator{{.*}} 'int *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'x_ix' 'int *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'y_ix' 'int *__indexable' + + int *__single x_sg = &a; + int *__single y_sg = &a; + int *__single z_sg = sel ? x_sg : y_sg; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} z_sg 'int *__single' cinit + // CHECK: | `-ConditionalOperator {{.*}} 'int *__single' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'y_sg' 'int *__single' + + int *__unsafe_indexable x_uix = &a; + int *__unsafe_indexable y_uix = &a; + int *__unsafe_indexable z_uix = sel ? x_uix : y_uix; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} z_uix 'int *__unsafe_indexable' cinit + // CHECK: | `-ConditionalOperator {{.*}} 'int *__unsafe_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'x_uix' 'int *__unsafe_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'y_uix' 'int *__unsafe_indexable' + + char *__unsafe_indexable x_uix_char = &c; + char *__unsafe_indexable y_uix_char = &c; + char *__unsafe_indexable z_uix_char = sel ? x_uix_char : y_uix_char; + // CHECK: |-DeclStmt + // CHECK: | `-VarDecl {{.*}} z_uix_char 'char *__unsafe_indexable' cinit + // CHECK: | `-ConditionalOperator {{.*}} 'char *__unsafe_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'char *__unsafe_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'char *__unsafe_indexable' lvalue Var {{.*}} 'x_uix_char' 'char *__unsafe_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'char *__unsafe_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'char *__unsafe_indexable' lvalue Var {{.*}} 'y_uix_char' 'char *__unsafe_indexable' + + z = sel ? x : y; + // CHECK: |-BinaryOperator {{.*}} 'int *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'z' 'int *__bidi_indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'int *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'y' 'int *__bidi_indexable' + + z = sel ? x : y_ix; + // CHECK: |-BinaryOperator {{.*}} 'int *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'z' 'int *__bidi_indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'int *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'y_ix' 'int *__indexable' + + z = x ?: y_sg; + // CHECK: |-BinaryOperator {{.*}} 'int *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'z' 'int *__bidi_indexable' + // CHECK: | `-BinaryConditionalOperator {{.*}} 'int *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'y_sg' 'int *__single' + + z = x ?: y_ix_void; + // CHECK: |-BinaryOperator {{.*}} 'int *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'z' 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-BinaryConditionalOperator {{.*}} 'void *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' + // CHECK: | | `-OpaqueValueExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'x' 'int *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' + + z_ix = sel ? x_ix_void : y; + // CHECK: |-BinaryOperator {{.*}} 'int *__indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'z_ix' 'int *__indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'int *__indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'void *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'y' 'int *__bidi_indexable' + + z_ix = sel ? x_sg : y_ix; + // CHECK: |-BinaryOperator {{.*}} 'int *__indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'z_ix' 'int *__indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'int *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'y_ix' 'int *__indexable' + + z_ix = x_sg ?: y_ix_void; + // CHECK: |-BinaryOperator {{.*}} 'int *__indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'z_ix' 'int *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-BinaryConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | |-OpaqueValueExpr {{.*}} 'int *__single' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-OpaqueValueExpr {{.*}} 'int *__single' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' + + z_ix_void = sel ? x_sg : y; + // CHECK: |-BinaryOperator {{.*}} 'void *__indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'z_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'int *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'y' 'int *__bidi_indexable' + + z_ix_void = sel ? x_sg : y_ix; + // CHECK: |-BinaryOperator {{.*}} 'void *__indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'z_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'int *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'y_ix' 'int *__indexable' + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wbounds-attributes-implicit-conversion-single-to-explicit-indexable" + z_ix_void = sel ? x_sg : y_sg; + #pragma clang diagnostic pop + // CHECK: |-BinaryOperator {{.*}} 'void *__indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'z_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'void *__single' + // CHECK: | `-ConditionalOperator {{.*}} 'int *__single' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'y_sg' 'int *__single' + + z_char = sel ? x_ix : y_ix_void; + // CHECK: |-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'x_ix' 'int *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' + + z_char = x_sg ?: y_ix_void; + // CHECK: |-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__indexable' + // CHECK: | `-BinaryConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | |-OpaqueValueExpr {{.*}} 'int *__single' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-OpaqueValueExpr {{.*}} 'int *__single' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' + + z_char = sel ? x_sg : y_ix_void; + // CHECK: |-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'x_sg' 'int *__single' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' + + z_char = sel ? x_ix_void : y; + // CHECK: |-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'char *__bidi_indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'void *__bidi_indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'y' 'int *__bidi_indexable' + + z_char = x_ix_void ?: y_ix; + // CHECK: |-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__indexable' + // CHECK: | `-BinaryConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | |-OpaqueValueExpr {{.*}} 'void *__indexable' + // CHECK: | | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__indexable' lvalue Var {{.*}} 'y_ix' 'int *__indexable' + + z_char = sel ? x_ix_void : y_sg; + // CHECK: |-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: | |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' + // CHECK: | `-ImplicitCastExpr {{.+}} 'char *__indexable' + // CHECK: | `-ConditionalOperator {{.*}} 'void *__indexable' + // CHECK: | |-ImplicitCastExpr {{.*}} 'int' + // CHECK: | | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'sel' 'int' + // CHECK: | |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'int *__single' + // CHECK: | `-DeclRefExpr {{.*}} 'int *__single' lvalue Var {{.*}} 'y_sg' 'int *__single' + + z_char = x_ix_void ?: y_ix_void; + // CHECK: `-BinaryOperator {{.*}} 'char *__bidi_indexable' '=' + // CHECK: |-DeclRefExpr {{.*}} 'char *__bidi_indexable' lvalue Var {{.*}} 'z_char' 'char *__bidi_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' + // CHECK: `-ImplicitCastExpr {{.+}} 'char *__indexable' + // CHECK: `-BinaryConditionalOperator {{.*}} 'void *__indexable' + // CHECK: |-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: |-OpaqueValueExpr {{.*}} 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: |-OpaqueValueExpr {{.*}} 'void *__indexable' + // CHECK: | `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: | `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'x_ix_void' 'void *__indexable' + // CHECK: `-ImplicitCastExpr {{.*}} 'void *__indexable' + // CHECK: `-DeclRefExpr {{.*}} 'void *__indexable' lvalue Var {{.*}} 'y_ix_void' 'void *__indexable' +} diff --git a/clang/test/BoundsSafety/AST/transform-auto-bound-init-no-recursion.c b/clang/test/BoundsSafety/AST/transform-auto-bound-init-no-recursion.c new file mode 100644 index 0000000000000..19ce611ab8f24 --- /dev/null +++ b/clang/test/BoundsSafety/AST/transform-auto-bound-init-no-recursion.c @@ -0,0 +1,51 @@ + + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +typedef long a; +typedef struct { + a *b; + a *c; +} d, *e; +#define f(g, h) \ + a *i; \ + d j = {&i[0], &i[h]}; \ + e g = &j; +#define k(g) sizeof(g) +#define l(g) k(g) +void m(void) { + int h; + f(g, h) l(g); +} + +// CHECK: `-FunctionDecl {{.+}} m 'void (void)' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used h 'int' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used i 'a *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used j 'd' cinit +// CHECK: | `-InitListExpr {{.+}} 'd' +// CHECK: | |-ImplicitCastExpr {{.+}} 'a *__single' +// CHECK: | | `-UnaryOperator {{.+}} 'a *__bidi_indexable' prefix '&' cannot overflow +// CHECK: | | `-ArraySubscriptExpr {{.+}} 'a':'long' lvalue +// CHECK: | | |-ImplicitCastExpr {{.+}} 'a *__bidi_indexable'{{.*}} +// CHECK: | | | `-DeclRefExpr {{.+}} 'a *__bidi_indexable'{{.*}} lvalue Var {{.+}} 'i' 'a *__bidi_indexable' +// CHECK: | | `-IntegerLiteral {{.+}} 'int' 0 +// CHECK: | `-ImplicitCastExpr {{.+}} 'a *__single' +// CHECK: | `-UnaryOperator {{.+}} 'a *__bidi_indexable' prefix '&' cannot overflow +// CHECK: | `-ArraySubscriptExpr {{.+}} 'a':'long' lvalue +// CHECK: | |-ImplicitCastExpr {{.+}} 'a *__bidi_indexable'{{.*}} +// CHECK: | | `-DeclRefExpr {{.+}} 'a *__bidi_indexable'{{.*}} lvalue Var {{.+}} 'i' 'a *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | `-DeclRefExpr {{.+}} 'int' lvalue Var {{.+}} 'h' 'int' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} referenced g 'struct d *__bidi_indexable'{{.*}} cinit +// CHECK: | `-UnaryOperator {{.+}} 'd *__bidi_indexable' prefix '&' cannot overflow +// CHECK: | `-DeclRefExpr {{.+}} 'd' lvalue Var {{.+}} 'j' 'd' +// CHECK: `-UnaryExprOrTypeTraitExpr {{.+}} 'unsigned long' sizeof +// CHECK: `-ParenExpr {{.+}} 'struct d *__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} 'struct d *__bidi_indexable'{{.*}} 'g' 'struct d *__bidi_indexable'{{.*}} non_odr_use_unevaluated diff --git a/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-call-shadow.c b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-call-shadow.c new file mode 100644 index 0000000000000..1a9591658d884 --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-call-shadow.c @@ -0,0 +1,53 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +// Make sure that the correct variable (var_c in AST) is used when promoting +// the result of the call f(c) to __bidi_indexable pointer p. + +const int count = 16; +// CHECK: VarDecl [[var_count:0x[^ ]+]] {{.*}} count 'const int' +// CHECK: `-IntegerLiteral {{.+}} 16 + +typedef int *__counted_by(count) cnt_t(int count); +// CHECK: TypedefDecl {{.+}} referenced cnt_t 'int *__single __counted_by(count)(int)' +// CHECK: `-FunctionProtoType {{.+}} 'int *__single __counted_by(count)(int)' cdecl +// CHECK: |-CountAttributedType {{.+}} 'int *__single __counted_by(count)' sugar +// CHECK: | `-PointerType {{.+}} 'int *__single' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: `-BuiltinType {{.+}} 'int' + +void foo(cnt_t f, int c) { + int count = 32; + int *p = f(c); +} +// CHECK: FunctionDecl [[func_foo:0x[^ ]+]] {{.*}} foo 'void (cnt_t *__single, int)' +// CHECK: |-ParmVarDecl [[var_f:0x[^ ]+]] {{.*}} f 'cnt_t *__single' +// CHECK: |-ParmVarDecl [[var_c:0x[^ ]+]] {{.*}} c 'int' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_count_1:0x[^ ]+]] {{.*}} count 'int' +// CHECK: | `-IntegerLiteral {{.+}} 32 +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p:0x[^ ]+]] {{.*}} p 'int *__bidi_indexable' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_c]] +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'cnt_t *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f]] +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' diff --git a/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-call.c b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-call.c new file mode 100644 index 0000000000000..3bda5e47956e8 --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-call.c @@ -0,0 +1,263 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +typedef int *__counted_by(count) cnt_t(int count); + +void test_cnt_fn(cnt_t cnt_fn) { + int *p = cnt_fn(16); +} +// CHECK: FunctionDecl [[func_test_cnt_fn:0x[^ ]+]] {{.+}} test_cnt_fn +// CHECK: |-ParmVarDecl [[var_cnt_fn:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-IntegerLiteral {{.+}} 16 +// CHECK: | `-OpaqueValueExpr [[ove]] +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'cnt_t *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_cnt_fn]] +// CHECK: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __counted_by(count)':'int *__single' + +typedef int *__counted_by(count) (*cnt_ptr_t)(int count); + +void test_cnt_fn_ptr(cnt_ptr_t cnt_fn_ptr) { + int *q = cnt_fn_ptr(32); +} +// CHECK: FunctionDecl [[func_test_cnt_fn_ptr:0x[^ ]+]] {{.+}} test_cnt_fn_ptr +// CHECK: |-ParmVarDecl [[var_cnt_fn_ptr:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_q:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(count)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-IntegerLiteral {{.+}} 32 +// CHECK: | `-OpaqueValueExpr [[ove_2]] +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(count)(*__single)(int)' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_cnt_fn_ptr]] +// CHECK: | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: |-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(count)':'int *__single' + +typedef void *__sized_by(size) sz_t(unsigned size); + +void test_sz_fn(sz_t sz_fn) { + void *r = sz_fn(64); +} +// CHECK: FunctionDecl [[func_test_sz_fn:0x[^ ]+]] {{.+}} test_sz_fn +// CHECK: |-ParmVarDecl [[var_sz_fn:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_r:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned int' +// CHECK: | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-IntegerLiteral {{.+}} 64 +// CHECK: | `-OpaqueValueExpr [[ove_4]] +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'sz_t *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_sz_fn]] +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned int' +// CHECK: |-OpaqueValueExpr [[ove_5]] {{.*}} 'unsigned int' +// CHECK: `-OpaqueValueExpr [[ove_4]] {{.*}} 'void *__single __sized_by(size)':'void *__single' + +typedef void *__sized_by(size) (*sz_ptr_t)(unsigned size); + +void test_sz_fn_ptr(sz_ptr_t sz_fn_ptr) { + void *s = sz_fn_ptr(128); +} +// CHECK: FunctionDecl [[func_test_sz_fn_ptr:0x[^ ]+]] {{.+}} test_sz_fn_ptr +// CHECK: |-ParmVarDecl [[var_sz_fn_ptr:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_s:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'unsigned int' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | `-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | |-CStyleCastExpr {{.+}} 'char *' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'void *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__single __sized_by(size)':'void *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned int' +// CHECK: | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'unsigned int' +// CHECK: | | `-IntegerLiteral {{.+}} 128 +// CHECK: | `-OpaqueValueExpr [[ove_6]] +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void *__single __sized_by(size)(*__single)(unsigned int)' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_sz_fn_ptr]] +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned int' +// CHECK: |-OpaqueValueExpr [[ove_7]] {{.*}} 'unsigned int' +// CHECK: `-OpaqueValueExpr [[ove_6]] {{.*}} 'void *__single __sized_by(size)':'void *__single' + +int *endptr; +// CHECK: VarDecl [[var_endptr:0x[^ ]+]] +typedef int *__ended_by(end) ent_t(int *end); + +void test_ent_fn(ent_t ent_fn) { + int *p = ent_fn(endptr); +} +// CHECK: FunctionDecl [[func_test_ent_fn:0x[^ ]+]] {{.+}} test_ent_fn +// CHECK: |-ParmVarDecl [[var_ent_fn:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_p_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'ent_t *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_ent_fn]] +// CHECK: | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_8]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_endptr]] +// CHECK: `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single' + +typedef int *__ended_by(end) (*ent_ptr_t)(int *end); + +void test_ent_fn_ptr(ent_ptr_t ent_fn_ptr) { + int *q = ent_fn_ptr(endptr); +} +// CHECK: FunctionDecl [[func_test_ent_fn_ptr:0x[^ ]+]] {{.+}} test_ent_fn_ptr +// CHECK: |-ParmVarDecl [[var_ent_fn_ptr:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: `-DeclStmt +// CHECK: `-VarDecl [[var_q_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int *__single __ended_by(end)(*__single)(int *__single)' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_ent_fn_ptr]] +// CHECK: | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_9]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_endptr]] +// CHECK: `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__single' + +typedef void ent_param_t(const char *__ended_by(end) start, const char *end); + +void test_ent_param_fn(ent_param_t ent_param_fn) { + const char array[10]; + ent_param_fn(array, array + 10); +} +// CHECK: FunctionDecl [[func_test_ent_param_fn:0x[^ ]+]] {{.+}} test_ent_param_fn +// CHECK: |-ParmVarDecl [[var_ent_param_fn:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_array:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'ent_param_t *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_ent_param_fn]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_array]] +// CHECK: | `-OpaqueValueExpr [[ove_11]] +// CHECK: | `-BinaryOperator {{.+}} 'const char *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_array]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: |-OpaqueValueExpr [[ove_10]] {{.*}} 'const char *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_11]] {{.*}} 'const char *__bidi_indexable' + +typedef void (*ent_param_ptr_t)(const char *__ended_by(end) start, const char *end); + +void test_ent_param_fn_ptr(ent_param_ptr_t ent_param_fn_ptr) { + const char array[10]; + ent_param_fn_ptr(array, array + 10); +} +// CHECK: FunctionDecl [[func_test_ent_param_fn_ptr:0x[^ ]+]] {{.+}} test_ent_param_fn_ptr +// CHECK: |-ParmVarDecl [[var_ent_param_fn_ptr:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_array_1:0x[^ ]+]] +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(const char *__single __ended_by(end), const char *__single /* __started_by(start) */ )' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_ent_param_fn_ptr]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const char *__single __ended_by(end)':'const char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'const char *__single /* __started_by(start) */ ':'const char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *' +// CHECK: | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'const char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_array_1]] +// CHECK: | `-OpaqueValueExpr [[ove_13]] +// CHECK: | `-BinaryOperator {{.+}} 'const char *__bidi_indexable' '+' +// CHECK: | |-ImplicitCastExpr {{.+}} 'const char *__bidi_indexable' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_array_1]] +// CHECK: | `-IntegerLiteral {{.+}} 10 +// CHECK: |-OpaqueValueExpr [[ove_12]] {{.*}} 'const char *__bidi_indexable' +// CHECK: `-OpaqueValueExpr [[ove_13]] {{.*}} 'const char *__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-param.c b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-param.c new file mode 100644 index 0000000000000..c12666658cfc3 --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-param.c @@ -0,0 +1,116 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +typedef void cb_t(int *__counted_by(count), int count); +typedef void sb_t(void *__sized_by(size), int size); +typedef void eb_t(void *__ended_by(end) start, void *end); + +typedef void (*cb_ptr_t)(int *__counted_by(count), int count); +typedef void (*sb_ptr_t)(void *__sized_by(size), int size); +typedef void (*eb_ptr_t)(void *__ended_by(end) start, void *end); + +cb_ptr_t g_cb_ptr; +sb_ptr_t g_sb_ptr; +eb_ptr_t g_eb_ptr; + +cb_t *g_ptr_cb; +sb_t *g_ptr_sb; +eb_t *g_ptr_eb; + +void foo( + cb_ptr_t a_cb_ptr, + sb_ptr_t a_sb_ptr, + eb_ptr_t a_eb_ptr, + cb_t *a_ptr_cb, + sb_t *a_ptr_sb, + eb_t *a_ptr_eb) { + cb_ptr_t l_cb_ptr; + sb_ptr_t l_sb_ptr; + eb_ptr_t l_eb_ptr; + + cb_t *l_ptr_cb; + sb_t *l_ptr_sb; + eb_t *l_ptr_eb; +} + +// CHECK: |-TypedefDecl {{.+}} cb_t 'void (int *__single __counted_by(count), int)' +// CHECK: | `-FunctionProtoType {{.+}} 'void (int *__single __counted_by(count), int)' +// CHECK: | |-BuiltinType {{.+}} 'void' +// CHECK: | |-CountAttributedType {{.+}} 'int *__single __counted_by(count)' sugar +// CHECK: | | `-PointerType {{.+}} 'int *__single' +// CHECK: | | `-BuiltinType {{.+}} 'int' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} sb_t 'void (void *__single __sized_by(size), int)' +// CHECK: | `-FunctionProtoType {{.+}} 'void (void *__single __sized_by(size), int)' +// CHECK: | |-BuiltinType {{.+}} 'void' +// CHECK: | |-CountAttributedType {{.+}} 'void *__single __sized_by(size)' sugar +// CHECK: | | `-PointerType {{.+}} 'void *__single' +// CHECK: | | `-BuiltinType {{.+}} 'void' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} eb_t 'void (void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: | `-FunctionProtoType {{.+}} 'void (void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: | |-BuiltinType {{.+}} 'void' +// CHECK: | |-DynamicRangePointerType {{.+}} 'void *__single __ended_by(end)' sugar +// CHECK: | | `-PointerType {{.+}} 'void *__single' +// CHECK: | | `-BuiltinType {{.+}} 'void' +// CHECK: | `-DynamicRangePointerType {{.+}} 'void *__single /* __started_by(start) */ ' sugar +// CHECK: | `-PointerType {{.+}} 'void *__single' +// CHECK: | `-BuiltinType {{.+}} 'void' +// CHECK: |-TypedefDecl {{.+}} cb_ptr_t 'void (*)(int *__single __counted_by(count), int)' +// CHECK: | `-PointerType {{.+}} 'void (*)(int *__single __counted_by(count), int)' +// CHECK: | `-ParenType {{.+}} 'void (int *__single __counted_by(count), int)' sugar +// CHECK: | `-FunctionProtoType {{.+}} 'void (int *__single __counted_by(count), int)' +// CHECK: | |-BuiltinType {{.+}} 'void' +// CHECK: | |-CountAttributedType {{.+}} 'int *__single __counted_by(count)' sugar +// CHECK: | | `-PointerType {{.+}} 'int *__single' +// CHECK: | | `-BuiltinType {{.+}} 'int' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} sb_ptr_t 'void (*)(void *__single __sized_by(size), int)' +// CHECK: | `-PointerType {{.+}} 'void (*)(void *__single __sized_by(size), int)' +// CHECK: | `-ParenType {{.+}} 'void (void *__single __sized_by(size), int)' sugar +// CHECK: | `-FunctionProtoType {{.+}} 'void (void *__single __sized_by(size), int)' +// CHECK: | |-BuiltinType {{.+}} 'void' +// CHECK: | |-CountAttributedType {{.+}} 'void *__single __sized_by(size)' sugar +// CHECK: | | `-PointerType {{.+}} 'void *__single' +// CHECK: | | `-BuiltinType {{.+}} 'void' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} eb_ptr_t 'void (*)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: | `-PointerType {{.+}} 'void (*)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: | `-ParenType {{.+}} 'void (void *__single __ended_by(end), void *__single /* __started_by(start) */ )' sugar +// CHECK: | `-FunctionProtoType {{.+}} 'void (void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: | |-BuiltinType {{.+}} 'void' +// CHECK: | |-DynamicRangePointerType {{.+}} 'void *__single __ended_by(end)' sugar +// CHECK: | | `-PointerType {{.+}} 'void *__single' +// CHECK: | | `-BuiltinType {{.+}} 'void' +// CHECK: | `-DynamicRangePointerType {{.+}} 'void *__single /* __started_by(start) */ ' sugar +// CHECK: | `-PointerType {{.+}} 'void *__single' +// CHECK: | `-BuiltinType {{.+}} 'void' +// CHECK: |-VarDecl {{.+}} g_cb_ptr 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK: |-VarDecl {{.+}} g_sb_ptr 'void (*__single)(void *__single __sized_by(size), int)' +// CHECK: |-VarDecl {{.+}} g_eb_ptr 'void (*__single)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: |-VarDecl {{.+}} g_ptr_cb 'cb_t *__single' +// CHECK: |-VarDecl {{.+}} g_ptr_sb 'sb_t *__single' +// CHECK: |-VarDecl {{.+}} g_ptr_eb 'eb_t *__single' +// CHECK: `-FunctionDecl {{.+}} foo 'void (void (*__single)(int *__single __counted_by(count), int), void (*__single)(void *__single __sized_by(size), int), void (*__single)(void *__single __ended_by(end), void *__single /* __started_by(start) */ ), cb_t *__single, sb_t *__single, eb_t *__single)' +// CHECK: |-ParmVarDecl {{.+}} a_cb_ptr 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK: |-ParmVarDecl {{.+}} a_sb_ptr 'void (*__single)(void *__single __sized_by(size), int)' +// CHECK: |-ParmVarDecl {{.+}} a_eb_ptr 'void (*__single)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: |-ParmVarDecl {{.+}} a_ptr_cb 'cb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_ptr_sb 'sb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_ptr_eb 'eb_t *__single' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_cb_ptr 'void (*__single)(int *__single __counted_by(count), int)' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_sb_ptr 'void (*__single)(void *__single __sized_by(size), int)' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_eb_ptr 'void (*__single)(void *__single __ended_by(end), void *__single /* __started_by(start) */ )' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_ptr_cb 'cb_t *__single' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_ptr_sb 'sb_t *__single' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.+}} l_ptr_eb 'eb_t *__single' diff --git a/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-ret.c b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-ret.c new file mode 100644 index 0000000000000..2b0d664328168 --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-function-attrs-late-parsed-ret.c @@ -0,0 +1,139 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +typedef int *__counted_by(count) cb_t(int count); +typedef void *__sized_by(size) sb_t(int size); +typedef int *__ended_by(end) eb_t(int *end); + +typedef int *__counted_by(count) (*cb_ptr_t)(int count); +typedef void *__sized_by(size) (*sb_ptr_t)(int size); +typedef int *__ended_by(end) (*eb_ptr_t)(int *end); + +cb_t g_cb; +sb_t g_sb; +eb_t g_eb; + +cb_ptr_t g_cb_ptr; +sb_ptr_t g_sb_ptr; +eb_ptr_t g_eb_ptr; + +cb_t *g_ptr_cb; +sb_t *g_ptr_sb; +eb_t *g_ptr_eb; + +void foo( + cb_t a_cb, + sb_t a_sb, + eb_t a_eb, + cb_ptr_t a_cb_ptr, + sb_ptr_t a_sb_ptr, + eb_ptr_t a_eb_ptr, + cb_t *a_ptr_cb, + sb_t *a_ptr_sb, + eb_t *a_ptr_eb) { + cb_t l_cb; + sb_t l_sb; + eb_t l_eb; + + cb_ptr_t l_cb_ptr; + sb_ptr_t l_sb_ptr; + eb_ptr_t l_eb_ptr; + + cb_t *l_ptr_cb; + sb_t *l_ptr_sb; + eb_t *l_ptr_eb; +} + +// CHECK: |-TypedefDecl {{.+}} referenced cb_t 'int *__single __counted_by(count)(int)' +// CHECK: | `-FunctionProtoType {{.+}} 'int *__single __counted_by(count)(int)' cdecl +// CHECK: | |-CountAttributedType {{.+}} 'int *__single __counted_by(count)' sugar +// CHECK: | | `-PointerType {{.+}} 'int *__single' +// CHECK: | | `-BuiltinType {{.+}} 'int' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} referenced sb_t 'void *__single __sized_by(size)(int)' +// CHECK: | `-FunctionProtoType {{.+}} 'void *__single __sized_by(size)(int)' cdecl +// CHECK: | |-CountAttributedType {{.+}} 'void *__single __sized_by(size)' sugar +// CHECK: | | `-PointerType {{.+}} 'void *__single' +// CHECK: | | `-BuiltinType {{.+}} 'void' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} referenced eb_t 'int *__single __ended_by(end)(int *__single)' +// CHECK: | `-FunctionProtoType {{.+}} 'int *__single __ended_by(end)(int *__single)' cdecl +// CHECK: | |-DynamicRangePointerType {{.+}} 'int *__single __ended_by(end)' sugar +// CHECK: | | `-PointerType {{.+}} 'int *__single' +// CHECK: | | `-BuiltinType {{.+}} 'int' +// CHECK: | `-AttributedType {{.+}} 'int *__single' sugar +// CHECK: | `-PointerType {{.+}} 'int *__single' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} referenced cb_ptr_t 'int *__single __counted_by(count)(*)(int)' +// CHECK: | `-PointerType {{.+}} 'int *__single __counted_by(count)(*)(int)' +// CHECK: | `-ParenType {{.+}} 'int *__single __counted_by(count)(int)' sugar +// CHECK: | `-FunctionProtoType {{.+}} 'int *__single __counted_by(count)(int)' cdecl +// CHECK: | |-CountAttributedType {{.+}} 'int *__single __counted_by(count)' sugar +// CHECK: | | `-PointerType {{.+}} 'int *__single' +// CHECK: | | `-BuiltinType {{.+}} 'int' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} referenced sb_ptr_t 'void *__single __sized_by(size)(*)(int)' +// CHECK: | `-PointerType {{.+}} 'void *__single __sized_by(size)(*)(int)' +// CHECK: | `-ParenType {{.+}} 'void *__single __sized_by(size)(int)' sugar +// CHECK: | `-FunctionProtoType {{.+}} 'void *__single __sized_by(size)(int)' cdecl +// CHECK: | |-CountAttributedType {{.+}} 'void *__single __sized_by(size)' sugar +// CHECK: | | `-PointerType {{.+}} 'void *__single' +// CHECK: | | `-BuiltinType {{.+}} 'void' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-TypedefDecl {{.+}} referenced eb_ptr_t 'int *__single __ended_by(end)(*)(int *__single)' +// CHECK: | `-PointerType {{.+}} 'int *__single __ended_by(end)(*)(int *__single)' +// CHECK: | `-ParenType {{.+}} 'int *__single __ended_by(end)(int *__single)' sugar +// CHECK: | `-FunctionProtoType {{.+}} 'int *__single __ended_by(end)(int *__single)' cdecl +// CHECK: | |-DynamicRangePointerType {{.+}} 'int *__single __ended_by(end)' sugar +// CHECK: | | `-PointerType {{.+}} 'int *__single' +// CHECK: | | `-BuiltinType {{.+}} 'int' +// CHECK: | `-AttributedType {{.+}} 'int *__single' sugar +// CHECK: | `-PointerType {{.+}} 'int *__single' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: |-FunctionDecl {{.+}} g_cb 'int *__single __counted_by(count)(int)' +// CHECK: | `-ParmVarDecl {{.+}} implicit used count 'int' +// CHECK: |-FunctionDecl {{.+}} g_sb 'void *__single __sized_by(size)(int)' +// CHECK: | `-ParmVarDecl {{.+}} implicit used size 'int' +// CHECK: |-FunctionDecl {{.+}} g_eb 'int *__single __ended_by(end)(int *__single)' +// CHECK: | `-ParmVarDecl {{.+}} implicit used end 'int *__single' +// CHECK: |-VarDecl {{.+}} g_cb_ptr 'int *__single __counted_by(count)(*__single)(int)' +// CHECK: |-VarDecl {{.+}} g_sb_ptr 'void *__single __sized_by(size)(*__single)(int)' +// CHECK: |-VarDecl {{.+}} g_eb_ptr 'int *__single __ended_by(end)(*__single)(int *__single)' +// CHECK: |-VarDecl {{.+}} g_ptr_cb 'cb_t *__single' +// CHECK: |-VarDecl {{.+}} g_ptr_sb 'sb_t *__single' +// CHECK: |-VarDecl {{.+}} g_ptr_eb 'eb_t *__single' +// CHECK: `-FunctionDecl {{.+}} foo 'void (cb_t *__single, sb_t *__single, eb_t *__single, int *__single __counted_by(count)(*__single)(int), void *__single __sized_by(size)(*__single)(int), int *__single __ended_by(end)(*__single)(int *__single), cb_t *__single, sb_t *__single, eb_t *__single)' +// CHECK: |-ParmVarDecl {{.+}} a_cb 'cb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_sb 'sb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_eb 'eb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_cb_ptr 'int *__single __counted_by(count)(*__single)(int)' +// CHECK: |-ParmVarDecl {{.+}} a_sb_ptr 'void *__single __sized_by(size)(*__single)(int)' +// CHECK: |-ParmVarDecl {{.+}} a_eb_ptr 'int *__single __ended_by(end)(*__single)(int *__single)' +// CHECK: |-ParmVarDecl {{.+}} a_ptr_cb 'cb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_ptr_sb 'sb_t *__single' +// CHECK: |-ParmVarDecl {{.+}} a_ptr_eb 'eb_t *__single' +// CHECK: `-CompoundStmt {{.+}} +// CHECK: |-DeclStmt +// CHECK: | `-FunctionDecl {{.+}} l_cb 'int *__single __counted_by(count)(int)' +// CHECK: | `-ParmVarDecl {{.+}} implicit used count 'int' +// CHECK: |-DeclStmt +// CHECK: | `-FunctionDecl {{.+}} l_sb 'void *__single __sized_by(size)(int)' +// CHECK: | `-ParmVarDecl {{.+}} implicit used size 'int' +// CHECK: |-DeclStmt +// CHECK: | `-FunctionDecl {{.+}} l_eb 'int *__single __ended_by(end)(int *__single)' +// CHECK: | `-ParmVarDecl {{.+}} implicit used end 'int *__single' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_cb_ptr 'int *__single __counted_by(count)(*__single)(int)' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_sb_ptr 'void *__single __sized_by(size)(*__single)(int)' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_eb_ptr 'int *__single __ended_by(end)(*__single)(int *__single)' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_ptr_cb 'cb_t *__single' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} l_ptr_sb 'sb_t *__single' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.+}} l_ptr_eb 'eb_t *__single' diff --git a/clang/test/BoundsSafety/AST/typedef-function-bounds-attributed.c b/clang/test/BoundsSafety/AST/typedef-function-bounds-attributed.c new file mode 100644 index 0000000000000..40de590df6df8 --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-function-bounds-attributed.c @@ -0,0 +1,155 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -ast-dump %s 2>&1 | FileCheck %s + +#include + +/* typeof */ + +// CHECK: FunctionDecl {{.+}} to_cb_in_in 'void (int *{{.*}} __counted_by(len), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} p 'int *{{.*}} __counted_by(len)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used len 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +// CHECK-NEXT: FunctionDecl {{.+}} to_cb_in_in_x 'void (int *{{.*}} __counted_by(len), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit 'int *{{.*}} __counted_by(len)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used len 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +void to_cb_in_in(int *__counted_by(len) p, int len); +__typeof__(to_cb_in_in) to_cb_in_in_x; + +// CHECK: FunctionDecl {{.+}} to_cb_in_out 'void (int *{{.*}} __counted_by(*len), int *{{.*}})' +// CHECK-NEXT: |-ParmVarDecl {{.+}} p 'int *{{.*}} __counted_by(*len)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used len 'int *{{.*}}' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit IsDeref {{.+}} 0 +// CHECK-NEXT: FunctionDecl {{.+}} to_cb_in_out_x 'void (int *{{.*}} __counted_by(*len), int *{{.*}})' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit 'int *{{.*}} __counted_by(*len)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used len 'int *{{.*}}' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit IsDeref {{.+}} 0 +void to_cb_in_out(int *__counted_by(*len) p, int *len); +__typeof__(to_cb_in_out) to_cb_in_out_x; + +// CHECK: FunctionDecl {{.+}} to_cb_out_in 'void (int *{{.*}} __counted_by(len)*{{.*}}, int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} p 'int *{{.*}} __counted_by(len)*{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used len 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 1 +// CHECK-NEXT: FunctionDecl {{.+}} to_cb_out_in_x 'void (int *{{.*}} __counted_by(len)*{{.*}}, int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit 'int *{{.*}} __counted_by(len)*{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used len 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 1 +void to_cb_out_in(int *__counted_by(len) * p, int len); +__typeof__(to_cb_out_in) to_cb_out_in_x; + +// CHECK: FunctionDecl {{.+}} to_cb_out_out 'void (int *{{.*}} __counted_by(*len)*{{.*}}, int *{{.*}})' +// CHECK-NEXT: |-ParmVarDecl {{.+}} p 'int *{{.*}} __counted_by(*len)*{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used len 'int *{{.*}}' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit IsDeref {{.+}} 1 +// CHECK-NEXT: FunctionDecl {{.+}} to_cb_out_out_x 'void (int *{{.*}} __counted_by(*len)*{{.*}}, int *{{.*}})' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit 'int *{{.*}} __counted_by(*len)*{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used len 'int *{{.*}}' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit IsDeref {{.+}} 1 +void to_cb_out_out(int *__counted_by(*len) * p, int *len); +__typeof__(to_cb_out_out) to_cb_out_out_x; + +// CHECK: FunctionDecl {{.+}} to_cb_ret 'int *{{.*}} __counted_by(len)(int)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used len 'int' +// CHECK-NEXT: FunctionDecl {{.+}} to_cb_ret_x 'int *{{.*}} __counted_by(len)(int)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used len 'int' +int *__counted_by(len) to_cb_ret(int len); +__typeof__(to_cb_ret) to_cb_ret_x; + +// CHECK: FunctionDecl {{.+}} to_sb_in_in 'void (void *{{.*}} __sized_by(size), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} p 'void *{{.*}} __sized_by(size)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used size 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +// CHECK-NEXT: FunctionDecl {{.+}} to_sb_in_in_x 'void (void *{{.*}} __sized_by(size), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit 'void *{{.*}} __sized_by(size)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used size 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +void to_sb_in_in(void *__sized_by(size) p, int size); +__typeof__(to_sb_in_in) to_sb_in_in_x; + +// CHECK: FunctionDecl {{.+}} to_cbn_in_in 'void (int *{{.*}} __counted_by_or_null(len), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} p 'int *{{.*}} __counted_by_or_null(len)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used len 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +// CHECK-NEXT: FunctionDecl {{.+}} to_cbn_in_in_x 'void (int *{{.*}} __counted_by_or_null(len), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit 'int *{{.*}} __counted_by_or_null(len)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used len 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +void to_cbn_in_in(int *__counted_by_or_null(len) p, int len); +__typeof__(to_cbn_in_in) to_cbn_in_in_x; + +// CHECK: FunctionDecl {{.+}} to_eb 'void (int *{{.*}} __ended_by(end), int *{{.*}} /* __started_by(start) */ )' +// CHECK-NEXT: |-ParmVarDecl {{.+}} used start 'int *{{.*}} __ended_by(end)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} used end 'int *{{.*}} /* __started_by(start) */ ' +// CHECK-NEXT: FunctionDecl {{.+}} to_eb_x 'void (int *{{.*}} __ended_by(end), int *{{.*}} /* __started_by(start) */ )' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit used start 'int *{{.*}} __ended_by(end)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used end 'int *{{.*}} /* __started_by(start) */ ' +void to_eb(int *__ended_by(end) start, int *end); +__typeof__(to_eb) to_eb_x; + +/* typedef */ + +// CHECK: TypedefDecl {{.+}} td_cb_in_in 'void (int *{{.*}} __counted_by(len), int)' +// CHECK: FunctionDecl {{.+}} tb_cb_in_in_x 'void (int *{{.*}} __counted_by(len), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit 'int *{{.*}} __counted_by(len)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used len 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +typedef void(td_cb_in_in)(int *__counted_by(len) p, int len); +td_cb_in_in tb_cb_in_in_x; + +// CHECK: TypedefDecl {{.+}} td_cb_in_out 'void (int *{{.*}} __counted_by(*len), int *{{.*}})' +// CHECK: FunctionDecl {{.+}} td_cb_in_out_x 'void (int *{{.*}} __counted_by(*len), int *{{.*}})' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit 'int *{{.*}} __counted_by(*len)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used len 'int *{{.*}}' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit IsDeref {{.+}} 0 +typedef void(td_cb_in_out)(int *__counted_by(*len) p, int *len); +td_cb_in_out td_cb_in_out_x; + +// CHECK: TypedefDecl {{.+}} td_cb_out_in 'void (int *{{.*}} __counted_by(len)*{{.*}}, int)' +// CHECK: FunctionDecl {{.+}} td_cb_out_in_x 'void (int *{{.*}} __counted_by(len)*{{.*}}, int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit 'int *{{.*}} __counted_by(len)*{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used len 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 1 +typedef void(td_cb_out_in)(int *__counted_by(len) * p, int len); +td_cb_out_in td_cb_out_in_x; + +// CHECK: TypedefDecl {{.+}} td_cb_out_out 'void (int *{{.*}} __counted_by(*len)*{{.*}}, int *{{.*}})' +// CHECK: FunctionDecl {{.+}} td_cb_out_out_x 'void (int *{{.*}} __counted_by(*len)*{{.*}}, int *{{.*}})' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit 'int *{{.*}} __counted_by(*len)*{{.*}}' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used len 'int *{{.*}}' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit IsDeref {{.+}} 1 +typedef void(td_cb_out_out)(int *__counted_by(*len) * p, int *len); +td_cb_out_out td_cb_out_out_x; + +// CHECK: TypedefDecl {{.+}} td_cb_ret 'int *{{.*}} __counted_by(len)(int)' +// CHECK: FunctionDecl {{.+}} td_cb_ret_x 'int *{{.*}} __counted_by(len)(int)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used len 'int' +typedef int *__counted_by(len)(td_cb_ret)(int len); +td_cb_ret td_cb_ret_x; + +// CHECK: TypedefDecl {{.+}} td_sb_in_in 'void (void *{{.*}} __sized_by(size), int)' +// CHECK: FunctionDecl {{.+}} tb_sb_in_in_x 'void (void *{{.*}} __sized_by(size), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit 'void *{{.*}} __sized_by(size)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used size 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +typedef void(td_sb_in_in)(void *__sized_by(size) p, int size); +td_sb_in_in tb_sb_in_in_x; + +// CHECK: TypedefDecl {{.+}} td_cbn_in_in 'void (int *{{.*}} __counted_by_or_null(len), int)' +// CHECK: FunctionDecl {{.+}} tb_cbn_in_in_x 'void (int *{{.*}} __counted_by_or_null(len), int)' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit 'int *{{.*}} __counted_by_or_null(len)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used len 'int' +// CHECK-NEXT: `-DependerDeclsAttr {{.+}} Implicit {{.+}} 0 +typedef void(td_cbn_in_in)(int *__counted_by_or_null(len) p, int len); +td_cbn_in_in tb_cbn_in_in_x; + +// CHECK: TypedefDecl {{.+}} td_eb 'void (int *{{.*}} __ended_by(end), int *{{.*}} /* __started_by(start) */ )' +// CHECK: FunctionDecl {{.+}} td_eb_x 'void (int *{{.*}} __ended_by(end), int *{{.*}} /* __started_by(start) */ )' +// CHECK-NEXT: |-ParmVarDecl {{.+}} implicit used start 'int *{{.*}} __ended_by(end)' +// CHECK-NEXT: `-ParmVarDecl {{.+}} implicit used end 'int *{{.*}} /* __started_by(start) */ ' +typedef void(td_eb)(int *__ended_by(end) start, int *end); +td_eb td_eb_x; diff --git a/clang/test/BoundsSafety/AST/typedef-function-pointer-attrs.c b/clang/test/BoundsSafety/AST/typedef-function-pointer-attrs.c new file mode 100644 index 0000000000000..092334a2c8ba2 --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-function-pointer-attrs.c @@ -0,0 +1,76 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +typedef int *(*fp_proto_t)(int *p); +typedef int *(*fp_proto_va_t)(int *p, ...); +typedef int *(*fp_no_proto_t)(); + +// CHECK: VarDecl {{.+}} g_var_proto 'int *__single(*__single)(int *__single)' +fp_proto_t g_var_proto; + +// CHECK: VarDecl {{.+}} g_var_proto_va 'int *__single(*__single)(int *__single, ...)' +fp_proto_va_t g_var_proto_va; + +// CHECK: VarDecl {{.+}} g_var_no_proto 'int *__single(*__single)()' +fp_no_proto_t g_var_no_proto; + +void foo(void) { + // CHECK: VarDecl {{.+}} l_var_proto 'int *__single(*__single)(int *__single)' + fp_proto_t l_var_proto; + + // CHECK: VarDecl {{.+}} l_var_proto_va 'int *__single(*__single)(int *__single, ...)' + fp_proto_va_t l_var_proto_va; + + // CHECK: VarDecl {{.+}} l_var_no_proto 'int *__single(*__single)()' + fp_no_proto_t l_var_no_proto; +} + +// CHECK: ParmVarDecl {{.+}} param_proto 'int *__single(*__single)(int *__single)' +void f1(fp_proto_t param_proto); + +// CHECK: ParmVarDecl {{.+}} param_proto_va 'int *__single(*__single)(int *__single, ...)' +void f2(fp_proto_va_t param_proto_va); + +// CHECK: ParmVarDecl {{.+}} param_no_proto 'int *__single(*__single)()' +void f3(fp_no_proto_t param_no_proto); + +// CHECK: FunctionDecl {{.+}} ret_proto 'int *__single(*__single(void))(int *__single)' +fp_proto_t ret_proto(void); + +// CHECK: FunctionDecl {{.+}} ret_proto_va 'int *__single(*__single(void))(int *__single, ...)' +fp_proto_va_t ret_proto_va(void); + +// CHECK: FunctionDecl {{.+}} ret_no_proto 'int *__single(*__single(void))()' +fp_no_proto_t ret_no_proto(void); + +struct bar { + // CHECK: FieldDecl {{.+}} field_proto 'int *__single(*__single)(int *__single)' + fp_proto_t field_proto; + + // CHECK: FieldDecl {{.+}} field_proto_va 'int *__single(*__single)(int *__single, ...)' + fp_proto_va_t field_proto_va; + + // CHECK: FieldDecl {{.+}} field_no_proto 'int *__single(*__single)()' + fp_no_proto_t field_no_proto; +}; + +typedef fp_proto_t (*nested_t)(fp_proto_va_t p1, fp_no_proto_t p2); + +// CHECK: VarDecl {{.+}} g_nested 'int *__single(*__single(*__single)(int *__single(*__single)(int *__single, ...), int *__single(*__single)()))(int *__single)' +nested_t g_nested; + +typedef int *(f_proto_t)(int *p); +typedef int *(f_proto_va_t)(int *p, ...); +typedef int *(f_no_proto_t)(); + +// CHECK: FunctionDecl {{.+}} f_proto 'int *__single(int *__single)' +f_proto_t f_proto; + +// CHECK: FunctionDecl {{.+}} f_proto_va 'int *__single(int *__single, ...)' +f_proto_va_t f_proto_va; + +// CHECK: FunctionDecl {{.+}} f_no_proto 'int *__single()' +f_no_proto_t f_no_proto; diff --git a/clang/test/BoundsSafety/AST/typedef-pointer-attrs-explicit.c b/clang/test/BoundsSafety/AST/typedef-pointer-attrs-explicit.c new file mode 100644 index 0000000000000..40a35b228bedf --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-pointer-attrs-explicit.c @@ -0,0 +1,110 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +typedef int * unspecified_ptr_t; +typedef int *__single single_ptr_t; +typedef int *__bidi_indexable bidi_ptr_t; +typedef int *__unsafe_indexable unsafe_ptr_t; +typedef int *__unsafe_indexable* unsafe_ptr_ptr_t; +typedef single_ptr_t * single_ptr_ptr_t; +typedef unspecified_ptr_t * unspecified_ptr_ptr_t; +typedef int *__null_terminated term_ptr_t; + +unspecified_ptr_t g_single1; +single_ptr_t g_single2; +bidi_ptr_t g_bidi; +unsafe_ptr_t g_unsafe; +unspecified_ptr_ptr_t g_single_single; +term_ptr_t g_term; + +void foo(unspecified_ptr_t a_single1, + single_ptr_t a_single2, + bidi_ptr_t a_bidi, + unsafe_ptr_t a_unsafe, + term_ptr_t a_term) +{ + unspecified_ptr_t l_bidi1; + single_ptr_t l_single; + bidi_ptr_t l_bidi2; + unsafe_ptr_t l_unsafe; + + unspecified_ptr_t __single l_single2; + unspecified_ptr_t __unsafe_indexable l_unsafe2; + + unsafe_ptr_ptr_t l_unsafe_bidi; + single_ptr_ptr_t l_single_bidi1; + unspecified_ptr_ptr_t l_single_bidi2; + + // initialize to avoid 'must be initialized error' + term_ptr_t l_term = a_term; +} + +// CHECK: |-TypedefDecl {{.*}} referenced unspecified_ptr_t 'int *' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced single_ptr_t 'int *__single' +// CHECK: | `-PointerType {{.*}} 'int *__single' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced bidi_ptr_t 'int *__bidi_indexable' +// CHECK: | `-PointerType {{.*}} 'int *__bidi_indexable' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced unsafe_ptr_t 'int *__unsafe_indexable' +// CHECK: | `-PointerType {{.*}} 'int *__unsafe_indexable' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced unsafe_ptr_ptr_t 'int *__unsafe_indexable*' +// CHECK: | `-PointerType {{.*}} 'int *__unsafe_indexable*' +// CHECK: | `-PointerType {{.*}} 'int *__unsafe_indexable' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced single_ptr_ptr_t 'single_ptr_t *' +// CHECK: | `-PointerType {{.*}} 'single_ptr_t *' +// CHECK: | `-TypedefType {{.*}} 'single_ptr_t' sugar +// CHECK: | |-Typedef {{.*}} 'single_ptr_t' +// CHECK: | `-PointerType {{.*}} 'int *__single' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced unspecified_ptr_ptr_t 'unspecified_ptr_t *' +// CHECK: | `-PointerType {{.*}} 'unspecified_ptr_t *' +// CHECK: | `-TypedefType {{.*}} 'unspecified_ptr_t' sugar +// CHECK: | |-Typedef {{.*}} 'unspecified_ptr_t' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} term_ptr_t 'int * __terminated_by(0)':'int *' +// CHECK: | `-ValueTerminatedType {{.*}} 'int * __terminated_by(0)' sugar +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-VarDecl {{.*}} g_single1 'int *__single' +// CHECK: |-VarDecl {{.*}} g_single2 'single_ptr_t':'int *__single' +// CHECK: |-VarDecl {{.*}} g_bidi 'bidi_ptr_t':'int *__bidi_indexable' +// CHECK: |-VarDecl {{.*}} g_unsafe 'unsafe_ptr_t':'int *__unsafe_indexable' +// CHECK: |-VarDecl {{.*}} g_single_single 'int *__single*__single' +// CHECK: |-VarDecl {{.*}} g_term 'int *__single __terminated_by(0)':'int *__single' +// CHECK: `-FunctionDecl {{.*}} foo 'void (int *__single, single_ptr_t, bidi_ptr_t, unsafe_ptr_t, int *__single __terminated_by(0))' +// CHECK: |-ParmVarDecl {{.*}} a_single1 'int *__single' +// CHECK: |-ParmVarDecl {{.*}} a_single2 'single_ptr_t':'int *__single' +// CHECK: |-ParmVarDecl {{.*}} a_bidi 'bidi_ptr_t':'int *__bidi_indexable' +// CHECK: |-ParmVarDecl {{.*}} a_unsafe 'unsafe_ptr_t':'int *__unsafe_indexable' +// CHECK: |-ParmVarDecl {{.*}} a_term 'int *__single __terminated_by(0)':'int *__single' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_bidi1 'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_single 'single_ptr_t':'int *__single' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_bidi2 'bidi_ptr_t':'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_unsafe 'unsafe_ptr_t':'int *__unsafe_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_single2 'unspecified_ptr_t __single':'int *__single' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_unsafe2 'unspecified_ptr_t __unsafe_indexable':'int *__unsafe_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_unsafe_bidi 'int *__unsafe_indexable*__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_single_bidi1 'single_ptr_t *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} l_single_bidi2 'int *__single*__bidi_indexable' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} l_term 'int *__single __terminated_by(0)':'int *__single' diff --git a/clang/test/BoundsSafety/AST/typedef-pointer-attrs-late-parsed.c b/clang/test/BoundsSafety/AST/typedef-pointer-attrs-late-parsed.c new file mode 100644 index 0000000000000..d5d8f1e40b932 --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-pointer-attrs-late-parsed.c @@ -0,0 +1,48 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump -verify %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -verify %s 2>&1 | FileCheck %s + +#include + +typedef char * cptr_t; +typedef long long int64_t; + +void test_fdecl1(cptr_t __counted_by(len), int len); +// CHECK: FunctionDecl {{.*}} test_fdecl1 'void (char *__single __counted_by(len), int)' +// CHECK: |-ParmVarDecl {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: `-ParmVarDecl {{.*}} used len 'int' +// CHECK: `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 + + +void test_fdecl2(unsigned size, cptr_t __sized_by(size)); +// CHECK: FunctionDecl {{.*}} test_fdecl2 'void (unsigned int, char *__single __sized_by(size))' +// CHECK: |-ParmVarDecl {{.*}} used size 'unsigned int' +// CHECK: | `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 +// CHECK: `-ParmVarDecl {{.*}} 'char *__single __sized_by(size)':'char *__single' + +void test_fdecl_err1(int64_t __counted_by(len), int len); +// expected-error@-1{{'__counted_by' attribute only applies to pointer arguments}} + +int glen; +void test_fdecl_err2(cptr_t __counted_by(glen)); +// expected-error@-1{{count expression in function declaration may only reference parameters of that function}} + +struct test_count_fields { + cptr_t __sized_by(len) ptr; + int64_t len; +}; +// CHECK: RecordDecl {{.*}} struct test_count_fields definition +// CHECK: |-FieldDecl {{.*}} ptr 'char *__single __sized_by(len)':'char *__single' +// CHECK: `-FieldDecl {{.*}} referenced len 'int64_t':'long long' +// CHECK: `-DependerDeclsAttr {{.*}} Implicit {{.*}} 0 + +struct test_range_fields { + cptr_t __ended_by(end) iter; + cptr_t __ended_by(iter) start; + cptr_t end; +}; +// CHECK: RecordDecl {{.*}} struct test_range_fields definition +// CHECK: FieldDecl {{.*}} referenced iter 'char *__single __ended_by(end) /* __started_by(start) */ ':'char *__single' +// CHECK: FieldDecl {{.*}} referenced start 'char *__single __ended_by(iter)':'char *__single' +// CHECK: FieldDecl {{.*}} referenced end 'char *__single /* __started_by(iter) */ ':'char *__single' diff --git a/clang/test/BoundsSafety/AST/typedef-pointer-attrs.c b/clang/test/BoundsSafety/AST/typedef-pointer-attrs.c new file mode 100644 index 0000000000000..fe2cee0b1b2c0 --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-pointer-attrs.c @@ -0,0 +1,74 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include +#include "mock-typedef-header.h" + +typedef int * giptr_t; +typedef int ** giptr_ptr_t; +typedef siptr_t * gptr_siptr_t; + +giptr_t gip; +giptr_ptr_t gipp; +gptr_siptr_t gsipp; + +void foo() { + giptr_t gip_local; + giptr_ptr_t gipp_local; + + typedef char ** lcptr_ptr_t; + lcptr_ptr_t lcptr_local; + + siptr_t sip_local; + + typedef int ** giptr_ptr_t; + giptr_ptr_t gipp_local_redecl; +} + +// CHECK: |-TypedefDecl {{.*}} referenced siptr_t 'int *' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} siptr_ptr_t 'int **' +// CHECK: | `-PointerType {{.*}} 'int **' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced giptr_t 'int *' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced giptr_ptr_t 'int **' +// CHECK: | `-PointerType {{.*}} 'int **' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} gptr_siptr_t 'siptr_t *' +// CHECK: | `-PointerType {{.*}} 'siptr_t *' +// CHECK: | `-TypedefType {{.*}} 'siptr_t' sugar +// CHECK: | |-Typedef {{.*}} 'siptr_t' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-VarDecl {{.*}} gip 'int *__single' +// CHECK: |-VarDecl {{.*}} gipp 'int *__single*__single' +// CHECK: |-VarDecl {{.*}} gsipp 'int *__single*__single' +// CHECK: `-FunctionDecl {{.*}} foo 'void ()' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} gip_local 'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} gipp_local 'int *__single*__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-TypedefDecl {{.*}} referenced lcptr_ptr_t 'char **' +// CHECK: | `-PointerType {{.*}} 'char **' +// CHECK: | `-PointerType {{.*}} 'char *' +// CHECK: | `-BuiltinType {{.*}} 'char' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} lcptr_local 'char *__single*__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} sip_local 'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-TypedefDecl {{.*}} referenced giptr_ptr_t 'int **' +// CHECK: | `-PointerType {{.*}} 'int **' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} gipp_local_redecl 'int *__single*__bidi_indexable' diff --git a/clang/test/BoundsSafety/AST/typedef-quals.c b/clang/test/BoundsSafety/AST/typedef-quals.c new file mode 100644 index 0000000000000..1bef64662454d --- /dev/null +++ b/clang/test/BoundsSafety/AST/typedef-quals.c @@ -0,0 +1,54 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s + +#include + +typedef int *my_ptr_t; +typedef my_ptr_t const my_cptr_t; + +typedef int *__indexable my_iptr_t; +typedef my_iptr_t const my_ciptr_t; + +void foo(void) { + my_ptr_t bp1; + my_cptr_t bp2; + const my_ptr_t bp3; + + my_iptr_t ip1; + my_ciptr_t ip2; + const my_iptr_t ip3; +} + +// CHECK: |-TypedefDecl {{.*}} referenced my_ptr_t 'int *' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced my_cptr_t 'const my_ptr_t':'int *const' +// CHECK: | `-QualType {{.*}} 'const my_ptr_t' const +// CHECK: | `-TypedefType {{.*}} 'my_ptr_t' sugar +// CHECK: | |-Typedef {{.*}} 'my_ptr_t' +// CHECK: | `-PointerType {{.*}} 'int *' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced my_iptr_t 'int *__indexable' +// CHECK: | `-PointerType {{.*}} 'int *__indexable' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: |-TypedefDecl {{.*}} referenced my_ciptr_t 'const my_iptr_t':'int *__indexableconst' +// CHECK: | `-QualType {{.*}} 'const my_iptr_t' const +// CHECK: | `-TypedefType {{.*}} 'my_iptr_t' sugar +// CHECK: | |-Typedef {{.*}} 'my_iptr_t' +// CHECK: | `-PointerType {{.*}} 'int *__indexable' +// CHECK: | `-BuiltinType {{.*}} 'int' +// CHECK: `-FunctionDecl {{.*}} foo 'void (void)' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} bp1 'int *__bidi_indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} bp2 'int *__bidi_indexableconst' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} bp3 'int *__bidi_indexableconst' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} ip1 'my_iptr_t':'int *__indexable' +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.*}} ip2 'my_ciptr_t':'int *__indexableconst' +// CHECK: `-DeclStmt +// CHECK: `-VarDecl {{.*}} ip3 'const my_iptr_t':'int *__indexableconst' diff --git a/clang/test/BoundsSafety/AST/unify-auto.cpp b/clang/test/BoundsSafety/AST/unify-auto.cpp new file mode 100644 index 0000000000000..60263944c19ad --- /dev/null +++ b/clang/test/BoundsSafety/AST/unify-auto.cpp @@ -0,0 +1,28 @@ + + +// RUN: %clang_cc1 -x c++ -ast-dump -fbounds-safety -fbounds-attributes-cxx-experimental -verify %s | FileCheck %s + +#include + +typedef char * __terminated_by('\0') A; +typedef char * __terminated_by((char)0) B; + +A funcA(); +B funcB(); + +// CHECK: |-FunctionDecl {{.*}} testFunc 'char *__single __terminated_by('\x00')(int)' +auto testFunc(int pred) { + if (pred) return funcA(); + return funcB(); +} + +// CHECK: |-FunctionDecl {{.*}} testFunc2 'char *__single __counted_by(10)*__single(int, char *__single __counted_by(10)*__single, char *__single __counted_by(10)*__single)' +auto testFunc2(int pred, char * __counted_by(10) * c, char * __counted_by(10) * d) { + if (pred) return c; + return d; +} + +auto testFunc3(int pred, char * __counted_by(7) * c, char * __counted_by(10) * d) { + if (pred) return c; + return d; // expected-error{{'auto' in return type deduced as 'char *__single __counted_by(10)*__single' (aka 'char *__single*__single') here but deduced as 'char *__single __counted_by(7)*__single' (aka 'char *__single*__single') in earlier return statement}} +} diff --git a/clang/test/BoundsSafety/AST/unify-conditional-expr-types.c b/clang/test/BoundsSafety/AST/unify-conditional-expr-types.c new file mode 100644 index 0000000000000..0df721fca582a --- /dev/null +++ b/clang/test/BoundsSafety/AST/unify-conditional-expr-types.c @@ -0,0 +1,579 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -verify %s | FileCheck %s +#include + +typedef char * __terminated_by('\0') A; +typedef char * __terminated_by((char)0) B; + +A funcA(); +B funcB(); + +char testFunc(int pred) { + A foo = pred ? funcA() : funcB(); + return foo[0]; +} + +// CHECK: |-FunctionDecl [[func_funcA:0x[^ ]+]] {{.+}} funcA +// CHECK: |-FunctionDecl [[func_funcB:0x[^ ]+]] {{.+}} funcB +// CHECK: |-FunctionDecl [[func_testFunc:0x[^ ]+]] {{.+}} testFunc +// CHECK: | |-ParmVarDecl [[var_pred:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_foo:0x[^ ]+]] +// CHECK: | | `-ConditionalOperator {{.+}}'char *__single __terminated_by('\x00')':'char *__single' +// bHECK: | | |-ImplicitCastExpr {{.+}} 'bool' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_pred]] +// CHECK: | | |-CallExpr +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by('\x00')(*__single)()' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_funcA]] +// CHECK: | | `-CallExpr +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by((char)0)(*__single)()' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_funcB]] +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'char' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_foo]] +// CHECK: | `-IntegerLiteral {{.+}} 0 + +char testFunc2(int pred, char * __counted_by(cc) c, char * __single d, int cc, int dc) { + char * __counted_by(5) foo = pred ? c : d; + return foo[3]; +} +// CHECK: |-FunctionDecl [[func_testFunc2:0x[^ ]+]] {{.+}} testFunc2 +// CHECK: | |-ParmVarDecl [[var_pred_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_c:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_d:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_cc:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_dc:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_foo_1:0x[^ ]+]] +// CHECK: | | `-BoundsCheckExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(5)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'long' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-ConditionalOperator {{.+}} 'char *__bidi_indexable' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_pred_1]] +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_c]] +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_cc]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_d]] +// CHECK: | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 5 +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'char' +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'char *__single __counted_by(5)':'char *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'char *__single __counted_by(5)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(5)':'char *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_foo_1]] +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | | `-IntegerLiteral {{.+}} 5 +// CHECK: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'char *__single __counted_by(5)':'char *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | `-IntegerLiteral {{.+}} 3 + +char testFunc3(int pred, char * __ended_by(*f) * e, char * __ended_by(g) * f, char * g) { + // expected-error@+1{{conditional expression evaluates values with incompatible nested pointer types 'char *__single __ended_by(*f)*__single' (aka 'char *__single*__single') and 'char *__single __ended_by(g) /* __started_by(*e) */ *__single' (aka 'char *__single*__single')}} + char ** foo = pred ? e : f; + const char * const bar = *foo; + return bar[2]; +} + +// CHECK: |-FunctionDecl [[func_testFunc3:0x[^ ]+]] {{.+}} testFunc3 +// CHECK: | |-ParmVarDecl [[var_pred_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_e:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_f:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_g:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_foo_2:0x[^ ]+]] +// CHECK: | | `-RecoveryExpr +// CHECK: | | |-DeclRefExpr {{.+}} [[var_pred_2]] +// CHECK: | | |-DeclRefExpr {{.+}} [[var_e]] +// CHECK: | | `-DeclRefExpr {{.+}} [[var_f]] +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_bar:0x[^ ]+]] +// CHECK: | | `-ImplicitCastExpr {{.+}} contains-errors +// CHECK: | | `-ImplicitCastExpr {{.+}} contains-errors +// CHECK: | | `-ImplicitCastExpr {{.+}} contains-errors +// CHECK: | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | `-ImplicitCastExpr {{.+}} contains-errors +// CHECK: | | `-DeclRefExpr {{.+}} [[var_foo_2]] +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} contains-errors +// CHECK: | `-ArraySubscriptExpr +// CHECK: | |-ImplicitCastExpr {{.+}} contains-errors +// CHECK: | | `-DeclRefExpr {{.+}} [[var_bar]] +// CHECK: | `-IntegerLiteral {{.+}} 2 + +char externFunc(char * __counted_by(8)); + +// CHECK: |-FunctionDecl [[func_externFunc:0x[^ ]+]] {{.+}} externFunc + +char testFunc4(int pred, char * __counted_by(7) * c, char * __counted_by(10) * d) { + // expected-error@+1{{conditional expression evaluates values with incompatible nested pointer types 'char *__single __counted_by(7)*__single' (aka 'char *__single*__single') and 'char *__single __counted_by(10)*__single' (aka 'char *__single*__single')}} + return externFunc(*(pred ? c : d)); +} + +// CHECK: |-FunctionDecl [[func_testFunc4:0x[^ ]+]] {{.+}} testFunc4 +// CHECK: | |-ParmVarDecl [[var_pred_3:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_c_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_d_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-CallExpr +// CHECK: | |-DeclRefExpr {{.+}} [[func_externFunc]] +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-ParenExpr +// CHECK: | `-RecoveryExpr +// CHECK: | |-DeclRefExpr {{.+}} [[var_pred_3]] +// CHECK: | |-DeclRefExpr {{.+}} [[var_c_1]] +// CHECK: | `-DeclRefExpr {{.+}} [[var_d_1]] + +char testFunc5(int pred, char * __bidi_indexable * __counted_by(7) c, char * __bidi_indexable * __counted_by(10) d) { + return externFunc((pred ? c : d)[8]); +} + +// CHECK: |-FunctionDecl [[func_testFunc5:0x[^ ]+]] {{.+}} testFunc5 +// CHECK: | |-ParmVarDecl [[var_pred_4:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_c_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_d_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char (*__single)(char *__single __counted_by(8))' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_externFunc]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(8)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'char *__bidi_indexable*__single __counted_by(7)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'long' +// CHECK: | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-ArraySubscriptExpr +// CHECK: | | | |-ParenExpr +// CHECK: | | | | `-ConditionalOperator {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_pred_4]] +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'char *__bidi_indexable*__single __counted_by(7)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | | |-BinaryOperator {{.+}} 'char *__bidi_indexable*' '+' +// CHECK: | | | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'char *__bidi_indexable*__single __counted_by(7)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_11]] +// CHECK: | | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__single __counted_by(7)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[var_c_2]] +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] +// CHECK: | | | | | | `-IntegerLiteral {{.+}} 7 +// CHECK: | | | | | |-OpaqueValueExpr [[ove_11]] {{.*}} 'char *__bidi_indexable*__single __counted_by(7)':'char *__bidi_indexable*__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int' +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'char *__bidi_indexable*' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_13]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_d_2]] +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] +// CHECK: | | | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} 'int' +// CHECK: | | | `-IntegerLiteral {{.+}} 8 +// CHECK: | | `-OpaqueValueExpr [[ove_15]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 8 +// CHECK: | |-OpaqueValueExpr [[ove_10]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_15]] {{.*}} 'long' + +char testFunc6(int pred, char * __bidi_indexable * __indexable c, char * __bidi_indexable * __counted_by(10) d) { + char * __bidi_indexable *__indexable tmp = pred ? c : d; + return externFunc(tmp[11]); +} + +// CHECK: |-FunctionDecl [[func_testFunc6:0x[^ ]+]] {{.+}} testFunc6 +// CHECK: | |-ParmVarDecl [[var_pred_5:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_c_3:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_d_3:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_tmp:0x[^ ]+]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__indexable' +// CHECK: | | `-ConditionalOperator {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_pred_5]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__indexable' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_c_3]] +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *__bidi_indexable*' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_16]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_d_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_17]] +// CHECK: | | | `-IntegerLiteral {{.+}} 10 +// CHECK: | | |-OpaqueValueExpr [[ove_16]] {{.*}} 'char *__bidi_indexable*__single __counted_by(10)':'char *__bidi_indexable*__single' +// CHECK: | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int' +// CHECK: | `-ReturnStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char (*__single)(char *__single __counted_by(8))' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_externFunc]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(8)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'long' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'long' +// CHECK: | | |-OpaqueValueExpr [[ove_18]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | `-ArraySubscriptExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__bidi_indexable*__indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_tmp]] +// CHECK: | | | `-IntegerLiteral {{.+}} 11 +// CHECK: | | `-OpaqueValueExpr [[ove_19]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | `-IntegerLiteral {{.+}} 8 +// CHECK: | |-OpaqueValueExpr [[ove_18]] {{.*}} 'char *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_19]] {{.*}} 'long' + +char testFunc7(int pred, char * __unsafe_indexable * __single c, char * __single * __indexable d) { + // expected-error@+1{{conditional expression evaluates values with incompatible pointee types 'char *__unsafe_indexable*__indexable' and 'char *__single*__indexable'; use explicit casts to perform this conversion}} + char * __unsafe_indexable *__indexable tmp = pred ? c : d; + return tmp[9][2]; +} + +char testFunc8(int pred, char * __ended_by(g) e, char * g) { + // expected-error@+1{{local variable end must be declared right next to its dependent decl}} + char *end = g; + // expected-error@+2{{local variable foo must be declared right next to its dependent decl}} + // expected-note@+1 2 {{previous use is here}} + char * __ended_by(end) foo = e; + // expected-error@+3{{local variable foo2 must be declared right next to its dependent decl}} + // expected-error@+2{{variable 'end' referred to by __ended_by variable cannot be used in other dynamic bounds attributes}} + // expected-note@+1{{previous use is here}} + char * __ended_by(end) foo2 = foo + 1; + // expected-error@+2{{local variable bar must be declared right next to its dependent decl}} + // expected-error@+1 2 {{variable 'end' referred to by __ended_by variable cannot be used in other dynamic bounds attributes}} + char * __ended_by(end) bar = *(pred ? &foo : &foo2); + return bar[0]; +} + +char testFunc9(int pred, char * __counted_by(cc) c, char * __ended_by(e) d, int cc, char *e) { + char *foo = pred ? c : d; + return foo[3]; +} + +// CHECK: -FunctionDecl [[func_testFunc9:0x[^ ]+]] {{.+}} testFunc9 +// CHECK: |-ParmVarDecl [[var_pred_8:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_c_5:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_d_5:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_cc_1:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-ParmVarDecl [[var_e_2:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_foo_4:0x[^ ]+]] +// CHECK: | `-ConditionalOperator {{.+}} 'char *__bidi_indexable' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_pred_8]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_27]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_c_5]] +// CHECK: | | | `-OpaqueValueExpr [[ove_28]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_cc_1]] +// CHECK: | | |-OpaqueValueExpr [[ove_27]] {{.*}} 'char *__single __counted_by(cc)':'char *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int' +// CHECK: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | |-DeclRefExpr {{.+}} [[var_d_5]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(d) */ ':'char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_e_2]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__single __ended_by(e)':'char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_d_5]] +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'char' +// CHECK: `-ArraySubscriptExpr +// CHECK: |-ImplicitCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} [[var_foo_4]] +// CHECK: `-IntegerLiteral {{.+}} 3 + +// expected-error@+2{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} +// expected-error@+1{{'__ended_by' attribute on nested pointer type is only allowed on indirect parameters}} +char testFunc10(int pred, char * __counted_by(5) * __counted_by(cc) c, char * __ended_by(e) * __ended_by(f) d, int cc, char *e, char *f) { + char **foo = pred ? c : d; + // expected-error@+1{{array subscript on single pointer 'foo[3]' must use a constant index of 0 to be in bounds}} + return foo[3][2]; +} + +char testFunc11(int pred, char *__null_terminated *__null_terminated *__terminated_by(0x42) * __counted_by(cc) c, char *__terminated_by(0x40) *__null_terminated *__terminated_by(0x42) * __ended_by(e) d, int cc, char *e) { + // expected-error@+1{{conditional expression evaluates values with incompatible nested pointer types 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' (aka 'char *__single*__single*__single*__bidi_indexable') and 'char *__single __terminated_by(64)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' (aka 'char *__single*__single*__single*__bidi_indexable')}} + char *__null_terminated *__null_terminated *__terminated_by(0x42) * foo = pred ? c : d; + return ****foo; +} + +char testFunc12(int pred, char *__null_terminated *__null_terminated *__terminated_by(0x42) * __counted_by(cc) c, char * *__null_terminated *__terminated_by(0x42) * __ended_by(e) d, int cc, char *e) { + // expected-error@+1{{conditional expression evaluates values with incompatible nested pointer types 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' (aka 'char *__single*__single*__single*__bidi_indexable') and 'char *__single*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' (aka 'char *__single*__single*__single*__bidi_indexable')}} + char *__single *__null_terminated *__terminated_by(0x42) * foo = pred ? c : d; + return ****foo; +} + +char testFunc13(int pred, char *__null_terminated *__null_terminated *__terminated_by(0x42) * __counted_by(cc) c, char *__null_terminated *__null_terminated *__terminated_by(0x42) * __ended_by(e) d, int cc, char *e) { + char *__null_terminated *__null_terminated *__terminated_by(0x42) * foo = pred ? c : d; + return ****foo; +} + +// CHECK: -FunctionDecl [[func_testFunc13:0x[^ ]+]] {{.+}} testFunc13 +// CHECK: |-ParmVarDecl [[var_pred_12:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_c_9:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_d_9:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_cc_5:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr +// CHECK: |-ParmVarDecl [[var_e_6:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_foo_8:0x[^ ]+]] +// CHECK: | `-ConditionalOperator {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_pred_12]] +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single __counted_by(cc)':'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single __counted_by(cc)':'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_28]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single __counted_by(cc)':'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_c_9]] +// CHECK: | | | `-OpaqueValueExpr [[ove_29]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_cc_5]] +// CHECK: | | |-OpaqueValueExpr [[ove_28]] {{.*}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single __counted_by(cc)':'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single' +// CHECK: | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'int' +// CHECK: | `-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' +// CHECK: | |-DeclRefExpr {{.+}} [[var_d_9]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'char *__single /* __started_by(d) */ ':'char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_e_6]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single __ended_by(e)':'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_d_9]] +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'char' +// CHECK: `-UnaryOperator {{.+}} cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)':'char *__single' +// CHECK: `-UnaryOperator {{.+}} cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)':'char *__single __terminated_by(0)*__single' +// CHECK: `-UnaryOperator {{.+}} cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)':'char *__single __terminated_by(0)*__single __terminated_by(0)*__single' +// CHECK: `-UnaryOperator {{.+}} cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'char *__single __terminated_by(0)*__single __terminated_by(0)*__single __terminated_by(66)*__bidi_indexable' +// CHECK: `-DeclRefExpr {{.+}} [[var_foo_8]] + +char testFunc14(int pred, const char *__single _Nullable __null_terminated c, const char * _Nullable __null_terminated d) { + const char *__null_terminated foo = pred ? c : d; + return *foo; +} + +// CHECK: |-FunctionDecl [[func_testFunc14:0x[^ ]+]] {{.+}} testFunc14 +// CHECK: | |-ParmVarDecl [[var_pred_13:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_c_10:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_d_10:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_foo_9:0x[^ ]+]] +// CHECK: | | `-ConditionalOperator {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_pred_13]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_c_10]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_d_10]] +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'char' +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_foo_9]] + +char testFunc15(int pred, const char *__single _Nullable __null_terminated c, const char * _Nullable d) { + const char *__null_terminated foo = pred ? c : d; + return *foo; +} + +// CHECK: |-FunctionDecl [[func_testFunc15:0x[^ ]+]] {{.+}} testFunc15 +// CHECK: | |-ParmVarDecl [[var_pred_14:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_c_11:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_d_11:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl [[var_foo_10:0x[^ ]+]] +// CHECK: | | `-ConditionalOperator {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_pred_14]] +// CHECK: | | |-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_c_11]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_d_11]] +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'char' +// CHECK: | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_foo_10]] + +char testFunc16(int pred, const char * _Nullable __null_terminated c, const char * _Nullable d) { + const char *__null_terminated foo = pred ? c : d; + return *foo; +} + +// CHECK: -FunctionDecl [[func_testFunc16:0x[^ ]+]] {{.+}} testFunc16 +// CHECK: |-ParmVarDecl [[var_pred_15:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_c_12:0x[^ ]+]] +// CHECK: |-ParmVarDecl [[var_d_12:0x[^ ]+]] +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl [[var_foo_11:0x[^ ]+]] +// CHECK: | `-ConditionalOperator {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_pred_15]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_c_12]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0) _Nullable':'const char *__single' +// CHECK: | `-DeclRefExpr {{.+}} [[var_d_12]] +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'char' +// CHECK: `-UnaryOperator {{.+}} cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'const char *__single __terminated_by(0)':'const char *__single' +// CHECK: `-DeclRefExpr {{.+}} [[var_foo_11]] + diff --git a/clang/test/BoundsSafety/AST/unify-function-types.c b/clang/test/BoundsSafety/AST/unify-function-types.c new file mode 100644 index 0000000000000..f5f6fd7bcf1a6 --- /dev/null +++ b/clang/test/BoundsSafety/AST/unify-function-types.c @@ -0,0 +1,261 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -verify=c %s | FileCheck %s +// RUN: %clang_cc1 -x c++ -ast-dump -fbounds-safety -fbounds-attributes-cxx-experimental -verify=cpp %s +#include + +char funcA(char buf[__counted_by(len)], int len, int len2); +char funcB(char arr[__counted_by(size)], int size, int size2); +char funcC(char arr[__counted_by(size2)], int size, int size2); + +// CHECK: TranslationUnitDecl +// CHECK: |-FunctionDecl [[func_funcA:0x[^ ]+]] {{.+}} funcA +// CHECK: | |-ParmVarDecl [[var_buf:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_len2:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_funcB:0x[^ ]+]] {{.+}} funcB +// CHECK: | |-ParmVarDecl [[var_arr:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_size2:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_funcC:0x[^ ]+]] {{.+}} funcC +// CHECK: | |-ParmVarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_size2_1:0x[^ ]+]] +// CHECK: | `-DependerDeclsAttr + +char test1(char src_buf[__counted_by(src_len)], int src_len, int src_len2) { + return (src_len % 2 == 0 ? funcA : funcB)(src_buf, src_len, src_len2); +} + +// CHECK: |-FunctionDecl [[func_test1:0x[^ ]+]] {{.+}} test1 +// CHECK: | |-ParmVarDecl [[var_src_buf:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_src_len:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_src_len2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ParenExpr +// CHECK: | | | | | `-ConditionalOperator {{.+}} 'char (*__single)(char *__single __counted_by(len), int, int)' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '==' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int' '%' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | | `-DeclRefExpr {{.+}} [[var_src_len]] +// CHECK: | | | | | | | `-IntegerLiteral {{.+}} 2 +// CHECK: | | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char (*__single)(char *__single __counted_by(len), int, int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_funcA]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char (*__single)(char *__single __counted_by(size), int, int)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_funcB]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_1]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_src_buf]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_src_len]] +// CHECK: | | | |-OpaqueValueExpr [[ove_1]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_3]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_src_len]] +// CHECK: | | `-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_src_len2]] +// CHECK: | |-OpaqueValueExpr [[ove]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' + + +char test2(char src_buf[__counted_by(src_len)], int src_len, int src_len2) { + // c-error@+2{{conditional expression evaluates functions with incompatible bound attributes 'char (*__single)(char *__single __counted_by(len), int, int)' (aka 'char (*__single)(char *__single, int, int)') and 'char (*__single)(char *__single __counted_by(size2), int, int)' (aka 'char (*__single)(char *__single, int, int)')}} + // cpp-error@+1{{conditional expression evaluates functions with incompatible bound attributes 'char (char *__single __counted_by(len), int, int)' (aka 'char (char *__single, int, int)') and 'char (char *__single __counted_by(size2), int, int)' (aka 'char (char *__single, int, int)')}} + return (src_len % 2 == 0 ? funcA : funcC)(src_buf, src_len, src_len2); +} + +char * __counted_by(len) funcD(char buf[__counted_by(len)], int len, int len2); +char * __counted_by(size) funcE(char arr[__counted_by(size)], int size, int size2); +char * __counted_by(size2) funcF(char arr[__counted_by(size)], int size, int size2); + +// CHECK: |-FunctionDecl [[func_funcD:0x[^ ]+]] {{.+}} funcD +// CHECK: | |-ParmVarDecl [[var_buf_1:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_len_1:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_len2_1:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_funcE:0x[^ ]+]] {{.+}} funcE +// CHECK: | |-ParmVarDecl [[var_arr_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_2:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_size2_2:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_funcF:0x[^ ]+]] {{.+}} funcF +// CHECK: | |-ParmVarDecl [[var_arr_3:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_size_3:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_size2_3:0x[^ ]+]] + +char * __counted_by(src_len) test3(char src_buf[__counted_by(src_len)], int src_len, int src_len2) { + return (src_len % 2 == 0 ? funcD : funcE)(src_buf, src_len, src_len2); +} + +// CHECK: |-FunctionDecl [[func_test3:0x[^ ]+]] {{.+}} test3 +// CHECK: | |-ParmVarDecl [[var_src_buf_2:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_src_len_2:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_src_len2_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'char *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_src_buf_2]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_src_len_2]] +// CHECK: | | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'char *__single __counted_by(src_len)':'char *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_src_len_2]] +// CHECK: | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_src_len2_2]] +// CHECK: | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-BoundsCheckExpr +// CHECK: | | |-CallExpr +// CHECK: | | | |-ParenExpr +// CHECK: | | | | `-ConditionalOperator {{.+}} 'char *__single __counted_by(len)(*__single)(char *__single __counted_by(len), int, int)' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '==' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '%' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | | `-DeclRefExpr {{.+}} [[var_src_len_2]] +// CHECK: | | | | | | `-IntegerLiteral {{.+}} 2 +// CHECK: | | | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)(*__single)(char *__single __counted_by(len), int, int)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_funcD]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *__single __counted_by(size)(*__single)(char *__single __counted_by(size), int, int)' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_funcE]] +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *__single __counted_by(len)':'char *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} 'char *__bidi_indexable' +// CHECK: | |-OpaqueValueExpr [[ove_9]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'char *__single __counted_by(len)':'char *__single' + +char test4(char src_buf[__counted_by(src_len)], int src_len, int src_len2) { + // c-error@+2{{conditional expression evaluates functions with incompatible bound attributes 'char *__single __counted_by(len)(*__single)(char *__single __counted_by(len), int, int)' (aka 'char *__single(*__single)(char *__single, int, int)') and 'char *__single __counted_by(size2)(*__single)(char *__single __counted_by(size), int, int)' (aka 'char *__single(*__single)(char *__single, int, int)')}} + // cpp-error@+1{{conditional expression evaluates functions with incompatible bound attributes 'char *__single __counted_by(len)(char *__single __counted_by(len), int, int)' (aka 'char *__single(char *__single, int, int)') and 'char *__single __counted_by(size2)(char *__single __counted_by(size), int, int)' (aka 'char *__single(char *__single, int, int)')}} + return *(src_len % 2 == 0 ? funcD : funcF)(src_buf, src_len, src_len2); +} + +#ifdef __cplusplus +char * __counted_by(src_len) test5(char src_buf[__counted_by(src_len)], int src_len, int src_len2) { + auto &funcRefD (funcD); + auto &funcRefE (funcE); + return (src_len % 2 == 0 ? funcRefD : funcRefE)(src_buf, src_len, src_len2); +} + +char * __counted_by(src_len) test6(char src_buf[__counted_by(src_len)], int src_len, int src_len2) { + auto &funcRefD (funcD); + auto &funcRefF (funcF); + // cpp-error@+1{{conditional expression evaluates functions with incompatible bound attributes 'char *__single __counted_by(len)(char *__single __counted_by(len), int, int)' (aka 'char *__single(char *__single, int, int)') and 'char *__single __counted_by(size2)(char *__single __counted_by(size), int, int)' (aka 'char *__single(char *__single, int, int)')}} + return (src_len % 2 == 0 ? funcRefD : funcRefF)(src_buf, src_len, src_len2); +} + +#endif diff --git a/clang/test/BoundsSafety/AST/vararg-unsafe.c b/clang/test/BoundsSafety/AST/vararg-unsafe.c new file mode 100644 index 0000000000000..ff82f23cbd8d8 --- /dev/null +++ b/clang/test/BoundsSafety/AST/vararg-unsafe.c @@ -0,0 +1,226 @@ + + +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple x86_64 -verify %s 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_ARRAY +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple i686 %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_CHAR_PTR +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple arm %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_STRUCT +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple arm-apple-watchos -verify %s 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_VOID_PTR +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple arm64-apple-macosx %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_CHAR_PTR +// RUN: %clang_cc1 -fbounds-safety -ast-dump -triple arm64-apple-ios %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_CHAR_PTR +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple x86_64 -verify %s 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_ARRAY +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple i686 %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_CHAR_PTR +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple arm %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_STRUCT +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple arm-apple-watchos -verify %s 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_VOID_PTR +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple arm64-apple-macosx %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_CHAR_PTR +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -triple arm64-apple-ios %s -verify 2>&1 | FileCheck %s --check-prefix=COMMON --check-prefix=VALIST_CHAR_PTR + +// expected-no-diagnostics + +#include +#include + +void baz(unsigned int n, va_list argp) { + int *__unsafe_indexable u = 0; + u = va_arg(argp, int *); + va_end(argp); +} + +void foo(unsigned int n, ...) { + int *__unsafe_indexable u = 0; + va_list argp; + va_start(argp, n); + baz(n, argp); + u = va_arg(argp, int *); + va_end(argp); +} + +typedef void (*f_t)(unsigned int n, va_list args); + +void test_va_list_on_fp(unsigned int n, va_list args) { + f_t f = baz; + f(n, args); +} + +void bar(void) { + int arr[10] = { 0 }; + int *p; + foo(1, p, arr); +} + +// VALIST_ARRAY: |-FunctionDecl {{.*}} used baz 'void (unsigned int, struct __va_list_tag *)' +// VALIST_ARRAY: | |-ParmVarDecl {{.*}} used argp 'struct __va_list_tag *' +// VALIST_ARRAY-LABEL: foo +// VALIST_ARRAY: CompoundStmt +// VALIST_ARRAY: |-DeclStmt +// VALIST_ARRAY: | `-VarDecl {{.*}} used u 'int *__unsafe_indexable' cinit +// VALIST_ARRAY: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_ARRAY: | `-IntegerLiteral {{.*}} 'int' 0 +// VALIST_ARRAY: |-DeclStmt +// VALIST_ARRAY: | `-VarDecl {{.*}} used argp 'va_list':'struct __va_list_tag[1]' +// VALIST_ARRAY: |-CallExpr +// VALIST_ARRAY: | |-ImplicitCastExpr {{.*}} 'void (*)(struct __va_list_tag *, ...)' +// VALIST_ARRAY: | | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_start' 'void (struct __va_list_tag *, ...)' +// VALIST_ARRAY: | |-ImplicitCastExpr {{.*}} 'struct __va_list_tag *' +// VALIST_ARRAY: | | `-DeclRefExpr {{.*}} 'va_list':'struct __va_list_tag[1]' lvalue Var {{.*}} 'argp' 'va_list':'struct __va_list_tag[1]' +// VALIST_ARRAY: | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_ARRAY: |-BinaryOperator {{.*}} 'int *__unsafe_indexable' '=' +// VALIST_ARRAY: | |-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'u' 'int *__unsafe_indexable' +// VALIST_ARRAY: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_ARRAY: | `-VAArgExpr {{.*}} 'int *' +// VALIST_ARRAY: | `-ImplicitCastExpr {{.*}} 'struct __va_list_tag *' +// VALIST_ARRAY: | `-DeclRefExpr {{.*}} 'va_list':'struct __va_list_tag[1]' lvalue Var {{.*}} 'argp' 'va_list':'struct __va_list_tag[1]' +// VALIST_ARRAY: `-CallExpr +// VALIST_ARRAY: |-ImplicitCastExpr {{.*}} 'void (*)(struct __va_list_tag *)' +// VALIST_ARRAY: | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_end' 'void (struct __va_list_tag *)' +// VALIST_ARRAY: `-ImplicitCastExpr {{.*}} 'struct __va_list_tag *' +// VALIST_ARRAY: `-DeclRefExpr {{.*}} 'va_list':'struct __va_list_tag[1]' lvalue Var {{.*}} 'argp' 'va_list':'struct __va_list_tag[1]' + +// VALIST_ARRAY-LABEL: test_va_list_on_fp +// VALIST_ARRAY: |-ParmVarDecl {{.*}} used n 'unsigned int' +// VALIST_ARRAY: |-ParmVarDecl {{.*}} used args 'struct __va_list_tag *' +// VALIST_ARRAY: `-CompoundStmt +// VALIST_ARRAY: |-DeclStmt +// VALIST_ARRAY: | `-VarDecl {{.*}} used f 'void (*__single)(unsigned int, struct __va_list_tag *)' cinit +// VALIST_ARRAY: | `-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, struct __va_list_tag *)' +// VALIST_ARRAY: | `-DeclRefExpr {{.*}} 'void (unsigned int, struct __va_list_tag *)' Function {{.*}} 'baz' 'void (unsigned int, struct __va_list_tag *)' +// VALIST_ARRAY: `-CallExpr {{.*}} 'void' +// VALIST_ARRAY: |-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, struct __va_list_tag *)' +// VALIST_ARRAY: | `-DeclRefExpr {{.*}} 'void (*__single)(unsigned int, struct __va_list_tag *)' lvalue Var {{.*}} 'f' 'void (*__single)(unsigned int, struct __va_list_tag *)' +// VALIST_ARRAY: |-ImplicitCastExpr {{.*}} 'unsigned int' +// VALIST_ARRAY: | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_ARRAY: `-ImplicitCastExpr {{.*}} 'struct __va_list_tag *' +// VALIST_ARRAY: `-DeclRefExpr {{.*}} 'struct __va_list_tag *' lvalue ParmVar {{.*}} 'args' 'struct __va_list_tag *' + +// VALIST_CHAR_PTR: |-FunctionDecl {{.*}} used baz 'void (unsigned int, va_list)' +// VALIST_CHAR_PTR: | |-ParmVarDecl {{.*}} used argp 'va_list':'char *' +// VALIST_CHAR_PTR-LABEL: foo +// VALIST_CHAR_PTR: CompoundStmt +// VALIST_CHAR_PTR: |-DeclStmt +// VALIST_CHAR_PTR: | `-VarDecl {{.*}} used u 'int *__unsafe_indexable' cinit +// VALIST_CHAR_PTR: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_CHAR_PTR: | `-IntegerLiteral {{.*}} 'int' 0 +// VALIST_CHAR_PTR: |-DeclStmt +// VALIST_CHAR_PTR: | `-VarDecl {{.*}} used argp 'va_list':'char *' +// VALIST_CHAR_PTR: |-CallExpr +// VALIST_CHAR_PTR: | |-ImplicitCastExpr {{.*}} 'void (*)(__builtin_va_list &, ...)' +// VALIST_CHAR_PTR: | | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_start' 'void (__builtin_va_list &, ...)' +// VALIST_CHAR_PTR: | |-DeclRefExpr {{.*}} 'va_list':'char *' lvalue Var {{.*}} 'argp' 'va_list':'char *' +// VALIST_CHAR_PTR: | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_CHAR_PTR: |-BinaryOperator {{.*}} 'int *__unsafe_indexable' '=' +// VALIST_CHAR_PTR: | |-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'u' 'int *__unsafe_indexable' +// VALIST_CHAR_PTR: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_CHAR_PTR: | `-VAArgExpr {{.*}} 'int *' +// VALIST_CHAR_PTR: | `-DeclRefExpr {{.*}} 'va_list':'char *' lvalue Var {{.*}} 'argp' 'va_list':'char *' +// VALIST_CHAR_PTR: `-CallExpr +// VALIST_CHAR_PTR: |-ImplicitCastExpr {{.*}} 'void (*)(__builtin_va_list &)' +// VALIST_CHAR_PTR: | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_end' 'void (__builtin_va_list &)' +// VALIST_CHAR_PTR: `-DeclRefExpr {{.*}} 'va_list':'char *' lvalue Var {{.*}} 'argp' 'va_list':'char *' +// VALIST_CHAR_PTR-LABEL: test_va_list_on_fp 'void (unsigned int, va_list)' +// VALIST_CHAR_PTR: | |-ParmVarDecl {{.*}} used n 'unsigned int' +// VALIST_CHAR_PTR: | |-ParmVarDecl {{.*}} used args 'va_list':'char *' +// VALIST_CHAR_PTR: | `-CompoundStmt {{.*}} +// VALIST_CHAR_PTR: | |-DeclStmt +// VALIST_CHAR_PTR: | | `-VarDecl {{.*}} used f 'void (*__single)(unsigned int, va_list)' cinit +// VALIST_CHAR_PTR: | | `-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, va_list)' +// VALIST_CHAR_PTR: | | `-DeclRefExpr {{.*}} 'void (unsigned int, va_list)' Function {{.*}} 'baz' 'void (unsigned int, va_list)' +// VALIST_CHAR_PTR: | `-CallExpr {{.*}} 'void' +// VALIST_CHAR_PTR: | |-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, va_list)' +// VALIST_CHAR_PTR: | | `-DeclRefExpr {{.*}} 'void (*__single)(unsigned int, va_list)' lvalue Var {{.*}} 'f' 'void (*__single)(unsigned int, va_list)' +// VALIST_CHAR_PTR: | |-ImplicitCastExpr {{.*}} 'unsigned int' +// VALIST_CHAR_PTR: | | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_CHAR_PTR: | `-ImplicitCastExpr {{.*}} 'va_list':'char *' +// VALIST_CHAR_PTR: | `-DeclRefExpr {{.*}} 'va_list':'char *' lvalue ParmVar {{.*}} 'args' 'va_list':'char *' + +// VALIST_STRUCT: |-FunctionDecl {{.*}} used baz 'void (unsigned int, va_list)' +// VALIST_STRUCT: | |-ParmVarDecl {{.*}} used argp 'va_list':'struct __va_list' +// VALIST_STRUCT-LABEL: foo +// VALIST_STRUCT: CompoundStmt +// VALIST_STRUCT: |-DeclStmt +// VALIST_STRUCT: | `-VarDecl {{.*}} used u 'int *__unsafe_indexable' cinit +// VALIST_STRUCT: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_STRUCT: | `-IntegerLiteral {{.*}} 'int' 0 +// VALIST_STRUCT: |-DeclStmt +// VALIST_STRUCT: | `-VarDecl {{.*}} used argp 'va_list':'struct __va_list' +// VALIST_STRUCT: |-CallExpr {{.*}} 'void' +// VALIST_STRUCT: | |-ImplicitCastExpr {{.*}} 'void (*)(__builtin_va_list &, ...)' +// VALIST_STRUCT: | | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_start' 'void (__builtin_va_list &, ...)' +// VALIST_STRUCT: | |-DeclRefExpr {{.*}} 'va_list':'struct __va_list' lvalue Var {{.*}} 'argp' 'va_list':'struct __va_list' +// VALIST_STRUCT: | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_STRUCT: |-BinaryOperator {{.*}} 'int *__unsafe_indexable' '=' +// VALIST_STRUCT: | |-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'u' 'int *__unsafe_indexable' +// VALIST_STRUCT: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_STRUCT: | `-VAArgExpr {{.*}} 'int *' +// VALIST_STRUCT: | `-DeclRefExpr {{.*}} 'va_list':'struct __va_list' lvalue Var {{.*}} 'argp' 'va_list':'struct __va_list' +// VALIST_STRUCT: `-CallExpr {{.*}} 'void' +// VALIST_STRUCT: |-ImplicitCastExpr {{.*}} 'void (*)(__builtin_va_list &)' +// VALIST_STRUCT: | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_end' 'void (__builtin_va_list &)' +// VALIST_STRUCT: `-DeclRefExpr {{.*}} 'va_list':'struct __va_list' lvalue Var {{.*}} 'argp' 'va_list':'struct __va_list' + +// VALIST_STRUCT-LABEL: test_va_list_on_fp 'void (unsigned int, va_list)' +// VALIST_STRUCT: | |-ParmVarDecl {{.*}} used n 'unsigned int' +// VALIST_STRUCT: | |-ParmVarDecl {{.*}} used args 'va_list':'struct __va_list' +// VALIST_STRUCT: | `-CompoundStmt +// VALIST_STRUCT: | |-DeclStmt +// VALIST_STRUCT: | | `-VarDecl {{.*}} used f 'void (*__single)(unsigned int, va_list)' cinit +// VALIST_STRUCT: | | `-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, va_list)' +// VALIST_STRUCT: | | `-DeclRefExpr {{.*}} 'void (unsigned int, va_list)' Function {{.*}} 'baz' 'void (unsigned int, va_list)' +// VALIST_STRUCT: | `-CallExpr {{.*}} 'void' +// VALIST_STRUCT: | |-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, va_list)' +// VALIST_STRUCT: | | `-DeclRefExpr {{.*}} 'void (*__single)(unsigned int, va_list)' lvalue Var {{.*}} 'f' 'void (*__single)(unsigned int, va_list)' +// VALIST_STRUCT: | |-ImplicitCastExpr {{.*}} 'unsigned int' +// VALIST_STRUCT: | | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_STRUCT: | `-ImplicitCastExpr {{.*}} 'va_list':'struct __va_list' +// VALIST_STRUCT: | `-DeclRefExpr {{.*}} 'va_list':'struct __va_list' lvalue ParmVar {{.*}} 'args' 'va_list':'struct __va_list' + +// VALIST_VOID_PTR: |-FunctionDecl {{.*}} used baz 'void (unsigned int, va_list)' +// VALIST_VOID_PTR: | |-ParmVarDecl {{.*}} used argp 'va_list':'void *' +// VALIST_VOID_PTR-LABEL: foo +// VALIST_VOID_PTR: CompoundStmt +// VALIST_VOID_PTR: |-DeclStmt +// VALIST_VOID_PTR: | `-VarDecl {{.*}} col:27 used u 'int *__unsafe_indexable' cinit +// VALIST_VOID_PTR: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_VOID_PTR: | `-IntegerLiteral {{.*}} 'int' 0 +// VALIST_VOID_PTR: |-DeclStmt +// VALIST_VOID_PTR: | `-VarDecl {{.*}} col:10 used argp 'va_list':'void *' +// VALIST_VOID_PTR: |-CallExpr +// VALIST_VOID_PTR: | |-ImplicitCastExpr {{.*}} 'void (*)(__builtin_va_list &, ...)' +// VALIST_VOID_PTR: | | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_start' 'void (__builtin_va_list &, ...)' +// VALIST_VOID_PTR: | |-DeclRefExpr {{.*}} 'va_list':'void *' lvalue Var {{.*}} 'argp' 'va_list':'void *' +// VALIST_VOID_PTR: | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_VOID_PTR: |-BinaryOperator {{.*}} 'int *__unsafe_indexable' '=' +// VALIST_VOID_PTR: | |-DeclRefExpr {{.*}} 'int *__unsafe_indexable' lvalue Var {{.*}} 'u' 'int *__unsafe_indexable' +// VALIST_VOID_PTR: | `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// VALIST_VOID_PTR: | `-VAArgExpr {{.*}} 'int *' +// VALIST_VOID_PTR: | `-DeclRefExpr {{.*}} 'va_list':'void *' lvalue Var {{.*}} 'argp' 'va_list':'void *' +// VALIST_VOID_PTR: `-CallExpr {{.*}} 'void' +// VALIST_VOID_PTR: |-ImplicitCastExpr {{.*}} 'void (*)(__builtin_va_list &)' +// VALIST_VOID_PTR: | `-DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_va_end' 'void (__builtin_va_list &)' +// VALIST_VOID_PTR: `-DeclRefExpr {{.*}} 'va_list':'void *' lvalue Var {{.*}} 'argp' 'va_list':'void *' + +// VALIST_VOID_PTR-LABEL: test_va_list_on_fp 'void (unsigned int, va_list)' +// VALIST_VOID_PTR: | |-ParmVarDecl {{.*}} used n 'unsigned int' +// VALIST_VOID_PTR: | |-ParmVarDecl {{.*}} used args 'va_list':'void *' +// VALIST_VOID_PTR: | `-CompoundStmt +// VALIST_VOID_PTR: | |-DeclStmt +// VALIST_VOID_PTR: | | `-VarDecl {{.*}} used f 'void (*__single)(unsigned int, va_list)' cinit +// VALIST_VOID_PTR: | | `-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, va_list)' +// VALIST_VOID_PTR: | | `-DeclRefExpr {{.*}} 'void (unsigned int, va_list)' Function {{.*}} 'baz' 'void (unsigned int, va_list)' +// VALIST_VOID_PTR: | `-CallExpr {{.*}} 'void' +// VALIST_VOID_PTR: | |-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, va_list)' +// VALIST_VOID_PTR: | | `-DeclRefExpr {{.*}} 'void (*__single)(unsigned int, va_list)' lvalue Var {{.*}} 'f' 'void (*__single)(unsigned int, va_list)' +// VALIST_VOID_PTR: | |-ImplicitCastExpr {{.*}} 'unsigned int' +// VALIST_VOID_PTR: | | `-DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'n' 'unsigned int' +// VALIST_VOID_PTR: | `-ImplicitCastExpr {{.*}} 'va_list':'void *' +// VALIST_VOID_PTR: | `-DeclRefExpr {{.*}} 'va_list':'void *' lvalue ParmVar {{.*}} 'args' 'va_list':'void *' + +// COMMON-LABEL: bar +// COMMON: CallExpr +// COMMON: |-ImplicitCastExpr {{.*}} 'void (*__single)(unsigned int, ...)' +// COMMON: | `-DeclRefExpr {{.*}} 'void (unsigned int, ...)' Function {{.*}} 'foo' 'void (unsigned int, ...)' +// COMMON: |-ImplicitCastExpr {{.*}} 'unsigned int' +// COMMON: | `-IntegerLiteral {{.*}} 'int' 1 +// COMMON: |-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// COMMON: | `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// COMMON: | `-DeclRefExpr {{.*}} 'int *__bidi_indexable' lvalue Var {{.*}} 'p' 'int *__bidi_indexable' +// COMMON: `-ImplicitCastExpr {{.*}} 'int *__unsafe_indexable' +// COMMON: `-ImplicitCastExpr {{.*}} 'int *__bidi_indexable' +// COMMON: `-DeclRefExpr {{.*}} 'int[10]' lvalue Var {{.*}} 'arr' 'int[10]' diff --git a/clang/test/BoundsSafety/AST/wide-ptr-init-with-incomplete-array.c b/clang/test/BoundsSafety/AST/wide-ptr-init-with-incomplete-array.c new file mode 100644 index 0000000000000..4e904e4c43a34 --- /dev/null +++ b/clang/test/BoundsSafety/AST/wide-ptr-init-with-incomplete-array.c @@ -0,0 +1,117 @@ + +// RUN: %clang_cc1 -ast-dump -verify -fbounds-safety %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -verify -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s | FileCheck %s + +#include + +extern float foo[]; +// CHECK:|-VarDecl {{.*}} used foo 'float[]' extern + +// expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} +float *__indexable wide_f[] = {foo}; +// CHECK-NEXT:|-VarDecl {{.*}} wide_f 'float *__indexable[1]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float *__indexable[1]' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__indexable' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT:| `-DeclRefExpr {{.*}} 'float[]' lvalue Var {{.*}} 'foo' 'float[]' + + +extern float bar[]; +float bar[] = {1, 2, 3, 4}; +float *__indexable wide_f2[] = {bar}; +// CHECK-NEXT:|-VarDecl {{.*}} used bar 'float[]' extern +// CHECK-NEXT:|-VarDecl {{.*}} prev {{.*}} used bar 'float[4]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float[4]' +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 1 +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 2 +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 3 +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| `-IntegerLiteral {{.*}} 'int' 4 +// CHECK-NEXT:|-VarDecl {{.*}} wide_f2 'float *__indexable[1]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float *__indexable[1]' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__indexable' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT:| `-DeclRefExpr {{.*}} 'float[4]' lvalue Var {{.*}} 'bar' 'float[4]' + +extern float baz[]; +float baz[] = {1, 2, 3, 4}; +extern float baz[]; +float *__indexable wide_f3[] = {baz}; +// CHECK-NEXT:|-VarDecl {{.*}} used baz 'float[]' extern +// CHECK-NEXT:|-VarDecl {{.*}} prev {{.*}} used baz 'float[4]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float[4]' +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 1 +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 2 +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 3 +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| `-IntegerLiteral {{.*}} 'int' 4 +// CHECK-NEXT:|-VarDecl {{.*}} prev {{.*}} used baz 'float[4]' extern +// CHECK-NEXT:|-VarDecl {{.*}} wide_f3 'float *__indexable[1]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float *__indexable[1]' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__indexable' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT:| `-DeclRefExpr {{.*}} 'float[4]' lvalue Var {{.*}} 'baz' 'float[4]' + +// expected-note@+2{{use __attribute__((visibility("hidden"))) attribute instead}} +// expected-warning@+1{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} +__private_extern__ float quz[]; +float quz[] = {1, 2, 3, 4}; +// expected-note@+2{{use __attribute__((visibility("hidden"))) attribute instead}} +// expected-warning@+1{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} +__private_extern__ float quz[]; +float *__indexable wide_f4[] = {quz}; +// CHECK-NEXT:|-VarDecl {{.*}} used quz 'float[]' __private_extern__ +// CHECK-NEXT:|-VarDecl {{.*}} prev {{.*}} used quz 'float[4]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float[4]' +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 1 +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 2 +// CHECK-NEXT:| |-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| | `-IntegerLiteral {{.*}} 'int' 3 +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float' +// CHECK-NEXT:| `-IntegerLiteral {{.*}} 'int' 4 +// CHECK-NEXT:|-VarDecl {{.*}} prev {{.*}} used quz 'float[4]' __private_extern__ +// CHECK-NEXT:|-VarDecl {{.*}} wide_f4 'float *__indexable[1]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float *__indexable[1]' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__indexable' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT:| `-DeclRefExpr {{.*}} 'float[4]' lvalue Var {{.*}} 'quz' 'float[4]' + +// expected-warning@+1{{tentative array definition assumed to have one element}} +static float qux[]; +// expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} +float *__bidi_indexable wide_f5[] = {qux}; +// CHECK-NEXT:|-VarDecl {{.*}} used qux 'float[1]' static +// CHECK-NEXT:|-VarDecl {{.*}} wide_f5 'float *__bidi_indexable[1]' cinit +// CHECK-NEXT:| `-InitListExpr {{.*}} 'float *__bidi_indexable[1]' +// CHECK-NEXT:| `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT:| `-DeclRefExpr {{.*}} 'float[]' lvalue Var {{.*}} 'qux' 'float[1]' + +void f(void) { + extern float bar[]; + extern float quxx[]; + // expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} + static float *__bidi_indexable wide_f6[] = {quxx, bar, baz}; +} +// CHECK-LABEL: f 'void (void)' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} parent {{.*}} prev {{.*}} used bar 'float[4]' extern +// CHECK-NEXT: |-DeclStmt {{.*}} +// CHECK-NEXT: | `-VarDecl {{.*}} parent {{.*}} used quxx 'float[]' extern +// CHECK-NEXT: `-DeclStmt {{.*}} +// CHECK-NEXT: `-VarDecl {{.*}} wide_f6 'float *__bidi_indexable[3]' static cinit +// CHECK-NEXT: `-InitListExpr {{.*}} 'float *__bidi_indexable[3]' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'float[]' lvalue Var {{.*}} 'quxx' 'float[]' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'float[4]' lvalue Var {{.*}} 'bar' 'float[4]' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'float *__bidi_indexable' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'float[4]' lvalue Var {{.*}} 'baz' 'float[4]' diff --git a/clang/test/BoundsSafety/AST/wide-to-counted-pointer-arg.c b/clang/test/BoundsSafety/AST/wide-to-counted-pointer-arg.c new file mode 100644 index 0000000000000..748e1e0de650a --- /dev/null +++ b/clang/test/BoundsSafety/AST/wide-to-counted-pointer-arg.c @@ -0,0 +1,584 @@ + + +// RUN: not %clang_cc1 -fbounds-safety -ast-dump %s 2> /dev/null | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: not %clang_cc1 -fbounds-safety -ast-dump %s 2> /dev/null | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + + +#include + +typedef struct { + int len; + int *__counted_by(len) buf; +} S; + +void Foo(int *__counted_by(len) buf, int len) {} +void Bar(int *__sized_by(siz) buf, int siz) {} +// CHECK: FunctionDecl [[func_Foo:0x[^ ]+]] {{.+}} Foo +// CHECK: FunctionDecl [[func_Bar:0x[^ ]+]] {{.+}} Bar + +void Test(void) { +// CHECK-LABEL: FunctionDecl {{.+}} Test + int value = 0; +// CHECK: VarDecl [[var_value:0x[^ ]+]] {{.+}} value + int *__single p = &value; +// CHECK: VarDecl [[var_p:0x[^ ]+]] {{.+}} p + int *q = &value; +// CHECK: VarDecl [[var_q:0x[^ ]+]] {{.+}} q + S s; +// CHECK: VarDecl [[var_s:0x[^ ]+]] {{.+}} s +Lassign: + s.buf = &value; + s.len = 1; +// CHECK-LABEL: Lassign +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK: | | | | |-MemberExpr {{.+}} .buf +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | `-UnaryOperator {{.+}} cannot overflow +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_value]] +// CHECK: | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | `-IntegerLiteral {{.+}} 1 +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | |-MemberExpr {{.+}} .len +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' + +Lfoo_sbuf_0: + Foo(s.buf, 0); +// CHECK-LABEL: Lfoo_sbuf_0 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Foo]] +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_5]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-MemberExpr {{.+}} .buf +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_4]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_6]] {{.*}} 'int' + +Lfoo_sbuf_1: + Foo(s.buf, 1); +// CHECK-LABEL: Lfoo_sbuf_1 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Foo]] +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_7]] +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_9]] +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-MemberExpr {{.+}} .buf +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_9]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_7]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' + +Lfoo_sbuf_5: + Foo(s.buf, 5); +// CHECK-LABEL: Lfoo_sbuf_5 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Foo]] +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_15:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_16:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_14]] +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_15]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_13]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-MemberExpr {{.+}} .buf +// CHECK: | | | | | `-OpaqueValueExpr [[ove_14]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_14]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_15]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_16]] +// CHECK: | | | `-IntegerLiteral {{.+}} 5 +// CHECK: | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_16]] {{.*}} 'int' + +Lfoo_p_0: + Foo(p, 0); +// CHECK-LABEL: Lfoo_p_0 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Foo]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_17:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_18:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_17]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: | | | `-OpaqueValueExpr [[ove_18]] +// CHECK: | | | `-IntegerLiteral {{.+}} 0 +// CHECK: | | |-OpaqueValueExpr [[ove_17]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_18]] {{.*}} 'int' + +Lfoo_p_1: + Foo(p, 1); +// CHECK-LABEL: Lfoo_p_1 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Foo]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_19:0x[^ ]+]] {{.*}} 'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_20:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_19]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_p]] +// CHECK: | | | `-OpaqueValueExpr [[ove_20]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | |-OpaqueValueExpr [[ove_19]] {{.*}} 'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_20]] {{.*}} 'int' + +Lfoo_q_5: + Foo(q, 5); +// CHECK-LABEL: Lfoo_q_5 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __counted_by(len), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Foo]] +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_21:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_22:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_21]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_q]] +// CHECK: | | | `-OpaqueValueExpr [[ove_22]] +// CHECK: | | | `-IntegerLiteral {{.+}} 5 +// CHECK: | | |-OpaqueValueExpr [[ove_21]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_22]] {{.*}} 'int' + +Lbar_sbuf_slen: + Bar(s.buf, s.len * sizeof(int)); +// CHECK-LABEL: Lbar_sbuf_slen +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(siz), int)' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_Bar]] +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(siz)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_23:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | | |-OpaqueValueExpr [[ove_24:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_25:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | | `-OpaqueValueExpr [[ove_26:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_27:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_23]] +// CHECK: | | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | | |-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_25]] +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_26]] +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_25]] {{.*}} lvalue +// CHECK: | | | | | `-OpaqueValueExpr [[ove_24]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | `-MemberExpr {{.+}} .buf +// CHECK: | | | | | `-OpaqueValueExpr [[ove_25]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_25]] {{.*}} lvalue +// CHECK: | | | | |-OpaqueValueExpr [[ove_26]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_24]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_27]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | `-UnaryExprOrTypeTraitExpr +// CHECK: | | |-OpaqueValueExpr [[ove_23]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-OpaqueValueExpr [[ove_27]] {{.*}} 'int' + +Lbar_sbuf_11: + Bar(s.buf, 11 * sizeof(int)); +// CHECK-LABEL: Lbar_sbuf_11 +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsCheckExpr +// CHECK: | | | |-CallExpr +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(siz), int)' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[func_Bar]] +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *__single __sized_by(siz)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_28:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_29:0x[^ ]+]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_30:0x[^ ]+]] {{.*}} lvalue +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_31:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-OpaqueValueExpr [[ove_32:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'char *' +// CHECK: | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// CHECK: | | | | `-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_28]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_29]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_30]] +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_s]] +// CHECK: | | | | |-OpaqueValueExpr [[ove_31]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-MemberExpr {{.+}} .len +// CHECK: | | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} lvalue +// CHECK: | | | | `-OpaqueValueExpr [[ove_29]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | | | `-MemberExpr {{.+}} .buf +// CHECK: | | | | `-OpaqueValueExpr [[ove_30]] {{.*}} lvalue +// CHECK: | | | |-OpaqueValueExpr [[ove_30]] {{.*}} lvalue +// CHECK: | | | |-OpaqueValueExpr [[ove_31]] {{.*}} 'int' +// CHECK: | | | `-OpaqueValueExpr [[ove_29]] {{.*}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_32]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'unsigned long' '*' +// CHECK: | | |-ImplicitCastExpr {{.+}} 'unsigned long' +// CHECK: | | | `-IntegerLiteral {{.+}} 11 +// CHECK: | | `-UnaryExprOrTypeTraitExpr +// CHECK: | |-OpaqueValueExpr [[ove_28]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-OpaqueValueExpr [[ove_32]] {{.*}} 'int' +} + +// CHECK-LABEL: TestError +void TestError(void) { + int value = 0; + int *__single p = &value; + // expected-error@+1{{passing 'int *__single' to parameter 'buf' of type 'int *__single __counted_by(len)' (aka 'int *__single') with count value of 5 always fails}} + Foo(p, 5); +} diff --git a/clang/test/BoundsSafety/Analysis/analyzer_test.py b/clang/test/BoundsSafety/Analysis/analyzer_test.py new file mode 100644 index 0000000000000..03124333fe7bf --- /dev/null +++ b/clang/test/BoundsSafety/Analysis/analyzer_test.py @@ -0,0 +1,48 @@ +import lit.formats +import lit.TestRunner + +# Custom format class for static analyzer tests +class AnalyzerTest(lit.formats.ShTest): + + def __init__(self, execute_external, use_z3_solver=False): + super(AnalyzerTest, self).__init__(execute_external) + self.use_z3_solver = use_z3_solver + + def execute(self, test, litConfig): + results = [] + + # Parse any test requirements ('REQUIRES: ') + saved_test = test + lit.TestRunner.parseIntegratedTestScript(test) + + if 'z3' not in test.requires: + results.append(self.executeWithAnalyzeSubstitution( + saved_test, litConfig, '-analyzer-constraints=range')) + + if results[-1].code == lit.Test.FAIL: + return results[-1] + + # If z3 backend available, add an additional run line for it + if self.use_z3_solver == '1': + assert(test.config.clang_staticanalyzer_z3 == '1') + results.append(self.executeWithAnalyzeSubstitution( + saved_test, litConfig, '-analyzer-constraints=z3 -DANALYZER_CM_Z3')) + + # Combine all result outputs into the last element + for x in results: + if x != results[-1]: + results[-1].output = x.output + results[-1].output + + if results: + return results[-1] + return lit.Test.Result(lit.Test.UNSUPPORTED, + "Test requires the following unavailable features: z3") + + def executeWithAnalyzeSubstitution(self, test, litConfig, substitution): + saved_substitutions = list(test.config.substitutions) + test.config.substitutions.append(('%analyze', substitution)) + result = lit.TestRunner.executeShTest(test, litConfig, + self.execute_external) + test.config.substitutions = saved_substitutions + + return result diff --git a/clang/test/BoundsSafety/Analysis/cfg.c b/clang/test/BoundsSafety/Analysis/cfg.c new file mode 100644 index 0000000000000..fe2e0d0fdb7dd --- /dev/null +++ b/clang/test/BoundsSafety/Analysis/cfg.c @@ -0,0 +1,183 @@ + + +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck %s + +#include + +// BoundsSafetyPointerPromotionExpr + +// CHECK-LABEL: int *__bidi_indexablepromote_counted_by(int *ptr, unsigned int len) +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: ptr +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, int *__single __counted_by(len)) +// CHECK-NEXT: 3: len +// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, LValueToRValue, unsigned int) +// CHECK-NEXT: 5: [B1.2] (BoundsSafetyPointerPromotionExpr, int *__bidi_indexable) +// CHECK-NEXT: 6: return [B1.5]; +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +int *__bidi_indexable +promote_counted_by(int *__counted_by(len) ptr, unsigned len) { + return ptr; +} + +// BoundsCheckExpr + +typedef struct { + unsigned long long size; + void *__sized_by(size) buf; +} counted_buf; + +// CHECK-LABEL: void bounds_check(counted_buf *bf, void *__bidi_indexablebuf, unsigned long long size) +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: buf +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, void *__bidi_indexable) +// CHECK-NEXT: 3: size +// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, LValueToRValue, unsigned long long) +// CHECK-NEXT: 5: [B1.2] (ImplicitCastExpr, BoundsSafetyPointerCast, void *__single __sized_by(size)) +// CHECK-NEXT: 6: bf +// CHECK-NEXT: 7: [B1.6] (ImplicitCastExpr, LValueToRValue, counted_buf *__single) +// CHECK-NEXT: 8: [B1.7]->buf +// CHECK-NEXT: 9: [B1.8] = [B1.5] +// CHECK-NEXT: 10: bf +// CHECK-NEXT: 11: [B1.10] (ImplicitCastExpr, LValueToRValue, counted_buf *__single) +// CHECK-NEXT: 12: [B1.11]->size +// CHECK-NEXT: 13: [B1.12] = [B1.4] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void bounds_check(counted_buf *bf, + void *__bidi_indexable buf, + unsigned long long size) { + bf->buf = buf; + bf->size = size; +} + +// Combined + +void *__sized_by(size) my_alloc(unsigned long long size); + +// CHECK-LABEL: int promote_and_bounds_check_in_middle(int v, counted_buf *bf) +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B1] +// CHECK-NEXT: 1: v +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: return [B1.2]; +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: 9 +// CHECK-NEXT: 2: return [B2.1]; +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B3] +// CHECK-NEXT: 1: 10 +// CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, IntegralCast, unsigned long long) +// CHECK-NEXT: 3: my_alloc +// CHECK-NEXT: 4: [B3.3] (ImplicitCastExpr, FunctionToPointerDecay, void *__single __sized_by(size)(*__single)(unsigned long long)) +// CHECK-NEXT: 5: [B3.4]([B3.2]) +// CHECK-NEXT: 6: [B3.5] (BoundsSafetyPointerPromotionExpr, void *__bidi_indexable) +// CHECK-NEXT: 7: 8 +// CHECK-NEXT: 8: [B3.7] (ImplicitCastExpr, IntegralCast, unsigned long long) +// CHECK-NEXT: 9: [B3.6] (ImplicitCastExpr, BoundsSafetyPointerCast, void *__single __sized_by(size)) +// CHECK-NEXT: 10: bf +// CHECK-NEXT: 11: [B3.10] (ImplicitCastExpr, LValueToRValue, counted_buf *__single) +// CHECK-NEXT: 12: [B3.11]->buf +// CHECK-NEXT: 13: [B3.12] = [B3.9] +// CHECK-NEXT: 14: bf +// CHECK-NEXT: 15: [B3.14] (ImplicitCastExpr, LValueToRValue, counted_buf *__single) +// CHECK-NEXT: 16: [B3.15]->size +// CHECK-NEXT: 17: [B3.16] = [B3.8] +// CHECK-NEXT: 18: v +// CHECK-NEXT: 19: [B3.18] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 20: 9 +// CHECK-NEXT: 21: [B3.19] < [B3.20] +// CHECK-NEXT: T: if [B3.21] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B2 B1 +// CHECK: [B4] +// CHECK-NEXT: 1: 0 +// CHECK-NEXT: 2: return [B4.1]; +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: v +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: ![B5.2] +// CHECK-NEXT: T: if [B5.3] +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (3): B1 B2 B4 +int promote_and_bounds_check_in_middle(int v, counted_buf *bf) { + if (!v) + return 0; + + bf->buf = my_alloc(10); + bf->size = 8; + + if (v < 9) + return 9; + + return v; +} + + +// example from Sema/unreachable-noret.c + +#define NO_RETURN __attribute__((noreturn)) +void NO_RETURN halt(const void * const p_fatal_error); + +// CHECK-LABEL: static void handler_private(const void *p_stack) +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1 (NORETURN)] +// CHECK-NEXT: 1: int foo; +// CHECK-NEXT: 2: halt +// CHECK-NEXT: 3: [B1.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*__single)(const void *__singleconst) __attribute__((noreturn))) +// CHECK-NEXT: 4: foo +// CHECK-NEXT: 5: &[B1.4] +// CHECK-NEXT: 6: [B1.5] (ImplicitCastExpr, BitCast, const void *__bidi_indexable) +// CHECK-NEXT: 7: [B1.6] (ImplicitCastExpr, BoundsSafetyPointerCast, const void *__single) +// CHECK-NEXT: 8: [B1.3]([B1.7]) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +static void NO_RETURN handler_private(const void *__sized_by(0x78) p_stack) +{ + int foo; + halt(&foo); +} + +// CHECK-LABEL: void handler_irq(const void *p_stack) +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1 (NORETURN)] +// CHECK-NEXT: 1: p_stack +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, const void *__single __sized_by(120)) +// CHECK-NEXT: 3: 120 +// CHECK-NEXT: 4: [B1.2] (BoundsSafetyPointerPromotionExpr, const void *__bidi_indexable) +// CHECK-NEXT: 5: 120 +// CHECK-NEXT: 6: [B1.5] (ImplicitCastExpr, IntegralCast, long) +// CHECK-NEXT: 7: handler_private +// CHECK-NEXT: 8: [B1.7] (ImplicitCastExpr, FunctionToPointerDecay, void (*__single)(const void *__single __sized_by(120)) __attribute__((noreturn))) +// CHECK-NEXT: 9: [B1.4] (ImplicitCastExpr, BoundsSafetyPointerCast, const void *__single __sized_by(120)) +// CHECK-NEXT: 10: [B1.8]([B1.9]) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void NO_RETURN handler_irq(const void *__sized_by(0x78) p_stack) +{ + handler_private(p_stack); +} diff --git a/clang/test/BoundsSafety/Analysis/forge_ptr_expr_deadcode.c b/clang/test/BoundsSafety/Analysis/forge_ptr_expr_deadcode.c new file mode 100644 index 0000000000000..7af200af27be5 --- /dev/null +++ b/clang/test/BoundsSafety/Analysis/forge_ptr_expr_deadcode.c @@ -0,0 +1,11 @@ + + +// RUN: %clang_cc1 -analyze -analyzer-checker=deadcode.DeadStores -fbounds-safety -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=deadcode.DeadStores -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +int foo() { + int arr[10]; + int a = 10; + // expected-warning@+1{{Value stored to 'ptr' during its initialization is never read [deadcode.DeadStores]}} + int *ptr = __builtin_unsafe_forge_bidi_indexable(arr, 4 * sizeof(int)); + return 0; +} diff --git a/clang/test/BoundsSafety/Analysis/forge_ptr_expr_uninitialized.c b/clang/test/BoundsSafety/Analysis/forge_ptr_expr_uninitialized.c new file mode 100644 index 0000000000000..b52f36aee48e2 --- /dev/null +++ b/clang/test/BoundsSafety/Analysis/forge_ptr_expr_uninitialized.c @@ -0,0 +1,13 @@ + + +// RUN: %clang_cc1 -analyze -analyzer-checker=core.uninitialized.UndefReturn -fbounds-safety -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core.uninitialized.UndefReturn -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// XFAIL: * +// rdar://72163355 +int foo() { + int arr[10]; + int a = 10; + int *ptr = __builtin_unsafe_forge_bidi_indexable(arr, 4 * sizeof(int)); + // expected-warning@+1{{Undefined or garbage value returned to caller [core.uninitialized.UndefReturn]}} + return arr[a]; +} diff --git a/clang/test/BoundsSafety/Analysis/lit.local.cfg b/clang/test/BoundsSafety/Analysis/lit.local.cfg new file mode 100644 index 0000000000000..1e8cf4c3b7c4b --- /dev/null +++ b/clang/test/BoundsSafety/Analysis/lit.local.cfg @@ -0,0 +1,28 @@ +# -*- Python -*- vim: set ft=python ts=4 sw=4 expandtab tw=79: +from lit.llvm.subst import ToolSubst +import site + +# Load the custom analyzer test format, which runs the test again with Z3 if it +# is available. +site.addsitedir(os.path.dirname(__file__)) +import analyzer_test +config.test_format = analyzer_test.AnalyzerTest( + config.test_format.execute_external, config.use_z3_solver) + +# Filtering command used by Clang Analyzer tests (when comparing .plist files +# with reference output) +config.substitutions.append(('%normalize_plist', + "grep -Ev '%s|%s|%s'" % + ('^[[:space:]]*.* version .*[[:space:]]*$', + '^[[:space:]]*/.*[[:space:]]*$', + '^[[:space:]]*.:.*[[:space:]]*$'))) + +# Filtering command for testing SARIF output against reference output. +config.substitutions.append(('%normalize_sarif', + "grep -Ev '^[[:space:]]*(%s|%s|%s)[[:space:]]*$'" % + ('"uri": "file:.*%basename_t"', + '"version": ".* version .*"', + '"version": "2.1.0"'))) + +if not config.root.clang_staticanalyzer: + config.unsupported = True diff --git a/clang/test/BoundsSafety/Analysis/pointer-cast.c b/clang/test/BoundsSafety/Analysis/pointer-cast.c new file mode 100644 index 0000000000000..c99dbd3b232a4 --- /dev/null +++ b/clang/test/BoundsSafety/Analysis/pointer-cast.c @@ -0,0 +1,21 @@ + + +// RUN: %clang_cc1 -analyze -fbounds-safety -w -verify -analyzer-checker=core %s +// RUN: %clang_cc1 -analyze -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -w -verify -analyzer-checker=core %s + +// expected-no-diagnostics + +#include + +void test_void_pointer(void *__bidi_indexable p) { + void *q = p; + if (q) {} + q != 0; // no-crash +} + +void test_opaque_pointer(struct S *__bidi_indexable p) { + struct S *q = p; + if (!q) { + q != 0; // no-crash + } +} diff --git a/clang/test/BoundsSafety/CodeGen/access-size-check-elt-size.c b/clang/test/BoundsSafety/CodeGen/access-size-check-elt-size.c new file mode 100644 index 0000000000000..9a4eaeb8208c2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/access-size-check-elt-size.c @@ -0,0 +1,319 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin +// RUN: %clang_cc1 -O0 -fbounds-safety -fbounds-safety-bringup-missing-checks=access_size -triple arm64-apple-iphoneos -emit-llvm %s -o - | FileCheck --check-prefixes=NEW %s +// RUN: %clang_cc1 -O0 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=access_size -triple arm64-apple-iphoneos -emit-llvm %s -o - | FileCheck --check-prefixes=LEGACY %s +#include +#include + + +// NEW-LABEL: define dso_local i8 @access_uint8( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0:[0-9]+]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], !annotation [[META2:![0-9]+]] +// NEW-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4:![0-9]+]] +// NEW-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP3:%.*]] = load i8, ptr [[ARRAYIDX]], align 1 +// NEW-NEXT: ret i8 [[TMP3]] +// +// LEGACY-LABEL: define dso_local i8 @access_uint8( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], !annotation [[META2:![0-9]+]] +// LEGACY-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// LEGACY: [[TRAP]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// LEGACY-NEXT: unreachable, !annotation [[META2]] +// LEGACY: [[CONT]]: +// LEGACY-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4:![0-9]+]] +// LEGACY-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META4]] +// LEGACY: [[TRAP1]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// LEGACY-NEXT: unreachable, !annotation [[META4]] +// LEGACY: [[CONT2]]: +// LEGACY-NEXT: [[TMP3:%.*]] = load i8, ptr [[ARRAYIDX]], align 1 +// LEGACY-NEXT: ret i8 [[TMP3]] +// +uint8_t access_uint8(uint8_t* __bidi_indexable ptr, int idx) { + return ptr[idx]; +} + +// NEW-LABEL: define dso_local i16 @access_uint16( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr i16, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = getelementptr i16, ptr [[ARRAYIDX]], i64 1, !annotation [[META5:![0-9]+]] +// NEW-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META5]] +// NEW-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META5]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// NEW-NEXT: unreachable, !annotation [[META5]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], !annotation [[META5]] +// NEW-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META5]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// NEW-NEXT: unreachable, !annotation [[META5]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: [[TMP5:%.*]] = load i16, ptr [[ARRAYIDX]], align 2 +// NEW-NEXT: ret i16 [[TMP5]] +// +// LEGACY-LABEL: define dso_local i16 @access_uint16( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr i16, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], !annotation [[META2]] +// LEGACY-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// LEGACY: [[TRAP]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// LEGACY-NEXT: unreachable, !annotation [[META2]] +// LEGACY: [[CONT]]: +// LEGACY-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// LEGACY-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META4]] +// LEGACY: [[TRAP1]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// LEGACY-NEXT: unreachable, !annotation [[META4]] +// LEGACY: [[CONT2]]: +// LEGACY-NEXT: [[TMP3:%.*]] = load i16, ptr [[ARRAYIDX]], align 2 +// LEGACY-NEXT: ret i16 [[TMP3]] +// +uint16_t access_uint16(uint16_t* __bidi_indexable ptr, int idx) { + return ptr[idx]; +} + +// NEW-LABEL: define dso_local i32 @access_uint32( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[ARRAYIDX]], i64 1, !annotation [[META5]] +// NEW-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META5]] +// NEW-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META5]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// NEW-NEXT: unreachable, !annotation [[META5]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], !annotation [[META5]] +// NEW-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META5]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// NEW-NEXT: unreachable, !annotation [[META5]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// NEW-NEXT: ret i32 [[TMP5]] +// +// LEGACY-LABEL: define dso_local i32 @access_uint32( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], !annotation [[META2]] +// LEGACY-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// LEGACY: [[TRAP]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// LEGACY-NEXT: unreachable, !annotation [[META2]] +// LEGACY: [[CONT]]: +// LEGACY-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// LEGACY-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META4]] +// LEGACY: [[TRAP1]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// LEGACY-NEXT: unreachable, !annotation [[META4]] +// LEGACY: [[CONT2]]: +// LEGACY-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// LEGACY-NEXT: ret i32 [[TMP3]] +// +uint32_t access_uint32(uint32_t* __bidi_indexable ptr, int idx) { + return ptr[idx]; +} + +// NEW-LABEL: define dso_local i64 @access_uint64( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr i64, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = getelementptr i64, ptr [[ARRAYIDX]], i64 1, !annotation [[META5]] +// NEW-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META5]] +// NEW-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META5]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// NEW-NEXT: unreachable, !annotation [[META5]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], !annotation [[META5]] +// NEW-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META5]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// NEW-NEXT: unreachable, !annotation [[META5]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: [[TMP5:%.*]] = load i64, ptr [[ARRAYIDX]], align 8 +// NEW-NEXT: ret i64 [[TMP5]] +// +// LEGACY-LABEL: define dso_local i64 @access_uint64( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr i64, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], !annotation [[META2]] +// LEGACY-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// LEGACY: [[TRAP]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// LEGACY-NEXT: unreachable, !annotation [[META2]] +// LEGACY: [[CONT]]: +// LEGACY-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// LEGACY-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META4]] +// LEGACY: [[TRAP1]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// LEGACY-NEXT: unreachable, !annotation [[META4]] +// LEGACY: [[CONT2]]: +// LEGACY-NEXT: [[TMP3:%.*]] = load i64, ptr [[ARRAYIDX]], align 8 +// LEGACY-NEXT: ret i64 [[TMP3]] +// +uint64_t access_uint64(uint64_t* __bidi_indexable ptr, int idx) { + return ptr[idx]; +} + +//. +// NEW: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// NEW: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// NEW: [[META4]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// NEW: [[META5]] = !{!"bounds-safety-check-ptr-le-upper-bound"} +//. +// LEGACY: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// LEGACY: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// LEGACY: [[META4]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/access-size-check-unsized-elt.c b/clang/test/BoundsSafety/CodeGen/access-size-check-unsized-elt.c new file mode 100644 index 0000000000000..2cce8cb848a47 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/access-size-check-unsized-elt.c @@ -0,0 +1,78 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin +// RUN: %clang_cc1 -O0 -fbounds-safety -fbounds-safety-bringup-missing-checks=access_size -triple arm64-apple-iphoneos -emit-llvm %s -o - | FileCheck --check-prefix ACCESS-SIZE %s +// RUN: %clang_cc1 -O0 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=access_size -triple arm64-apple-iphoneos -emit-llvm %s -o - | FileCheck --check-prefix ACCESS-SIZE %s +#include +int get_int(void*); +typedef int fn_t(void *); + +void receive(fn_t); + +// The codegen with and without `access_size` should be identical + +// ACCESS-SIZE-LABEL: define dso_local void @borked( +// ACCESS-SIZE-SAME: ) #[[ATTR0:[0-9]+]] { +// ACCESS-SIZE-NEXT: [[ENTRY:.*:]] +// ACCESS-SIZE-NEXT: [[F:%.*]] = alloca ptr, align 8 +// ACCESS-SIZE-NEXT: [[W:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// ACCESS-SIZE-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// ACCESS-SIZE-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// ACCESS-SIZE-NEXT: store ptr @get_int, ptr [[F]], align 8 +// ACCESS-SIZE-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[W]], i8 0, i64 24, i1 false) +// ACCESS-SIZE-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[W]], i64 24, i1 false) +// ACCESS-SIZE-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// ACCESS-SIZE-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// ACCESS-SIZE-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// ACCESS-SIZE-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// ACCESS-SIZE-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// ACCESS-SIZE-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// ACCESS-SIZE-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP2]], align 8 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// ACCESS-SIZE-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// ACCESS-SIZE-NEXT: [[TMP3:%.*]] = icmp ne ptr [[WIDE_PTR_PTR3]], null, !annotation [[META2:![0-9]+]] +// ACCESS-SIZE-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT9:.*]], !annotation [[META2]] +// ACCESS-SIZE: [[BOUNDSCHECK_NOTNULL]]: +// ACCESS-SIZE-NEXT: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_UB5]], !annotation [[META3:![0-9]+]] +// ACCESS-SIZE-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF4:![0-9]+]], !annotation [[META3]] +// ACCESS-SIZE: [[TRAP]]: +// ACCESS-SIZE-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META3]] +// ACCESS-SIZE-NEXT: unreachable, !annotation [[META3]] +// ACCESS-SIZE: [[CONT]]: +// ACCESS-SIZE-NEXT: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_LB7]], !annotation [[META5:![0-9]+]] +// ACCESS-SIZE-NEXT: br i1 [[TMP5]], label %[[CONT9]], label %[[TRAP8:.*]], !prof [[PROF4]], !annotation [[META5]] +// ACCESS-SIZE: [[TRAP8]]: +// ACCESS-SIZE-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META5]] +// ACCESS-SIZE-NEXT: unreachable, !annotation [[META5]] +// ACCESS-SIZE: [[CONT9]]: +// ACCESS-SIZE-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[F]], align 8 +// ACCESS-SIZE-NEXT: [[TMP6:%.*]] = load ptr, ptr [[F]], align 8 +// ACCESS-SIZE-NEXT: call void @receive(ptr noundef [[TMP6]]) +// ACCESS-SIZE-NEXT: ret void +// +void borked(void) +{ + fn_t *f = get_int; // This is implicitly __single, not __bidi_indexable + void *w = 0; // Implicitly __bidi_indexable + // Bounds check here + // Converting + // int (*__bidi_indexable)(void *__single) -> rn_matchf_t *__single + // + // requires checking that the wide pointer is in bounds. + f = w; + receive(f); +} +//. +// ACCESS-SIZE: [[META2]] = !{!"bounds-safety-check-ptr-neq-null"} +// ACCESS-SIZE: [[META3]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// ACCESS-SIZE: [[PROF4]] = !{!"branch_weights", i32 1048575, i32 1} +// ACCESS-SIZE: [[META5]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/addr-of-array-elem-O2.c b/clang/test/BoundsSafety/CodeGen/addr-of-array-elem-O2.c new file mode 100644 index 0000000000000..a94ffc9b0d1ac --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/addr-of-array-elem-O2.c @@ -0,0 +1,32 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-iphoneos -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-iphoneos -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +int arr[10]; + +int v; + +int main() { + int *ptr = &arr[v]; + return *ptr; +} + +// CHECK-LABEL: @v +// CHECK-LABEL: @arr +// CHECK-LABEL: @main( +// CHECK: entry: +// CHECK: [[TMP0:%.*]] = load i32, ptr @v, align 4, {{!tbaa ![0-9]+}} +// CHECK: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr @arr, i64 [[IDXPROM]] +// CHECK: [[TMP1:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], getelementptr inbounds nuw (i8, ptr @arr, i64 40), {{!annotation ![0-9]+}} +// CHECK: [[TMP2:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], @arr, {{!annotation ![0-9]+}} +// CHECK: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK: unreachable +// CHECK: cont1: +// CHECK: [[TMP3:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK: ret i32 [[TMP3]] +// diff --git a/clang/test/BoundsSafety/CodeGen/addr-of-array-elem.c b/clang/test/BoundsSafety/CodeGen/addr-of-array-elem.c new file mode 100644 index 0000000000000..e2cd4b12086c5 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/addr-of-array-elem.c @@ -0,0 +1,63 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +int arr[10]; + +int v; + +// CHECK-LABEL: @main( +// CHECK: entry: +// CHECK: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK: store i32 0, ptr [[RETVAL]], align 4 +// CHECK: [[TMP0:%.*]] = load i32, ptr @v, align 4 +// CHECK: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK: store ptr @arr, ptr [[TMP1]], align 8 +// CHECK: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK: store ptr getelementptr inbounds (i32, ptr @arr, i64 10), ptr [[TMP2]], align 8 +// CHECK: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK: store ptr @arr, ptr [[TMP3]], align 8 +// CHECK: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 [[IDXPROM]] +// CHECK: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK: unreachable +// CHECK: cont: +// CHECK: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK: br i1 [[TMP14]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK: unreachable +// CHECK: cont2: +// CHECK: [[TMP15:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK: ret i32 [[TMP15]] +// +int main() { + int *ptr = &arr[v]; + return *ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/addr-of-packed-struct.c b/clang/test/BoundsSafety/CodeGen/addr-of-packed-struct.c new file mode 100644 index 0000000000000..ac2b39b8f29fd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/addr-of-packed-struct.c @@ -0,0 +1,22 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64-apple-iphoneos -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-iphoneos -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64-apple-iphoneos -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-iphoneos -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +struct __attribute__((packed)) foo { + char x; // field 0 + short s; // field 1 + // : field 2 + unsigned __attribute__((aligned(4))) u; // field 3 +}; + +// CHECK-LABEL: @p( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[S:%.*]], i64 4 +// CHECK-NEXT: ret ptr [[TMP0]] +// +unsigned *p(struct foo *s) { + return &s->u; +} diff --git a/clang/test/BoundsSafety/CodeGen/addr-of-struct-field.c b/clang/test/BoundsSafety/CodeGen/addr-of-struct-field.c new file mode 100644 index 0000000000000..1ab49c92bb449 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/addr-of-struct-field.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +struct S { + int i; + char *p; + long l; +}; + +// CHECK-LABEL: @fails_oob +// CHECK: tail call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !annotation ![[ANNOTATION:[0-9]+]] +// CHECK-NEXT: unreachable, !annotation ![[ANNOTATION]] +int fails_oob() { + struct S s = {1, 0, 2}; + struct S *ps = &s; + ps = &ps->i; + ps = &s.i; + return ps->i; +} diff --git a/clang/test/BoundsSafety/CodeGen/addr-of-union-field.c b/clang/test/BoundsSafety/CodeGen/addr-of-union-field.c new file mode 100644 index 0000000000000..8285e78b50f23 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/addr-of-union-field.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +union U { + int i; + long l; +}; + +// CHECK-LABEL: @foo +// CHECK: ret i32 1 +int foo(void) { + union U u; + int *i = &u.i; + long *l = &u.l; + return (void *)i == (void *)l; +} diff --git a/clang/test/BoundsSafety/CodeGen/addrof-member-to-single-O2.c b/clang/test/BoundsSafety/CodeGen/addrof-member-to-single-O2.c new file mode 100644 index 0000000000000..b1a637bed382e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/addrof-member-to-single-O2.c @@ -0,0 +1,191 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64e-apple-iphoneos %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple arm64e-apple-iphoneos %s -o - | FileCheck %s + +#include + +struct foo { + char *ptr; + int i; + long l; +}; + +// CHECK-LABEL: @addrof_single_ptr_to_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr [[F:%.*]] +// +char **addrof_single_ptr_to_single(struct foo *f) { + return &f->ptr; +} + +// CHECK-LABEL: @addrof_single_i_to_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[F:%.*]], i64 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *addrof_single_i_to_single(struct foo *f) { + return &f->i; +} + +// CHECK-LABEL: @addrof_single_l_to_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[F:%.*]], i64 16 +// CHECK-NEXT: ret ptr [[TMP0]] +// +long *addrof_single_l_to_single(struct foo *f) { + return &f->l; +} + +static inline char **__addrof_bidi_ptr_to_single(struct foo *__bidi_indexable f) { + return &f->ptr; +} + +// CHECK-LABEL: @addrof_bidi_ptr_to_single( +// CHECK-NEXT: __addrof_bidi_ptr_to_single.exit: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: ret ptr [[F]] +// +char **addrof_bidi_ptr_to_single(void) { + struct foo f; + return __addrof_bidi_ptr_to_single(&f); +} + +// CHECK-LABEL: @addrof_bidi_ptr_to_single_oob_upper( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +char **addrof_bidi_ptr_to_single_oob_upper(void) { + struct foo f; + struct foo *fp = &f + 1; + return __addrof_bidi_ptr_to_single(fp); +} + + +// CHECK-LABEL: @addrof_bidi_ptr_to_single_oob_lower( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[F]], i64 24 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[F]], i64 -24 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[F]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_I:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_I]], label [[__ADDROF_BIDI_PTR_TO_SINGLE_EXIT:%.*]], label [[TRAP_I:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: __addrof_bidi_ptr_to_single.exit: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: ret ptr [[BOUND_PTR_ARITH]] +// +char **addrof_bidi_ptr_to_single_oob_lower(void) { + struct foo f; + struct foo *fp = &f - 1; + return __addrof_bidi_ptr_to_single(fp); +} + +static inline int *__addrof_bidi_i_to_single(struct foo *__bidi_indexable f) { + return &f->i; +} + +// CHECK-LABEL: @addrof_bidi_i_to_single( +// CHECK-NEXT: __addrof_bidi_i_to_single.exit: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[F]], i64 8 +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *addrof_bidi_i_to_single(void) { + struct foo f; + return __addrof_bidi_i_to_single(&f); +} + +// CHECK-LABEL: @addrof_bidi_i_to_single_oob_upper( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *addrof_bidi_i_to_single_oob_upper(void) { + struct foo f; + struct foo *fp = &f+1; + return __addrof_bidi_i_to_single(fp); +} + +// CHECK-LABEL: @addrof_bidi_i_to_single_oob_lower( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[F]], i64 24 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[F]], i64 -24 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[F]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_I:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_I]], label [[__ADDROF_BIDI_I_TO_SINGLE_EXIT:%.*]], label [[TRAP_I:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: __addrof_bidi_i_to_single.exit: +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[F]], i64 -16 +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: ret ptr [[TMP3]] +// +int *addrof_bidi_i_to_single_oob_lower(void) { + struct foo f; + struct foo *fp = &f-1; + return __addrof_bidi_i_to_single(fp); +} + +static inline long *__addrof_bidi_l_to_single(struct foo *__bidi_indexable f) { + return &f->l; +} + +// CHECK-LABEL: @addrof_bidi_l_to_single( +// CHECK-NEXT: __addrof_bidi_l_to_single.exit: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[F]], i64 16 +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: ret ptr [[TMP0]] +// +long *addrof_bidi_l_to_single(void) { + struct foo f; + return __addrof_bidi_l_to_single(&f); +} + +// CHECK-LABEL: @addrof_bidi_l_to_single_oob_upper( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +long *addrof_bidi_l_to_single_oob_upper(void) { + struct foo f; + struct foo *fp = &f+1; + return __addrof_bidi_l_to_single(fp); +} + +// CHECK-LABEL: @addrof_bidi_l_to_single_oob_lower( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[F]], i64 24 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[F]], i64 -24 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[F]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_I:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_I]], label [[__ADDROF_BIDI_L_TO_SINGLE_EXIT:%.*]], label [[TRAP_I:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: __addrof_bidi_l_to_single.exit: +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[F]], i64 -8 +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[F]]) #[[ATTR6]] +// CHECK-NEXT: ret ptr [[TMP3]] +// +long *addrof_bidi_l_to_single_oob_lower(void) { + struct foo f; + struct foo *fp = &f-1; + return __addrof_bidi_l_to_single(fp); +} diff --git a/clang/test/BoundsSafety/CodeGen/alloc-sized-calloc/alloc-sized-calloc.c b/clang/test/BoundsSafety/CodeGen/alloc-sized-calloc/alloc-sized-calloc.c new file mode 100644 index 0000000000000..dab393a36813e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/alloc-sized-calloc/alloc-sized-calloc.c @@ -0,0 +1,89 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -emit-llvm -triple arm64e -fbounds-safety %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o - | FileCheck %s + +#include +#include "mock-header.h" + + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[SIZ:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 10, ptr [[CNT]], align 4 +// CHECK-NEXT: store i32 4, ptr [[SIZ]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[CNT]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZ]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @my_calloc(i32 noundef [[TMP0]], i32 noundef [[TMP1]]) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 [[TMP0]], [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[CALL]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[MUL]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = load i32, ptr [[CNT]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP12]], 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR3]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = icmp ult ptr [[TMP13]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP15:%.*]] = icmp uge ptr [[TMP13]], [[WIDE_PTR_LB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT9:%.*]], label [[TRAP8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont9: +// CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[TMP13]], align 4 +// CHECK-NEXT: ret i32 [[TMP16]] +// +int foo() { + int cnt = 10; + int siz = sizeof(int); + int *ptr = my_calloc(cnt, siz); + return ptr[cnt-1]; +} diff --git a/clang/test/BoundsSafety/CodeGen/alloc-sized-calloc/mock-header.h b/clang/test/BoundsSafety/CodeGen/alloc-sized-calloc/mock-header.h new file mode 100644 index 0000000000000..a4e57d475c428 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/alloc-sized-calloc/mock-header.h @@ -0,0 +1,7 @@ +#ifndef MOCK_HEADER_H +#define MOCK_HEADER_H + +#pragma clang system_header +void *my_calloc(int count, int size) __attribute__((alloc_size(1,2))); + +#endif /* MOCK_HEADER_H */ diff --git a/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-assign-O2.c b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-assign-O2.c new file mode 100644 index 0000000000000..ac63675310ced --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-assign-O2.c @@ -0,0 +1,189 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64e-apple-iphoneos -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64e-apple-iphoneos -Wno-bounds-safety-init-list -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +#include + +// CHECK-LABEL: @count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void count_ok(int *__counted_by(len - 2) buf, int len) { + int arr[10]; + buf = arr; + len = 12; +} + +// CHECK-LABEL: @negative_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void negative_count_trap(int *__counted_by(len - 2) buf, int len) { + int arr[10]; + int n = 0; + len = n; + buf = arr; +} + +// CHECK-LABEL: @too_big_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void too_big_count_trap(int *__counted_by(len - 2) buf, int len) { + int arr[10]; + int n = 13; + buf = arr; + len = n; +} + +// CHECK-LABEL: @overflow_unsigned_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void overflow_unsigned_count_trap(int *__counted_by(len - 2) buf, size_t len) { + int arr[10]; + int n = 0; + buf = arr; + len = n; +} + +// CHECK-LABEL: @local_count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void local_count_ok(void) { + int arr[10]; + int len = 12; + int *__counted_by(len - 2) buf = arr; +} + +// CHECK-LABEL: @local_count_ok_2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void local_count_ok_2(void) { + int arr[10]; + int len = 2; + int *__counted_by(len - 2) buf; + len = 12; + buf = arr; +} + +// CHECK-LABEL: @local_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void local_count_trap(void) { + int arr[10]; + int n = 0; + int len = n; + int *__counted_by(len - 2) buf = arr; +} + +// CHECK-LABEL: @loob_local_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP16_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP16_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void loob_local_count_trap(void) { + int arr[10]; + int len = 12; + int *__counted_by(len - 2) buf = arr - 1; +} + +// CHECK-LABEL: @uoob_local_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void uoob_local_count_trap(void) { + int arr[10]; + int len = 12; + int *__counted_by(len - 2) buf = arr + 1; +} + +// CHECK-LABEL: @too_big_local_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void too_big_local_count_trap(void) { + int arr[10]; + int n = 13; + int len = 2; + int *__counted_by(len - 2) buf; + buf = arr; + len = n; +} + +// CHECK-LABEL: @overflow_local_unsigned_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void overflow_local_unsigned_count_trap(void) { + int arr[10]; + int n = 0; + int len = n; + int *__counted_by(len - 2) buf = arr; +} + +// CHECK-LABEL: @overflow_unsigned_count_size_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void overflow_unsigned_count_size_ok(void *__sized_by(count * size) buf, size_t count, size_t size) { + int arr[10]; + buf = arr; + count = __SIZE_MAX__; + size = __SIZE_MAX__; +} + +// CHECK-LABEL: @overflow_unsigned_count_size_trap2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void overflow_unsigned_count_size_trap2(void *__sized_by(count * size) buf, size_t count, size_t size) { + int arr[10]; + buf = arr; + count = __SIZE_MAX__; + size = __SIZE_MAX__; + + char *p = buf; + p[1] = 0; +} + +// CHECK-LABEL: @overflow_unsigned_count_size_index_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[MUL:%.*]] = mul i64 [[SIZE:%.*]], [[COUNT:%.*]] +// CHECK-NEXT: [[CMP:%.*]] = icmp sgt i64 [[MUL]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void overflow_unsigned_count_size_index_trap(int *__counted_by(count * size) buf, size_t count, size_t size) { + buf = buf; + count = (size_t)(__SIZE_MAX__ >> 2) + (size_t)1; + size = 1; + + buf[0] = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-assign.c b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-assign.c new file mode 100644 index 0000000000000..a7db2f13909e3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-assign.c @@ -0,0 +1,227 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64e-apple-iphoneos -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64e-apple-iphoneos -Wno-bounds-safety-init-list -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +#include + +// CHECK-LABEL: @count_size_mul( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[C_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[S_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[COUNT:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[SIZE:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[BUF:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR:%.*]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i64 [[C:%.*]], ptr [[C_ADDR]], align 8 +// CHECK-NEXT: store i64 [[S:%.*]], ptr [[S_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[COUNT]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store i64 0, ptr [[SIZE]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr [[C_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: [[MUL:%.*]] = mul i64 [[TMP0]], [[TMP1]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR22]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB24]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB26]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp ule i64 [[MUL]], [[SUB_PTR_SUB]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP33]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i64 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: store i64 [[TMP1]], ptr [[SIZE]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void count_size_mul(void *__bidi_indexable ptr, size_t c, size_t s) { + size_t count; + size_t size; + void *__sized_by(count * size) buf; + + count = c; + size = s; + buf = ptr; +} + +// CHECK-LABEL: @iptr_count_size_mul( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[C_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[S_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[COUNT:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[SIZE:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[BUF:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[PTR:%.*]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i64 [[C:%.*]], ptr [[C_ADDR]], align 8 +// CHECK-NEXT: store i64 [[S:%.*]], ptr [[S_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[COUNT]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store i64 0, ptr [[SIZE]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr [[C_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR4]], [[WIDE_PTR_UB11]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP12]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP15]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP15]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP15]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8 +// CHECK-NEXT: [[CMP22:%.*]] = icmp ule ptr [[WIDE_PTR_LB14]], [[WIDE_PTR_PTR17]] +// CHECK-NEXT: br i1 [[CMP22]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: [[MUL:%.*]] = mul i64 [[TMP0]], [[TMP1]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP23]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// CHECK-NEXT: [[CMP33:%.*]] = icmp ule i64 [[MUL]], [[SUB_PTR_DIV]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP33]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i64 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: store i64 [[TMP1]], ptr [[SIZE]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void iptr_count_size_mul(void *__bidi_indexable ptr, size_t c, size_t s) { + size_t count; + size_t size; + int *__counted_by(count * size) buf; + + count = c; + size = s; + buf = ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-calls-O2.c b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-calls-O2.c new file mode 100644 index 0000000000000..7304766eeb3f1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-calls-O2.c @@ -0,0 +1,124 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64e-apple-iphoneos -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64e-apple-iphoneos -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +#include + +inline void param_with_count(int *__counted_by(len - 2) buf, int len) {} + +// CHECK-LABEL: @count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void count_ok(void) { + int arr[10]; + param_with_count(arr, 12); +} + +// CHECK-LABEL: @negative_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void negative_count_trap(void) { + int arr[10]; + int len = 0; + param_with_count(arr, len); +} + +// CHECK-LABEL: @too_big_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void too_big_count_trap(void) { + int arr[10]; + int n = 13; + param_with_count(arr, n); +} + +inline void param_with_unsigned_count(int *__counted_by(len - 2) buf, size_t len) {} + +// CHECK-LABEL: @overflow_unsigned_count_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void overflow_unsigned_count_trap(void) { + int arr[10]; + int n = 0; + param_with_unsigned_count(arr, n); +} + +void *__sized_by(count * size) return_buf(size_t count, size_t size); + +// CHECK-LABEL: @returned_buf_ok( +// CHECK-NEXT: cont8: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @return_buf(i64 noundef 4, i64 noundef 5) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[CALL]], i64 19 +// CHECK-NEXT: store i8 0, ptr [[TMP0]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void returned_buf_ok(void) { + char *p = return_buf(4, 5); + p[19] = 0; +} + +// CHECK-LABEL: @oob_returned_buf_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @return_buf(i64 noundef 4, i64 noundef 5) #[[ATTR6]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void oob_returned_buf_trap(void) { + char *p = return_buf(4, 5); + p[20] = 0; +} + +// CHECK-LABEL: @overflow_returned_buf_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @return_buf(i64 noundef -1, i64 noundef -1) #[[ATTR6]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void overflow_returned_buf_trap(void) { + char *p = return_buf(__SIZE_MAX__, __SIZE_MAX__); + p[1] = 0; +} + +// CHECK-LABEL: @overflow_returned_buf_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @return_buf(i64 noundef -1, i64 noundef -1) #[[ATTR6]] +// CHECK-NEXT: store i8 0, ptr [[CALL]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void overflow_returned_buf_ok(void) { + char *p = return_buf(__SIZE_MAX__, __SIZE_MAX__); + p[0] = 0; +} + +// CHECK-LABEL: @too_big_returned_buf_undef( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @return_buf(i64 noundef -1, i64 noundef 1) #[[ATTR6]] +// CHECK-NEXT: unreachable +// +void too_big_returned_buf_undef(void) { + char *p = return_buf(__SIZE_MAX__, 1); + p[0] = 0; +} + +// XXX: In this case, the count argument already violates the assumption that the offset doesn't overflow in a signed sense. +// This should actually be caught already when the count is assigned in side the callee, but if the callee's definition is +// not -fbounds-safety'ed, there's not much we can do here. +// CHECK-LABEL: @index_overflow_returned_buf_fixme( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @return_buf(i64 noundef 9223372036854775807, i64 noundef 1) #[[ATTR6]] +// CHECK-NEXT: store i8 0, ptr [[CALL]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void index_overflow_returned_buf_fixme(void) { + char *p = return_buf(__SIZE_MAX__ >> 1, 1); + p[0] = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-calls.c b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-calls.c new file mode 100644 index 0000000000000..83f70f7447fa6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/arithmetic-ops-in-counted-by-calls.c @@ -0,0 +1,184 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64e-apple-ios -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64e-apple-ios -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +#include + +inline void param_with_count(int *__counted_by(len - 2) buf, int len) {} + +// CHECK-LABEL: @pass_count( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BUF]], i64 24, i1 false) +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP0]], 2 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP1]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END51:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB14]], ptr [[TMP2]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP28]], label [[LAND_RHS:%.*]], label [[LAND_END51]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB32]], ptr [[TMP3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR34]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR41]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP46:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP46]], label [[LAND_RHS48:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs48: +// CHECK-NEXT: [[CMP49:%.*]] = icmp sle i64 0, [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP49]], [[LAND_RHS48]] ] +// CHECK-NEXT: br label [[LAND_END51]], {{!annotation ![0-9]+}} +// CHECK: land.end51: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP4]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: call void @param_with_count(ptr noundef [[WIDE_PTR_PTR54]], i32 noundef [[TMP0]]) +// CHECK-NEXT: ret void +// +void pass_count(int *__bidi_indexable buf, int len) { + param_with_count(buf, len); +} + +void *__sized_by(count * size) return_buf(size_t count, size_t size); + +// CHECK-LABEL: @get_returned_buf( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store i64 [[COUNT:%.*]], ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: store i64 [[SIZE:%.*]], ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i64, ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @return_buf(i64 noundef [[TMP0]], i64 noundef [[TMP1]]) +// CHECK-NEXT: [[MUL:%.*]] = mul i64 [[TMP0]], [[TMP1]] +// CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[MUL]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[CALL]], i64 [[MUL]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR3]], i64 1 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[TMP8]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[TMP8]], [[WIDE_PTR_LB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT9:%.*]], label [[TRAP8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont9: +// CHECK-NEXT: store i8 0, ptr [[TMP8]], align 1 +// CHECK-NEXT: ret void +// +void get_returned_buf(size_t count, size_t size) { + char *p = return_buf(count, size); + p[1] = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/array-bound-deref-addrof.c b/clang/test/BoundsSafety/CodeGen/array-bound-deref-addrof.c new file mode 100644 index 0000000000000..2197b5247eb69 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/array-bound-deref-addrof.c @@ -0,0 +1,153 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +#include + +// CHECK-O0-LABEL: @foo( +// CHECK-O0: {{.*}}: +// CHECK-O0: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-O0: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: [[TMP0:%.*]] = getelementptr [10 x i32], ptr [[ARR]], i64 1 +// CHECK-O0: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0: store ptr [[ARR]], ptr [[TMP1]], align 8 +// CHECK-O0: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-O0: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0: store ptr [[ARR]], ptr [[TMP3]], align 8 +// CHECK-O0: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP5]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[WIDE_PTR_PTR]], i64 0, i64 0 +// CHECK-O0: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP6]], align 8 +// CHECK-O0: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0: store ptr [[UPPER]], ptr [[TMP7]], align 8 +// CHECK-O0: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP8]], align 8 +// CHECK-O0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-O0: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-O0: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR5]], i64 10 +// CHECK-O0: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-O0: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-O0: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-O0: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-O0: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP10]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP11]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// CHECK-O0: ret i32 [[TMP12]] +// +// CHECK-O2-LABEL: @foo( +// CHECK-O2: {{.*}}: +// CHECK-O2: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2: unreachable +// +int foo() { + int arr[10]; + int *ptr = *&arr; + return ptr[10]; +} + +// CHECK-O0-LABEL: @bar( +// CHECK-O0: {{.*}}: +// CHECK-O0: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-O0: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: [[TMP0:%.*]] = getelementptr [10 x i32], ptr [[ARR]], i64 1 +// CHECK-O0: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0: store ptr [[ARR]], ptr [[TMP1]], align 8 +// CHECK-O0: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-O0: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0: store ptr [[ARR]], ptr [[TMP3]], align 8 +// CHECK-O0: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP5]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[WIDE_PTR_PTR]], i64 0, i64 0 +// CHECK-O0: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP6]], align 8 +// CHECK-O0: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0: store ptr [[UPPER]], ptr [[TMP7]], align 8 +// CHECK-O0: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP8]], align 8 +// CHECK-O0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-O0: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-O0: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR5]], i64 9 +// CHECK-O0: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-O0: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-O0: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-O0: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-O0: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP10]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP11]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// CHECK-O0: ret i32 [[TMP12]] +// +// CHECK-O2-LABEL: @bar( +// CHECK-O2: {{.*}}: +// CHECK-O2: ret i32 undef +// +int bar() { + int arr[10]; + int *ptr = *&arr; + return ptr[9]; +} diff --git a/clang/test/BoundsSafety/CodeGen/array-decay.c b/clang/test/BoundsSafety/CodeGen/array-decay.c new file mode 100644 index 0000000000000..e397937981a0f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/array-decay.c @@ -0,0 +1,101 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 + +// CHECK-O0-LABEL: @foo( +// CHECK-O0: {{.*}}: +// CHECK-O0: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: call void @llvm.memset.p0.i64(ptr align 4 [[ARR]], i8 0, i64 40, i1 false) +// CHECK-O0: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0: [[TMP3:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 10 +// CHECK-O0: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0: [[TMP4:%.*]] = icmp ult ptr [[TMP3]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP5:%.*]] = icmp uge ptr [[TMP3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP5]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP6:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-O0: ret i32 [[TMP6]] +// +// CHECK-O2-LABEL: @foo( +// CHECK-O2: {{.*}}: +// CHECK-O2: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2: unreachable +// +int foo() { + int arr[10] = {0}; + int *ptr = arr; + return ptr[10]; +} + +// CHECK-O0-LABEL: @bar( +// CHECK-O0: {{.*}}: +// CHECK-O0: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0: call void @llvm.memset.p0.i64(ptr align 4 [[ARR]], i8 0, i64 40, i1 false) +// CHECK-O0: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0: [[TMP3:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 9 +// CHECK-O0: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0: [[TMP4:%.*]] = icmp ult ptr [[TMP3]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP5:%.*]] = icmp uge ptr [[TMP3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0: br i1 [[TMP5]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: {{.*}}: +// CHECK-O0: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0: unreachable +// CHECK-O0: {{.*}}: +// CHECK-O0: [[TMP6:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-O0: ret i32 [[TMP6]] +// +// CHECK-O2-LABEL: @bar( +// CHECK-O2: entry: +// CHECK-O2: ret i32 0 +// +int bar() { + int arr[10] = {0}; + int *ptr = arr; + return ptr[9]; +} diff --git a/clang/test/BoundsSafety/CodeGen/array_subscript_agg.c b/clang/test/BoundsSafety/CodeGen/array_subscript_agg.c new file mode 100644 index 0000000000000..cb7869dc0d795 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/array_subscript_agg.c @@ -0,0 +1,1723 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin +// Include the `access_size` because this effects the checks that are emitted. +// RUN: %clang_cc1 -O0 -fbounds-safety -fbounds-safety-bringup-missing-checks=access_size,array_subscript_agg -triple arm64-apple-iphoneos -emit-llvm %s -o - | FileCheck --check-prefixes=NEW %s +// RUN: %clang_cc1 -O0 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=access_size,array_subscript_agg -triple arm64-apple-iphoneos -emit-llvm %s -o - | FileCheck --check-prefixes=LEGACY %s +#include +#include + +struct Foo { + int x; + int y; +}; + +// NEW-LABEL: define dso_local i64 @access_Foo_bi( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0:[0-9]+]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2:![0-9]+]] +// NEW-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4:![0-9]+]] +// NEW-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// NEW-NEXT: [[TMP5:%.*]] = load i64, ptr [[RETVAL]], align 4 +// NEW-NEXT: ret i64 [[TMP5]] +// +// LEGACY-LABEL: define dso_local i64 @access_Foo_bi( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// LEGACY-NEXT: [[TMP1:%.*]] = load i64, ptr [[RETVAL]], align 4 +// LEGACY-NEXT: ret i64 [[TMP1]] +// +struct Foo access_Foo_bi(struct Foo* __bidi_indexable ptr, int idx) { + return ptr[idx]; +} + +// NEW-LABEL: define dso_local i64 @access_Foo_idx( +// NEW-SAME: [2 x i64] noundef [[PTR_COERCE:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// NEW-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// NEW-NEXT: store [2 x i64] [[PTR_COERCE]], ptr [[PTR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 16, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// NEW-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP3]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR3]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[WIDE_PTR_UB5]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP5]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP6:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP4]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP6]], label %[[CONT7:.*]], label %[[TRAP6:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP6]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT7]]: +// NEW-NEXT: [[TMP7:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP7]], label %[[CONT9:.*]], label %[[TRAP8:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP8]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT9]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// NEW-NEXT: [[TMP8:%.*]] = load i64, ptr [[RETVAL]], align 4 +// NEW-NEXT: ret i64 [[TMP8]] +// +// LEGACY-LABEL: define dso_local i64 @access_Foo_idx( +// LEGACY-SAME: [2 x i64] noundef [[PTR_COERCE:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// LEGACY-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// LEGACY-NEXT: store [2 x i64] [[PTR_COERCE]], ptr [[PTR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 16, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP3]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR3]], i64 [[IDXPROM]] +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// LEGACY-NEXT: [[TMP4:%.*]] = load i64, ptr [[RETVAL]], align 4 +// LEGACY-NEXT: ret i64 [[TMP4]] +// +struct Foo access_Foo_idx(struct Foo* __indexable ptr, int idx) { + return ptr[idx]; +} + +// NEW-LABEL: define dso_local i64 @access_Foo_cb( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// NEW-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[TMP0]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP5]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP6:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP7:%.*]] = icmp ule ptr [[TMP6]], [[WIDE_PTR_UB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP8:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP6]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP8]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP9:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP9]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// NEW-NEXT: [[TMP10:%.*]] = load i64, ptr [[RETVAL]], align 4 +// NEW-NEXT: ret i64 [[TMP10]] +// +// LEGACY-LABEL: define dso_local i64 @access_Foo_cb( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[TMP0]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP5]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// LEGACY-NEXT: [[TMP6:%.*]] = load i64, ptr [[RETVAL]], align 4 +// LEGACY-NEXT: ret i64 [[TMP6]] +// +struct Foo access_Foo_cb(struct Foo* __counted_by(count) ptr, int idx, int count) { + return ptr[idx]; +} + +// NEW-LABEL: define dso_local i64 @access_Foo_cbon( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// NEW-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// NEW-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META5:![0-9]+]] +// NEW-NEXT: br i1 [[TMP2]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// NEW: [[BOUNDSCHECK_NOTNULL]]: +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[TMP0]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// NEW-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// NEW: [[BOUNDSCHECK_NULL]]: +// NEW-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr null, ptr [[TMP6]], align 8 +// NEW-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr null, ptr [[TMP7]], align 8 +// NEW-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr null, ptr [[TMP8]], align 8 +// NEW-NEXT: br label %[[BOUNDSCHECK_CONT]] +// NEW: [[BOUNDSCHECK_CONT]]: +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP9:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP9]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP10:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP11:%.*]] = icmp ule ptr [[TMP10]], [[WIDE_PTR_UB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP11]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP12:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP10]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP12]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP13:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP13]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// NEW-NEXT: [[TMP14:%.*]] = load i64, ptr [[RETVAL]], align 4 +// NEW-NEXT: ret i64 [[TMP14]] +// +// LEGACY-LABEL: define dso_local i64 @access_Foo_cbon( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2:![0-9]+]] +// LEGACY-NEXT: br i1 [[TMP2]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META2]] +// LEGACY: [[BOUNDSCHECK_NOTNULL]]: +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[TMP0]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// LEGACY-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// LEGACY: [[BOUNDSCHECK_NULL]]: +// LEGACY-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr null, ptr [[TMP6]], align 8 +// LEGACY-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr null, ptr [[TMP7]], align 8 +// LEGACY-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr null, ptr [[TMP8]], align 8 +// LEGACY-NEXT: br label %[[BOUNDSCHECK_CONT]] +// LEGACY: [[BOUNDSCHECK_CONT]]: +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP9:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP9]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// LEGACY-NEXT: [[TMP10:%.*]] = load i64, ptr [[RETVAL]], align 4 +// LEGACY-NEXT: ret i64 [[TMP10]] +// +struct Foo access_Foo_cbon(struct Foo* __counted_by_or_null(count) ptr, int idx, int count) { + return ptr[idx]; +} + +// NEW-LABEL: define dso_local i64 @access_Foo_sb( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// NEW-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP5]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP6:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP7:%.*]] = icmp ule ptr [[TMP6]], [[WIDE_PTR_UB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP8:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP6]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP8]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP9:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP9]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// NEW-NEXT: [[TMP10:%.*]] = load i64, ptr [[RETVAL]], align 4 +// NEW-NEXT: ret i64 [[TMP10]] +// +// LEGACY-LABEL: define dso_local i64 @access_Foo_sb( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP5]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// LEGACY-NEXT: [[TMP6:%.*]] = load i64, ptr [[RETVAL]], align 4 +// LEGACY-NEXT: ret i64 [[TMP6]] +// +struct Foo access_Foo_sb(struct Foo* __sized_by(count) ptr, int idx, int count) { + return ptr[idx]; +} + +// NEW-LABEL: define dso_local i64 @access_Foo_sbon( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// NEW-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// NEW-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META5]] +// NEW-NEXT: br i1 [[TMP2]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// NEW: [[BOUNDSCHECK_NOTNULL]]: +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// NEW-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// NEW: [[BOUNDSCHECK_NULL]]: +// NEW-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr null, ptr [[TMP6]], align 8 +// NEW-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr null, ptr [[TMP7]], align 8 +// NEW-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr null, ptr [[TMP8]], align 8 +// NEW-NEXT: br label %[[BOUNDSCHECK_CONT]] +// NEW: [[BOUNDSCHECK_CONT]]: +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP9:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP9]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP10:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP11:%.*]] = icmp ule ptr [[TMP10]], [[WIDE_PTR_UB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP11]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP12:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP10]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP12]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP13:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP13]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// NEW-NEXT: [[TMP14:%.*]] = load i64, ptr [[RETVAL]], align 4 +// NEW-NEXT: ret i64 [[TMP14]] +// +// LEGACY-LABEL: define dso_local i64 @access_Foo_sbon( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// LEGACY-NEXT: br i1 [[TMP2]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META2]] +// LEGACY: [[BOUNDSCHECK_NOTNULL]]: +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// LEGACY-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// LEGACY: [[BOUNDSCHECK_NULL]]: +// LEGACY-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr null, ptr [[TMP6]], align 8 +// LEGACY-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr null, ptr [[TMP7]], align 8 +// LEGACY-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr null, ptr [[TMP8]], align 8 +// LEGACY-NEXT: br label %[[BOUNDSCHECK_CONT]] +// LEGACY: [[BOUNDSCHECK_CONT]]: +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP9:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP9]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// LEGACY-NEXT: [[TMP10:%.*]] = load i64, ptr [[RETVAL]], align 4 +// LEGACY-NEXT: ret i64 [[TMP10]] +// +struct Foo access_Foo_sbon(struct Foo* __sized_by_or_null(count) ptr, int idx, int count) { + return ptr[idx]; +} + +// NEW-LABEL: define dso_local i64 @access_Foo_var_array_size( +// NEW-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// NEW-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// NEW-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 +// NEW-NEXT: [[TMP2:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[TMP2]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// NEW-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP7:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP7]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP9]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP10:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP8]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP10]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP11:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP11]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// NEW-NEXT: [[TMP12:%.*]] = load i64, ptr [[RETVAL]], align 4 +// NEW-NEXT: ret i64 [[TMP12]] +// +// LEGACY-LABEL: define dso_local i64 @access_Foo_var_array_size( +// LEGACY-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// LEGACY-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[TMP2:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[TMP2]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP7:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP7]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// LEGACY-NEXT: [[TMP8:%.*]] = load i64, ptr [[RETVAL]], align 4 +// LEGACY-NEXT: ret i64 [[TMP8]] +// +struct Foo access_Foo_var_array_size(int count, struct Foo ptr[count], int idx) { + return ptr[idx]; +} + +// NEW-LABEL: define dso_local i64 @access_Foo_const_array_size( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[TMP0]], i64 5 +// NEW-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// NEW-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP4]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP6]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP7:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP5]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP7]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP8:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP8]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// NEW-NEXT: [[TMP9:%.*]] = load i64, ptr [[RETVAL]], align 4 +// NEW-NEXT: ret i64 [[TMP9]] +// +// LEGACY-LABEL: define dso_local i64 @access_Foo_const_array_size( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[TMP0]], i64 5 +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP4]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// LEGACY-NEXT: [[TMP5:%.*]] = load i64, ptr [[RETVAL]], align 4 +// LEGACY-NEXT: ret i64 [[TMP5]] +// +struct Foo access_Foo_const_array_size(struct Foo ptr[5], int idx) { + return ptr[idx]; +} + +struct NestedArrayOfStructs { + struct Foo arr[5]; +}; + +// NEW-LABEL: define dso_local i64 @access_Foo_from_struct_arr( +// NEW-SAME: ptr noundef [[NAOS:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// NEW-NEXT: [[NAOS_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: store ptr [[NAOS]], ptr [[NAOS_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NAOS_ADDR]], align 8 +// NEW-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTEDARRAYOFSTRUCTS:%.*]], ptr [[TMP0]], i32 0, i32 0 +// NEW-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [5 x %struct.Foo], ptr [[ARR]], i64 0, i64 0 +// NEW-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[ARRAYDECAY]], i64 5 +// NEW-NEXT: [[TMP1:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP1]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYDECAY]], i64 [[IDXPROM]] +// NEW-NEXT: [[TMP2:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP3:%.*]] = icmp ule ptr [[TMP2]], [[UPPER]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP4:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP2]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP4]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP5:%.*]] = icmp uge ptr [[ARRAYIDX]], [[ARRAYDECAY]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP5]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// NEW-NEXT: [[TMP6:%.*]] = load i64, ptr [[RETVAL]], align 4 +// NEW-NEXT: ret i64 [[TMP6]] +// +// LEGACY-LABEL: define dso_local i64 @access_Foo_from_struct_arr( +// LEGACY-SAME: ptr noundef [[NAOS:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// LEGACY-NEXT: [[NAOS_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store ptr [[NAOS]], ptr [[NAOS_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NAOS_ADDR]], align 8 +// LEGACY-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTEDARRAYOFSTRUCTS:%.*]], ptr [[TMP0]], i32 0, i32 0 +// LEGACY-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [5 x %struct.Foo], ptr [[ARR]], i64 0, i64 0 +// LEGACY-NEXT: [[UPPER:%.*]] = getelementptr inbounds [[STRUCT_FOO]], ptr [[ARRAYDECAY]], i64 5 +// LEGACY-NEXT: [[TMP1:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP1]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYDECAY]], i64 [[IDXPROM]] +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// LEGACY-NEXT: [[TMP2:%.*]] = load i64, ptr [[RETVAL]], align 4 +// LEGACY-NEXT: ret i64 [[TMP2]] +// +struct Foo access_Foo_from_struct_arr(struct NestedArrayOfStructs* naos, int idx) { + return naos->arr[idx]; +} + +struct HasFAM { + int count; + struct Foo fam[__counted_by(count)]; +}; + +// FIXME: The `__counted_by` attribute isn't used for the bounds check on the +// ArraySubscriptExpr (rdar://145253815). + +// NEW-LABEL: define dso_local i64 @access_Foo_from_HasFAM( +// NEW-SAME: ptr noundef [[HAS_FAM:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// NEW-NEXT: [[HAS_FAM_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: store ptr [[HAS_FAM]], ptr [[HAS_FAM_INDIRECT_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[HAS_FAM]], i64 24, i1 false) +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_HASFAM:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// NEW-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META6:![0-9]+]] +// NEW-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META6]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META6]] +// NEW-NEXT: unreachable, !annotation [[META6]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP2]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_HASFAM]], ptr [[WIDE_PTR_PTR]], i32 0, i32 1 +// NEW-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x %struct.Foo], ptr [[FAM]], i64 0, i64 0 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 0 +// NEW-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR9]], ptr [[TMP5]], align 8 +// NEW-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP6]], align 8 +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8 +// NEW-NEXT: [[TMP7:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP7]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR11]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8 +// NEW-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB13]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP9]], label %[[CONT17:.*]], label %[[TRAP16:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP16]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT17]]: +// NEW-NEXT: [[TMP10:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP8]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP10]], label %[[CONT19:.*]], label %[[TRAP18:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP18]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT19]]: +// NEW-NEXT: [[TMP11:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB15]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP11]], label %[[CONT21:.*]], label %[[TRAP20:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP20]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT21]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// NEW-NEXT: [[TMP12:%.*]] = load i64, ptr [[RETVAL]], align 4 +// NEW-NEXT: ret i64 [[TMP12]] +// +// LEGACY-LABEL: define dso_local i64 @access_Foo_from_HasFAM( +// LEGACY-SAME: ptr noundef [[HAS_FAM:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// LEGACY-NEXT: [[HAS_FAM_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// LEGACY-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// LEGACY-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// LEGACY-NEXT: store ptr [[HAS_FAM]], ptr [[HAS_FAM_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[HAS_FAM]], i64 24, i1 false) +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_HASFAM:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// LEGACY-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META3:![0-9]+]] +// LEGACY-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF4:![0-9]+]], !annotation [[META3]] +// LEGACY: [[TRAP]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META3]] +// LEGACY-NEXT: unreachable, !annotation [[META3]] +// LEGACY: [[CONT]]: +// LEGACY-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META5:![0-9]+]] +// LEGACY-NEXT: br i1 [[TMP2]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF4]], !annotation [[META5]] +// LEGACY: [[TRAP3]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// LEGACY-NEXT: unreachable, !annotation [[META5]] +// LEGACY: [[CONT4]]: +// LEGACY-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_HASFAM]], ptr [[WIDE_PTR_PTR]], i32 0, i32 1 +// LEGACY-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x %struct.Foo], ptr [[FAM]], i64 0, i64 0 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR9]], ptr [[TMP5]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP6]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8 +// LEGACY-NEXT: [[TMP7:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP7]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR11]], i64 [[IDXPROM]] +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// LEGACY-NEXT: [[TMP8:%.*]] = load i64, ptr [[RETVAL]], align 4 +// LEGACY-NEXT: ret i64 [[TMP8]] +// +struct Foo access_Foo_from_HasFAM(struct HasFAM* __bidi_indexable has_fam, int idx) { + return has_fam->fam[idx]; +} + +// Access with a MemberExpr has identical codegen + +// NEW-LABEL: define dso_local i64 @access_Foo_eb( +// NEW-SAME: ptr noundef [[START:%.*]], i32 noundef [[IDX:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// NEW-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// NEW-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP6:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP6]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP7:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP8:%.*]] = icmp ule ptr [[TMP7]], [[WIDE_PTR_UB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP9:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP7]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP9]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP10:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP10]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// NEW-NEXT: [[TMP11:%.*]] = load i64, ptr [[RETVAL]], align 4 +// NEW-NEXT: ret i64 [[TMP11]] +// +// LEGACY-LABEL: define dso_local i64 @access_Foo_eb( +// LEGACY-SAME: ptr noundef [[START:%.*]], i32 noundef [[IDX:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// LEGACY-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// LEGACY-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP6]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[ARRAYIDX]], i64 8, i1 false) +// LEGACY-NEXT: [[TMP7:%.*]] = load i64, ptr [[RETVAL]], align 4 +// LEGACY-NEXT: ret i64 [[TMP7]] +// +struct Foo access_Foo_eb(struct Foo* __ended_by(end) start, int idx, struct Foo* end) { + return start[idx]; +} + +// NEW-LABEL: define dso_local i32 @access_Foo_member_bi( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP3]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP4]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[ARRAYIDX]], i32 0, i32 0 +// NEW-NEXT: [[TMP5:%.*]] = load i32, ptr [[X]], align 4 +// NEW-NEXT: ret i32 [[TMP5]] +// +// LEGACY-LABEL: define dso_local i32 @access_Foo_member_bi( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], !annotation [[META3]] +// LEGACY-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF4]], !annotation [[META3]] +// LEGACY: [[TRAP]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// LEGACY-NEXT: unreachable, !annotation [[META3]] +// LEGACY: [[CONT]]: +// LEGACY-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META5]] +// LEGACY-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF4]], !annotation [[META5]] +// LEGACY: [[TRAP1]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// LEGACY-NEXT: unreachable, !annotation [[META5]] +// LEGACY: [[CONT2]]: +// LEGACY-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[ARRAYIDX]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP3:%.*]] = load i32, ptr [[X]], align 4 +// LEGACY-NEXT: ret i32 [[TMP3]] +// +int access_Foo_member_bi(struct Foo* __bidi_indexable ptr, int idx) { + return ptr[idx].x; +} + +// NEW-LABEL: define dso_local i32 @access_Foo_member_idx( +// NEW-SAME: [2 x i64] noundef [[PTR_COERCE:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// NEW-NEXT: store [2 x i64] [[PTR_COERCE]], ptr [[PTR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 16, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// NEW-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP3]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR3]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[WIDE_PTR_UB5]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP5]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP6:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP4]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP6]], label %[[CONT7:.*]], label %[[TRAP6:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP6]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT7]]: +// NEW-NEXT: [[TMP7:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP7]], label %[[CONT9:.*]], label %[[TRAP8:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP8]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT9]]: +// NEW-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[ARRAYIDX]], i32 0, i32 0 +// NEW-NEXT: [[TMP8:%.*]] = load i32, ptr [[X]], align 4 +// NEW-NEXT: ret i32 [[TMP8]] +// +// LEGACY-LABEL: define dso_local i32 @access_Foo_member_idx( +// LEGACY-SAME: [2 x i64] noundef [[PTR_COERCE:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// LEGACY-NEXT: store [2 x i64] [[PTR_COERCE]], ptr [[PTR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 16, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP3]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR3]], i64 [[IDXPROM]] +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB5]], !annotation [[META3]] +// LEGACY-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF4]], !annotation [[META3]] +// LEGACY: [[TRAP]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// LEGACY-NEXT: unreachable, !annotation [[META3]] +// LEGACY: [[CONT]]: +// LEGACY-NEXT: [[TMP5:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META5]] +// LEGACY-NEXT: br i1 [[TMP5]], label %[[CONT7:.*]], label %[[TRAP6:.*]], !prof [[PROF4]], !annotation [[META5]] +// LEGACY: [[TRAP6]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// LEGACY-NEXT: unreachable, !annotation [[META5]] +// LEGACY: [[CONT7]]: +// LEGACY-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[ARRAYIDX]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP6:%.*]] = load i32, ptr [[X]], align 4 +// LEGACY-NEXT: ret i32 [[TMP6]] +// +int access_Foo_member_idx(struct Foo* __indexable ptr, int idx) { + return ptr[idx].x; +} + +// NEW-LABEL: define dso_local i32 @access_Foo_member_cb( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// NEW-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP5]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP6:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP7:%.*]] = icmp ule ptr [[TMP6]], [[WIDE_PTR_UB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP8:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP6]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP8]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP9:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP9]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[ARRAYIDX]], i32 0, i32 0 +// NEW-NEXT: [[TMP10:%.*]] = load i32, ptr [[X]], align 4 +// NEW-NEXT: ret i32 [[TMP10]] +// +// LEGACY-LABEL: define dso_local i32 @access_Foo_member_cb( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP5]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], !annotation [[META3]] +// LEGACY-NEXT: br i1 [[TMP6]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF4]], !annotation [[META3]] +// LEGACY: [[TRAP]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// LEGACY-NEXT: unreachable, !annotation [[META3]] +// LEGACY: [[CONT]]: +// LEGACY-NEXT: [[TMP7:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META5]] +// LEGACY-NEXT: br i1 [[TMP7]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF4]], !annotation [[META5]] +// LEGACY: [[TRAP1]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// LEGACY-NEXT: unreachable, !annotation [[META5]] +// LEGACY: [[CONT2]]: +// LEGACY-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[ARRAYIDX]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP8:%.*]] = load i32, ptr [[X]], align 4 +// LEGACY-NEXT: ret i32 [[TMP8]] +// +int access_Foo_member_cb(struct Foo* __counted_by(count) ptr, int idx, int count) { + return ptr[idx].x; +} + +// Member access is also an aggregate +struct MemberIsAgg { + struct Foo agg; +}; +// NEW-LABEL: define dso_local i64 @access_MemberIsAgg_member_bidi( +// NEW-SAME: [2 x i64] noundef [[PTR_COERCE:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// NEW-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// NEW-NEXT: store [2 x i64] [[PTR_COERCE]], ptr [[PTR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 16, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// NEW-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// NEW-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP3]] to i64 +// NEW-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_MEMBERISAGG:%.*]], ptr [[WIDE_PTR_PTR3]], i64 [[IDXPROM]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_MEMBERISAGG]], ptr [[ARRAYIDX]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[WIDE_PTR_UB5]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP5]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP6:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP4]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP6]], label %[[CONT7:.*]], label %[[TRAP6:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP6]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT7]]: +// NEW-NEXT: [[TMP7:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP7]], label %[[CONT9:.*]], label %[[TRAP8:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP8]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT9]]: +// NEW-NEXT: [[AGG:%.*]] = getelementptr inbounds nuw [[STRUCT_MEMBERISAGG]], ptr [[ARRAYIDX]], i32 0, i32 0 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[AGG]], i64 8, i1 false) +// NEW-NEXT: [[TMP8:%.*]] = load i64, ptr [[RETVAL]], align 4 +// NEW-NEXT: ret i64 [[TMP8]] +// +// LEGACY-LABEL: define dso_local i64 @access_MemberIsAgg_member_bidi( +// LEGACY-SAME: [2 x i64] noundef [[PTR_COERCE:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// LEGACY-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// LEGACY-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// LEGACY-NEXT: store [2 x i64] [[PTR_COERCE]], ptr [[PTR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 16, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP3]] to i64 +// LEGACY-NEXT: [[ARRAYIDX:%.*]] = getelementptr [[STRUCT_MEMBERISAGG:%.*]], ptr [[WIDE_PTR_PTR3]], i64 [[IDXPROM]] +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB5]], !annotation [[META3]] +// LEGACY-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF4]], !annotation [[META3]] +// LEGACY: [[TRAP]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// LEGACY-NEXT: unreachable, !annotation [[META3]] +// LEGACY: [[CONT]]: +// LEGACY-NEXT: [[TMP5:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META5]] +// LEGACY-NEXT: br i1 [[TMP5]], label %[[CONT7:.*]], label %[[TRAP6:.*]], !prof [[PROF4]], !annotation [[META5]] +// LEGACY: [[TRAP6]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// LEGACY-NEXT: unreachable, !annotation [[META5]] +// LEGACY: [[CONT7]]: +// LEGACY-NEXT: [[AGG:%.*]] = getelementptr inbounds nuw [[STRUCT_MEMBERISAGG]], ptr [[ARRAYIDX]], i32 0, i32 0 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[AGG]], i64 8, i1 false) +// LEGACY-NEXT: [[TMP6:%.*]] = load i64, ptr [[RETVAL]], align 4 +// LEGACY-NEXT: ret i64 [[TMP6]] +// +struct Foo access_MemberIsAgg_member_bidi(struct MemberIsAgg* __indexable ptr, int idx) { + return ptr[idx].agg; +} + +// Access using ptr arithmetic has identical codegen + +// NEW-LABEL: define dso_local i64 @access_Foo_bi_ptr_arith( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP1]], align 8 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// NEW-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[TMP2]], i64 [[IDXPROM]] +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// NEW-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// NEW-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP10:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP11:%.*]] = icmp ule ptr [[TMP10]], [[WIDE_PTR_UB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP11]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP12:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP10]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP12]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP13]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[WIDE_PTR_PTR]], i64 8, i1 false) +// NEW-NEXT: [[TMP14:%.*]] = load i64, ptr [[RETVAL]], align 4 +// NEW-NEXT: ret i64 [[TMP14]] +// +// LEGACY-LABEL: define dso_local i64 @access_Foo_bi_ptr_arith( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP1]], align 8 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[TMP2]], i64 [[IDXPROM]] +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// LEGACY-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// LEGACY-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// LEGACY-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// LEGACY-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP10:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], !annotation [[META3]] +// LEGACY-NEXT: br i1 [[TMP10]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF4]], !annotation [[META3]] +// LEGACY: [[TRAP]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// LEGACY-NEXT: unreachable, !annotation [[META3]] +// LEGACY: [[CONT]]: +// LEGACY-NEXT: [[TMP11:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], !annotation [[META5]] +// LEGACY-NEXT: br i1 [[TMP11]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF4]], !annotation [[META5]] +// LEGACY: [[TRAP1]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// LEGACY-NEXT: unreachable, !annotation [[META5]] +// LEGACY: [[CONT2]]: +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[RETVAL]], ptr align 4 [[WIDE_PTR_PTR]], i64 8, i1 false) +// LEGACY-NEXT: [[TMP12:%.*]] = load i64, ptr [[RETVAL]], align 4 +// LEGACY-NEXT: ret i64 [[TMP12]] +// +struct Foo access_Foo_bi_ptr_arith(struct Foo* __bidi_indexable ptr, int idx) { + return *(ptr + idx); +} + +// NEW-LABEL: define dso_local void @compute_addr_with_subscript_Foo_bi( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[F:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP1]], align 8 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// NEW-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[TMP2]], i64 [[IDXPROM]] +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[F]], i32 0, i32 0 +// NEW-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[F]], i32 0, i32 1 +// NEW-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// NEW-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// NEW-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[F]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// NEW-NEXT: ret void +// +// LEGACY-LABEL: define dso_local void @compute_addr_with_subscript_Foo_bi( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[F:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP1]], align 8 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[TMP2]], i64 [[IDXPROM]] +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[F]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// LEGACY-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[F]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// LEGACY-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// LEGACY-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// LEGACY-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[F]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// LEGACY-NEXT: ret void +// +void compute_addr_with_subscript_Foo_bi(struct Foo* __bidi_indexable ptr, int idx) { + struct Foo* f = &ptr[idx]; +} + +// NEW-LABEL: define dso_local void @compute_addr_with_ptr_arith_Foo_bi( +// NEW-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[F:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP1]], align 8 +// NEW-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// NEW-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[TMP2]], i64 [[IDXPROM]] +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// NEW-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// NEW-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NEW-NEXT: [[TMP10:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 1, !annotation [[META2]] +// NEW-NEXT: [[TMP11:%.*]] = icmp ule ptr [[TMP10]], [[WIDE_PTR_UB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP11]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[TMP12:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP10]], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP12]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP1]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT2]]: +// NEW-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP13]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// NEW: [[TRAP3]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// NEW-NEXT: unreachable, !annotation [[META4]] +// NEW: [[CONT4]]: +// NEW-NEXT: [[TMP14:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 1 +// NEW-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[F]], i32 0, i32 0 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP15]], align 8 +// NEW-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[F]], i32 0, i32 1 +// NEW-NEXT: store ptr [[TMP14]], ptr [[TMP16]], align 8 +// NEW-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[F]], i32 0, i32 2 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP17]], align 8 +// NEW-NEXT: ret void +// +// LEGACY-LABEL: define dso_local void @compute_addr_with_ptr_arith_Foo_bi( +// LEGACY-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[F:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP1]], align 8 +// LEGACY-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[TMP2]], i64 [[IDXPROM]] +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// LEGACY-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// LEGACY-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// LEGACY-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// LEGACY-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: [[TMP10:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], !annotation [[META3]] +// LEGACY-NEXT: br i1 [[TMP10]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF4]], !annotation [[META3]] +// LEGACY: [[TRAP]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// LEGACY-NEXT: unreachable, !annotation [[META3]] +// LEGACY: [[CONT]]: +// LEGACY-NEXT: [[TMP11:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], !annotation [[META5]] +// LEGACY-NEXT: br i1 [[TMP11]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF4]], !annotation [[META5]] +// LEGACY: [[TRAP1]]: +// LEGACY-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// LEGACY-NEXT: unreachable, !annotation [[META5]] +// LEGACY: [[CONT2]]: +// LEGACY-NEXT: [[TMP12:%.*]] = getelementptr [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i64 1 +// LEGACY-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[F]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP13]], align 8 +// LEGACY-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[F]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[TMP12]], ptr [[TMP14]], align 8 +// LEGACY-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[F]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP15]], align 8 +// LEGACY-NEXT: ret void +// +void compute_addr_with_ptr_arith_Foo_bi(struct Foo* __bidi_indexable ptr, int idx) { + struct Foo* f = &*(ptr + idx); +} + + +//. +// NEW: [[META2]] = !{!"bounds-safety-check-ptr-le-upper-bound"} +// NEW: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// NEW: [[META4]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// NEW: [[META5]] = !{!"bounds-safety-check-ptr-neq-null"} +// NEW: [[META6]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +//. +// LEGACY: [[META2]] = !{!"bounds-safety-check-ptr-neq-null"} +// LEGACY: [[META3]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// LEGACY: [[PROF4]] = !{!"branch_weights", i32 1048575, i32 1} +// LEGACY: [[META5]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/array-on-stack.c b/clang/test/BoundsSafety/CodeGen/auto-init/array-on-stack.c new file mode 100644 index 0000000000000..4f0483edbefec --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/array-on-stack.c @@ -0,0 +1,14 @@ + + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +int main(int argc, char **argv) { + int *__single foo__single[3]; +// CHECK: [[TMP0:%.*]] = alloca [3 x ptr] +// CHECK: call void @llvm.memset.{{.*}}({{.*}} [[TMP0]], {{.*}} 0, {{.*}} 24{{.*}}){{.*}} !annotation ![[ANNOT_ZERO_INIT:[0-9]+]] +// ... +// CHECK: ![[ANNOT_ZERO_INIT]] = !{!"bounds-safety-zero-init"} +} diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/dependent-count-auto-var.c b/clang/test/BoundsSafety/CodeGen/auto-init/dependent-count-auto-var.c new file mode 100644 index 0000000000000..e665a3806eec6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/dependent-count-auto-var.c @@ -0,0 +1,15 @@ + + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +int main(int argc, char **argv) { + int len; + int *__single __counted_by(len) foo__single__counted_by; + +// CHECK: store i32 0, ptr {{%.*}}, align 4, !annotation ![[ANNOT_ZEROINIT:[0-9]+]] +// ... +// CHECK: ![[ANNOT_ZEROINIT]] = !{!"bounds-safety-zero-init"} +} diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/dependent-count-field.c b/clang/test/BoundsSafety/CodeGen/auto-init/dependent-count-field.c new file mode 100644 index 0000000000000..d6f78f58bc38c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/dependent-count-field.c @@ -0,0 +1,21 @@ + + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +struct Foo { + int len; + int *__single __counted_by(len) foo__single__counted_by; +}; + +int main(int argc, char **argv) { + struct Foo f; +// CHECK: [[LEN:%.*]] = getelementptr inbounds nuw %struct.Foo, ptr [[STRUCT_F:%.*]], i32 0, i32 0 +// CHECK: store i32 0, ptr [[LEN]], align 8, !annotation ![[ANNOT_ZEROINIT:[0-9]+]] +// CHECK: [[FOO_SINGLE_COUNTED_BY:%.*]] = getelementptr inbounds nuw %struct.Foo, ptr [[STRUCT_F]], i32 0, i32 1 +// CHECK: store ptr null, ptr [[FOO_SINGLE_COUNTED_BY]], align 8, !annotation ![[ANNOT_ZEROINIT]] +// ... +// CHECK: ![[ANNOT_ZEROINIT]] = !{!"bounds-safety-zero-init"} +} diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/global-bidi-indexable-init-scalar.c b/clang/test/BoundsSafety/CodeGen/auto-init/global-bidi-indexable-init-scalar.c new file mode 100644 index 0000000000000..f3b6217840839 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/global-bidi-indexable-init-scalar.c @@ -0,0 +1,14 @@ + +// RUN: %clang_cc1 -S -fbounds-safety -triple arm64-apple-darwin -O0 %s -o - | FileCheck %s +// RUN: %clang_cc1 -S -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm64-apple-darwin -O0 %s -o - | FileCheck %s + +#include + +char a; +int *__bidi_indexable ptr = &a; +// CHECK: .globl _ptr {{.*}} @ptr +// ... +// CHECK: _ptr: +// CHECK-NEXT: .quad _a +// CHECK-NEXT: .quad _a+1 +// CHECK-NEXT: .quad _a diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/global-bidi-indexable.c b/clang/test/BoundsSafety/CodeGen/auto-init/global-bidi-indexable.c new file mode 100644 index 0000000000000..ad1e8c91011a6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/global-bidi-indexable.c @@ -0,0 +1,41 @@ + +// RUN: %clang_cc1 -S -fbounds-safety -O0 -triple arm64-apple-darwin %s -o - | FileCheck %s +// RUN: %clang_cc1 -S -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +int array1[1]; +int array10[10]; + +int *__bidi_indexable bidi_ptr10 = array10; +// CHECK: .globl _bidi_ptr10 {{.*}} @bidi_ptr10 +// ... +// CHECK:_bidi_ptr10: +// CHECK-NEXT: .quad _array10 +// CHECK-NEXT: .quad _array10+40 +// CHECK-NEXT: .quad _array10 + +int *__bidi_indexable bidi_ptr1 = array1; +// CHECK: .globl _bidi_ptr1 {{.*}} @bidi_ptr1 +// ... +// CHECK:_bidi_ptr1: +// CHECK-NEXT: .quad _array1 +// CHECK-NEXT: .quad _array1 +// CHECK-NEXT: .quad _array1 + +int *__bidi_indexable bidi_ptr10_offset3 = array10 + 3; +// CHECK: .globl _bidi_ptr10_offset3 {{.*}} @bidi_ptr10_offset3 +// ... +// CHECK: _bidi_ptr10_offset3: +// CHECK-NEXT: .quad _array10+12 +// CHECK-NEXT: .quad _array10+40 +// CHECK-NEXT: .quad _array10 + +int array_pp11_7[11][7]; +int *__bidi_indexable bidi_ptrpp11_7_offset3 = array_pp11_7[3]; +// CHECK: .globl _bidi_ptrpp11_7_offset3 {{.*}} @bidi_ptrpp11_7_offset3 +// ... +// CHECK: _bidi_ptrpp11_7_offset3: +// CHECK-NEXT: .quad _array_pp11_7+84 +// CHECK-NEXT: .quad _array_pp11_7+308 +// CHECK-NEXT: .quad _array_pp11_7 diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/global-wide-ptr-init-out-of-range.c b/clang/test/BoundsSafety/CodeGen/auto-init/global-wide-ptr-init-out-of-range.c new file mode 100644 index 0000000000000..c6cd37512c9d4 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/global-wide-ptr-init-out-of-range.c @@ -0,0 +1,14 @@ + +// RUN: %clang_cc1 -S -fbounds-safety -O0 -triple arm64-apple-darwin %s -o - | FileCheck %s +// RUN: %clang_cc1 -S -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +int array10[10]; + +int *__attribute__((bidi_indexable)) bidi0 = array10 + 10; + +// CHECK: _bidi0: +// CHECK: .quad _array10+40 +// CHECK: .quad _array10+40 +// CHECK: .quad _array10 diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/global-wide-ptr-init-zero-len-array.c b/clang/test/BoundsSafety/CodeGen/auto-init/global-wide-ptr-init-zero-len-array.c new file mode 100644 index 0000000000000..7310e621af109 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/global-wide-ptr-init-zero-len-array.c @@ -0,0 +1,13 @@ + +// RUN: %clang_cc1 -S -fbounds-safety -O0 -triple arm64-apple-darwin %s -o - | FileCheck %s +// RUN: %clang_cc1 -S -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +int array0[0]; +int *__attribute__((bidi_indexable)) bidi_ptr0 = array0; + +// CHECK: _bidi_ptr0: +// CHECK: .quad _array0 +// CHECK: .quad _array0 +// CHECK: .quad _array0 diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/ptr-global.c b/clang/test/BoundsSafety/CodeGen/auto-init/ptr-global.c new file mode 100644 index 0000000000000..08487b737364e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/ptr-global.c @@ -0,0 +1,16 @@ + + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +int *__single foo__single; +int *__indexable foo__indexable; +int *__bidi_indexable foo__bidi_indexable; + +// CHECK: @foo__single = global ptr null +// CHECK: @foo__indexable = global %"__bounds_safety::wide_ptr.indexable" zeroinitializer +// CHECK: @foo__bidi_indexable = global %"__bounds_safety::wide_ptr.bidi_indexable" zeroinitializer + +int main(int argc, char **argv) { } diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/ptr-on-stack.c b/clang/test/BoundsSafety/CodeGen/auto-init/ptr-on-stack.c new file mode 100644 index 0000000000000..aa9c5d78ebd4e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/ptr-on-stack.c @@ -0,0 +1,33 @@ + + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +void single(void) { + // CHECK: [[TMP0:%.*]] = alloca ptr, align 8 + // CHECK: store ptr null, ptr [[TMP0:%.*]], align 8, !annotation ![[ANNOT_ZERO_INIT:[0-9]+]] + int *__single foo__single; +} + +void indexable(void) { + // CHECK: [[TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 + // CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[TMP1]], i8 0, i64 16, i1 false), !annotation ![[ANNOT_ZERO_INIT]] + int *__indexable foo__indexable; +} + +void bidi_indexable(void) { + // CHECK: [[TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 + // CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[TMP2]], i8 0, i64 24, i1 false), !annotation ![[ANNOT_ZERO_INIT]] + int *__bidi_indexable foo__bidi_indexable; +} + +void counted_by(void) { + // CHECK: [[TMP3:%.*]] = alloca ptr, align 8 + // CHECK: store ptr null, ptr [[TMP3:%.*]], align 8, !annotation ![[ANNOT_ZERO_INIT]] + int len; + int *__single __counted_by(len) foo__single__counted_by; +} + +// CHECK: ![[ANNOT_ZERO_INIT]] = !{!"bounds-safety-zero-init"} diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/struct-on-stack.c b/clang/test/BoundsSafety/CodeGen/auto-init/struct-on-stack.c new file mode 100644 index 0000000000000..12b82321ed317 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/struct-on-stack.c @@ -0,0 +1,29 @@ + + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +struct Foo { + int *__single foo__single; + int *__indexable foo__indexable; + int *__bidi_indexable foo__bidi_indexable; + int len; + int *__single __counted_by(len) foo__single__counted_by; +}; + +int main(int argc, char **argv) { + struct Foo f; +// CHECK: [[FOO_SINGLE:%.*]] = getelementptr inbounds nuw %struct.Foo, ptr [[FOO_F:%.*]], i32 0, i32 0 +// CHECK: store ptr null, ptr [[FOO_SINGLE]]{{.*}} !annotation ![[ANNOT_ZERO_INIT:[0-9]+]] +// CHECK: [[FOO_INDEXABLE:%.*]] = getelementptr inbounds nuw %struct.Foo, ptr [[FOO_F]], i32 0, i32 1 +// CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[FOO_INDEXABLE]], i8 0, i64 16, i1 false){{.*}} !annotation ![[ANNOT_ZERO_INIT:[0-9]+]] +// CHECK: [[FOO_BIDI_INDEX:%.*]] = getelementptr inbounds nuw %struct.Foo, ptr [[FOO_F]], i32 0, i32 2 +// CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[FOO_BIDI_INDEX]], i8 0, i64 24, i1 false){{.*}} !annotation ![[ANNOT_ZERO_INIT]] +// CHECK: getelementptr inbounds nuw %struct.Foo, ptr [[FOO_F]], i32 0, i32 3 +// CHECK: [[FOO_SINGLE_COUNTED_BY:%.*]] = getelementptr inbounds nuw %struct.Foo, ptr [[FOO_F]], i32 0, i32 4 +// CHECK: store ptr null, ptr [[FOO_SINGLE_COUNTED_BY]], align 8{{.*}} !annotation ![[ANNOT_ZERO_INIT]] +// ... +// CHECK: ![[ANNOT_ZERO_INIT]] = !{!"bounds-safety-zero-init"} +} diff --git a/clang/test/BoundsSafety/CodeGen/auto-init/union-on-stack.c b/clang/test/BoundsSafety/CodeGen/auto-init/union-on-stack.c new file mode 100644 index 0000000000000..651773ccfdb6e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/auto-init/union-on-stack.c @@ -0,0 +1,47 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -O0 -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple arm64 %s -o - | FileCheck %s + +#include + +union Foo1 { + int *__single foo__single; + int b; +}; + +union Foo2 { + int a; + int *__indexable foo__indexable; + int b; +}; + +union Foo3 { + int *__single a; + int *__single b; + int *__single c; +}; + +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARGC_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARGV_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[F1:%.*]] = alloca [[UNION_FOO1:%.*]], align 8 +// CHECK-NEXT: [[F2:%.*]] = alloca [[UNION_FOO2:%.*]], align 8 +// CHECK-NEXT: [[F3:%.*]] = alloca [[UNION_FOO3:%.*]], align 8 +// CHECK-NEXT: store i32 [[ARGC:%.*]], ptr [[ARGC_ADDR]], align 4 +// CHECK-NEXT: store ptr [[ARGV:%.*]], ptr [[ARGV_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[F1]], i8 0, i64 8, i1 false), !annotation ![[ANNOT_BSS_ZEROINIT:[0-9]+]] +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[F2]], i8 0, i64 16, i1 false), !annotation ![[ANNOT_BSS_ZEROINIT]] +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[F3]], i8 0, i64 8, i1 false), !annotation ![[ANNOT_BSS_ZEROINIT]] +// CHECK-NEXT: ret i32 0 +// +int main(int argc, char **argv) { + union Foo1 f1; + union Foo2 f2; + union Foo3 f3; + +// ... +// CHECK: ![[ANNOT_BSS_ZEROINIT]] = !{!"bounds-safety-zero-init"} +} diff --git a/clang/test/BoundsSafety/CodeGen/bidi-indexable-argument-abi.c b/clang/test/BoundsSafety/CodeGen/bidi-indexable-argument-abi.c new file mode 100644 index 0000000000000..90c4f2cb85748 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bidi-indexable-argument-abi.c @@ -0,0 +1,40 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 +// RUN: %clang_cc1 -triple aarch64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=AARCH64 +// RUN: %clang_cc1 -triple i686 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=I686 +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 +// RUN: %clang_cc1 -triple aarch64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=AARCH64 +// RUN: %clang_cc1 -triple i686 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=I686 + +#include + +// X86_64-LABEL: @foo( +// X86_64-NEXT: entry: +// X86_64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT:%.*]], ptr align 8 [[PTRARG:%.*]], i64 24, i1 false) +// X86_64-NEXT: ret void +// +// AARCH64-LABEL: @foo( +// AARCH64-NEXT: entry: +// AARCH64-NEXT: [[PTRARG_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// AARCH64-NEXT: store ptr [[PTRARG:%.*]], ptr [[PTRARG_INDIRECT_ADDR]], align 8 +// AARCH64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT:%.*]], ptr align 8 [[PTRARG]], i64 24, i1 false) +// AARCH64-NEXT: ret void +// +// I686-LABEL: @foo( +// I686-NEXT: entry: +// I686-NEXT: [[RESULT_PTR:%.*]] = alloca ptr, align 4 +// I686-NEXT: store ptr [[AGG_RESULT:%.*]], ptr [[RESULT_PTR]], align 4 +// I686-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AGG_RESULT]], ptr align 4 [[PTRARG:%.*]], i32 12, i1 false) +// I686-NEXT: ret void +// +int*__bidi_indexable foo(int *__bidi_indexable ptrArg) { + return ptrArg; +} + + + + + + diff --git a/clang/test/BoundsSafety/CodeGen/bitcast-wide-ptr-ignored-dest.c b/clang/test/BoundsSafety/CodeGen/bitcast-wide-ptr-ignored-dest.c new file mode 100644 index 0000000000000..6161e0e6e0508 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bitcast-wide-ptr-ignored-dest.c @@ -0,0 +1,21 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +// + +void *bar(void); + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[A]], i8 0, i64 24, i1 false) +// CHECK-NEXT: [[CALL:%.*]] = call ptr @bar() +// CHECK-NEXT: ret void +// +void foo(void) { + int * a = 0; + (unsigned*) bar(); +} diff --git a/clang/test/BoundsSafety/CodeGen/bound-compare.c b/clang/test/BoundsSafety/CodeGen/bound-compare.c new file mode 100644 index 0000000000000..c21e6221b8d4d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bound-compare.c @@ -0,0 +1,59 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64-apple-darwin -emit-llvm %s -o - | FileCheck %s --check-prefix=NOPT +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-darwin -emit-llvm %s -o - | FileCheck %s --check-prefix=OPT +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefix=NOPT +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefix=OPT +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm64-apple-darwin -emit-llvm %s -o - | FileCheck %s --check-prefix=NOPT +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm64-apple-darwin -emit-llvm %s -o - | FileCheck %s --check-prefix=OPT +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefix=NOPT +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s --check-prefix=OPT + + +int main() { + int arr[10]; + int *a = arr; + if (a == 0) + return 0; + return 1; +} + +// NOPT-LABEL: @main( +// NOPT-NEXT: entry: +// NOPT-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// NOPT-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// NOPT-NEXT: [[A:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NOPT-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NOPT-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// NOPT-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// NOPT-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// NOPT-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[A]], i32 0, i32 0 +// NOPT-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// NOPT-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[A]], i32 0, i32 1 +// NOPT-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// NOPT-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[A]], i32 0, i32 2 +// NOPT-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// NOPT-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[A]], i64 24, i1 false) +// NOPT-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPT-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NOPT-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPT-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NOPT-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NOPT-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NOPT-NEXT: [[CMP:%.*]] = icmp eq ptr [[WIDE_PTR_PTR]], null +// NOPT-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// NOPT: if.then: +// NOPT-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// NOPT-NEXT: br label [[RETURN:%.*]] +// NOPT: if.end: +// NOPT-NEXT: store i32 1, ptr [[RETVAL]], align 4 +// NOPT-NEXT: br label [[RETURN]] +// NOPT: return: +// NOPT-NEXT: [[TMP3:%.*]] = load i32, ptr [[RETVAL]], align 4 +// NOPT-NEXT: ret i32 [[TMP3]] + +// OPT-LABEL: @main( +// OPT-NEXT: entry: +// OPT-NEXT: ret i32 1 +// diff --git a/clang/test/BoundsSafety/CodeGen/bound-to-vaarg.c b/clang/test/BoundsSafety/CodeGen/bound-to-vaarg.c new file mode 100644 index 0000000000000..9972bb28d9871 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bound-to-vaarg.c @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -O0 -fbounds-safety -triple x86_64-linux-gnu %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64-linux-gnu %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64-linux-gnu %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64-linux-gnu %s -o /dev/null + +#include + +int foo_vaarg(int num, ...) { + __builtin_va_list ap; + __builtin_va_start(ap, num); + + int *__unsafe_indexable ptr = __builtin_va_arg(ap, int*); + return *ptr; +} + +int main() { + int a = 0; + int *ptr = &a; + foo_vaarg(1, ptr); + + int len; + int *__counted_by(len) countPtr; + foo_vaarg(1, countPtr); + + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header-O0.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header-O0.c new file mode 100644 index 0000000000000..3520441f448e4 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header-O0.c @@ -0,0 +1,106 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck --check-prefix=LEGACY %s +#include "bounds-attributed-in-return-null-system-header.h" + +// FIXME: The IR for `inline_header_func_unspecified_ptr` and +// `inline_header_func_unsafe_indexable_ptr` are not included by +// `utils/update_cc_test_checks.py` but that's the IR we **actually** want to +// match. There doesn't seem to be a way to persuade the script to generate +// CHECK lines for it. + +void consume(int* __bidi_indexable); + +// CHECK-LABEL: define dso_local void @use_inline_header_func_unspecified_ptr( +// CHECK-SAME: i32 noundef [[COUNT:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @inline_header_func_unspecified_ptr(i32 noundef [[TMP0]]) +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @consume(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +// LEGACY-LABEL: define dso_local void @use_inline_header_func_unspecified_ptr( +// LEGACY-SAME: i32 noundef [[COUNT:%.*]]) #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[CALL:%.*]] = call ptr @inline_header_func_unspecified_ptr(i32 noundef [[TMP0]]) +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: call void @consume(ptr noundef [[BYVAL_TEMP]]) +// LEGACY-NEXT: ret void +// +void use_inline_header_func_unspecified_ptr(int count) { + int* ptr = inline_header_func_unspecified_ptr(count); + consume(ptr); +} + +// CHECK-LABEL: define dso_local void @use_inline_header_func_unsafe_indexable_ptr( +// CHECK-SAME: i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @inline_header_func_unsafe_indexable_ptr(i32 noundef [[TMP0]]) +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @consume(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +// LEGACY-LABEL: define dso_local void @use_inline_header_func_unsafe_indexable_ptr( +// LEGACY-SAME: i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// LEGACY-NEXT: [[CALL:%.*]] = call ptr @inline_header_func_unsafe_indexable_ptr(i32 noundef [[TMP0]]) +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: call void @consume(ptr noundef [[BYVAL_TEMP]]) +// LEGACY-NEXT: ret void +// +void use_inline_header_func_unsafe_indexable_ptr(int count) { + int* ptr = inline_header_func_unsafe_indexable_ptr(count); + consume(ptr); +} diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header-O2.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header-O2.c new file mode 100644 index 0000000000000..097275445cf3a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header-O2.c @@ -0,0 +1,72 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck --check-prefix=LEGACY %s +#include "bounds-attributed-in-return-null-system-header.h" + +void consume(int* __bidi_indexable); + +// CHECK-LABEL: define dso_local void @use_inline_header_func_unspecified_ptr( +// CHECK-SAME: i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[CMP_I:%.*]] = icmp eq i32 [[COUNT]], 0, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_I]], label %[[INLINE_HEADER_FUNC_UNSPECIFIED_PTR_EXIT:.*]], label %[[TRAP_I:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP_I]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[INLINE_HEADER_FUNC_UNSPECIFIED_PTR_EXIT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[BYVAL_TEMP]], i8 0, i64 24, i1 false) +// CHECK-NEXT: call void @consume(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +// LEGACY-LABEL: define dso_local void @use_inline_header_func_unspecified_ptr( +// LEGACY-SAME: i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR4:[0-9]+]] +// LEGACY-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[BYVAL_TEMP]], i8 0, i64 24, i1 false) +// LEGACY-NEXT: call void @consume(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR4]] +// LEGACY-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR4]] +// LEGACY-NEXT: ret void +// +void use_inline_header_func_unspecified_ptr(int count) { + int* ptr = inline_header_func_unspecified_ptr(count); + consume(ptr); +} + +// CHECK-LABEL: define dso_local void @use_inline_header_func_unsafe_indexable_ptr( +// CHECK-SAME: i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[CMP_I:%.*]] = icmp eq i32 [[COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_I]], label %[[INLINE_HEADER_FUNC_UNSAFE_INDEXABLE_PTR_EXIT:.*]], label %[[TRAP_I:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP_I]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[INLINE_HEADER_FUNC_UNSAFE_INDEXABLE_PTR_EXIT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[BYVAL_TEMP]], i8 0, i64 24, i1 false) +// CHECK-NEXT: call void @consume(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +// LEGACY-LABEL: define dso_local void @use_inline_header_func_unsafe_indexable_ptr( +// LEGACY-SAME: i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR4]] +// LEGACY-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[BYVAL_TEMP]], i8 0, i64 24, i1 false) +// LEGACY-NEXT: call void @consume(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR4]] +// LEGACY-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR4]] +// LEGACY-NEXT: ret void +// +void use_inline_header_func_unsafe_indexable_ptr(int count) { + int* ptr = inline_header_func_unsafe_indexable_ptr(count); + consume(ptr); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +//. diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header.h b/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header.h new file mode 100644 index 0000000000000..de7a0ebee5da9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-in-return-null-system-header.h @@ -0,0 +1,19 @@ +// Pretend this is a system header +#pragma clang system_header +#include + + +inline int* __counted_by(count) inline_header_func_unspecified_ptr(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. This is a case + // where an implicit cast to __bidi_indexable is needed. + return (int*)0; +} + +inline int* __counted_by(count) inline_header_func_unsafe_indexable_ptr(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. This is a case + // where an implicit cast to __bidi_indexable is needed. + return (int* __unsafe_indexable)0; +} + diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-O0.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-O0.c new file mode 100644 index 0000000000000..bec3d64a933a9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-O0.c @@ -0,0 +1,654 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: define dso_local ptr @cb_in_from_bidi( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR33]] +// +int *__counted_by(count) cb_in_from_bidi(int count, int *__bidi_indexable p) { + return p; +} + +// + +int *__counted_by(*count) cb_in_from_bidi_redecl(int *count, int *__bidi_indexable p); +// CHECK-LABEL: define dso_local ptr @cb_in_from_bidi_redecl( +// CHECK-SAME: ptr noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4, !annotation [[META2]] +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP0]], align 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR33]] +// +int *__counted_by(*count) cb_in_from_bidi_redecl(int *count, int *__bidi_indexable p) { + return p; +} + +// FIXME: This had to be patched manually. `utils/update_cc_test_checks.py` seems +// to generate CHECK lines here for something that's not a function body and so it +// tries to match the IR for the same function twice. +int *__counted_by(*count) cb_in_from_bidi_typeof(int *count, int *__bidi_indexable p); +__typeof__(cb_in_from_bidi_typeof) cb_in_from_bidi_typeof; +extern __attribute__((weak_import)) __typeof__(cb_in_from_bidi_typeof) cb_in_from_bidi_typeof; + +// CHECK-LABEL: define dso_local ptr @cb_in_from_bidi_typeof( +// CHECK-SAME: ptr noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4, !annotation [[META2]] +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP0]], align 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR33]] +// +int *__counted_by(*count) cb_in_from_bidi_typeof(int *count, int *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @cb_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END13:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[UPPER2:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER2]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP3:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP3]], label %[[LAND_RHS:.*]], label %[[LAND_END13]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP8:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP8]], label %[[LAND_RHS10:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS10]]: +// CHECK-NEXT: [[CMP11:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP11]], %[[LAND_RHS10]] ] +// CHECK-NEXT: br label %[[LAND_END13]], !annotation [[META2]] +// CHECK: [[LAND_END13]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP11]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by(count) cb_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @cb_out_from_single( +// CHECK-SAME: ptr noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END13:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[UPPER2:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER2]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP3:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP3]], label %[[LAND_RHS:.*]], label %[[LAND_END13]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP1]], align 4, !annotation [[META2]] +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP8]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP8:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP8]], label %[[LAND_RHS10:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS10]]: +// CHECK-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP1]], align 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP11:%.*]] = icmp sle i32 0, [[TMP12]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP11]], %[[LAND_RHS10]] ] +// CHECK-NEXT: br label %[[LAND_END13]], !annotation [[META2]] +// CHECK: [[LAND_END13]]: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP13]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by(*count) cb_out_from_single(int *count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @cbn_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END13:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[UPPER2:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER2]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP3:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP3]], label %[[LAND_RHS:.*]], label %[[LAND_END13]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP8:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP8]], label %[[LAND_RHS10:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS10]]: +// CHECK-NEXT: [[CMP11:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP11]], %[[LAND_RHS10]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END13]], !annotation [[META2]] +// CHECK: [[LAND_END13]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by_or_null(count) cbn_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @sb_in_from_bidi( +// CHECK-SAME: i32 noundef [[SIZE:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store i32 [[SIZE]], ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR21]], ptr [[TMP1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB23]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB25]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP4]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR40]] +// +void *__sized_by(size) sb_in_from_bidi(int size, void *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @eb_from_bidi( +// CHECK-SAME: ptr noundef [[END:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR18]] +// +void *__ended_by(end) eb_from_bidi(void *end, void *__bidi_indexable p) { + return p; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +//. diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-O2.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-O2.c new file mode 100644 index 0000000000000..b26e65877f07a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-O2.c @@ -0,0 +1,316 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + + + +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: define dso_local ptr @cb_in_from_bidi( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] +// +int *__counted_by(count) cb_in_from_bidi(int count, int *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_indexable( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], [2 x i64] noundef [[P_COERCE:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_COERCE_FCA_0_EXTRACT:%.*]] = extractvalue [2 x i64] [[P_COERCE]], 0 +// CHECK-NEXT: [[P_COERCE_FCA_1_EXTRACT:%.*]] = extractvalue [2 x i64] [[P_COERCE]], 1 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt i64 [[P_COERCE_FCA_0_EXTRACT]], [[P_COERCE_FCA_1_EXTRACT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub nuw i64 [[P_COERCE_FCA_1_EXTRACT]], [[P_COERCE_FCA_0_EXTRACT]], !annotation [[META8]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP34:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP37:%.*]] = icmp sgt i32 [[COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP37]], [[CMP34]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP0:%.*]] = inttoptr i64 [[P_COERCE_FCA_0_EXTRACT]] to ptr +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by(count) cb_in_from_indexable(int count, int *__indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readnone returned captures(ret: address, provenance) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = icmp ult i32 [[COUNT]], 2 +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(count) cb_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_cb( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readnone returned captures(ret: address, provenance) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(count) cb_in_from_cb(int count, int *__counted_by(count) p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_cb2( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readnone returned captures(ret: address, provenance) [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[LEN]] to i64 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[LEN]], 0, !annotation [[META2]] +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT_NOT:%.*]] = icmp ugt i64 [[CONV]], [[IDX_EXT]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[SPEC_SELECT_NOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof [[PROF11:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(count) cb_in_from_cb2(int count, int *__counted_by(len) p, int len) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_cbn( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef returned [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[P]], null, !annotation [[META12:![0-9]+]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[COUNT]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP_SROA_9_0:%.*]] = select i1 [[DOTNOT]], ptr null, ptr [[ADD_PTR]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[P]], [[TMP_SROA_9_0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[TMP_SROA_9_0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[P]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[IDX_EXT]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(count) cb_in_from_cbn(int count, int *__counted_by_or_null(count) p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_cbn2( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef returned [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[P]], null, !annotation [[META12]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[LEN]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP_SROA_9_0:%.*]] = select i1 [[DOTNOT]], ptr null, ptr [[ADD_PTR]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[P]], [[TMP_SROA_9_0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[TMP_SROA_9_0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[P]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(count) cb_in_from_cbn2(int count, int *__counted_by_or_null(len) p, int len) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_out_from_single( +// CHECK-SAME: ptr noundef readonly captures(none) [[COUNT:%.*]], ptr noundef readnone returned captures(ret: address, provenance) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT]], align 4, !tbaa [[TBAA13:![0-9]+]] +// CHECK-NEXT: [[OR_COND:%.*]] = icmp ult i32 [[TMP0]], 2, !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF15:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(*count) cb_out_from_single(int *__single count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cbn_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readnone returned captures(address_is_null, ret: address, provenance) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[P]], null, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = icmp ult i32 [[COUNT]], 2 +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[SPEC_SELECT]], [[TOBOOL_NOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF16:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by_or_null(count) cbn_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @sb_in_from_bidi( +// CHECK-SAME: i32 noundef [[SIZE:%.*]], ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA17:![0-9]+]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[SIZE]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[SIZE]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] +// +void *__sized_by(size) sb_in_from_bidi(int size, void *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @sb_in_from_single( +// CHECK-SAME: i32 noundef [[SIZE:%.*]], ptr noundef readnone returned captures(ret: address, provenance) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = icmp ult i32 [[SIZE]], 5 +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +void *__sized_by(size) sb_in_from_single(int size, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @eb_in_from_bidi( +// CHECK-SAME: ptr noundef readnone captures(address) [[END:%.*]], ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8, !tbaa [[TBAA17]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF18:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] +// +void *__ended_by(end) eb_in_from_bidi(void *__single end, void *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @eb_in_from_single( +// CHECK-SAME: ptr noundef readnone captures(address) [[END:%.*]], ptr noundef readnone returned captures(address, ret: address, provenance) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[END]], [[UPPER]], !annotation [[META2]] +// CHECK-NEXT: [[CMP1:%.*]] = icmp ule ptr [[P]], [[END]], !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP1]], [[CMP]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr [[P]] +// +int *__ended_by(end) eb_in_from_single(int *__single end, int *__single p) { + return p; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"p1 int", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"any pointer", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"omnipotent char", [[META7:![0-9]+]], i64 0} +// CHECK: [[META7]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META8]] = !{!"bounds-safety-generic", [[META9:![0-9]+]]} +// CHECK: [[META9]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[PROF10]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[PROF11]] = !{!"branch_weights", i32 1048577, i32 1048575} +// CHECK: [[META12]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[TBAA13]] = !{[[META14:![0-9]+]], [[META14]], i64 0} +// CHECK: [[META14]] = !{!"int", [[META6]], i64 0} +// CHECK: [[PROF15]] = !{!"branch_weights", i32 1048575, i32 1048577} +// CHECK: [[PROF16]] = !{!"branch_weights", i32 2097151, i32 1} +// CHECK: [[TBAA17]] = !{[[META5]], [[META5]], i64 0} +// CHECK: [[PROF18]] = !{!"branch_weights", i32 1, i32 1048575} +//. diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-disabled-O0.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-disabled-O0.c new file mode 100644 index 0000000000000..86f8d347615c0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-disabled-O0.c @@ -0,0 +1,112 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + + + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: define dso_local ptr @cb_in_from_bidi( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int *__counted_by(count) cb_in_from_bidi(int count, int *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @cb_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by(count) cb_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @cb_out_from_single( +// CHECK-SAME: ptr noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[COUNT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by(*count) cb_out_from_single(int *count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @cbn_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by_or_null(count) cbn_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @sb_in_from_bidi( +// CHECK-SAME: i32 noundef [[SIZE:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store i32 [[SIZE]], ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +void *__sized_by(size) sb_in_from_bidi(int size, void *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @eb_from_bidi( +// CHECK-SAME: ptr noundef [[END:%.*]], ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +void *__ended_by(end) eb_from_bidi(void *end, void *__bidi_indexable p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-disabled-O2.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-disabled-O2.c new file mode 100644 index 0000000000000..4a020c48ec42b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-disabled-O2.c @@ -0,0 +1,64 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + + + +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: define dso_local ptr @cb_in_from_bidi( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: ret ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] +// +int *__counted_by(count) cb_in_from_bidi(int count, int *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readnone returned captures(ret: address, provenance) [[P:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(count) cb_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cb_out_from_single( +// CHECK-SAME: ptr noundef readnone captures(none) [[COUNT:%.*]], ptr noundef readnone returned captures(ret: address, provenance) [[P:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by(*count) cb_out_from_single(int *count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local noundef ptr @cbn_in_from_single( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef readnone returned captures(ret: address, provenance) [[P:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr [[P]] +// +int *__counted_by_or_null(count) cbn_in_from_single(int count, int *__single p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @sb_in_from_bidi( +// CHECK-SAME: i32 noundef [[SIZE:%.*]], ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: ret ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] +// +void *__sized_by(size) sb_in_from_bidi(int size, void *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: define dso_local ptr @eb_from_bidi( +// CHECK-SAME: ptr noundef readnone captures(none) [[END:%.*]], ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: ret ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] +// +void *__ended_by(end) eb_from_bidi(void *end, void *__bidi_indexable p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-from-bidi-preinc-O0.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-from-bidi-preinc-O0.c new file mode 100644 index 0000000000000..18ae6803a7f94 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-from-bidi-preinc-O0.c @@ -0,0 +1,125 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + + + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -emit-llvm %s -o - | FileCheck %s + +// XFAIL: !rdar137311963 + +#include + +// CHECK-LABEL: define dso_local ptr @foo( +// CHECK-SAME: i32 noundef [[COUNT:%.*]], ptr noundef [[BUF:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LOCAL:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[BUF]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[LOCAL]], i64 24, i1 false) +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[TMP5]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP6]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP12]], ptr [[TMP13]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[LOCAL]], i64 24, i1 false) +// CHECK-NEXT: [[TMP14:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END31:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP14]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS28:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS28]]: +// CHECK-NEXT: [[CMP29:%.*]] = icmp sle i32 0, [[TMP14]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP29]], %[[LAND_RHS28]] ] +// CHECK-NEXT: br label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_END31]]: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP15]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT:.*]], label %[[TRAP:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR34]] +// +int *__counted_by(count) foo(int count, int *__counted_by(count) buf) { + int *local = buf; + return ++local; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-null-O0.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-null-O0.c new file mode 100644 index 0000000000000..ab5af56b0a57e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-null-O0.c @@ -0,0 +1,298 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -emit-llvm %s -o - | FileCheck --check-prefix=LEGACY %s + +#include + +// CHECK-LABEL: define dso_local ptr @cb_0( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cb_0( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_0(int len) { + return 0; +} + +// CHECK-LABEL: define dso_local ptr @cb_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cb_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_NULL(int len) { + return (void *)0; +} + +// CHECK-LABEL: define dso_local ptr @cb_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cb_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_int_cast(int len) { + return (int *)0; +} + +// CHECK-LABEL: define dso_local ptr @cb_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cb_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_int_cast_NULL(int len) { + return (int *)(void*)0; +} + +// CHECK-LABEL: define dso_local ptr @sb_0( +// CHECK-SAME: i32 noundef [[SIZE:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[SIZE]], ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @sb_0( +// LEGACY-SAME: i32 noundef [[SIZE:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[SIZE]], ptr [[SIZE_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__sized_by(size) sb_0(int size) { + return 0; +} + +// CHECK-LABEL: define dso_local ptr @sb_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @sb_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__sized_by(len) sb_int_cast(int len) { + return (int *)0; +} + +// CHECK-LABEL: define dso_local ptr @sb_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @sb_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__sized_by(len) sb_int_cast_NULL(int len) { + return (int *)(void*)0; +} + +// CHECK-LABEL: define dso_local ptr @cbn_0( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cbn_0( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by_or_null(len) cbn_0(int len) { + return 0; +} + +// CHECK-LABEL: define dso_local ptr @cbn_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cbn_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by_or_null(len) cbn_int_cast(int len) { + return (int*)0; +} + +// CHECK-LABEL: define dso_local ptr @cbn_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @cbn_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__counted_by_or_null(len) cbn_int_cast_NULL(int len) { + return (int*)(void*)0; +} + +// CHECK-LABEL: define dso_local ptr @sbn_0( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @sbn_0( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__sized_by_or_null(len) sbn_0(int len) { + return 0; +} + +// CHECK-LABEL: define dso_local ptr @sbn_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @sbn_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__sized_by_or_null(len) sbn_int_cast(int len) { + return (int*)0; +} + +// CHECK-LABEL: define dso_local ptr @sbn_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local ptr @sbn_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// LEGACY-NEXT: ret ptr null +// +int *__sized_by_or_null(len) sbn_int_cast_NULL(int len) { + return (int*)(void*)0; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +//. diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-null-O2.c b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-null-O2.c new file mode 100644 index 0000000000000..ddde2f5fbe0ff --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributed-return-null-O2.c @@ -0,0 +1,233 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -Wno-bounds-safety-single-to-count -emit-llvm %s -o - | FileCheck --check-prefix=LEGACY %s + +#include + +// CHECK-LABEL: define dso_local noalias noundef ptr @cb_0( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LEN]], 0, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cb_0( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_0(int len) { + return 0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @cb_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LEN]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cb_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_NULL(int len) { + return (void *)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @cb_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LEN]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cb_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_int_cast(int len) { + return (int *)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @cb_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LEN]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cb_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by(len) cb_int_cast_NULL(int len) { + return (int *)(void*)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @sb_0( +// CHECK-SAME: i32 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[SIZE]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @sb_0( +// LEGACY-SAME: i32 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__sized_by(size) sb_0(int size) { + return 0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @sb_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LEN]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @sb_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__sized_by(len) sb_int_cast(int len) { + return (int *)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @sb_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[LEN]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @sb_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__sized_by(len) sb_int_cast_NULL(int len) { + return (int *)(void*)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @cbn_0( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cbn_0( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by_or_null(len) cbn_0(int len) { + return 0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @cbn_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cbn_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by_or_null(len) cbn_int_cast(int len) { + return (int*)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @cbn_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @cbn_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__counted_by_or_null(len) cbn_int_cast_NULL(int len) { + return (int*)(void*)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @sbn_0( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @sbn_0( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__sized_by_or_null(len) sbn_0(int len) { + return 0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @sbn_int_cast( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @sbn_int_cast( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__sized_by_or_null(len) sbn_int_cast(int len) { + return (int*)0; +} + +// CHECK-LABEL: define dso_local noalias noundef ptr @sbn_int_cast_NULL( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret ptr null +// +// LEGACY-LABEL: define dso_local noalias noundef ptr @sbn_int_cast_NULL( +// LEGACY-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: ret ptr null +// +int *__sized_by_or_null(len) sbn_int_cast_NULL(int len) { + return (int*)(void*)0; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +//. diff --git a/clang/test/BoundsSafety/CodeGen/bounds-attributes-feature.c b/clang/test/BoundsSafety/CodeGen/bounds-attributes-feature.c new file mode 100644 index 0000000000000..dc6f76dab03cf --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-attributes-feature.c @@ -0,0 +1,16 @@ + + +// RUN: %clang_cc1 %s -E -fbounds-safety | FileCheck %s --check-prefix=ENABLED +// RUN: %clang_cc1 %s -E -fbounds-safety | FileCheck %s --check-prefix=ENABLED +// RUN: %clang_cc1 %s -E | FileCheck %s --check-prefix=DISABLED +// RUN: %clang_cc1 %s -E -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --check-prefix=ENABLED +// RUN: %clang_cc1 %s -E -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --check-prefix=ENABLED +// RUN: %clang_cc1 %s -E -x objective-c | FileCheck %s --check-prefix=DISABLED + +#if __has_feature(bounds_attributes) +// ENABLED: has_bounds_attributes +void has_bounds_attributes() {} +#else +// DISABLED: no_bounds_attributes +void no_bounds_attributes() {} +#endif diff --git a/clang/test/BoundsSafety/CodeGen/bounds-expr.c b/clang/test/BoundsSafety/CodeGen/bounds-expr.c new file mode 100644 index 0000000000000..6feb7f46ca974 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-expr.c @@ -0,0 +1,85 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +int top_level_array[10]; + +int *__unsafe_indexable top_level_lower = __builtin_get_pointer_lower_bound(top_level_array); +int *__unsafe_indexable top_level_upper = __builtin_get_pointer_upper_bound(top_level_array); +// CHECK: @top_level_lower = global ptr @top_level_array, align 8 +// CHECK: @top_level_upper = global ptr getelementptr (i8, ptr @top_level_array, i64 40), align 8 + +int *__unsafe_indexable top_level_added_lower = __builtin_get_pointer_lower_bound(top_level_array + 5); +int *__unsafe_indexable top_level_added_upper = __builtin_get_pointer_upper_bound(top_level_array + 5); +// CHECK: @top_level_added_lower = global ptr @top_level_array, align 8 +// CHECK: @top_level_added_upper = global ptr getelementptr (i8, ptr @top_level_array, i64 40), align 8 + +int *__unsafe_indexable top_level_indexed_lower = __builtin_get_pointer_lower_bound(&top_level_array[5]); +int *__unsafe_indexable top_level_indexed_upper = __builtin_get_pointer_upper_bound(&top_level_array[5]); +// CHECK: @top_level_indexed_lower = global ptr @top_level_array, align 8 +// CHECK: @top_level_indexed_upper = global ptr getelementptr (i8, ptr @top_level_array, i64 40), align 8 + + +struct subobject_array { + int foo; + int array[10]; + int bar; +}; + +struct subobject_array subobj; + +int *__unsafe_indexable subobject_lower = __builtin_get_pointer_lower_bound(subobj.array); +int *__unsafe_indexable subobject_upper = __builtin_get_pointer_upper_bound(subobj.array); +// CHECK: @subobject_lower = global ptr getelementptr (i8, ptr @subobj, i64 4), align 8 +// CHECK: @subobject_upper = global ptr getelementptr (i8, ptr @subobj, i64 44), align 8 + +int *__unsafe_indexable subobject_added_lower = __builtin_get_pointer_lower_bound(subobj.array + 5); +int *__unsafe_indexable subobject_added_upper = __builtin_get_pointer_upper_bound(subobj.array + 5); +// CHECK: @subobject_added_lower = global ptr getelementptr (i8, ptr @subobj, i64 4), align 8 +// CHECK: @subobject_added_upper = global ptr getelementptr (i8, ptr @subobj, i64 44), align 8 + +int *__unsafe_indexable subobject_indexed_lower = __builtin_get_pointer_lower_bound(&subobj.array[5]); +int *__unsafe_indexable subobject_indexed_upper = __builtin_get_pointer_upper_bound(&subobj.array[5]); +// CHECK: @subobject_indexed_lower = global ptr getelementptr (i8, ptr @subobj, i64 4), align 8 +// CHECK: @subobject_indexed_upper = global ptr getelementptr (i8, ptr @subobj, i64 44), align 8 + +struct subobject_array *__unsafe_indexable lower = __builtin_get_pointer_lower_bound(&subobj); +struct subobject_array *__unsafe_indexable upper = __builtin_get_pointer_upper_bound(&subobj); +// CHECK: @lower = global ptr @subobj, align 8 +// CHECK: @upper = global ptr getelementptr (i8, ptr @subobj, i64 48), align 8 + + +// CHECK-LABEL: @getLowerBound( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[PTR:%.*]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT:%.*]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP0]], align 8 +// CHECK-NEXT: ret void +// +int *__bidi_indexable getLowerBound(int *__bidi_indexable ptr) { + return __builtin_get_pointer_lower_bound(ptr); +} + + +// CHECK-LABEL: @getUpperBound( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[PTR:%.*]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT:%.*]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP0]], align 8 +// CHECK-NEXT: ret void +// +int *__bidi_indexable getUpperBound(int *__bidi_indexable ptr) { + return __builtin_get_pointer_upper_bound(ptr); +} + diff --git a/clang/test/BoundsSafety/CodeGen/bounds-safety-feature.c b/clang/test/BoundsSafety/CodeGen/bounds-safety-feature.c new file mode 100644 index 0000000000000..c5a029456569d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-safety-feature.c @@ -0,0 +1,14 @@ + + +// RUN: %clang_cc1 %s -E -fbounds-safety | FileCheck %s --check-prefix=ENABLED +// RUN: %clang_cc1 %s -E | FileCheck %s --check-prefix=DISABLED +// RUN: %clang_cc1 %s -E -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental | FileCheck %s --check-prefix=ENABLED +// RUN: %clang_cc1 %s -E -x objective-c | FileCheck %s --check-prefix=DISABLED + +#if __has_feature(bounds_safety) +// ENABLED: has_bounds_safety +void has_bounds_safety() {} +#else +// DISABLED: no_bounds_safety +void no_bounds_safety() {} +#endif diff --git a/clang/test/BoundsSafety/CodeGen/bounds-safety-introduced-arithmetic-ubsan-checks-O2.c b/clang/test/BoundsSafety/CodeGen/bounds-safety-introduced-arithmetic-ubsan-checks-O2.c new file mode 100644 index 0000000000000..a3b28c225b98f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/bounds-safety-introduced-arithmetic-ubsan-checks-O2.c @@ -0,0 +1,126 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// REQUIRES: x86-registered-target + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 -fsanitize=pointer-overflow,signed-integer-overflow,unsigned-integer-overflow -fsanitize-trap=pointer-overflow,signed-integer-overflow,unsigned-integer-overflow %s -o - | FileCheck %s --check-prefix=UBSAN +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s --check-prefix=NOUBSAN +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 -fsanitize=pointer-overflow,signed-integer-overflow,unsigned-integer-overflow -fsanitize-trap=pointer-overflow,signed-integer-overflow,unsigned-integer-overflow %s -o - | FileCheck %s --check-prefix=UBSAN +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s --check-prefix=NOUBSAN + +#include + +void f_callee(char *__counted_by(inLen), unsigned long long inLen); +// UBSAN-LABEL: @f_unsafe_outlen( +// UBSAN-NEXT: entry: +// UBSAN-NEXT: [[TMP0:%.*]] = load i64, ptr [[OUTLEN:%.*]], align 8, {{!tbaa ![0-9]+}} +// UBSAN-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DEROUT:%.*]], i64 [[TMP0]] +// UBSAN-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[DEROUT]], [[TMP1]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// UBSAN: trap: +// UBSAN-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: unreachable, {{!annotation ![0-9]+}} +// UBSAN: cont: +// UBSAN-NEXT: tail call void @f_callee(ptr noundef [[DEROUT]], i64 noundef [[TMP0]]) #[[ATTR5:[0-9]+]] +// UBSAN-NEXT: ret void +// +// NOUBSAN-LABEL: @f_unsafe_outlen( +// NOUBSAN-NEXT: entry: +// NOUBSAN-NEXT: [[TMP0:%.*]] = load i64, ptr [[OUTLEN:%.*]], align 8, {{!tbaa ![0-9]+}} +// NOUBSAN-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DEROUT:%.*]], i64 [[TMP0]] +// NOUBSAN-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[DEROUT]], [[TMP1]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// NOUBSAN: trap: +// NOUBSAN-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: unreachable, {{!annotation ![0-9]+}} +// NOUBSAN: cont: +// NOUBSAN-NEXT: tail call void @f_callee(ptr noundef [[DEROUT]], i64 noundef [[TMP0]]) #[[ATTR5:[0-9]+]] +// NOUBSAN-NEXT: ret void +// +void f_unsafe_outlen(char *__unsafe_indexable derOut, unsigned long long *outLen) { + f_callee(__unsafe_forge_bidi_indexable(char *, derOut, *outLen), *outLen); +} + +// UBSAN-LABEL: @f_outlen( +// UBSAN-NEXT: entry: +// UBSAN-NEXT: [[TMP0:%.*]] = load i64, ptr [[OUTLEN:%.*]], align 8, {{!tbaa ![0-9]+}} +// UBSAN-NEXT: [[CMP:%.*]] = icmp sgt i64 [[TMP0]], -1 +// UBSAN-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +// UBSAN-NEXT: tail call void @f_callee(ptr noundef [[DEROUT:%.*]], i64 noundef [[TMP0]]) #[[ATTR5]] +// UBSAN-NEXT: ret void +// +// NOUBSAN-LABEL: @f_outlen( +// NOUBSAN-NEXT: entry: +// NOUBSAN-NEXT: [[TMP0:%.*]] = load i64, ptr [[OUTLEN:%.*]], align 8, {{!tbaa ![0-9]+}} +// NOUBSAN-NEXT: [[CMP:%.*]] = icmp sgt i64 [[TMP0]], -1 +// NOUBSAN-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +// NOUBSAN-NEXT: tail call void @f_callee(ptr noundef [[DEROUT:%.*]], i64 noundef [[TMP0]]) #[[ATTR5]] +// NOUBSAN-NEXT: ret void +// +void f_outlen(char *__counted_by(*outLen) derOut, unsigned long long *outLen) { + f_callee(derOut, *outLen); +} + +// UBSAN-LABEL: @f_call_bound_checks( +// UBSAN-NEXT: entry: +// UBSAN-NEXT: [[TMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[BUF:%.*]], align 8 +// UBSAN-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP_SROA_0_0_COPYLOAD]], i64 [[OFFSET:%.*]] +// UBSAN-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[TMP_SROA_0_0_COPYLOAD]] to i64, {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: [[TMP1:%.*]] = add i64 [[OFFSET]], [[TMP0]], {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP_SROA_0_0_COPYLOAD]], null, {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: [[TMP3:%.*]] = icmp eq i64 [[TMP1]], 0 +// UBSAN-NEXT: [[TMP4:%.*]] = xor i1 [[TMP2]], [[TMP3]] +// UBSAN-NEXT: [[TMP5:%.*]] = icmp uge i64 [[TMP1]], [[TMP0]], {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: [[TMP6:%.*]] = and i1 [[TMP5]], [[TMP4]], {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!nosanitize ![0-9]+}} +// UBSAN: trap: +// UBSAN-NEXT: tail call void @llvm.ubsantrap(i8 19) #[[ATTR6:[0-9]+]], {{!nosanitize ![0-9]+}} +// UBSAN-NEXT: unreachable, {{!nosanitize ![0-9]+}} +// UBSAN: cont: +// UBSAN-NEXT: [[TMP_SROA_4_0_BUF_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BUF]], i64 16 +// UBSAN-NEXT: [[TMP_SROA_4_0_COPYLOAD:%.*]] = load ptr, ptr [[TMP_SROA_4_0_BUF_SROA_IDX]], align 8, {{!tbaa ![0-9]+}} +// UBSAN-NEXT: [[TMP_SROA_3_0_BUF_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BUF]], i64 8 +// UBSAN-NEXT: [[TMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[TMP_SROA_3_0_BUF_SROA_IDX]], align 8 +// UBSAN-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[TMP_SROA_3_0_COPYLOAD]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[CMP27_NOT:%.*]] = icmp ugt ptr [[TMP_SROA_4_0_COPYLOAD]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[OR_COND:%.*]] = select i1 [[CMP_NOT]], i1 true, i1 [[CMP27_NOT]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[TMP_SROA_3_0_COPYLOAD]] to i64, {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[BOUND_PTR_ARITH]] to i64, {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[CMP44_NOT:%.*]] = icmp ugt i64 [[LEN:%.*]], [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: [[OR_COND55:%.*]] = or i1 [[OR_COND]], [[CMP44_NOT]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: br i1 [[OR_COND55]], label [[TRAP45:%.*]], label [[CONT46:%.*]], {{!annotation ![0-9]+}} +// UBSAN: trap45: +// UBSAN-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// UBSAN-NEXT: unreachable, {{!annotation ![0-9]+}} +// UBSAN: cont46: +// UBSAN-NEXT: tail call void @f_callee(ptr noundef [[BOUND_PTR_ARITH]], i64 noundef [[LEN]]) #[[ATTR5]] +// UBSAN-NEXT: ret void +// +// NOUBSAN-LABEL: @f_call_bound_checks( +// NOUBSAN-NEXT: entry: +// NOUBSAN-NEXT: [[TMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[BUF:%.*]], align 8 +// NOUBSAN-NEXT: [[TMP_SROA_2_0_BUF_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BUF]], i64 8 +// NOUBSAN-NEXT: [[TMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[TMP_SROA_2_0_BUF_SROA_IDX]], align 8 +// NOUBSAN-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP_SROA_0_0_COPYLOAD]], i64 [[OFFSET:%.*]] +// NOUBSAN-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[TMP_SROA_2_0_COPYLOAD]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[TMP_SROA_3_0_BUF_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BUF]], i64 16 +// NOUBSAN-NEXT: [[TMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[TMP_SROA_3_0_BUF_SROA_IDX]], align 8 +// NOUBSAN-NEXT: [[CMP27_NOT:%.*]] = icmp ugt ptr [[TMP_SROA_3_0_COPYLOAD]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[OR_COND:%.*]] = select i1 [[CMP_NOT]], i1 true, i1 [[CMP27_NOT]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[TMP_SROA_2_0_COPYLOAD]] to i64, {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[BOUND_PTR_ARITH]] to i64, {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[CMP44_NOT:%.*]] = icmp ugt i64 [[LEN:%.*]], [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: [[OR_COND52:%.*]] = or i1 [[OR_COND]], [[CMP44_NOT]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: br i1 [[OR_COND52]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// NOUBSAN: trap: +// NOUBSAN-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// NOUBSAN-NEXT: unreachable, {{!annotation ![0-9]+}} +// NOUBSAN: cont: +// NOUBSAN-NEXT: tail call void @f_callee(ptr noundef [[BOUND_PTR_ARITH]], i64 noundef [[LEN]]) #[[ATTR5]] +// NOUBSAN-NEXT: ret void +// +void f_call_bound_checks(char *__bidi_indexable buf, unsigned long long offset, + unsigned long long len) { + f_callee(buf + offset, len); +} diff --git a/clang/test/BoundsSafety/CodeGen/builtin-memcpy-count-annotation-O2.c b/clang/test/BoundsSafety/CodeGen/builtin-memcpy-count-annotation-O2.c new file mode 100644 index 0000000000000..561d2ec27f0cf --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/builtin-memcpy-count-annotation-O2.c @@ -0,0 +1,53 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK(void) { + char dst[10]; + char src[10]; + int len = 10; + __builtin_memcpy(dst, src, len); +} + +// CHECK-LABEL: @TestDstFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestDstFail(void) { + char dst[9]; + char src[10]; + int len = 10; + __builtin_memcpy(dst, src, len); +} + +// CHECK-LABEL: @TestSrcFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestSrcFail(void) { + char dst[10]; + char src[9]; + int len = 10; + __builtin_memcpy(dst, src, len); +} + +// CHECK-LABEL: @TestFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestFail(void) { + char dst[10]; + char src[10]; + int len = 11; + __builtin_memcpy(dst, src, len); +} diff --git a/clang/test/BoundsSafety/CodeGen/builtin-memcpy-count-annotation.c b/clang/test/BoundsSafety/CodeGen/builtin-memcpy-count-annotation.c new file mode 100644 index 0000000000000..650aa399d765b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/builtin-memcpy-count-annotation.c @@ -0,0 +1,280 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + + +// + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DST:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[SRC:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP59:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-NEXT: [[AGG_TEMP60:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP74:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP81:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP92:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP101:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP110:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP111:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP126:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP127:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP147:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP154:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[DST]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[SRC]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[DST]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[SRC]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB9]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB19]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_PTR21]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB28]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR37:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB41:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR40]], align 8 +// CHECK-NEXT: [[CMP42:%.*]] = icmp ule ptr [[WIDE_PTR_PTR30]], [[WIDE_PTR_PTR37]] +// CHECK-NEXT: br i1 [[CMP42]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB46]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR48:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB50:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB52:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR51]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR48]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB50]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB52]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP60]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP60]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP60]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP60]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP59]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR62]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP59]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB64]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP59]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB66]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR67:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP59]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR68:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR67]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP59]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB70:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR71:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP59]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB72:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR71]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR54]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR68]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP73:%.*]] = icmp ule i64 10, [[SUB_PTR_SUB]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP73]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP74]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR75:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP74]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR76:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR75]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR77:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP74]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB78:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR77]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR79:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP74]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB80:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR79]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP81]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR82:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP81]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB83:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR82]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP81]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB83]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR84:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP81]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR85:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR84]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR86:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP81]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB87:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR86]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR88:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP81]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB89:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR88]], align 8 +// CHECK-NEXT: [[CMP90:%.*]] = icmp ule ptr [[WIDE_PTR_PTR76]], [[WIDE_PTR_PTR85]] +// CHECK-NEXT: br i1 [[CMP90]], label [[LAND_LHS_TRUE91:%.*]], label [[LAND_END144:%.*]] +// CHECK: land.lhs.true91: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP92]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR93:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB94:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR93]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB94]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR95:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR96:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR95]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR97:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB98:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR97]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR99:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB100:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR99]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP101]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR102:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP101]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR103:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR102]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR104:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP101]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB105:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR104]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR106:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP101]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB107:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR106]], align 8 +// CHECK-NEXT: [[CMP108:%.*]] = icmp ule ptr [[WIDE_PTR_PTR96]], [[WIDE_PTR_PTR103]] +// CHECK-NEXT: br i1 [[CMP108]], label [[LAND_RHS109:%.*]], label [[LAND_END144]] +// CHECK: land.rhs109: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP111]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR112:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP111]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB113:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR112]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP111]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB113]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR114:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP111]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR115:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR114]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR116:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP111]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB117:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR116]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR118:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP111]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB119:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR118]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP110]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR115]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP110]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB117]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP110]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB119]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR120:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP110]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR121:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR120]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR122:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP110]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB123:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR122]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR124:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP110]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB125:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR124]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP127]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR128:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP127]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR129:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR128]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR130:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP127]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB131:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR130]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR132:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP127]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB133:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR132]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP126]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR129]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP126]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB131]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP126]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB133]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR134:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP126]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR135:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR134]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR136:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP126]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB137:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR136]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR138:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP126]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB139:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR138]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST140:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR121]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST141:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR135]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB142:%.*]] = sub i64 [[SUB_PTR_LHS_CAST140]], [[SUB_PTR_RHS_CAST141]] +// CHECK-NEXT: [[CMP143:%.*]] = icmp ule i64 10, [[SUB_PTR_SUB142]] +// CHECK-NEXT: br label [[LAND_END144]] +// CHECK: land.end144: +// CHECK-NEXT: [[TMP25:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE91]] ], [ false, [[CONT]] ], [ [[CMP143]], [[LAND_RHS109]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP25]], label [[CONT146:%.*]], label [[TRAP145:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap145: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont146: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP147]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR148:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP147]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR149:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR148]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR150:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP147]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB151:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR150]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR152:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP147]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB153:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR152]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP154]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR155:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP154]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR156:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR155]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR157:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP154]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB158:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR157]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR159:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP154]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB160:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR159]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[WIDE_PTR_PTR149]], ptr align 1 [[WIDE_PTR_PTR156]], i64 10, i1 false) +// CHECK-NEXT: call void @llvm.assume(i1 true) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[WIDE_PTR_PTR149]], i64 10 +// CHECK-NEXT: ret void +// +void foo(void) { + char *dst, *src; + __builtin_memcpy(dst, src, 10); +} diff --git a/clang/test/BoundsSafety/CodeGen/call-with-count-ptr.c b/clang/test/BoundsSafety/CodeGen/call-with-count-ptr.c new file mode 100644 index 0000000000000..a24517351b767 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/call-with-count-ptr.c @@ -0,0 +1,522 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -Wno-int-conversion -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_X64_O0 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -Wno-int-conversion -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_ARM64_O0 +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-int-conversion -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_X64_O0 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -Wno-int-conversion -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_ARM64_O0 +#include + +#define memmove_(b, ...) __builtin___memmove_chk(b, __VA_ARGS__, b) +#define memmove(d, h, e) memmove_(d, h, e) + +int arr[] = {0, 1, 2, 3, 4, 5}; + + + +// CHECK_X64_O0-LABEL: @foo( +// CHECK_X64_O0-NEXT: entry: +// CHECK_X64_O0-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK_X64_O0-NEXT: [[LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP56:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP66:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP75:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP83:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.3", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP84:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP99:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.3", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP100:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP119:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_X64_O0-NEXT: [[AGG_TEMP126:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_X64_O0-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK_X64_O0-NEXT: store ptr [[LEN:%.*]], ptr [[LEN_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[TMP1:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK_X64_O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK_X64_O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK_X64_O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK_X64_O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK_X64_O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP6]], align 8 +// CHECK_X64_O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP7]], align 8 +// CHECK_X64_O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP8]], align 8 +// CHECK_X64_O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr @arr, ptr [[TMP9]], align 8 +// CHECK_X64_O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr getelementptr inbounds (i32, ptr @arr, i64 6), ptr [[TMP10]], align 8 +// CHECK_X64_O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr @arr, ptr [[TMP11]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK_X64_O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP12]], align 8 +// CHECK_X64_O0-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP13]], align 8 +// CHECK_X64_O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB9]], ptr [[TMP14]], align 8 +// CHECK_X64_O0-NEXT: [[TMP15:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[TMP16:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK_X64_O0-NEXT: [[TMP17:%.*]] = load i32, ptr [[TMP16]], align 4 +// CHECK_X64_O0-NEXT: [[IDX_EXT11:%.*]] = sext i32 [[TMP17]] to i64 +// CHECK_X64_O0-NEXT: [[ADD_PTR12:%.*]] = getelementptr inbounds i32, ptr [[TMP15]], i64 [[IDX_EXT11]] +// CHECK_X64_O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// CHECK_X64_O0-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[ADD_PTR12]], ptr [[TMP19]], align 8 +// CHECK_X64_O0-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[TMP15]], ptr [[TMP20]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8 +// CHECK_X64_O0-NEXT: [[TMP21:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR14]] to i64 +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8 +// CHECK_X64_O0-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP22]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8 +// CHECK_X64_O0-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_PTR24]], ptr [[TMP23]], align 8 +// CHECK_X64_O0-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB26]], ptr [[TMP24]], align 8 +// CHECK_X64_O0-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB28]], ptr [[TMP25]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK_X64_O0-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[TMP26]], align 8 +// CHECK_X64_O0-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB40]], ptr [[TMP27]], align 8 +// CHECK_X64_O0-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB42]], ptr [[TMP28]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8 +// CHECK_X64_O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR30]] to i64 +// CHECK_X64_O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR44]] to i64 +// CHECK_X64_O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK_X64_O0-NEXT: [[CMP:%.*]] = icmp ule i64 24, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK_X64_O0-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK_X64_O0: trap: +// CHECK_X64_O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK_X64_O0-NEXT: unreachable +// CHECK_X64_O0: cont: +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP49]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP56]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK_X64_O0-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB58]], ptr [[TMP29]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8 +// CHECK_X64_O0-NEXT: [[CMP65:%.*]] = icmp ule ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_PTR60]] +// CHECK_X64_O0-NEXT: br i1 [[CMP65]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK_X64_O0: land.lhs.true: +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP66]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR67:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB68:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR67]], align 8 +// CHECK_X64_O0-NEXT: [[TMP30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB68]], ptr [[TMP30]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP75]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR76:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP75]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR77:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR76]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR78:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP75]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB79:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR78]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR80:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP75]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB81:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR80]], align 8 +// CHECK_X64_O0-NEXT: [[CMP82:%.*]] = icmp ule ptr [[WIDE_PTR_PTR70]], [[WIDE_PTR_PTR77]] +// CHECK_X64_O0-NEXT: br i1 [[CMP82]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK_X64_O0: land.rhs: +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP84]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR85:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB86:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR85]], align 8 +// CHECK_X64_O0-NEXT: [[TMP31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB86]], ptr [[TMP31]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR87:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR88:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR87]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR89:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB90:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR89]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR91:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB92:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR91]], align 8 +// CHECK_X64_O0-NEXT: [[TMP32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_PTR88]], ptr [[TMP32]], align 8 +// CHECK_X64_O0-NEXT: [[TMP33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB90]], ptr [[TMP33]], align 8 +// CHECK_X64_O0-NEXT: [[TMP34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB92]], ptr [[TMP34]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR93:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR94:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR93]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR95:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB96:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR95]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR97:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB98:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR97]], align 8 +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP100]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR101:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP100]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR102:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR101]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR103:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP100]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB104:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR103]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR105:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP100]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB106:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR105]], align 8 +// CHECK_X64_O0-NEXT: [[TMP35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_PTR102]], ptr [[TMP35]], align 8 +// CHECK_X64_O0-NEXT: [[TMP36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_UB104]], ptr [[TMP36]], align 8 +// CHECK_X64_O0-NEXT: [[TMP37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: store ptr [[WIDE_PTR_LB106]], ptr [[TMP37]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR107:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR108:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR107]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR109:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB110:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR109]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR111:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB112:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR111]], align 8 +// CHECK_X64_O0-NEXT: [[SUB_PTR_LHS_CAST113:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR94]] to i64 +// CHECK_X64_O0-NEXT: [[SUB_PTR_RHS_CAST114:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR108]] to i64 +// CHECK_X64_O0-NEXT: [[SUB_PTR_SUB115:%.*]] = sub i64 [[SUB_PTR_LHS_CAST113]], [[SUB_PTR_RHS_CAST114]] +// CHECK_X64_O0-NEXT: [[CMP116:%.*]] = icmp ule i64 24, [[SUB_PTR_SUB115]] +// CHECK_X64_O0-NEXT: br label [[LAND_END]] +// CHECK_X64_O0: land.end: +// CHECK_X64_O0-NEXT: [[TMP38:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT]] ], [ [[CMP116]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK_X64_O0-NEXT: br i1 [[TMP38]], label [[CONT118:%.*]], label [[TRAP117:%.*]], {{!annotation ![0-9]+}} +// CHECK_X64_O0: trap117: +// CHECK_X64_O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK_X64_O0-NEXT: unreachable +// CHECK_X64_O0: cont118: +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP119]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR120:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP119]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR121:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR120]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR122:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP119]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB123:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR122]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR124:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP119]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB125:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR124]], align 8 +// CHECK_X64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP126]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR_ADDR127:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP126]], i32 0, i32 0 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_PTR128:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR127]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB_ADDR129:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP126]], i32 0, i32 1 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_UB130:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR129]], align 8 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB_ADDR131:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP126]], i32 0, i32 2 +// CHECK_X64_O0-NEXT: [[WIDE_PTR_LB132:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR131]], align 8 +// CHECK_X64_O0-NEXT: [[CALL:%.*]] = call ptr @__memmove_chk(ptr noundef [[WIDE_PTR_PTR121]], ptr noundef [[WIDE_PTR_PTR128]], i64 noundef 24, i64 noundef [[TMP21]]) #[[ATTR6:[0-9]+]] +// CHECK_X64_O0-NEXT: call void @llvm.assume(i1 true) +// CHECK_X64_O0-NEXT: [[ADD_PTR133:%.*]] = getelementptr inbounds nuw i8, ptr [[CALL]], i64 24 +// CHECK_X64_O0-NEXT: ret i32 0 +// +// CHECK_ARM64_O0-LABEL: @foo( +// CHECK_ARM64_O0-NEXT: entry: +// CHECK_ARM64_O0-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK_ARM64_O0-NEXT: [[LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP56:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP66:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP75:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP83:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.3", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP84:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP99:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.3", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP100:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP119:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_ARM64_O0-NEXT: [[AGG_TEMP126:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK_ARM64_O0-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: store ptr [[LEN:%.*]], ptr [[LEN_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP1:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK_ARM64_O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK_ARM64_O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK_ARM64_O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP6]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP7]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP8]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr @arr, ptr [[TMP9]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr getelementptr inbounds (i32, ptr @arr, i64 6), ptr [[TMP10]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr @arr, ptr [[TMP11]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP12]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP13]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB9]], ptr [[TMP14]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP15:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP16:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP17:%.*]] = load i32, ptr [[TMP16]], align 4 +// CHECK_ARM64_O0-NEXT: [[IDX_EXT11:%.*]] = sext i32 [[TMP17]] to i64 +// CHECK_ARM64_O0-NEXT: [[ADD_PTR12:%.*]] = getelementptr inbounds i32, ptr [[TMP15]], i64 [[IDX_EXT11]] +// CHECK_ARM64_O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[ADD_PTR12]], ptr [[TMP19]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[TMP15]], ptr [[TMP20]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP21:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR14]] to i64 +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP22]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_PTR24]], ptr [[TMP23]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB26]], ptr [[TMP24]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB28]], ptr [[TMP25]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[TMP26]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB40]], ptr [[TMP27]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB42]], ptr [[TMP28]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8 +// CHECK_ARM64_O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR30]] to i64 +// CHECK_ARM64_O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR44]] to i64 +// CHECK_ARM64_O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK_ARM64_O0-NEXT: [[CMP:%.*]] = icmp ule i64 24, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK_ARM64_O0-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK_ARM64_O0: trap: +// CHECK_ARM64_O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK_ARM64_O0-NEXT: unreachable +// CHECK_ARM64_O0: cont: +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP49]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP56]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB58]], ptr [[TMP29]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8 +// CHECK_ARM64_O0-NEXT: [[CMP65:%.*]] = icmp ule ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_PTR60]] +// CHECK_ARM64_O0-NEXT: br i1 [[CMP65]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK_ARM64_O0: land.lhs.true: +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP66]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR67:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB68:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR67]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB68]], ptr [[TMP30]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP75]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR76:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP75]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR77:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR76]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR78:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP75]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB79:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR78]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR80:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP75]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB81:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR80]], align 8 +// CHECK_ARM64_O0-NEXT: [[CMP82:%.*]] = icmp ule ptr [[WIDE_PTR_PTR70]], [[WIDE_PTR_PTR77]] +// CHECK_ARM64_O0-NEXT: br i1 [[CMP82]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK_ARM64_O0: land.rhs: +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP84]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR85:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB86:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR85]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB86]], ptr [[TMP31]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR87:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR88:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR87]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR89:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB90:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR89]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR91:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP84]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB92:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR91]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_PTR88]], ptr [[TMP32]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB90]], ptr [[TMP33]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB92]], ptr [[TMP34]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR93:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR94:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR93]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR95:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB96:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR95]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR97:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP83]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB98:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR97]], align 8 +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP100]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR101:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP100]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR102:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR101]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR103:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP100]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB104:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR103]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR105:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP100]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB106:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR105]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_PTR102]], ptr [[TMP35]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_UB104]], ptr [[TMP36]], align 8 +// CHECK_ARM64_O0-NEXT: [[TMP37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: store ptr [[WIDE_PTR_LB106]], ptr [[TMP37]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR107:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR108:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR107]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR109:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB110:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR109]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR111:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP99]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB112:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR111]], align 8 +// CHECK_ARM64_O0-NEXT: [[SUB_PTR_LHS_CAST113:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR94]] to i64 +// CHECK_ARM64_O0-NEXT: [[SUB_PTR_RHS_CAST114:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR108]] to i64 +// CHECK_ARM64_O0-NEXT: [[SUB_PTR_SUB115:%.*]] = sub i64 [[SUB_PTR_LHS_CAST113]], [[SUB_PTR_RHS_CAST114]] +// CHECK_ARM64_O0-NEXT: [[CMP116:%.*]] = icmp ule i64 24, [[SUB_PTR_SUB115]] +// CHECK_ARM64_O0-NEXT: br label [[LAND_END]] +// CHECK_ARM64_O0: land.end: +// CHECK_ARM64_O0-NEXT: [[TMP38:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT]] ], [ [[CMP116]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK_ARM64_O0-NEXT: br i1 [[TMP38]], label [[CONT118:%.*]], label [[TRAP117:%.*]], {{!annotation ![0-9]+}} +// CHECK_ARM64_O0: trap117: +// CHECK_ARM64_O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK_ARM64_O0-NEXT: unreachable +// CHECK_ARM64_O0: cont118: +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP119]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR120:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP119]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR121:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR120]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR122:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP119]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB123:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR122]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR124:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP119]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB125:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR124]], align 8 +// CHECK_ARM64_O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP126]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR_ADDR127:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP126]], i32 0, i32 0 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_PTR128:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR127]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB_ADDR129:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP126]], i32 0, i32 1 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_UB130:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR129]], align 8 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB_ADDR131:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP126]], i32 0, i32 2 +// CHECK_ARM64_O0-NEXT: [[WIDE_PTR_LB132:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR131]], align 8 +// CHECK_ARM64_O0-NEXT: [[CALL:%.*]] = call ptr @__memmove_chk(ptr noundef [[WIDE_PTR_PTR121]], ptr noundef [[WIDE_PTR_PTR128]], i64 noundef 24, i64 noundef [[TMP21]]) #[[ATTR6:[0-9]+]] +// CHECK_ARM64_O0-NEXT: call void @llvm.assume(i1 true) +// CHECK_ARM64_O0-NEXT: [[ADD_PTR133:%.*]] = getelementptr inbounds nuw i8, ptr [[CALL]], i64 24 +// CHECK_ARM64_O0-NEXT: ret i32 0 +// +int foo(int *__counted_by(*len) buf, int *len) { + memmove(buf, arr, sizeof(arr)); + + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-O0.c b/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-O0.c new file mode 100644 index 0000000000000..6333ab2bf56ad --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-O0.c @@ -0,0 +1,34 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP3]] +// +int *__indexable test(int *__bidi_indexable p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-O2.c b/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-O2.c new file mode 100644 index 0000000000000..d24140d924954 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-O2.c @@ -0,0 +1,27 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP_SROA_3_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[AGG_TEMP_SROA_2_0_COPYLOAD]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable test(int *__bidi_indexable p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-trivial-O2.c new file mode 100644 index 0000000000000..52b447ed29bf0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-bidi-to-indexable-trivial-O2.c @@ -0,0 +1,75 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static struct { + int _a[42]; + int array[3]; + int _b[42]; +} foo; + +// CHECK-LABEL: @good( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr getelementptr inbounds nuw ([[STRUCT_ANON:%.*]], ptr @foo, i32 0, i32 1), ptr getelementptr inbounds (i32, ptr getelementptr inbounds nuw ([[STRUCT_ANON]], ptr @foo, i32 0, i32 1), i64 3) } +// +int *__indexable good(void) { + int *__bidi_indexable p = foo.array; + return p; +} + +// CHECK-LABEL: @good_upper( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr getelementptr inbounds nuw (i8, ptr @foo, i64 176), ptr getelementptr inbounds (i32, ptr getelementptr inbounds nuw ([[STRUCT_ANON:%.*]], ptr @foo, i32 0, i32 1), i64 3) } +// +int *__indexable good_upper(void) { + int *__bidi_indexable p = foo.array; + p += 2; + return p; +} + +// CHECK-LABEL: @good_upper2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr getelementptr inbounds nuw (i8, ptr @foo, i64 208), ptr getelementptr inbounds (i32, ptr getelementptr inbounds nuw ([[STRUCT_ANON:%.*]], ptr @foo, i32 0, i32 1), i64 3) } +// +int *__indexable good_upper2(void) { + int *__bidi_indexable p = foo.array; + p += 10; + return p; +} + +// CHECK-LABEL: @bad_lower( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *__indexable bad_lower(void) { + int *__bidi_indexable p = foo.array; + p -= 1; + return p; +} + +// CHECK-LABEL: @good_null( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } zeroinitializer +// +int *__indexable good_null(void) { + int *__bidi_indexable p = 0; + return p; +} + +// CHECK-LABEL: @bad_null( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *__indexable bad_null(void) { + // Try to cast { null, non-null, non-null } and make sure that we don't + // return { null, non-null }. + int *__bidi_indexable p = foo.array; + while (--p) + ; + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-indexable-to-bidi-O0.c b/clang/test/BoundsSafety/CodeGen/cast-indexable-to-bidi-O0.c new file mode 100644 index 0000000000000..5f02baa9687c9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-indexable-to-bidi-O0.c @@ -0,0 +1,31 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: ret void +// +int *__bidi_indexable test(int *__indexable p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-indexable-to-bidi-O2.c b/clang/test/BoundsSafety/CodeGen/cast-indexable-to-bidi-O2.c new file mode 100644 index 0000000000000..8209098506d81 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-indexable-to-bidi-O2.c @@ -0,0 +1,20 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[AGG_RESULT:%.*]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_RESULT]], i64 8 +// CHECK-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_RESULT]], i64 16 +// CHECK-NEXT: store ptr [[P_COERCE0]], ptr [[TMP1]], align 8 +// CHECK-NEXT: ret void +// +int *__bidi_indexable test(int *__indexable p) { + return p; +} + diff --git a/clang/test/BoundsSafety/CodeGen/cast-member-expr.c b/clang/test/BoundsSafety/CodeGen/cast-member-expr.c new file mode 100644 index 0000000000000..c41025594db08 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-member-expr.c @@ -0,0 +1,11 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null + +struct ctx { int x; }; + +void foo(void) { + int *p = 0; + ((struct ctx *)p)->x = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-O0.c b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-O0.c new file mode 100644 index 0000000000000..c458846568215 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-O0.c @@ -0,0 +1,274 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +void Foo(int *__counted_by(len) buf, int len); +void Bar(int *__counted_by(10) ptr); + +// CHECK-LABEL: @TestFoo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[END_PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 11 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: br label [[HERE:%.*]] +// CHECK: here: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[END_PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END46:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END46]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP44:%.*]] = icmp sle i64 1, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP44]], label [[LAND_RHS45:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs45: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS45]] ] +// CHECK-NEXT: br label [[LAND_END46]], {{!annotation ![0-9]+}} +// CHECK: land.end46: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[HERE]] ], [ [[TMP15]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: call void @Foo(ptr noundef [[WIDE_PTR_PTR49]], i32 noundef 1) +// CHECK-NEXT: ret void +// +void TestFoo(void) { + int arr[10]; + int *end_ptr = arr + 11; +here: + Foo(end_ptr, 1); // trap +} + +// CHECK-LABEL: @TestBar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[END_PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: br label [[HERE:%.*]] +// CHECK: here: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[END_PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END46:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END46]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP44:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP44]], label [[LAND_RHS45:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs45: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS45]] ] +// CHECK-NEXT: br label [[LAND_END46]], {{!annotation ![0-9]+}} +// CHECK: land.end46: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[HERE]] ], [ [[TMP15]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: call void @Bar(ptr noundef [[WIDE_PTR_PTR49]]) +// CHECK-NEXT: ret void +// +void TestBar(void) { + int arr[10]; + int *end_ptr = arr + 1; +here: + Bar(({end_ptr;})); // trap (needs 10 elements) +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-O2.c b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-O2.c new file mode 100644 index 0000000000000..a1c4a96f12b1f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-O2.c @@ -0,0 +1,182 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int len; + int *__counted_by(len) buf; +} S; + +void Foo(int *__counted_by(len) buf, int len); +void Bar(int *__counted_by(10) ptr); + +// CHECK-LABEL: @TestZeroCount( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestZeroCount(void) { + int arr[10]; + int *end_ptr = arr + 10; + + S s; + s.len = 0; + s.buf = end_ptr; // okay +} + +// CHECK-LABEL: @TestCountPtrArgFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountPtrArgFail(void) { + int arr[10]; + int *end_ptr = arr + 9; + + Foo(end_ptr, 10); // trap +} + +// CHECK-LABEL: @TestCountPtrArgFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountPtrArgFail2(void) { + int arr[10]; + int *end_ptr = arr + 11; + + Foo(end_ptr, 10); // trap +} + +// CHECK-LABEL: @TestCountPtrArgFail3( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountPtrArgFail3(void) { + int arr[10]; + int n = 12; + Foo(arr, n); // trap +} + +// CHECK-LABEL: @TestCountPtrArgOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: call void @Foo(ptr noundef nonnull [[UPPER]], i32 noundef 0) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestCountPtrArgOK(void) { + int arr[10]; + int *end_ptr = arr + 10; + + Foo(end_ptr, 0); // ok +} + +// CHECK-LABEL: @TestCountPtrArgOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 36 +// CHECK-NEXT: call void @Foo(ptr noundef nonnull [[BOUND_PTR_ARITH]], i32 noundef 1) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestCountPtrArgOK2(void) { + int arr[10]; + int *end_ptr = arr + 9; + + Foo(end_ptr, 1); // ok +} + +// CHECK-LABEL: @TestConstCountPtrArgFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestConstCountPtrArgFail(void) { + int arr[10]; + int *end_ptr = arr + 10; + + Bar(({end_ptr;})); // trap +} + +// CHECK-LABEL: @TestConstCountPtrArgOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @Bar(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestConstCountPtrArgOK(void) { + int arr[10]; + int *end_ptr = arr; + + Bar(({end_ptr;})); // ok +} + +// CHECK-LABEL: @TestConstZeroCountOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestConstZeroCountOK(void) { + int arr[10]; + int *end_ptr = arr + 10; + + int *__counted_by(0) zero_buf; + zero_buf = end_ptr; // ok +} + +// CHECK-LABEL: @TestBidiOverBounds( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestBidiOverBounds() { + int arr[10]; + Foo(arr + 11, 10); +} + +// XXX: unexpectedly this still has a bounds check +// CHECK-LABEL: @TestBidiUnderBounds( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP27_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @Foo(ptr noundef [[BOUND_PTR_ARITH]], i32 noundef 10) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestBidiUnderBounds() { + int arr[10]; + Foo(arr - 1, 10); +} + +// XXX: this might be just fine not to trap +// CHECK-LABEL: @TestConstZeroCountFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestConstZeroCountFail(void) { + int arr[10]; + int *end_ptr = arr + 11; + + int *__counted_by(0) zero_buf; + zero_buf = end_ptr; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-stmt-expr-O2.c b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-stmt-expr-O2.c new file mode 100644 index 0000000000000..f7dd575a8753d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-stmt-expr-O2.c @@ -0,0 +1,103 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +typedef struct { + int len; + int *__counted_by(len) buf; +} S; + +void Foo(int *__indexable ptr); + +// + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: call void @Foo(ptr noundef nonnull [[UPPER]], ptr noundef nonnull [[UPPER]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void TestOK(void) { + int arr[10]; + int *end_ptr = arr + 10; + + Foo(({ + S s = {10, arr}; + int *ptr; + s.buf = end_ptr; + s.len = 0; + ptr = s.buf; + })); +} + +void FooCount1(int *__counted_by(1) ptr); + +// CHECK-LABEL: @TestCountArgFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountArgFail(void) { + int arr[10]; + int *end_ptr = arr + 10; + + FooCount1(({ + S s = {10, arr}; + int *ptr; + s.buf = end_ptr; + s.len = 0; + ptr = s.buf; + })); +} + +void FooSingle(int *ptr); + +// CHECK-LABEL: @TestSingleArgFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestSingleArgFail(void) { + int arr[10]; + int *end_ptr = arr + 10; + + FooSingle(({ + S s = {10, arr}; + int *ptr; + s.buf = end_ptr; + s.len = 0; + ptr = s.buf; + })); +} + +void *__sized_by(siz) my_alloc(unsigned long siz); + +// CHECK-LABEL: @TestCountArgOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc(i64 noundef 10) #[[ATTR5]] +// CHECK-NEXT: tail call void @FooCount1(ptr noundef [[CALL]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void TestCountArgOK(void) { + FooCount1(my_alloc(10)); +} + + + + + + + + + + + + + diff --git a/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-stmt-expr.c b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-stmt-expr.c new file mode 100644 index 0000000000000..e054e0df93cdf --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-to-count-argument-stmt-expr.c @@ -0,0 +1,200 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int len; + int *__counted_by(len) buf; +} S; + +void Foo(int *__indexable ptr); + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[END_PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 10 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[END_PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP14]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store i32 10, ptr [[LEN]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[S]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP15]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[END_PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP13]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR8]], [[WIDE_PTR_UB15]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END39:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp ule ptr [[WIDE_PTR_LB18]], [[WIDE_PTR_PTR21]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[LAND_RHS:%.*]], label [[LAND_END39]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB29]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP37:%.*]] = icmp sle i64 0, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP37]], label [[LAND_RHS38:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs38: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS38]] ] +// CHECK-NEXT: br label [[LAND_END39]], {{!annotation ![0-9]+}} +// CHECK: land.end39: +// CHECK-NEXT: [[TMP17:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT]] ], [ [[TMP16]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT41:%.*]], label [[TRAP40:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap40: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont41: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP42]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8 +// CHECK-NEXT: [[BUF49:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR44]], ptr [[BUF49]], align 8 +// CHECK-NEXT: [[LEN50:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[LEN50]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[S]], i64 16, i1 false) +// CHECK-NEXT: [[LEN52:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP51]], i32 0, i32 0 +// CHECK-NEXT: [[TMP18:%.*]] = load i32, ptr [[LEN52]], align 8 +// CHECK-NEXT: [[BUF53:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP51]], i32 0, i32 1 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[BUF53]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP18]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP19]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP22]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR55:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR56:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB57:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR56]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR58:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB59:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR58]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP23:%.*]] = icmp uge ptr [[WIDE_PTR_PTR55]], [[WIDE_PTR_LB59]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP23]], label [[CONT61:%.*]], label [[TRAP60:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap60: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont61: +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR55]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB57]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP27:%.*]] = load ptr, ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP29:%.*]] = load ptr, ptr [[TMP28]], align 8 +// CHECK-NEXT: call void @Foo(ptr noundef [[TMP27]], ptr noundef [[TMP29]]) +// CHECK-NEXT: ret i32 0 +// +int Test(void) { + int arr[10]; + int *end_ptr = arr + 10; + + Foo(({ + S s = {10, arr}; + int *ptr; + s.buf = end_ptr; + s.len = 0; + ptr = s.buf; + })); + + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/cast-to-size-argument-O2.c b/clang/test/BoundsSafety/CodeGen/cast-to-size-argument-O2.c new file mode 100644 index 0000000000000..b4427a692b163 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/cast-to-size-argument-O2.c @@ -0,0 +1,148 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +typedef struct { + int len; + int *__sized_by(len) buf; +} S; + +void Foo(int *__sized_by(len) buf, int len); +void Bar(int *__sized_by(40) ptr); + +// CHECK-LABEL: @TestZeroCount( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestZeroCount(void) { + int arr[10]; + int *end_ptr = arr + 10; + + S s; + s.len = 0; + s.buf = end_ptr; // okay +} + +// CHECK-LABEL: @TestCountPtrArgFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountPtrArgFail(void) { + int arr[10]; + int *end_ptr = arr + 9; + + Foo(end_ptr, 40); // trap +} + +// CHECK-LABEL: @TestCountPtrArgFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountPtrArgFail2(void) { + int arr[10]; + int *end_ptr = arr + 11; + + Foo(end_ptr, 40); // trap +} + +// CHECK-LABEL: @TestCountPtrArgFail3( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestCountPtrArgFail3(void) { + int arr[10]; + int n = 41; + Foo(arr, n); // trap +} + +// CHECK-LABEL: @TestCountPtrArgOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: call void @Foo(ptr noundef nonnull [[UPPER]], i32 noundef 0) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestCountPtrArgOK(void) { + int arr[10]; + int *end_ptr = arr + 10; + + Foo(end_ptr, 0); // ok +} + +// CHECK-LABEL: @TestCountPtrArgOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 36 +// CHECK-NEXT: call void @Foo(ptr noundef nonnull [[BOUND_PTR_ARITH]], i32 noundef 4) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestCountPtrArgOK2(void) { + int arr[10]; + int *end_ptr = arr + 9; + + Foo(end_ptr, 4); // ok +} + +// CHECK-LABEL: @TestConstCountPtrArgFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestConstCountPtrArgFail(void) { + int arr[10]; + int *end_ptr = arr + 10; + + Bar(({end_ptr;})); // trap +} + +// CHECK-LABEL: @TestConstCountPtrArgOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @Bar(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void TestConstCountPtrArgOK(void) { + int arr[10]; + int *end_ptr = arr; + + Bar(({end_ptr;})); // ok +} + +// CHECK-LABEL: @TestConstZeroCountOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestConstZeroCountOK(void) { + int arr[10]; + int *end_ptr = arr + 10; + + int *__sized_by(0) zero_buf; + zero_buf = end_ptr; // ok +} + +// XXX: this might be just fine not to trap +// CHECK-LABEL: @TestConstZeroCountFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestConstZeroCountFail(void) { + int arr[10]; + int *end_ptr = arr + 11; + + int *__sized_by(0) zero_buf; + zero_buf = end_ptr; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/compound-assign-count-O2.c b/clang/test/BoundsSafety/CodeGen/compound-assign-count-O2.c new file mode 100644 index 0000000000000..2292e5157855d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-assign-count-O2.c @@ -0,0 +1,71 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +struct Foo { + unsigned count; + int *__counted_by(count) bytes; +}; + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int arr[10]; + struct Foo f = { 10, arr }; + + unsigned amt = 10; + + f.bytes += amt; + f.count -= amt; +} + +// CHECK-LABEL: @TestOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK2() { + int arr[10]; + struct Foo f = { 10, arr }; + + unsigned amt = 10; + + f.count -= amt; + f.bytes += amt; +} + +// CHECK-LABEL: @TestFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestFail() { + int arr[10]; + struct Foo f = { 10, arr }; + + unsigned amt = 10; + unsigned amt_1 = 9; + + f.bytes += amt; + f.count -= amt_1; +} + +// CHECK-LABEL: @TestFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestFail2() { + int arr[10]; + struct Foo f = { 10, arr }; + + unsigned amt = 10; + unsigned amt_1 = 9; + + f.count -= amt_1; + f.bytes += amt; +} diff --git a/clang/test/BoundsSafety/CodeGen/compound-assign-count-and-ptr-O2.c b/clang/test/BoundsSafety/CodeGen/compound-assign-count-and-ptr-O2.c new file mode 100644 index 0000000000000..2ee568409cf67 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-assign-count-and-ptr-O2.c @@ -0,0 +1,34 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 + + + +#include + +struct foo { + unsigned count; + unsigned char *__counted_by(count) bytes; +}; +// CHECK-O2-LABEL: @TestOK( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void TestOK(void) { + unsigned char arr[10]; + struct foo f = {10, arr}; + f.bytes = f.bytes + 10; + f.count = f.count - 10; +} + +// CHECK-O2-LABEL: @TestFail( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +void TestFail(void) { + unsigned char arr[10]; + struct foo f = {10, arr}; + f.bytes = f.bytes + 10; + f.count = f.count - 9; +} diff --git a/clang/test/BoundsSafety/CodeGen/compound-assign-count-and-ptr.c b/clang/test/BoundsSafety/CodeGen/compound-assign-count-and-ptr.c new file mode 100644 index 0000000000000..6dfdc314f161b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-assign-count-and-ptr.c @@ -0,0 +1,123 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +struct foo { + unsigned count; + unsigned char *__counted_by(count) bytes; +}; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT]], align 8 +// CHECK-NEXT: [[BYTES:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[BYTES]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 10 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[TMP15]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub i32 [[TMP16]], 10 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB5]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8 +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]] +// CHECK-NEXT: br i1 [[CMP16]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB19]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR22]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP17:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP27]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[BYTES36:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[TMP18]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[BYTES36]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[COUNT37:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[TMP19]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[SUB]], ptr [[COUNT37]], align 8 +// CHECK-NEXT: ret void +// +void foo(struct foo *f) { + f->bytes = f->bytes + 10; + f->count = f->count - 10; +} diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O0-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O0-disabled-checks.c new file mode 100644 index 0000000000000..6c8c1c526327c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O0-disabled-checks.c @@ -0,0 +1,1389 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -x objective-c -emit-llvm -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cb { + int count; + char* __counted_by(count) buf; +}; +void consume_cb(struct cb); +void consume_cb_arr(struct cb (*arr)[]); + +struct nested_cb { + struct cb nested; + int other; +}; + +struct nested_and_outer_cb { + struct cb nested; + int other; + int count; + char* __counted_by(count) buf; +}; + + +int get_int(void); + +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cb_with_other_data_arr(struct cb_with_other_data (*arr)[]); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cb([2 x i64] [[TMP2]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP2]] +// +struct cb return_cb(int new_count, char* __bidi_indexable new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cb* ptr, int new_count) { + *ptr = (struct cb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP3]], ptr [[COUNT1]], align 4 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr [2 x %struct.cb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP11]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF5:![0-9]+]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT18]], label %[[TRAP17:.*]], !prof [[PROF5]], !annotation [[META6]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT2:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[TMP4]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[COUNT2]], align 8 +// CHECK-NEXT: [[BUF3:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[TMP4]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[BUF3]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP5]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP6]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cb* ptr) { + *ptr = (struct cb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cb(struct cb* ptr, int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cb([2 x i64] [[TMP7]]) +// CHECK-NEXT: ret void +// +void call_arg_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP7]] +// +struct cb return_cb_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP7]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [2 x %struct.cb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF5]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT18]], label %[[TRAP17:.*]], !prof [[PROF5]], !annotation [[META6]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cb(char* __counted_by(new_count) new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[PROF5]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O0.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O0.c new file mode 100644 index 0000000000000..d4bec954dba0d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O0.c @@ -0,0 +1,3087 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -x objective-c -emit-llvm -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cb { + int count; + char* __counted_by(count) buf; +}; +void consume_cb(struct cb); +void consume_cb_arr(struct cb (*arr)[]); + +struct nested_cb { + struct cb nested; + int other; +}; + +struct nested_and_outer_cb { + struct cb nested; + int other; + int count; + char* __counted_by(count) buf; +}; + + +int get_int(void); + +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cb_with_other_data_arr(struct cb_with_other_data (*arr)[]); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT31:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT31]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF32:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[BUF32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cb([2 x i64] [[TMP4]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP4]] +// +struct cb return_cb(int new_count, char* __bidi_indexable new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cb* ptr, int new_count) { + *ptr = (struct cb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_CB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP58:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP61:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP80:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP89:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP41:%.*]] = icmp ule ptr [[WIDE_PTR_PTR33]], [[WIDE_PTR_UB40]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP41]], label %[[LAND_LHS_TRUE43:.*]], label %[[LAND_END77:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE43]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP54:%.*]] = icmp ule ptr [[WIDE_PTR_LB46]], [[WIDE_PTR_PTR49]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP54]], label %[[LAND_RHS56:.*]], label %[[LAND_END77]], !annotation [[META2]] +// CHECK: [[LAND_RHS56]]: +// CHECK-NEXT: [[CONV57:%.*]] = sext i32 [[TMP4]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP58]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB60:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP61]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR62:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR63:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR62]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR64:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB65:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR64]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR66:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB67:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR66]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST68:%.*]] = ptrtoint ptr [[WIDE_PTR_UB60]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST69:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR63]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB70:%.*]] = sub i64 [[SUB_PTR_LHS_CAST68]], [[SUB_PTR_RHS_CAST69]], !annotation [[META2]] +// CHECK-NEXT: [[CMP71:%.*]] = icmp sle i64 [[CONV57]], [[SUB_PTR_SUB70]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP71]], label %[[LAND_RHS73:.*]], label %[[LAND_END76:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS73]]: +// CHECK-NEXT: [[CMP74:%.*]] = icmp sle i32 0, [[TMP4]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END76]], !annotation [[META2]] +// CHECK: [[LAND_END76]]: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, %[[LAND_RHS56]] ], [ [[CMP74]], %[[LAND_RHS73]] ] +// CHECK-NEXT: br label %[[LAND_END77]], !annotation [[META2]] +// CHECK: [[LAND_END77]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE43]] ], [ false, %[[CONT]] ], [ [[TMP5]], %[[LAND_END76]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT79:.*]], label %[[TRAP78:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP78]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT79]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP4]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP7]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP80]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR81:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR82:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR81]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR83:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB84:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR83]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR85:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB86:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR85]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR82]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT87:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT87]], align 4 +// CHECK-NEXT: [[BUF88:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP89]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR90:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR91:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR90]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR92:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB93:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR92]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR94:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB95:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR94]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR91]], ptr [[BUF88]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: br i1 true, label %[[CONT39:.*]], label %[[TRAP38:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[COUNT40:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT40]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF41:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF41]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [2 x %struct.cb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB47]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB49]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = icmp ne ptr [[WIDE_PTR_PTR51]], null, !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT59:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_UB53]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_LB55]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT59]], label %[[TRAP58:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP58]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT59]]: +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef [[WIDE_PTR_PTR51]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[BUF]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP5]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END31:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP2]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS28:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS28]]: +// CHECK-NEXT: [[CMP29:%.*]] = icmp sle i32 0, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP29]], %[[LAND_RHS28]] ] +// CHECK-NEXT: br label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_END31]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP9]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT32:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT32]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP11]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF33:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[BUF33]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cb* ptr) { + *ptr = (struct cb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP9]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cb(struct cb* ptr, int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT31:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT31]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF32:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[BUF32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cb([2 x i64] [[TMP9]]) +// CHECK-NEXT: ret void +// +void call_arg_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP9]] +// +struct cb return_cb_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP9]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP10]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP9]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP10]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: br i1 true, label %[[CONT39:.*]], label %[[TRAP38:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[COUNT40:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT40]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP9]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF41:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF41]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr [2 x %struct.cb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB47]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB49]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ne ptr [[WIDE_PTR_PTR51]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT59:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ult ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_UB53]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP18]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[TMP19:%.*]] = icmp uge ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_LB55]], !annotation [[META7]] +// CHECK-NEXT: br i1 [[TMP19]], label %[[CONT59]], label %[[TRAP58:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP58]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT59]]: +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef [[WIDE_PTR_PTR51]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cb(char* __counted_by(new_count) new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP9]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP10]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META4]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META7]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O2-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O2-disabled-checks.c new file mode 100644 index 0000000000000..a24fca7e99224 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O2-disabled-checks.c @@ -0,0 +1,697 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cb { + int count; + char* __counted_by(count) buf; +}; +void consume_cb(struct cb); +void consume_cb_arr(struct cb (*arr)[]); + +struct nested_cb { + struct cb nested; + int other; +}; + +struct nested_and_outer_cb { + struct cb nested; + int other; + int count; + char* __counted_by(count) buf; +}; + + +int get_int(void); + +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cb_with_other_data_arr(struct cb_with_other_data (*arr)[]); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6:![0-9]+]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR8:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cb return_cb(int new_count, char* __bidi_indexable new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR7:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cb* ptr, int new_count) { + *ptr = (struct cb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 32)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA12:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA15:![0-9]+]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA16:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((4, 8)) [[PTR:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cb* ptr) { + *ptr = (struct cb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cb(struct cb* ptr, int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cb return_cb_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cb(char* __counted_by(new_count) new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA16]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0} +// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0} +// CHECK: [[META7]] = !{!"p1 omnipotent char", [[META8:![0-9]+]], i64 0} +// CHECK: [[META8]] = !{!"any pointer", [[META4]], i64 0} +// CHECK: [[TBAA9]] = !{[[META10:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META10]] = !{!"cb", [[META3]], i64 0, [[META7]], i64 8} +// CHECK: [[TBAA11]] = !{[[META10]], [[META7]], i64 8} +// CHECK: [[TBAA12]] = !{[[META13:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META13]] = !{!"cb_with_other_data", [[META3]], i64 0, [[META7]], i64 8, [[META3]], i64 16} +// CHECK: [[TBAA14]] = !{[[META13]], [[META7]], i64 8} +// CHECK: [[TBAA15]] = !{[[META13]], [[META3]], i64 16} +// CHECK: [[TBAA16]] = !{[[META4]], [[META4]], i64 0} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O2.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O2.c new file mode 100644 index 0000000000000..6c5d4b26b52c2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by-O2.c @@ -0,0 +1,1108 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -x objective-c -emit-llvm -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cb { + int count; + char* __counted_by(count) buf; +}; +void consume_cb(struct cb); +void consume_cb_arr(struct cb (*arr)[]); + +struct nested_cb { + struct cb nested; + int other; +}; + +struct nested_and_outer_cb { + struct cb nested; + int other; + int count; + char* __counted_by(count) buf; +}; + + +int get_int(void); + +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cb_with_other_data_arr(struct cb_with_other_data (*arr)[]); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cb return_cb(int new_count, char* __bidi_indexable new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[PTR]], i8 0, i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cb* ptr, int new_count) { + *ptr = (struct cb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp slt i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT_NOT98:%.*]] = or i1 [[CMP28]], [[CMP25]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[SPEC_SELECT_NOT98]], label %[[TRAP]], label %[[CONT78:.*]], !prof [[PROF13:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT78]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META14:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META14]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA15:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA17:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[PTR]], i8 0, i64 16, i1 false) +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA18:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA20:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA21:![0-9]+]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA22:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cb* ptr) { + *ptr = (struct cb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cb(struct cb* ptr, int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new; + new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + struct cb new = (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + consume_cb((struct cb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cb_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cb return_cb_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + return (struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used_from_cb(int new_count, + char* __counted_by(new_count) new_ptr) { + (void)(struct cb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cb(struct nested_cb* ptr, + char* __counted_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cb) { + .nested = (struct cb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META14]] +// CHECK-NEXT: unreachable, !annotation [[META14]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA17]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_cb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cb(char* __counted_by(new_count) new_ptr, + int new_count) { + struct cb arr[] = (struct cb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[PTR]], i8 0, i64 16, i1 false) +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cb(struct cb_with_other_data* ptr, + int new_count, + char* __counted_by(new_count) new_ptr) { + *ptr = (struct cb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA18]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA20]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA21]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA22]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"p1 omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"any pointer", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"omnipotent char", [[META7:![0-9]+]], i64 0} +// CHECK: [[META7]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META8]] = !{!"bounds-safety-generic", [[META9:![0-9]+]]} +// CHECK: [[META9]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[PROF10]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[TBAA11]] = !{[[META12:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META12]] = !{!"int", [[META6]], i64 0} +// CHECK: [[PROF13]] = !{!"branch_weights", i32 1048577, i32 1048575} +// CHECK: [[META14]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[TBAA15]] = !{[[META16:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META16]] = !{!"cb", [[META12]], i64 0, [[META4]], i64 8} +// CHECK: [[TBAA17]] = !{[[META16]], [[META4]], i64 8} +// CHECK: [[TBAA18]] = !{[[META19:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META19]] = !{!"cb_with_other_data", [[META12]], i64 0, [[META4]], i64 8, [[META12]], i64 16} +// CHECK: [[TBAA20]] = !{[[META19]], [[META4]], i64 8} +// CHECK: [[TBAA21]] = !{[[META19]], [[META12]], i64 16} +// CHECK: [[TBAA22]] = !{[[META6]], [[META6]], i64 0} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O0-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O0-disabled-checks.c new file mode 100644 index 0000000000000..af11a4dbb6786 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O0-disabled-checks.c @@ -0,0 +1,1531 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cbon { + int count; + char* __counted_by_or_null(count) buf; +}; +void consume_cbon(struct cbon); +void consume_cbon_arr(struct cbon (*arr)[]); + +struct nested_cbon { + struct cbon nested; + int other; +}; + +struct nested_and_outer_cbon { + struct cbon nested; + int other; + int count; + char* __counted_by_or_null(count) buf; +}; + +int get_int(void); + +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cbon_with_other_data_arr(struct cbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct cbon_with_other_data cbon; + struct no_attr_with_other_data no_cbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cbon([2 x i64] [[TMP2]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP2]] +// +struct cbon return_cbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cbon* ptr, int new_count) { + *ptr = (struct cbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP3]], ptr [[COUNT1]], align 4 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr [2 x %struct.cbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP11]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF5:![0-9]+]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT18]], label %[[TRAP17:.*]], !prof [[PROF5]], !annotation [[META6]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT2:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[TMP4]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[COUNT2]], align 8 +// CHECK-NEXT: [[BUF3:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[TMP4]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[BUF3]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ne ptr [[TMP6]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP5]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP6]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP13]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cbon* ptr) { + *ptr = (struct cbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[TMP3]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP8]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cbon(struct cbon* ptr, int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cbon([2 x i64] [[TMP11]]) +// CHECK-NEXT: ret void +// +void call_arg_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP11]] +// +struct cbon return_cbon_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[TMP3]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP8]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP12]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[TMP3]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP8]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP12]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP11]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr [2 x %struct.cbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP12]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP19]], label %[[BOUNDSCHECK_NOTNULL17:.*]], label %[[CONT19:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL17]]: +// CHECK-NEXT: [[TMP20:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP20]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF5]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP21:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP21]], label %[[CONT19]], label %[[TRAP18:.*]], !prof [[PROF5]], !annotation [[META6]] +// CHECK: [[TRAP18]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT19]]: +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cbon(char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[TMP3]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP8]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP12]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[PROF5]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O0.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O0.c new file mode 100644 index 0000000000000..207adfc402084 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O0.c @@ -0,0 +1,3579 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cbon { + int count; + char* __counted_by_or_null(count) buf; +}; +void consume_cbon(struct cbon); +void consume_cbon_arr(struct cbon (*arr)[]); + +struct nested_cbon { + struct cbon nested; + int other; +}; + +struct nested_and_outer_cbon { + struct cbon nested; + int other; + int count; + char* __counted_by_or_null(count) buf; +}; + +int get_int(void); + +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cbon_with_other_data_arr(struct cbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct cbon_with_other_data cbon; + struct no_attr_with_other_data no_cbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT38:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT38]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF39:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[BUF39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cbon([2 x i64] [[TMP5]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP5]] +// +struct cbon return_cbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cbon* ptr, int new_count) { + *ptr = (struct cbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP6]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP6]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_CBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP54:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP74:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP77:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP97:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP106:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP45]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP48:%.*]] = icmp ule ptr [[WIDE_PTR_PTR40]], [[WIDE_PTR_UB47]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP48]], label %[[LAND_LHS_TRUE50:.*]], label %[[LAND_END94:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE50]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP54]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP61:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR56]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP61]], label %[[LAND_RHS63:.*]], label %[[LAND_END94]], !annotation [[META2]] +// CHECK: [[LAND_RHS63]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB70:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR69]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL71:%.*]] = icmp ne ptr [[WIDE_PTR_PTR66]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL71]], label %[[LOR_RHS72:.*]], label %[[LOR_END93:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS72]]: +// CHECK-NEXT: [[CONV73:%.*]] = sext i32 [[TMP5]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP74]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR75:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB76:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR75]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP77]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR78:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR79:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR78]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR80:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB81:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR80]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR82:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB83:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR82]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST84:%.*]] = ptrtoint ptr [[WIDE_PTR_UB76]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST85:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR79]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB86:%.*]] = sub i64 [[SUB_PTR_LHS_CAST84]], [[SUB_PTR_RHS_CAST85]], !annotation [[META2]] +// CHECK-NEXT: [[CMP87:%.*]] = icmp sle i64 [[CONV73]], [[SUB_PTR_SUB86]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP87]], label %[[LAND_RHS89:.*]], label %[[LAND_END92:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS89]]: +// CHECK-NEXT: [[CMP90:%.*]] = icmp sle i32 0, [[TMP5]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END92]], !annotation [[META2]] +// CHECK: [[LAND_END92]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LOR_RHS72]] ], [ [[CMP90]], %[[LAND_RHS89]] ] +// CHECK-NEXT: br label %[[LOR_END93]], !annotation [[META2]] +// CHECK: [[LOR_END93]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ true, %[[LAND_RHS63]] ], [ [[TMP6]], %[[LAND_END92]] ] +// CHECK-NEXT: br label %[[LAND_END94]], !annotation [[META2]] +// CHECK: [[LAND_END94]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE50]] ], [ false, %[[CONT]] ], [ [[TMP7]], %[[LOR_END93]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT96:.*]], label %[[TRAP95:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP95]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT96]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP5]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP9]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP97]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR98:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP97]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR99:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR98]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR100:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP97]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB101:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR100]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR102:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP97]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB103:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR102]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR99]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT104:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT104]], align 4 +// CHECK-NEXT: [[BUF105:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP106]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR107:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP106]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR108:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR107]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR109:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP106]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB110:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR109]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR111:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP106]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB112:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR111]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR108]], ptr [[BUF105]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT45:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT45]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF46:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF46]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr [2 x %struct.cbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR50:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB52:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB54:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR53]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR50]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB52]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB54]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = icmp ne ptr [[WIDE_PTR_PTR56]], null, !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT64:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP14:%.*]] = icmp ult ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_UB58]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT62:.*]], label %[[TRAP61:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP61]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT62]]: +// CHECK-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_LB60]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT64]], label %[[TRAP63:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP63]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT64]]: +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef [[WIDE_PTR_PTR56]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP6]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ne ptr [[TMP5]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP5]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP12]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END38:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END38]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR18]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP2]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP33]], label %[[LAND_RHS35:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS35]]: +// CHECK-NEXT: [[CMP36:%.*]] = icmp sle i32 0, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP36]], %[[LAND_RHS35]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP13]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END38]], !annotation [[META2]] +// CHECK: [[LAND_END38]]: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP14]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT39:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT39]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP16]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF40:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR43:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB47:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR46]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR43]], ptr [[BUF40]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cbon* ptr) { + *ptr = (struct cbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP14]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cbon(struct cbon* ptr, int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT38:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT38]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF39:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[BUF39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_cbon([2 x i64] [[TMP14]]) +// CHECK-NEXT: ret void +// +void call_arg_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP14]] +// +struct cbon return_cbon_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP14]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP15]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_CBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP14]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_CBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP15]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_CBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT45:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT45]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP14]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF46:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF46]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr [2 x %struct.cbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR50:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB52:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB54:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR53]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR50]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB52]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB54]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = icmp ne ptr [[WIDE_PTR_PTR56]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP22]], label %[[BOUNDSCHECK_NOTNULL61:.*]], label %[[CONT65:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL61]]: +// CHECK-NEXT: [[TMP23:%.*]] = icmp ult ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_UB58]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP23]], label %[[CONT63:.*]], label %[[TRAP62:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP62]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT63]]: +// CHECK-NEXT: [[TMP24:%.*]] = icmp uge ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_LB60]], !annotation [[META7]] +// CHECK-NEXT: br i1 [[TMP24]], label %[[CONT65]], label %[[TRAP64:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP64]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT65]]: +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef [[WIDE_PTR_PTR56]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cbon(char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP14]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP15]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META4]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META7]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O2-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O2-disabled-checks.c new file mode 100644 index 0000000000000..9d2bfd2d15dc4 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O2-disabled-checks.c @@ -0,0 +1,696 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cbon { + int count; + char* __counted_by_or_null(count) buf; +}; +void consume_cbon(struct cbon); +void consume_cbon_arr(struct cbon (*arr)[]); + +struct nested_cbon { + struct cbon nested; + int other; +}; + +struct nested_and_outer_cbon { + struct cbon nested; + int other; + int count; + char* __counted_by_or_null(count) buf; +}; + +int get_int(void); + +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cbon_with_other_data_arr(struct cbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct cbon_with_other_data cbon; + struct no_attr_with_other_data no_cbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6:![0-9]+]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR8:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cbon return_cbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR7:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cbon* ptr, int new_count) { + *ptr = (struct cbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 32)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA12:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA15:![0-9]+]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA16:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((4, 8)) [[PTR:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cbon* ptr) { + *ptr = (struct cbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cbon( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cbon(struct cbon* ptr, int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cbon return_cbon_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cbon( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cbon( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cbon(char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cbon( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA16]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0} +// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0} +// CHECK: [[META7]] = !{!"p1 omnipotent char", [[META8:![0-9]+]], i64 0} +// CHECK: [[META8]] = !{!"any pointer", [[META4]], i64 0} +// CHECK: [[TBAA9]] = !{[[META10:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META10]] = !{!"cbon", [[META3]], i64 0, [[META7]], i64 8} +// CHECK: [[TBAA11]] = !{[[META10]], [[META7]], i64 8} +// CHECK: [[TBAA12]] = !{[[META13:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META13]] = !{!"cbon_with_other_data", [[META3]], i64 0, [[META7]], i64 8, [[META3]], i64 16} +// CHECK: [[TBAA14]] = !{[[META13]], [[META7]], i64 8} +// CHECK: [[TBAA15]] = !{[[META13]], [[META3]], i64 16} +// CHECK: [[TBAA16]] = !{[[META4]], [[META4]], i64 0} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O2.c b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O2.c new file mode 100644 index 0000000000000..745307120e457 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-counted_by_or_null-O2.c @@ -0,0 +1,1179 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct cbon { + int count; + char* __counted_by_or_null(count) buf; +}; +void consume_cbon(struct cbon); +void consume_cbon_arr(struct cbon (*arr)[]); + +struct nested_cbon { + struct cbon nested; + int other; +}; + +struct nested_and_outer_cbon { + struct cbon nested; + int other; + int count; + char* __counted_by_or_null(count) buf; +}; + +int get_int(void); + +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_cbon_with_other_data_arr(struct cbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct cbon_with_other_data cbon; + struct no_attr_with_other_data no_cbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct cbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS_CONT_CRIT_EDGE]]: +// CHECK-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: br label %[[CONT:.*]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// CHECK-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS_CONT_CRIT_EDGE]]: +// CHECK-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: br label %[[CONT:.*]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cbon return_cbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct cbon* ptr, int new_count) { + *ptr = (struct cbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT95:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META13:![0-9]+]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT95]], label %[[TRAP]], !prof [[PROF14:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT95]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_cbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META15:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META15]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA16:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA18:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct cbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA19:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA21:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA22:![0-9]+]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA23:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __counted_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 8, !tbaa [[TBAA16]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA18]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META24:![0-9]+]] +// CHECK-NEXT: [[CMP_NOT50:%.*]] = icmp slt i32 [[TMP0]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = select i1 [[DOTNOT]], i1 [[CMP_NOT50]], i1 false, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct cbon* ptr) { + *ptr = (struct cbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_cbon( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_cbon(struct cbon* ptr, int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(address_is_null) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT48:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT48]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new; + new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(address_is_null) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + struct cbon new = (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + consume_cbon((struct cbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_cbon_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct cbon return_cbon_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + return (struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(address_is_null) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used_from_cbon(int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + (void)(struct cbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_cbon( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT46]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_cbon( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT46]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_cbon(struct nested_cbon* ptr, + char* __counted_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_cbon) { + .nested = (struct cbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_cbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.cbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT65:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT65]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META15]] +// CHECK-NEXT: unreachable, !annotation [[META15]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA16]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA18]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_cbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_cbon(char* __counted_by_or_null(new_count) new_ptr, + int new_count) { + struct cbon arr[] = (struct cbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_cbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_cbon( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_cbon(struct cbon_with_other_data* ptr, + int new_count, + char* __counted_by_or_null(new_count) new_ptr) { + *ptr = (struct cbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA19]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA21]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA22]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct cbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_cbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA23]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_cbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .cbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"p1 omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"any pointer", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"omnipotent char", [[META7:![0-9]+]], i64 0} +// CHECK: [[META7]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META8]] = !{!"bounds-safety-generic", [[META9:![0-9]+]]} +// CHECK: [[META9]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[PROF10]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[TBAA11]] = !{[[META12:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META12]] = !{!"int", [[META6]], i64 0} +// CHECK: [[META13]] = !{[[META9]]} +// CHECK: [[PROF14]] = !{!"branch_weights", i32 -8192, i32 8191} +// CHECK: [[META15]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[TBAA16]] = !{[[META17:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META17]] = !{!"cbon", [[META12]], i64 0, [[META4]], i64 8} +// CHECK: [[TBAA18]] = !{[[META17]], [[META4]], i64 8} +// CHECK: [[TBAA19]] = !{[[META20:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META20]] = !{!"cbon_with_other_data", [[META12]], i64 0, [[META4]], i64 8, [[META12]], i64 16} +// CHECK: [[TBAA21]] = !{[[META20]], [[META4]], i64 8} +// CHECK: [[TBAA22]] = !{[[META20]], [[META12]], i64 16} +// CHECK: [[TBAA23]] = !{[[META6]], [[META6]], i64 0} +// CHECK: [[META24]] = !{!"bounds-safety-check-ptr-neq-null"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O0-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O0-disabled-checks.c new file mode 100644 index 0000000000000..f31acea8233e1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O0-disabled-checks.c @@ -0,0 +1,1484 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null %s -o - | FileCheck %s + +#include + +struct eb { + char* __ended_by(end) start; + char* end; +}; +void consume_eb(struct eb); +void consume_eb_arr(struct eb (*arr)[]); + +struct nested_eb { + struct eb nested; + int other; +}; + +struct nested_and_outer_eb { + struct eb nested; + int other; + char* __ended_by(end) start; + char* end; +}; + +int get_int(void); + +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_eb_with_other_data_arr(struct eb_with_other_data (*arr)[]); + +union TransparentUnion { + struct eb_with_other_data eb; + struct no_attr_with_other_data no_eb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct eb* ptr, + char* __bidi_indexable new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[START1:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START1]], align 8 +// CHECK-NEXT: [[END2:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(char* __bidi_indexable new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(char* __bidi_indexable new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_eb([2 x i64] [[TMP1]]) +// CHECK-NEXT: ret void +// +void call_arg(char* __bidi_indexable new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP1]] +// +struct eb return_eb(char* __bidi_indexable new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(char* __bidi_indexable new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[START1:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[START1]], align 8 +// CHECK-NEXT: [[END9:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[END9]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 40, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_and_outer_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0, + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[START1:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START1]], align 8 +// CHECK-NEXT: [[END2:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END2]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr [2 x %struct.eb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF5:![0-9]+]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT18]], label %[[TRAP17:.*]], !prof [[PROF5]], !annotation [[META6]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct eb_with_other_data* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently( + char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __ended_by source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_eb(struct eb* ptr, + char* __ended_by(new_end) new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[START1:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START1]], align 8 +// CHECK-NEXT: [[END2:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[END2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_eb([2 x i64] [[TMP12]]) +// CHECK-NEXT: ret void +// +void call_arg_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP12]] +// +struct eb return_eb_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr_from_eb(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[START8:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START8]], align 8 +// CHECK-NEXT: [[END9:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END9]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr [2 x %struct.eb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP12]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR13]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB15]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB17]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = icmp ne ptr [[WIDE_PTR_PTR19]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP19]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT25:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP20:%.*]] = icmp ult ptr [[WIDE_PTR_PTR19]], [[WIDE_PTR_UB21]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP20]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF5]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP21:%.*]] = icmp uge ptr [[WIDE_PTR_PTR19]], [[WIDE_PTR_LB23]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP21]], label %[[CONT25]], label %[[TRAP24:.*]], !prof [[PROF5]], !annotation [[META6]] +// CHECK: [[TRAP24]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT25]]: +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef [[WIDE_PTR_PTR19]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_eb(struct eb_with_other_data* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_eb(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP12]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_eb(char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP12]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_eb( + char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[PROF5]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O0.c b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O0.c new file mode 100644 index 0000000000000..8c7588eec20ef --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O0.c @@ -0,0 +1,2851 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name UNNAMED_TMP_ --version 5 +// REQUIRES: system-darwin + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null %s -o - | FileCheck %s + +#include + +struct eb { + char* __ended_by(end) start; + char* end; +}; +void consume_eb(struct eb); +void consume_eb_arr(struct eb (*arr)[]); + +struct nested_eb { + struct eb nested; + int other; +}; + +struct nested_and_outer_eb { + struct eb nested; + int other; + char* __ended_by(end) start; + char* end; +}; + +int get_int(void); + +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_eb_with_other_data_arr(struct eb_with_other_data (*arr)[]); + +union TransparentUnion { + struct eb_with_other_data eb; + struct no_attr_with_other_data no_eb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct eb* ptr, + char* __bidi_indexable new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START16:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR19]], ptr [[START16]], align 8 +// CHECK-NEXT: [[END24:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END24]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(char* __bidi_indexable new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(char* __bidi_indexable new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_eb([2 x i64] [[TMP3]]) +// CHECK-NEXT: ret void +// +void call_arg(char* __bidi_indexable new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP3]] +// +struct eb return_eb(char* __bidi_indexable new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(char* __bidi_indexable new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr getelementptr inbounds (i8, ptr null, i64 1), ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CMP1:%.*]] = icmp ule ptr null, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP1]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB18]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_LHS_TRUE20:.*]], label %[[LAND_END41:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE20]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR23]], [[TMP5]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP28]], label %[[LAND_RHS29:.*]], label %[[LAND_END41]], !annotation [[META2]] +// CHECK: [[LAND_RHS29]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP40:%.*]] = icmp ule ptr [[WIDE_PTR_LB32]], [[WIDE_PTR_PTR35]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END41]], !annotation [[META2]] +// CHECK: [[LAND_END41]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE20]] ], [ false, %[[CONT]] ], [ [[CMP40]], %[[LAND_RHS29]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT43:.*]], label %[[TRAP42:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP42]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT43]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR46]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP7]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[START51:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[START51]], align 8 +// CHECK-NEXT: [[END59:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END59]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 40, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_and_outer_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0, + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: br i1 true, label %[[CONT24:.*]], label %[[TRAP23:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP23]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT24]]: +// CHECK-NEXT: [[START25:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START25]], align 8 +// CHECK-NEXT: [[END26:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END26]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr [2 x %struct.eb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP28]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP28]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP28]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP28]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP28]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP28]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR30]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB32]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB34]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ne ptr [[WIDE_PTR_PTR36]], null, !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT44:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR36]], [[WIDE_PTR_UB38]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP11]], label %[[CONT42:.*]], label %[[TRAP41:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP41]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT42]]: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR36]], [[WIDE_PTR_LB40]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT44]], label %[[TRAP43:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP43]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT44]]: +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef [[WIDE_PTR_PTR36]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct eb_with_other_data* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr getelementptr inbounds (i8, ptr null, i64 1), ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CMP1:%.*]] = icmp ule ptr null, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP1]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP7]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP1]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[NEW_START]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP15]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[NEW_START]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently( + char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __ended_by source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNNAMED_TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_eb(struct eb* ptr, + char* __ended_by(new_end) new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNNAMED_TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START31:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[START31]], align 8 +// CHECK-NEXT: [[END39:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[END39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNNAMED_TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNNAMED_TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_eb([2 x i64] [[TMP13]]) +// CHECK-NEXT: ret void +// +void call_arg_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNNAMED_TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP13]] +// +struct eb return_eb_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNNAMED_TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr getelementptr inbounds (i8, ptr null, i64 1), ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CMP1:%.*]] = icmp ule ptr null, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP1]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr_from_eb(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNNAMED_TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP14]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_EB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNNAMED_TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_EB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP14]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNNAMED_TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_EB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: br i1 true, label %[[CONT46:.*]], label %[[TRAP45:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP45]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT46]]: +// CHECK-NEXT: [[START47:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START47]], align 8 +// CHECK-NEXT: [[END48:%.*]] = getelementptr inbounds nuw [[STRUCT_EB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END48]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [2 x %struct.eb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP50]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP50]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP50]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP50]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR52:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP50]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP50]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB56:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR55]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR52]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB54]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB56]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR58:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR57]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB60:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR59]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB62:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR61]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = icmp ne ptr [[WIDE_PTR_PTR58]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP20]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT66:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP21:%.*]] = icmp ult ptr [[WIDE_PTR_PTR58]], [[WIDE_PTR_UB60]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP21]], label %[[CONT64:.*]], label %[[TRAP63:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP63]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT64]]: +// CHECK-NEXT: [[TMP22:%.*]] = icmp uge ptr [[WIDE_PTR_PTR58]], [[WIDE_PTR_LB62]], !annotation [[META7]] +// CHECK-NEXT: br i1 [[TMP22]], label %[[CONT66]], label %[[TRAP65:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP65]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT66]]: +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef [[WIDE_PTR_PTR58]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNNAMED_TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP14]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_eb(struct eb_with_other_data* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_eb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_EB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr getelementptr inbounds (i8, ptr null, i64 1), ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr null, ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CMP1:%.*]] = icmp ule ptr null, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP1]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP7]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_eb(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNNAMED_TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_eb(char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNNAMED_TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[NEW_START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[NEW_END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[UNNAMED_TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP19:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[UNNAMED_TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[END]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_EB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_eb( + char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META4]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META7]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O2-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O2-disabled-checks.c new file mode 100644 index 0000000000000..722acc956056b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O2-disabled-checks.c @@ -0,0 +1,655 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name _NAMED_ --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -o - %s | FileCheck %s + +#include + +struct eb { + char* __ended_by(end) start; + char* end; +}; +void consume_eb(struct eb); +void consume_eb_arr(struct eb (*arr)[]); + +struct nested_eb { + struct eb nested; + int other; +}; + +struct nested_and_outer_eb { + struct eb nested; + int other; + char* __ended_by(end) start; + char* end; +}; + +int get_int(void); + +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_eb_with_other_data_arr(struct eb_with_other_data (*arr)[]); + +union TransparentUnion { + struct eb_with_other_data eb; + struct no_attr_with_other_data no_eb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct eb* ptr, + char* __bidi_indexable new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: ptr noundef readnone captures(none) [[NEW_START:%.*]], ptr noundef readnone captures(none) [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator(char* __bidi_indexable new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: ptr noundef readnone captures(none) [[NEW_START:%.*]], ptr noundef readnone captures(none) [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init(char* __bidi_indexable new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: tail call void @consume_eb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR8:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(char* __bidi_indexable new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct eb return_eb(char* __bidi_indexable new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: ptr noundef readnone captures(none) [[NEW_START:%.*]], ptr noundef readnone captures(none) [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used(char* __bidi_indexable new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 40)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 32 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_and_outer_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0, + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[ARR]], align 8, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct eb_with_other_data* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA12:![0-9]+]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA15:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA16:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently( + char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __ended_by source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_eb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_eb(struct eb* ptr, + char* __ended_by(new_end) new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_eb( +// CHECK-SAME: ptr noundef readnone captures(none) [[NEW_START:%.*]], ptr noundef readnone captures(none) [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_eb( +// CHECK-SAME: ptr noundef readnone captures(none) [[NEW_START:%.*]], ptr noundef readnone captures(none) [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_START]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: tail call void @consume_eb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_START]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct eb return_eb_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_eb( +// CHECK-SAME: ptr noundef readnone captures(none) [[NEW_START:%.*]], ptr noundef readnone captures(none) [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr_from_eb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr_from_eb(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_eb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_eb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR6]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[ARR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_eb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_eb(struct eb_with_other_data* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_eb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA7]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_eb(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_eb(char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA16]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_eb( + char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META3]] = !{!"p1 omnipotent char", [[META4:![0-9]+]], i64 0} +// CHECK: [[META4]] = !{!"any pointer", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TBAA7]] = !{[[META8:![0-9]+]], [[META8]], i64 0} +// CHECK: [[META8]] = !{!"int", [[META5]], i64 0} +// CHECK: [[TBAA9]] = !{[[META10:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META10]] = !{!"eb", [[META3]], i64 0, [[META3]], i64 8} +// CHECK: [[TBAA11]] = !{[[META10]], [[META3]], i64 8} +// CHECK: [[TBAA12]] = !{[[META13:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META13]] = !{!"eb_with_other_data", [[META3]], i64 0, [[META3]], i64 8, [[META8]], i64 16} +// CHECK: [[TBAA14]] = !{[[META13]], [[META3]], i64 8} +// CHECK: [[TBAA15]] = !{[[META13]], [[META8]], i64 16} +// CHECK: [[TBAA16]] = !{[[META5]], [[META5]], i64 0} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O2.c b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O2.c new file mode 100644 index 0000000000000..888b1baf2168c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-ended_by-O2.c @@ -0,0 +1,966 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name _NAMED_ --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null -o - %s | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -Wno-bounds-safety-init-list-partial-null %s -o - | FileCheck %s + +#include + +struct eb { + char* __ended_by(end) start; + char* end; +}; +void consume_eb(struct eb); +void consume_eb_arr(struct eb (*arr)[]); + +struct nested_eb { + struct eb nested; + int other; +}; + +struct nested_and_outer_eb { + struct eb nested; + int other; + char* __ended_by(end) start; + char* end; +}; + +int get_int(void); + +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_eb_with_other_data_arr(struct eb_with_other_data (*arr)[]); + +union TransparentUnion { + struct eb_with_other_data eb; + struct no_attr_with_other_data no_eb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF8:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct eb* ptr, + char* __bidi_indexable new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef readnone captures(address) [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator(char* __bidi_indexable new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef readnone captures(address) [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init(char* __bidi_indexable new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: tail call void @consume_eb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(char* __bidi_indexable new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct eb return_eb(char* __bidi_indexable new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef readnone captures(address) [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used(char* __bidi_indexable new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], inttoptr (i64 1 to ptr), !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT42:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT42]]: +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 32 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_eb* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct nested_and_outer_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0, + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META11:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[ARR]], align 8, !tbaa [[TBAA12:![0-9]+]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct eb_with_other_data* ptr, + char* __bidi_indexable new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], inttoptr (i64 1 to ptr), !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA15:![0-9]+]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA17:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA18:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_1_0_NEW_START_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_START]], i64 16 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], [[AGG_TEMP_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_START]], align 8 +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_START_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: store ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA19:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently( + char* __bidi_indexable new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __ended_by source ptr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_eb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_eb(struct eb* ptr, + char* __ended_by(new_end) new_start, char* new_end) { + *ptr = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_eb( +// CHECK-SAME: ptr noundef readnone captures(address) [[NEW_START:%.*]], ptr noundef readnone captures(address) [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new; + new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_eb( +// CHECK-SAME: ptr noundef readnone captures(address) [[NEW_START:%.*]], ptr noundef readnone captures(address) [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb new = (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_START]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: tail call void @consume_eb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + consume_eb((struct eb) { + .start = new_start, + .end = new_end + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_eb_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_START]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[TMP0]], 0 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[NEW_END]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP1]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct eb return_eb_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + return (struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_eb( +// CHECK-SAME: ptr noundef readnone captures(address) [[NEW_START:%.*]], ptr noundef readnone captures(address) [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + (void)(struct eb) { + .start = new_start, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr_from_eb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], inttoptr (i64 1 to ptr), !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr_from_eb(struct eb* ptr, char* new_end) { + *ptr = (struct eb) { + // Diagnostic emitted here but suppressed for this test case + .start = 0x0, + .end = new_end + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_eb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = {.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_eb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_eb(struct nested_eb* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct nested_eb) { + .nested = (struct eb){.start = new_start, .end = new_end }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.eb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META11]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[ARR]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_eb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_eb(char* __ended_by(new_end) new_start, char* new_end) { + struct eb arr[] = (struct eb[]) { + {.start = new_start, .end = new_end}, + {.start = 0x0, .end = 0x0} + }; + consume_eb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_eb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_eb(struct eb_with_other_data* ptr, + char* __ended_by(new_end) new_start, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_eb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[NEW_END]], inttoptr (i64 1 to ptr), !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof [[PROF8]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_eb(struct eb_with_other_data* ptr, + char* new_end) { + *ptr = (struct eb_with_other_data) { + .start = 0x0, + .end = new_end, // Generates a warning but it's suppressed in this test + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[END]], align 8, !tbaa [[TBAA17]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA18]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_eb(char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (struct eb_with_other_data) { + .start = new_start, + .end = new_end, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_eb( +// CHECK-SAME: ptr noundef [[NEW_START:%.*]], ptr noundef [[NEW_END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[CMP19_NOT:%.*]] = icmp ugt ptr [[NEW_START]], [[NEW_END]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP19_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: store ptr [[NEW_START]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[NEW_END]], ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA19]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_eb( + char* __ended_by(new_end) new_start, + char* new_end) { + receive_transparent_union( + (union TransparentUnion) { + .eb = { + .start = new_start, + .end = new_end, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"p1 omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"any pointer", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"omnipotent char", [[META7:![0-9]+]], i64 0} +// CHECK: [[META7]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[PROF8]] = !{!"branch_weights", i32 1, i32 1048575} +// CHECK: [[TBAA9]] = !{[[META10:![0-9]+]], [[META10]], i64 0} +// CHECK: [[META10]] = !{!"int", [[META6]], i64 0} +// CHECK: [[META11]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[TBAA12]] = !{[[META13:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META13]] = !{!"eb", [[META4]], i64 0, [[META4]], i64 8} +// CHECK: [[TBAA14]] = !{[[META13]], [[META4]], i64 8} +// CHECK: [[TBAA15]] = !{[[META16:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META16]] = !{!"eb_with_other_data", [[META4]], i64 0, [[META4]], i64 8, [[META10]], i64 16} +// CHECK: [[TBAA17]] = !{[[META16]], [[META4]], i64 8} +// CHECK: [[TBAA18]] = !{[[META16]], [[META10]], i64 16} +// CHECK: [[TBAA19]] = !{[[META6]], [[META6]], i64 0} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O0-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O0-disabled-checks.c new file mode 100644 index 0000000000000..cb39c257ea953 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O0-disabled-checks.c @@ -0,0 +1,1391 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect %s -o - | FileCheck %s + +#include + +struct sb { + int count; + char* __sized_by(count) buf; +}; +void consume_sb(struct sb); +void consume_sb_arr(struct sb (*arr)[]); + +struct nested_sb { + struct sb nested; + int other; +}; + +struct nested_and_outer_sb { + struct sb nested; + int other; + int count; + char* __sized_by(count) buf; +}; + + +int get_int(void); + +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sb_with_other_data_arr(struct sb_with_other_data (*arr)[]); + +union TransparentUnion { + struct sb_with_other_data sb; + struct no_attr_with_other_data no_sb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sb([2 x i64] [[TMP2]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP2]] +// +struct sb return_sb(int new_count, char* __bidi_indexable new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sb* ptr, int new_count) { + *ptr = (struct sb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP3]], ptr [[COUNT1]], align 4 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr [2 x %struct.sb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP11]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF5:![0-9]+]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT18]], label %[[TRAP17:.*]], !prof [[PROF5]], !annotation [[META6]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT2:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[TMP4]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[COUNT2]], align 8 +// CHECK-NEXT: [[BUF3:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[TMP4]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[BUF3]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP5]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP6]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sb* ptr) { + *ptr = (struct sb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sb(struct sb* ptr, int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sb([2 x i64] [[TMP7]]) +// CHECK-NEXT: ret void +// +void call_arg_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP7]] +// +struct sb return_sb_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP7]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [2 x %struct.sb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF5]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT18]], label %[[TRAP17:.*]], !prof [[PROF5]], !annotation [[META6]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sb(char* __sized_by(new_count) new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[PROF5]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O0.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O0.c new file mode 100644 index 0000000000000..ec781bba3a2a3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O0.c @@ -0,0 +1,3089 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect %s -o - | FileCheck %s + +#include + +struct sb { + int count; + char* __sized_by(count) buf; +}; +void consume_sb(struct sb); +void consume_sb_arr(struct sb (*arr)[]); + +struct nested_sb { + struct sb nested; + int other; +}; + +struct nested_and_outer_sb { + struct sb nested; + int other; + int count; + char* __sized_by(count) buf; +}; + + +int get_int(void); + +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sb_with_other_data_arr(struct sb_with_other_data (*arr)[]); + +union TransparentUnion { + struct sb_with_other_data sb; + struct no_attr_with_other_data no_sb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT31:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT31]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF32:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[BUF32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sb([2 x i64] [[TMP4]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP4]] +// +struct sb return_sb(int new_count, char* __bidi_indexable new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sb* ptr, int new_count) { + *ptr = (struct sb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_SB:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP58:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP61:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP80:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP89:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP41:%.*]] = icmp ule ptr [[WIDE_PTR_PTR33]], [[WIDE_PTR_UB40]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP41]], label %[[LAND_LHS_TRUE43:.*]], label %[[LAND_END77:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE43]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP54:%.*]] = icmp ule ptr [[WIDE_PTR_LB46]], [[WIDE_PTR_PTR49]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP54]], label %[[LAND_RHS56:.*]], label %[[LAND_END77]], !annotation [[META2]] +// CHECK: [[LAND_RHS56]]: +// CHECK-NEXT: [[CONV57:%.*]] = sext i32 [[TMP4]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP58]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB60:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP61]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR62:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR63:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR62]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR64:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB65:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR64]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR66:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB67:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR66]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST68:%.*]] = ptrtoint ptr [[WIDE_PTR_UB60]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST69:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR63]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB70:%.*]] = sub i64 [[SUB_PTR_LHS_CAST68]], [[SUB_PTR_RHS_CAST69]], !annotation [[META2]] +// CHECK-NEXT: [[CMP71:%.*]] = icmp sle i64 [[CONV57]], [[SUB_PTR_SUB70]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP71]], label %[[LAND_RHS73:.*]], label %[[LAND_END76:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS73]]: +// CHECK-NEXT: [[CMP74:%.*]] = icmp sle i32 0, [[TMP4]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END76]], !annotation [[META2]] +// CHECK: [[LAND_END76]]: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, %[[LAND_RHS56]] ], [ [[CMP74]], %[[LAND_RHS73]] ] +// CHECK-NEXT: br label %[[LAND_END77]], !annotation [[META2]] +// CHECK: [[LAND_END77]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE43]] ], [ false, %[[CONT]] ], [ [[TMP5]], %[[LAND_END76]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT79:.*]], label %[[TRAP78:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP78]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT79]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP4]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP7]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP80]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR81:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR82:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR81]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR83:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB84:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR83]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR85:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB86:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR85]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR82]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT87:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT87]], align 4 +// CHECK-NEXT: [[BUF88:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP89]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR90:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR91:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR90]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR92:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB93:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR92]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR94:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB95:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR94]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR91]], ptr [[BUF88]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: br i1 true, label %[[CONT39:.*]], label %[[TRAP38:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[COUNT40:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT40]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF41:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF41]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [2 x %struct.sb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB47]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB49]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = icmp ne ptr [[WIDE_PTR_PTR51]], null, !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT59:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_UB53]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_LB55]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT59]], label %[[TRAP58:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP58]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT59]]: +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef [[WIDE_PTR_PTR51]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[BUF]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP5]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END31:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP2]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS28:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS28]]: +// CHECK-NEXT: [[CMP29:%.*]] = icmp sle i32 0, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP29]], %[[LAND_RHS28]] ] +// CHECK-NEXT: br label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_END31]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP9]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT32:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT32]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP11]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF33:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[BUF33]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sb* ptr) { + *ptr = (struct sb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP9]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sb(struct sb* ptr, int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT31:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT31]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF32:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[BUF32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sb([2 x i64] [[TMP9]]) +// CHECK-NEXT: ret void +// +void call_arg_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP9]] +// +struct sb return_sb_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP9]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP10]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SB:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP9]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SB]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP10]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SB]], ptr [[ARR]], i64 1 +// CHECK-NEXT: br i1 true, label %[[CONT39:.*]], label %[[TRAP38:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[COUNT40:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT40]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP9]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF41:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF41]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr [2 x %struct.sb], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB47]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB49]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ne ptr [[WIDE_PTR_PTR51]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT59:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ult ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_UB53]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP18]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[TMP19:%.*]] = icmp uge ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_LB55]], !annotation [[META7]] +// CHECK-NEXT: br i1 [[TMP19]], label %[[CONT59]], label %[[TRAP58:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP58]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT59]]: +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef [[WIDE_PTR_PTR51]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sb(char* __sized_by(new_count) new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP7]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP9]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP10]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sb( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SB_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP1]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// CHECK: [[LAND_END30]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR33]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SB_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META4]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META7]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O2-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O2-disabled-checks.c new file mode 100644 index 0000000000000..3622b6bdfa225 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O2-disabled-checks.c @@ -0,0 +1,699 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct sb { + int count; + char* __sized_by(count) buf; +}; +void consume_sb(struct sb); +void consume_sb_arr(struct sb (*arr)[]); + +struct nested_sb { + struct sb nested; + int other; +}; + +struct nested_and_outer_sb { + struct sb nested; + int other; + int count; + char* __sized_by(count) buf; +}; + + +int get_int(void); + +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sb_with_other_data_arr(struct sb_with_other_data (*arr)[]); + +union TransparentUnion { + struct sb_with_other_data sb; + struct no_attr_with_other_data no_sb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6:![0-9]+]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR8:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sb return_sb(int new_count, char* __bidi_indexable new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR7:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sb* ptr, int new_count) { + *ptr = (struct sb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 32)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA12:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA15:![0-9]+]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA16:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((4, 8)) [[PTR:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sb* ptr) { + *ptr = (struct sb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sb(struct sb* ptr, int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sb return_sb_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sb(char* __sized_by(new_count) new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sb( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA16]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0} +// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0} +// CHECK: [[META7]] = !{!"p1 omnipotent char", [[META8:![0-9]+]], i64 0} +// CHECK: [[META8]] = !{!"any pointer", [[META4]], i64 0} +// CHECK: [[TBAA9]] = !{[[META10:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META10]] = !{!"sb", [[META3]], i64 0, [[META7]], i64 8} +// CHECK: [[TBAA11]] = !{[[META10]], [[META7]], i64 8} +// CHECK: [[TBAA12]] = !{[[META13:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META13]] = !{!"sb_with_other_data", [[META3]], i64 0, [[META7]], i64 8, [[META3]], i64 16} +// CHECK: [[TBAA14]] = !{[[META13]], [[META7]], i64 8} +// CHECK: [[TBAA15]] = !{[[META13]], [[META3]], i64 16} +// CHECK: [[TBAA16]] = !{[[META4]], [[META4]], i64 0} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O2.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O2.c new file mode 100644 index 0000000000000..2cc6666307da5 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by-O2.c @@ -0,0 +1,1110 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect %s -o - | FileCheck %s + +#include + +struct sb { + int count; + char* __sized_by(count) buf; +}; +void consume_sb(struct sb); +void consume_sb_arr(struct sb (*arr)[]); + +struct nested_sb { + struct sb nested; + int other; +}; + +struct nested_and_outer_sb { + struct sb nested; + int other; + int count; + char* __sized_by(count) buf; +}; + + +int get_int(void); + +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sb_with_other_data_arr(struct sb_with_other_data (*arr)[]); + +union TransparentUnion { + struct sb_with_other_data sb; + struct no_attr_with_other_data no_sb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// These are more readable due to the lack of BoundsSafetyPromotionExpr +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sb* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sb return_sb(int new_count, char* __bidi_indexable new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[PTR]], i8 0, i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sb* ptr, int new_count) { + *ptr = (struct sb) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp slt i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT_NOT98:%.*]] = or i1 [[CMP28]], [[CMP25]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[SPEC_SELECT_NOT98]], label %[[TRAP]], label %[[CONT78:.*]], !prof [[PROF13:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT78]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sb* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META14:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META14]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA15:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA17:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[PTR]], i8 0, i64 16, i1 false) +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sb_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA18:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA20:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA21:![0-9]+]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA22:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by source ptr +// +// These are less readable due to the BoundsSafetyPromotionExpr +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[TMP0]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sb* ptr) { + *ptr = (struct sb) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sb(struct sb* ptr, int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new; + new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + struct sb new = (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + consume_sb((struct sb) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sb_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sb return_sb_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + return (struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used_from_sb(int new_count, + char* __sized_by(new_count) new_ptr) { + (void)(struct sb) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sb(struct nested_sb* ptr, + char* __sized_by(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sb) { + .nested = (struct sb){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sb( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sb], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META14]] +// CHECK-NEXT: unreachable, !annotation [[META14]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA17]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_sb_arr(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sb(char* __sized_by(new_count) new_ptr, + int new_count) { + struct sb arr[] = (struct sb[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sb_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sb( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[NEW_COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[PTR]], i8 0, i64 16, i1 false) +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sb(struct sb_with_other_data* ptr, + int new_count, + char* __sized_by(new_count) new_ptr) { + *ptr = (struct sb_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA18]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA20]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA21]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sb_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sb( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA22]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sb(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sb = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"p1 omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"any pointer", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"omnipotent char", [[META7:![0-9]+]], i64 0} +// CHECK: [[META7]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META8]] = !{!"bounds-safety-generic", [[META9:![0-9]+]]} +// CHECK: [[META9]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[PROF10]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[TBAA11]] = !{[[META12:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META12]] = !{!"int", [[META6]], i64 0} +// CHECK: [[PROF13]] = !{!"branch_weights", i32 1048577, i32 1048575} +// CHECK: [[META14]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[TBAA15]] = !{[[META16:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META16]] = !{!"sb", [[META12]], i64 0, [[META4]], i64 8} +// CHECK: [[TBAA17]] = !{[[META16]], [[META4]], i64 8} +// CHECK: [[TBAA18]] = !{[[META19:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META19]] = !{!"sb_with_other_data", [[META12]], i64 0, [[META4]], i64 8, [[META12]], i64 16} +// CHECK: [[TBAA20]] = !{[[META19]], [[META4]], i64 8} +// CHECK: [[TBAA21]] = !{[[META19]], [[META12]], i64 16} +// CHECK: [[TBAA22]] = !{[[META6]], [[META6]], i64 0} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O0-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O0-disabled-checks.c new file mode 100644 index 0000000000000..c7c9bbfd834a6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O0-disabled-checks.c @@ -0,0 +1,1531 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect %s -o - | FileCheck %s + +#include + +struct sbon { + int count; + char* __sized_by_or_null(count) buf; +}; +void consume_sbon(struct sbon); +void consume_sbon_arr(struct sbon (*arr)[]); + +struct nested_sbon { + struct sbon nested; + int other; +}; + +struct nested_and_outer_sbon { + struct sbon nested; + int other; + int count; + char* __sized_by_or_null(count) buf; +}; + +int get_int(void); + +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sbon_with_other_data_arr(struct sbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct sbon_with_other_data sbon; + struct no_attr_with_other_data no_sbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sbon([2 x i64] [[TMP2]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP2]] +// +struct sbon return_sbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sbon* ptr, int new_count) { + *ptr = (struct sbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP3]], ptr [[COUNT1]], align 4 +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr [2 x %struct.sbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT18:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP11]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF5:![0-9]+]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT18]], label %[[TRAP17:.*]], !prof [[PROF5]], !annotation [[META6]] +// CHECK: [[TRAP17]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT18]]: +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT2:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[TMP4]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[COUNT2]], align 8 +// CHECK-NEXT: [[BUF3:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[TMP4]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[BUF3]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ne ptr [[TMP6]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP5]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP6]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP13]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sbon* ptr) { + *ptr = (struct sbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[TMP3]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP8]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sbon(struct sbon* ptr, int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sbon([2 x i64] [[TMP11]]) +// CHECK-NEXT: ret void +// +void call_arg_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP11]] +// +struct sbon return_sbon_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[TMP3]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP8]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP12]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[TMP3]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP8]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP12]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP11]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF2:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF2]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr [2 x %struct.sbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP12]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = icmp ne ptr [[WIDE_PTR_PTR12]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP19]], label %[[BOUNDSCHECK_NOTNULL17:.*]], label %[[CONT19:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL17]]: +// CHECK-NEXT: [[TMP20:%.*]] = icmp ult ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_UB14]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP20]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF5]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP21:%.*]] = icmp uge ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_LB16]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP21]], label %[[CONT19]], label %[[TRAP18:.*]], !prof [[PROF5]], !annotation [[META6]] +// CHECK: [[TRAP18]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT19]]: +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef [[WIDE_PTR_PTR12]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sbon(char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[TMP3]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META3]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP8]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP12]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[PROF5]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O0.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O0.c new file mode 100644 index 0000000000000..51fc7ca88bf06 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O0.c @@ -0,0 +1,3579 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect %s -o - | FileCheck %s + +#include + +struct sbon { + int count; + char* __sized_by_or_null(count) buf; +}; +void consume_sbon(struct sbon); +void consume_sbon_arr(struct sbon (*arr)[]); + +struct nested_sbon { + struct sbon nested; + int other; +}; + +struct nested_and_outer_sbon { + struct sbon nested; + int other; + int count; + char* __sized_by_or_null(count) buf; +}; + +int get_int(void); + +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sbon_with_other_data_arr(struct sbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct sbon_with_other_data sbon; + struct no_attr_with_other_data no_sbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT38:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT38]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF39:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[BUF39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sbon([2 x i64] [[TMP5]]) +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP5]] +// +struct sbon return_sbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sbon* ptr, int new_count) { + *ptr = (struct sbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP6]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP6]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_AND_OUTER_SBON:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP54:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP74:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP77:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP97:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP106:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP45]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP48:%.*]] = icmp ule ptr [[WIDE_PTR_PTR40]], [[WIDE_PTR_UB47]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP48]], label %[[LAND_LHS_TRUE50:.*]], label %[[LAND_END94:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE50]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP54]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP61:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR56]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP61]], label %[[LAND_RHS63:.*]], label %[[LAND_END94]], !annotation [[META2]] +// CHECK: [[LAND_RHS63]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB70:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR69]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL71:%.*]] = icmp ne ptr [[WIDE_PTR_PTR66]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL71]], label %[[LOR_RHS72:.*]], label %[[LOR_END93:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS72]]: +// CHECK-NEXT: [[CONV73:%.*]] = sext i32 [[TMP5]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP74]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR75:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB76:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR75]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP77]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR78:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR79:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR78]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR80:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB81:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR80]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR82:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB83:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR82]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST84:%.*]] = ptrtoint ptr [[WIDE_PTR_UB76]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST85:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR79]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB86:%.*]] = sub i64 [[SUB_PTR_LHS_CAST84]], [[SUB_PTR_RHS_CAST85]], !annotation [[META2]] +// CHECK-NEXT: [[CMP87:%.*]] = icmp sle i64 [[CONV73]], [[SUB_PTR_SUB86]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP87]], label %[[LAND_RHS89:.*]], label %[[LAND_END92:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS89]]: +// CHECK-NEXT: [[CMP90:%.*]] = icmp sle i32 0, [[TMP5]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END92]], !annotation [[META2]] +// CHECK: [[LAND_END92]]: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LOR_RHS72]] ], [ [[CMP90]], %[[LAND_RHS89]] ] +// CHECK-NEXT: br label %[[LOR_END93]], !annotation [[META2]] +// CHECK: [[LOR_END93]]: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ true, %[[LAND_RHS63]] ], [ [[TMP6]], %[[LAND_END92]] ] +// CHECK-NEXT: br label %[[LAND_END94]], !annotation [[META2]] +// CHECK: [[LAND_END94]]: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE50]] ], [ false, %[[CONT]] ], [ [[TMP7]], %[[LOR_END93]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP8]], label %[[CONT96:.*]], label %[[TRAP95:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP95]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT96]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP5]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP9]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP97]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR98:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP97]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR99:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR98]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR100:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP97]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB101:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR100]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR102:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP97]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB103:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR102]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR99]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[COUNT104:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT104]], align 4 +// CHECK-NEXT: [[BUF105:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_AND_OUTER_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP106]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR107:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP106]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR108:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR107]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR109:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP106]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB110:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR109]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR111:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP106]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB112:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR111]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR108]], ptr [[BUF105]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 32, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT45:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT45]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF46:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF46]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr [2 x %struct.sbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR50:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB52:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB54:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR53]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR50]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB52]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB54]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = icmp ne ptr [[WIDE_PTR_PTR56]], null, !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT64:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP14:%.*]] = icmp ult ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_UB58]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT62:.*]], label %[[TRAP61:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP61]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT62]]: +// CHECK-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_LB60]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT64]], label %[[TRAP63:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP63]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT64]]: +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef [[WIDE_PTR_PTR56]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP2]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP6]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ne ptr [[TMP5]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP5]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP12]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END38:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END38]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR18]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP2]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP33]], label %[[LAND_RHS35:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS35]]: +// CHECK-NEXT: [[CMP36:%.*]] = icmp sle i32 0, [[TMP2]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP36]], %[[LAND_RHS35]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP13]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END38]], !annotation [[META2]] +// CHECK: [[LAND_END38]]: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP14]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT39:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP2]], ptr [[COUNT39]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP16]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF40:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR43:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB47:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR46]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR43]], ptr [[BUF40]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sbon* ptr) { + *ptr = (struct sbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP14]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sbon(struct sbon* ptr, int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT38:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT38]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF39:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[BUF39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NEW]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 16, i1 false) +// CHECK-NEXT: ret void +// +void assign_operator_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[NEW]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NEW]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void local_var_init_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load [2 x i64], ptr [[DOTCOMPOUNDLITERAL]], align 8 +// CHECK-NEXT: call void @consume_sbon([2 x i64] [[TMP14]]) +// CHECK-NEXT: ret void +// +void call_arg_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[RETVAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret [2 x i64] [[TMP14]] +// +struct sbon return_sbon_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[AGG_TMP_ENSURED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: ret void +// +void construct_not_used_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP14]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP15]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_NESTED_SBON:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[NESTED:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[NESTED]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[NESTED]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP14]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[NESTED]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_NESTED_SBON]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP15]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON:%.*]], ptr [[ARR]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[ARR]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds [[STRUCT_SBON]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[COUNT45:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[COUNT45]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[ARRAYINIT_ELEMENT]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP14]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF46:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[ARRAYINIT_ELEMENT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF46]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr [2 x %struct.sbon], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARR]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR50:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB52:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB54:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR53]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR50]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB52]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB54]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = icmp ne ptr [[WIDE_PTR_PTR56]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP22]], label %[[BOUNDSCHECK_NOTNULL61:.*]], label %[[CONT65:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL61]]: +// CHECK-NEXT: [[TMP23:%.*]] = icmp ult ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_UB58]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP23]], label %[[CONT63:.*]], label %[[TRAP62:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP62]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT63]]: +// CHECK-NEXT: [[TMP24:%.*]] = icmp uge ptr [[WIDE_PTR_PTR56]], [[WIDE_PTR_LB60]], !annotation [[META7]] +// CHECK-NEXT: br i1 [[TMP24]], label %[[CONT65]], label %[[TRAP64:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP64]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT65]]: +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef [[WIDE_PTR_PTR56]]) +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sbon(char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META5]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP11]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP12]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP14]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP15]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon( +// CHECK-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP2]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_int() +// CHECK-NEXT: store i32 [[CALL]], ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP0]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[NEW_COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[NEW_PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[NEW_PTR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[NEW_COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[NEW_PTR]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS34]]: +// CHECK-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// CHECK-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// CHECK: [[LOR_END]]: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// CHECK-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// CHECK: [[LAND_END37]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA:%.*]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[NEW_PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON_WITH_OTHER_DATA]], ptr [[DOTCOMPOUNDLITERAL]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[DOTCOMPOUNDLITERAL]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[DOTCOMPOUNDLITERAL]], i64 24, i1 false) +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META4]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META7]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O2-disabled-checks.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O2-disabled-checks.c new file mode 100644 index 0000000000000..dd939241c44e8 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O2-disabled-checks.c @@ -0,0 +1,696 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct sbon { + int count; + char* __sized_by_or_null(count) buf; +}; +void consume_sbon(struct sbon); +void consume_sbon_arr(struct sbon (*arr)[]); + +struct nested_sbon { + struct sbon nested; + int other; +}; + +struct nested_and_outer_sbon { + struct sbon nested; + int other; + int count; + char* __sized_by_or_null(count) buf; +}; + +int get_int(void); + +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sbon_with_other_data_arr(struct sbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct sbon_with_other_data sbon; + struct no_attr_with_other_data no_sbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6:![0-9]+]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR8:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR6:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sbon return_sbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR7:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sbon* ptr, int new_count) { + *ptr = (struct sbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 32)) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA12:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA14:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA15:![0-9]+]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA16:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((4, 8)) [[PTR:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sbon* ptr) { + *ptr = (struct sbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sbon( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sbon(struct sbon* ptr, int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void assign_operator_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void local_var_init_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sbon return_sbon_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void construct_not_used_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sbon( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sbon( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR7]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sbon(char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sbon( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA6]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA14]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA15]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR4]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA16]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR8]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0} +// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TBAA6]] = !{[[META7:![0-9]+]], [[META7]], i64 0} +// CHECK: [[META7]] = !{!"p1 omnipotent char", [[META8:![0-9]+]], i64 0} +// CHECK: [[META8]] = !{!"any pointer", [[META4]], i64 0} +// CHECK: [[TBAA9]] = !{[[META10:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META10]] = !{!"sbon", [[META3]], i64 0, [[META7]], i64 8} +// CHECK: [[TBAA11]] = !{[[META10]], [[META7]], i64 8} +// CHECK: [[TBAA12]] = !{[[META13:![0-9]+]], [[META3]], i64 0} +// CHECK: [[META13]] = !{!"sbon_with_other_data", [[META3]], i64 0, [[META7]], i64 8, [[META3]], i64 16} +// CHECK: [[TBAA14]] = !{[[META13]], [[META7]], i64 8} +// CHECK: [[TBAA15]] = !{[[META13]], [[META3]], i64 16} +// CHECK: [[TBAA16]] = !{[[META4]], [[META4]], i64 0} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O2.c b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O2.c new file mode 100644 index 0000000000000..eda7200a7d5df --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound-literal-sized_by_or_null-O2.c @@ -0,0 +1,1179 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -Wno-bounds-attributes-init-list-side-effect -o - %s | FileCheck %s + +#include + +struct sbon { + int count; + char* __sized_by_or_null(count) buf; +}; +void consume_sbon(struct sbon); +void consume_sbon_arr(struct sbon (*arr)[]); + +struct nested_sbon { + struct sbon nested; + int other; +}; + +struct nested_and_outer_sbon { + struct sbon nested; + int other; + int count; + char* __sized_by_or_null(count) buf; +}; + +int get_int(void); + +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == + sizeof(struct no_attr_with_other_data), "size mismatch"); +void consume_sbon_with_other_data_arr(struct sbon_with_other_data (*arr)[]); + +union TransparentUnion { + struct sbon_with_other_data sbon; + struct no_attr_with_other_data no_sbon; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + +// NOTE: We currently don't test globals because those don't generate bounds +// checks. We currently rely on Sema checks to prevent all invalid externally +// counted pointers. This only works because global initializers must be +// constant evaluable. + +// ============================================================================= +// Tests with __bidi_indexable source ptr +// +// ============================================================================= + +// CHECK-LABEL: define dso_local void @assign_via_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11:![0-9]+]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr(struct sbon* ptr, int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init(int new_count, char* __bidi_indexable new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS_CONT_CRIT_EDGE]]: +// CHECK-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: br label %[[CONT:.*]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// CHECK-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: ret void +// +void call_arg(int new_count, char* __bidi_indexable new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS_CONT_CRIT_EDGE]]: +// CHECK-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: br label %[[CONT:.*]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sbon return_sbon(int new_count, char* __bidi_indexable new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used(int new_count, char* __bidi_indexable new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nullptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 16)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR5:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nullptr(struct sbon* ptr, int new_count) { + *ptr = (struct sbon) { + .count = new_count, + .buf = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2(struct nested_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v3( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT95:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META13:![0-9]+]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT95]], label %[[TRAP]], !prof [[PROF14:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT95]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 24 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_6_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v3(struct nested_and_outer_sbon* ptr, + char* __bidi_indexable new_ptr, + int new_count) { + *ptr = (struct nested_and_outer_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0, + .buf = new_ptr, + .count = new_count + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init( +// CHECK-SAME: ptr noundef readonly captures(none) [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META15:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META15]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA16:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA18:![0-9]+]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void array_of_struct_init(char* __bidi_indexable new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr(struct sbon_with_other_data* ptr, + int new_count, + char* __bidi_indexable new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA19:![0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA21:![0-9]+]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA22:![0-9]+]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA23:![0-9]+]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} + +// ============================================================================= +// Tests with __sized_by_or_null source ptr +// +// ============================================================================= +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_ptr( +// CHECK-SAME: ptr noundef captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 8, !tbaa [[TBAA16]] +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA18]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META24:![0-9]+]] +// CHECK-NEXT: [[CMP_NOT50:%.*]] = icmp slt i32 [[TMP0]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = select i1 [[DOTNOT]], i1 [[CMP_NOT50]], i1 false, !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_ptr(struct sbon* ptr) { + *ptr = (struct sbon) { + .count = ptr->count, + .buf = ptr->buf + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_from_sbon( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: ret void +// +void assign_via_ptr_from_sbon(struct sbon* ptr, int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_operator_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(address_is_null) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT48:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT48]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void assign_operator_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new; + new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + + +// CHECK-LABEL: define dso_local void @local_var_init_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(address_is_null) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void local_var_init_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + struct sbon new = (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[DOTCOMPOUNDLITERAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + consume_sbon((struct sbon) { + .count = new_count, + .buf = new_ptr + }); +} + +// CHECK-LABEL: define dso_local [2 x i64] @return_sbon_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[RETVAL_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[NEW_COUNT]] to i64 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[RETVAL_SROA_0_0_INSERT_EXT]], 0 +// CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[NEW_PTR]] to i64 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// CHECK-NEXT: ret [2 x i64] [[DOTFCA_1_INSERT]] +// +struct sbon return_sbon_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + return (struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @construct_not_used_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(address_is_null) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void construct_not_used_from_sbon(int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + (void)(struct sbon) { + .count = new_count, + .buf = new_ptr + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_from_sbon( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT46]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = {.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_nested_v2_from_sbon( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT46]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_nested_v2_from_sbon(struct nested_sbon* ptr, + char* __sized_by_or_null(new_count)new_ptr, + int new_count) { + *ptr = (struct nested_sbon) { + .nested = (struct sbon){.buf = new_ptr, .count = new_count }, + .other = 0x0 + }; +} + +// CHECK-LABEL: define dso_local void @array_of_struct_init_from_sbon( +// CHECK-SAME: ptr noundef [[NEW_PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [2 x %struct.sbon], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT65:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT65]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META15]] +// CHECK-NEXT: unreachable, !annotation [[META15]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[ARR]], align 8, !tbaa [[TBAA16]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[BUF]], align 8, !tbaa [[TBAA18]] +// CHECK-NEXT: [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) [[ARRAYINIT_ELEMENT]], i8 0, i64 16, i1 false) +// CHECK-NEXT: call void @consume_sbon_arr(ptr noundef nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 32, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void array_of_struct_init_from_sbon(char* __sized_by_or_null(new_count) new_ptr, + int new_count) { + struct sbon arr[] = (struct sbon[]) { + {.count = new_count, .buf = new_ptr}, + {.count = 0x0, .buf = 0x0} + }; + consume_sbon_arr(&arr); +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_from_sbon( +// CHECK-SAME: ptr noundef writeonly captures(none) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[NEW_PTR]], null, !annotation [[META24]] +// CHECK-NEXT: [[CMP_NOT46:%.*]] = icmp slt i32 [[NEW_COUNT]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT46]], [[DOTNOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr [[NEW_PTR]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[PTR:%.*]], i32 noundef [[NEW_COUNT:%.*]], ptr noundef readnone captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call i32 @get_int() #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[PTR]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0__SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: store ptr null, ptr [[DOTCOMPOUNDLITERAL_SROA_3_0__SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: store i32 [[CALL]], ptr [[DOTCOMPOUNDLITERAL_SROA_4_0__SROA_IDX]], align 8, !tbaa [[TBAA11]] +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0__SROA_IDX]], align 4 +// CHECK-NEXT: ret void +// +void assign_via_ptr_other_data_side_effect_zero_ptr_from_sbon(struct sbon_with_other_data* ptr, + int new_count, + char* __sized_by_or_null(new_count) new_ptr) { + *ptr = (struct sbon_with_other_data) { + .count = new_count, + .buf = 0x0, + // Side effect. Generates a warning but it is suppressed for this test + .other = get_int() + }; +} + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[AGG_TMP]], align 8, !tbaa [[TBAA19]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[BUF]], align 8, !tbaa [[TBAA21]] +// CHECK-NEXT: [[OTHER:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[OTHER]], align 8, !tbaa [[TBAA22]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[TMP1]], align 4 +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[AGG_TMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (struct sbon_with_other_data) { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + ); +} + + +// CHECK-LABEL: define dso_local void @call_arg_transparent_union_untransparently_from_sbon( +// CHECK-SAME: i32 noundef [[NEW_COUNT:%.*]], ptr noundef readonly captures(none) [[NEW_PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[UNION_TRANSPARENTUNION:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[NEW_PTR]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_NEW_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[NEW_PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_NEW_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[NEW_COUNT]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[NEW_COUNT]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: store i32 [[NEW_COUNT]], ptr [[BYVAL_TEMP]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_2_0_BYVAL_TEMP_SROA_IDX]], align 4 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 8 +// CHECK-NEXT: store ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], ptr [[DOTCOMPOUNDLITERAL_SROA_3_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_4_0_BYVAL_TEMP_SROA_IDX]], align 8 +// CHECK-NEXT: [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BYVAL_TEMP]], i64 20 +// CHECK-NEXT: store i32 0, ptr [[DOTCOMPOUNDLITERAL_SROA_5_0_BYVAL_TEMP_SROA_IDX]], align 4, !tbaa [[TBAA23]] +// CHECK-NEXT: call void @receive_transparent_union(ptr noundef nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[BYVAL_TEMP]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void call_arg_transparent_union_untransparently_from_sbon(int new_count, + char* __bidi_indexable new_ptr) { + receive_transparent_union( + (union TransparentUnion) { + .sbon = { + .count = new_count, + .buf = new_ptr, + .other = 0x0 + } + } + ); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"p1 omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"any pointer", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"omnipotent char", [[META7:![0-9]+]], i64 0} +// CHECK: [[META7]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META8]] = !{!"bounds-safety-generic", [[META9:![0-9]+]]} +// CHECK: [[META9]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[PROF10]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[TBAA11]] = !{[[META12:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META12]] = !{!"int", [[META6]], i64 0} +// CHECK: [[META13]] = !{[[META9]]} +// CHECK: [[PROF14]] = !{!"branch_weights", i32 -8192, i32 8191} +// CHECK: [[META15]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[TBAA16]] = !{[[META17:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META17]] = !{!"sbon", [[META12]], i64 0, [[META4]], i64 8} +// CHECK: [[TBAA18]] = !{[[META17]], [[META4]], i64 8} +// CHECK: [[TBAA19]] = !{[[META20:![0-9]+]], [[META12]], i64 0} +// CHECK: [[META20]] = !{!"sbon_with_other_data", [[META12]], i64 0, [[META4]], i64 8, [[META12]], i64 16} +// CHECK: [[TBAA21]] = !{[[META20]], [[META4]], i64 8} +// CHECK: [[TBAA22]] = !{[[META20]], [[META12]], i64 16} +// CHECK: [[TBAA23]] = !{[[META6]], [[META6]], i64 0} +// CHECK: [[META24]] = !{!"bounds-safety-check-ptr-neq-null"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/compound_expr_on_dbp.c b/clang/test/BoundsSafety/CodeGen/compound_expr_on_dbp.c new file mode 100644 index 0000000000000..b686ac648f436 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/compound_expr_on_dbp.c @@ -0,0 +1,223 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int foo(); + +// CHECK-LABEL: @TestCount( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR:%.*]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call i32 (...) @foo() +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[TMP5]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[CALL]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP6]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP12]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub i32 [[TMP14]], 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP26]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: [[IDX_EXT28:%.*]] = sext i32 [[CALL]] to i64 +// CHECK-NEXT: [[ADD_PTR29:%.*]] = getelementptr inbounds i32, ptr [[TMP16]], i64 [[IDX_EXT28]] +// CHECK-NEXT: store ptr [[ADD_PTR29]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[SUB]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: ret void +// +void TestCount(int *__counted_by(len) ptr, unsigned len) { + ptr += foo(); + len = len - 4; +} + +// CHECK-LABEL: @TestRange( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[START:%.*]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END:%.*]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP13]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR5]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TMP21:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP21]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: [[CMP24:%.*]] = icmp ule ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_PTR19]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP27:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP24]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP27]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR27]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP28]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void TestRange(int *__ended_by(end) start, char *end) { + end = end; + start += 1; +} diff --git a/clang/test/BoundsSafety/CodeGen/const-bound-ptr-conversion.c b/clang/test/BoundsSafety/CodeGen/const-bound-ptr-conversion.c new file mode 100644 index 0000000000000..3e94635f98fc3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/const-bound-ptr-conversion.c @@ -0,0 +1,56 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_O0 +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_O2 +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_O0 +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK_O2 +// CHECK_O0-LABEL: @foo( +// CHECK_O0: {{.*}}: +// CHECK_O0: [[ARRAY:%.*]] = alloca [10 x i32], align 16 +// CHECK_O0: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK_O0: [[LOCAL:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_O0: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK_O0: call void @llvm.memset.p0.i64(ptr align 16 [[ARRAY]], i8 0, i64 40, i1 false) +// CHECK_O0: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARRAY]], i64 0, i64 0 +// CHECK_O0: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK_O0: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK_O0: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK_O0: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK_O0: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK_O0: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK_O0: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK_O0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[LOCAL]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK_O0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[LOCAL]], i64 24, i1 false) +// CHECK_O0: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK_O0: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK_O0: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK_O0: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK_O0: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK_O0: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK_O0: [[TMP3:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK_O0: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK_O0: trap: +// CHECK_O0: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK_O0: unreachable +// CHECK_O0: cont: +// CHECK_O0: [[TMP4:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK_O0: br i1 [[TMP4]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK_O0: trap1: +// CHECK_O0: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK_O0: unreachable +// CHECK_O0: cont2: +// CHECK_O0: [[TMP5:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK_O0: ret i32 [[TMP5]] +// +// CHECK_O2-LABEL: @foo( +// CHECK_O2: {{.*}}: +// CHECK_O2: ret i32 0 +// +int foo(void) { + int array[10] = {0}; + int *ptr = array; + const int *local = ptr; + + return *local; +} diff --git a/clang/test/BoundsSafety/CodeGen/constant-eval-count-static-init.c b/clang/test/BoundsSafety/CodeGen/constant-eval-count-static-init.c new file mode 100644 index 0000000000000..44ba3b6fde46f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/constant-eval-count-static-init.c @@ -0,0 +1,59 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +typedef struct { + unsigned int *__sized_by(length) data; + int length; +} Item; + +static unsigned int _oidRsa[] = { 0, 1, 2 }; +const Item oidRsa = { _oidRsa, sizeof(_oidRsa)}; + +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca [[STRUCT_ITEM:%.*]], align 8 +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 @oidRsa, i64 16, i1 false) +// CHECK-NEXT: [[LENGTH:%.*]] = getelementptr inbounds nuw [[STRUCT_ITEM]], ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LENGTH]], align 8 +// CHECK-NEXT: [[DATA:%.*]] = getelementptr inbounds nuw [[STRUCT_ITEM]], ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[DATA]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 3 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ult ptr [[TMP5]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[TMP5]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont3: +// CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP5]], align 4 +// CHECK-NEXT: ret i32 [[TMP8]] +// +int main() { + return oidRsa.data[3]; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/constant-forge-ptr-expr.c b/clang/test/BoundsSafety/CodeGen/constant-forge-ptr-expr.c new file mode 100644 index 0000000000000..08389d0fd9d39 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/constant-forge-ptr-expr.c @@ -0,0 +1,130 @@ +// REQUIRES: x86-registered-target + +// RUN: %clang_cc1 %s -O0 -fbounds-safety -triple x86_64 -S -o - | FileCheck --check-prefixes CHECK,CHECKBS %s +// RUN: %clang_cc1 %s -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -S -o - | FileCheck --check-prefixes CHECK,CHECKBS %s +// RUN: %clang_cc1 %s -O0 -fexperimental-bounds-safety-attributes -x c -triple x86_64 -S -o - | FileCheck --check-prefixes CHECK %s +// RUN: %clang_cc1 %s -O0 -fexperimental-bounds-safety-attributes -x c++ -triple x86_64 -S -o - | FileCheck --check-prefixes CHECK %s +// RUN: %clang_cc1 %s -O0 -fexperimental-bounds-safety-attributes -x objective-c -triple x86_64 -S -o - | FileCheck --check-prefixes CHECK %s +// RUN: %clang_cc1 %s -O0 -fexperimental-bounds-safety-attributes -x objective-c++ -triple x86_64 -S -o - | FileCheck --check-prefixes CHECK %s + +#include + +/* Define to nothing in attribute-only mode */ +#ifndef __bidi_indexable +#define __bidi_indexable +#endif + +struct Foo { + void *dummy; + int buf[18]; +}; +_Static_assert(sizeof(struct Foo) == 80, "sizeof(struct Foo) should be 80"); + +int arr[2]; +int pad[2]; +struct Foo foo[8]; + +int *ptrSingle = __unsafe_forge_single(int *, 0); +// CHECK-LABEL: ptrSingle: +// CHECK: .quad 0 +// CHECK: .size ptrSingle, 8 + +/* XXX: won't work until __unsafe_forge_single knows the size it's forging +int *__indexable ptrIndexable = __unsafe_forge_single(int *, 0); +// TODO-CHECK-LABEL: ptrIndexable: +// TODO-CHECK: .zero 16 +// TODO-CHECK: .size ptrIndexable, 16 + */ + +int *__bidi_indexable ptrBidiIndexable = + __unsafe_forge_bidi_indexable(int *, arr + 2, 14); +// CHECKBS-LABEL: ptrBidiIndexable: +// CHECKBS: .quad arr+8 +// CHECKBS: .quad arr+8+14 +// CHECKBS: .quad arr+8 + +int *__bidi_indexable ptrBidiIndexable2 = __unsafe_forge_single(int *, &arr[0]); +// CHECKBS-LABEL: ptrBidiIndexable2: +// CHECKBS: .quad arr +// CHECKBS: .quad arr+4 +// CHECKBS: .quad arr + +int *__bidi_indexable ptrBidiIndexable3 = __unsafe_forge_single(int *, 0x1234); +// CHECKBS-LABEL: ptrBidiIndexable3: +// CHECKBS: .quad 4660 +// CHECKBS: .quad 4664 +// CHECKBS: .quad 4660 + +int *__bidi_indexable ptrBidiIndexable4 = __unsafe_forge_bidi_indexable(int *, 8000, 16); +// CHECKBS-LABEL: ptrBidiIndexable4: +// CHECKBS: .quad 8000 +// CHECKBS: .quad 8016 +// CHECKBS: .quad 8000 + +// (4 * 80) + 8 + 4 * 9 = 364 +int *__bidi_indexable ptrBidiIndexable5 = __unsafe_forge_bidi_indexable(int *, &foo[4].buf[9], 16); +// CHECKBS-LABEL: ptrBidiIndexable5: +// CHECKBS: .quad foo+364 +// CHECKBS: .quad foo+364+16 +// CHECKBS: .quad foo+364 +// CHECKBS: .size ptrBidiIndexable5, 24 + +#define FOO_PLUS_364 (&foo[4].buf[9]) +#define FOO_PLUS_364_LEN_16 __unsafe_forge_bidi_indexable(int *, FOO_PLUS_364, 16) +#define FOO_PLUS_380_LEN_0 (FOO_PLUS_364_LEN_16 + 4) +int *__bidi_indexable ptrBidiIndexable6 = + __unsafe_forge_bidi_indexable(int *, FOO_PLUS_380_LEN_0, 8) + 4; +// CHECKBS-LABEL: ptrBidiIndexable6: +// CHECKBS: .quad foo+380+16 +// CHECKBS: .quad foo+380+8 +// CHECKBS: .quad foo+380 +// CHECKBS: .size ptrBidiIndexable6, 24 + +int *ptrSingle2 = __unsafe_forge_bidi_indexable(int *, 0, 0); +// CHECKBS-LABEL: ptrSingle2: +// CHECKBS: .quad 0 +// CHECKBS: .size ptrSingle2, 8 + +int *ptrSingle3 = __unsafe_forge_bidi_indexable(int *, arr + 2, 4); +// CHECKBS-LABEL: ptrSingle3: +// CHECKBS: .quad arr+8 +// CHECKBS: .size ptrSingle3, 8 + +/* This is invalid; converting a pointer with a size of 3 to an int *__single + never works. */ +// int *ptrSingle4 = __unsafe_forge_bidi_indexable(int *, arr + 2, 3); + +int *ptrSingle5 = __unsafe_forge_single(int *, __unsafe_forge_bidi_indexable(int *, arr + 2, 3) + 4); +// CHECKBS-LABEL: ptrSingle5: +// CHECKBS: .quad arr+24 +// CHECKBS: .size ptrSingle5, 8 + +int *ptrSingle6 = __unsafe_forge_single(int *, &arr[0]); +// CHECK-LABEL: ptrSingle6: +// CHECK: .quad arr +// CHECK: .size ptrSingle6, 8 + +int *ptrSingle7 = __unsafe_forge_single(int *, 1337); +// CHECK-LABEL: ptrSingle7: +// CHECK: .quad 1337 +// CHECK: .size ptrSingle7, 8 + +int *ptrSingle8 = __unsafe_forge_single(int *, __unsafe_forge_single(int *, arr + 2)); +// CHECK-LABEL: ptrSingle8: +// CHECK: .quad arr+8 +// CHECK: .size ptrSingle8, 8 + +int *__terminated_by(5) ptrTerminated = __unsafe_forge_terminated_by(int *, arr, 5); +// CHECK-LABEL: ptrTerminated: +// CHECK: .quad arr +// CHECK: .size ptrTerminated, 8 + +char *__null_terminated ptrNt1 = __unsafe_forge_null_terminated(char *, __unsafe_forge_bidi_indexable(char *, arr+12, 10)); +// CHECKBS-LABEL: ptrNt1: +// CHECKBS: .quad arr+48 +// CHECKBS: .size ptrNt1, 8 + +char *__null_terminated ptrNt2 = __unsafe_forge_null_terminated(char *, __unsafe_forge_single(char *, arr+2)); +// CHECK-LABEL: ptrNt2: +// CHECK: .quad arr+8 +// CHECK: .size ptrNt2, 8 diff --git a/clang/test/BoundsSafety/CodeGen/count-attr-fields-assign-checks.c b/clang/test/BoundsSafety/CodeGen/count-attr-fields-assign-checks.c new file mode 100644 index 0000000000000..0415b8e81bad1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-attr-fields-assign-checks.c @@ -0,0 +1,26 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null + +#include + +struct S { + int *__counted_by(count) ptr; + int count; +}; + +int assign_count_only_never_succeeds(struct S *s) { + s->count += 1; + s->ptr = s->ptr; +} + +int assign_count_only(struct S *s) { + s->count -= 1; + s->ptr = s->ptr; +} + +int assign_ptr(struct S *s) { + s->count = s->count - 1; + s->ptr = s->ptr + 1; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-attributed-type-attribute-only-mode-O0.c b/clang/test/BoundsSafety/CodeGen/count-attributed-type-attribute-only-mode-O0.c new file mode 100644 index 0000000000000..103705a4e2205 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-attributed-type-attribute-only-mode-O0.c @@ -0,0 +1,140 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -x c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c++ -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c++ -emit-llvm %s -o - | FileCheck %s + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// CHECK-LABEL: define dso_local i32 @fn_deref( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: ret i32 [[TMP1]] +// +int fn_deref(int *__counted_by(count) p, int count) { + return *p; +} + +// CHECK-LABEL: define dso_local i32 @fn_subscript( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP1]], 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret i32 [[TMP2]] +// +int fn_subscript(int *__counted_by(count) p, int count) { + return p[count - 1]; +} + +// CHECK-LABEL: define dso_local ptr @fn_assign( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP0]], 42 +// CHECK-NEXT: store i32 [[SUB]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 42 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP2]] +// +int *fn_assign(int *__counted_by(count) p, int count) { + count = count - 42; + p = p + 42; + return p; +} + +struct bar { + int *__sized_by(size) q; + int size; +}; + +// CHECK-LABEL: define dso_local i32 @struct_deref( +// CHECK-SAME: ptr noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 [[TMP2]] +// +int struct_deref(struct bar *b) { + return *b->q; +} + +// CHECK-LABEL: define dso_local i32 @struct_subscript( +// CHECK-SAME: ptr noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[SIZE:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR]], ptr [[TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[SIZE]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP3]], 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int struct_subscript(struct bar *b) { + return b->q[b->size - 1]; +} + +// CHECK-LABEL: define dso_local void @struct_assign( +// CHECK-SAME: ptr noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[SIZE:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZE]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP1]], 42 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[SIZE1:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR]], ptr [[TMP2]], i32 0, i32 1 +// CHECK-NEXT: store i32 [[SUB]], ptr [[SIZE1]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP4]], i64 42 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q2:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR]], ptr [[TMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[Q2]], align 8 +// CHECK-NEXT: ret void +// +void struct_assign(struct bar *b) { + b->size = b->size - 42; + b->q = b->q + 42; +} + +#ifdef __cplusplus +} +#endif diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign-2-bigger-length-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign-2-bigger-length-O2.c new file mode 100644 index 0000000000000..b94ae43ebc84c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign-2-bigger-length-O2.c @@ -0,0 +1,45 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3 + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// Executable version of byte-count-attr-fields-assign.c + +#include + +struct S { + int *__sized_by(l) bp; + int *bp2 __sized_by(l+4); + int l; +}; + +// CHECK-LABEL: define dso_local void @TestFail +// CHECK-SAME: () local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// +void TestFail() { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10 * sizeof(int); + s.bp = &arr[1]; + s.l = n; // run-time trap - s.l cannot be bigger than element count of &arr[1] (9) and s.1 + 1 cannot be bigger than element count of arr (10) + s.bp2 = arr; +} + +// CHECK-LABEL: define dso_local void @TestOK +// CHECK-SAME: () local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.l = 9 * sizeof(int); + s.bp2 = arr; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign-O2.c new file mode 100644 index 0000000000000..13dbae609b6a9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign-O2.c @@ -0,0 +1,38 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK + +#include + +struct S { + int *__sized_by(l) bp; + int *bp2 __sized_by(l+4); + int l; +}; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9 * sizeof(int); // no trap +} + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{.*}} +// CHECK-NEXT: unreachable +// +void bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10 * sizeof(int); + s.bp = &arr[1]; + s.bp2 = arr; + s.l = n; // trap s.l > bounds of (s.bp) +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign.c new file mode 100644 index 0000000000000..e3aabd408bcfe --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/byte-count-attr-fields-assign.c @@ -0,0 +1,223 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct S { + int *__sized_by(l) bp; + int *bp2 __sized_by(l+4); + int l; +}; + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[N:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP24:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP46:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP79:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: br i1 true, label [[CONT5:%.*]], label [[TRAP4:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap4: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont5: +// CHECK-NEXT: [[BP:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BP]], align 8 +// CHECK-NEXT: [[BP2:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR8]], ptr [[BP2]], align 8 +// CHECK-NEXT: [[L:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store i32 0, ptr [[L]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[S]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP6]], i8 0, i64 4, i1 false) +// CHECK-NEXT: store i32 40, ptr [[N]], align 4 +// CHECK-NEXT: br label [[HERE:%.*]] +// CHECK: here: +// CHECK-NEXT: [[ARRAYDECAY15:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER16:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY15]], i64 10 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY15]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER16]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY15]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 0 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP11]], i64 1 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 1 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 2 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[ARRAYDECAY18:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER19:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY18]], i64 10 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY18]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY18]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = load i32, ptr [[N]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP22]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP13]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP24]], ptr align 8 [[AGG_TEMP13]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR26:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB30:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR26]], ptr [[TMP23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB28]], ptr [[TMP24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB30]], ptr [[TMP25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB22]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: [[CMP38:%.*]] = icmp sle i32 0, [[TMP22]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP26:%.*]] = phi i1 [ false, [[HERE]] ], [ [[CMP38]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP26]], label [[CONT41:%.*]], label [[TRAP40:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap40: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont41: +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP22]], 4 +// CHECK-NEXT: [[CONV42:%.*]] = sext i32 [[ADD]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP43]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR49]], ptr [[TMP27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB51]], ptr [[TMP28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB53]], ptr [[TMP29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR55:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR54]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR56:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB57:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR56]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR58:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB59:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR58]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST60:%.*]] = ptrtoint ptr [[WIDE_PTR_UB45]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST61:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR55]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB62:%.*]] = sub i64 [[SUB_PTR_LHS_CAST60]], [[SUB_PTR_RHS_CAST61]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP63:%.*]] = icmp sle i64 [[CONV42]], [[SUB_PTR_SUB62]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP63]], label [[LAND_RHS65:%.*]], label [[LAND_END68:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs65: +// CHECK-NEXT: [[CMP66:%.*]] = icmp sle i64 0, [[CONV42]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END68]], {{!annotation ![0-9]+}} +// CHECK: land.end68: +// CHECK-NEXT: [[TMP30:%.*]] = phi i1 [ false, [[CONT41]] ], [ [[CMP66]], [[LAND_RHS65]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP30]], label [[CONT70:%.*]], label [[TRAP69:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap69: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont70: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[AGG_TEMP13]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: [[BP78:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR73]], ptr [[BP78]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP79]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR80:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR81:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR80]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR82:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB83:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR82]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR84:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB85:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR84]], align 8 +// CHECK-NEXT: [[BP286:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR81]], ptr [[BP286]], align 8 +// CHECK-NEXT: [[L87:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store i32 [[TMP22]], ptr [[L87]], align 8 +// CHECK-NEXT: ret void +// +void Test() { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10 * sizeof(int); +here: + s.bp = &arr[1]; + s.bp2 = arr; + s.l = n; // trap s.l > bounds of (s.bp) +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-bigger-length.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-bigger-length.c new file mode 100644 index 0000000000000..52850d67f82a6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-bigger-length.c @@ -0,0 +1,23 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o /dev/null +// Executable version of "count-attr-fields-assign-2.c" +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int main () { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10; + s.bp = &arr[1]; + s.l = n; // run-time trap - s.l cannot be bigger than element count of &arr[1] (9) and s.1 + 1 cannot be bigger than element count of arr (10) + s.bp2 = arr; + + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-oob1-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-oob1-O2.c new file mode 100644 index 0000000000000..551dc10a9760d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-oob1-O2.c @@ -0,0 +1,46 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3 + + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +// CHECK-O2-LABEL: define dso_local noundef i32 @foo +// CHECK-O2-SAME: () local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2:![0-9]+]] +// CHECK-O2-NEXT: unreachable, !annotation [[META2]] +// +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.l = 8; + s.bp2 = arr; + + return s.bp2[9]; // trap : oob s.bp2[9] +} + +// CHECK-O2-LABEL: define dso_local i32 @bar +// CHECK-O2-SAME: () local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 undef +// +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.l = 8; + s.bp2 = arr; + + return s.bp2[8]; // ok +} +//. +// CHECK-O2: [[META2]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-oob2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-oob2.c new file mode 100644 index 0000000000000..39a10e41bf936 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2-oob2.c @@ -0,0 +1,283 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +// CHECK-O0-LABEL: @foo( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-O0-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP_TMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP24:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP43:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-O0-NEXT: [[AGG_TEMP56:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP57:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-O0-NEXT: [[AGG_TEMP73:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP74:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: br i1 true, label [[CONT5:%.*]], label [[TRAP4:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap4: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont5: +// CHECK-O0-NEXT: [[BP:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[BP]], align 8 +// CHECK-O0-NEXT: [[BP2:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[TMP_TMP1]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR8]], ptr [[BP2]], align 8 +// CHECK-O0-NEXT: [[L:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-O0-NEXT: store i32 0, ptr [[L]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[S]], i64 20 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP6]], i8 0, i64 4, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY15:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER16:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY15]], i64 10 +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY15]], ptr [[TMP7]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER16]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY15]], ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP11]], i64 1 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TMP13]], align 8 +// CHECK-O0-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP14]], ptr [[TMP15]], align 8 +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP14]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP17:%.*]] = load ptr, ptr [[TMP16]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP17]], ptr [[TMP18]], align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY18:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER19:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY18]], i64 10 +// CHECK-O0-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY18]], ptr [[TMP19]], align 8 +// CHECK-O0-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER19]], ptr [[TMP20]], align 8 +// CHECK-O0-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY18]], ptr [[TMP21]], align 8 +// CHECK-O0-NEXT: br i1 true, label [[CONT21:%.*]], label [[TRAP20:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap20: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont21: +// CHECK-O0-NEXT: br i1 true, label [[CONT23:%.*]], label [[TRAP22:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap22: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont23: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP24]], ptr align 8 [[AGG_TEMP13]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR26:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR25]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB30:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR29]], align 8 +// CHECK-O0-NEXT: [[BP31:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR26]], ptr [[BP31]], align 8 +// CHECK-O0-NEXT: [[L32:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-O0-NEXT: store i32 8, ptr [[L32]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-O0-NEXT: [[BP240:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[BP240]], align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[I]], align 4 +// CHECK-O0-NEXT: br label [[FOR_COND:%.*]] +// CHECK-O0: for.cond: +// CHECK-O0-NEXT: [[TMP22:%.*]] = load i32, ptr [[I]], align 4 +// CHECK-O0-NEXT: [[L41:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP23:%.*]] = load i32, ptr [[L41]], align 8 +// CHECK-O0-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP22]], [[TMP23]] +// CHECK-O0-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] +// CHECK-O0: for.body: +// CHECK-O0-NEXT: [[TMP24:%.*]] = load i32, ptr [[I]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP43]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-O0-NEXT: [[L44:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP25:%.*]] = load i32, ptr [[L44]], align 8 +// CHECK-O0-NEXT: [[BP45:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP26:%.*]] = load ptr, ptr [[BP45]], align 8 +// CHECK-O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP25]] to i64 +// CHECK-O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP26]], i64 [[IDX_EXT]] +// CHECK-O0-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP26]], ptr [[TMP27]], align 8 +// CHECK-O0-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP28]], align 8 +// CHECK-O0-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP26]], ptr [[TMP29]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR47:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR46]], align 8 +// CHECK-O0-NEXT: [[TMP30:%.*]] = load i32, ptr [[I]], align 4 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP30]] to i64 +// CHECK-O0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR47]], i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB51:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR50]], align 8 +// CHECK-O0-NEXT: [[TMP31:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB49]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP31]], label [[CONT53:%.*]], label [[TRAP52:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap52: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont53: +// CHECK-O0-NEXT: [[TMP32:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB51]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP32]], label [[CONT55:%.*]], label [[TRAP54:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap54: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont55: +// CHECK-O0-NEXT: store i32 [[TMP24]], ptr [[ARRAYIDX]], align 4 +// CHECK-O0-NEXT: br label [[FOR_INC:%.*]] +// CHECK-O0: for.inc: +// CHECK-O0-NEXT: [[TMP33:%.*]] = load i32, ptr [[I]], align 4 +// CHECK-O0-NEXT: [[INC:%.*]] = add nsw i32 [[TMP33]], 1 +// CHECK-O0-NEXT: store i32 [[INC]], ptr [[I]], align 4 +// CHECK-O0-NEXT: br label [[FOR_COND]], !llvm.loop [[LOOP5:![0-9]+]] +// CHECK-O0: for.end: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP57]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-O0-NEXT: [[L58:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP34:%.*]] = load i32, ptr [[L58]], align 8 +// CHECK-O0-NEXT: [[BP259:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP35:%.*]] = load ptr, ptr [[BP259]], align 8 +// CHECK-O0-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP34]], 1 +// CHECK-O0-NEXT: [[IDX_EXT60:%.*]] = sext i32 [[ADD]] to i64 +// CHECK-O0-NEXT: [[ADD_PTR61:%.*]] = getelementptr inbounds i32, ptr [[TMP35]], i64 [[IDX_EXT60]] +// CHECK-O0-NEXT: [[TMP36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP35]], ptr [[TMP36]], align 8 +// CHECK-O0-NEXT: [[TMP37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR61]], ptr [[TMP37]], align 8 +// CHECK-O0-NEXT: [[TMP38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP35]], ptr [[TMP38]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR62:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR63:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR62]], align 8 +// CHECK-O0-NEXT: [[ARRAYIDX64:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR63]], i64 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB66:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR65]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR67:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP56]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB68:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR67]], align 8 +// CHECK-O0-NEXT: [[TMP39:%.*]] = icmp ult ptr [[ARRAYIDX64]], [[WIDE_PTR_UB66]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP39]], label [[CONT70:%.*]], label [[TRAP69:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap69: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont70: +// CHECK-O0-NEXT: [[TMP40:%.*]] = icmp uge ptr [[ARRAYIDX64]], [[WIDE_PTR_LB68]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP40]], label [[CONT72:%.*]], label [[TRAP71:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap71: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont72: +// CHECK-O0-NEXT: [[TMP41:%.*]] = load i32, ptr [[ARRAYIDX64]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP74]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-O0-NEXT: [[L75:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP74]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP42:%.*]] = load i32, ptr [[L75]], align 8 +// CHECK-O0-NEXT: [[BP76:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP74]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP43:%.*]] = load ptr, ptr [[BP76]], align 8 +// CHECK-O0-NEXT: [[IDX_EXT77:%.*]] = sext i32 [[TMP42]] to i64 +// CHECK-O0-NEXT: [[ADD_PTR78:%.*]] = getelementptr inbounds i32, ptr [[TMP43]], i64 [[IDX_EXT77]] +// CHECK-O0-NEXT: [[TMP44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP43]], ptr [[TMP44]], align 8 +// CHECK-O0-NEXT: [[TMP45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR78]], ptr [[TMP45]], align 8 +// CHECK-O0-NEXT: [[TMP46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP43]], ptr [[TMP46]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR79:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR80:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR79]], align 8 +// CHECK-O0-NEXT: [[ARRAYIDX81:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR80]], i64 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR82:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB83:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR82]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR84:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB85:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR84]], align 8 +// CHECK-O0-NEXT: [[TMP47:%.*]] = icmp ult ptr [[ARRAYIDX81]], [[WIDE_PTR_UB83]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP47]], label [[CONT87:%.*]], label [[TRAP86:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap86: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont87: +// CHECK-O0-NEXT: [[TMP48:%.*]] = icmp uge ptr [[ARRAYIDX81]], [[WIDE_PTR_LB85]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP48]], label [[CONT89:%.*]], label [[TRAP88:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap88: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont89: +// CHECK-O0-NEXT: [[TMP49:%.*]] = load i32, ptr [[ARRAYIDX81]], align 4 +// CHECK-O0-NEXT: [[ADD90:%.*]] = add nsw i32 [[TMP41]], [[TMP49]] +// CHECK-O0-NEXT: ret i32 [[ADD90]] +// +// CHECK-O2-LABEL: @foo( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.l = 8; + s.bp2 = arr; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[8]; // trap : oob s.bp[8] +} + + diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2.c new file mode 100644 index 0000000000000..3f300417249bb --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-2.c @@ -0,0 +1,49 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.l = 9; + s.bp2 = arr; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[7]; // in bounds +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10; + s.bp = &arr[1]; + s.l = n; + s.bp2 = arr; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[7]; // in bounds +} + +// CHECK: define noundef {{.*}}i32 @foo() {{.*}} +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret i32 14 + +// CHECK: define noundef i32 @bar() {{.*}} +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-bigger-length.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-bigger-length.c new file mode 100644 index 0000000000000..a4c65be24f95f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-bigger-length.c @@ -0,0 +1,25 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o /dev/null +// Executable version of "count-attr-fields-assign.c" +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo(); + +int main () { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = n; // trap : s.1 + 1 >= countof(arr or s.bp2) + + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-check-buf-only.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-check-buf-only.c new file mode 100644 index 0000000000000..44d1190b39941 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-check-buf-only.c @@ -0,0 +1,58 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + s.bp = &arr[9]; // trap: can't make the bounds smaller than s.l + s.l = s.l; + s.bp2 = s.bp2; + + return 0; +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + s.bp = &arr[0]; // no trap + s.l = s.l; + s.bp2 = s.bp2; + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable +// CHECK: } + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 +// CHECK: } diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-neg-length.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-neg-length.c new file mode 100644 index 0000000000000..8a732d32599dc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-neg-length.c @@ -0,0 +1,44 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = -1; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = n; // trap : no negative len + + return 0; +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 0; // no trap + + return 0; +} + + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable +// CHECK: } + +// CHECK: define noundef i32 @bar() +// CHECK: entry: +// CHECK: ret i32 0 +// CHECK: } diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-oob1.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-oob1.c new file mode 100644 index 0000000000000..e1406ba917469 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-oob1.c @@ -0,0 +1,50 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 8; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[9] + s.bp[7]; // trap : oobs +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 8; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[7]; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable +// CHECK: } + +// CHECK: define noundef {{.*}}i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 14 +// CHECK: } diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-oob2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-oob2.c new file mode 100644 index 0000000000000..116bacbf31580 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-oob2.c @@ -0,0 +1,49 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 8; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[8]; // trap : oob s.bp[8] +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 8; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[7]; +} +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable +// CHECK: } + +// CHECK: define noundef {{.*}}i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 14 +// CHECK: } diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple-O0.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple-O0.c new file mode 100644 index 0000000000000..bb4626cb35cf6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple-O0.c @@ -0,0 +1,81 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct Foo { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: [[N:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[LEN]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store i32 12, ptr [[N]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[N]], align 4 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: [[CMP6:%.*]] = icmp sle i32 0, [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP6]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[LEN8:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[LEN8]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8 +// CHECK-NEXT: [[PTR16:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR11]], ptr [[PTR16]], align 8 +// CHECK-NEXT: ret void +// +void Test() { + int arr[10]; + struct Foo f; + int n = 12; + f.len = n; + f.ptr = arr; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple-O2.c new file mode 100644 index 0000000000000..a36713c8ae9ab --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple-O2.c @@ -0,0 +1,34 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct Foo { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @TestOOB( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap{{.*}} +// CHECK-NEXT: unreachable +// +void TestOOB() { + int arr[10]; + struct Foo f; + int n = 12; + f.len = n; + f.ptr = arr; +} + +// CHECK-LABEL: @TestIB( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestIB() { + int arr[10]; + struct Foo f; + f.len = 10; + f.ptr = arr; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple.c new file mode 100644 index 0000000000000..50c738e120d93 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-simple.c @@ -0,0 +1,104 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct Foo { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[LEN1:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[LEN1]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[PTR2:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[PTR2]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END33:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP17:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR12]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP17]], label [[LAND_RHS:%.*]], label [[LAND_END33]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB20]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR23]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP28]], label [[LAND_RHS30:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs30: +// CHECK-NEXT: [[CMP31:%.*]] = icmp sle i32 0, [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP31]], [[LAND_RHS30]] ] +// CHECK-NEXT: br label [[LAND_END33]], {{!annotation ![0-9]+}} +// CHECK: land.end33: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP1]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[LEN34:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[LEN34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR37:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB41:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR40]], align 8 +// CHECK-NEXT: [[PTR42:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR37]], ptr [[PTR42]], align 8 +// CHECK-NEXT: ret void +// +void Test(int len, int *__bidi_indexable ptr) { + struct Foo f; + f.len = len; + f.ptr = ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-thin-buf.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-thin-buf.c new file mode 100644 index 0000000000000..8d307811b2c12 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign-thin-buf.c @@ -0,0 +1,61 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[16] = {0}; + int *__single tp = arr; + struct S s = {arr, arr, 0}; + s.bp = &arr[3]; + s.bp2 = arr; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp2[i] = i; + + int l = 10; + s.l = l; + s.bp = tp; // trap: s.l > 1 + s.bp2 = s.bp2; + + return 0; +} + +int bar () { + int arr[16] = {0}; + int *__single tp = arr; + struct S s = {arr, arr, 0}; + s.bp = &arr[3]; + s.bp2 = arr; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp2[i] = i; + + s.l = 1; + s.bp = tp; + s.bp2 = s.bp2; + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable +// CHECK: } + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 +// CHECK: } diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign.c new file mode 100644 index 0000000000000..0916b08506812 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-assign.c @@ -0,0 +1,49 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[7]; // in bounds +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + int n = 10; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = n; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + return s.bp2[8] + s.bp[7]; // in bounds +} + +// CHECK: define noundef {{.*}}i32 @foo() {{.*}} +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret i32 14 + +// CHECK: define noundef i32 @bar() {{.*}} +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-self-assign.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-self-assign.c new file mode 100644 index 0000000000000..3415de11c1e60 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/count-attr-fields-self-assign.c @@ -0,0 +1,55 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +void side_effect(); + +int foo () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9; + + side_effect(); + + s.bp = s.bp; + s.bp2 = &arr[8]; // trap : bound of &arr[8] < s.l + 1 + s.l = s.l; + + return 0; +} + +int bar () { + int arr[10]; + struct S s = {arr, arr, 0}; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9; + + side_effect(); + + s.bp = s.bp; + s.bp2 = &arr[0]; // no trap + s.l = s.l; + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable +// CHECK: } + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/counted-to-counted-assignments-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/counted-to-counted-assignments-O2.c new file mode 100644 index 0000000000000..e8554d34bd974 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/counted-to-counted-assignments-O2.c @@ -0,0 +1,73 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +#include +struct S { + int *__counted_by(len) ptr; + int *__counted_by(len - 1) ptr2; + int len; +}; + +// CHECK-LABEL: @TestPtrFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestPtrFail() { + int arr[10]; + struct S s = {arr, &arr[1], 10}; + s.ptr = s.ptr2; // trap! bounds_of(s.ptr2) == 9 < 10 + s.ptr2 = arr; + s.len = 10; +} + +// CHECK-LABEL: @TestPtrOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestPtrOK() { + int arr[10]; + struct S s = {arr, &arr[1], 10}; + s.ptr = s.ptr2; // ok: bounds_of(s.ptr2) == 9 <= 9 + s.ptr2 += 1; // bounds_of(s.ptr2 + 1) == 8 <= 8 + s.len = 9; +} + +// CHECK-LABEL: @TestPtrLBFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH18:%.*]] = getelementptr i8, ptr [[ARR]], i64 -8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH18]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP42_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP42_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT57:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont57: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void TestPtrLBFail() { + int arr[10]; + struct S s = {arr, &arr[1], 10}; + s.ptr = s.ptr - 2; // trap! ptr < lb + s.ptr2 = arr; + s.len = 10; +} + +// CHECK-LABEL: @TestPtrUBFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestPtrUBFail() { + int arr[10]; + struct S s = {arr, &arr[1], 10}; + s.ptr = s.ptr; + s.ptr2 = s.ptr2 + 10; // trap! ptr2 oob + s.len = 1; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/counted-to-counted-assignments.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/counted-to-counted-assignments.c new file mode 100644 index 0000000000000..b231efc1668bb --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/counted-to-counted-assignments.c @@ -0,0 +1,415 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include +struct S { + int *__counted_by(len) ptr; + int *__counted_by(len - 1) ptr2; + int len; +}; + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP59:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP67:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY3]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: br i1 true, label [[CONT6:%.*]], label [[TRAP5:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap5: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont6: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[PTR2:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR9]], ptr [[PTR2]], align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store i32 10, ptr [[LEN]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[S]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP15]], i8 0, i64 4, i1 false) +// CHECK-NEXT: br label [[HERE:%.*]] +// CHECK: here: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-NEXT: [[LEN16:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP15]], i32 0, i32 2 +// CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[LEN16]], align 8 +// CHECK-NEXT: [[PTR217:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP15]], i32 0, i32 1 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[PTR217]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP16]], 1 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP17]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[ARRAYDECAY19:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER20:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY19]], i64 10 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY19]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER20]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY19]], ptr [[TMP23]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR23]], [[WIDE_PTR_UB30]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END54:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP41:%.*]] = icmp ule ptr [[WIDE_PTR_LB33]], [[WIDE_PTR_PTR36]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP41]], label [[LAND_RHS:%.*]], label [[LAND_END54]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP42]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP45]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR47:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR46]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB51:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR50]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB44]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR47]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP52:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP52]], label [[LAND_RHS53:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs53: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP24:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS53]] ] +// CHECK-NEXT: br label [[LAND_END54]], {{!annotation ![0-9]+}} +// CHECK: land.end54: +// CHECK-NEXT: [[TMP25:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[HERE]] ], [ [[TMP24]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP25]], label [[CONT56:%.*]], label [[TRAP55:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap55: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont56: +// CHECK-NEXT: br i1 true, label [[CONT58:%.*]], label [[TRAP57:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap57: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont58: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP59]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR60:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP59]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR61:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR60]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR62:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP59]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB63:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR62]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR64:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP59]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB65:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR64]], align 8 +// CHECK-NEXT: [[PTR66:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR61]], ptr [[PTR66]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP67]], ptr align 8 [[AGG_TEMP18]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR68:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP67]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR69:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR68]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR70:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP67]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB71:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR70]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP67]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB73:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR72]], align 8 +// CHECK-NEXT: [[PTR274:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR69]], ptr [[PTR274]], align 8 +// CHECK-NEXT: [[LEN75:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store i32 10, ptr [[LEN75]], align 8 +// CHECK-NEXT: ret i32 0 +// +int Foo() { + int arr[10]; + struct S s = {arr, &arr[1], 10}; +here: + s.ptr = s.ptr2; + s.ptr2 = arr; + s.len = 10; + return 0; +} + +// CHECK-LABEL: @Bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP61:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP69:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY3]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: br i1 true, label [[CONT6:%.*]], label [[TRAP5:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap5: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont6: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[PTR2:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP_TMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR9]], ptr [[PTR2]], align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store i32 10, ptr [[LEN]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[S]], i64 20 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP15]], i8 0, i64 4, i1 false) +// CHECK-NEXT: br label [[HERE:%.*]] +// CHECK: here: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-NEXT: [[LEN17:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[LEN17]], align 8 +// CHECK-NEXT: [[PTR18:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[PTR18]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP16]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP17]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP15]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP15]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP15]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP15]], i32 0, i32 0 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH19:%.*]] = getelementptr i32, ptr [[TMP22]], i64 -2 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH19]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP15]], i32 0, i32 1 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP15]], i32 0, i32 2 +// CHECK-NEXT: [[TMP28:%.*]] = load ptr, ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP28]], ptr [[TMP29]], align 8 +// CHECK-NEXT: [[ARRAYDECAY21:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER22:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY21]], i64 10 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY21]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER22]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY21]], ptr [[TMP32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR25]], [[WIDE_PTR_UB32]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END56:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP43:%.*]] = icmp ule ptr [[WIDE_PTR_LB35]], [[WIDE_PTR_PTR38]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP43]], label [[LAND_RHS:%.*]], label [[LAND_END56]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB46]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR49]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP54:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP54]], label [[LAND_RHS55:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs55: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP33:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS55]] ] +// CHECK-NEXT: br label [[LAND_END56]], {{!annotation ![0-9]+}} +// CHECK: land.end56: +// CHECK-NEXT: [[TMP34:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[HERE]] ], [ [[TMP33]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP34]], label [[CONT58:%.*]], label [[TRAP57:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap57: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont58: +// CHECK-NEXT: br i1 true, label [[CONT60:%.*]], label [[TRAP59:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap59: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont60: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP61]], ptr align 8 [[AGG_TEMP14]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR62:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR63:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR62]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR64:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB65:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR64]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR66:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP61]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB67:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR66]], align 8 +// CHECK-NEXT: [[PTR68:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR63]], ptr [[PTR68]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP69]], ptr align 8 [[AGG_TEMP20]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR70:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR71:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR70]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB73:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR74:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB75:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR74]], align 8 +// CHECK-NEXT: [[PTR276:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR71]], ptr [[PTR276]], align 8 +// CHECK-NEXT: [[LEN77:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store i32 10, ptr [[LEN77]], align 8 +// CHECK-NEXT: ret i32 0 +// +int Bar() { + int arr[10]; + struct S s = {arr, &arr[1], 10}; +here: + s.ptr = s.ptr - 2; + s.ptr2 = arr; + s.len = 10; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-basic-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-basic-O2.c new file mode 100644 index 0000000000000..92f7309f51c0c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-basic-O2.c @@ -0,0 +1,130 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @TestInitFail1( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestInitFail1() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {10, ptr}; // trap: +} + +// CHECK-LABEL: @TestInitFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestInitFail2() { + int arr[9] = {0}; + int *ptr = arr; + int len = -1; + struct S s = {len, ptr}; // trap: +} + +struct SU { + unsigned long len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @TestUInitFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestUInitFail() { + int arr[9] = {0}; + int *ptr = arr; + long len = -1; + struct SU s = {len, ptr}; // trap: +} + +// CHECK-LABEL: @TestUInitOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestUInitOK() { + int arr[9] = {0}; + int *ptr = arr; + long len = 8; + struct SU s = {len, ptr}; +} + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {9, ptr}; +} + +// CHECK-LABEL: @TestAccessOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestAccessOK() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {9, ptr}; + (void)s.ptr[8]; +} + +// CHECK-LABEL: @TestAccessFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestAccessFail() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {9, ptr}; + (void)s.ptr[9]; +} + +// CHECK-LABEL: @TestAccessFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [9 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 36, ptr nonnull [[ARR]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 36 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[ARRAYIDX]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT47:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont47: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 36, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void TestAccessFail2() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {9, ptr}; + (void)s.ptr[-1]; +} + +// CHECK-LABEL: @TestAccessFail3( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestAccessFail3() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {9, ptr}; + (void)*(s.ptr + 9); +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-basic.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-basic.c new file mode 100644 index 0000000000000..9074b48192940 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-basic.c @@ -0,0 +1,106 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s --check-prefix=CHECK-O0 +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-O0-LABEL: @Test( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [9 x i32], align 16 +// CHECK-O0-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 36, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [9 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 9 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END27:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: land.lhs.true: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP14]], label [[LAND_RHS:%.*]], label [[LAND_END27]], {{!annotation ![0-9]+}} +// CHECK-O0: land.rhs: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[CMP25:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP25]], label [[LAND_RHS26:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: land.rhs26: +// CHECK-O0-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK-O0: land.end: +// CHECK-O0-NEXT: [[TMP3:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS26]] ] +// CHECK-O0-NEXT: br label [[LAND_END27]], {{!annotation ![0-9]+}} +// CHECK-O0: land.end27: +// CHECK-O0-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP3]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-O0-NEXT: store i32 10, ptr [[LEN]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[S]], i64 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-O0-NEXT: [[PTR28:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[PTR28]], align 8 +// CHECK-O0-NEXT: ret void +// +void Test() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {10, ptr}; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-designated-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-designated-O2.c new file mode 100644 index 0000000000000..61303d0698371 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-designated-O2.c @@ -0,0 +1,79 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @TestFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestFail() { + int arr[10]; + int len = 11; + struct S s = {.ptr = arr, .len = len}; +} + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int len = 0; + struct S s = {.len = len}; +} + +struct S2 { + int len; + int *__counted_by(len+1) ptr; +}; + +// CHECK-LABEL: @Test2OK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void Test2OK() { + int len = -1; + struct S2 s = {.len = len, .ptr = 0}; +} + +// CHECK-LABEL: @Test2Fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void Test2Fail() { + int len = 0; + struct S2 s = {.len = len, .ptr = 0}; +} + +struct S3 { + int *__counted_by(len+2) ptr; + int len; +}; + + +// CHECK-LABEL: @Test3OK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void Test3OK() { + int a[2]; + struct S3 s = {.len = 0, .ptr = a}; +} + +// CHECK-LABEL: @Test3Fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void Test3Fail() { + int a; + struct S3 s = {.len = 0, .ptr = &a}; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-designated.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-designated.c new file mode 100644 index 0000000000000..07bf886f2cf02 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-designated.c @@ -0,0 +1,109 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s --check-prefix=CHECK-O0 +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +// ... +// ... + +// CHECK-O0-LABEL: @Test( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [9 x i32], align 16 +// CHECK-O0-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 36, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [9 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 9 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END27:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: land.lhs.true: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP14]], label [[LAND_RHS:%.*]], label [[LAND_END27]], {{!annotation ![0-9]+}} +// CHECK-O0: land.rhs: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[CMP25:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP25]], label [[LAND_RHS26:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: land.rhs26: +// CHECK-O0-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK-O0: land.end: +// CHECK-O0-NEXT: [[TMP3:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS26]] ] +// CHECK-O0-NEXT: br label [[LAND_END27]], {{!annotation ![0-9]+}} +// CHECK-O0: land.end27: +// CHECK-O0-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP3]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-O0-NEXT: store i32 10, ptr [[LEN]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[S]], i64 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP5]], i8 0, i64 4, i1 false) +// CHECK-O0-NEXT: [[PTR28:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[PTR28]], align 8 +// CHECK-O0-NEXT: ret void +// +void Test() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {.ptr = ptr, .len = 10}; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-good.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-good.c new file mode 100644 index 0000000000000..47b8d7af93a68 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-good.c @@ -0,0 +1,28 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +struct T { + union { + struct S s; + int arr[2]; + } u; + + void *vptr; +}; + +int main() { + int arr[9] = {0}; + struct S s = {8, arr}; // in bounds + + return s.ptr[7]; +} + +// CHECK: ret i32 0 \ No newline at end of file diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-implicit-O2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-implicit-O2.c new file mode 100644 index 0000000000000..c8ead4cfd70ee --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-implicit-O2.c @@ -0,0 +1,78 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-LABEL: @TestFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestFail() { + int cnt = 1; + struct S s = {cnt}; +} + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int len = 0; + struct S s = {len}; +} + +struct S2 { + int len; + int *__counted_by(len+1) ptr; +}; + +// CHECK-LABEL: @Test2OK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void Test2OK() { + int len = -1; + struct S2 s = {len}; +} + +// CHECK-LABEL: @Test2Fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void Test2Fail() { + int len = 0; + struct S2 s = {len}; +} + +struct S3 { + int *__counted_by(len+2) ptr; + int len; +}; + + +// CHECK-LABEL: @Test3OK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void Test3OK() { + int a[2]; + struct S3 s = {a}; +} + +// CHECK-LABEL: @Test3Fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void Test3Fail() { + int a; + struct S3 s = {&a}; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-implicit.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-implicit.c new file mode 100644 index 0000000000000..436673e15bb6a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-implicit.c @@ -0,0 +1,34 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s --check-prefix=CHECK-O0 +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +// CHECK-O0-LABEL: @Test( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[VAR_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-O0-NEXT: store i32 [[VAR:%.*]], ptr [[VAR_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[VAR_ADDR]], align 4 +// CHECK-O0-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-O0-NEXT: store i32 [[TMP0]], ptr [[LEN]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[S]], i64 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// CHECK-O0-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr null, ptr [[PTR]], align 8 +// CHECK-O0-NEXT: ret void +// +void Test(int var) { + struct S s = {var}; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-in-array-2.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-in-array-2.c new file mode 100644 index 0000000000000..5190950ee6635 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-in-array-2.c @@ -0,0 +1,43 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +struct T { + union { + struct S s; + int arr[2]; + } u; + + void *vptr; +}; + +int foo() { + int arr[9] = {0}; + struct S s = {9, arr}; + struct S sa[10] = {s, {8, &arr[2]}, 0}; // trap + + return 0; +} + +int bar() { + int arr[9] = {0}; + struct S s = {9, arr}; + struct S sa[10] = {s, {7, &arr[2]}, 0}; // no trap + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-in-array-good.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-in-array-good.c new file mode 100644 index 0000000000000..670159ad05a06 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-in-array-good.c @@ -0,0 +1,29 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +struct T { + union { + struct S s; + int arr[2]; + } u; + + void *vptr; +}; + +int main() { + int arr[9] = {0}; + struct S s = {9, arr}; + struct S sa[10] = {s, {7, &arr[2]}, 0}; // all in bounds + + return 0; +} + +// CHECK: ret i32 0 \ No newline at end of file diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-nested-in-union.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-nested-in-union.c new file mode 100644 index 0000000000000..c6996da3e3dc0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list-nested-in-union.c @@ -0,0 +1,44 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +struct T { + union { + struct S s; + int arr[2]; + } u; +}; + +int foo() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {9, ptr}; // ok + // XXX: We shouldn't check again for s. Verify. + struct T t = {10, ptr}; // trap : len > boundof(ptr) + + return 0; +} + +int bar() { + int arr[9] = {0}; + struct S s = {9, arr}; // ok + // XXX: We shouldn't check again for s. Verify. + struct T t = {9, arr}; // ok + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list.c new file mode 100644 index 0000000000000..0728e5492c510 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dep-count-init-list.c @@ -0,0 +1,42 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s +#include + +struct S { + int len; + int *__counted_by(len) ptr; +}; + +struct T { + union { + struct S s; + int arr[2]; + } u; + + void *vptr; +}; + +int foo() { + int arr[9] = {0}; + int *ptr = arr; + struct S s = {10, ptr}; // trap: len > boundof(ptr) + + return 0; +} + +int bar() { + int arr[9] = {0}; + struct S s = {9, arr}; // ok + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dependent-count-out-param-assign-check.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dependent-count-out-param-assign-check.c new file mode 100644 index 0000000000000..f70f11d9f800b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dependent-count-out-param-assign-check.c @@ -0,0 +1,41 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s +#include + +int foo(int *__counted_by(*out_len)* out_buf, int *out_len) { + int arr[10]; + int n = 11; + *out_len = n; // trap : 11 > boundof(arr) + *out_buf = arr; + return 0; +} + +int bar(int *__counted_by(*out_len)* out_buf, int *out_len) { + int arr[10]; + *out_len = 9; + *out_buf = arr; + return 0; +} + +int main() { + int len; + int *__counted_by(len) buf; + foo(&buf, &len); + + return buf[len-1]; +} + +// CHECK: define noundef i32 @foo +// CHECK: {{.*}}: +// CHECK: call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable + +// CHECK: define noundef i32 @bar +// CHECK: {{.*}}: +// CHECK: ret i32 0 + +// CHECK: define noundef i32 @main() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dynamic-count-local-decl-init-checks.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dynamic-count-local-decl-init-checks.c new file mode 100644 index 0000000000000..649b98d894afc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/dynamic-count-local-decl-init-checks.c @@ -0,0 +1,30 @@ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o - | FileCheck %s + +#include +int foo () { + int arr[10]; + int n = 11; + int len = n; // trap : 11 > boundsof(arr) + int *__counted_by(len) buf = arr; + + return 0; +} + +int bar () { + int arr[10]; + int len = 10; + int *__counted_by(len) buf = arr; + + return 0; +} + +// CHECK: define noundef i32 @foo() +// CHECK: {{.*}}: +// CHECK: tail call void @llvm.ubsantrap(i8 25) +// CHECK: unreachable + +// CHECK: define noundef i32 @bar() +// CHECK: {{.*}}: +// CHECK: ret i32 0 diff --git a/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/index-extension.c b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/index-extension.c new file mode 100644 index 0000000000000..01c1a5d818fcd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-dependent-assignment-checks/index-extension.c @@ -0,0 +1,201 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK + +#include + +// CHECK-LABEL: @idx_char( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i8 [[IDX:%.*]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_char(int *__counted_by(n) dst, unsigned n, char idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_unsigned_char( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i8 [[IDX:%.*]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_unsigned_char(int *__counted_by(n) dst, unsigned n, unsigned char idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_short_int( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i16 [[IDX:%.*]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_short_int(int *__counted_by(n) dst, unsigned n, short int idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_short_unsigned( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i16 [[IDX:%.*]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_short_unsigned(int *__counted_by(n) dst, unsigned n, short unsigned idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_int( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[IDX:%.*]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_int(int *__counted_by(n) dst, unsigned n, int idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_unsigned( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i32 [[IDX:%.*]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_unsigned(int *__counted_by(n) dst, unsigned n, unsigned idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_long_int( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDX:%.*]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_long_int(int *__counted_by(n) dst, unsigned n, long int idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_long_unsigned( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDX:%.*]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_long_unsigned(int *__counted_by(n) dst, unsigned n, long unsigned idx) { + // + dst[idx] = 0; +} + +// CHECK-LABEL: @idx_long_unsigned_counted_by_int( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDX:%.*]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void idx_long_unsigned_counted_by_int(int *__counted_by(n) dst, int n, long unsigned idx) { + // + dst[idx] = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-ptr-arithmetic.c b/clang/test/BoundsSafety/CodeGen/count-ptr-arithmetic.c new file mode 100644 index 0000000000000..662bb9b00ed8e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-ptr-arithmetic.c @@ -0,0 +1,17 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +#include + +int main() { + int len; + int *__counted_by(len) ptr1; + int len2; + int *__counted_by(len2) ptr2; + unsigned long long diff = (unsigned long long)(ptr1 - ptr2); + + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/count-return.c b/clang/test/BoundsSafety/CodeGen/count-return.c new file mode 100644 index 0000000000000..cc204b05da867 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/count-return.c @@ -0,0 +1,135 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +#include + +int *__sized_by(len) alloc(int len); + +// CHECK-O0-LABEL: @Test1( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[BUFAUTO:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[BUFBOUND:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 16, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[CALL:%.*]] = call ptr @alloc(i32 noundef [[TMP0]]) +// CHECK-O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFAUTO]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFAUTO]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFAUTO]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[CALL1:%.*]] = call ptr @alloc(i32 noundef 40) +// CHECK-O0-NEXT: [[ADD_PTR2:%.*]] = getelementptr inbounds i8, ptr [[CALL1]], i64 40 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFBOUND]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[CALL1]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFBOUND]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR2]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFBOUND]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[CALL1]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BUFBOUND]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 9 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = icmp ult ptr [[TMP7]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP9:%.*]] = icmp uge ptr [[TMP7]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP9]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap3: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont4: +// CHECK-O0-NEXT: [[TMP10:%.*]] = load i32, ptr [[TMP7]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP10]] +// +// CHECK-O2-LABEL: @Test1( +// CHECK-O2-NEXT: cont3: +// CHECK-O2-NEXT: [[CALL:%.*]] = tail call ptr @alloc(i32 noundef 16) #[[ATTR4:[0-9]+]] +// CHECK-O2-NEXT: [[CALL1:%.*]] = tail call ptr @alloc(i32 noundef 40) #[[ATTR4]] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[CALL1]], i64 36 +// CHECK-O2-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-O2-NEXT: ret i32 [[TMP1]] +// +int Test1() { + int len = 16; + int *bufAuto = alloc(len); + int *__bidi_indexable bufBound = alloc(sizeof(int) * 10); + return bufBound[9]; +} + +// CHECK-O0-LABEL: @Test2( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[BUFAUTO:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[BUFBOUND:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 16, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[CALL:%.*]] = call ptr @alloc(i32 noundef [[TMP0]]) +// CHECK-O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFAUTO]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFAUTO]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFAUTO]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[CALL1:%.*]] = call ptr @alloc(i32 noundef 40) +// CHECK-O0-NEXT: [[ADD_PTR2:%.*]] = getelementptr inbounds i8, ptr [[CALL1]], i64 40 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFBOUND]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[CALL1]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFBOUND]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR2]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BUFBOUND]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[CALL1]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BUFBOUND]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 10 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = icmp ult ptr [[TMP7]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP9:%.*]] = icmp uge ptr [[TMP7]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP9]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap3: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont4: +// CHECK-O0-NEXT: [[TMP10:%.*]] = load i32, ptr [[TMP7]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP10]] +// +// CHECK-O2-LABEL: @Test2( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[CALL:%.*]] = tail call ptr @alloc(i32 noundef 16) #[[ATTR4]] +// CHECK-O2-NEXT: [[CALL1:%.*]] = tail call ptr @alloc(i32 noundef 40) #[[ATTR4]] +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int Test2() { + int len = 16; + int *bufAuto = alloc(len); + int *__bidi_indexable bufBound = alloc(sizeof(int) * 10); + return bufBound[10]; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-nested-assignments-O0.c b/clang/test/BoundsSafety/CodeGen/counted-by-nested-assignments-O0.c new file mode 100644 index 0000000000000..7625555020374 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-nested-assignments-O0.c @@ -0,0 +1,297 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 5 + + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -fbounds-safety-bringup-missing-checks=indirect_count_update -DWITH %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s -o - | FileCheck %s --check-prefix WITHOUT +#include + + +// CHECK-LABEL: define dso_local void @foo( +// CHECK-SAME: ptr noundef [[X:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[TMP5]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP6]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP12]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub i32 [[TMP14]], 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP26]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[X_ADDR]], align 8 +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP16]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[X_ADDR]], align 8 +// CHECK-NEXT: store i32 0, ptr [[TMP16]], align 4 +// CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[DEC:%.*]] = add i32 [[TMP17]], -1 +// CHECK-NEXT: store i32 [[DEC]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @foo( +// WITHOUT-SAME: ptr noundef [[X:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0:[0-9]+]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8 +// WITHOUT-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// WITHOUT-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8 +// WITHOUT-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// WITHOUT-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8 +// WITHOUT-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP0]], i32 1 +// WITHOUT-NEXT: store ptr [[INCDEC_PTR]], ptr [[X_ADDR]], align 8 +// WITHOUT-NEXT: store i32 0, ptr [[TMP0]], align 4 +// WITHOUT-NEXT: ret void +// +void foo(int *__counted_by(count) x, unsigned count) { + *x++ = 0; +#ifdef WITH + // NOTE: It's necessary to exclude the count update with + // indirect_count_update disabled otherwise we'll get diagnostics about + // the a missing assignment to `x`. + count--; +#endif +} + +// CHECK-LABEL: define dso_local void @bar( +// CHECK-SAME: ptr noundef [[X:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP0]], 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[X_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END31:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[SUB]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS28:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS28]]: +// CHECK-NEXT: [[CMP29:%.*]] = icmp sle i32 0, [[SUB]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP29]], %[[LAND_RHS28]] ] +// CHECK-NEXT: br label %[[LAND_END31]], !annotation [[META2]] +// CHECK: [[LAND_END31]]: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP15]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 [[SUB]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[X_ADDR]], align 8 +// CHECK-NEXT: store i32 0, ptr [[WIDE_PTR_PTR34]], align 4 +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @bar( +// WITHOUT-SAME: ptr noundef [[X:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: [[X_ADDR:%.*]] = alloca ptr, align 8 +// WITHOUT-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// WITHOUT-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// WITHOUT-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// WITHOUT-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8 +// WITHOUT-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// WITHOUT-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// WITHOUT-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// WITHOUT-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// WITHOUT-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// WITHOUT-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// WITHOUT-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// WITHOUT-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// WITHOUT-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// WITHOUT-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// WITHOUT-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// WITHOUT-NEXT: [[TMP6:%.*]] = load ptr, ptr [[TMP5]], align 8 +// WITHOUT-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP6]], i64 1 +// WITHOUT-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// WITHOUT-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP7]], align 8 +// WITHOUT-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// WITHOUT-NEXT: [[TMP9:%.*]] = load ptr, ptr [[TMP8]], align 8 +// WITHOUT-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// WITHOUT-NEXT: store ptr [[TMP9]], ptr [[TMP10]], align 8 +// WITHOUT-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// WITHOUT-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// WITHOUT-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// WITHOUT-NEXT: store ptr [[TMP12]], ptr [[TMP13]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// WITHOUT-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// WITHOUT-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// WITHOUT-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// WITHOUT-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[X_ADDR]], align 8 +// WITHOUT-NEXT: store i32 0, ptr [[WIDE_PTR_PTR]], align 4 +// WITHOUT-NEXT: ret void +// +void bar(int *__counted_by(count) x, int count) { +#ifdef WITH + // NOTE: It's necessary to exclude the count update with + // indirect_count_update disabled otherwise we'll get diagnostics about + // the a missing assignment to `x`. + count = count - 1; +#endif + *(x = x+1) = 0; +} + + + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-nested-assignments-O2.c b/clang/test/BoundsSafety/CodeGen/counted-by-nested-assignments-O2.c new file mode 100644 index 0000000000000..c042170964c1c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-nested-assignments-O2.c @@ -0,0 +1,111 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 5 + + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -fbounds-safety-bringup-missing-checks=indirect_count_update -DWITH -O2 %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -fno-bounds-safety-bringup-missing-checks=indirect_count_update -O2 %s -o - | FileCheck %s --check-prefix WITHOUT +#include + +// CHECK-LABEL: define dso_local void @foo( +// CHECK-SAME: ptr noundef writeonly captures(address) [[X:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[COUNT]] to i64 +// CHECK-NEXT: [[ADD_PTR_IDX:%.*]] = shl nuw nsw i64 [[IDX_EXT]], 2, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[X]], i64 [[ADD_PTR_IDX]], !annotation [[META2]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[X]], i64 4 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]], !annotation [[META2]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[X]], [[BOUND_PTR_ARITH]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP15_NOT]], [[CMP_NOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[SUB:%.*]] = add i32 [[COUNT]], -1, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[GEPDIFF:%.*]] = add nsw i64 [[ADD_PTR_IDX]], -4, !annotation [[META5:![0-9]+]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[GEPDIFF]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP26_NOT:%.*]] = icmp slt i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26_NOT]], label %[[TRAP]], label %[[CONT:.*]], !prof [[PROF7:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 0, ptr [[X]], align 4, !tbaa [[TBAA8:![0-9]+]] +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @foo( +// WITHOUT-SAME: ptr noundef writeonly captures(none) initializes((0, 4)) [[X:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: store i32 0, ptr [[X]], align 4, !tbaa [[TBAA2:![0-9]+]] +// WITHOUT-NEXT: ret void +// +void foo(int *__counted_by(count) x, unsigned count) { + *x++ = 0; + #ifdef WITH + // NOTE: It's necessary to exclude the count update with + // indirect_count_update disabled otherwise we'll get diagnostics about + // the a missing assignment to `x`. + count--; + #endif +} + +// CHECK-LABEL: define dso_local void @bar( +// CHECK-SAME: ptr noundef writeonly captures(address) [[X:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[COUNT]] to i64 +// CHECK-NEXT: [[ADD_PTR_IDX:%.*]] = shl nsw i64 [[IDX_EXT]], 2, !annotation [[META5]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[X]], i64 [[ADD_PTR_IDX]], !annotation [[META2]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[X]], i64 4 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]], !annotation [[META2]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[X]], [[BOUND_PTR_ARITH]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP15_NOT]], [[CMP_NOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[SUB:%.*]] = add nsw i32 [[COUNT]], -1, !annotation [[META12:![0-9]+]] +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[SUB]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[GEPDIFF:%.*]] = add nsw i64 [[ADD_PTR_IDX]], -4, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[GEPDIFF]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP29:%.*]] = icmp sgt i32 [[COUNT]], 0, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP29]], [[CMP26]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF13:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 0, ptr [[BOUND_PTR_ARITH]], align 4, !tbaa [[TBAA8]] +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @bar( +// WITHOUT-SAME: ptr noundef writeonly captures(none) initializes((4, 8)) [[X:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[X]], i64 4 +// WITHOUT-NEXT: store i32 0, ptr [[BOUND_PTR_ARITH]], align 4, !tbaa [[TBAA2]] +// WITHOUT-NEXT: ret void +// +void bar(int *__counted_by(count) x, int count) { + #ifdef WITH + // NOTE: It's necessary to exclude the count update with + // indirect_count_update disabled otherwise we'll get diagnostics about + // the a missing assignment to `x`. + count = count - 1; + #endif + *(x = x+1) = 0; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{[[META4:![0-9]+]]} +// CHECK: [[META4]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[META5]] = !{!"bounds-safety-generic", [[META6:![0-9]+]]} +// CHECK: [[META6]] = !{!"bounds-safety-missed-optimization-nuw", !"Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[PROF7]] = !{!"branch_weights", i32 1, i32 1048575} +// CHECK: [[TBAA8]] = !{[[META9:![0-9]+]], [[META9]], i64 0} +// CHECK: [[META9]] = !{!"int", [[META10:![0-9]+]], i64 0} +// CHECK: [[META10]] = !{!"omnipotent char", [[META11:![0-9]+]], i64 0} +// CHECK: [[META11]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META12]] = !{[[META6]]} +// CHECK: [[PROF13]] = !{!"branch_weights", i32 1048575, i32 1} +//. +// WITHOUT: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// WITHOUT: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0} +// WITHOUT: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// WITHOUT: [[META5]] = !{!"Simple C/C++ TBAA"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-or-null-from-bidi-O0.c b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-from-bidi-O0.c new file mode 100644 index 0000000000000..db14a37c3e32e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-from-bidi-O0.c @@ -0,0 +1,589 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3 + + +// RUN: %clang_cc1 -fbounds-safety -Wno-bounds-safety-init-list -O0 -triple arm64 -emit-llvm %s -o - | FileCheck %s + +#include + +// TODO: rdar://114446928 +// CHECK-LABEL: define dso_local ptr @foo +// CHECK-SAME: (ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int * __counted_by_or_null(len) foo(int * __bidi_indexable p, int len) { + return p; +} + +// CHECK-LABEL: define dso_local void @foo_assign +// CHECK-SAME: (ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[SIZE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P2:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP24:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: store i32 [[TMP0]], ptr [[SIZE]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZE]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END39:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END39]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR19]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP24]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB26]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR29]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP34:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP34]], label [[LAND_RHS36:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs36: +// CHECK-NEXT: [[CMP37:%.*]] = icmp sle i64 0, [[CONV]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP37]], [[LAND_RHS36]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP2]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END39]], !annotation [[META2]] +// CHECK: land.end39: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP3]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[P2]], align 8 +// CHECK-NEXT: ret void +// +void foo_assign(int * __bidi_indexable p, int len) { + int size = len; + int * __counted_by_or_null(size) p2 = p; +} + +// CHECK-LABEL: define dso_local void @bar +// CHECK-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP2]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +int * __bidi_indexable bar(int * __counted_by_or_null(len) p, int len) { + return p; +} + +// CHECK-LABEL: define dso_local void @bar_assign +// CHECK-SAME: (ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP2]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +void bar_assign(int * __counted_by_or_null(len) p, int len) { + int * __bidi_indexable p2 = p; +} + +// CHECK-LABEL: define dso_local void @ptr_oob +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[X:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[Q:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[X]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[X]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[X]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 -1 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END34:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP14]], label [[LAND_RHS:%.*]], label [[LAND_END34]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[P]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP32:%.*]] = icmp sle i64 4, [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP32]], label [[LAND_RHS33:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs33: +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ true, [[LAND_RHS33]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP13]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END34]], !annotation [[META2]] +// CHECK: land.end34: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP14]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR37:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB41:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR40]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR37]], ptr [[Q]], align 8 +// CHECK-NEXT: ret void +// +void ptr_oob(void) { + int x; + int *__bidi_indexable p = &x; + --p; + int *__counted_by_or_null(4) q = p; +} + +// CHECK-LABEL: define dso_local void @null_count_neg +// CHECK-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[COUNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[Q:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[P]], i8 0, i64 24, i1 false) +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 4, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: store ptr null, ptr [[Q]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END35:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END35]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR18]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 -42, [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP33]], label [[LAND_RHS34:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs34: +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ false, [[LAND_RHS34]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP0]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END35]], !annotation [[META2]] +// CHECK: land.end35: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP1]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: store i32 -42, ptr [[COUNT]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[TMP3]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP5]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +int *__bidi_indexable null_count_neg(void) { + int *__bidi_indexable p = 0; + int count; + int *__counted_by_or_null(count) q; + count = -42; + q = p; + return q; +} + +// CHECK-LABEL: define dso_local void @null_count_too_big +// CHECK-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[COUNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[Q:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[P]], i8 0, i64 24, i1 false) +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 4, !annotation [[META4]] +// CHECK-NEXT: store ptr null, ptr [[Q]], align 8, !annotation [[META4]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END35:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END35]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR18]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 42, [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP33]], label [[LAND_RHS34:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs34: +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ true, [[LAND_RHS34]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP0]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END35]], !annotation [[META2]] +// CHECK: land.end35: +// CHECK-NEXT: [[TMP2:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP1]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[COUNT]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[TMP3]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP5]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP11]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +int *__bidi_indexable null_count_too_big(void) { + int *__bidi_indexable p = 0; + int count; + int *__counted_by_or_null(count) q; + count = 42; + q = p; + return q; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-zero-init"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-or-null-from-bidi-O2.c b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-from-bidi-O2.c new file mode 100644 index 0000000000000..f9825f548bac9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-from-bidi-O2.c @@ -0,0 +1,137 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + + +// RUN: %clang_cc1 -fbounds-safety -Wno-bounds-safety-init-list -O2 -triple arm64 -emit-llvm %s -o - | FileCheck %s + +#include + +// TODO: rdar://114446928 +// CHECK-LABEL: define dso_local ptr @foo( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: ret ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] +// +int * __counted_by_or_null(len) foo(int * __bidi_indexable p, int len) { + return p; +} + +// CHECK-LABEL: define dso_local void @foo_assign( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[LEN]] to i64 +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP5_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP15_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP5_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[CONT:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// CHECK: [[LOR_RHS]]: +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP34:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// CHECK-NEXT: [[CMP37:%.*]] = icmp sgt i32 [[LEN]], -1, !annotation [[META2]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP37]], [[CMP34]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: ret void +// +void foo_assign(int * __bidi_indexable p, int len) { + int size = len; + int * __counted_by_or_null(size) p2 = p; +} + +// CHECK-LABEL: define dso_local void @bar( +// CHECK-SAME: ptr dead_on_unwind noalias writable writeonly sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 captures(none) initializes((0, 24)) [[AGG_RESULT:%.*]], ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[P]], null, !annotation [[META11:![0-9]+]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[LEN]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[ADD_PTR_SINK:%.*]] = select i1 [[DOTNOT]], ptr null, ptr [[ADD_PTR]] +// CHECK-NEXT: store ptr [[P]], ptr [[AGG_RESULT]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_RESULT]], i64 8 +// CHECK-NEXT: store ptr [[ADD_PTR_SINK]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_RESULT]], i64 16 +// CHECK-NEXT: store ptr [[P]], ptr [[TMP1]], align 8 +// CHECK-NEXT: ret void +// +int * __bidi_indexable bar(int * __counted_by_or_null(len) p, int len) { + return p; +} + +// CHECK-LABEL: define dso_local void @bar_assign( +// CHECK-SAME: ptr noundef readnone captures(none) [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR4:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret void +// +void bar_assign(int * __counted_by_or_null(len) p, int len) { + int * __bidi_indexable p2 = p; +} + +// CHECK-LABEL: define dso_local void @ptr_oob( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR5:[0-9]+]] { +// CHECK-NEXT: [[TRAP:.*:]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// +void ptr_oob(void) { + int x; + int *__bidi_indexable p = &x; + --p; + int *__counted_by_or_null(4) q = p; +} + +// CHECK-LABEL: define dso_local void @null_count_neg( +// CHECK-SAME: ptr dead_on_unwind noalias writable writeonly sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 captures(none) initializes((0, 24)) [[AGG_RESULT:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[AGG_RESULT]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +int *__bidi_indexable null_count_neg(void) { + int *__bidi_indexable p = 0; + int count; + int *__counted_by_or_null(count) q; + count = -42; + q = p; + return q; +} + +// CHECK-LABEL: define dso_local void @null_count_too_big( +// CHECK-SAME: ptr dead_on_unwind noalias writable writeonly sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 captures(none) initializes((0, 24)) [[AGG_RESULT:%.*]]) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[AGG_RESULT]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +int *__bidi_indexable null_count_too_big(void) { + int *__bidi_indexable p = 0; + int count; + int *__counted_by_or_null(count) q; + count = 42; + q = p; + return q; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// CHECK: [[META4]] = !{!"p1 int", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"any pointer", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"omnipotent char", [[META7:![0-9]+]], i64 0} +// CHECK: [[META7]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META8]] = !{!"bounds-safety-generic", [[META9:![0-9]+]]} +// CHECK: [[META9]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[PROF10]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META11]] = !{!"bounds-safety-check-ptr-neq-null"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-or-null-negative-O0.c b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-negative-O0.c new file mode 100644 index 0000000000000..8beac8cff4b43 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-negative-O0.c @@ -0,0 +1,468 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3 + + +// RUN: %clang_cc1 -fbounds-safety -O0 -triple arm64 -emit-llvm -Wno-bounds-safety-init-list %s -o - | FileCheck %s + +#include +#include + +// CHECK-LABEL: define dso_local void @to_bidi +// CHECK-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]], ptr noundef [[ARG:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARG_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARG]], ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: store i32 -1, ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END15:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP4:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP4]], label [[LAND_RHS:%.*]], label [[LAND_END15]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[UPPER7:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER7]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB9]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP10:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP10]], label [[LAND_RHS12:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs12: +// CHECK-NEXT: [[CMP13:%.*]] = icmp sle i64 0, [[CONV]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP13]], [[LAND_RHS12]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP11]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END15]], !annotation [[META2]] +// CHECK: land.end15: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP12]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[P]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ne ptr [[TMP15]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP17]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP16]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP15]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP20]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP23]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +int *__bidi_indexable to_bidi(int * arg) { + int len = -1; + int * __counted_by_or_null(len) p = arg; + return p; +} + +// CHECK-LABEL: define dso_local void @to_bidi_literal_count +// CHECK-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]], ptr noundef [[ARG:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARG_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARG]], ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END10:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[UPPER2:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER2]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP3:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP3]], label [[LAND_RHS:%.*]], label [[LAND_END10]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP8:%.*]] = icmp sle i64 -1, [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP8]], label [[LAND_RHS9:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs9: +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ false, [[LAND_RHS9]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP10]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END10]], !annotation [[META2]] +// CHECK: land.end10: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP11]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[P]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[TMP14]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP15]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP14]], i64 -1 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP18]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +int *__bidi_indexable to_bidi_literal_count(int * arg) { + int * __counted_by_or_null(-1) p = arg; + return p; +} + +// CHECK-LABEL: define dso_local void @to_bidi_const_count +// CHECK-SAME: (ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_RESULT:%.*]], ptr noundef [[ARG:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARG_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARG]], ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: store i32 -1, ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END10:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[UPPER2:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER2]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP3:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP3]], label [[LAND_RHS:%.*]], label [[LAND_END10]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP8:%.*]] = icmp sle i64 -1, [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP8]], label [[LAND_RHS9:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs9: +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ false, [[LAND_RHS9]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP10]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END10]], !annotation [[META2]] +// CHECK: land.end10: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP11]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[ARG_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[P]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[TMP14]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP15]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP14]], i64 -1 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP18]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +int *__bidi_indexable to_bidi_const_count(int * arg) { + const int len = -1; + int * __counted_by_or_null(len) p = arg; + return p; +} + +// CHECK-LABEL: define dso_local void @back_and_forth_to_bidi +// CHECK-SAME: (ptr noundef [[ARG:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARG_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARG]], ptr [[ARG_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 -1, ptr [[LEN]], align 4 +// CHECK-NEXT: store ptr null, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP2]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[ARG]], i64 24, i1 false) +// CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END38:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END38]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR18]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP9]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP33]], label [[LAND_RHS35:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs35: +// CHECK-NEXT: [[CMP36:%.*]] = icmp sle i32 0, [[TMP9]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP36]], [[LAND_RHS35]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP10]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END38]], !annotation [[META2]] +// CHECK: land.end38: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[TMP11]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[P]], align 8 +// CHECK-NEXT: store i32 [[TMP9]], ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[TMP13]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP15]], label [[BOUNDSCHECK_NOTNULL46:%.*]], label [[BOUNDSCHECK_NULL49:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull46: +// CHECK-NEXT: [[IDX_EXT47:%.*]] = sext i32 [[TMP14]] to i64 +// CHECK-NEXT: [[ADD_PTR48:%.*]] = getelementptr inbounds i32, ptr [[TMP13]], i64 [[IDX_EXT47]] +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR48]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP18]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT50:%.*]] +// CHECK: boundscheck.null49: +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[ARG]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT50]] +// CHECK: boundscheck.cont50: +// CHECK-NEXT: ret void +// +void back_and_forth_to_bidi(int * __bidi_indexable arg) { + int len = -1; + int * __counted_by_or_null(len) p = NULL; + arg = p; + p = arg; + len = len; + arg = p; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-or-null-negative-O2.c b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-negative-O2.c new file mode 100644 index 0000000000000..490c451c7d9c3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-negative-O2.c @@ -0,0 +1,76 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3 + + +// RUN: %clang_cc1 -fbounds-safety -O2 -triple arm64 -emit-llvm -Wno-bounds-safety-init-list %s -o - | FileCheck %s + +#include +#include + +// CHECK-LABEL: define dso_local void @to_bidi( +// CHECK-SAME: ptr dead_on_unwind noalias writable writeonly sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 captures(none) [[AGG_RESULT:%.*]], ptr noundef readnone captures(address_is_null) [[ARG:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[ARG]], null, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[BOUNDSCHECK_NULL:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: boundscheck.null: +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[AGG_RESULT]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +int *__bidi_indexable to_bidi(int * arg) { + int len = -1; + int * __counted_by_or_null(len) p = arg; + return p; +} + +// CHECK-LABEL: define dso_local void @to_bidi_literal_count( +// CHECK-SAME: ptr dead_on_unwind noalias writable writeonly sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 captures(none) [[AGG_RESULT:%.*]], ptr noundef readnone captures(address_is_null) [[ARG:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[ARG]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[BOUNDSCHECK_NULL:%.*]], label [[TRAP_CRITEDGE:%.*]], !annotation [[META2]] +// CHECK: trap.critedge: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: boundscheck.null: +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[AGG_RESULT]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +int *__bidi_indexable to_bidi_literal_count(int * arg) { + int * __counted_by_or_null(-1) p = arg; + return p; +} + +// CHECK-LABEL: define dso_local void @to_bidi_const_count( +// CHECK-SAME: ptr dead_on_unwind noalias writable writeonly sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 captures(none) [[AGG_RESULT:%.*]], ptr noundef readnone captures(address_is_null) [[ARG:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[ARG]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[BOUNDSCHECK_NULL:%.*]], label [[TRAP_CRITEDGE:%.*]], !annotation [[META2]] +// CHECK: trap.critedge: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: boundscheck.null: +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[AGG_RESULT]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +int *__bidi_indexable to_bidi_const_count(int * arg) { + const int len = -1; + int * __counted_by_or_null(len) p = arg; + return p; +} + +// CHECK-LABEL: define dso_local void @back_and_forth_to_bidi( +// CHECK-SAME: ptr noundef writeonly captures(none) initializes((0, 24)) [[ARG:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: boundscheck.null49: +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(24) [[ARG]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +void back_and_forth_to_bidi(int * __bidi_indexable arg) { + int len = -1; + int * __counted_by_or_null(len) p = NULL; + arg = p; + p = arg; + len = len; + arg = p; +} + diff --git a/clang/test/BoundsSafety/CodeGen/counted-by-or-null-to-counted-by-or-null.c b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-to-counted-by-or-null.c new file mode 100644 index 0000000000000..39628beee72fd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted-by-or-null-to-counted-by-or-null.c @@ -0,0 +1,152 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -fbounds-safety -O0 -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +struct Packet { + int *__counted_by_or_null(len) buf; + int len; +}; + +int *__counted_by_or_null(cnt) my_alloc_int(int cnt); + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P:%.*]] = alloca [[STRUCT_PACKET:%.*]], align 8 +// CHECK-NEXT: [[SIZ:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[LEN]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store i32 9, ptr [[SIZ]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZ]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @my_alloc_int(i32 noundef [[TMP0]]) +// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[CALL]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP4]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END35:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END35]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR18]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: lor.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB25]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[LAND_RHS34:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs34: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ true, [[LAND_RHS34]] ] +// CHECK-NEXT: br label [[LOR_END]], {{!annotation ![0-9]+}} +// CHECK: lor.end: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP8]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END35]], {{!annotation ![0-9]+}} +// CHECK: land.end35: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[TMP9]], [[LOR_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[BUF43:%.*]] = getelementptr inbounds nuw [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[BUF43]], align 8 +// CHECK-NEXT: [[LEN44:%.*]] = getelementptr inbounds nuw [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store i32 10, ptr [[LEN44]], align 8 +// CHECK-NEXT: ret void +// +void Foo(void) { + struct Packet p; + int siz = 9; + p.buf = my_alloc_int(siz); + p.len = 10; +} + + + + + + + + + + diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_const_call.c b/clang/test/BoundsSafety/CodeGen/counted_by_const_call.c new file mode 100644 index 0000000000000..aad1f20bfb3b6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_const_call.c @@ -0,0 +1,101 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +unsigned data_const_global_count __unsafe_late_const; + +// CHECK-LABEL: @get_const_count( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @data_const_global_count, align 4 +// CHECK-NEXT: ret i32 [[TMP0]] +// +__attribute__((const)) int get_const_count() { + return data_const_global_count; +} + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[COUNTED_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[COUNTED_PTR:%.*]], ptr [[COUNTED_PTR_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[NEW_PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_const_count() #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[CALL]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END33:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP16]], label [[LAND_RHS:%.*]], label [[LAND_END33]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB20]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR23]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP28:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP28]], label [[LAND_RHS30:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs30: +// CHECK-NEXT: [[CMP31:%.*]] = icmp sle i64 0, [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP31]], [[LAND_RHS30]] ] +// CHECK-NEXT: br label [[LAND_END33]], {{!annotation ![0-9]+}} +// CHECK: land.end33: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP0]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[COUNTED_PTR_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void test(int *__counted_by(get_const_count()) counted_ptr, int *__bidi_indexable new_ptr) { + counted_ptr = new_ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_const_count-O2.c b/clang/test/BoundsSafety/CodeGen/counted_by_const_count-O2.c new file mode 100644 index 0000000000000..48fe983fc4a19 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_const_count-O2.c @@ -0,0 +1,77 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +__attribute__((const)) static int get_const_count() { + return 4; +} + +// CHECK-LABEL: @test_assign_const_count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_assign_const_count_ok(int *__counted_by(get_const_count()) counted_ptr) { + int arr[4]; + counted_ptr = arr; +} + +// CHECK-LABEL: @test_assign_const_count_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void test_assign_const_count_fail(int *__counted_by(get_const_count()) counted_ptr) { + int arr[3]; + counted_ptr = arr; +} + +__attribute__((const)) static int get_const_size() { + return 10; +} + +// CHECK-LABEL: @test_assign_const_size_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_assign_const_size_ok(void *__sized_by(get_const_size()) sized_ptr) { + char arr[10]; + sized_ptr = arr; +} + +// CHECK-LABEL: @test_assign_const_size_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void test_assign_const_size_fail(void *__sized_by(get_const_size()) sized_ptr) { + char arr[9]; + sized_ptr = arr; +} + +// flexible array member +struct struct_flex { + long dummy; + long fam[__counted_by(get_const_count())]; +}; + +// CHECK-LABEL: @test_flexible_assign_const_count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_flexible_assign_const_count_ok(struct struct_flex *flex) { + char arr[sizeof(long) * 5]; + flex = (struct struct_flex *)arr; +} + +// CHECK-LABEL: @test_flexible_assign_const_count_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void test_flexible_assign_const_count_fail(struct struct_flex *flex) { + char arr[sizeof(long) * 4]; + flex = (struct struct_flex *)arr; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_global_assign.c b/clang/test/BoundsSafety/CodeGen/counted_by_global_assign.c new file mode 100644 index 0000000000000..ab9f92dc22e9a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_global_assign.c @@ -0,0 +1,92 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -emit-llvm -triple arm64 -fbounds-safety %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -triple arm64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o - | FileCheck %s +#include + +int glen; +int *__counted_by(glen) gptr; + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARG_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARG:%.*]], ptr [[ARG_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[ARG]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END28:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END28]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 0, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[LAND_RHS27:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs27: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP0:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS27]] ] +// CHECK-NEXT: br label [[LAND_END28]], {{!annotation ![0-9]+}} +// CHECK: land.end28: +// CHECK-NEXT: [[TMP1:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP0]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 0, ptr @glen, align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr @gptr, align 8 +// CHECK-NEXT: ret void +// +void test(int *__bidi_indexable arg) { + glen = 0; + gptr = arg; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_inc_constant_count_valid_codegen.c b/clang/test/BoundsSafety/CodeGen/counted_by_inc_constant_count_valid_codegen.c new file mode 100644 index 0000000000000..eb143ec4df649 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_inc_constant_count_valid_codegen.c @@ -0,0 +1,113 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// First make sure the previously buggy diagnostic is present +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -verify -Wno-error=bounds-safety-externally-counted-ptr-arith-constant-count %s +// RUN: %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -verify -Wno-error=bounds-safety-externally-counted-ptr-arith-constant-count %s + + +// Now make sure the codegen is as expected and identical with and without the new bounds checks +// RUN: %clang_cc1 -fbounds-safety -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety-bringup-missing-checks=all -Wno-bounds-safety-externally-counted-ptr-arith-constant-count %s -o - | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -O0 -triple arm64-apple-iphoneos -emit-llvm -fno-bounds-safety-bringup-missing-checks=all -Wno-bounds-safety-externally-counted-ptr-arith-constant-count %s -o - | FileCheck %s + + +#include +// CHECK-LABEL: define dso_local void @test_cb_const_inc( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 3 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 3, [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META2]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP13]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP15]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void test_cb_const_inc(int* __counted_by(3) p) { // expected-note{{__counted_by attribute is here}} + ++p; // expected-warning{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_incdec-O2.c b/clang/test/BoundsSafety/CodeGen/counted_by_incdec-O2.c new file mode 100644 index 0000000000000..36120ce6c2427 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_incdec-O2.c @@ -0,0 +1,67 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct Item { + int *__counted_by(len) buf; + unsigned len; +}; + +// CHECK-LABEL: @TestIncOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestIncOK() { + int arr[10]; + struct Item s = {arr, 10}; + s.buf++; + s.len--; +} + +// CHECK-LABEL: @TestIncOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestIncOK2() { + int arr[10]; + struct Item s = {arr, 10}; + s.buf++; + s.len = 9; +} + +// CHECK-LABEL: @TestIncOK3( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestIncOK3() { + int arr[10]; + struct Item s = {arr, 10}; + s.buf = arr + 1; + s.len--; +} + +// CHECK-LABEL: @TestIncTrap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestIncTrap() { + int arr[10]; + struct Item s = {arr, 10}; + s.buf++; + s.len = 10; +} + +// CHECK-LABEL: @TestIncTrap2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestIncTrap2() { + int arr[10]; + struct Item s = {arr, 10}; + s.buf++; + s.len = s.len; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_incdec.c b/clang/test/BoundsSafety/CodeGen/counted_by_incdec.c new file mode 100644 index 0000000000000..739f171cade64 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_incdec.c @@ -0,0 +1,115 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct Item { + int *__counted_by(len) buf; + unsigned len; +}; + +// CHECK-LABEL: @TestInc( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[S_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[S:%.*]], ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_ITEM:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_ITEM]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[BUF]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[LEN1:%.*]] = getelementptr inbounds nuw [[STRUCT_ITEM]], ptr [[TMP15]], i32 0, i32 1 +// CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[LEN1]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub i32 [[TMP16]], 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB5]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8 +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]] +// CHECK-NEXT: br i1 [[CMP16]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB19]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR22]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// CHECK-NEXT: [[CMP27:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP17:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP27]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[BUF]], align 8 +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP18]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[BUF]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = load i32, ptr [[LEN1]], align 8 +// CHECK-NEXT: [[DEC:%.*]] = add i32 [[TMP19]], -1 +// CHECK-NEXT: store i32 [[DEC]], ptr [[LEN1]], align 8 +// CHECK-NEXT: ret void +// +void TestInc(struct Item *s) { + s->buf++; + s->len--; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_or_null_call-O2.c b/clang/test/BoundsSafety/CodeGen/counted_by_or_null_call-O2.c new file mode 100644 index 0000000000000..1c209d902b89e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_or_null_call-O2.c @@ -0,0 +1,208 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 + +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s + +#include + +void foo(int *__counted_by_or_null(len) p, int len); + +// CHECK-LABEL: define dso_local void @caller_1( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @foo(ptr noundef null, i32 noundef 2) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: ret void +// +void caller_1() { + foo(0, 2); +} + +// CHECK-LABEL: define dso_local void @caller_2( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @foo(ptr noundef null, i32 noundef 0) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_2() { + foo(0, 0); +} + +// rdar://118117905 +// CHECK-LABEL: define dso_local void @caller_3( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[P]], null, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[CMP_NOT65:%.*]] = icmp slt i32 [[LEN]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT65]], !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont: +// CHECK-NEXT: tail call void @foo(ptr noundef [[P]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_3(int *__counted_by_or_null(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_4( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// +void caller_4() { + int i = 0; + foo(&i, -1); +} + +// CHECK-LABEL: define dso_local void @caller_5( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR3]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// +void caller_5() { + int i = 0; + foo(&i, 2); +} + +// CHECK-LABEL: define dso_local void @caller_6( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[LEN]], 0, !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[LAND_RHS:%.*]], !annotation [[META3]] +// CHECK: land.rhs: +// CHECK-NEXT: tail call void @foo(ptr noundef [[P]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// +void caller_6(int *__counted_by(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_7( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_9_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_9_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_9_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP_SROA_9_0_COPYLOAD]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[LAND_LHS_TRUE:%.*]], !annotation [[META3]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[AGG_TEMP_SROA_17_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_17_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_17_0_P_SROA_IDX]], align 8, !tbaa [[TBAA4:![0-9]+]] +// CHECK-NEXT: [[CMP27_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_17_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP27_NOT]], label [[TRAP]], label [[LAND_RHS:%.*]], !annotation [[META3]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[CONT:%.*]], label [[LOR_RHS:%.*]], !annotation [[META3]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[LEN]] to i64, !annotation [[META3]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_9_0_COPYLOAD]] to i64, !annotation [[META3]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META3]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META9:![0-9]+]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META3]] +// CHECK-NEXT: [[CMP51:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META3]] +// CHECK-NEXT: [[CMP54:%.*]] = icmp sgt i32 [[LEN]], -1, !annotation [[META3]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP54]], [[CMP51]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label [[CONT]], label [[TRAP]], !prof [[PROF11:![0-9]+]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont: +// CHECK-NEXT: tail call void @foo(ptr noundef [[AGG_TEMP_SROA_0_0_COPYLOAD]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_7(int *__bidi_indexable p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_8( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[P]], null, !annotation [[META3]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = icmp ult i32 [[LEN]], 2 +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[TOBOOL_NOT]], [[SPEC_SELECT]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF12:![0-9]+]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont: +// CHECK-NEXT: tail call void @foo(ptr noundef [[P]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_8(int *__single p, int len) { + foo(p, len); +} + +void bar(int *__counted_by(*len) *out, int *len); + +// CHECK-LABEL: define dso_local void @caller_9( +// CHECK-SAME: ptr noundef [[OUT:%.*]], ptr noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @bar(ptr noundef [[OUT]], ptr noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_9(int *__counted_by(*len) *out, int *len){ + bar(out, len); +} + +// CHECK-LABEL: define dso_local ptr @caller_10( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[COUNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[COUNT]]) #[[ATTR5]] +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 4, !annotation [[META13:![0-9]+]] +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr nonnull [[P]]) #[[ATTR5]] +// CHECK-NEXT: store ptr null, ptr [[P]], align 8, !annotation [[META13]] +// CHECK-NEXT: call void @bar(ptr noundef nonnull [[P]], ptr noundef nonnull [[COUNT]]) #[[ATTR5]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P]], align 8, !tbaa [[TBAA4]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[CMP_NOT83:%.*]] = icmp slt i32 [[TMP1]], 0, !annotation [[META3]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = select i1 [[DOTNOT]], i1 [[CMP_NOT83]], i1 false, !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[LAND_RHS:%.*]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META14:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META14]] +// CHECK: land.rhs: +// CHECK-NEXT: br i1 [[DOTNOT]], label [[LOR_RHS:%.*]], label [[CONT60:%.*]], !annotation [[META3]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CMP54:%.*]] = icmp sge i32 [[TMP1]], [[LEN]], !annotation [[META3]] +// CHECK-NEXT: [[CMP57:%.*]] = icmp sgt i32 [[LEN]], -1, !annotation [[META3]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP57]], [[CMP54]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label [[CONT60]], label [[TRAP]], !prof [[PROF11]], !annotation [[META3]] +// CHECK: cont60: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr nonnull [[P]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[COUNT]]) #[[ATTR5]] +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__counted_by_or_null(len) caller_10(int len) { + int count; + int *__counted_by_or_null(count) p; + bar(&p, &count); + p = p; // workaround for missing return bounds check + count = len; + return p; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META3]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA4]] = !{[[META5:![0-9]+]], [[META5]], i64 0} +// CHECK: [[META5]] = !{!"p1 int", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"any pointer", [[META7:![0-9]+]], i64 0} +// CHECK: [[META7]] = !{!"omnipotent char", [[META8:![0-9]+]], i64 0} +// CHECK: [[META8]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META9]] = !{!"bounds-safety-generic", [[META10:![0-9]+]]} +// CHECK: [[META10]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[PROF11]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[PROF12]] = !{!"branch_weights", i32 2097151, i32 1} +// CHECK: [[META13]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META14]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound", !"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_or_null_call.c b/clang/test/BoundsSafety/CodeGen/counted_by_or_null_call.c new file mode 100644 index 0000000000000..b6500d60d6a68 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_or_null_call.c @@ -0,0 +1,863 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s + +#include + +void foo(int *__counted_by_or_null(len) p, int len); + +// CHECK-LABEL: define dso_local void @caller_1( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2:![0-9]+]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @foo(ptr noundef null, i32 noundef 2) +// CHECK-NEXT: ret void +// +void caller_1() { + foo(0, 2); +} + +// CHECK-LABEL: define dso_local void @caller_2( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @foo(ptr noundef null, i32 noundef 0) +// CHECK-NEXT: ret void +// +void caller_2() { + foo(0, 0); +} + +// CHECK-LABEL: define dso_local void @caller_3( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP2]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END56:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END56]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP9]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB37]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP51:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP51]], label [[LAND_RHS53:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs53: +// CHECK-NEXT: [[CMP54:%.*]] = icmp sle i32 0, [[TMP9]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP54]], [[LAND_RHS53]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP13]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END56]], !annotation [[META2]] +// CHECK: land.end56: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[TMP14]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP57]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR58:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR59:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR58]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR60:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB61:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR60]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR62:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB63:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR62]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR59]], i32 noundef [[TMP9]]) +// CHECK-NEXT: ret void +// +void caller_3(int *__counted_by_or_null(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_4( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[I]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[I]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP3]], align 8 +// CHECK-NEXT: br i1 false, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR]], i32 noundef -1) +// CHECK-NEXT: ret void +// +void caller_4() { + int i = 0; + foo(&i, -1); +} + +// CHECK-LABEL: define dso_local void @caller_5( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[I]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[I]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP3]], align 8 +// CHECK-NEXT: br i1 false, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR]], i32 noundef 2) +// CHECK-NEXT: ret void +// +void caller_5() { + int i = 0; + foo(&i, 2); +} + +// CHECK-LABEL: define dso_local void @caller_6( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END56:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END56]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP5]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB37]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP51:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP51]], label [[LAND_RHS53:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs53: +// CHECK-NEXT: [[CMP54:%.*]] = icmp sle i32 0, [[TMP5]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP54]], [[LAND_RHS53]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP9]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END56]], !annotation [[META2]] +// CHECK: land.end56: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP10]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP57]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR58:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR59:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR58]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR60:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB61:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR60]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR62:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB63:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR62]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR59]], i32 noundef [[TMP5]]) +// CHECK-NEXT: ret void +// +void caller_6(int *__counted_by(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_7( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END56:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END56]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB37]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP51:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP51]], label [[LAND_RHS53:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs53: +// CHECK-NEXT: [[CMP54:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP54]], [[LAND_RHS53]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP4]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END56]], !annotation [[META2]] +// CHECK: land.end56: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP5]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP57]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR58:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR59:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR58]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR60:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB61:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR60]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR62:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB63:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR62]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR59]], i32 noundef [[TMP0]]) +// CHECK-NEXT: ret void +// +void caller_7(int *__bidi_indexable p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_8( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB2:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_PTR]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END29:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB6]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP13:%.*]] = icmp ule ptr [[WIDE_PTR_PTR8]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP13]], label [[LAND_RHS:%.*]], label [[LAND_END29]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[UPPER15:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER15]], ptr [[TMP11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB17]], ptr [[TMP13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR19]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP24:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP24]], label [[LAND_RHS26:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs26: +// CHECK-NEXT: [[CMP27:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP27]], [[LAND_RHS26]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP14]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END29]], !annotation [[META2]] +// CHECK: land.end29: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP15]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @foo(ptr noundef [[TMP0]], i32 noundef [[TMP1]]) +// CHECK-NEXT: ret void +// +void caller_8(int *__single p, int len) { + foo(p, len); +} + +void bar(int *__counted_by(*len) *out, int *len); + +// CHECK-LABEL: define dso_local void @caller_9( +// CHECK-SAME: ptr noundef [[OUT:%.*]], ptr noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[OUT]], ptr [[OUT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[LEN]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OUT_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: call void @bar(ptr noundef [[TMP0]], ptr noundef [[TMP1]]) +// CHECK-NEXT: ret void +// +void caller_9(int *__counted_by(*len) *out, int *len){ + bar(out, len); +} + +// CHECK-LABEL: define dso_local ptr @caller_10( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[COUNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP65:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP72:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 4, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: store ptr null, ptr [[P]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr ptr, ptr [[P]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[P]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[P]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i32, ptr [[COUNT]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[TMP7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP8]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT4:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META5]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: cont: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT4]], label [[TRAP3:%.*]], !annotation [[META6]] +// CHECK: trap3: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: cont4: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR7]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP11]], label [[BOUNDSCHECK_NOTNULL12:%.*]], label [[CONT16:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull12: +// CHECK-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_UB9]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT14:%.*]], label [[TRAP13:%.*]], !annotation [[META5]] +// CHECK: trap13: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: cont14: +// CHECK-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_LB11]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT16]], label [[TRAP15:%.*]], !annotation [[META6]] +// CHECK: trap15: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: cont16: +// CHECK-NEXT: call void @bar(ptr noundef [[WIDE_PTR_PTR]], ptr noundef [[WIDE_PTR_PTR7]]) +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP16:%.*]] = icmp ne ptr [[TMP14]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP16]], label [[BOUNDSCHECK_NOTNULL18:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull18: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP15]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP14]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP19]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP22]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[TMP23:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR21]], [[WIDE_PTR_UB28]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END62:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP39:%.*]] = icmp ule ptr [[WIDE_PTR_LB31]], [[WIDE_PTR_PTR34]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP39]], label [[LAND_RHS:%.*]], label [[LAND_END62]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR42]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP23]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP50]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP50]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR52:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR51]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP50]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP50]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB56:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR55]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB49]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR52]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP57:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP57]], label [[LAND_RHS59:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs59: +// CHECK-NEXT: [[CMP60:%.*]] = icmp sle i32 0, [[TMP23]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP24:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP60]], [[LAND_RHS59]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP25:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP24]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END62]], !annotation [[META2]] +// CHECK: land.end62: +// CHECK-NEXT: [[TMP26:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[TMP25]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP26]], label [[CONT64:%.*]], label [[TRAP63:%.*]], !annotation [[META2]] +// CHECK: trap63: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont64: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP65]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR66:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP65]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR67:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR66]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR68:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP65]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB69:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR68]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR70:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP65]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB71:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR70]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR67]], ptr [[P]], align 8 +// CHECK-NEXT: store i32 [[TMP23]], ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP27:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP29:%.*]] = icmp ne ptr [[TMP27]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP29]], label [[BOUNDSCHECK_NOTNULL73:%.*]], label [[BOUNDSCHECK_NULL76:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull73: +// CHECK-NEXT: [[IDX_EXT74:%.*]] = sext i32 [[TMP28]] to i64 +// CHECK-NEXT: [[ADD_PTR75:%.*]] = getelementptr inbounds i32, ptr [[TMP27]], i64 [[IDX_EXT74]] +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR75]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP32]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT77:%.*]] +// CHECK: boundscheck.null76: +// CHECK-NEXT: [[TMP33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP33]], align 8 +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP34]], align 8 +// CHECK-NEXT: [[TMP35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP35]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT77]] +// CHECK: boundscheck.cont77: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR78:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR79:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR78]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR80:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB81:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR80]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR82:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB83:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR82]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR79]] +// +int *__counted_by_or_null(len) caller_10(int len) { + int count; + int *__counted_by_or_null(count) p; + bar(&p, &count); + p = p; // workaround for missing return bounds check + count = len; + return p; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_to_counted_by-O2.c b/clang/test/BoundsSafety/CodeGen/counted_by_to_counted_by-O2.c new file mode 100644 index 0000000000000..1e776843b365f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_to_counted_by-O2.c @@ -0,0 +1,134 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +struct Packet { + int *__counted_by(len) buf; + int len; +}; + +struct PacketUnsigned { + int *__counted_by(len) buf; + unsigned long long len; +}; + +int *__counted_by(cnt) my_alloc_int(int cnt); +void *__sized_by(siz) my_alloc(int siz); +int *__sized_by(siz) my_alloc_int_siz(int siz); + + +// CHECK-LABEL: @TestCountIntFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int(i32 noundef 9) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestCountIntFail(void) { + struct Packet p; + int siz = 9; + p.buf = my_alloc_int(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestCountNegFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int(i32 noundef -1) #[[ATTR4]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestCountNegFail(void) { + struct Packet p; + int siz = -1; + p.buf = my_alloc_int(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestUCountNegFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int(i32 noundef -1) #[[ATTR4]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestUCountNegFail(void) { + struct PacketUnsigned p; + int siz = -1; + p.buf = my_alloc_int(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestCountNegRetFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int(i32 noundef -1) #[[ATTR4]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestCountNegRetFail(void) { + int *local_p = my_alloc_int(-1); //rdar://80808704 + (void)*local_p; +} + +// CHECK-LABEL: @TestCountIntOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int(i32 noundef 10) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void TestCountIntOK(void) { + struct Packet p; + int siz = 10; + p.buf = my_alloc_int(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestSizeFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc(i32 noundef 39) #[[ATTR4]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestSizeFail(void) { + struct Packet p; + int siz = 10 * sizeof(int) - 1; + p.buf = my_alloc(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestSizeOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc(i32 noundef 40) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void TestSizeOK(void) { + struct Packet p; + int siz = 10 * sizeof(int); + p.buf = my_alloc(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestIntSizeFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int_siz(i32 noundef 39) #[[ATTR4]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void TestIntSizeFail(void) { + struct Packet p; + int siz = 10 * sizeof(int) - 1; + p.buf = my_alloc_int_siz(siz); + p.len = 10; +} + +// CHECK-LABEL: @TestIntSizeOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @my_alloc_int_siz(i32 noundef 40) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void TestIntSizeOK(void) { + struct Packet p; + int siz = 10 * sizeof(int); + p.buf = my_alloc_int_siz(siz); + p.len = 10; +} diff --git a/clang/test/BoundsSafety/CodeGen/counted_by_to_counted_by.c b/clang/test/BoundsSafety/CodeGen/counted_by_to_counted_by.c new file mode 100644 index 0000000000000..188cd14e15eee --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/counted_by_to_counted_by.c @@ -0,0 +1,124 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -fbounds-safety -O0 -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -O0 -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +struct Packet { + int *__counted_by(len) buf; + int len; +}; + +int *__counted_by(cnt) my_alloc_int(int cnt); + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P:%.*]] = alloca [[STRUCT_PACKET:%.*]], align 8 +// CHECK-NEXT: [[SIZ:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[BUF]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[LEN]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store i32 9, ptr [[SIZ]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZ]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @my_alloc_int(i32 noundef [[TMP0]]) +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END28:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END28]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[LAND_RHS27:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs27: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS27]] ] +// CHECK-NEXT: br label [[LAND_END28]], {{!annotation ![0-9]+}} +// CHECK: land.end28: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP4]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: [[BUF36:%.*]] = getelementptr inbounds nuw [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[BUF36]], align 8 +// CHECK-NEXT: [[LEN37:%.*]] = getelementptr inbounds nuw [[STRUCT_PACKET]], ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store i32 10, ptr [[LEN37]], align 8 +// CHECK-NEXT: ret void +// +void Foo(void) { + struct Packet p; + int siz = 9; + p.buf = my_alloc_int(siz); + p.len = 10; +} + + + + + + + + + diff --git a/clang/test/BoundsSafety/CodeGen/debug-info-bounds_safety_composite_type.c b/clang/test/BoundsSafety/CodeGen/debug-info-bounds_safety_composite_type.c new file mode 100644 index 0000000000000..af6e310b0b12f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/debug-info-bounds_safety_composite_type.c @@ -0,0 +1,23 @@ + +// RUN: %clang_cc1 -fbounds-safety -emit-llvm -debug-info-kind=standalone -triple %itanium_abi_triple %s -o - | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -debug-info-kind=standalone -triple %itanium_abi_triple %s -o - | FileCheck %s + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "__bounds_safety::wide_ptr + +#include + +void f() { + int arr[10]; + int *ptr = &arr[0]; +} + +int test_attrs(int *__counted_by(num_elements - 1) ptr_counted_by, + int *__sized_by(num_elements * 4) ptr_sized_by, int *end, + int *__ended_by(end) ptr_ended_by, int num_elements) { + return 0; +} + +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "__bounds_safety::counted_by::num_elements - 1" +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "__bounds_safety::sized_by::num_elements * 4" +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "__bounds_safety::dynamic_range::ptr_ended_by::" +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "__bounds_safety::dynamic_range::::end" diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-bound-assign-dynamic-bound.c b/clang/test/BoundsSafety/CodeGen/dynamic-bound-assign-dynamic-bound.c new file mode 100644 index 0000000000000..410f5f1d3c8dd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-bound-assign-dynamic-bound.c @@ -0,0 +1,125 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -Wno-bounds-safety-init-list -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct buf { + void *__sized_by(len) data; + long len; +}; + +struct buf *slice; + +// CHECK-LABEL: @f( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LEN2:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[X:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[LEN2]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @slice, align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_BUF:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[LEN]], align 8 +// CHECK-NEXT: [[DATA:%.*]] = getelementptr inbounds nuw [[STRUCT_BUF]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[DATA]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[LEN2]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP6]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END39:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END39]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR23]], ptr [[TMP7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB25]], ptr [[TMP8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB27]], ptr [[TMP9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB19]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR29]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP34:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP34]], label [[LAND_RHS36:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs36: +// CHECK-NEXT: [[CMP37:%.*]] = icmp sle i64 0, [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP37]], [[LAND_RHS36]] ] +// CHECK-NEXT: br label [[LAND_END39]], {{!annotation ![0-9]+}} +// CHECK: land.end39: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP10]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[X]], align 8 +// CHECK-NEXT: ret void +// +void f(void) { + int len2; + void *__sized_by(len2) x = slice->data; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-bound-assign-subexpr-cast-crash.c b/clang/test/BoundsSafety/CodeGen/dynamic-bound-assign-subexpr-cast-crash.c new file mode 100644 index 0000000000000..609822ee7f9f3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-bound-assign-subexpr-cast-crash.c @@ -0,0 +1,76 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s | FileCheck %s + +#include + +struct S { int *__counted_by(len) ptr; int len; }; +int get_len(int *__counted_by(max_len) ptr, int max_len); + +void foo(void) { + struct S s; + int arr[10] = {0}; + int *ptr = arr; + s.len = get_len(ptr, 10); + s.ptr = (ptr); +} + +// CHECK-LABEL: bar +void bar(void) { + struct S s; + int arr[10] = {0}; + void *ptr = arr; +assignment: + s.len = 10; + s.ptr = (int *)ptr; +// CHECK-LABEL: assignment +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-BoundsCheckExpr +// CHECK: | | |-BinaryOperator {{.+}} 'int' '=' +// CHECK: | | | |-MemberExpr {{.+}} .len +// CHECK: | | | | `-DeclRefExpr {{.+}} 's' +// CHECK: | | | `-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-GetBoundExpr {{.+}} lower +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | |-GetBoundExpr {{.+}} upper +// CHECK: | | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | `-IntegerLiteral {{.+}} 10 +// CHECK: | `-OpaqueValueExpr [[ove_5]] +// CHECK: | `-CStyleCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-ImplicitCastExpr {{.+}} 'void *__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'ptr' +// CHECK: `-MaterializeSequenceExpr {{.+}} +// CHECK: |-BinaryOperator {{.+}} 'int *__single __counted_by(len)':'int *__single' '=' +// CHECK: | |-MemberExpr {{.+}} .ptr +// CHECK: | | `-DeclRefExpr {{.+}} 's' +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(len)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +// CHECK: |-OpaqueValueExpr [[ove_4]] {{.*}} 'int' +// CHECK: `-OpaqueValueExpr [[ove_5]] {{.*}} 'int *__bidi_indexable' +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-bound-assignment-in-comma-op.c b/clang/test/BoundsSafety/CodeGen/dynamic-bound-assignment-in-comma-op.c new file mode 100644 index 0000000000000..a4e4fe1d8a9ce --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-bound-assignment-in-comma-op.c @@ -0,0 +1,279 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @for_cond_inc( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[VAL:%.*]] = alloca i8, align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP58:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP77:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP80:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP90:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP93:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i64 [[LEN:%.*]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[TMP1]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP6:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP7:%.*]] = load i8, ptr [[WIDE_PTR_PTR]], align 1 +// CHECK-NEXT: store i8 [[TMP7]], ptr [[VAL]], align 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[CMP4:%.*]] = icmp sge i64 [[TMP9]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP4]]) +// CHECK-NEXT: [[ADD_PTR5:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP8]], i64 [[TMP9]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR5]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TMP13]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP14]], i64 1 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP20:%.*]] = load ptr, ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP20]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub i64 [[TMP22]], 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP13]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_PTR8]], [[WIDE_PTR_UB15]] +// CHECK-NEXT: br i1 [[CMP16]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8 +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_LB19]], [[WIDE_PTR_PTR22]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB30]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR33]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP38:%.*]] = icmp ule i64 [[SUB]], [[SUB_PTR_SUB]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP23:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT2]] ], [ [[CMP38]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP23]], label [[CONT40:%.*]], label [[TRAP39:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap39: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont40: +// CHECK-NEXT: [[TMP24:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP24]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[DEC:%.*]] = add i64 [[TMP25]], -1 +// CHECK-NEXT: store i64 [[DEC]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: br label [[FOR_COND:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[TMP26:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[CMP41:%.*]] = icmp ugt i64 [[TMP26]], 0 +// CHECK-NEXT: br i1 [[CMP41]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] +// CHECK: for.body: +// CHECK-NEXT: [[TMP27:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[CMP43:%.*]] = icmp sge i64 [[TMP28]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP43]]) +// CHECK-NEXT: [[ADD_PTR44:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP27]], i64 [[TMP28]] +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR44]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = icmp ult ptr [[WIDE_PTR_PTR46]], [[WIDE_PTR_UB48]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP32]], label [[CONT52:%.*]], label [[TRAP51:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap51: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont52: +// CHECK-NEXT: [[TMP33:%.*]] = icmp uge ptr [[WIDE_PTR_PTR46]], [[WIDE_PTR_LB50]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP33]], label [[CONT54:%.*]], label [[TRAP53:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap53: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont54: +// CHECK-NEXT: [[TMP34:%.*]] = load i8, ptr [[WIDE_PTR_PTR46]], align 1 +// CHECK-NEXT: [[CONV:%.*]] = sext i8 [[TMP34]] to i32 +// CHECK-NEXT: [[TMP35:%.*]] = load i8, ptr [[VAL]], align 1 +// CHECK-NEXT: [[CONV55:%.*]] = sext i8 [[TMP35]] to i32 +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[CONV55]], [[CONV]] +// CHECK-NEXT: [[CONV56:%.*]] = trunc i32 [[ADD]] to i8 +// CHECK-NEXT: store i8 [[CONV56]], ptr [[VAL]], align 1 +// CHECK-NEXT: br label [[FOR_INC:%.*]] +// CHECK: for.inc: +// CHECK-NEXT: [[TMP36:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP37:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[CMP59:%.*]] = icmp sge i64 [[TMP37]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP59]]) +// CHECK-NEXT: [[ADD_PTR61:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP36]], i64 [[TMP37]] +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP58]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP36]], ptr [[TMP38]], align 8 +// CHECK-NEXT: [[TMP39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP58]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR61]], ptr [[TMP39]], align 8 +// CHECK-NEXT: [[TMP40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP58]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP36]], ptr [[TMP40]], align 8 +// CHECK-NEXT: [[TMP41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP58]], i32 0, i32 0 +// CHECK-NEXT: [[TMP42:%.*]] = load ptr, ptr [[TMP41]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH62:%.*]] = getelementptr i8, ptr [[TMP42]], i64 1 +// CHECK-NEXT: [[TMP43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH62]], ptr [[TMP43]], align 8 +// CHECK-NEXT: [[TMP44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP58]], i32 0, i32 1 +// CHECK-NEXT: [[TMP45:%.*]] = load ptr, ptr [[TMP44]], align 8 +// CHECK-NEXT: [[TMP46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP45]], ptr [[TMP46]], align 8 +// CHECK-NEXT: [[TMP47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP58]], i32 0, i32 2 +// CHECK-NEXT: [[TMP48:%.*]] = load ptr, ptr [[TMP47]], align 8 +// CHECK-NEXT: [[TMP49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP48]], ptr [[TMP49]], align 8 +// CHECK-NEXT: [[TMP50:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[SUB63:%.*]] = sub i64 [[TMP50]], 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB70:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR69]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB73:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR72]], align 8 +// CHECK-NEXT: [[CMP74:%.*]] = icmp ule ptr [[WIDE_PTR_PTR66]], [[WIDE_PTR_UB73]] +// CHECK-NEXT: br i1 [[CMP74]], label [[LAND_LHS_TRUE76:%.*]], label [[LAND_END105:%.*]] +// CHECK: land.lhs.true76: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP77]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR78:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP77]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB79:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR78]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP80]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR81:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR82:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR81]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR83:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB84:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR83]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR85:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB86:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR85]], align 8 +// CHECK-NEXT: [[CMP87:%.*]] = icmp ule ptr [[WIDE_PTR_LB79]], [[WIDE_PTR_PTR82]] +// CHECK-NEXT: br i1 [[CMP87]], label [[LAND_RHS89:%.*]], label [[LAND_END105]] +// CHECK: land.rhs89: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP90]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR91:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP90]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB92:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR91]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP93]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR94:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP93]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR95:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR94]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR96:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP93]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB97:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR96]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR98:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP93]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB99:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR98]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST100:%.*]] = ptrtoint ptr [[WIDE_PTR_UB92]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST101:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR95]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB102:%.*]] = sub i64 [[SUB_PTR_LHS_CAST100]], [[SUB_PTR_RHS_CAST101]] +// CHECK-NEXT: [[CMP103:%.*]] = icmp ule i64 [[SUB63]], [[SUB_PTR_SUB102]] +// CHECK-NEXT: br label [[LAND_END105]] +// CHECK: land.end105: +// CHECK-NEXT: [[TMP51:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE76]] ], [ false, [[FOR_INC]] ], [ [[CMP103]], [[LAND_RHS89]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP51]], label [[CONT107:%.*]], label [[TRAP106:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap106: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont107: +// CHECK-NEXT: [[TMP52:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[INCDEC_PTR108:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP52]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR108]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP53:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[DEC109:%.*]] = add i64 [[TMP53]], -1 +// CHECK-NEXT: store i64 [[DEC109]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: br label [[FOR_COND]], !llvm.loop [[LOOP5:![0-9]+]] +// CHECK: for.end: +// CHECK-NEXT: [[TMP54:%.*]] = load i8, ptr [[VAL]], align 1 +// CHECK-NEXT: ret i8 [[TMP54]] +// +char for_cond_inc(char *__sized_by(len) p, unsigned long long len) { + char val = *p; + for (p++, len--; len > 0; p++, len--) { + val += *p; + } + return val; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-count-increment-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-count-increment-O2.c new file mode 100644 index 0000000000000..fcb036c010804 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-count-increment-O2.c @@ -0,0 +1,94 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Foo { + int *__counted_by(l) bp; + int l; +}; + +struct FooUnsigned { + int *__counted_by(l) bp; + unsigned long long l; +}; + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK(void) { + int arr[11]; + struct Foo f = {arr, 11}; + f.bp = f.bp; + f.l--; +} + +// CHECK-LABEL: @TestEdgeOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestEdgeOK(void) { + int arr[11]; + struct Foo f = {arr, 1}; + f.bp = f.bp; + f.l--; +} + +// CHECK-LABEL: @TestUnsignedEdgeOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestUnsignedEdgeOK(void) { + int arr[11]; + struct FooUnsigned f = {arr, 1}; + f.bp = f.bp; + f.l--; +} + +// CHECK-LABEL: @TestFail1( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestFail1(void) { + int arr[11]; + struct Foo f = {arr, 0}; + f.bp = f.bp; + f.l--; +} + + +// CHECK-LABEL: @TestUnsignedCountFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestUnsignedCountFail(void) { + int arr[11]; + struct FooUnsigned f = {arr, 0}; + f.bp = f.bp; + f.l--; +} + +struct Bar { + int *__counted_by(l - m) bp; + int l; + int m; +}; + +// CHECK-LABEL: @TestBarFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void TestBarFail(void) { + int arr[11]; + struct Bar f = {arr, 11, 1}; + f.bp = f.bp; + f.l = f.l; + f.m--; // FIXME: rdar://80811527 +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-count-increment.c b/clang/test/BoundsSafety/CodeGen/dynamic-count-increment.c new file mode 100644 index 0000000000000..0a8ea4f63632d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-count-increment.c @@ -0,0 +1,188 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct T { + int *__counted_by(l) bp; + int l; +}; + +// XXX: We have two additional null checks for 'tp' due to counted_by to bidi_indexable conversion. +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TP_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[TP:%.*]], ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: [[L:%.*]] = getelementptr inbounds nuw [[STRUCT_T:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[L]], align 8 +// CHECK-NEXT: [[BP:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[BP]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: [[L1:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[TMP6]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr [[L1]], align 8 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP7]], 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END32:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP16]], label [[LAND_RHS:%.*]], label [[LAND_END32]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[SUB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB19]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR22]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS29:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs29: +// CHECK-NEXT: [[CMP30:%.*]] = icmp sle i32 0, [[SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP8:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP30]], [[LAND_RHS29]] ] +// CHECK-NEXT: br label [[LAND_END32]], {{!annotation ![0-9]+}} +// CHECK: land.end32: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP8]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: [[BP40:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[TMP10]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[BP40]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr [[L1]], align 8 +// CHECK-NEXT: [[DEC:%.*]] = add nsw i32 [[TMP11]], -1 +// CHECK-NEXT: store i32 [[DEC]], ptr [[L1]], align 8 +// CHECK-NEXT: ret void +// +void foo(struct T *tp) { + tp->bp = tp->bp; + tp->l--; +} + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TP_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[TP:%.*]], ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 40, i1 false) +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: [[L:%.*]] = getelementptr inbounds nuw [[STRUCT_T:%.*]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[L]], align 8 +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP4]], 1 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[ADD]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: [[CMP6:%.*]] = icmp sle i32 0, [[ADD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP6]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[TP_ADDR]], align 8 +// CHECK-NEXT: [[BP:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[TMP6]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR10]], ptr [[BP]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr [[L]], align 8 +// CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP7]], 1 +// CHECK-NEXT: store i32 [[INC]], ptr [[L]], align 8 +// CHECK-NEXT: ret void +// +void bar(struct T *tp) { + int arr[10] = {0}; + tp->bp = arr; + tp->l++; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-calls-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-calls-O2.c new file mode 100644 index 0000000000000..3fe7525c16289 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-calls-O2.c @@ -0,0 +1,63 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +// CHECK-LABEL: @f_inout_count( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void f_inout_count(int *__counted_by(*out_len) buf, int *out_len) {} + +// CHECK-LABEL: @success( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void success() { + int arr[10]; + int len = 10; + f_inout_count(arr, &len); +} + +// CHECK-LABEL: @fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// +void fail() { + int arr[10]; + int len = 11; + f_inout_count(arr, &len); +} + +// CHECK-LABEL: @pass_out_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[OUT_LEN:%.*]], align 4, !tbaa [[TBAA3:![0-9]+]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[TMP0]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: ret void +// +void pass_out_len(int *__counted_by(*out_len) arr, int *out_len) { + f_inout_count(arr, out_len); +} + +// CHECK-LABEL: @pass_addr_of_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[CONT77:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation !{{[0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont77: +// CHECK-NEXT: ret void +// +void pass_addr_of_len(int *__counted_by(len) arr, int len) { + f_inout_count(arr, &len); +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-calls.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-calls.c new file mode 100644 index 0000000000000..ba4b20f23c87a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-calls.c @@ -0,0 +1,307 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @f_inout_count( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[OUT_LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store ptr [[OUT_LEN:%.*]], ptr [[OUT_LEN_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void f_inout_count(int *__counted_by(*out_len) buf, int *out_len) {} + +// CHECK-LABEL: @pass_addr_of_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARR:%.*]], ptr [[ARR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i32, ptr [[LEN_ADDR]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[LEN_ADDR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[LEN_ADDR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap3: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont4: +// CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP11]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB14]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_PTR16]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END61:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB24]], ptr [[TMP13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR26:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB30:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP38:%.*]] = icmp ule ptr [[WIDE_PTR_PTR26]], [[WIDE_PTR_PTR33]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP38]], label [[LAND_RHS:%.*]], label [[LAND_END61]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP49]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR44]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR51]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP56:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP56]], label [[LAND_RHS58:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs58: +// CHECK-NEXT: [[CMP59:%.*]] = icmp sle i64 0, [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP59]], [[LAND_RHS58]] ] +// CHECK-NEXT: br label [[LAND_END61]], {{!annotation ![0-9]+}} +// CHECK: land.end61: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT4]] ], [ [[TMP15]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT63:%.*]], label [[TRAP62:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap62: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont63: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP64]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB70:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR69]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ne ptr [[WIDE_PTR_PTR73]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT81:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ult ptr [[WIDE_PTR_PTR73]], [[WIDE_PTR_UB75]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP18]], label [[CONT79:%.*]], label [[TRAP78:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap78: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont79: +// CHECK-NEXT: [[TMP19:%.*]] = icmp uge ptr [[WIDE_PTR_PTR73]], [[WIDE_PTR_LB77]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP19]], label [[CONT81]], label [[TRAP80:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap80: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont81: +// CHECK-NEXT: call void @f_inout_count(ptr noundef [[WIDE_PTR_PTR66]], ptr noundef [[WIDE_PTR_PTR73]]) +// CHECK-NEXT: ret void +// +void pass_addr_of_len(int *__counted_by(len) arr, int len) { + f_inout_count(arr, &len); +} + +// CHECK-LABEL: @pass_inout_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[OUT_LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ARR:%.*]], ptr [[ARR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[OUT_LEN:%.*]], ptr [[OUT_LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[OUT_LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[OUT_LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr [[TMP6]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP7]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END51:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB14]], ptr [[TMP9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP28]], label [[LAND_RHS:%.*]], label [[LAND_END51]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB32]], ptr [[TMP10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR34]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR41]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP46:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP46]], label [[LAND_RHS48:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs48: +// CHECK-NEXT: [[CMP49:%.*]] = icmp sle i64 0, [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ [[CMP49]], [[LAND_RHS48]] ] +// CHECK-NEXT: br label [[LAND_END51]], {{!annotation ![0-9]+}} +// CHECK: land.end51: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP11]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: call void @f_inout_count(ptr noundef [[WIDE_PTR_PTR54]], ptr noundef [[TMP6]]) +// CHECK-NEXT: ret void +// +void pass_inout_len(int *__counted_by(*out_len) arr, int *out_len) { + f_inout_count(arr, out_len); +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-signed-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-signed-O2.c new file mode 100644 index 0000000000000..e38406aa074f3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-signed-O2.c @@ -0,0 +1,89 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = icmp sgt i32 [[TMP0]], 41, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo1(int *__counted_by(*len) buf, int *len) { + *len = 42; +} + +// CHECK-LABEL: @foo2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = icmp sgt i32 [[TMP0]], 41, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo2(int *__counted_by(*len) buf, int *len) { + buf = buf; + *len = 42; +} + +// CHECK-LABEL: @bar1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = icmp sgt i32 [[TMP0]], 41, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 10, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void bar1(int *__counted_by(*len) buf, int *len) { + *len = 42; +bb: + *len = 10; +} + +// CHECK-LABEL: @bar2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = icmp sgt i32 [[TMP0]], 41, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[TRAP]] +// +void bar2(int *__counted_by(*len) buf, int *len) { + *len = 42; +bb: + *len = 100; +} + +inline void baz1(int *__counted_by(*len) buf, int *len) { *len = 11; } + +// CHECK-LABEL: @baz2( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void baz2(void) { + int arr[10]; + int len = 10; + int *__counted_by(len) buf = arr; + baz1(buf, &len); +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-signed.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-signed.c new file mode 100644 index 0000000000000..fbee697c8881a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-signed.c @@ -0,0 +1,92 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store ptr [[LEN:%.*]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END28:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END28]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 42, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[LAND_RHS27:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs27: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS27]] ] +// CHECK-NEXT: br label [[LAND_END28]], {{!annotation ![0-9]+}} +// CHECK: land.end28: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP6]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: store i32 42, ptr [[TMP8]], align 4 +// CHECK-NEXT: ret void +// +void foo(int *__counted_by(*len) buf, int *len) { + *len = 42; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-unsigned-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-unsigned-O2.c new file mode 100644 index 0000000000000..cf781585e8e98 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-unsigned-O2.c @@ -0,0 +1,89 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp ugt i32 [[TMP0]], 41, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo1(int *__counted_by(*len) buf, unsigned *len) { + *len = 42; +} + +// CHECK-LABEL: @foo2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp ugt i32 [[TMP0]], 41, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo2(int *__counted_by(*len) buf, unsigned *len) { + buf = buf; + *len = 42; +} + +// CHECK-LABEL: @bar1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp ugt i32 [[TMP0]], 41, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 10, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void bar1(int *__counted_by(*len) buf, unsigned *len) { + *len = 42; +bb: + *len = 10; +} + +// CHECK-LABEL: @bar2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP26:%.*]] = icmp ugt i32 [[TMP0]], 41, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[LEN]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[TRAP]] +// +void bar2(int *__counted_by(*len) buf, unsigned *len) { + *len = 42; +bb: + *len = 100; +} + +inline void baz1(int *__counted_by(*len) buf, unsigned *len) { *len = 11; } + +// CHECK-LABEL: @baz2( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void baz2(void) { + int arr[10]; + unsigned len = 10; + int *__counted_by(len) buf = arr; + baz1(buf, &len); +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-unsigned.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-unsigned.c new file mode 100644 index 0000000000000..5acc82fb98dcb --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-counted-by-unsigned.c @@ -0,0 +1,87 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store ptr [[LEN:%.*]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 42, [[SUB_PTR_DIV]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP26]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: store i32 42, ptr [[TMP7]], align 4 +// CHECK-NEXT: ret void +// +void foo(int *__counted_by(*len) buf, unsigned *len) { + *len = 42; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-signed-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-signed-O2.c new file mode 100644 index 0000000000000..9f3280f328407 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-signed-O2.c @@ -0,0 +1,89 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp sgt i32 [[TMP0]], 39, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 40, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo1(int *__sized_by(*size) buf, int *size) { + *size = 40; +} + +// CHECK-LABEL: @foo2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp sgt i32 [[TMP0]], 39, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 40, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo2(int *__sized_by(*size) buf, int *size) { + buf = buf; + *size = 40; +} + +// CHECK-LABEL: @bar1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp sgt i32 [[TMP0]], 39, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 8, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void bar1(int *__sized_by(*size) buf, int *size) { + *size = 40; +bb: + *size = 8; +} + +// CHECK-LABEL: @bar2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp sgt i32 [[TMP0]], 39, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 40, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[TRAP]] +// +void bar2(int *__sized_by(*size) buf, int *size) { + *size = 40; +bb: + *size = 100; +} + +inline void baz1(int *__sized_by(*size) buf, int *size) { *size = 64; } + +// CHECK-LABEL: @baz2( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void baz2(void) { + int arr[10]; + int size = 40; + int *__sized_by(size) buf = arr; + baz1(buf, &size); +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-signed.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-signed.c new file mode 100644 index 0000000000000..a913da42736f4 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-signed.c @@ -0,0 +1,105 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store ptr [[SIZE:%.*]], ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END35:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END35]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR22]], ptr [[TMP6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB24]], ptr [[TMP7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB26]], ptr [[TMP8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 40, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[LAND_RHS34:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs34: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS34]] ] +// CHECK-NEXT: br label [[LAND_END35]], {{!annotation ![0-9]+}} +// CHECK: land.end35: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP9]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: store i32 40, ptr [[TMP11]], align 4 +// CHECK-NEXT: ret void +// +void foo(int *__sized_by(*size) buf, int *size) { + *size = 40; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-unsigned-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-unsigned-O2.c new file mode 100644 index 0000000000000..950ca7e73cd1e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-unsigned-O2.c @@ -0,0 +1,89 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp ugt i32 [[TMP0]], 39, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 40, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo1(int *__sized_by(*size) buf, unsigned *size) { + *size = 40; +} + +// CHECK-LABEL: @foo2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp ugt i32 [[TMP0]], 39, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 40, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo2(int *__sized_by(*size) buf, unsigned *size) { + buf = buf; + *size = 40; +} + +// CHECK-LABEL: @bar1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp ugt i32 [[TMP0]], 39, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 8, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void bar1(int *__sized_by(*size) buf, unsigned *size) { + *size = 40; +bb: + *size = 8; +} + +// CHECK-LABEL: @bar2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp ugt i32 [[TMP0]], 39, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: store i32 40, ptr [[SIZE]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[TRAP]] +// +void bar2(int *__sized_by(*size) buf, unsigned *size) { + *size = 40; +bb: + *size = 100; +} + +inline void baz1(int *__sized_by(*size) buf, unsigned *size) { *size = 64; } + +// CHECK-LABEL: @baz2( +// CHECK-NEXT: trap.i: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void baz2(void) { + int arr[10]; + unsigned size = 40; + int *__sized_by(size) buf = arr; + baz1(buf, &size); +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-unsigned.c b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-unsigned.c new file mode 100644 index 0000000000000..0b1fb7c1b0eab --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-inout-count-sized-by-unsigned.c @@ -0,0 +1,100 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store ptr [[SIZE:%.*]], ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]] +// CHECK-NEXT: br i1 [[CMP15]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR22]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB24]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB26]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR28]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp sle i64 40, [[SUB_PTR_SUB]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP33]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[SIZE_ADDR]], align 8 +// CHECK-NEXT: store i32 40, ptr [[TMP10]], align 4 +// CHECK-NEXT: ret void +// +void foo(int *__sized_by(*size) buf, unsigned *size) { + *size = 40; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list-O2.c b/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list-O2.c new file mode 100644 index 0000000000000..18e3e95e62e33 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list-O2.c @@ -0,0 +1,96 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct RangePtrs { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + void *end; +}; + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK(void) { + int arr[10]; + struct RangePtrs rptrs = { arr, arr, arr + 10 }; +} + +// CHECK-LABEL: @TestRangeFail1( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail1(void) { + int arr[10]; + struct RangePtrs rptrs = { arr + 2, arr + 1, arr + 10 }; +} + +// CHECK-LABEL: @TestRangeFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail2(void) { + int arr[10]; + struct RangePtrs rptrs = { arr, arr + 2, arr + 1 }; +} + +// CHECK-LABEL: @TestStartFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestStartFail(void) { + int arr[10]; + struct RangePtrs rptrs = { arr - 1, arr, arr + 10 }; +} + +// CHECK-LABEL: @TestIterFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 44 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP34_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP34_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT86:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont86: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void TestIterFail(void) { + int arr[10]; + struct RangePtrs rptrs = { arr, arr + 11, arr + 11 }; +} + +// CHECK-LABEL: @TestEndFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 44 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP43_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP28_NOT]], [[CMP43_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT55:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont55: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void TestEndFail(void) { + int arr[10]; + struct RangePtrs rptrs = { arr, arr, arr + 11 }; +} diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list-null.c b/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list-null.c new file mode 100644 index 0000000000000..646ba97cb1ca0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list-null.c @@ -0,0 +1,31 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o /dev/null 2> /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null 2> /dev/null +// This is to ensure no crash in code gen. A general version of filecheck is in dynamic-range-init-list.c. + +#include + +struct RangePtrs { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + void *end; +}; + +void Test1(void) { + int arr[10]; + int *ptr = arr; + struct RangePtrs rptrs = { ptr, 0, arr + 10 }; +} + +void Test2(void) { + int arr[10]; + int *ptr = arr; + struct RangePtrs rptrs = { ptr, arr, 0 }; +} + +void Test3(void) { + int arr[10]; + int *ptr = arr; + struct RangePtrs rptrs = { 0, arr, arr + 1 }; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list.c b/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list.c new file mode 100644 index 0000000000000..7a98ed342ed8d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/dynamic-range-init-list.c @@ -0,0 +1,234 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct RangePtrs { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + void *end; +}; + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[RPTRS:%.*]] = alloca [[STRUCT_RANGEPTRS:%.*]], align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP73:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP76:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP87:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP94:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP101:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 11 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[ARRAYDECAY6:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER7:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY6]], i64 10 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY6]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER7]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY6]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 0 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH8:%.*]] = getelementptr i32, ptr [[TMP19]], i64 11 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH8]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 1 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 2 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP_TMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP_TMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP28]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP_TMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP29]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR11]], [[WIDE_PTR_UB18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP33:%.*]] = icmp ule ptr [[WIDE_PTR_PTR21]], [[WIDE_PTR_PTR28]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP33]], label [[LAND_RHS:%.*]], label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[PTR]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP44:%.*]] = icmp ule ptr [[WIDE_PTR_LB36]], [[WIDE_PTR_PTR39]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP30:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP44]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP30]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP45]], ptr align 8 [[TMP_TMP4]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR47:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR46]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB51:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR50]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP55:%.*]] = icmp ule ptr [[WIDE_PTR_PTR47]], [[WIDE_PTR_UB54]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP55]], label [[LAND_LHS_TRUE56:%.*]], label [[LAND_END84:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true56: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP57]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR58:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR59:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR58]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR60:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB61:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR60]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR62:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP57]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB63:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR62]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[TMP_TMP4]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP64]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP64]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP64]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB70:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR69]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP71:%.*]] = icmp ule ptr [[WIDE_PTR_PTR59]], [[WIDE_PTR_PTR66]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP71]], label [[LAND_RHS72:%.*]], label [[LAND_END84]], {{!annotation ![0-9]+}} +// CHECK: land.rhs72: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP73]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR74:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP73]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB75:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR74]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP76]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR77:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP76]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR78:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR77]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR79:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP76]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB80:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR79]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR81:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP76]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB82:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR81]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP83:%.*]] = icmp ule ptr [[WIDE_PTR_LB75]], [[WIDE_PTR_PTR78]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END84]], {{!annotation ![0-9]+}} +// CHECK: land.end84: +// CHECK-NEXT: [[TMP31:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE56]] ], [ false, [[CONT]] ], [ [[CMP83]], [[LAND_RHS72]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP31]], label [[CONT86:%.*]], label [[TRAP85:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap85: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont86: +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_RANGEPTRS]], ptr [[RPTRS]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP87]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR88:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP87]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR89:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR88]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR90:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP87]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB91:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR90]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR92:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP87]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB93:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR92]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR89]], ptr [[START]], align 8 +// CHECK-NEXT: [[ITER:%.*]] = getelementptr inbounds nuw [[STRUCT_RANGEPTRS]], ptr [[RPTRS]], i32 0, i32 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP94]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR95:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP94]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR96:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR95]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR97:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP94]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB98:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR97]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR99:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP94]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB100:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR99]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR96]], ptr [[ITER]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_RANGEPTRS]], ptr [[RPTRS]], i32 0, i32 2 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP101]], ptr align 8 [[TMP_TMP4]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR102:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP101]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR103:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR102]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR104:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP101]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB105:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR104]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR106:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP101]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB107:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR106]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR103]], ptr [[END]], align 8 +// CHECK-NEXT: ret void +// +void Test(void) { + int arr[10]; + int *ptr = arr; + struct RangePtrs rptrs = { ptr, arr + 11, arr + 11 }; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended-by-attribute-only-mode-O0.c b/clang/test/BoundsSafety/CodeGen/ended-by-attribute-only-mode-O0.c new file mode 100644 index 0000000000000..65499a5626d1a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended-by-attribute-only-mode-O0.c @@ -0,0 +1,142 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -x c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x c++ -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fexperimental-bounds-safety-attributes -x objective-c++ -emit-llvm %s -o - | FileCheck %s + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// CHECK-LABEL: define dso_local i32 @fn_deref( +// CHECK-SAME: ptr noundef [[P:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: ret i32 [[TMP1]] +// +int fn_deref(int *__ended_by(end) p, int *end) { + return *p; +} + +// CHECK-LABEL: define dso_local i32 @fn_subscript( +// CHECK-SAME: ptr noundef [[P:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP2]], 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret i32 [[TMP3]] +// +int fn_subscript(int *__ended_by(end) p, int *end) { + return p[*end - 1]; +} + +// CHECK-LABEL: define dso_local ptr @fn_assign( +// CHECK-SAME: ptr noundef [[P:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 -42 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR1:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 42 +// CHECK-NEXT: store ptr [[ADD_PTR1]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP2]] +// +int *fn_assign(int *__ended_by(end) p, int *end) { + end = end - 42; + p = p + 42; + return p; +} + +struct bar { + int *__ended_by(end) q; + int *end; +}; + +// CHECK-LABEL: define dso_local i32 @struct_deref( +// CHECK-SAME: ptr noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 [[TMP2]] +// +int struct_deref(struct bar *b) { + return *b->q; +} + +// CHECK-LABEL: define dso_local i32 @struct_subscript( +// CHECK-SAME: ptr noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR]], ptr [[TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[END]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP4]], 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret i32 [[TMP5]] +// +int struct_subscript(struct bar *b) { + return b->q[*b->end - 1]; +} + +// CHECK-LABEL: define dso_local void @struct_assign( +// CHECK-SAME: ptr noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[B]], ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[END]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 -42 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[END1:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR]], ptr [[TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[END1]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR]], ptr [[TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[Q]], align 8 +// CHECK-NEXT: [[ADD_PTR2:%.*]] = getelementptr inbounds i32, ptr [[TMP4]], i64 42 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[B_ADDR]], align 8 +// CHECK-NEXT: [[Q3:%.*]] = getelementptr inbounds nuw [[STRUCT_BAR]], ptr [[TMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ADD_PTR2]], ptr [[Q3]], align 8 +// CHECK-NEXT: ret void +// +void struct_assign(struct bar *b) { + b->end = b->end - 42; + b->q = b->q + 42; +} + +#ifdef __cplusplus +} +#endif diff --git a/clang/test/BoundsSafety/CodeGen/ended-by-nested-assignments-O0.c b/clang/test/BoundsSafety/CodeGen/ended-by-nested-assignments-O0.c new file mode 100644 index 0000000000000..d0820fdb2fa50 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended-by-nested-assignments-O0.c @@ -0,0 +1,362 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 5 + + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -fbounds-safety-bringup-missing-checks=indirect_count_update %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s -o - | FileCheck --check-prefix WITHOUT %s +#include + +// CHECK-LABEL: define dso_local void @foo( +// CHECK-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 -1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH3:%.*]] = getelementptr i32, ptr [[TMP22]], i64 1 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH3]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP28:%.*]] = load ptr, ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP28]], ptr [[TMP29]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP22:%.*]] = icmp ule ptr [[WIDE_PTR_PTR10]], [[WIDE_PTR_PTR17]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP22]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp ule ptr [[WIDE_PTR_LB25]], [[WIDE_PTR_PTR28]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP30:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP33]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP30]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP31:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP31]], i32 -1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: store i32 0, ptr [[TMP31]], align 4 +// CHECK-NEXT: [[TMP32:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[INCDEC_PTR34:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP32]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR34]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store i32 0, ptr [[TMP32]], align 4 +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @foo( +// WITHOUT-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0:[0-9]+]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// WITHOUT-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// WITHOUT-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP0:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i32 -1 +// WITHOUT-NEXT: store ptr [[INCDEC_PTR]], ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: store i32 0, ptr [[TMP0]], align 4 +// WITHOUT-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: [[INCDEC_PTR1:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP1]], i32 1 +// WITHOUT-NEXT: store ptr [[INCDEC_PTR1]], ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: store i32 0, ptr [[TMP1]], align 4 +// WITHOUT-NEXT: ret void +// +void foo(int *__ended_by(end) start, int * end) { + *end-- = 0; + *start++ = 0; +} + +// CHECK-LABEL: define dso_local void @bar( +// CHECK-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH3:%.*]] = getelementptr i32, ptr [[TMP22]], i64 -1 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH3]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP28:%.*]] = load ptr, ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP28]], ptr [[TMP29]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP22:%.*]] = icmp ule ptr [[WIDE_PTR_PTR10]], [[WIDE_PTR_PTR17]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP22]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP33:%.*]] = icmp ule ptr [[WIDE_PTR_LB25]], [[WIDE_PTR_PTR28]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP30:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP33]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP30]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store i32 0, ptr [[WIDE_PTR_PTR36]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR43:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB47:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR46]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR43]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: store i32 0, ptr [[WIDE_PTR_PTR43]], align 4 +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @bar( +// WITHOUT-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// WITHOUT-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// WITHOUT-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// WITHOUT-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// WITHOUT-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// WITHOUT-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// WITHOUT-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// WITHOUT-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// WITHOUT-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// WITHOUT-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// WITHOUT-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// WITHOUT-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// WITHOUT-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// WITHOUT-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// WITHOUT-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 1 +// WITHOUT-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// WITHOUT-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// WITHOUT-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// WITHOUT-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// WITHOUT-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// WITHOUT-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// WITHOUT-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// WITHOUT-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// WITHOUT-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// WITHOUT-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// WITHOUT-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// WITHOUT-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// WITHOUT-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// WITHOUT-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: store i32 0, ptr [[WIDE_PTR_PTR]], align 4 +// WITHOUT-NEXT: [[TMP15:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP16:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP17:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// WITHOUT-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// WITHOUT-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// WITHOUT-NEXT: store ptr [[TMP17]], ptr [[TMP19]], align 8 +// WITHOUT-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// WITHOUT-NEXT: store ptr [[TMP16]], ptr [[TMP20]], align 8 +// WITHOUT-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// WITHOUT-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// WITHOUT-NEXT: [[BOUND_PTR_ARITH3:%.*]] = getelementptr i32, ptr [[TMP22]], i64 -1 +// WITHOUT-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// WITHOUT-NEXT: store ptr [[BOUND_PTR_ARITH3]], ptr [[TMP23]], align 8 +// WITHOUT-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// WITHOUT-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// WITHOUT-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// WITHOUT-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// WITHOUT-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// WITHOUT-NEXT: [[TMP28:%.*]] = load ptr, ptr [[TMP27]], align 8 +// WITHOUT-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// WITHOUT-NEXT: store ptr [[TMP28]], ptr [[TMP29]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// WITHOUT-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// WITHOUT-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// WITHOUT-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// WITHOUT-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// WITHOUT-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[END_ADDR]], align 8 +// WITHOUT-NEXT: store i32 0, ptr [[WIDE_PTR_PTR5]], align 4 +// WITHOUT-NEXT: ret void +// +void bar(int *__ended_by(end) start, int * end) { + *(start = start+1) = 0; + *(end = end-1) = 0; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +//. diff --git a/clang/test/BoundsSafety/CodeGen/ended-by-nested-assignments-O2.c b/clang/test/BoundsSafety/CodeGen/ended-by-nested-assignments-O2.c new file mode 100644 index 0000000000000..5cd24d89162b2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended-by-nested-assignments-O2.c @@ -0,0 +1,83 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 5 + + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -O2 -fbounds-safety-bringup-missing-checks=indirect_count_update %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm -O2 -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s -o - | FileCheck --check-prefix WITHOUT %s +#include + +// CHECK-LABEL: define dso_local void @foo( +// CHECK-SAME: ptr noundef writeonly captures(address) [[START:%.*]], ptr noundef writeonly captures(address) [[END:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[END]], i64 -4 +// CHECK-NEXT: [[BOUND_PTR_ARITH3:%.*]] = getelementptr i8, ptr [[START]], i64 4 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[END]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[CMP22_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH3]], [[BOUND_PTR_ARITH]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP22_NOT]], !annotation [[META2]] +// CHECK-NEXT: [[CMP33_NOT:%.*]] = icmp ugt ptr [[START]], [[BOUND_PTR_ARITH3]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND41:%.*]] = or i1 [[CMP33_NOT]], [[OR_COND]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND41]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 0, ptr [[END]], align 4, !tbaa [[TBAA4:![0-9]+]] +// CHECK-NEXT: store i32 0, ptr [[START]], align 4, !tbaa [[TBAA4]] +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @foo( +// WITHOUT-SAME: ptr noundef writeonly captures(none) initializes((0, 4)) [[START:%.*]], ptr noundef writeonly captures(none) initializes((0, 4)) [[END:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: store i32 0, ptr [[END]], align 4, !tbaa [[TBAA2:![0-9]+]] +// WITHOUT-NEXT: store i32 0, ptr [[START]], align 4, !tbaa [[TBAA2]] +// WITHOUT-NEXT: ret void +// +void foo(int *__ended_by(end) start, int * end) { + *end-- = 0; + *start++ = 0; +} + +// CHECK-LABEL: define dso_local void @bar( +// CHECK-SAME: ptr noundef writeonly captures(address) [[START:%.*]], ptr noundef writeonly captures(address) [[END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[START]], i64 4 +// CHECK-NEXT: [[BOUND_PTR_ARITH3:%.*]] = getelementptr i8, ptr [[END]], i64 -4 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH3]], [[END]], !annotation [[META2]] +// CHECK-NEXT: [[CMP22_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[BOUND_PTR_ARITH3]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP22_NOT]], !annotation [[META2]] +// CHECK-NEXT: [[CMP33_NOT:%.*]] = icmp ugt ptr [[START]], [[BOUND_PTR_ARITH]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND52:%.*]] = or i1 [[CMP33_NOT]], [[OR_COND]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND52]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: store i32 0, ptr [[BOUND_PTR_ARITH]], align 4, !tbaa [[TBAA4]] +// CHECK-NEXT: store i32 0, ptr [[BOUND_PTR_ARITH3]], align 4, !tbaa [[TBAA4]] +// CHECK-NEXT: ret void +// +// WITHOUT-LABEL: define dso_local void @bar( +// WITHOUT-SAME: ptr noundef writeonly captures(none) initializes((4, 8)) [[START:%.*]], ptr noundef writeonly captures(none) initializes((-4, 0)) [[END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// WITHOUT-NEXT: [[ENTRY:.*:]] +// WITHOUT-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[START]], i64 4 +// WITHOUT-NEXT: store i32 0, ptr [[BOUND_PTR_ARITH]], align 4, !tbaa [[TBAA2]] +// WITHOUT-NEXT: [[BOUND_PTR_ARITH3:%.*]] = getelementptr i8, ptr [[END]], i64 -4 +// WITHOUT-NEXT: store i32 0, ptr [[BOUND_PTR_ARITH3]], align 4, !tbaa [[TBAA2]] +// WITHOUT-NEXT: ret void +// +void bar(int *__ended_by(end) start, int * end) { + *(start = start+1) = 0; + *(end = end-1) = 0; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048577, i32 1048575} +// CHECK: [[TBAA4]] = !{[[META5:![0-9]+]], [[META5]], i64 0} +// CHECK: [[META5]] = !{!"int", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"omnipotent char", [[META7:![0-9]+]], i64 0} +// CHECK: [[META7]] = !{!"Simple C/C++ TBAA"} +//. +// WITHOUT: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// WITHOUT: [[META3]] = !{!"int", [[META4:![0-9]+]], i64 0} +// WITHOUT: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// WITHOUT: [[META5]] = !{!"Simple C/C++ TBAA"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks-O2.c new file mode 100644 index 0000000000000..47f28c24db2e9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks-O2.c @@ -0,0 +1,85 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct S { + int *__ended_by(end) start; + int *end; +}; + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 +// +int TestOK() { + int arr[10]; + struct S s; + s.start = arr; + s.end = arr + 10; + return 0; +} + +// CHECK-LABEL: @TestStartFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[CMP24_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP35_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP24_NOT]], [[CMP35_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: ret i32 0 +// +int TestStartFail() { + int arr[10]; + struct S s; + s.start = arr - 1; + s.end = arr + 10; + return 0; +} + +// CHECK-LABEL: @TestEndFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 44 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP22_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP22_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: ret i32 0 +// +int TestEndFail() { + int arr[10]; + struct S s; + s.start = arr; + s.end = arr + 11; + return 0; +} + +// CHECK-LABEL: @TestRangeFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int TestRangeFail() { + int arr[10]; + struct S s; + s.start = arr + 1; + s.end = arr; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks.c new file mode 100644 index 0000000000000..e25fdecf693eb --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks.c @@ -0,0 +1,149 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct S { + int *__ended_by(end) start; + int *end; +}; + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 -1 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY3]], i64 10 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i32, ptr [[TMP16]], i64 11 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH5]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB9]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: [[CMP24:%.*]] = icmp ule ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_PTR19]] +// CHECK-NEXT: br i1 [[CMP24]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: [[CMP35:%.*]] = icmp ule ptr [[WIDE_PTR_LB27]], [[WIDE_PTR_PTR30]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP24:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP35]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP24]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[START43:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[START43]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: [[END51:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR46]], ptr [[END51]], align 8 +// CHECK-NEXT: ret i32 0 +// +int Foo() { + int arr[10]; + struct S s; + s.start = arr - 1; + s.end = arr + 11; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_bidi.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_bidi.c new file mode 100644 index 0000000000000..7f4e149426c7f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_bidi.c @@ -0,0 +1,38 @@ + +// XFAIL: * +// BoundsSafety doesn't allow ended_by to reference a __bidi_indexable or __indexable pointer. +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct S { + int *__ended_by(end) start; + int *__bidi_indexable end; +}; + +int Foo(void) { + int arr[10]; + struct S s = {arr, arr + 10}; + int *ptr = s.start; + return 0; +} + +// CHECK: [[STRUCT_TY:%.*]] = type { i32*, [[BIDI_TY:%.*]] } +// CHECK: [[BIDI_TY]] = type { i32*, i32*, i32* } + +// CHECK-LABEL: @Foo +// CHECK: getelementptr inbounds [[STRUCT_TY]], [[STRUCT_TY]]* [[S_ALLOC:%.*]], i32 0, i32 0 +// ... +// CHECK: [[S_START_ADDR:%.*]] = getelementptr inbounds [[STRUCT_TY]], [[STRUCT_TY]]* [[S_ALLOC:%.*]], i32 0, i32 0 +// CHECK: [[S_START:%.*]] = load i32*, i32** [[S_START_ADDR]], align 8 +// CHECK: [[PTR_PTR:%.*]] = getelementptr inbounds [[BIDI_TY]], [[BIDI_TY]]* [[PTR:%.*]], i32 0, i32 0 +// CHECK: store i32* [[S_START]], i32** [[PTR_PTR]], align 8 +// CHECK: [[S_END:%.*]] = getelementptr inbounds [[STRUCT_TY]], [[STRUCT_TY]]* [[S_ALLOC]], i32 0, i32 1 +// ... +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64{{.*}} +// CHECK: [[TMP_END_ADDR:%.*]] = getelementptr inbounds [[BIDI_TY]], [[BIDI_TY]]* [[TEMP_END_LOC:%.*]], i32 0, i32 0 +// CHECK: [[TMP_END_PTR:%.*]] = load i32*, i32** [[TMP_END_ADDR]], align 8 +// CHECK: [[PTR_UPPER:%.*]] = getelementptr inbounds [[BIDI_TY]], [[BIDI_TY]]* [[PTR]], i32 0, i32 1 +// CHECK: store i32* [[TMP_END_PTR]], i32** [[PTR_UPPER]], align 8 +// CHECK: [[PTR_LOWER:%.*]] = getelementptr inbounds [[BIDI_TY]], [[BIDI_TY]]* [[PTR]], i32 0, i32 2 +// CHECK: store i32* [[S_START]], i32** [[PTR_LOWER]], align 8 diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_parms-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_parms-O2.c new file mode 100644 index 0000000000000..f0265790999c6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_parms-O2.c @@ -0,0 +1,100 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +static inline void TestOKImpl(int *__ended_by(*out_end) *out_start, + int **out_end) { + int arr[10]; + *out_start = arr; + *out_end = arr + 10; +} + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int *end; + int *__ended_by(end) start; + TestOKImpl(&start, &end); +} + +static inline void TestStartFailImpl(int *__ended_by(*out_end) *out_start, + int **out_end) { + int arr[10]; + *out_start = arr - 1; + *out_end = arr + 10; +} + +// CHECK-LABEL: @TestStartFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR_I:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR_I]]) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: [[UPPER_I:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR_I]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH_I:%.*]] = getelementptr i8, ptr [[ARR_I]], i64 -4 +// CHECK-NEXT: [[CMP24_NOT_I:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH_I]], [[UPPER_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP35_NOT_I:%.*]] = icmp ugt ptr [[ARR_I]], [[BOUND_PTR_ARITH_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_I:%.*]] = or i1 [[CMP24_NOT_I]], [[CMP35_NOT_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_I]], label [[TRAP_I:%.*]], label [[TESTSTARTFAILIMPL_EXIT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: TestStartFailImpl.exit: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR_I]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void TestStartFail() { + int *end; + int *__ended_by(end) start; + TestStartFailImpl(&start, &end); +} + +static inline void TestEndFailImpl(int *__ended_by(*out_end) *out_start, + int **out_end) { + int arr[10]; + *out_start = arr; + *out_end = arr + 11; +} + +// CHECK-LABEL: @TestEndFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR_I:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR_I]]) #[[ATTR5]] +// CHECK-NEXT: [[UPPER_I:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR_I]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH_I:%.*]] = getelementptr i8, ptr [[ARR_I]], i64 44 +// CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH_I]], [[UPPER_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP22_NOT_I:%.*]] = icmp ugt ptr [[ARR_I]], [[BOUND_PTR_ARITH_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_I:%.*]] = or i1 [[CMP_NOT_I]], [[CMP22_NOT_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_I]], label [[TRAP_I:%.*]], label [[TESTENDFAILIMPL_EXIT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: TestEndFailImpl.exit: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR_I]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void TestEndFail() { + int *end; + int *__ended_by(end) start; + TestEndFailImpl(&start, &end); +} + +static inline void TestRangeFailImpl(int *__ended_by(*out_end) *out_start, + int **out_end) { + int arr[10]; + *out_start = arr + 1; + *out_end = arr; +} + +// CHECK-LABEL: @TestRangeFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail() { + int *end; + int *__ended_by(end) start; + TestRangeFailImpl(&start, &end); +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_parms.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_parms.c new file mode 100644 index 0000000000000..69c92c4332ea1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_parms.c @@ -0,0 +1,141 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[OUT_START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[OUT_END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[OUT_START:%.*]], ptr [[OUT_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[OUT_END:%.*]], ptr [[OUT_END_ADDR]], align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 -1 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY3]], i64 10 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i32, ptr [[TMP16]], i64 11 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH5]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB9]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: [[CMP24:%.*]] = icmp ule ptr [[WIDE_PTR_PTR12]], [[WIDE_PTR_PTR19]] +// CHECK-NEXT: br i1 [[CMP24]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: [[CMP35:%.*]] = icmp ule ptr [[WIDE_PTR_LB27]], [[WIDE_PTR_PTR30]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP24:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP35]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP24]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[OUT_START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR38]], ptr [[TMP25]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP43]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = load ptr, ptr [[OUT_END_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP26]], align 8 +// CHECK-NEXT: ret void +// +void Foo(int *__ended_by(*out_end) *out_start, int **out_end) { + int arr[10]; + *out_start = arr - 1; + *out_end = arr + 11; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_seq-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_seq-O2.c new file mode 100644 index 0000000000000..ed84e258e45fc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_seq-O2.c @@ -0,0 +1,181 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include +struct S { + int *__ended_by(end) iter; + int *end; + int *__ended_by(iter) start; +}; + +void foo(void); + +// CHECK-LABEL: @TestRangeOK1( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestRangeOK1(void) { + int arr[10]; + struct S s; + s.start = arr; + s.iter = arr; + s.end = arr + 1; +} + +// CHECK-LABEL: @TestRangeOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestRangeOK2(void) { + int arr[10]; + struct S s; + s.start = arr; + s.iter = arr; + s.end = arr; +} + +// CHECK-LABEL: @TestRangeOK3( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestRangeOK3(void) { + int arr[10]; + struct S s; + s.start = arr; + s.iter = arr + 10; + s.end = arr + 10; +} + +// CHECK-LABEL: @TestRangeOK4( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestRangeOK4(void) { + int arr[10]; + struct S s; + s.start = arr + 10; + s.iter = arr + 10; + s.end = arr + 10; +} + +// CHECK-LABEL: @TestIterFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH8:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: tail call void @foo() #[[ATTR6]] +// CHECK-NEXT: [[BOUND_PTR_ARITH60:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4 +// CHECK-NEXT: [[CMP80_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH60]], [[BOUND_PTR_ARITH8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP95_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH60]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP80_NOT]], [[CMP95_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT96:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR7:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont96: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void TestIterFail(void) { + int arr[10]; + struct S s; + s.start = arr; + s.iter = arr + 2; + s.end = arr + 10; + foo(); + s.start = s.start; + s.iter = s.iter - 3; // is prevented since s.iter - 3 < s.start + s.end = s.end; +} + +// XXX: Why this can be optimized when the next case can't? +// CHECK-LABEL: @TestStartFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestStartFail(void) { + int arr[10]; + struct S s; + s.start = arr - 1; + s.iter = arr; + s.end = arr + 1; +} + +// CHECK-LABEL: @TestEndFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 44 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP25_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP25_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT48:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont48: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void TestEndFail(void) { + int arr[10]; + struct S s; + s.start = arr; + s.iter = arr; + s.end = arr + 11; +} + +// CHECK-LABEL: @TestRangeFail1( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail1(void) { + int arr[10]; + struct S s; + s.start = arr + 1; + s.iter = arr; + s.end = arr + 1; +} + +// CHECK-LABEL: @TestRangeFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail2(void) { + int arr[10]; + struct S s; + s.start = arr + 1; + s.iter = arr + 2; + s.end = arr + 1; +} + +// CHECK-LABEL: @TestRangeFail3( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail3(void) { + int arr[10]; + struct S s; + s.start = arr - 1; + s.iter = s.iter; + s.end = s.end; +} + +// CHECK-LABEL: @TestRangeFail4( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void TestRangeFail4(void) { + int arr[10]; + struct S s; + s.start = s.start; + s.iter = s.iter; + s.end = s.end + 1; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_seq.c b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_seq.c new file mode 100644 index 0000000000000..974a716077224 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_assign_checks_seq.c @@ -0,0 +1,292 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include +struct S { + int *__ended_by(end) iter; + int *end; + int *__ended_by(iter) start; +}; + +void foo(void); + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[AGG_TEMP55:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP56:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[AGG_TEMP62:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP63:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[AGG_TEMP67:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP74:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP82:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP89:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP99:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP107:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP115:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ITER:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[ITER]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[END]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[START]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 2 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[ARRAYDECAY6:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER7:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY6]], i64 10 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY6]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER7]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY6]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 0 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH8:%.*]] = getelementptr i32, ptr [[TMP19]], i64 10 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH8]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 1 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP5]], i32 0, i32 2 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP13]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP23:%.*]] = icmp ule ptr [[WIDE_PTR_LB15]], [[WIDE_PTR_PTR18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP23]], label [[CONT25:%.*]], label [[TRAP24:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap24: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont25: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// CHECK-NEXT: [[START33:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR28]], ptr [[START33]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: [[ITER41:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR36]], ptr [[ITER41]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP42]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8 +// CHECK-NEXT: [[END49:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR44]], ptr [[END49]], align 8 +// CHECK-NEXT: call void @foo() +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-NEXT: [[ITER52:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP51]], i32 0, i32 0 +// CHECK-NEXT: [[TMP27:%.*]] = load ptr, ptr [[ITER52]], align 8 +// CHECK-NEXT: [[START53:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: [[TMP28:%.*]] = load ptr, ptr [[START53]], align 8 +// CHECK-NEXT: [[START54:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: [[TMP29:%.*]] = load ptr, ptr [[START54]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP50]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP28]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP50]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP50]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP29]], ptr [[TMP32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP57]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-NEXT: [[START58:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-NEXT: [[TMP33:%.*]] = load ptr, ptr [[START58]], align 8 +// CHECK-NEXT: [[END59:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-NEXT: [[TMP34:%.*]] = load ptr, ptr [[END59]], align 8 +// CHECK-NEXT: [[ITER60:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: [[TMP35:%.*]] = load ptr, ptr [[ITER60]], align 8 +// CHECK-NEXT: [[TMP36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP56]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP35]], ptr [[TMP36]], align 8 +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP56]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP34]], ptr [[TMP37]], align 8 +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP56]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP33]], ptr [[TMP38]], align 8 +// CHECK-NEXT: [[TMP39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP56]], i32 0, i32 0 +// CHECK-NEXT: [[TMP40:%.*]] = load ptr, ptr [[TMP39]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH61:%.*]] = getelementptr i32, ptr [[TMP40]], i64 -3 +// CHECK-NEXT: [[TMP41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP55]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH61]], ptr [[TMP41]], align 8 +// CHECK-NEXT: [[TMP42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP56]], i32 0, i32 1 +// CHECK-NEXT: [[TMP43:%.*]] = load ptr, ptr [[TMP42]], align 8 +// CHECK-NEXT: [[TMP44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP55]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP43]], ptr [[TMP44]], align 8 +// CHECK-NEXT: [[TMP45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP56]], i32 0, i32 2 +// CHECK-NEXT: [[TMP46:%.*]] = load ptr, ptr [[TMP45]], align 8 +// CHECK-NEXT: [[TMP47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP55]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP46]], ptr [[TMP47]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP63]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-NEXT: [[ITER64:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP63]], i32 0, i32 0 +// CHECK-NEXT: [[TMP48:%.*]] = load ptr, ptr [[ITER64]], align 8 +// CHECK-NEXT: [[END65:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: [[TMP49:%.*]] = load ptr, ptr [[END65]], align 8 +// CHECK-NEXT: [[END66:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: [[TMP50:%.*]] = load ptr, ptr [[END66]], align 8 +// CHECK-NEXT: [[TMP51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP49]], ptr [[TMP51]], align 8 +// CHECK-NEXT: [[TMP52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP50]], ptr [[TMP52]], align 8 +// CHECK-NEXT: [[TMP53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP48]], ptr [[TMP53]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP67]], ptr align 8 [[AGG_TEMP55]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR68:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP67]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR69:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR68]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR70:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP67]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB71:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR70]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP67]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB73:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR72]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP74]], ptr align 8 [[AGG_TEMP62]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR75:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR76:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR75]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR77:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB78:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR77]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR79:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB80:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR79]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP81:%.*]] = icmp ule ptr [[WIDE_PTR_PTR69]], [[WIDE_PTR_PTR76]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP81]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP82]], ptr align 8 [[AGG_TEMP50]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR83:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP82]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR84:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR83]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR85:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP82]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB86:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR85]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR87:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP82]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB88:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR87]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP89]], ptr align 8 [[AGG_TEMP55]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR90:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR91:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR90]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR92:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB93:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR92]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR94:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP89]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB95:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR94]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP96:%.*]] = icmp ule ptr [[WIDE_PTR_PTR84]], [[WIDE_PTR_PTR91]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP54:%.*]] = phi i1 [ false, [[CONT25]] ], [ [[CMP96]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP54]], label [[CONT98:%.*]], label [[TRAP97:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap97: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont98: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP99]], ptr align 8 [[AGG_TEMP50]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR100:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP99]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR101:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR100]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR102:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP99]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB103:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR102]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR104:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP99]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB105:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR104]], align 8 +// CHECK-NEXT: [[START106:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR101]], ptr [[START106]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP107]], ptr align 8 [[AGG_TEMP55]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR108:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP107]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR109:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR108]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR110:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP107]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB111:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR110]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR112:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP107]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB113:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR112]], align 8 +// CHECK-NEXT: [[ITER114:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR109]], ptr [[ITER114]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP115]], ptr align 8 [[AGG_TEMP62]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR116:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP115]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR117:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR116]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR118:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP115]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB119:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR118]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR120:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP115]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB121:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR120]], align 8 +// CHECK-NEXT: [[END122:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR117]], ptr [[END122]], align 8 +// CHECK-NEXT: ret i32 0 +// +int bar(void) { + int arr[10]; + struct S s; + s.start = arr; + s.iter = arr + 2; + s.end = arr + 10; + foo(); + s.start = s.start; + s.iter = s.iter - 3; // is prevented since s.iter - 3 < s.start + s.end = s.end; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_bag_of_bytes-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_bag_of_bytes-O2.c new file mode 100644 index 0000000000000..c2e924f54b4d5 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_bag_of_bytes-O2.c @@ -0,0 +1,59 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include + +struct S { + int *__ended_by(end) iter; + int *end; + int *__ended_by(iter) start; +}; + +struct T { + int cnt; + int *__counted_by(cnt) ptr; +}; + +// CHECK-LABEL: @TestIterOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 +// +int TestIterOK() { + int arr[10]; + struct S s = { arr, arr + 10, arr }; + struct T t = { 2, arr + 1 }; + s.start = s.start; + s.iter = t.ptr; // this passes as 's.start <= t.ptr <= s.end' + s.end = s.end; + return 0; +} + +// CHECK-LABEL: @TestIterOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 +// +int TestIterOK2() { + int arr[10]; + struct S s = { arr, arr + 6, arr }; + struct T t = { 4, arr + 6 }; + s.start = s.start; + s.iter = t.ptr; + s.end = s.end; + return 0; +} + +// CHECK-LABEL: @TestIterFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +int TestIterFail() { + int arr[10]; + struct S s = { arr, arr + 5, arr }; + struct T t = { 4, arr + 6 }; + s.start = s.start; + s.iter = t.ptr; + s.end = s.end; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_bag_of_bytes.c b/clang/test/BoundsSafety/CodeGen/ended_by_bag_of_bytes.c new file mode 100644 index 0000000000000..ef5361488cd52 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_bag_of_bytes.c @@ -0,0 +1,151 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include + +struct S { + int *__ended_by(end) iter; + int *end; + int *__ended_by(iter) start; +}; + +struct T { + int cnt; + int *__counted_by(cnt) ptr; +}; + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[T:%.*]] = alloca [[STRUCT_T:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca [[STRUCT_T]], align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca [[STRUCT_S]], align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP24:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { i32, ptr }, ptr [[T]], i32 0, i32 0 +// CHECK-NEXT: store i32 [[T_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { i32, ptr }, ptr [[T]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[T_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[S:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[ITER:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[ITER]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[START]], align 8 +// CHECK-NEXT: [[START2:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[START2]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[TMP7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[T]], i64 16, i1 false) +// CHECK-NEXT: [[CNT:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[CNT]], align 8 +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP8]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP9]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[S]], i64 24, i1 false) +// CHECK-NEXT: [[ITER7:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[ITER7]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[END]], align 8 +// CHECK-NEXT: [[END8:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[END8]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP18]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR12]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP24]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR26:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB30:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR29]], align 8 +// CHECK-NEXT: [[CMP31:%.*]] = icmp ule ptr [[WIDE_PTR_PTR19]], [[WIDE_PTR_PTR26]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP19:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP31]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP19]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: [[START39:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[START39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: [[ITER47:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[ITER47]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP48]], ptr align 8 [[AGG_TEMP5]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR50:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB52:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB54:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR53]], align 8 +// CHECK-NEXT: [[END55:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR50]], ptr [[END55]], align 8 +// CHECK-NEXT: ret i32 0 +// +int Foo(struct S s, struct T t) { + s.start = s.start; + s.iter = t.ptr; + s.end = s.end; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_const_param-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_const_param-O2.c new file mode 100644 index 0000000000000..802d4e717a0b0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_const_param-O2.c @@ -0,0 +1,90 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct S { + int *end; + int *__ended_by(end) iter; +}; + +void foo(int * const __ended_by(end) start, int* const end); + + +// CHECK-LABEL: define dso_local void @good( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [40 x i32], align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 160, ptr nonnull [[ARR]]) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 160 +// CHECK-NEXT: call void @foo(ptr noundef nonnull [[ARR]], ptr noundef nonnull [[BOUND_PTR_ARITH]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 160, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void good(void) { + int arr[40]; + foo(arr, arr + 40); +} + +// CHECK-LABEL: define dso_local void @oob_upper( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [40 x i32], align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 160, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 160 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 164 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP28_NOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @foo(ptr noundef nonnull [[ARR]], ptr noundef [[BOUND_PTR_ARITH]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 160, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void oob_upper(void) { + int arr[40]; + foo(arr, arr + 41); +} + +// CHECK-LABEL: define dso_local void @oob_lower( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [40 x i32], align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 160, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 160 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], !annotation [[META2]] +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], !annotation [[META2]] +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP28_NOT]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @foo(ptr noundef nonnull [[ARR]], ptr noundef [[BOUND_PTR_ARITH]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 160, ptr nonnull [[ARR]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void oob_lower(void) { + int arr[40]; + foo(arr, arr - 1); +} + +// CHECK-LABEL: define dso_local void @oob_order( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR4:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// +void oob_order(void) { + int arr[40]; + foo(arr + 2, arr + 1); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048577, i32 1048575} +//. diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_const_param.c b/clang/test/BoundsSafety/CodeGen/ended_by_const_param.c new file mode 100644 index 0000000000000..b88fda158c69e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_const_param.c @@ -0,0 +1,118 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct S { + int *end; + int *__ended_by(end) iter; +}; + +// CHECK-LABEL: define dso_local void @foo( +// CHECK-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LOCAL:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[LOCAL]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: ret void +// +void foo(int * const __ended_by(end) start, int* const end) { + int *local = end; +} + +// CHECK-LABEL: define dso_local void @bar( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [40 x i32], align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [40 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 40 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [40 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY2]], i64 40 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 40 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR14]], ptr noundef [[WIDE_PTR_PTR21]]) +// CHECK-NEXT: ret void +// +void bar(void) { + int arr[40]; + foo(arr, arr + 40); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +//. diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr-O2.c new file mode 100644 index 0000000000000..21a8f07466f45 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr-O2.c @@ -0,0 +1,66 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef void (*ended_by_t)(const char *__ended_by(end) start, const char *end); + +void ended_by(const char *__ended_by(end) start, const char *end); + +ended_by_t ended_by_p = &ended_by; + +struct { + char _dummy; + const char array[10]; + char _dummy2; +} x; + +// CHECK-LABEL: @ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @ended_by_p, align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: tail call void [[TMP0]](ptr noundef nonnull getelementptr inbounds nuw (i8, ptr @x, i64 1), ptr noundef nonnull getelementptr inbounds nuw (i8, ptr @x, i64 11)) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: ret void +// +void ok(void) { + ended_by_p(x.array, x.array + 10); +} + +// FIXME: rdar://105524069 +// CHECK-LABEL: @start_fail_lower( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @ended_by_p, align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: tail call void [[TMP0]](ptr noundef nonnull @x, ptr noundef nonnull getelementptr inbounds nuw (i8, ptr @x, i64 11)) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void start_fail_lower(void) { + ended_by_p(x.array - 1, x.array + 10); +} + +// CHECK-LABEL: @start_fail_upper( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void start_fail_upper(void) { + ended_by_p(x.array + 11, x.array + 10); +} + +// CHECK-LABEL: @end_fail_lower( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void end_fail_lower(void) { + ended_by_p(x.array, x.array - 1); +} + +// CHECK-LABEL: @end_fail_upper( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void end_fail_upper(void) { + ended_by_p(x.array, x.array + 11); +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr-ae48eba2.ll.tmp b/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr-ae48eba2.ll.tmp new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr.c b/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr.c new file mode 100644 index 0000000000000..da2cc39c7e57a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_func_ptr.c @@ -0,0 +1,94 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +typedef void (*ended_by_t)(const char *__ended_by(end) start, const char *end); + +void ended_by(const char *__ended_by(end) start, const char *end); + +ended_by_t ended_by_p = &ended_by; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARRAY:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[ARRAY]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i8], ptr [[ARRAY]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 10 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr @ended_by_p, align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// CHECK-NEXT: call void [[TMP15]](ptr noundef [[WIDE_PTR_PTR14]], ptr noundef [[WIDE_PTR_PTR21]]) +// CHECK-NEXT: ret void +// +void foo(void) { + const char array[10]; + ended_by_p(array, array + 10); +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_incdec-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_incdec-O2.c new file mode 100644 index 0000000000000..028d5d53b179a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_incdec-O2.c @@ -0,0 +1,52 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct EndedByData { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + int *end; +}; + +// CHECK-LABEL: @TestIncDecOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestIncDecOK() { + int arr[10]; + struct EndedByData s = { arr, arr + 2, arr + 10 }; + + s.start++; + s.iter--; // start == iter + s.end--; +} + +// CHECK-LABEL: @TestIncDecTrap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation +// CHECK-NEXT: unreachable +// +void TestIncDecTrap() { + int arr[10]; + struct EndedByData s = { arr + 1, arr + 1, arr + 10 }; + + s.start = s.start; + --s.iter; // trap: start > iter + s.end = s.end; +} + +// CHECK-LABEL: @TestIncDecTrap2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation +// CHECK-NEXT: unreachable +// +void TestIncDecTrap2() { + int arr[10]; + struct EndedByData s = { arr, arr + 9, arr + 10 }; + + s.start++; + s.iter++; + s.end--; // trap : iter > end +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_incdec.c b/clang/test/BoundsSafety/CodeGen/ended_by_incdec.c new file mode 100644 index 0000000000000..ae5cfef3b534f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_incdec.c @@ -0,0 +1,224 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +struct T { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + int *end; +}; + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[T_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP53:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP60:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP69:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP72:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[T:%.*]], ptr [[T_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[T_ADDR]], align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_T:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[ITER:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ITER]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[START]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[START]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP8]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[T_ADDR]], align 8 +// CHECK-NEXT: [[ITER1:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[TMP16]], i32 0, i32 1 +// CHECK-NEXT: [[START4:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[TMP16]], i32 0, i32 0 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[START4]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[TMP16]], i32 0, i32 2 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[END]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[ITER1]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP3]], i32 0, i32 0 +// CHECK-NEXT: [[TMP24:%.*]] = load ptr, ptr [[TMP23]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i32, ptr [[TMP24]], i64 -1 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH5]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP27:%.*]] = load ptr, ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP28]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP3]], i32 0, i32 2 +// CHECK-NEXT: [[TMP30:%.*]] = load ptr, ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP30]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = load ptr, ptr [[T_ADDR]], align 8 +// CHECK-NEXT: [[END6:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[TMP32]], i32 0, i32 2 +// CHECK-NEXT: [[ITER9:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[TMP32]], i32 0, i32 1 +// CHECK-NEXT: [[TMP33:%.*]] = load ptr, ptr [[ITER9]], align 8 +// CHECK-NEXT: [[TMP34:%.*]] = load ptr, ptr [[END6]], align 8 +// CHECK-NEXT: [[TMP35:%.*]] = load ptr, ptr [[END6]], align 8 +// CHECK-NEXT: [[TMP36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP8]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP34]], ptr [[TMP36]], align 8 +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP8]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP35]], ptr [[TMP37]], align 8 +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP8]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP33]], ptr [[TMP38]], align 8 +// CHECK-NEXT: [[TMP39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP8]], i32 0, i32 0 +// CHECK-NEXT: [[TMP40:%.*]] = load ptr, ptr [[TMP39]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH10:%.*]] = getelementptr i32, ptr [[TMP40]], i64 -1 +// CHECK-NEXT: [[TMP41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH10]], ptr [[TMP41]], align 8 +// CHECK-NEXT: [[TMP42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP8]], i32 0, i32 1 +// CHECK-NEXT: [[TMP43:%.*]] = load ptr, ptr [[TMP42]], align 8 +// CHECK-NEXT: [[TMP44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP43]], ptr [[TMP44]], align 8 +// CHECK-NEXT: [[TMP45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP8]], i32 0, i32 2 +// CHECK-NEXT: [[TMP46:%.*]] = load ptr, ptr [[TMP45]], align 8 +// CHECK-NEXT: [[TMP47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP46]], ptr [[TMP47]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP7]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB14]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[AGG_TEMP7]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP29:%.*]] = icmp ule ptr [[WIDE_PTR_PTR17]], [[WIDE_PTR_PTR24]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP29]], label [[LAND_RHS:%.*]], label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP33]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP40:%.*]] = icmp ule ptr [[WIDE_PTR_LB32]], [[WIDE_PTR_PTR35]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP48:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP40]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP48]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR43:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP41]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB47:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR46]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP48]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB50:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR49]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP51:%.*]] = icmp ule ptr [[WIDE_PTR_PTR43]], [[WIDE_PTR_UB50]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP51]], label [[LAND_LHS_TRUE52:%.*]], label [[LAND_END80:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true52: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP53]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR55:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR54]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR56:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB57:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR56]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR58:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB59:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR58]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP60]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP60]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP60]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP60]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP67:%.*]] = icmp ule ptr [[WIDE_PTR_PTR55]], [[WIDE_PTR_PTR62]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP67]], label [[LAND_RHS68:%.*]], label [[LAND_END80]], {{!annotation ![0-9]+}} +// CHECK: land.rhs68: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP69]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR70:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB71:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR70]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP72]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR73:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR74:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR73]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR75:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB76:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR75]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR77:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB78:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR77]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP79:%.*]] = icmp ule ptr [[WIDE_PTR_LB71]], [[WIDE_PTR_PTR74]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END80]], {{!annotation ![0-9]+}} +// CHECK: land.end80: +// CHECK-NEXT: [[TMP49:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE52]] ], [ false, [[CONT]] ], [ [[CMP79]], [[LAND_RHS68]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP49]], label [[CONT82:%.*]], label [[TRAP81:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap81: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont82: +// CHECK-NEXT: [[TMP50:%.*]] = load ptr, ptr [[START]], align 8 +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP50]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[START]], align 8 +// CHECK-NEXT: [[TMP51:%.*]] = load ptr, ptr [[ITER1]], align 8 +// CHECK-NEXT: [[INCDEC_PTR83:%.*]] = getelementptr inbounds i32, ptr [[TMP51]], i32 -1 +// CHECK-NEXT: store ptr [[INCDEC_PTR83]], ptr [[ITER1]], align 8 +// CHECK-NEXT: [[TMP52:%.*]] = load ptr, ptr [[END6]], align 8 +// CHECK-NEXT: [[INCDEC_PTR84:%.*]] = getelementptr inbounds i32, ptr [[TMP52]], i32 -1 +// CHECK-NEXT: store ptr [[INCDEC_PTR84]], ptr [[END6]], align 8 +// CHECK-NEXT: ret void +// +void Test(struct T *t) { + t->start++; + --t->iter; + t->end--; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_locals.c b/clang/test/BoundsSafety/CodeGen/ended_by_locals.c new file mode 100644 index 0000000000000..66c5e02e2c019 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_locals.c @@ -0,0 +1,317 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name NAMED_ --version 3 + + +// Regression test for crash tracked by rdar://103382748 + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=OBJC %s +#include + +// CHECK-LABEL: define dso_local void @foo( +// CHECK-SAME: ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[ASDF:%.*]], i32 noundef [[ASDF_LEN:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ASDF_LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[MYENDPTR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[NAMED_TMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[MYENDEDBYPTR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP46:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP60:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store i32 [[ASDF_LEN]], ptr [[ASDF_LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ASDF_LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NAMED_TMP1]], ptr align 8 [[ASDF]], i64 24, i1 false) +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP1]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP2]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP1]], i32 0, i32 1 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP1]], i32 0, i32 2 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB5]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP6]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP16]], [[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr [[ASDF_LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NAMED_TMP17]], ptr align 8 [[ASDF]], i64 24, i1 false) +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP17]], i32 0, i32 0 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[IDXPROM18:%.*]] = sext i32 [[TMP11]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH19:%.*]] = getelementptr i32, ptr [[TMP13]], i64 [[IDXPROM18]] +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH19]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP17]], i32 0, i32 1 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP17]], i32 0, i32 2 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR21]], ptr [[MYENDPTR]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = load ptr, ptr [[MYENDPTR]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[MYENDEDBYPTR]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = load ptr, ptr [[MYENDPTR]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP21]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP33]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP36:%.*]] = icmp ule ptr [[WIDE_PTR_PTR28]], [[WIDE_PTR_UB35]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP36]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END57:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP44:%.*]] = icmp ule ptr [[WIDE_PTR_PTR39]], [[WIDE_PTR_PTR28]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP44]], label [[LAND_RHS45:%.*]], label [[LAND_END57]], !annotation [[META2]] +// CHECK: land.rhs45: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP46]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP49]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP56:%.*]] = icmp ule ptr [[WIDE_PTR_LB48]], [[WIDE_PTR_PTR51]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END57]], !annotation [[META2]] +// CHECK: land.end57: +// CHECK-NEXT: [[TMP27:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT]] ], [ [[CMP56]], [[LAND_RHS45]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP27]], label [[CONT59:%.*]], label [[TRAP58:%.*]], !annotation [[META2]] +// CHECK: trap58: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont59: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP60]], ptr align 8 [[ASDF]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP60]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP60]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP60]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR62]], ptr [[MYENDEDBYPTR]], align 8 +// CHECK-NEXT: ret void +// +// OBJC-LABEL: define dso_local void @foo( +// OBJC-SAME: ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[ASDF:%.*]], i32 noundef [[ASDF_LEN:%.*]]) #[[ATTR0:[0-9]+]] { +// OBJC-NEXT: entry: +// OBJC-NEXT: [[ASDF_LEN_ADDR:%.*]] = alloca i32, align 4 +// OBJC-NEXT: [[MYENDPTR:%.*]] = alloca ptr, align 8 +// OBJC-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OBJC-NEXT: [[NAMED_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OBJC-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[NAMED_TMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OBJC-NEXT: [[MYENDEDBYPTR:%.*]] = alloca ptr, align 8 +// OBJC-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP46:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: [[AGG_TEMP60:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OBJC-NEXT: store i32 [[ASDF_LEN]], ptr [[ASDF_LEN_ADDR]], align 4 +// OBJC-NEXT: [[TMP0:%.*]] = load i32, ptr [[ASDF_LEN_ADDR]], align 4 +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NAMED_TMP1]], ptr align 8 [[ASDF]], i64 24, i1 false) +// OBJC-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP1]], i32 0, i32 0 +// OBJC-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP1]], align 8 +// OBJC-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// OBJC-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP2]], i64 [[IDXPROM]] +// OBJC-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// OBJC-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP3]], align 8 +// OBJC-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP1]], i32 0, i32 1 +// OBJC-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// OBJC-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// OBJC-NEXT: store ptr [[TMP5]], ptr [[TMP6]], align 8 +// OBJC-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP1]], i32 0, i32 2 +// OBJC-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// OBJC-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// OBJC-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META7:![0-9]+]] +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META7]] +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, !annotation [[META7]] +// OBJC-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB5]], !annotation [[META7]] +// OBJC-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], !annotation [[META7]] +// OBJC: land.rhs: +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP6]], i32 0, i32 2, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, !annotation [[META7]] +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 0, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 1, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 2, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, !annotation [[META7]] +// OBJC-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]], !annotation [[META7]] +// OBJC-NEXT: br label [[LAND_END]], !annotation [[META7]] +// OBJC: land.end: +// OBJC-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP16]], [[LAND_RHS]] ], !annotation [[META7]] +// OBJC-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META7]] +// OBJC: trap: +// OBJC-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META7]] +// OBJC-NEXT: unreachable, !annotation [[META7]] +// OBJC: cont: +// OBJC-NEXT: [[TMP11:%.*]] = load i32, ptr [[ASDF_LEN_ADDR]], align 4 +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[NAMED_TMP17]], ptr align 8 [[ASDF]], i64 24, i1 false) +// OBJC-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP17]], i32 0, i32 0 +// OBJC-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// OBJC-NEXT: [[IDXPROM18:%.*]] = sext i32 [[TMP11]] to i64 +// OBJC-NEXT: [[BOUND_PTR_ARITH19:%.*]] = getelementptr i32, ptr [[TMP13]], i64 [[IDXPROM18]] +// OBJC-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// OBJC-NEXT: store ptr [[BOUND_PTR_ARITH19]], ptr [[TMP14]], align 8 +// OBJC-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP17]], i32 0, i32 1 +// OBJC-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// OBJC-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// OBJC-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// OBJC-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[NAMED_TMP17]], i32 0, i32 2 +// OBJC-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// OBJC-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// OBJC-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// OBJC-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// OBJC-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// OBJC-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// OBJC-NEXT: store ptr [[WIDE_PTR_PTR21]], ptr [[MYENDPTR]], align 8 +// OBJC-NEXT: [[TMP21:%.*]] = load ptr, ptr [[MYENDPTR]], align 8 +// OBJC-NEXT: [[TMP22:%.*]] = load ptr, ptr [[MYENDEDBYPTR]], align 8 +// OBJC-NEXT: [[TMP23:%.*]] = load ptr, ptr [[MYENDPTR]], align 8 +// OBJC-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 0 +// OBJC-NEXT: store ptr [[TMP21]], ptr [[TMP24]], align 8 +// OBJC-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 1 +// OBJC-NEXT: store ptr [[TMP23]], ptr [[TMP25]], align 8 +// OBJC-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 2 +// OBJC-NEXT: store ptr [[TMP22]], ptr [[TMP26]], align 8 +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 0 +// OBJC-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 1 +// OBJC-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 2 +// OBJC-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP33]], i32 0, i32 1, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, !annotation [[META7]] +// OBJC-NEXT: [[CMP36:%.*]] = icmp ule ptr [[WIDE_PTR_PTR28]], [[WIDE_PTR_UB35]], !annotation [[META7]] +// OBJC-NEXT: br i1 [[CMP36]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END57:%.*]], !annotation [[META7]] +// OBJC: land.lhs.true: +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 0, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 1, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 2, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, !annotation [[META7]] +// OBJC-NEXT: [[CMP44:%.*]] = icmp ule ptr [[WIDE_PTR_PTR39]], [[WIDE_PTR_PTR28]], !annotation [[META7]] +// OBJC-NEXT: br i1 [[CMP44]], label [[LAND_RHS45:%.*]], label [[LAND_END57]], !annotation [[META7]] +// OBJC: land.rhs45: +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP46]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP46]], i32 0, i32 2, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8, !annotation [[META7]] +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP49]], ptr align 8 [[ASDF]], i64 24, i1 false), !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 0, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 1, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP49]], i32 0, i32 2, !annotation [[META7]] +// OBJC-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8, !annotation [[META7]] +// OBJC-NEXT: [[CMP56:%.*]] = icmp ule ptr [[WIDE_PTR_LB48]], [[WIDE_PTR_PTR51]], !annotation [[META7]] +// OBJC-NEXT: br label [[LAND_END57]], !annotation [[META7]] +// OBJC: land.end57: +// OBJC-NEXT: [[TMP27:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[CONT]] ], [ [[CMP56]], [[LAND_RHS45]] ], !annotation [[META7]] +// OBJC-NEXT: br i1 [[TMP27]], label [[CONT59:%.*]], label [[TRAP58:%.*]], !annotation [[META7]] +// OBJC: trap58: +// OBJC-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META7]] +// OBJC-NEXT: unreachable, !annotation [[META7]] +// OBJC: cont59: +// OBJC-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP60]], ptr align 8 [[ASDF]], i64 24, i1 false) +// OBJC-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP60]], i32 0, i32 0 +// OBJC-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// OBJC-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP60]], i32 0, i32 1 +// OBJC-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// OBJC-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP60]], i32 0, i32 2 +// OBJC-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// OBJC-NEXT: store ptr [[WIDE_PTR_PTR62]], ptr [[MYENDEDBYPTR]], align 8 +// OBJC-NEXT: ret void +// +void foo(int * __bidi_indexable asdf, int asdf_len) { + const int *myEndPtr = asdf + asdf_len; + const int * __ended_by(myEndPtr) myEndedByPtr = asdf; +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O0-disabled-lb-check.c b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O0-disabled-lb-check.c new file mode 100644 index 0000000000000..4ca821d0543fe --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O0-disabled-lb-check.c @@ -0,0 +1,783 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name _TMP_ --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -x objective-c -emit-llvm -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +#include + + +void ended_by(const char *__ended_by(end) start, const char *end); + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_in_bounds( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 10 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR14]], ptr noundef [[WIDE_PTR_PTR21]]) +// CHECK-NEXT: ret void +// +void pass_const_size_arr_in_bounds(void) { + char local[10]; + ended_by(local, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_start_oob( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP4]], i64 -2 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY3]], i64 10 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i8, ptr [[TMP16]], i64 10 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH5]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB9]], ptr [[TMP24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR11]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_PTR18]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP25:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP30]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP25]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR33]], ptr noundef [[WIDE_PTR_PTR40]]) +// CHECK-NEXT: ret void +// +void pass_const_size_arr_start_oob(void) { + char local[10]; + ended_by(local - 2, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_end_oob( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 11 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP28]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR31]], ptr noundef [[WIDE_PTR_PTR38]]) +// CHECK-NEXT: ret void +// +void pass_const_size_arr_end_oob(void) { + char local[10]; + ended_by(local, local + 11); +} + +// CHECK-LABEL: define dso_local void @pass_explicit_indexable( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[ILOCAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP49:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[ILOCAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[ILOCAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[ILOCAL]], i64 16, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[ILOCAL]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP10]], i64 10 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[TMP18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB20]], ptr [[TMP19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[TMP20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR10]], [[WIDE_PTR_PTR24]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP41:%.*]] = icmp ule ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_PTR36]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP22:%.*]] = phi i1 [ false, %[[CONT]] ], [ [[CMP41]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP22]], label %[[CONT43:.*]], label %[[TRAP42:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP42]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT43]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP49]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP49]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR46]], ptr noundef [[WIDE_PTR_PTR51]]) +// CHECK-NEXT: ret void +// +void pass_explicit_indexable(void) { + char local[10]; + char* __indexable ilocal = local; + ended_by(ilocal, &ilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_explict_bidi_indexable( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[BILOCAL:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[BILOCAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[BILOCAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[BILOCAL]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BILOCAL]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[BILOCAL]], i64 24, i1 false) +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP4]], i64 10 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp ule ptr [[WIDE_PTR_PTR14]], [[WIDE_PTR_PTR21]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP26]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR29]], ptr noundef [[WIDE_PTR_PTR36]]) +// CHECK-NEXT: ret void +// +void pass_explict_bidi_indexable(void) { + char local[10]; + char* __bidi_indexable bilocal = local; + ended_by(bilocal, &bilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_ended_by( +// CHECK-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp ule ptr [[WIDE_PTR_PTR14]], [[WIDE_PTR_PTR21]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP26]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP13]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR29]], ptr noundef [[WIDE_PTR_PTR36]]) +// CHECK-NEXT: ret void +// +void pass_ended_by(char* __ended_by(end) start, const char* end) { + ended_by(start, end); +} + +// CHECK-LABEL: define dso_local void @pass_counted_by( +// CHECK-SAME: ptr noundef [[START:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT2:%.*]] = sext i32 [[TMP7]] to i64 +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr inbounds i8, ptr [[TMP6]], i64 [[IDX_EXT2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR3]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP5]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP12]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP19]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_RHS:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP21:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP28]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP21]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR31]], ptr noundef [[WIDE_PTR_PTR38]]) +// CHECK-NEXT: ret void +// +void pass_counted_by(char* __counted_by(count) start, int count) { + ended_by(start, start + count); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META4]] = !{!"bounds-safety-check-bidi-to-indexable-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O0.c b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O0.c new file mode 100644 index 0000000000000..91206805062cc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O0.c @@ -0,0 +1,934 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name _TMP_ --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -x objective-c -emit-llvm -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +#include + + +void ended_by(const char *__ended_by(end) start, const char *end); + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_in_bounds( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 10 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR16]], ptr noundef [[WIDE_PTR_PTR23]]) +// CHECK-NEXT: ret void +// +void pass_const_size_arr_in_bounds(void) { + char local[10]; + ended_by(local, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_start_oob( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[_TMP_TMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP31:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP48:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP55:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP4]], i64 -2 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY3]], i64 10 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY3]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i8, ptr [[TMP16]], i64 10 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH5]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 1 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[_TMP_TMP2]], i32 0, i32 2 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB9]], ptr [[TMP24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR11]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP23]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP30:%.*]] = icmp ule ptr [[WIDE_PTR_PTR18]], [[WIDE_PTR_PTR25]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP30]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP31]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB33]], ptr [[TMP25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP31]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP47:%.*]] = icmp ule ptr [[WIDE_PTR_PTR35]], [[WIDE_PTR_PTR42]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP26:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP47]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP26]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP48]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR50:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB52:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP48]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB54:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR53]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP55]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR56:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP55]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR57:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR56]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR58:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP55]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB59:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR58]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR60:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP55]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB61:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR60]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR50]], ptr noundef [[WIDE_PTR_PTR57]]) +// CHECK-NEXT: ret void +// +void pass_const_size_arr_start_oob(void) { + char local[10]; + ended_by(local - 2, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_end_oob( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP46:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP53:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[ARRAYDECAY2:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY2]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER3]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY2]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 11 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP28]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB31]], ptr [[TMP16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP45:%.*]] = icmp ule ptr [[WIDE_PTR_PTR33]], [[WIDE_PTR_PTR40]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP17:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP45]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP46]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR48:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB50:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB52:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR51]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP53]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR55:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR56:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB57:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR56]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR58:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB59:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR58]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR48]], ptr noundef [[WIDE_PTR_PTR55]]) +// CHECK-NEXT: ret void +// +void pass_const_size_arr_end_oob(void) { + char local[10]; + ended_by(local, local + 11); +} + +// CHECK-LABEL: define dso_local void @pass_explicit_indexable( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[ILOCAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP34:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP56:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP69:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[ILOCAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[ILOCAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[ILOCAL]], i64 16, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[ILOCAL]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP10]], i64 10 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[TMP18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB20]], ptr [[TMP19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[TMP20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR10]], [[WIDE_PTR_PTR24]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP34]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR36:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR35]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP34]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB40:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP41:%.*]] = icmp ule ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_PTR36]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP41]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP43]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP43]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB47]], ptr [[TMP23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR45]], ptr [[TMP24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB49]], ptr [[TMP25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP56]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP56]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR58:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR57]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP56]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB60:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP61:%.*]] = icmp ule ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_PTR58]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP26:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[CONT]] ], [ [[CMP61]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP26]], label %[[CONT63:.*]], label %[[TRAP62:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP62]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT63]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[AGG_TEMP1]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP64]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.1", ptr [[AGG_TEMP64]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP69]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR70:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR71:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR70]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB73:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR74:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB75:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR74]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR66]], ptr noundef [[WIDE_PTR_PTR71]]) +// CHECK-NEXT: ret void +// +void pass_explicit_indexable(void) { + char local[10]; + char* __indexable ilocal = local; + ended_by(ilocal, &ilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_explict_bidi_indexable( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: [[BILOCAL:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8], ptr [[LOCAL]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[BILOCAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[BILOCAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[BILOCAL]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BILOCAL]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[BILOCAL]], i64 24, i1 false) +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP4]], i64 10 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp ule ptr [[WIDE_PTR_PTR14]], [[WIDE_PTR_PTR21]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB29]], ptr [[TMP13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP43:%.*]] = icmp ule ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_PTR38]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP43]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR53:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR56:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB57:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR56]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR46]], ptr noundef [[WIDE_PTR_PTR53]]) +// CHECK-NEXT: ret void +// +void pass_explict_bidi_indexable(void) { + char local[10]; + char* __bidi_indexable bilocal = local; + ended_by(bilocal, &bilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_ended_by( +// CHECK-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp ule ptr [[WIDE_PTR_PTR14]], [[WIDE_PTR_PTR21]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB29]], ptr [[TMP13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP43:%.*]] = icmp ule ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_PTR38]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP14:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP43]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR53:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR56:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB57:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR56]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR46]], ptr noundef [[WIDE_PTR_PTR53]]) +// CHECK-NEXT: ret void +// +void pass_ended_by(char* __ended_by(end) start, const char* end) { + ended_by(start, end); +} + +// CHECK-LABEL: define dso_local void @pass_counted_by( +// CHECK-SAME: ptr noundef [[START:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[START_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP46:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP53:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[START]], ptr [[START_ADDR]], align 8 +// CHECK-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[START_ADDR]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT2:%.*]] = sext i32 [[TMP7]] to i64 +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr inbounds i8, ptr [[TMP6]], i64 [[IDX_EXT2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR3]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP5]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP12]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP19]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB7]], ptr [[TMP20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP28]], label %[[LAND_RHS:.*]], label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB31]], ptr [[TMP21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP45:%.*]] = icmp ule ptr [[WIDE_PTR_PTR33]], [[WIDE_PTR_PTR40]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP22:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[CMP45]], %[[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP22]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP46]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR48:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB50:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB52:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR51]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP53]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR55:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR56:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB57:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR56]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR58:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB59:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR58]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR48]], ptr noundef [[WIDE_PTR_PTR55]]) +// CHECK-NEXT: ret void +// +void pass_counted_by(char* __counted_by(count) start, int count) { + ended_by(start, start + count); +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META4]] = !{!"bounds-safety-check-bidi-to-indexable-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O2-disabled-lb-check.c b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O2-disabled-lb-check.c new file mode 100644 index 0000000000000..868e721ed07a3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O2-disabled-lb-check.c @@ -0,0 +1,136 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name _TMP_ --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -x objective-c -emit-llvm -fbounds-attributes-objc-experimental -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +#include + + +void ended_by(const char *__ended_by(end) start, const char *end); + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_in_bounds( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr inbounds nuw i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef nonnull [[BOUND_PTR_ARITH]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_const_size_arr_in_bounds(void) { + char local[10]; + ended_by(local, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_start_oob( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[LOCAL]], i64 -2 +// CHECK-NEXT: [[CMP30_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[CMP30_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @ended_by(ptr noundef [[BOUND_PTR_ARITH]], ptr noundef nonnull [[UPPER]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_const_size_arr_start_oob(void) { + char local[10]; + ended_by(local - 2, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_end_oob( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[LOCAL]], i64 11 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp ugt ptr [[LOCAL]], [[BOUND_PTR_ARITH]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP28_NOT]], !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef [[BOUND_PTR_ARITH]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_const_size_arr_end_oob(void) { + char local[10]; + ended_by(local, local + 11); +} + +// CHECK-LABEL: define dso_local void @pass_explicit_indexable( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef nonnull [[UPPER]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_explicit_indexable(void) { + char local[10]; + char* __indexable ilocal = local; + ended_by(ilocal, &ilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_explict_bidi_indexable( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef nonnull [[UPPER]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_explict_bidi_indexable(void) { + char local[10]; + char* __bidi_indexable bilocal = local; + ended_by(bilocal, &bilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_ended_by( +// CHECK-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP26_NOT:%.*]] = icmp ugt ptr [[START]], [[END]], !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT]]: +// CHECK-NEXT: tail call void @ended_by(ptr noundef [[START]], ptr noundef [[END]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_ended_by(char* __ended_by(end) start, const char* end) { + ended_by(start, end); +} + +// CHECK-LABEL: define dso_local void @pass_counted_by( +// CHECK-SAME: ptr noundef [[START:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp slt i32 [[COUNT]], 0, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[CMP28_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[COUNT]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[START]], i64 [[IDX_EXT]] +// CHECK-NEXT: tail call void @ended_by(ptr noundef [[START]], ptr noundef [[ADD_PTR]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_counted_by(char* __counted_by(count) start, int count) { + ended_by(start, start + count); +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O2.c b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O2.c new file mode 100644 index 0000000000000..2f532537d2a81 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_param_in_call-O2.c @@ -0,0 +1,138 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name _TMP_ --version 5 + +// Note: Specifying the triple seems to be necessary for `update_cc_test_checks.py` to work +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -emit-llvm -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -x objective-c -emit-llvm -fbounds-attributes-objc-experimental -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -o - %s | FileCheck %s +#include + + +void ended_by(const char *__ended_by(end) start, const char *end); + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_in_bounds( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr inbounds nuw i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef nonnull [[BOUND_PTR_ARITH]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_const_size_arr_in_bounds(void) { + char local[10]; + ended_by(local, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_start_oob( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[LOCAL]], i64 -2 +// CHECK-NEXT: [[CMP30_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[CMP47_NOT:%.*]] = icmp ugt ptr [[LOCAL]], [[BOUND_PTR_ARITH]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP30_NOT]], [[CMP47_NOT]], !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[CONT:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @ended_by(ptr noundef [[BOUND_PTR_ARITH]], ptr noundef nonnull [[UPPER]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_const_size_arr_start_oob(void) { + char local[10]; + ended_by(local - 2, &local[10]); +} + +// CHECK-LABEL: define dso_local void @pass_const_size_arr_end_oob( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[LOCAL]], i64 11 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[UPPER]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp ugt ptr [[LOCAL]], [[BOUND_PTR_ARITH]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP28_NOT]], !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef [[BOUND_PTR_ARITH]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_const_size_arr_end_oob(void) { + char local[10]; + ended_by(local, local + 11); +} + +// CHECK-LABEL: define dso_local void @pass_explicit_indexable( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef nonnull [[UPPER]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_explicit_indexable(void) { + char local[10]; + char* __indexable ilocal = local; + ended_by(ilocal, &ilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_explict_bidi_indexable( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [10 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[LOCAL]], i64 10 +// CHECK-NEXT: call void @ended_by(ptr noundef nonnull [[LOCAL]], ptr noundef nonnull [[UPPER]]) #[[ATTR4]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 10, ptr nonnull [[LOCAL]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_explict_bidi_indexable(void) { + char local[10]; + char* __bidi_indexable bilocal = local; + ended_by(bilocal, &bilocal[10]); +} + +// CHECK-LABEL: define dso_local void @pass_ended_by( +// CHECK-SAME: ptr noundef [[START:%.*]], ptr noundef [[END:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP26_NOT:%.*]] = icmp ugt ptr [[START]], [[END]], !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[CMP26_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT]]: +// CHECK-NEXT: tail call void @ended_by(ptr noundef [[START]], ptr noundef [[END]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_ended_by(char* __ended_by(end) start, const char* end) { + ended_by(start, end); +} + +// CHECK-LABEL: define dso_local void @pass_counted_by( +// CHECK-SAME: ptr noundef [[START:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp slt i32 [[COUNT]], 0, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[CMP28_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT]]: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[COUNT]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[START]], i64 [[IDX_EXT]] +// CHECK-NEXT: tail call void @ended_by(ptr noundef [[START]], ptr noundef [[ADD_PTR]]) #[[ATTR4]] +// CHECK-NEXT: ret void +// +void pass_counted_by(char* __counted_by(count) start, int count) { + ended_by(start, start + count); +} diff --git a/clang/test/BoundsSafety/CodeGen/ended_by_returns.c b/clang/test/BoundsSafety/CodeGen/ended_by_returns.c new file mode 100644 index 0000000000000..4cbd26dad176a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ended_by_returns.c @@ -0,0 +1,364 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// TODO: rdar://83900556 +// CHECK-LABEL: @chunk( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BEGIN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[END_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BEGIN:%.*]], ptr [[BEGIN_ADDR]], align 8 +// CHECK-NEXT: store ptr [[END:%.*]], ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BEGIN_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BEGIN_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[END_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP7]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +char *__ended_by(end) chunk(char *__ended_by(end) begin, char *end) { + return begin + 1; +} + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP58:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[ARRAYDECAY5:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER6:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY5]], i64 10 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER6]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP10]], i64 10 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR8]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB10]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB12]], ptr [[TMP20]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP13]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR24]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[CMP43:%.*]] = icmp ule ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_PTR38]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP22:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP43]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP22]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR53:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR56:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB57:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR56]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @chunk(ptr noundef [[WIDE_PTR_PTR46]], ptr noundef [[WIDE_PTR_PTR53]]) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP58]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR60]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR62]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB64]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB66]], ptr [[TMP28]], align 8 +// CHECK-NEXT: ret void +// +void foo(void) { + int arr[10]; + int *p = chunk(arr, arr+10); +} + +// CHECK-LABEL: @fooCast( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP13:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP58:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[ARRAYDECAY5:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER6:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY5]], i64 10 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER6]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP10]], i64 10 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR8]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB10]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB12]], ptr [[TMP20]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP13]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP13]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR24]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[CMP43:%.*]] = icmp ule ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_PTR38]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP22:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP43]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP22]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP44]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP44]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR53:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR56:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB57:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR56]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @chunk(ptr noundef [[WIDE_PTR_PTR46]], ptr noundef [[WIDE_PTR_PTR53]]) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP58]], ptr align 8 [[AGG_TEMP3]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR60]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR62]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB64]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB66]], ptr [[TMP28]], align 8 +// CHECK-NEXT: ret void +// +void fooCast(void) { + int arr[10]; + int *p = (int*)chunk(arr, arr+10); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-arith-count-assign-null.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-arith-count-assign-null.c new file mode 100644 index 0000000000000..757b2af7bddc9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-arith-count-assign-null.c @@ -0,0 +1,52 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct flex { + unsigned char count; + unsigned elems[__counted_by(count - 1)]; +}; + +// CHECK-LABEL: @var_decl( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr null, ptr [[F]], align 8 +// CHECK-NEXT: ret void +// +void var_decl(void) { + struct flex *__single f = 0; +} + +// CHECK-LABEL: @assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr null, ptr [[F]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr null, ptr [[F]], align 8 +// CHECK-NEXT: ret void +// +void assign(void) { + struct flex *__single f; + f = 0; +} + +// CHECK-LABEL: @return_flex( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr null +// +struct flex *return_flex(void) { + return 0; +} + +void take_flex(struct flex *f); + +// CHECK-LABEL: @give_flex( +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @take_flex(ptr noundef null) +// CHECK-NEXT: ret void +// +void give_flex(void) { + take_flex(0); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-assign-with-single.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-assign-with-single.c new file mode 100644 index 0000000000000..c5e4197e01590 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-assign-with-single.c @@ -0,0 +1,67 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +// CHECK-LABEL: @init_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[S:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[S]], align 8 +// CHECK-NEXT: ret void +// +void init_single(void *p) { + struct flexible *__single s = p; +} + +// CHECK-LABEL: @init_casted_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[S:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[S]], align 8 +// CHECK-NEXT: ret void +// +void init_casted_single(void *p) { + struct flexible *__single s = (struct flexible *)p; +} + +// CHECK-LABEL: @assign_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[S:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr null, ptr [[S]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[S]], align 8 +// CHECK-NEXT: ret void +// +void assign_single(void *p) { + struct flexible *__single s; + s = p; +} + +// CHECK-LABEL: @assign_casted_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[S:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr null, ptr [[S]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[S]], align 8 +// CHECK-NEXT: ret void +// +void assign_casted_single(void *p) { + struct flexible *__single s; + s = (struct flexible *)p; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-assignment.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-assignment.c new file mode 100644 index 0000000000000..168085fa8b9c0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-assignment.c @@ -0,0 +1,121 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[FLEX_BIDI_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: store ptr [[FLEX_BIDI:%.*]], ptr [[FLEX_BIDI_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[FLEX_BIDI]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT21:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLEX_T:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: br i1 true, label [[CONT15:%.*]], label [[TRAP14:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap14: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont15: +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT17:%.*]], label [[TRAP16:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap16: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont17: +// CHECK-NEXT: [[TMP6:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT19:%.*]], label [[TRAP18:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap18: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont19: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 123, [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT21]], label [[TRAP20:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap20: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont21: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ne ptr [[WIDE_PTR_PTR23]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT31:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP8:%.*]] = icmp ult ptr [[WIDE_PTR_PTR23]], [[WIDE_PTR_UB25]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT29:%.*]], label [[TRAP28:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap28: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont29: +// CHECK-NEXT: [[TMP9:%.*]] = icmp uge ptr [[WIDE_PTR_PTR23]], [[WIDE_PTR_LB27]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT31]], label [[TRAP30:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap30: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont31: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR23]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[TMP10]], i32 0, i32 0 +// CHECK-NEXT: store i32 123, ptr [[COUNT]], align 4 +// CHECK-NEXT: ret void +// +void test(flex_t *flex, flex_t *__bidi_indexable flex_bidi) { + flex = flex_bidi; + flex->count = 123; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-bidi-O0.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-bidi-O0.c new file mode 100644 index 0000000000000..a07426ec74c97 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-bidi-O0.c @@ -0,0 +1,1437 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64e-apple-iphoneos -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -triple arm64e-apple-iphoneos -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Simple { + int len; + int fam[__counted_by(len)]; +}; + +// rdar://132731845 the flexible arrays are not bounds checked + +// CHECK-LABEL: define dso_local void @simple_no_flexbase_update( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_SIMPLE:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_SIMPLE]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 4 +// CHECK-NEXT: ret void +// +void simple_no_flexbase_update(struct Simple * __bidi_indexable p) { + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @simple_flexbase_update( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P2]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_SIMPLE:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_SIMPLE]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 4 +// CHECK-NEXT: ret void +// +void simple_flexbase_update(struct Simple * __bidi_indexable p) { + struct Simple * __bidi_indexable p2 = p; + p2->len = 11; +} + +// CHECK-LABEL: define dso_local void @simple_flexbase_self_assign( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_SIMPLE:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_SIMPLE]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 4 +// CHECK-NEXT: ret void +// +void simple_flexbase_self_assign(struct Simple * __bidi_indexable p) { + p = p; + p->len = 11; +} + +struct Shared { + int len; + int * __counted_by(len) ptr; + int fam[__counted_by(len)]; +}; +int * __counted_by(len) baz(int len); + +// CHECK-LABEL: define dso_local void @shared_no_flexbase_update( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef 11) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 11 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META5]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META5]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META5]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META5]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META5]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META5]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META5]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR31]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB33]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT37:.*]], label %[[TRAP36:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP36]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT37]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB35]], [[WIDE_PTR_PTR31]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT39:.*]], label %[[TRAP38:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR31]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB51]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT55:.*]], label %[[TRAP54:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP54]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT55]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR49]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[PTR]], align 8 +// CHECK-NEXT: ret void +// +void shared_no_flexbase_update(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p->len = 11; + p->ptr = p2; +} + +// CHECK-LABEL: define dso_local void @shared_no_flexbase_update_reverse( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef 11) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 11 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META5]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META5]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META5]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META5]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META5]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META5]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META5]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR38]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB40]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT44:.*]], label %[[TRAP43:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP43]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT44]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB42]], [[WIDE_PTR_PTR38]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT46:.*]], label %[[TRAP45:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP45]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT46]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR38]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[PTR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB51]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT55:.*]], label %[[TRAP54:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP54]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT55]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR49]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 8 +// CHECK-NEXT: ret void +// +void shared_no_flexbase_update_reverse(struct Shared * __bidi_indexable p) { + p->ptr = baz(11); + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_update( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef 11) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 11 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P2]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P3]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META5]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META5]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META5]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META5]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META5]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META5]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META5]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR38]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB40]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT44:.*]], label %[[TRAP43:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP43]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT44]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB42]], [[WIDE_PTR_PTR38]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT46:.*]], label %[[TRAP45:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP45]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT46]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR38]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[PTR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB51]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT55:.*]], label %[[TRAP54:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP54]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT55]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR49]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 8 +// CHECK-NEXT: ret void +// +void shared_flexbase_update(struct Shared * __bidi_indexable p) { + int * p3 = baz(11); + struct Shared * __bidi_indexable p2 = p; + p2->ptr = p3; + p2->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_update_reverse( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef 11) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 11 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P2]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P3]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META5]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META5]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META5]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META5]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META5]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META5]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META5]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR31]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB33]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT37:.*]], label %[[TRAP36:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP36]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT37]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB35]], [[WIDE_PTR_PTR31]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT39:.*]], label %[[TRAP38:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR31]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB51]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT55:.*]], label %[[TRAP54:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP54]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT55]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR49]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[PTR]], align 8 +// CHECK-NEXT: ret void +// +void shared_flexbase_update_reverse(struct Shared * __bidi_indexable p) { + int * p3 = baz(11); + struct Shared * __bidi_indexable p2 = p; + p2->len = 11; + p2->ptr = p3; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef 11) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 11 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META5]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META5]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META5]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META5]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META5]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META5]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META5]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR38:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR37]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB42:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR41]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR38]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB40]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT44:.*]], label %[[TRAP43:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP43]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT44]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB42]], [[WIDE_PTR_PTR38]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT46:.*]], label %[[TRAP45:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP45]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT46]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR38]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[PTR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB51]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT55:.*]], label %[[TRAP54:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP54]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT55]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR49]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 8 +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p = p; + p->ptr = p2; + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign_reverse( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef 11) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 11 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB4]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END28:.*]], !annotation [[META5]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB7]], [[WIDE_PTR_PTR10]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP15]], label %[[LAND_RHS:.*]], label %[[LAND_END28]], !annotation [[META5]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB18]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR21]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META5]] +// CHECK-NEXT: [[CMP26:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP26]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META5]] +// CHECK: [[LAND_RHS27]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META5]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS27]] ] +// CHECK-NEXT: br label %[[LAND_END28]], !annotation [[META5]] +// CHECK: [[LAND_END28]]: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP3]], %[[LAND_END]] ], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META5]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR31]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB33]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT37:.*]], label %[[TRAP36:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP36]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT37]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB35]], [[WIDE_PTR_PTR31]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT39:.*]], label %[[TRAP38:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP38]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT39]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR31]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB51]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP9]], label %[[CONT55:.*]], label %[[TRAP54:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP54]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT55]]: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB53]], [[WIDE_PTR_PTR49]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT57:.*]], label %[[TRAP56:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP56]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT57]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR49]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[PTR]], align 8 +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign_reverse(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p = p; + p->len = 11; + p->ptr = p2; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign_fr( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP59:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP3]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT4]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[LEN]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR7]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[WIDE_PTR_UB9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[CONT13:.*]], label %[[TRAP12:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP12]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT13]]: +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[WIDE_PTR_LB11]], [[WIDE_PTR_PTR7]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT15:.*]], label %[[TRAP14:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP14]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT15]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR7]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP7]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR18]], [[WIDE_PTR_UB25]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END49:.*]], !annotation [[META5]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP36:%.*]] = icmp ule ptr [[WIDE_PTR_LB28]], [[WIDE_PTR_PTR31]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP36]], label %[[LAND_RHS:.*]], label %[[LAND_END49]], !annotation [[META5]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB39]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR42]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META5]] +// CHECK-NEXT: [[CMP47:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP47]], label %[[LAND_RHS48:.*]], label %[[LAND_END:.*]], !annotation [[META5]] +// CHECK: [[LAND_RHS48]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META5]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS48]] ] +// CHECK-NEXT: br label %[[LAND_END49]], !annotation [[META5]] +// CHECK: [[LAND_END49]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[CONT15]] ], [ [[TMP11]], %[[LAND_END]] ], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT51:.*]], label %[[TRAP50:.*]], !prof [[PROF3]], !annotation [[META5]] +// CHECK: [[TRAP50]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT51]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP59]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR60:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP59]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR61:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR60]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR62:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP59]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB63:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR62]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR64:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP59]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB65:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR64]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR61]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = icmp ule ptr [[TMP13]], [[WIDE_PTR_UB63]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT67:.*]], label %[[TRAP66:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP66]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT67]]: +// CHECK-NEXT: [[TMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB65]], [[WIDE_PTR_PTR61]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT69:.*]], label %[[TRAP68:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP68]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT69]]: +// CHECK-NEXT: [[PTR70:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR61]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[PTR70]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR73]], i64 1 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ule ptr [[TMP16]], [[WIDE_PTR_UB75]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT79:.*]], label %[[TRAP78:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP78]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT79]]: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ule ptr [[WIDE_PTR_LB77]], [[WIDE_PTR_PTR73]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP18]], label %[[CONT81:.*]], label %[[TRAP80:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP80]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT81]]: +// CHECK-NEXT: [[LEN82:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR73]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN82]], align 8 +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign_fr(struct Shared * __bidi_indexable p) { + p = p; + p->ptr = p->ptr; + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign_fr_reverse( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP64:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_SHARED:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT4:.*]], label %[[TRAP3:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP3]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT4]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[LEN]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR7]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[WIDE_PTR_UB9]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[CONT13:.*]], label %[[TRAP12:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP12]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT13]]: +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[WIDE_PTR_LB11]], [[WIDE_PTR_PTR7]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT15:.*]], label %[[TRAP14:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP14]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT15]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR7]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP7]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP16]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP23]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP23]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR18]], [[WIDE_PTR_UB25]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END49:.*]], !annotation [[META5]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP26]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[CMP36:%.*]] = icmp ule ptr [[WIDE_PTR_LB28]], [[WIDE_PTR_PTR31]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP36]], label %[[LAND_RHS:.*]], label %[[LAND_END49]], !annotation [[META5]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8, !annotation [[META5]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2, !annotation [[META5]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB39]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR42]] to i64, !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META5]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META5]] +// CHECK-NEXT: [[CMP47:%.*]] = icmp sle i64 11, [[SUB_PTR_DIV]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[CMP47]], label %[[LAND_RHS48:.*]], label %[[LAND_END:.*]], !annotation [[META5]] +// CHECK: [[LAND_RHS48]]: +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META5]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ true, %[[LAND_RHS48]] ] +// CHECK-NEXT: br label %[[LAND_END49]], !annotation [[META5]] +// CHECK: [[LAND_END49]]: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[CONT15]] ], [ [[TMP11]], %[[LAND_END]] ], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT51:.*]], label %[[TRAP50:.*]], !prof [[PROF3]], !annotation [[META5]] +// CHECK: [[TRAP50]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT51]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR54]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = icmp ule ptr [[TMP13]], [[WIDE_PTR_UB56]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT60:.*]], label %[[TRAP59:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP59]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT60]]: +// CHECK-NEXT: [[TMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB58]], [[WIDE_PTR_PTR54]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT62:.*]], label %[[TRAP61:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP61]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT62]]: +// CHECK-NEXT: [[LEN63:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR54]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN63]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP64]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP64]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR66:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR65]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR67:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP64]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB68:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR67]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP64]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB70:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR69]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR73]], i64 1 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ule ptr [[TMP16]], [[WIDE_PTR_UB75]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT79:.*]], label %[[TRAP78:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP78]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT79]]: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ule ptr [[WIDE_PTR_LB77]], [[WIDE_PTR_PTR73]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP18]], label %[[CONT81:.*]], label %[[TRAP80:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP80]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT81]]: +// CHECK-NEXT: [[PTR82:%.*]] = getelementptr inbounds nuw [[STRUCT_SHARED]], ptr [[WIDE_PTR_PTR73]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR66]], ptr [[PTR82]], align 8 +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign_fr_reverse(struct Shared * __bidi_indexable p) { + p = p; + p->len = 11; + p->ptr = p->ptr; +} + +struct Double { + int len; + int len2; + int fam[__counted_by(len + len2)]; +}; + +// CHECK-LABEL: define dso_local void @double_no_flexbase_update_once( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_DOUBLE:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_DOUBLE]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 4 +// CHECK-NEXT: ret void +// +void double_no_flexbase_update_once(struct Double * __bidi_indexable p) { + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @double_no_flexbase_update_both( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_DOUBLE:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_DOUBLE]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr [[STRUCT_DOUBLE]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[WIDE_PTR_UB7]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP4]], label %[[CONT11:.*]], label %[[TRAP10:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP10]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT11]]: +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR5]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP5]], label %[[CONT13:.*]], label %[[TRAP12:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP12]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT13]]: +// CHECK-NEXT: [[LEN2:%.*]] = getelementptr inbounds nuw [[STRUCT_DOUBLE]], ptr [[WIDE_PTR_PTR5]], i32 0, i32 1 +// CHECK-NEXT: store i32 11, ptr [[LEN2]], align 4 +// CHECK-NEXT: ret void +// +void double_no_flexbase_update_both(struct Double * __bidi_indexable p) { + p->len = 11; + p->len2 = 11; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[META5]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-bidi-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-bidi-O2.c new file mode 100644 index 0000000000000..5d3aec0e34dd7 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-bidi-O2.c @@ -0,0 +1,441 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --version 5 + + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64e-apple-iphoneos -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64e-apple-iphoneos -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Simple { + int len; + int fam[__counted_by(len)]; +}; + +// rdar://132731845 the flexible arrays are not bounds checked + +// CHECK-LABEL: define dso_local void @simple_no_flexbase_update( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP_SROA_3_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT1:.*]], label %[[TRAP:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT1]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], align 4, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret void +// +void simple_no_flexbase_update(struct Simple * __bidi_indexable p) { + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @simple_flexbase_update( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P2_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[P2_SROA_4_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[P2_SROA_4_0_COPYLOAD:%.*]] = load ptr, ptr [[P2_SROA_4_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[P2_SROA_5_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[P2_SROA_5_0_COPYLOAD:%.*]] = load ptr, ptr [[P2_SROA_5_0_P_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[P2_SROA_0_0_COPYLOAD]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[P2_SROA_4_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[P2_SROA_5_0_COPYLOAD]], [[P2_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT1:.*]], label %[[TRAP:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT1]]: +// CHECK-NEXT: store i32 11, ptr [[P2_SROA_0_0_COPYLOAD]], align 4, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret void +// +void simple_flexbase_update(struct Simple * __bidi_indexable p) { + struct Simple * __bidi_indexable p2 = p; + p2->len = 11; +} + +// CHECK-LABEL: define dso_local void @simple_flexbase_self_assign( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP_SROA_3_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT1:.*]], label %[[TRAP:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT1]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], align 4, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret void +// +void simple_flexbase_self_assign(struct Simple * __bidi_indexable p) { + p = p; + p->len = 11; +} + +struct Shared { + int len; + int * __counted_by(len) ptr; + int fam[__counted_by(len)]; +}; +int * __counted_by(len) baz(int len); + +// CHECK-LABEL: define dso_local void @shared_no_flexbase_update( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef 11) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[AGG_TEMP29_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP29_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP29_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP29_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP29_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP29_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP29_SROA_3_0_P_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP29_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP29_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP29_SROA_3_0_COPYLOAD]], [[AGG_TEMP29_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT37:.*]], label %[[TRAP:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT37]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP29_SROA_0_0_COPYLOAD]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[AGG_TEMP45_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP45_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP29_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[AGG_TEMP45_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[AGG_TEMP29_SROA_3_0_COPYLOAD]], [[AGG_TEMP45_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND54:%.*]] = select i1 [[TMP4]], i1 [[TMP5]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND54]], label %[[CONT53:.*]], label %[[TRAP]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[CONT53]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[CALL]], ptr [[PTR]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret void +// +void shared_no_flexbase_update(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p->len = 11; + p->ptr = p2; +} + +// CHECK-LABEL: define dso_local void @shared_no_flexbase_update_reverse( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef 11) #[[ATTR4]] +// CHECK-NEXT: [[AGG_TEMP36_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP36_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP36_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP36_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP36_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_3_0_P_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP36_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP36_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP36_SROA_3_0_COPYLOAD]], [[AGG_TEMP36_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT44:.*]], label %[[TRAP:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT44]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TEMP36_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[CALL]], ptr [[PTR]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[AGG_TEMP45_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP45_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[AGG_TEMP45_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[AGG_TEMP36_SROA_3_0_COPYLOAD]], [[AGG_TEMP45_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND54:%.*]] = select i1 [[TMP4]], i1 [[TMP5]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND54]], label %[[CONT53:.*]], label %[[TRAP]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[CONT53]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret void +// +void shared_no_flexbase_update_reverse(struct Shared * __bidi_indexable p) { + p->ptr = baz(11); + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_update( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef 11) #[[ATTR4]] +// CHECK-NEXT: [[P2_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[P2_SROA_5_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[P2_SROA_5_0_COPYLOAD:%.*]] = load ptr, ptr [[P2_SROA_5_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[P2_SROA_7_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[P2_SROA_7_0_COPYLOAD:%.*]] = load ptr, ptr [[P2_SROA_7_0_P_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[P2_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[P2_SROA_5_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[P2_SROA_7_0_COPYLOAD]], [[P2_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT44:.*]], label %[[TRAP:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT44]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[P2_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[CALL]], ptr [[PTR]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: store i32 11, ptr [[P2_SROA_0_0_COPYLOAD]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret void +// +void shared_flexbase_update(struct Shared * __bidi_indexable p) { + int * p3 = baz(11); + struct Shared * __bidi_indexable p2 = p; + p2->ptr = p3; + p2->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_update_reverse( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef 11) #[[ATTR4]] +// CHECK-NEXT: [[P2_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[P2_SROA_5_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[P2_SROA_5_0_COPYLOAD:%.*]] = load ptr, ptr [[P2_SROA_5_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[P2_SROA_7_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[P2_SROA_7_0_COPYLOAD:%.*]] = load ptr, ptr [[P2_SROA_7_0_P_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[P2_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[P2_SROA_5_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[P2_SROA_7_0_COPYLOAD]], [[P2_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT37:.*]], label %[[TRAP:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT37]]: +// CHECK-NEXT: store i32 11, ptr [[P2_SROA_0_0_COPYLOAD]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[P2_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[CALL]], ptr [[PTR]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret void +// +void shared_flexbase_update_reverse(struct Shared * __bidi_indexable p) { + int * p3 = baz(11); + struct Shared * __bidi_indexable p2 = p; + p2->len = 11; + p2->ptr = p3; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef 11) #[[ATTR4]] +// CHECK-NEXT: [[AGG_TEMP36_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP36_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP36_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP36_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP36_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_3_0_P_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP36_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP36_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP36_SROA_3_0_COPYLOAD]], [[AGG_TEMP36_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT44:.*]], label %[[TRAP:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT44]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TEMP36_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[CALL]], ptr [[PTR]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[AGG_TEMP45_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP45_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP36_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[AGG_TEMP45_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[AGG_TEMP36_SROA_3_0_COPYLOAD]], [[AGG_TEMP45_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND54:%.*]] = select i1 [[TMP4]], i1 [[TMP5]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND54]], label %[[CONT53:.*]], label %[[TRAP]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[CONT53]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p = p; + p->ptr = p2; + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign_reverse( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef 11) #[[ATTR4]] +// CHECK-NEXT: [[AGG_TEMP29_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP29_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP29_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP29_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP29_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP29_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP29_SROA_3_0_P_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP29_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP29_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP29_SROA_3_0_COPYLOAD]], [[AGG_TEMP29_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT37:.*]], label %[[TRAP:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT37]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP29_SROA_0_0_COPYLOAD]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[AGG_TEMP45_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP45_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP29_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[AGG_TEMP45_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[AGG_TEMP29_SROA_3_0_COPYLOAD]], [[AGG_TEMP45_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND54:%.*]] = select i1 [[TMP4]], i1 [[TMP5]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND54]], label %[[CONT53:.*]], label %[[TRAP]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[CONT53]]: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TEMP45_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[CALL]], ptr [[PTR]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign_reverse(struct Shared * __bidi_indexable p) { + int * p2 = baz(11); + p = p; + p->len = 11; + p->ptr = p2; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign_fr( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_5_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP1_SROA_5_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_5_0_P_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP1_SROA_3_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP1_SROA_5_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT12:.*]], label %[[TRAP:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT12]]: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[OR_COND78:%.*]] = icmp sgt i32 [[TMP3]], 10, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND78]], label %[[CONT73:.*]], label %[[TRAP]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[CONT73]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign_fr(struct Shared * __bidi_indexable p) { + p = p; + p->ptr = p->ptr; + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @shared_flexbase_self_assign_fr_reverse( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_5_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP1_SROA_5_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_5_0_P_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP1_SROA_3_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP1_SROA_5_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT12:.*]], label %[[TRAP:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT12]]: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[PTR]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[OR_COND78:%.*]] = icmp sgt i32 [[TMP3]], 10, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND78]], label %[[CONT56:.*]], label %[[TRAP]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[CONT56]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[AGG_TEMP65_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP65_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[AGG_TEMP65_SROA_0_0_COPYLOAD]], i64 16 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[AGG_TEMP65_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[AGG_TEMP1_SROA_5_0_COPYLOAD]], [[AGG_TEMP65_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND77:%.*]] = select i1 [[TMP6]], i1 [[TMP7]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND77]], label %[[CONT73:.*]], label %[[TRAP]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[CONT73]]: +// CHECK-NEXT: [[PTR74:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TEMP65_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[PTR74]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret void +// +void shared_flexbase_self_assign_fr_reverse(struct Shared * __bidi_indexable p) { + p = p; + p->len = 11; + p->ptr = p->ptr; +} + +struct Double { + int len; + int len2; + int fam[__counted_by(len + len2)]; +}; + +// CHECK-LABEL: define dso_local void @double_no_flexbase_update_once( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP_SROA_3_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT1:.*]], label %[[TRAP:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT1]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], align 4, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret void +// +void double_no_flexbase_update_once(struct Double * __bidi_indexable p) { + p->len = 11; +} + +// CHECK-LABEL: define dso_local void @double_no_flexbase_update_both( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_P_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP_SROA_3_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label %[[CONT1:.*]], label %[[TRAP:.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: [[CONT1]]: +// CHECK-NEXT: store i32 11, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], align 4, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[AGG_TEMP2_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP2_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[AGG_TEMP2_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[TMP3]], [[AGG_TEMP2_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[AGG_TEMP_SROA_3_0_COPYLOAD]], [[AGG_TEMP2_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND11:%.*]] = select i1 [[TMP4]], i1 [[TMP5]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND11]], label %[[CONT10:.*]], label %[[TRAP]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: [[CONT10]]: +// CHECK-NEXT: [[LEN2:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TEMP2_SROA_0_0_COPYLOAD]], i64 4 +// CHECK-NEXT: store i32 11, ptr [[LEN2]], align 4, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret void +// +void double_no_flexbase_update_both(struct Double * __bidi_indexable p) { + p->len = 11; + p->len2 = 11; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-O2.c new file mode 100644 index 0000000000000..7e099399f82c3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-O2.c @@ -0,0 +1,373 @@ +// XFAIL: * +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + + +// CHECK-LABEL: @test_lvalue_base_count_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[DOTB:%.*]] = load i1, ptr @test_lvalue_base_count_fail.g_flex.0, align 4 +// CHECK-NEXT: br i1 [[DOTB]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: store i1 true, ptr @test_lvalue_base_count_fail.g_flex.0, align 4 +// CHECK-NEXT: ret void +// +void test_lvalue_base_count_fail() { + static flex_t g_flex = {4, {1, 2, 3, 4}}; + g_flex.count = 5; +} + +// CHECK-LABEL: @test_lvalue_base_count_may_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @test_lvalue_base_count_may_fail.g_flex.0, align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: [[DEC:%.*]] = add nsw i32 [[TMP0]], -1 +// CHECK-NEXT: store i32 [[DEC]], ptr @test_lvalue_base_count_may_fail.g_flex.0, align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void test_lvalue_base_count_may_fail() { + static flex_t g_flex = {4, {1, 2, 3, 4}}; + g_flex.count--; +} + + +// CHECK-LABEL: @test_under_base_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_under_base_fail() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *__single flex = (flex_t *)oob_ptr; +} + +// CHECK-LABEL: @test_under_base_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_under_base_ok() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *flex = (flex_t *)oob_ptr; +} + + +// CHECK-LABEL: @test_under_base_fail2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR11:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT8:%.*]], {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR11]] +// CHECK-NEXT: ret void +// +void test_under_base_fail2() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *flex = (flex_t *)oob_ptr; + int a = flex->count; +} + + +// CHECK-LABEL: @test_over_base_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_over_base_fail() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *__single flex = (flex_t *)oob_ptr; +} + +// CHECK-LABEL: @test_over_base_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_over_base_ok() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *flex = (flex_t *)oob_ptr; +} + +// CHECK-LABEL: @test_over_base_fail2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR11]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 20 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT8:%.*]], {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: {{.*}}: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR11]] +// CHECK-NEXT: ret void +// +void test_over_base_fail2() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *flex = (flex_t *)oob_ptr; + int a = flex->count; +} + +// CHECK-LABEL: @test_small_base_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[ARR:%.*]] = alloca [3 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 3, ptr nonnull [[ARR]]) #[[ATTR11]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 3 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLEX_T:%.*]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT46:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT46]], {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.assume(i1 [[OR_COND]]) +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_small_base_fail(flex_t *flex) { + char arr[3]; + flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_small_base_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_small_base_ok() { + char arr[3]; + flex_t *flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_small_base_fail2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[ARR:%.*]] = alloca [3 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 3, ptr nonnull [[ARR]]) #[[ATTR11]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLEX_T:%.*]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 3 +// CHECK-NEXT: [[DOTNOT49:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT49]], {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.assume(i1 [[OR_COND]]) +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_small_base_fail2() { + char arr[3]; + flex_t *flex = (flex_t *)arr; + (void)(*flex); +} + +// CHECK-LABEL: @test_count_from_buf_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_count_from_buf_ok() { + char arr[20] = {4, 0}; + flex_t *__single flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_count_from_buf_ok2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_count_from_buf_ok2(flex_t *flex) { + char arr[20] = {4, 0}; + flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_count_from_buf_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_count_from_buf_fail() { + char arr[20] = {5, 0}; + flex_t *__single flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_count_from_buf_fail2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_count_from_buf_fail2(flex_t *flex) { + char arr[20] = {5, 0}; + flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_base_count_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_base_count_ok(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->count = 4; +} + +// CHECK-LABEL: @test_base_count_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_base_count_fail(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->count = 5; +} + +// CHECK-LABEL: @test_base_fam_access( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_base_fam_access(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->count = 4; + + flex->elems[3] = 0; +} + +// CHECK-LABEL: @test_base_fam_access_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_base_fam_access_fail(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->count = 4; + + flex->elems[4] = 0; +} + +// CHECK-LABEL: @test_unsafe_base_fam_access( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_unsafe_base_fam_access(flex_t *__unsafe_indexable flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->count = 4; + + flex->elems[3] = 0; +} + +// CHECK-LABEL: @test_unsafe_base_fam_access_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_unsafe_base_fam_access_fail(flex_t *__unsafe_indexable flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->count = 4; + + flex->elems[4] = 0; +} + +// CHECK-LABEL: @test_null_base_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: unreachable +// +void test_null_base_fail(flex_t *__single flex) { + void *p = 0; + flex = p; + flex->count = 4; +} + +// CHECK-LABEL: @test_null_base_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_null_base_ok(flex_t *__single flex) { + flex = 0; +} + +// CHECK-LABEL: @test_null_base_fam_access_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_null_base_fam_access_fail(flex_t *__single flex) { + flex = 0; + + flex->elems[4] = 42; +} + +typedef struct { + unsigned dummy; + flex_t *flex; +} flex_wrapper_t; + +// CHECK-LABEL: @test_flex_init_list_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void test_flex_init_list_ok() { + char buf[20] = {4, 0}; + flex_t *flex = (flex_t *)buf; + flex_wrapper_t flex_wrap = {0, flex}; +} + +// CHECK-LABEL: @test_flex_init_list_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_flex_init_list_fail() { + char buf[20] = {5, 0}; + flex_t *flex = (flex_t *)buf; + flex_wrapper_t flex_wrap = {0, flex}; +} + +void sink(flex_t *__single flex); + +// CHECK-LABEL: @test_flex_argument_ok( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: [[BUF:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[BUF]]) #[[ATTR11]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(20) [[BUF]], ptr noundef nonnull align 1 dereferenceable(20) @__const.test_flex_argument_ok.buf, i64 20, i1 false) +// CHECK-NEXT: call void @sink(ptr noundef nonnull [[BUF]]) #[[ATTR11]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[BUF]]) #[[ATTR11]] +// CHECK-NEXT: ret void +// +void test_flex_argument_ok() { + char buf[20] = {4, 0}; + flex_t *flex = (flex_t *)buf; + sink(flex); +} + +// CHECK-LABEL: @test_flex_argument_fail( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_flex_argument_fail() { + char buf[20] = {5, 0}; + flex_t *flex = (flex_t *)buf; + sink(flex); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-bitcast-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-bitcast-O2.c new file mode 100644 index 0000000000000..1628ee81683e1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-bitcast-O2.c @@ -0,0 +1,280 @@ +// XFAIL: * +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @test_under_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_under_base_fail() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *__single flex = oob_ptr; +} + +// CHECK-LABEL: @test_under_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_under_base_ok() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *flex = oob_ptr; +} + + +// CHECK-LABEL: @test_under_base_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR10:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR10]] +// CHECK-NEXT: ret void +// +void test_under_base_fail2() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *flex = oob_ptr; + int a = flex->count; +} + + +// CHECK-LABEL: @test_over_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_over_base_fail() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *__single flex = oob_ptr; +} + +// CHECK-LABEL: @test_over_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_over_base_ok() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *flex = oob_ptr; +} + +// CHECK-LABEL: @test_over_base_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR10]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 20 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR10]] +// CHECK-NEXT: ret void +// +void test_over_base_fail2() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *flex = oob_ptr; + int a = flex->count; +} + +// XXX: Missing assumption? +// CHECK-LABEL: @test_small_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [3 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 3, ptr nonnull [[ARR]]) #[[ATTR10]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 3 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLEX_T:%.*]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT46:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT46]], {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.assume(i1 [[OR_COND]]) +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_small_base_fail(flex_t *flex) { + char arr[3]; + flex = arr; +} + +// CHECK-LABEL: @test_small_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_small_base_ok() { + char arr[3]; + flex_t *flex = arr; +} + +// CHECK-LABEL: @test_small_base_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [3 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 3, ptr nonnull [[ARR]]) #[[ATTR10]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLEX_T:%.*]], ptr [[ARR]], i64 1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARR]], i64 3 +// CHECK-NEXT: [[DOTNOT49:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT49]], {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.assume(i1 [[OR_COND]]) +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_small_base_fail2() { + char arr[3]; + flex_t *flex = arr; + (void)(*flex); +} + +// CHECK-LABEL: @test_count_from_buf_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_count_from_buf_ok() { + char arr[20] = {4, 0}; + flex_t *__single flex = arr; +} + +// CHECK-LABEL: @test_count_from_buf_ok2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_count_from_buf_ok2(flex_t *flex) { + char arr[20] = {4, 0}; + flex = arr; +} + +// CHECK-LABEL: @test_count_from_buf_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_count_from_buf_fail() { + char arr[20] = {5, 0}; + flex_t *__single flex = arr; +} + +// CHECK-LABEL: @test_count_from_buf_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_count_from_buf_fail2(flex_t *flex) { + char arr[20] = {5, 0}; + flex = arr; +} + +// CHECK-LABEL: @test_base_count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_base_count_ok(flex_t *__single flex) { + char arr[20]; + flex = arr; + flex->count = 4; +} + +// CHECK-LABEL: @test_base_count_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_base_count_fail(flex_t *__single flex) { + char arr[20]; + flex = arr; + flex->count = 5; +} + +// CHECK-LABEL: @test_base_fam_access( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_base_fam_access(flex_t *__single flex) { + char arr[20]; + flex = arr; + flex->count = 4; + + flex->elems[3] = 0; +} + +// CHECK-LABEL: @test_base_fam_access_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_base_fam_access_fail(flex_t *__single flex) { + char arr[20]; + flex = arr; + flex->count = 4; + + flex->elems[4] = 0; +} + +typedef struct { + unsigned dummy; + flex_t *flex; +} flex_wrapper_t; + +// CHECK-LABEL: @test_flex_init_list_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_flex_init_list_ok() { + char buf[20] = {4, 0}; + flex_wrapper_t flex_wrap = {0, buf}; +} + +// CHECK-LABEL: @test_flex_init_list_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_flex_init_list_fail() { + char buf[20] = {5, 0}; + flex_wrapper_t flex_wrap = {0, buf}; +} + +void sink(flex_t *__single flex); + +// CHECK-LABEL: @test_flex_argument_ok( +// CHECK-NEXT: cont41: +// CHECK-NEXT: [[BUF:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[BUF]]) #[[ATTR10]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(20) [[BUF]], ptr noundef nonnull align 1 dereferenceable(20) @__const.test_flex_argument_ok.buf, i64 20, i1 false) +// CHECK-NEXT: call void @sink(ptr noundef nonnull [[BUF]]) #[[ATTR10]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[BUF]]) #[[ATTR10]] +// CHECK-NEXT: ret void +// +void test_flex_argument_ok() { + char buf[20] = {4, 0}; + sink(buf); +} + +// CHECK-LABEL: @test_flex_argument_fail( +// CHECK-NEXT: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_flex_argument_fail() { + char buf[20] = {5, 0}; + sink(buf); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-no-count-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-no-count-O2.c new file mode 100644 index 0000000000000..4d4a35afafa57 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-checks-no-count-O2.c @@ -0,0 +1,297 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int count; + int elems[]; +} flex_t; + +// CHECK-LABEL: @test_under_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR7:[0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[BOUND_PTR_ARITH]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[CONT8:%.*]], label [[BOUNDSCHECK_NOTNULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 20 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT8]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR8:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void test_under_base_fail() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *__single flex = (flex_t *)oob_ptr; +} + +// CHECK-LABEL: @test_under_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_under_base_ok() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *flex = (flex_t *)oob_ptr; +} + + +// CHECK-LABEL: @test_under_base_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -1 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARR]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void test_under_base_fail2() { + char arr[20]; + char *oob_ptr = arr - 1; + flex_t *flex = (flex_t *)oob_ptr; + int a = flex->count; +} + + +// CHECK-LABEL: @test_over_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_over_base_fail() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *__single flex = (flex_t *)oob_ptr; +} + +// CHECK-LABEL: @test_over_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_over_base_ok() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *flex = (flex_t *)oob_ptr; +} + +// CHECK-LABEL: @test_over_base_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [20 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 20 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARR]], i64 24 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 20, ptr nonnull [[ARR]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void test_over_base_fail2() { + char arr[20]; + char *oob_ptr = arr + 20; + flex_t *flex = (flex_t *)oob_ptr; + int a = flex->count; +} + +// FIXME: rdar://84809738 +// CHECK-LABEL: @test_small_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_small_base_fail(flex_t *flex) { + char arr[3]; + flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_small_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_small_base_ok() { + char arr[3]; + flex_t *flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_small_base_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_small_base_fail2() { + char arr[3]; + flex_t *flex = (flex_t *)arr; + (void)(*flex); +} + +// CHECK-LABEL: @test_base_only_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_base_only_ok() { + char arr[20]; + flex_t *__single flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_count_from_buf_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_count_from_buf_ok() { + char arr[4]; + flex_t *__single flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_count_from_buf_ok2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_count_from_buf_ok2(flex_t *flex) { + char arr[4]; + flex = (flex_t *)arr; +} + +// FIXME: rdar://84809738 +// CHECK-LABEL: @test_count_from_buf_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_count_from_buf_fail() { + char arr[3]; + flex_t *__single flex = (flex_t *)arr; +} + +// CHECK-LABEL: @test_base_count_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_base_count_ok(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + (void)flex->count; +} + +// CHECK-LABEL: @test_base_fam_access_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_base_fam_access_fail(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->elems[0] = 0; +} + +// CHECK-LABEL: @test_base_fam_access_fail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_base_fam_access_fail2(flex_t *__single flex) { + char arr[20]; + flex = (flex_t *)arr; + flex->elems[1] = 0; +} + +// FIXME: rdar://80820825 +// CHECK-LABEL: @test_null_base_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: unreachable +// +void test_null_base_fail(flex_t *__single flex) { + flex = 0; + flex->count = 4; +} + +// CHECK-LABEL: @test_null_base_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_null_base_ok(flex_t *__single flex) { + flex = 0; +} + +// CHECK-LABEL: @test_null_base_fam_access_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void test_null_base_fam_access_fail(flex_t *__single flex) { + flex = 0; + + flex->elems[4] = 42; +} + +typedef struct { + unsigned dummy; + flex_t *flex; +} flex_wrapper_t; + +// CHECK-LABEL: @test_flex_init_list_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_flex_init_list_ok() { + char buf[4]; + flex_t *flex = (flex_t *)buf; + flex_wrapper_t flex_wrap = {0, flex}; +} + +// FIXME: rdar://84809738 +// CHECK-LABEL: @test_flex_init_list_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void test_flex_init_list_fail() { + char buf[3]; + flex_t *flex = (flex_t *)buf; + flex_wrapper_t flex_wrap = {0, flex}; +} + +void sink(flex_t *__single flex); + +// CHECK-LABEL: @test_flex_argument_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca [4 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[BUF]]) #[[ATTR7]] +// CHECK-NEXT: call void @sink(ptr noundef nonnull [[BUF]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[BUF]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void test_flex_argument_ok() { + char buf[4]; + flex_t *flex = (flex_t *)buf; + sink(flex); +} + +// FIXME: rdar://84809738 +// CHECK-LABEL: @test_flex_argument_fail( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca [3 x i8], align 1 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 3, ptr nonnull [[BUF]]) #[[ATTR7]] +// CHECK-NEXT: call void @sink(ptr noundef nonnull [[BUF]]) #[[ATTR7]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 3, ptr nonnull [[BUF]]) #[[ATTR7]] +// CHECK-NEXT: ret void +// +void test_flex_argument_fail() { + char buf[3]; + flex_t *flex = (flex_t *)buf; + sink(flex); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-const-call.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-const-call.c new file mode 100644 index 0000000000000..db1a3befb30d7 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-const-call.c @@ -0,0 +1,143 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +unsigned data_const_global_count __unsafe_late_const; + +// CHECK-LABEL: @get_const_count( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @data_const_global_count, align 4 +// CHECK-NEXT: ret i32 [[TMP0]] +// +__attribute__((const)) int get_const_count() { + return data_const_global_count; +} + +struct struct_flex { + void *dummy; + int fam[__counted_by(get_const_count())]; +}; + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[NEW_PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT28:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr [[STRUCT_STRUCT_FLEX:%.*]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[WIDE_PTR_PTR5]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_STRUCT_FLEX]], ptr [[WIDE_PTR_PTR12]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB14]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT18:%.*]], label [[TRAP17:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap17: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont18: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB16]], [[WIDE_PTR_PTR12]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT20:%.*]], label [[TRAP19:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap19: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont20: +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_STRUCT_FLEX]], ptr [[WIDE_PTR_PTR12]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM]], i64 0, i64 0 +// CHECK-NEXT: [[CALL:%.*]] = call i32 @get_const_count() #[[ATTR5:[0-9]+]] +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[CALL]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label [[CONT22:%.*]], label [[TRAP21:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap21: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont22: +// CHECK-NEXT: [[TMP8:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT24:%.*]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap23: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont24: +// CHECK-NEXT: [[TMP9:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT26:%.*]], label [[TRAP25:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap25: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont26: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[CALL]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT28]], label [[TRAP27:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap27: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont28: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT38:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR30]], [[WIDE_PTR_UB32]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT36:%.*]], label [[TRAP35:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap35: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont36: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR30]], [[WIDE_PTR_LB34]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT38]], label [[TRAP37:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap37: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont38: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR30]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void test(struct struct_flex *flex, void *__bidi_indexable new_ptr) { + flex = new_ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-globals.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-globals.c new file mode 100644 index 0000000000000..8abe30e2413d3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-globals.c @@ -0,0 +1,52 @@ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct flex { + unsigned char count; + unsigned elems[__counted_by(count)]; +}; + +// CHECK: @f0 = global { i8, [3 x i8], [0 x i32] } zeroinitializer, align 4 +struct flex f0 = { }; + +// CHECK: @f1 = global { i8, [3 x i8], [0 x i32] } zeroinitializer, align 4 +struct flex f1 = { 0 }; + +// CHECK: @f2 = global { i8, [3 x i8], [0 x i32] } zeroinitializer, align 4 +struct flex f2 = { .count = 0 }; + +// CHECK: @f3 = global { i8, [3 x i8], [0 x i32] } zeroinitializer, align 4 +struct flex f3 = { 0, {} }; + +// CHECK: @f4 = global { i8, [3 x i8], [0 x i32] } zeroinitializer, align 4 +struct flex f4 = { .count = 0, {} }; + +// CHECK: @f5 = global { i8, [3 x i8], [0 x i32] } zeroinitializer, align 4 +struct flex f5 = { .count = 0, .elems = {} }; + +// CHECK: @f6 = global { i8, [3 x i8], [1 x i32] } { i8 1, [3 x i8] zeroinitializer, [1 x i32] [i32 1] }, align 4 +struct flex f6 = { .count = 1, { 1 } }; + +// CHECK: @f7 = global { i8, [3 x i8], [1 x i32] } { i8 1, [3 x i8] zeroinitializer, [1 x i32] [i32 1] }, align 4 +struct flex f7 = { 1, { 1 } }; + +// CHECK: @f8 = global { i8, [3 x i8], [1 x i32] } { i8 1, [3 x i8] zeroinitializer, [1 x i32] [i32 1] }, align 4 +struct flex f8 = { .count = 1, { 1 } }; + +// CHECK: @f9 = global { i8, [3 x i8], [1 x i32] } { i8 1, [3 x i8] zeroinitializer, [1 x i32] [i32 1] }, align 4 +struct flex f9 = { .count = 1, .elems = { 1 } }; + +// CHECK: @f10 = global { i8, [3 x i8], [3 x i32] } { i8 3, [3 x i8] zeroinitializer, [3 x i32] [i32 4, i32 1, i32 2] }, align 4 +struct flex f10 = { .count = 3, { 4, 1, 2 } }; + +// CHECK: @f11 = global { i8, [3 x i8], [3 x i32] } { i8 3, [3 x i8] zeroinitializer, [3 x i32] [i32 4, i32 1, i32 2] }, align 4 +struct flex f11 = { .count = 3, { 4, 1, [2] = 2 } }; + +// CHECK: @f12 = global { i8, [3 x i8], [3 x i32] } { i8 3, [3 x i8] zeroinitializer, [3 x i32] [i32 4, i32 0, i32 3] }, align 4 +struct flex f12 = { .count = 3, { 4, [2] = 3 } }; + +// CHECK: @f13 = global { i8, [3 x i8], [3 x i32] } { i8 3, [3 x i8] zeroinitializer, [3 x i32] [i32 0, i32 0, i32 3] }, align 4 +struct flex f13 = { .count = 3, { [2] = 3 } }; diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-multiple-decls-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-multiple-decls-O2.c new file mode 100644 index 0000000000000..0e64150ff2664 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-multiple-decls-O2.c @@ -0,0 +1,92 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int len; + int offs; + int fam[__counted_by(len - offs)]; +} S; + +// CHECK-LABEL: @good_fit( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void good_fit(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 2; + s->len = 10; +} + +// CHECK-LABEL: @good_fit2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void good_fit2(S *s) { + char arr[40] = {0}; + s = (S *)&arr[1]; + s->offs = 2; + s->len = 9; +} + +// CHECK-LABEL: @good_shrink( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void good_shrink(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 2; + s->len = 9; +} + +// CHECK-LABEL: @good_zero( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void good_zero(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 2; + s->len = 2; +} + +// CHECK-LABEL: @trap_access_to_count_zero( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void trap_access_to_count_zero(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 2; + s->len = 2; + s->fam[0] = 0; +} + +// CHECK-LABEL: @trap_len_too_big( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void trap_len_too_big(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 2; + s->len = 11; +} + +// CHECK-LABEL: @trap_negative_count( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void trap_negative_count(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 10; + s->len = 2; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-multiple-decls.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-multiple-decls.c new file mode 100644 index 0000000000000..5c6b2ad286a91 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-multiple-decls.c @@ -0,0 +1,202 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int len; + int offs; + int fam[__counted_by(len - offs)]; +} S; + + +// CHECK-LABEL: @trap_len_too_big( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[S_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[ARR:%.*]] = alloca [40 x i8], align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[S:%.*]], ptr [[S_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[ARR]], i8 0, i64 40, i1 false) +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [40 x i8], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY]], i64 40 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT29:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr [[STRUCT_S:%.*]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_PTR5]], [[TMP6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr [[STRUCT_S]], ptr [[WIDE_PTR_PTR12]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP8]], [[WIDE_PTR_UB14]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT18:%.*]], label [[TRAP17:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap17: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont18: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_LB16]], [[WIDE_PTR_PTR12]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT20:%.*]], label [[TRAP19:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap19: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont20: +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[WIDE_PTR_PTR12]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY21:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM]], i64 0, i64 0 +// CHECK-NEXT: br i1 true, label [[CONT23:%.*]], label [[TRAP22:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap22: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont23: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ule ptr [[ARRAYDECAY21]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT25:%.*]], label [[TRAP24:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap24: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont25: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT27:%.*]], label [[TRAP26:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap26: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont27: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY21]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 9, [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT29]], label [[TRAP28:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap28: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont29: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR31:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB33:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB35:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR34]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = icmp ne ptr [[WIDE_PTR_PTR31]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT39:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP14:%.*]] = icmp ult ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_UB33]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT37:%.*]], label [[TRAP36:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap36: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont37: +// CHECK-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR31]], [[WIDE_PTR_LB35]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT39]], label [[TRAP38:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap38: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont39: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR31]], ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[OFFS:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[TMP16]], i32 0, i32 1 +// CHECK-NEXT: store i32 2, ptr [[OFFS]], align 4 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[TMP17]], i32 0, i32 0 +// CHECK-NEXT: store i32 11, ptr [[LEN]], align 4 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[FAM41:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[TMP18]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY42:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM41]], i64 0, i64 0 +// CHECK-NEXT: [[FAM44:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[TMP18]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY45:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM44]], i64 0, i64 0 +// CHECK-NEXT: [[LEN46:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[TMP18]], i32 0, i32 0 +// CHECK-NEXT: [[TMP19:%.*]] = load i32, ptr [[LEN46]], align 4 +// CHECK-NEXT: [[OFFS47:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[TMP18]], i32 0, i32 1 +// CHECK-NEXT: [[TMP20:%.*]] = load i32, ptr [[OFFS47]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP19]], [[TMP20]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[SUB]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY45]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB49]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY42]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR51]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY42]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR53:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR52]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR53]], i64 0 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR56:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB57:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR56]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = icmp ult ptr [[TMP28]], [[WIDE_PTR_UB55]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP29]], label [[CONT59:%.*]], label [[TRAP58:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap58: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont59: +// CHECK-NEXT: [[TMP30:%.*]] = icmp uge ptr [[TMP28]], [[WIDE_PTR_LB57]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP30]], label [[CONT61:%.*]], label [[TRAP60:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap60: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont61: +// CHECK-NEXT: store i32 0, ptr [[TMP28]], align 4 +// CHECK-NEXT: ret void +// +void trap_len_too_big(S *s) { + char arr[40] = {0}; + s = (S *)arr; + s->offs = 2; + s->len = 11; + + s->fam[0] = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-null-deref-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-null-deref-O2.c new file mode 100644 index 0000000000000..7fba7ee9b14fc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-null-deref-O2.c @@ -0,0 +1,73 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @null_noderef( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_noderef() { + flex_t *__single b = (flex_t *)0; +} + +// CHECK-LABEL: @null_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void null_deref() { + flex_t *__single a = (flex_t *)0; + (void)*a; +} + +// CHECK-LABEL: @null_deref2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void null_deref2() { + flex_t *a = (flex_t *)0; + flex_t *__single b = a; + (void)b->elems[0]; +} + +// CHECK-LABEL: @null_deref3( +// CHECK-NEXT: entry: +// CHECK-NEXT: unreachable +// +void null_deref3() { + flex_t *a = (flex_t *)0; + flex_t *__single b = a; + b->count = 0; +} + +// CHECK-LABEL: @null_deref_member_access( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void null_deref_member_access() { + flex_t *a = 0; + flex_t *__single b = a; + (*b).count = 10; +} + +// XXX: rdar://103082765 +// CHECK-LABEL: @null_deref_array_access( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void null_deref_array_access() { + flex_t *a = (flex_t *)0; + flex_t *__single b = a; + (*b).elems[0] = 10; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-assignment-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-assignment-O2.c new file mode 100644 index 0000000000000..c27220390864d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-assignment-O2.c @@ -0,0 +1,147 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @pointer_assign_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_good() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex_t *__single b = a; +} + +// CHECK-LABEL: @pointer_assign_good2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_good2() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex_t *__single b = (flex_t *)a; +} + +// CHECK-LABEL: @pointer_count_assign_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_count_assign_trap() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex_t *__single b = (flex_t *)a; + b->count = 11; +} + +typedef struct { + int count; + int elems[__counted_by(count+1)]; +} flex1_t; + +// CHECK-LABEL: @pointer_assign_bidi_oob_notrap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_bidi_oob_notrap() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex1_t *b = (flex1_t *)a; +} + +// CHECK-LABEL: @pointer_assign_bidi_to_single_oob_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_assign_bidi_to_single_oob_trap() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex1_t *b = (flex1_t *)a; + flex1_t *__single c = b; +} + +// CHECK-LABEL: @pointer_assign_single_oob_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_assign_single_oob_trap() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex1_t *__single b = (flex1_t *)a; +} + +// CHECK-LABEL: @pointer_count_assign_single_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_count_assign_single_good() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)arr; + flex1_t *__single b = (flex1_t *)a; + b->count = 9; +} + +// CHECK-LABEL: @pointer_assign_single_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_single_good() { + int arr[11] = {9}; + flex1_t *__single a = (flex1_t *)arr; + flex_t *__single b = (flex_t *)a; +} + +// CHECK-LABEL: @pointer_assign_bidi_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_bidi_good() { + int arr[11] = {9}; + flex1_t *__single a = (flex1_t *)arr; + flex_t *b = (flex_t *)a; +} + +// CHECK-LABEL: @pointer_count_assign_single_good2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_count_assign_single_good2() { + int arr[11] = {9}; + flex1_t *__single a = (flex1_t *)arr; + flex_t *__single b = (flex_t *)a; + b->count = 9; +} + +// CHECK-LABEL: @pointer_count_assign_single_oob_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_count_assign_single_oob_trap() { + int arr[11] = {9}; + flex1_t *__single a = (flex1_t *)arr; + flex_t *__single b = (flex_t *)a; + b->count = 11; +} + +// CHECK-LABEL: @pointer_count_assign_single_negative_count_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_count_assign_single_negative_count_trap() { + int arr[11] = {9}; + flex1_t *__single a = (flex1_t *)arr; + flex_t *__single b = (flex_t *)a; + b->count = -1; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-assignment.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-assignment.c new file mode 100644 index 0000000000000..b8bfbe3a14091 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-assignment.c @@ -0,0 +1,398 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @pointer_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[B:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: ret void +// +void pointer_assign(flex_t *flex) { + flex_t *b = flex; +} + +// CHECK-LABEL: @pointer_assign_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[S:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP16:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT35:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP12:%.*]] = icmp ule ptr [[TMP11]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP13:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: [[ELEMS14:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY15:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS14]], i64 0, i64 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP16]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP16]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR18]], i64 1 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ule ptr [[TMP14]], [[WIDE_PTR_UB20]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT24:%.*]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap23: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont24: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB22]], [[WIDE_PTR_PTR18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT26:%.*]], label [[TRAP25:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap25: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont26: +// CHECK-NEXT: [[COUNT27:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR18]], i32 0, i32 0 +// CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[COUNT27]], align 4 +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[TMP17]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label [[CONT29:%.*]], label [[TRAP28:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap28: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont29: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ule ptr [[ARRAYDECAY15]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP18]], label [[CONT31:%.*]], label [[TRAP30:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap30: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont31: +// CHECK-NEXT: [[TMP19:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP19]], label [[CONT33:%.*]], label [[TRAP32:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap32: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont33: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY15]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[TMP17]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT35]], label [[TRAP34:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap34: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont35: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR37:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB41:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR40]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = icmp ne ptr [[WIDE_PTR_PTR37]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP20]], label [[BOUNDSCHECK_NOTNULL42:%.*]], label [[CONT46:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull42: +// CHECK-NEXT: [[TMP21:%.*]] = icmp ult ptr [[WIDE_PTR_PTR37]], [[WIDE_PTR_UB39]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP21]], label [[CONT44:%.*]], label [[TRAP43:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap43: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont44: +// CHECK-NEXT: [[TMP22:%.*]] = icmp uge ptr [[WIDE_PTR_PTR37]], [[WIDE_PTR_LB41]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP22]], label [[CONT46]], label [[TRAP45:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap45: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont46: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR37]], ptr [[S]], align 8 +// CHECK-NEXT: ret void +// +void pointer_assign_single(flex_t *flex) { + flex_t *__single s = flex; +} + +// CHECK-LABEL: @pointer_and_count_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[B:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[B]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[B]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[TMP9]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[COUNT3:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: store i32 10, ptr [[COUNT3]], align 4 +// CHECK-NEXT: ret void +// +void pointer_and_count_assign(flex_t *flex) { + flex_t *b = flex; + b->count = 10; +} + +// CHECK-LABEL: @pointer_and_count_assign_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[B:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT23:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP12:%.*]] = icmp ule ptr [[TMP11]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP13:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: [[ELEMS14:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY15:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS14]], i64 0, i64 0 +// CHECK-NEXT: br i1 true, label [[CONT17:%.*]], label [[TRAP16:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap16: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont17: +// CHECK-NEXT: [[TMP14:%.*]] = icmp ule ptr [[ARRAYDECAY15]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT19:%.*]], label [[TRAP18:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap18: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont19: +// CHECK-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT21:%.*]], label [[TRAP20:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap20: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont21: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY15]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 10, [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT23]], label [[TRAP22:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap22: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont23: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR25:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB27:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB29:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR28]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = icmp ne ptr [[WIDE_PTR_PTR25]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[BOUNDSCHECK_NOTNULL30:%.*]], label [[CONT34:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull30: +// CHECK-NEXT: [[TMP17:%.*]] = icmp ult ptr [[WIDE_PTR_PTR25]], [[WIDE_PTR_UB27]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT32:%.*]], label [[TRAP31:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap31: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont32: +// CHECK-NEXT: [[TMP18:%.*]] = icmp uge ptr [[WIDE_PTR_PTR25]], [[WIDE_PTR_LB29]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP18]], label [[CONT34]], label [[TRAP33:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap33: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont34: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR25]], ptr [[B]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[B]], align 8 +// CHECK-NEXT: [[COUNT35:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[TMP19]], i32 0, i32 0 +// CHECK-NEXT: store i32 10, ptr [[COUNT35]], align 4 +// CHECK-NEXT: ret void +// +void pointer_and_count_assign_single(flex_t *flex) { + flex_t *__single b = flex; + b->count = 10; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-call-builtin-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-call-builtin-O2.c new file mode 100644 index 0000000000000..e1c857061da1c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-call-builtin-O2.c @@ -0,0 +1,202 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @set( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[FLEX:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[BOUNDSCHECK_CONT:%.*]], label [[BOUNDSCHECK_NOTNULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds nuw i8, ptr [[FLEX]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[FLEX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ELEMS]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[AGG_TEMP2_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR]], [[BOUNDSCHECK_NOTNULL]] ], [ null, [[ENTRY:%.*]] ] +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SIZE:%.*]] to i64 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[FLEX]], [[AGG_TEMP2_SROA_3_0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP2_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[FLEX]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP68_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP68_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 1 [[FLEX]], i8 0, i64 [[CONV]], i1 false) +// CHECK-NEXT: [[DOTNOT89:%.*]] = icmp ne ptr [[FLEX]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT90:%.*]] = icmp eq i32 [[SIZE]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND91:%.*]] = and i1 [[DOTNOT89]], [[DOTNOT90]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND91]], label [[TRAP]], label [[CONT88:%.*]], {{!annotation ![0-9]+}} +// CHECK: cont88: +// CHECK-NEXT: ret ptr [[FLEX]] +// +void *set(flex_t *flex, unsigned size) { + return __builtin_memset(flex, 0, size); +} + +// CHECK-LABEL: @cpy( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[DEST:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[BOUNDSCHECK_CONT:%.*]], label [[BOUNDSCHECK_NOTNULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[DEST]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ELEMS]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[AGG_TEMP2_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR]], [[BOUNDSCHECK_NOTNULL]] ], [ null, [[ENTRY:%.*]] ] +// CHECK-NEXT: [[DOTNOT188:%.*]] = icmp eq ptr [[SRC:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT188]], label [[BOUNDSCHECK_CONT12:%.*]], label [[BOUNDSCHECK_NOTNULL5:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull5: +// CHECK-NEXT: [[ELEMS6:%.*]] = getelementptr inbounds nuw i8, ptr [[SRC]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[SRC]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT9:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR10:%.*]] = getelementptr inbounds i32, ptr [[ELEMS6]], i64 [[IDX_EXT9]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT12]] +// CHECK: boundscheck.cont12: +// CHECK-NEXT: [[AGG_TEMP4_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR10]], [[BOUNDSCHECK_NOTNULL5]] ], [ null, [[BOUNDSCHECK_CONT]] ] +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SIZE:%.*]] to i64 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[SRC]], [[AGG_TEMP4_SROA_3_0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP4_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP84_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP84_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP102_NOT:%.*]] = icmp ugt ptr [[DEST]], [[AGG_TEMP2_SROA_3_0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND191:%.*]] = select i1 [[OR_COND]], i1 true, i1 [[CMP102_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST154:%.*]] = ptrtoint ptr [[AGG_TEMP2_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST155:%.*]] = ptrtoint ptr [[DEST]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB156:%.*]] = sub i64 [[SUB_PTR_LHS_CAST154]], [[SUB_PTR_RHS_CAST155]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP157_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB156]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND192:%.*]] = select i1 [[OR_COND191]], i1 true, i1 [[CMP157_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND192]], label [[TRAP:%.*]], label [[CONT160:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont160: +// CHECK-NEXT: tail call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[SRC]], i64 [[CONV]], i1 false) +// CHECK-NEXT: [[DOTNOT189:%.*]] = icmp ne ptr [[DEST]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT190:%.*]] = icmp eq i32 [[SIZE]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND193:%.*]] = and i1 [[DOTNOT189]], [[DOTNOT190]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND193]], label [[TRAP]], label [[CONT186:%.*]], {{!annotation ![0-9]+}} +// CHECK: cont186: +// CHECK-NEXT: ret ptr [[DEST]] +// +void *cpy(flex_t *dest, const flex_t *src, unsigned size) { + return __builtin_memcpy(dest, src, size); +} + +// CHECK-LABEL: @pcpy( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[DEST:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[BOUNDSCHECK_CONT:%.*]], label [[BOUNDSCHECK_NOTNULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[DEST]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ELEMS]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR]], [[BOUNDSCHECK_NOTNULL]] ], [ null, [[ENTRY:%.*]] ] +// CHECK-NEXT: [[DOTNOT175:%.*]] = icmp eq ptr [[SRC:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT175]], label [[BOUNDSCHECK_CONT11:%.*]], label [[BOUNDSCHECK_NOTNULL4:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull4: +// CHECK-NEXT: [[ELEMS5:%.*]] = getelementptr inbounds nuw i8, ptr [[SRC]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[SRC]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT8:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR9:%.*]] = getelementptr inbounds i32, ptr [[ELEMS5]], i64 [[IDX_EXT8]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT11]] +// CHECK: boundscheck.cont11: +// CHECK-NEXT: [[AGG_TEMP3_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR9]], [[BOUNDSCHECK_NOTNULL4]] ], [ null, [[BOUNDSCHECK_CONT]] ] +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SIZE:%.*]] to i64 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[SRC]], [[AGG_TEMP3_SROA_3_0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP3_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP83_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP83_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP101_NOT:%.*]] = icmp ugt ptr [[DEST]], [[AGG_TEMP1_SROA_3_0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND176:%.*]] = select i1 [[OR_COND]], i1 true, i1 [[CMP101_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST153:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST154:%.*]] = ptrtoint ptr [[DEST]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB155:%.*]] = sub i64 [[SUB_PTR_LHS_CAST153]], [[SUB_PTR_RHS_CAST154]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP156_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB155]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND177:%.*]] = select i1 [[OR_COND176]], i1 true, i1 [[CMP156_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND177]], label [[TRAP:%.*]], label [[CONT159:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont159: +// CHECK-NEXT: tail call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[SRC]], i64 [[CONV]], i1 false) +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 [[CONV]] +// CHECK-NEXT: ret ptr [[TMP2]] +// +void *__unsafe_indexable pcpy(flex_t *dest, const flex_t *src, unsigned size) { + return __builtin_mempcpy(dest, src, size); +} + +// CHECK-LABEL: @move( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[DEST:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[BOUNDSCHECK_CONT:%.*]], label [[BOUNDSCHECK_NOTNULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds nuw i8, ptr [[DEST]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[DEST]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ELEMS]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[AGG_TEMP2_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR]], [[BOUNDSCHECK_NOTNULL]] ], [ null, [[ENTRY:%.*]] ] +// CHECK-NEXT: [[DOTNOT188:%.*]] = icmp eq ptr [[SRC:%.*]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT188]], label [[BOUNDSCHECK_CONT12:%.*]], label [[BOUNDSCHECK_NOTNULL5:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull5: +// CHECK-NEXT: [[ELEMS6:%.*]] = getelementptr inbounds nuw i8, ptr [[SRC]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[SRC]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT9:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR10:%.*]] = getelementptr inbounds i32, ptr [[ELEMS6]], i64 [[IDX_EXT9]] +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT12]] +// CHECK: boundscheck.cont12: +// CHECK-NEXT: [[AGG_TEMP4_SROA_3_0:%.*]] = phi ptr [ [[ADD_PTR10]], [[BOUNDSCHECK_NOTNULL5]] ], [ null, [[BOUNDSCHECK_CONT]] ] +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SIZE:%.*]] to i64 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[SRC]], [[AGG_TEMP4_SROA_3_0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP4_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP84_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP84_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP102_NOT:%.*]] = icmp ugt ptr [[DEST]], [[AGG_TEMP2_SROA_3_0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND191:%.*]] = select i1 [[OR_COND]], i1 true, i1 [[CMP102_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST154:%.*]] = ptrtoint ptr [[AGG_TEMP2_SROA_3_0]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST155:%.*]] = ptrtoint ptr [[DEST]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB156:%.*]] = sub i64 [[SUB_PTR_LHS_CAST154]], [[SUB_PTR_RHS_CAST155]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP157_NOT:%.*]] = icmp ult i64 [[SUB_PTR_SUB156]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND192:%.*]] = select i1 [[OR_COND191]], i1 true, i1 [[CMP157_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND192]], label [[TRAP:%.*]], label [[CONT160:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont160: +// CHECK-NEXT: tail call void @llvm.memmove.p0.p0.i64(ptr align 1 [[DEST]], ptr align 1 [[SRC]], i64 [[CONV]], i1 false) +// CHECK-NEXT: [[DOTNOT189:%.*]] = icmp ne ptr [[DEST]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT190:%.*]] = icmp eq i32 [[SIZE]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND193:%.*]] = and i1 [[DOTNOT189]], [[DOTNOT190]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND193]], label [[TRAP]], label [[CONT186:%.*]], {{!annotation ![0-9]+}} +// CHECK: cont186: +// CHECK-NEXT: ret ptr [[DEST]] +// +void *move(flex_t *dest, const flex_t *src, unsigned size) { + return __builtin_memmove(dest, src, size); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-call-builtin.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-call-builtin.c new file mode 100644 index 0000000000000..fbcc0fabfe643 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-call-builtin.c @@ -0,0 +1,183 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +// CHECK-LABEL: @set( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FLEX_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP38:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP53:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP54:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP69:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[FLEX:%.*]], ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: store i32 [[SIZE:%.*]], ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FLEX_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[TMP12]] to i64 +// CHECK-NEXT: [[TMP13:%.*]] = bitcast i64 [[CONV]] to i64 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB11]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR4]], [[WIDE_PTR_PTR13]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB21]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: [[CMP35:%.*]] = icmp ule ptr [[WIDE_PTR_PTR23]], [[WIDE_PTR_PTR30]] +// CHECK-NEXT: br i1 [[CMP35]], label [[LAND_RHS:%.*]], label [[LAND_END]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP38]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB40:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR39]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB40]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP38]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR42]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB44]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB46]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR48:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB50:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR49]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP37]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB52:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR51]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP54]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR56:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB58:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR57]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP54]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB60:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR59]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP53]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR56]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP53]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB58]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP53]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB60]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP53]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP53]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP53]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR48]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR62]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP67:%.*]] = icmp ule i64 [[CONV]], [[SUB_PTR_SUB]] +// CHECK-NEXT: br label [[LAND_END]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP23:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[CMP67]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP23]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP69]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR70:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR71:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR70]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB73:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR74:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP69]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB75:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR74]], align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[WIDE_PTR_PTR71]], i8 0, i64 [[CONV]], i1 false) +// CHECK-NEXT: [[CMP76:%.*]] = icmp sge i64 [[CONV]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP76]]) +// CHECK-NEXT: [[ADD_PTR78:%.*]] = getelementptr inbounds nuw i8, ptr [[WIDE_PTR_PTR71]], i64 [[CONV]] +// CHECK-NEXT: ret void +// +void set(flex_t *flex, unsigned size) { + __builtin_memset(flex, 0, size); +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-deref.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-deref.c new file mode 100644 index 0000000000000..8720b9ada9973 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-deref.c @@ -0,0 +1,459 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +flex_t g_flex = {2, {1, 2}}; + +// CHECK-LABEL: @addrof_g( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @g_flex, align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr getelementptr inbounds nuw ([[STRUCT_FLEX_T:%.*]], ptr @g_flex, i32 0, i32 1), i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr @g_flex, ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr @g_flex, ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT32:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[TMP6]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP8:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR16]], i64 1 +// CHECK-NEXT: [[TMP10:%.*]] = icmp ule ptr [[TMP9]], [[WIDE_PTR_UB18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT22:%.*]], label [[TRAP21:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap21: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont22: +// CHECK-NEXT: [[TMP11:%.*]] = icmp ule ptr [[WIDE_PTR_LB20]], [[WIDE_PTR_PTR16]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT24:%.*]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap23: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont24: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR16]], i32 0, i32 0 +// CHECK-NEXT: [[TMP12:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[TMP12]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label [[CONT26:%.*]], label [[TRAP25:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap25: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont26: +// CHECK-NEXT: [[TMP13:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT28:%.*]], label [[TRAP27:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap27: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont28: +// CHECK-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT30:%.*]], label [[TRAP29:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap29: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont30: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[TMP12]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT32]], label [[TRAP31:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap31: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont32: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[WIDE_PTR_PTR34]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT42:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ult ptr [[WIDE_PTR_PTR34]], [[WIDE_PTR_UB36]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT40:%.*]], label [[TRAP39:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap39: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont40: +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[WIDE_PTR_PTR34]], [[WIDE_PTR_LB38]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT42]], label [[TRAP41:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap41: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont42: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR34]] +// +flex_t *addrof_g(void) { + return &g_flex; +} + +// CHECK-LABEL: @addrof_deref_g( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca [[STRUCT_FLEX_T:%.*]], align 4 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @g_flex, align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr getelementptr inbounds nuw ([[STRUCT_FLEX_T]], ptr @g_flex, i32 0, i32 1), i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr @g_flex, ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr @g_flex, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap3: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont4: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[AGG_TEMP1]], ptr align 4 [[WIDE_PTR_PTR]], i64 4, i1 false) +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: [[ELEMS6:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY7:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS6]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[IDX_EXT8:%.*]] = sext i32 [[TMP6]] to i64 +// CHECK-NEXT: [[ADD_PTR9:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY7]], i64 [[IDX_EXT8]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[AGG_TEMP1]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR9]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[AGG_TEMP1]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB11]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR13]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR15]], i64 0 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ult ptr [[TMP14]], [[WIDE_PTR_UB17]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT21:%.*]], label [[TRAP20:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap20: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont21: +// CHECK-NEXT: [[TMP16:%.*]] = icmp uge ptr [[TMP14]], [[WIDE_PTR_LB19]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT23:%.*]], label [[TRAP22:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap22: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont23: +// CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[TMP14]], align 4 +// CHECK-NEXT: ret i32 [[TMP17]] +// +int addrof_deref_g(void) { + return (*&g_flex).elems[0]; +} + +// CHECK-LABEL: @assigning_array_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[FLEX:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP44:%.*]] = alloca [[STRUCT_FLEX_T:%.*]], align 4 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP62:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT32:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[TMP2]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP4:%.*]] = icmp ule ptr [[WIDE_PTR_LB9]], [[WIDE_PTR_PTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: [[ELEMS:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR5]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS]], i64 0, i64 0 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR16]], i64 1 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[WIDE_PTR_UB18]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT22:%.*]], label [[TRAP21:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap21: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont22: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[WIDE_PTR_LB20]], [[WIDE_PTR_PTR16]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT24:%.*]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap23: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont24: +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[WIDE_PTR_PTR16]], i32 0, i32 0 +// CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[TMP8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label [[CONT26:%.*]], label [[TRAP25:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap25: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont26: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT28:%.*]], label [[TRAP27:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap27: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont28: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT30:%.*]], label [[TRAP29:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap29: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont30: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[TMP8]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT32]], label [[TRAP31:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap31: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont32: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR34]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT42:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR34]], [[WIDE_PTR_UB36]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT40:%.*]], label [[TRAP39:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap39: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont40: +// CHECK-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR34]], [[WIDE_PTR_LB38]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT42]], label [[TRAP41:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap41: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont42: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[FLEX]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[FLEX]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[TMP14]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[BOUNDSCHECK_NOTNULL46:%.*]], label [[BOUNDSCHECK_NULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull46: +// CHECK-NEXT: [[ELEMS47:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[TMP14]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY48:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS47]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT49:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[TMP14]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[COUNT49]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP16]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY48]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP19]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP22]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR51:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB53:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR52]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB55:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR54]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = icmp ult ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_UB53]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP23]], label [[CONT57:%.*]], label [[TRAP56:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap56: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont57: +// CHECK-NEXT: [[TMP24:%.*]] = icmp uge ptr [[WIDE_PTR_PTR51]], [[WIDE_PTR_LB55]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP24]], label [[CONT59:%.*]], label [[TRAP58:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap58: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont59: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[AGG_TEMP44]], ptr align 4 [[WIDE_PTR_PTR51]], i64 4, i1 false) +// CHECK-NEXT: [[ELEMS60:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY61:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS60]], i64 0, i64 0 +// CHECK-NEXT: [[ELEMS63:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[AGG_TEMP44]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY64:%.*]] = getelementptr inbounds [0 x i32], ptr [[ELEMS63]], i64 0, i64 0 +// CHECK-NEXT: [[COUNT65:%.*]] = getelementptr inbounds nuw [[STRUCT_FLEX_T]], ptr [[AGG_TEMP44]], i32 0, i32 0 +// CHECK-NEXT: [[TMP25:%.*]] = load i32, ptr [[COUNT65]], align 4 +// CHECK-NEXT: [[IDX_EXT66:%.*]] = sext i32 [[TMP25]] to i64 +// CHECK-NEXT: [[ADD_PTR67:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY64]], i64 [[IDX_EXT66]] +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[AGG_TEMP44]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR67]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[AGG_TEMP44]], ptr [[TMP28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR68:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB69:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR68]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB69]], ptr [[TMP29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR70:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR71:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR70]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY61]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR71]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY61]], ptr [[TMP32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[TMP33:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR73]], i64 0 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: [[TMP34:%.*]] = icmp ult ptr [[TMP33]], [[WIDE_PTR_UB75]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP34]], label [[CONT79:%.*]], label [[TRAP78:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap78: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont79: +// CHECK-NEXT: [[TMP35:%.*]] = icmp uge ptr [[TMP33]], [[WIDE_PTR_LB77]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP35]], label [[CONT81:%.*]], label [[TRAP80:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap80: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont81: +// CHECK-NEXT: store i32 10, ptr [[TMP33]], align 4 +// CHECK-NEXT: ret void +// +void assigning_array_single(flex_t *__bidi_indexable p) { + flex_t *__single flex = p; + (*flex).elems[0] = 10; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-nullptr-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-nullptr-O2.c new file mode 100644 index 0000000000000..54e47b343a2cf --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-nullptr-O2.c @@ -0,0 +1,162 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +typedef struct { + flex_t *f; +} wflex_t; + +// CHECK-LABEL: @null_init( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_init(void) { + wflex_t b = {0}; + flex_t *f = b.f; +} + +// CHECK-LABEL: @null_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_assign(void) { + wflex_t b = {0}; + flex_t *f; + f = b.f; +} + +// CHECK-LABEL: @null_init_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_init_assign(void) { + wflex_t b = {0}; + flex_t *f = 0; + f = b.f; +} + +// CHECK-LABEL: @null_init_ret( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr null +// +flex_t *null_init_ret(void) { + wflex_t b = {0}; + flex_t *f = b.f; + return f; +} + +// CHECK-LABEL: @null_assign_ret( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr null +// +flex_t *null_assign_ret(void) { + wflex_t b = {0}; + flex_t *f = 0; + return f = b.f; +} + +// CHECK-LABEL: @null_init_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_init_single(void) { + wflex_t b = {0}; + flex_t *__single f = b.f; +} + +// CHECK-LABEL: @null_assign_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_assign_single(void) { + wflex_t b = {0}; + flex_t *__single f = 0; + f = b.f; +} + +// CHECK-LABEL: @null_init_single_ret( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr null +// +flex_t *null_init_single_ret(void) { + wflex_t b = {0}; + flex_t *__single f = b.f; + return f; +} + +// CHECK-LABEL: @null_init_bidi_to_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void null_init_bidi_to_single(void) { + wflex_t b = {0}; + flex_t *f = b.f; + flex_t *__single f2 = f; +} + + +typedef struct { + int dummy; + flex_t inner; +} flex_wrap_t; + +// CHECK-LABEL: @nested_init( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void nested_init(void) { + flex_wrap_t *a = 0; + flex_wrap_t *__single b = a; +} + + +// CHECK-LABEL: @nested_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void nested_assign(void) { + flex_wrap_t *a = 0; + flex_wrap_t *__single b = 0; + b = a; +} + +// CHECK-LABEL: @nested_single_to_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void nested_single_to_single(void) { + flex_wrap_t *__single a = 0; + flex_wrap_t *__single b = 0; + b = a; +} + +static inline flex_wrap_t *accept_flex(flex_wrap_t *p) { + flex_wrap_t *__single a = p; + return a; +} + +// CHECK-LABEL: @pass_zero_to_flex( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void pass_zero_to_flex(void) { + accept_flex(0); +} + + +// CHECK-LABEL: @nested_return( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr null +// +flex_wrap_t *nested_return(void) { + flex_wrap_t *a = 0; + return a; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-returns-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-returns-O2.c new file mode 100644 index 0000000000000..eaa2ca8388bbe --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-promotion-returns-O2.c @@ -0,0 +1,120 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + + +#include + +typedef struct { + int count; + int elems[__counted_by(count)]; +} flex_t; + +inline flex_t *return_flex(int *__counted_by(11) buf) { + return (flex_t *)buf; +} + +// CHECK-LABEL: @pointer_assign_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_good() { + int arr[11] = {10}; + flex_t *__single a; + a = return_flex(arr); +} + +// CHECK-LABEL: @pointer_assign_good2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_assign_good2() { + int arr[11] = {10}; + flex_t *__single a; + a = (flex_t *)return_flex(arr); +} + +// CHECK-LABEL: @pointer_init_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_init_good() { + int arr[11] = {10}; + flex_t *__single a = return_flex(arr); +} + +// CHECK-LABEL: @pointer_init_good2( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_init_good2() { + int arr[11] = {10}; + flex_t *__single a = (flex_t *)return_flex(arr); +} + +// CHECK-LABEL: @pointer_count_assign_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_count_assign_trap() { + int arr[11] = {10}; + flex_t *__single a; + a = return_flex(arr); + a->count = 11; +} + +// CHECK-LABEL: @pointer_count_init_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void pointer_count_init_trap() { + int arr[11] = {10}; + flex_t *__single a = return_flex(arr); + a->count = 11; +} + +// CHECK-LABEL: @pointer_count_assign_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_count_assign_good() { + int arr[11] = {10}; + flex_t *__single a; + a = return_flex(arr); + a->count = 9; +} + +// CHECK-LABEL: @pointer_count_init_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void pointer_count_init_good() { + int arr[11] = {10}; + flex_t *__single a = return_flex(arr); + a->count = 9; +} + +// CHECK-LABEL: @elem_access_good( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: ret void +// +void elem_access_good() { + int arr[11] = {10}; + flex_t *__single a; + a = return_flex(arr); + a->elems[9] = 0; +} + +// CHECK-LABEL: @elem_access_trap( +// CHECK-NEXT: {{.*}}: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void elem_access_trap() { + int arr[11] = {10}; + flex_t *__single a; + a = return_flex(arr); + a->elems[10] = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-O0.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-O0.c new file mode 100644 index 0000000000000..ce6b0ddd7d76d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-O0.c @@ -0,0 +1,562 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct Inner { + int * __counted_by(len) ptr; + int len; +}; +struct Outer { + struct Inner hdr; + int fam[__counted_by(hdr.len)]; +}; + +struct Outer * __sized_by(sizeof(struct Outer) + sizeof(int) * len) bar(int len); +int * __counted_by(len) baz(int len); + +// CHECK-LABEL: define dso_local ptr @foo( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP24:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP80:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP88:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef [[TMP0]]) +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CALL1:%.*]] = call ptr @bar(i32 noundef [[TMP4]]) +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[MUL:%.*]] = mul i64 4, [[CONV]] +// CHECK-NEXT: [[ADD:%.*]] = add i64 16, [[MUL]] +// CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[ADD]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr inbounds nuw i8, ptr [[CALL1]], i64 [[ADD]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL1]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP9:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB8]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP9]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END39:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP21:%.*]] = icmp ule ptr [[WIDE_PTR_LB13]], [[WIDE_PTR_PTR16]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP21]], label %[[LAND_RHS:.*]], label %[[LAND_END39]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV23:%.*]] = sext i32 [[TMP8]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP24]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB26]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR29]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP34:%.*]] = icmp sle i64 [[CONV23]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP34]], label %[[LAND_RHS36:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS36]]: +// CHECK-NEXT: [[CMP37:%.*]] = icmp sle i32 0, [[TMP8]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP37]], %[[LAND_RHS36]] ] +// CHECK-NEXT: br label %[[LAND_END39]], !annotation [[META2]] +// CHECK: [[LAND_END39]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP9]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR43:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB47:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR46]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR43]], null, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label %[[FLEX_BASE_NONNULL:.*]], label %[[CONT68:.*]], !annotation [[META4]] +// CHECK: [[FLEX_BASE_NONNULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr [[STRUCT_OUTER:%.*]], ptr [[WIDE_PTR_PTR43]], i64 1 +// CHECK-NEXT: [[TMP12:%.*]] = icmp ule ptr [[WIDE_PTR_PTR43]], [[TMP11]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT49:.*]], label %[[TRAP48:.*]], !prof [[PROF3]], !annotation [[META5]] +// CHECK: [[TRAP48]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT49]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP50]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR52:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB56:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR55]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [[STRUCT_OUTER]], ptr [[WIDE_PTR_PTR52]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = icmp ule ptr [[TMP13]], [[WIDE_PTR_UB54]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT58:.*]], label %[[TRAP57:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP57]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT58]]: +// CHECK-NEXT: [[TMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB56]], [[WIDE_PTR_PTR52]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT60:.*]], label %[[TRAP59:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP59]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT60]]: +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[WIDE_PTR_PTR52]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM]], i64 0, i64 0 +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[TMP8]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label %[[CONT62:.*]], label %[[TRAP61:.*]], !prof [[PROF3]], !annotation [[META8]] +// CHECK: [[TRAP61]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META8]] +// CHECK-NEXT: unreachable, !annotation [[META8]] +// CHECK: [[CONT62]]: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB45]], !annotation [[META9:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT64:.*]], label %[[TRAP63:.*]], !prof [[PROF3]], !annotation [[META9]] +// CHECK: [[TRAP63]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META9]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// CHECK: [[CONT64]]: +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[WIDE_PTR_PTR43]], [[WIDE_PTR_LB47]], !annotation [[META10:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT66:.*]], label %[[TRAP65:.*]], !prof [[PROF3]], !annotation [[META10]] +// CHECK: [[TRAP65]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META10]] +// CHECK-NEXT: unreachable, !annotation [[META10]] +// CHECK: [[CONT66]]: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB45]] to i64, !annotation [[META11:![0-9]+]] +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, !annotation [[META11]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], !annotation [[META11]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, !annotation [[META11]] +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[TMP8]] to i64, !annotation [[META11]] +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT_DIV]], !annotation [[META11]] +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label %[[CONT68]], label %[[TRAP67:.*]], !prof [[PROF3]], !annotation [[META11]] +// CHECK: [[TRAP67]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META11]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT68]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = icmp ne ptr [[WIDE_PTR_PTR70]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP18]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT78:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP19:%.*]] = icmp ult ptr [[WIDE_PTR_PTR70]], [[WIDE_PTR_UB72]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP19]], label %[[CONT76:.*]], label %[[TRAP75:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP75]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT76]]: +// CHECK-NEXT: [[TMP20:%.*]] = icmp uge ptr [[WIDE_PTR_PTR70]], [[WIDE_PTR_LB74]], !annotation [[META7]] +// CHECK-NEXT: br i1 [[TMP20]], label %[[CONT78]], label %[[TRAP77:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP77]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT78]]: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR70]], ptr [[P]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[HDR:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP21]], i32 0, i32 0 +// CHECK-NEXT: [[LEN79:%.*]] = getelementptr inbounds nuw [[STRUCT_INNER:%.*]], ptr [[HDR]], i32 0, i32 1 +// CHECK-NEXT: store i32 [[TMP8]], ptr [[LEN79]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP80]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR81:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR82:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR81]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR83:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB84:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR83]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR85:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP80]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB86:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR85]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[HDR87:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP22]], i32 0, i32 0 +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_INNER]], ptr [[HDR87]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR82]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = icmp ne ptr [[TMP23]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP24]], label %[[BOUNDSCHECK_NOTNULL89:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL89]]: +// CHECK-NEXT: [[FAM90:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP23]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY91:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM90]], i64 0, i64 0 +// CHECK-NEXT: [[HDR92:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP23]], i32 0, i32 0 +// CHECK-NEXT: [[LEN93:%.*]] = getelementptr inbounds nuw [[STRUCT_INNER]], ptr [[HDR92]], i32 0, i32 1 +// CHECK-NEXT: [[TMP25:%.*]] = load i32, ptr [[LEN93]], align 8 +// CHECK-NEXT: [[IDX_EXT94:%.*]] = sext i32 [[TMP25]] to i64 +// CHECK-NEXT: [[ADD_PTR95:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY91]], i64 [[IDX_EXT94]] +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR95]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP28]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP31]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR96:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR97:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR96]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR98:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB99:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR98]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR100:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB101:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR100]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = icmp ne ptr [[WIDE_PTR_PTR97]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP32]], label %[[BOUNDSCHECK_NOTNULL102:.*]], label %[[CONT106:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL102]]: +// CHECK-NEXT: [[TMP33:%.*]] = icmp ult ptr [[WIDE_PTR_PTR97]], [[WIDE_PTR_UB99]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP33]], label %[[CONT104:.*]], label %[[TRAP103:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP103]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT104]]: +// CHECK-NEXT: [[TMP34:%.*]] = icmp uge ptr [[WIDE_PTR_PTR97]], [[WIDE_PTR_LB101]], !annotation [[META7]] +// CHECK-NEXT: br i1 [[TMP34]], label %[[CONT106]], label %[[TRAP105:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP105]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT106]]: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR97]] +// +struct Outer *foo(int len) { + int * p2 = baz(len); + struct Outer * __single p = bar(len); + p->hdr.len = len; + p->hdr.ptr = p2; + return p; +} + +// CHECK-LABEL: define dso_local ptr @foo2( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP24:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP79:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP88:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @baz(i32 noundef [[TMP0]]) +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[CALL]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[CALL1:%.*]] = call ptr @bar(i32 noundef [[TMP4]]) +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[MUL:%.*]] = mul i64 4, [[CONV]] +// CHECK-NEXT: [[ADD:%.*]] = add i64 16, [[MUL]] +// CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[ADD]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr inbounds nuw i8, ptr [[CALL1]], i64 [[ADD]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[CALL1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR3]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[CALL1]], ptr [[TMP7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[P2]], i64 24, i1 false) +// CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP9:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB8]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP9]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END39:.*]], !annotation [[META2]] +// CHECK: [[LAND_LHS_TRUE]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP21:%.*]] = icmp ule ptr [[WIDE_PTR_LB13]], [[WIDE_PTR_PTR16]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP21]], label %[[LAND_RHS:.*]], label %[[LAND_END39]], !annotation [[META2]] +// CHECK: [[LAND_RHS]]: +// CHECK-NEXT: [[CONV23:%.*]] = sext i32 [[TMP8]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP24]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP24]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB26]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR29]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP34:%.*]] = icmp sle i64 [[CONV23]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP34]], label %[[LAND_RHS36:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// CHECK: [[LAND_RHS36]]: +// CHECK-NEXT: [[CMP37:%.*]] = icmp sle i32 0, [[TMP8]], !annotation [[META2]] +// CHECK-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// CHECK: [[LAND_END]]: +// CHECK-NEXT: [[TMP9:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP37]], %[[LAND_RHS36]] ] +// CHECK-NEXT: br label %[[LAND_END39]], !annotation [[META2]] +// CHECK: [[LAND_END39]]: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP9]], %[[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP10]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR43:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB45:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB47:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR46]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR43]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label %[[FLEX_BASE_NONNULL:.*]], label %[[CONT68:.*]], !annotation [[META4]] +// CHECK: [[FLEX_BASE_NONNULL]]: +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr [[STRUCT_OUTER:%.*]], ptr [[WIDE_PTR_PTR43]], i64 1 +// CHECK-NEXT: [[TMP12:%.*]] = icmp ule ptr [[WIDE_PTR_PTR43]], [[TMP11]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP12]], label %[[CONT49:.*]], label %[[TRAP48:.*]], !prof [[PROF3]], !annotation [[META5]] +// CHECK: [[TRAP48]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT49]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP50]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR52:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB56:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR55]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [[STRUCT_OUTER]], ptr [[WIDE_PTR_PTR52]], i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = icmp ule ptr [[TMP13]], [[WIDE_PTR_UB54]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP14]], label %[[CONT58:.*]], label %[[TRAP57:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP57]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT58]]: +// CHECK-NEXT: [[TMP15:%.*]] = icmp ule ptr [[WIDE_PTR_LB56]], [[WIDE_PTR_PTR52]], !annotation [[META7]] +// CHECK-NEXT: br i1 [[TMP15]], label %[[CONT60:.*]], label %[[TRAP59:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP59]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT60]]: +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[WIDE_PTR_PTR52]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM]], i64 0, i64 0 +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[TMP8]], !annotation [[META8]] +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label %[[CONT62:.*]], label %[[TRAP61:.*]], !prof [[PROF3]], !annotation [[META8]] +// CHECK: [[TRAP61]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META8]] +// CHECK-NEXT: unreachable, !annotation [[META8]] +// CHECK: [[CONT62]]: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB45]], !annotation [[META9]] +// CHECK-NEXT: br i1 [[TMP16]], label %[[CONT64:.*]], label %[[TRAP63:.*]], !prof [[PROF3]], !annotation [[META9]] +// CHECK: [[TRAP63]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META9]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// CHECK: [[CONT64]]: +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[WIDE_PTR_PTR43]], [[WIDE_PTR_LB47]], !annotation [[META10]] +// CHECK-NEXT: br i1 [[TMP17]], label %[[CONT66:.*]], label %[[TRAP65:.*]], !prof [[PROF3]], !annotation [[META10]] +// CHECK: [[TRAP65]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META10]] +// CHECK-NEXT: unreachable, !annotation [[META10]] +// CHECK: [[CONT66]]: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB45]] to i64, !annotation [[META11]] +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, !annotation [[META11]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], !annotation [[META11]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = sdiv exact i64 [[FLEX_AVAIL_COUNT]], 4, !annotation [[META11]] +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[TMP8]] to i64, !annotation [[META11]] +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT_DIV]], !annotation [[META11]] +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label %[[CONT68]], label %[[TRAP67:.*]], !prof [[PROF3]], !annotation [[META11]] +// CHECK: [[TRAP67]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META11]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: [[CONT68]]: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = icmp ne ptr [[WIDE_PTR_PTR70]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP18]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[CONT78:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL]]: +// CHECK-NEXT: [[TMP19:%.*]] = icmp ult ptr [[WIDE_PTR_PTR70]], [[WIDE_PTR_UB72]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP19]], label %[[CONT76:.*]], label %[[TRAP75:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP75]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT76]]: +// CHECK-NEXT: [[TMP20:%.*]] = icmp uge ptr [[WIDE_PTR_PTR70]], [[WIDE_PTR_LB74]], !annotation [[META7]] +// CHECK-NEXT: br i1 [[TMP20]], label %[[CONT78]], label %[[TRAP77:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP77]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT78]]: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR70]], ptr [[P]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP79]], ptr align 8 [[AGG_TEMP4]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR80:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR81:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR80]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR82:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB83:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR82]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR84:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB85:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR84]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[HDR:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP21]], i32 0, i32 0 +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_INNER:%.*]], ptr [[HDR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR81]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[HDR86:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP22]], i32 0, i32 0 +// CHECK-NEXT: [[LEN87:%.*]] = getelementptr inbounds nuw [[STRUCT_INNER]], ptr [[HDR86]], i32 0, i32 1 +// CHECK-NEXT: store i32 [[TMP8]], ptr [[LEN87]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = icmp ne ptr [[TMP23]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP24]], label %[[BOUNDSCHECK_NOTNULL89:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL89]]: +// CHECK-NEXT: [[FAM90:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP23]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY91:%.*]] = getelementptr inbounds [0 x i32], ptr [[FAM90]], i64 0, i64 0 +// CHECK-NEXT: [[HDR92:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP23]], i32 0, i32 0 +// CHECK-NEXT: [[LEN93:%.*]] = getelementptr inbounds nuw [[STRUCT_INNER]], ptr [[HDR92]], i32 0, i32 1 +// CHECK-NEXT: [[TMP25:%.*]] = load i32, ptr [[LEN93]], align 8 +// CHECK-NEXT: [[IDX_EXT94:%.*]] = sext i32 [[TMP25]] to i64 +// CHECK-NEXT: [[ADD_PTR95:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY91]], i64 [[IDX_EXT94]] +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR95]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP28]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// CHECK: [[BOUNDSCHECK_NULL]]: +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP31]], align 8 +// CHECK-NEXT: br label %[[BOUNDSCHECK_CONT]] +// CHECK: [[BOUNDSCHECK_CONT]]: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR96:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR97:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR96]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR98:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB99:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR98]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR100:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP88]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB101:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR100]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = icmp ne ptr [[WIDE_PTR_PTR97]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP32]], label %[[BOUNDSCHECK_NOTNULL102:.*]], label %[[CONT106:.*]], !annotation [[META4]] +// CHECK: [[BOUNDSCHECK_NOTNULL102]]: +// CHECK-NEXT: [[TMP33:%.*]] = icmp ult ptr [[WIDE_PTR_PTR97]], [[WIDE_PTR_UB99]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP33]], label %[[CONT104:.*]], label %[[TRAP103:.*]], !prof [[PROF3]], !annotation [[META6]] +// CHECK: [[TRAP103]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: [[CONT104]]: +// CHECK-NEXT: [[TMP34:%.*]] = icmp uge ptr [[WIDE_PTR_PTR97]], [[WIDE_PTR_LB101]], !annotation [[META7]] +// CHECK-NEXT: br i1 [[TMP34]], label %[[CONT106]], label %[[TRAP105:.*]], !prof [[PROF3]], !annotation [[META7]] +// CHECK: [[TRAP105]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: [[CONT106]]: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR97]] +// +struct Outer *foo2(int len) { + int * p2 = baz(len); + struct Outer * __single p = bar(len); + p->hdr.ptr = p2; + p->hdr.len = len; + return p; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META5]] = !{!"bounds-safety-check-one-past-end-overflow"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META7]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[META8]] = !{!"bounds-safety-check-count-negative"} +// CHECK: [[META9]] = !{!"bounds-safety-check-flexible-count-gt-bounds", !"bounds-safety-check-ptr-le-upper-bound"} +// CHECK: [[META10]] = !{!"bounds-safety-check-flexible-count-gt-bounds", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[META11]] = !{!"bounds-safety-check-flexible-count-gt-bounds"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-O2.c new file mode 100644 index 0000000000000..c33dced3d3f09 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-O2.c @@ -0,0 +1,136 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + + +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +struct Inner { + int * __counted_by(len) ptr; + int len; +}; +struct Outer { + struct Inner hdr; + int fam[__counted_by(hdr.len)]; +}; + +struct Outer * __sized_by(sizeof(struct Outer) + sizeof(int) * len) bar(int len); +int * __counted_by(len) baz(int len); + +// CHECK-LABEL: define dso_local ptr @foo( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef [[LEN]]) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[LEN]] to i64 +// CHECK-NEXT: [[CALL1:%.*]] = tail call ptr @bar(i32 noundef [[LEN]]) #[[ATTR4]] +// CHECK-NEXT: [[MUL:%.*]] = shl nsw i64 [[IDX_EXT]], 2, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[LEN]], -5 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[CALL1]], i64 [[MUL]] +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr i8, ptr [[TMP0]], i64 16 +// CHECK-NEXT: [[CMP9_NOT:%.*]] = icmp slt i32 [[LEN]], 0, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP9_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK_NOT:%.*]] = icmp eq ptr [[CALL1]], null, !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK_NOT]], label %[[CONT69:.*]], label %[[FLEX_BASE_NONNULL:.*]], !annotation [[META6]] +// CHECK: [[FLEX_BASE_NONNULL]]: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[CALL1]], i64 16 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[CALL1]], [[TMP1]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: [[DOTNOT106:%.*]] = icmp ugt ptr [[TMP1]], [[ADD_PTR3]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT106]], !annotation [[META8]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP]], label %[[CONT60:.*]], !prof [[PROF9:![0-9]+]], !annotation [[META7]] +// CHECK: [[CONT60]]: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[ADD_PTR3]] to i64, !annotation [[META10:![0-9]+]] +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[TMP1]] to i64, !annotation [[META10]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], !annotation [[META10]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = ashr exact i64 [[FLEX_AVAIL_COUNT]], 2, !annotation [[META10]] +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext nneg i32 [[LEN]] to i64, !annotation [[META10]] +// CHECK-NEXT: [[FLEX_COUNT_CHECK_NOT:%.*]] = icmp uge i64 [[FLEX_AVAIL_COUNT_DIV]], [[FLEX_COUNT_INTPTR]], !annotation [[META10]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[CALL1]], [[ADD_PTR3]], !annotation [[META8]] +// CHECK-NEXT: [[OR_COND108:%.*]] = select i1 [[FLEX_COUNT_CHECK_NOT]], i1 [[TMP2]], i1 false, !annotation [[META6]] +// CHECK-NEXT: br i1 [[OR_COND108]], label %[[CONT69]], label %[[TRAP]], !prof [[PROF11:![0-9]+]], !annotation [[META10]] +// CHECK: [[CONT69]]: +// CHECK-NEXT: [[LEN70:%.*]] = getelementptr inbounds nuw i8, ptr [[CALL1]], i64 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN70]], align 8, !tbaa [[TBAA12:![0-9]+]] +// CHECK-NEXT: store ptr [[CALL]], ptr [[CALL1]], align 8, !tbaa [[TBAA19:![0-9]+]] +// CHECK-NEXT: ret ptr [[CALL1]] +// +struct Outer *foo(int len) { + int * p2 = baz(len); + struct Outer * __single p = bar(len); + p->hdr.len = len; + p->hdr.ptr = p2; + return p; +} + +// CHECK-LABEL: define dso_local ptr @foo2( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @baz(i32 noundef [[LEN]]) #[[ATTR4]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[LEN]] to i64 +// CHECK-NEXT: [[CALL1:%.*]] = tail call ptr @bar(i32 noundef [[LEN]]) #[[ATTR4]] +// CHECK-NEXT: [[MUL:%.*]] = shl nsw i64 [[IDX_EXT]], 2, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[LEN]], -5 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[CALL1]], i64 [[MUL]] +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr i8, ptr [[TMP0]], i64 16 +// CHECK-NEXT: [[CMP9_NOT:%.*]] = icmp slt i32 [[LEN]], 0, !annotation [[META4]] +// CHECK-NEXT: br i1 [[CMP9_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META4]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK_NOT:%.*]] = icmp eq ptr [[CALL1]], null, !annotation [[META6]] +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK_NOT]], label %[[CONT69:.*]], label %[[FLEX_BASE_NONNULL:.*]], !annotation [[META6]] +// CHECK: [[FLEX_BASE_NONNULL]]: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[CALL1]], i64 16 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[CALL1]], [[TMP1]], !annotation [[META7]] +// CHECK-NEXT: [[DOTNOT106:%.*]] = icmp ugt ptr [[TMP1]], [[ADD_PTR3]], !annotation [[META8]] +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT106]], !annotation [[META8]] +// CHECK-NEXT: br i1 [[OR_COND]], label %[[TRAP]], label %[[CONT60:.*]], !prof [[PROF9]], !annotation [[META7]] +// CHECK: [[CONT60]]: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[ADD_PTR3]] to i64, !annotation [[META10]] +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[TMP1]] to i64, !annotation [[META10]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], !annotation [[META10]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = ashr exact i64 [[FLEX_AVAIL_COUNT]], 2, !annotation [[META10]] +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext nneg i32 [[LEN]] to i64, !annotation [[META10]] +// CHECK-NEXT: [[FLEX_COUNT_CHECK_NOT:%.*]] = icmp uge i64 [[FLEX_AVAIL_COUNT_DIV]], [[FLEX_COUNT_INTPTR]], !annotation [[META10]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[CALL1]], [[ADD_PTR3]], !annotation [[META8]] +// CHECK-NEXT: [[OR_COND108:%.*]] = select i1 [[FLEX_COUNT_CHECK_NOT]], i1 [[TMP2]], i1 false, !annotation [[META6]] +// CHECK-NEXT: br i1 [[OR_COND108]], label %[[CONT69]], label %[[TRAP]], !prof [[PROF11]], !annotation [[META10]] +// CHECK: [[CONT69]]: +// CHECK-NEXT: store ptr [[CALL]], ptr [[CALL1]], align 8, !tbaa [[TBAA19]] +// CHECK-NEXT: [[LEN78:%.*]] = getelementptr inbounds nuw i8, ptr [[CALL1]], i64 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN78]], align 8, !tbaa [[TBAA12]] +// CHECK-NEXT: ret ptr [[CALL1]] +// +struct Outer *foo2(int len) { + int * p2 = baz(len); + struct Outer * __single p = bar(len); + p->hdr.ptr = p2; + p->hdr.len = len; + return p; +} +//. +// CHECK: [[META2]] = !{[[META3:![0-9]+]]} +// CHECK: [[META3]] = !{!"bounds-safety-missed-optimization-nuw", !"Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[META4]] = !{!"bounds-safety-generic"} +// CHECK: [[META5]] = !{!"bounds-safety-generic", !"bounds-safety-check-one-past-end-overflow", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound", !"bounds-safety-check-count-negative", !"bounds-safety-check-ptr-le-upper-bound", !"bounds-safety-check-flexible-count-gt-bounds"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META7]] = !{!"bounds-safety-check-one-past-end-overflow"} +// CHECK: [[META8]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[PROF9]] = !{!"branch_weights", i32 12286, i32 -12288} +// CHECK: [[META10]] = !{!"bounds-safety-check-flexible-count-gt-bounds"} +// CHECK: [[PROF11]] = !{!"branch_weights", i32 -8193, i32 8190} +// CHECK: [[TBAA12]] = !{[[META13:![0-9]+]], [[META18:![0-9]+]], i64 8} +// CHECK: [[META13]] = !{!"Inner", [[META14:![0-9]+]], i64 0, [[META18]], i64 8} +// CHECK: [[META14]] = !{!"p1 int", [[META15:![0-9]+]], i64 0} +// CHECK: [[META15]] = !{!"any pointer", [[META16:![0-9]+]], i64 0} +// CHECK: [[META16]] = !{!"omnipotent char", [[META17:![0-9]+]], i64 0} +// CHECK: [[META17]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META18]] = !{!"int", [[META16]], i64 0} +// CHECK: [[TBAA19]] = !{[[META13]], [[META14]], i64 0} +//. diff --git a/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-trivial-O2.c new file mode 100644 index 0000000000000..b8b8e482c1db8 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/flexible-array-member-shared-decls-trivial-O2.c @@ -0,0 +1,142 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -Wno-incompatible-pointer-types -emit-llvm %s -o - | FileCheck %s +// RN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static char a[42]; +static char b[42]; + +struct Inner { + char * __counted_by(len) ptr; + int len; +}; +struct Outer { + struct Inner hdr; + char fam[__counted_by(hdr.len - sizeof(struct Inner))]; +}; + +// CHECK-LABEL: define dso_local noundef nonnull ptr @good( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr @b, ptr @a, align 16, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: store i32 42, ptr getelementptr inbounds nuw (i8, ptr @a, i64 8), align 8, !tbaa [[TBAA8:![0-9]+]] +// CHECK-NEXT: ret ptr @a +// +struct Outer * good(void) { + struct Outer * p = a; + p->hdr.ptr = b; + p->hdr.len = 42; + return p; +} + +// CHECK-LABEL: define dso_local noundef nonnull ptr @good_recursive( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr @a, ptr @a, align 16, !tbaa [[TBAA2]] +// CHECK-NEXT: store i32 42, ptr getelementptr inbounds nuw (i8, ptr @a, i64 8), align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: ret ptr @a +// +struct Outer * good_recursive(void) { + struct Outer * p = a; + p->hdr.ptr = p; + p->hdr.len = 42; + return p; +} + +// CHECK-LABEL: define dso_local noundef nonnull ptr @good_margin( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr @b, ptr @a, align 16, !tbaa [[TBAA2]] +// CHECK-NEXT: store i32 20, ptr getelementptr inbounds nuw (i8, ptr @a, i64 8), align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: ret ptr @a +// +struct Outer * good_margin(void) { + struct Outer * p = a; + p->hdr.ptr = b; + p->hdr.len = 20; + return p; +} + +// CHECK-LABEL: define dso_local noundef nonnull ptr @good_no_fam( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr @b, ptr @a, align 16, !tbaa [[TBAA2]] +// CHECK-NEXT: store i32 16, ptr getelementptr inbounds nuw (i8, ptr @a, i64 8), align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: ret ptr @a +// +struct Outer * good_no_fam(void) { + struct Outer * p = a; + p->hdr.ptr = b; + p->hdr.len = 16; + return p; +} + +// CHECK-LABEL: define dso_local noalias noundef nonnull ptr @bad( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META9:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// +struct Outer * bad(void) { + int len = 43; + struct Outer * p = a; + p->hdr.ptr = b; + p->hdr.len = len; + return p; +} + +// CHECK-LABEL: define dso_local noalias noundef nonnull ptr @bad_neg_count( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr @b, ptr @a, align 16, !tbaa [[TBAA2]] +// CHECK-NEXT: store i32 15, ptr getelementptr inbounds nuw (i8, ptr @a, i64 8), align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META9]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// +struct Outer * bad_neg_count(void) { + struct Outer * p = a; + p->hdr.ptr = b; + p->hdr.len = 15; // fam has __counted_by(hdr.len - sizeof(struct Inner)), where sizeof(struct Inner) is 16 + return p; +} + +// CHECK-LABEL: define dso_local noalias noundef nonnull ptr @bad_ptr( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META9]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// +struct Outer * bad_ptr(void) { + struct Outer * p = a; + p->hdr.ptr = b + 1; + p->hdr.len = 42; + return p; +} + +// CHECK-LABEL: define dso_local noalias noundef nonnull ptr @bad_fam( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR2]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: store ptr @b, ptr getelementptr inbounds nuw (i8, ptr @a, i64 1), align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: store i32 42, ptr getelementptr inbounds nuw (i8, ptr @a, i64 9), align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META9]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// +struct Outer * bad_fam(void) { + struct Outer * p = a + 1; + p->hdr.ptr = b; + p->hdr.len = 42; + return p; +} +//. +// CHECK: [[TBAA2]] = !{[[META3:![0-9]+]], [[META4:![0-9]+]], i64 0} +// CHECK: [[META3]] = !{!"Inner", [[META4]], i64 0, [[META7:![0-9]+]], i64 8} +// CHECK: [[META4]] = !{!"p1 omnipotent char", [[META41:!.+]], i64 0} +// CHECK: [[META41]] = !{!"any pointer", [[META5:![0-9]+]], i64 0} +// CHECK: [[META5]] = !{!"omnipotent char", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[META7]] = !{!"int", [[META5]], i64 0} +// CHECK: [[TBAA8]] = !{[[META3]], [[META7]], i64 8} +// CHECK: [[META9]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound", !"bounds-safety-check-one-past-end-overflow", !"bounds-safety-check-ptr-le-upper-bound", !"bounds-safety-check-flexible-count-gt-bounds"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/function-decl-and-def-with-dynamic-count-pointer-param.c b/clang/test/BoundsSafety/CodeGen/function-decl-and-def-with-dynamic-count-pointer-param.c new file mode 100644 index 0000000000000..f37c2dcaddcdd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/function-decl-and-def-with-dynamic-count-pointer-param.c @@ -0,0 +1,15 @@ + +// This is a test to see if the compiler doesn't crash. + +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-int-conversion %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -Wno-int-conversion %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-int-conversion -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -Wno-int-conversion -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null + +#define a(b, ...) __builtin___memmove_chk(b, __VA_ARGS__, b) +#define c(d, h, e) a(d, h, e) +#define f(g) __attribute__((counted_by(g))) +int n; +void *j; +void l(unsigned char *f(*i), unsigned long *i); +void l(unsigned char *f(*i) k, unsigned long *i) { c(k, (char*)j, n); } diff --git a/clang/test/BoundsSafety/CodeGen/get-bound-void-ptr-bitcast-constfold-O2.c b/clang/test/BoundsSafety/CodeGen/get-bound-void-ptr-bitcast-constfold-O2.c new file mode 100644 index 0000000000000..837000e579f3e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/get-bound-void-ptr-bitcast-constfold-O2.c @@ -0,0 +1,94 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @check_upper_bound( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 +// +int check_upper_bound(void) { + int arr[10]; + void *p = arr; + if ((char *)__ptr_upper_bound((void *)arr) - (char *)arr == 40) + return 0; + return -1; +} + +// CHECK-LABEL: @check_forged_upper_bound( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 +// +int check_forged_upper_bound(void) { + int arr[10]; + void *p = arr; + if ((char *)__ptr_upper_bound(__unsafe_forge_bidi_indexable(int *, arr, 99)) - (char *)arr == 99) + return 0; + return -1; +} + +// CHECK-LABEL: @check_forged_upper_bound_void( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 +// +int check_forged_upper_bound_void(void) { + int arr[10]; + void *p = arr; + if ((char *)__ptr_upper_bound(__unsafe_forge_bidi_indexable(void *, arr, 99)) - (char *)arr == 99) + return 0; + return -1; +} + +void get_sized_by(void *__sized_by(40) buf); + +// CHECK-LABEL: @int_array_to_void_good( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: call void @get_sized_by(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void int_array_to_void_good(void) { + int arr[10]; + get_sized_by(arr); +} + +// CHECK-LABEL: @int_array_to_void_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void int_array_to_void_trap(void) { + int arr[9]; + int *p = arr; + get_sized_by(p); +} + +void get_counted_by(int *__counted_by(10) buf); + +// CHECK-LABEL: @int_array_to_int_good( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @get_counted_by(ptr noundef nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void int_array_to_int_good(void) { + int arr[10]; + get_counted_by(arr); +} + +// CHECK-LABEL: @int_array_to_int_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void int_array_to_int_trap(void) { + int arr[9]; + int *p = arr; + get_counted_by(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/get-bound-void-ptr-bitcast-constfold.c b/clang/test/BoundsSafety/CodeGen/get-bound-void-ptr-bitcast-constfold.c new file mode 100644 index 0000000000000..7cd3d18f812de --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/get-bound-void-ptr-bitcast-constfold.c @@ -0,0 +1,644 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @check_upper_bound( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: ret i32 0 +// +int check_upper_bound(void) { + int arr[10]; + void *p = arr; + if ((char *)__ptr_upper_bound((void *)arr) - (char *)arr == 40) + return 0; + return -1; +} + +// CHECK-LABEL: @check_forged_upper_bound( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[ARRAYDECAY5:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER6:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY5]], i64 10 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY5]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER6]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY5]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR8]], i64 99 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR8]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR8]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR10:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR9]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB12:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR10]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB12]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB14]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB16]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR18:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB20:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB22:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR21]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR18]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB20]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB22]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR24:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR23]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB26:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB28:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR27]], align 8 +// CHECK-NEXT: [[ARRAYDECAY31:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER32:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY31]], i64 10 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP30]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY31]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP30]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER32]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP30]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY31]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP30]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP30]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP30]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB36]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB38]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP29]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR24]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR40]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[SUB_PTR_SUB]], 99 +// CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK: if.then: +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-NEXT: br label [[RETURN:%.*]] +// CHECK: if.end: +// CHECK-NEXT: store i32 -1, ptr [[RETVAL]], align 4 +// CHECK-NEXT: br label [[RETURN]] +// CHECK: return: +// CHECK-NEXT: [[TMP26:%.*]] = load i32, ptr [[RETVAL]], align 4 +// CHECK-NEXT: ret i32 [[TMP26]] +// +int check_forged_upper_bound(void) { + int arr[10]; + void *p = arr; + if ((char *)__ptr_upper_bound(__unsafe_forge_bidi_indexable(int *, arr, 99)) - (char *)arr == 99) + return 0; + return -1; +} + +// CHECK-LABEL: @check_forged_upper_bound_void( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP23:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[ARRAYDECAY4:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY4]], i64 10 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY4]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY4]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR7]], i64 99 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR7]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB9]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR11]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB13]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB15]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8 +// CHECK-NEXT: [[ARRAYDECAY24:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER25:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY24]], i64 10 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY24]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER25]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY24]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP23]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP22]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR27]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP22]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB29]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP22]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB31]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP22]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR33:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR32]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP22]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB35:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP22]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB37:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR36]], align 8 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR17]] to i64 +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR33]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[SUB_PTR_SUB]], 99 +// CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK: if.then: +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-NEXT: br label [[RETURN:%.*]] +// CHECK: if.end: +// CHECK-NEXT: store i32 -1, ptr [[RETVAL]], align 4 +// CHECK-NEXT: br label [[RETURN]] +// CHECK: return: +// CHECK-NEXT: [[TMP23:%.*]] = load i32, ptr [[RETVAL]], align 4 +// CHECK-NEXT: ret i32 [[TMP23]] +// +int check_forged_upper_bound_void(void) { + int arr[10]; + void *p = arr; + if ((char *)__ptr_upper_bound(__unsafe_forge_bidi_indexable(void *, arr, 99)) - (char *)arr == 99) + return 0; + return -1; +} + +void get_sized_by(void *__sized_by(40) buf); + +// CHECK-LABEL: @int_array_to_void_good( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// CHECK-NEXT: call void @get_sized_by(ptr noundef [[WIDE_PTR_PTR4]]) +// CHECK-NEXT: ret void +// +void int_array_to_void_good(void) { + int arr[10]; + get_sized_by(arr); +} + +// CHECK-LABEL: @int_array_to_void_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [9 x i32], align 16 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP68:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [9 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 9 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB11]], ptr [[TMP6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR4]], [[WIDE_PTR_PTR13]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END67:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB20]], ptr [[TMP7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP34:%.*]] = icmp ule ptr [[WIDE_PTR_PTR22]], [[WIDE_PTR_PTR29]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP34]], label [[LAND_RHS:%.*]], label [[LAND_END67]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB38]], ptr [[TMP8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[TMP9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB44]], ptr [[TMP11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB56]], ptr [[TMP13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB58]], ptr [[TMP14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR60]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP65:%.*]] = icmp sle i64 40, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP65]], label [[LAND_RHS66:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs66: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS66]] ] +// CHECK-NEXT: br label [[LAND_END67]], {{!annotation ![0-9]+}} +// CHECK: land.end67: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP15]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP68]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP68]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP68]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP68]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK-NEXT: call void @get_sized_by(ptr noundef [[WIDE_PTR_PTR70]]) +// CHECK-NEXT: ret void +// +void int_array_to_void_trap(void) { + int arr[9]; + int *p = arr; + get_sized_by(p); +} + +void get_counted_by(int *__counted_by(10) buf); + +// CHECK-LABEL: @int_array_to_int_good( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @get_counted_by(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void int_array_to_int_good(void) { + int arr[10]; + get_counted_by(arr); +} + +// CHECK-LABEL: @int_array_to_int_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [9 x i32], align 16 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [9 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 9 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[P]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END46:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP4]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END46]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP37]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP44:%.*]] = icmp sle i64 10, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP44]], label [[LAND_RHS45:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs45: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS45]] ] +// CHECK-NEXT: br label [[LAND_END46]], {{!annotation ![0-9]+}} +// CHECK: land.end46: +// CHECK-NEXT: [[TMP7:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP6]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: call void @get_counted_by(ptr noundef [[WIDE_PTR_PTR49]]) +// CHECK-NEXT: ret void +// +void int_array_to_int_trap(void) { + int arr[9]; + int *p = arr; + get_counted_by(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/global-indexable.c b/clang/test/BoundsSafety/CodeGen/global-indexable.c new file mode 100644 index 0000000000000..1951d731c7d94 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/global-indexable.c @@ -0,0 +1,28 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -fbounds-safety %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o /dev/null +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +struct SArray { + int arr[10]; +}; + +struct MockWidePtr { + struct SArray *ptr; + struct SArray *ub; + struct SArray *lb; +}; + +// CHECK: @g_mock_wide_ptr = {{.+}} global {{.*}} zeroinitializer, align 8 +struct MockWidePtr g_mock_wide_ptr; +// CHECK: @g_wide_ptr_bidi_indexable = {{.+}} global {{.*}} zeroinitializer, align 8 +int *__bidi_indexable g_wide_ptr_bidi_indexable; +// CHECK: @g_wide_ptr_indexable = {{.+}} global {{.*}} zeroinitializer, align 8 +int *__indexable g_wide_ptr_indexable; +// CHECK: @g_single_ptr = {{.+}} global ptr null, align 8 +int *__single g_single_ptr; +// CHECK: @g_unsafe_indexable_ptr = {{.+}} global ptr null, align 8 +int *__unsafe_indexable g_unsafe_indexable_ptr; diff --git a/clang/test/BoundsSafety/CodeGen/global-init-with-array-elem.c b/clang/test/BoundsSafety/CodeGen/global-init-with-array-elem.c new file mode 100644 index 0000000000000..284dac8e14c40 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/global-init-with-array-elem.c @@ -0,0 +1,14 @@ +// REQUIRES: x86-registered-target + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +int array[100]; +int* __bidi_indexable ptr = &array[50]; + +// CHECK: _ptr: +// CHECK: .quad _array+200 +// CHECK: .quad _array+400 +// CHECK: .quad _array diff --git a/clang/test/BoundsSafety/CodeGen/global-init-with-array.c b/clang/test/BoundsSafety/CodeGen/global-init-with-array.c new file mode 100644 index 0000000000000..d8f0fdd871b15 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/global-init-with-array.c @@ -0,0 +1,14 @@ +// REQUIRES: x86-registered-target + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +int array[100][100]; +int* __bidi_indexable ptr = array; + +// CHECK: _ptr: +// CHECK: .quad _array +// CHECK: .quad _array+40000 +// CHECK: .quad _array diff --git a/clang/test/BoundsSafety/CodeGen/ignore-init-list-non-zero-count.c b/clang/test/BoundsSafety/CodeGen/ignore-init-list-non-zero-count.c new file mode 100644 index 0000000000000..6ca728f4182b2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ignore-init-list-non-zero-count.c @@ -0,0 +1,51 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-bounds-safety-init-list-null-non-zero-count -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-bounds-safety-init-list-null-non-zero-count -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck %s + +#include + +char array[256]; +char *__counted_by(256) p = array; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @p, align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 256 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ult ptr [[TMP5]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[TMP5]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP8:%.*]] = load i8, ptr [[TMP5]], align 1 +// CHECK-NEXT: ret i8 [[TMP8]] +// +char foo(int i) { + return p[i]; +} diff --git a/clang/test/BoundsSafety/CodeGen/incomplete-array-counted.c b/clang/test/BoundsSafety/CodeGen/incomplete-array-counted.c new file mode 100644 index 0000000000000..4eb3bf102e5df --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/incomplete-array-counted.c @@ -0,0 +1,50 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null +#include + +extern unsigned externArray[__counted_by(10)]; + +void bar(const unsigned *pointer); + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr @externArray, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr getelementptr inbounds (i32, ptr @externArray, i64 10), ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr @externArray, ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: call void @bar(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo(void){ + bar(externArray); +} + + diff --git a/clang/test/BoundsSafety/CodeGen/incomplete-array.c b/clang/test/BoundsSafety/CodeGen/incomplete-array.c new file mode 100644 index 0000000000000..d5a31c51d100b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/incomplete-array.c @@ -0,0 +1,53 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety -emit-llvm -Werror -verify=werr %s -o /dev/null +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety -emit-llvm %s -Wno-bounds-safety-incomplete-array -verify=wno -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety -emit-llvm %s -verify=default -o /dev/null +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -Werror -verify=werr %s -o /dev/null +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -Wno-bounds-safety-incomplete-array -verify=wno -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -verify=default -o /dev/null + +// wno-no-diagnostics +extern unsigned externArray[]; + +void bar(const unsigned *pointer); + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr @externArray, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr @externArray, ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr @externArray, ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: call void @bar(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo(void){ + // default-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} + bar(externArray); // werr-error{{accessing elements of an unannotated incomplete array always fails at runtime}} +} + + diff --git a/clang/test/BoundsSafety/CodeGen/incomplete-single-to-indexable.c b/clang/test/BoundsSafety/CodeGen/incomplete-single-to-indexable.c new file mode 100644 index 0000000000000..446eefec438ff --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/incomplete-single-to-indexable.c @@ -0,0 +1,33 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +#define NULL ((void *__single)0) + +// CHECK-LABEL: @test_null_to_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IMPL_BIDI_PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[IMPL_BIDI_PTR2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[IMPL_BIDI_PTR]], i8 0, i64 24, i1 false) +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[IMPL_BIDI_PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[IMPL_BIDI_PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr getelementptr inbounds (i32, ptr null, i64 1), ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[IMPL_BIDI_PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[IMPL_BIDI_PTR2]], i8 0, i64 24, i1 false) +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[IMPL_BIDI_PTR2]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[IMPL_BIDI_PTR2]], i32 0, i32 1 +// CHECK-NEXT: store ptr getelementptr inbounds (i32, ptr null, i64 1), ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[IMPL_BIDI_PTR2]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP5]], align 8 +// CHECK-NEXT: ret void +// +void test_null_to_bidi() { + int *impl_bidi_ptr = (int *__bidi_indexable)NULL; + int *impl_bidi_ptr2 = (void *)(char *)NULL; +} diff --git a/clang/test/BoundsSafety/CodeGen/index-enum-signed.c b/clang/test/BoundsSafety/CodeGen/index-enum-signed.c new file mode 100644 index 0000000000000..525215dce2688 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/index-enum-signed.c @@ -0,0 +1,53 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -fshort-enums -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fshort-enums -emit-llvm %s -o - | FileCheck %s + +#include + +enum bar { + bar_val = -1 +}; + +// CHECK-LABEL: @baz( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[B:%.*]] = alloca i8, align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[TMP0]] to i8 +// CHECK-NEXT: store i8 [[CONV]], ptr [[B]], align 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr [[B]], align 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i8 [[TMP1]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[TMP2]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[TMP2]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP2]], align 4 +// CHECK-NEXT: ret i32 [[TMP5]] +// +int baz(int i) { + int *__bidi_indexable ptr; + enum bar b = i; + return ptr[b]; +} + diff --git a/clang/test/BoundsSafety/CodeGen/index-enum-unsigned.c b/clang/test/BoundsSafety/CodeGen/index-enum-unsigned.c new file mode 100644 index 0000000000000..ff287ee7f770f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/index-enum-unsigned.c @@ -0,0 +1,52 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -fshort-enums -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fshort-enums -emit-llvm %s -o - | FileCheck %s + +#include + +enum bar { + bar_val = 1 +}; + +// CHECK-LABEL: @baz( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[B:%.*]] = alloca i8, align 1 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[CONV:%.*]] = trunc i32 [[TMP0]] to i8 +// CHECK-NEXT: store i8 [[CONV]], ptr [[B]], align 1 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr [[B]], align 1 +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i8 [[TMP1]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[TMP2]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[TMP2]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP2]], align 4 +// CHECK-NEXT: ret i32 [[TMP5]] +// +int baz(int i) { + int *__bidi_indexable ptr; + enum bar b = i; + return ptr[b]; +} diff --git a/clang/test/BoundsSafety/CodeGen/index-signed.c b/clang/test/BoundsSafety/CodeGen/index-signed.c new file mode 100644 index 0000000000000..c880484be0d84 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/index-signed.c @@ -0,0 +1,49 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[SIGNED_IDX:%.*]] = alloca i16, align 2 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[SIGNED_IDX]], align 2 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i16 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[TMP1]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[TMP1]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 0 +// +int main() { + int *__bidi_indexable ptr; + + short signed_idx; + ptr[signed_idx]; + return 0; +} + + diff --git a/clang/test/BoundsSafety/CodeGen/index-unsigned.c b/clang/test/BoundsSafety/CodeGen/index-unsigned.c new file mode 100644 index 0000000000000..59280312f55a8 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/index-unsigned.c @@ -0,0 +1,46 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[UNSIGNED_IDX:%.*]] = alloca i16, align 2 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR]], i8 0, i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: store i16 5, ptr [[UNSIGNED_IDX]], align 2 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[UNSIGNED_IDX]], align 2 +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i16 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[TMP1]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[TMP1]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 0 +// +int main() { + int *__bidi_indexable ptr; + + unsigned short unsigned_idx = 5; + ptr[unsigned_idx]; +} + diff --git a/clang/test/BoundsSafety/CodeGen/indexable-argument-abi.c b/clang/test/BoundsSafety/CodeGen/indexable-argument-abi.c new file mode 100644 index 0000000000000..084cff2f196b7 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-argument-abi.c @@ -0,0 +1,48 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 +// RUN: %clang_cc1 -triple aarch64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=AARCH64 +// RUN: %clang_cc1 -triple i686 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=I686 +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 +// RUN: %clang_cc1 -triple aarch64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=AARCH64 +// RUN: %clang_cc1 -triple i686 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=I686 + +#include + +int*__indexable bar(int *__indexable ptrArg) { + return ptrArg; +} + +// X86_64-LABEL: @bar( +// X86_64: entry: +// X86_64: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// X86_64: [[PTRARG:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// X86_64: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTRARG]], i32 0, i32 0 +// X86_64: store ptr [[PTRARG_COERCE0:%.*]], ptr [[TMP0]], align 8 +// X86_64: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTRARG]], i32 0, i32 1 +// X86_64: store ptr [[PTRARG_COERCE1:%.*]], ptr [[TMP1]], align 8 +// X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[PTRARG]], i64 16, i1 false) +// X86_64: [[TMP2:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// X86_64: ret { ptr, ptr } [[TMP2]] +// +// AARCH64-LABEL: @bar( +// AARCH64: entry: +// AARCH64: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// AARCH64: [[PTRARG:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// AARCH64: store [2 x i64] [[PTRARG_COERCE:%.*]], ptr [[PTRARG]], align 8 +// AARCH64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[PTRARG]], i64 16, i1 false) +// AARCH64: [[TMP0:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// AARCH64: ret [2 x i64] [[TMP0]] +// +// I686-LABEL: @bar( +// I686: entry: +// I686: [[RESULT_PTR:%.*]] = alloca ptr, align 4 +// I686: store ptr [[AGG_RESULT:%.*]], ptr [[RESULT_PTR]], align 4 +// I686: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AGG_RESULT]], ptr align 4 [[PTRARG:%.*]], i32 8, i1 false) +// I686: ret void +// + + + + diff --git a/clang/test/BoundsSafety/CodeGen/indexable-arithmetic.c b/clang/test/BoundsSafety/CodeGen/indexable-arithmetic.c new file mode 100644 index 0000000000000..cf5b8cb7efc65 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-arithmetic.c @@ -0,0 +1,51 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP1]], i64 4 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP10:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP11:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int *foo(int *__bidi_indexable ptr) { + return &ptr[4]; +} diff --git a/clang/test/BoundsSafety/CodeGen/indexable-array-subscript-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/indexable-array-subscript-trivial-O2.c new file mode 100644 index 0000000000000..df72ed98d8acd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-array-subscript-trivial-O2.c @@ -0,0 +1,106 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static struct { + int _a[10]; + int array[3]; + int _b[10]; +} foo; + +// CHECK-LABEL: @good_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_one(void) { + int *__indexable p = foo.array; + int index = 1; + (void)&p[index]; +} + +// CHECK-LABEL: @good_one_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_one_assign(void) { + int *__indexable p = foo.array; + int index = 1; + p = &p[index]; +} + +// CHECK-LABEL: @good_one_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_one_deref(void) { + int *__indexable p = foo.array; + int index = 1; + (void)p[index]; +} + +// CHECK-LABEL: @good_neg_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_neg_one(void) { + int *__indexable p = foo.array; + int index = -1; + (void)&p[index]; +} + +// CHECK-LABEL: @bad_neg_one_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_neg_one_assign(void) { + int *__indexable p = foo.array; + int index = -1; + p = &p[index]; +} + +// CHECK-LABEL: @bad_neg_one_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_neg_one_deref(void) { + int *__indexable p = foo.array; + int index = -1; + (void)p[index]; +} + +// CHECK-LABEL: @good_oob( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_oob(void) { + int *__indexable p = foo.array; + int index = 10; + (void)&p[index]; +} + +// CHECK-LABEL: @good_oob_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_oob_assign(void) { + int *__indexable p = foo.array; + int index = 10; + p = &p[index]; +} + +// CHECK-LABEL: @bad_oob_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_oob_deref(void) { + int *__indexable p = foo.array; + int index = 10; + (void)p[index]; +} diff --git a/clang/test/BoundsSafety/CodeGen/indexable-array-subscript.c b/clang/test/BoundsSafety/CodeGen/indexable-array-subscript.c new file mode 100644 index 0000000000000..8282caf36dd67 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-array-subscript.c @@ -0,0 +1,139 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +#include + +// CHECK-O0-LABEL: @ind( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TMP16]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-O0-NEXT: ret { ptr, ptr } [[TMP18]] +// +// CHECK-O2-LABEL: @ind( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDXPROM]] +// CHECK-O2-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[P_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK-O2: trap: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: cont: +// CHECK-O2-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[BOUND_PTR_ARITH]], 0 +// CHECK-O2-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[P_COERCE1:%.*]], 1 +// CHECK-O2-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable ind(int *__indexable p, int index) { + return &p[index]; +} + +// CHECK-O0-LABEL: @bidi( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: ret void +// +// CHECK-O2-LABEL: @bidi( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDXPROM]] +// CHECK-O2-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[AGG_RESULT:%.*]], align 8 +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_RESULT]], i64 8 +// CHECK-O2-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP0]], align 8 +// CHECK-O2-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_RESULT]], i64 16 +// CHECK-O2-NEXT: store ptr [[P_COERCE0]], ptr [[TMP1]], align 8 +// CHECK-O2-NEXT: ret void +// +int *__bidi_indexable bidi(int *__indexable p, int index) { + return &p[index]; +} diff --git a/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-O0.c b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-O0.c new file mode 100644 index 0000000000000..5c978129fb0a4 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-O0.c @@ -0,0 +1,47 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH_OLD:%.*]] = ptrtoint ptr [[TMP4]] to i64 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[P]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[P]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH_NEW:%.*]] = ptrtoint ptr [[BOUND_PTR_ARITH]] to i64 +// CHECK-NEXT: [[TMP9:%.*]] = icmp uge i64 [[BOUND_PTR_ARITH_NEW]], [[BOUND_PTR_ARITH_OLD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-NEXT: [[TMP10:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP10]] +// +int *__indexable test(int *__indexable p, int index) { + p += index; + return p; +} + diff --git a/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-O2.c b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-O2.c new file mode 100644 index 0000000000000..e39e66e1a0027 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-O2.c @@ -0,0 +1,25 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDXPROM]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[P_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[BOUND_PTR_ARITH]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[P_COERCE1:%.*]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable test(int *__indexable p, int index) { + p += index; + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-trivial-O2.c new file mode 100644 index 0000000000000..f20af90ee0bd7 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-assign-trivial-O2.c @@ -0,0 +1,82 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static struct { + int _a[10]; + int array[3]; + int _b[10]; +} foo; + +// CHECK-LABEL: @good_add_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr getelementptr inbounds nuw (i8, ptr @foo, i64 44), ptr getelementptr inbounds (i32, ptr getelementptr inbounds nuw ([[STRUCT_ANON:%.*]], ptr @foo, i32 0, i32 1), i64 3) } +// +int *__indexable good_add_one(void) { + int *__indexable p = foo.array; + int index = 1; + p += index; + return p; +} + +// CHECK-LABEL: @good_sub_neg_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr getelementptr inbounds nuw (i8, ptr @foo, i64 44), ptr getelementptr inbounds (i32, ptr getelementptr inbounds nuw ([[STRUCT_ANON:%.*]], ptr @foo, i32 0, i32 1), i64 3) } +// +int *__indexable good_sub_neg_one(void) { + int *__indexable p = foo.array; + int index = -1; + p -= index; + return p; +} + +// CHECK-LABEL: @good_oob( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr getelementptr inbounds nuw (i8, ptr @foo, i64 80), ptr getelementptr inbounds (i32, ptr getelementptr inbounds nuw ([[STRUCT_ANON:%.*]], ptr @foo, i32 0, i32 1), i64 3) } +// +int *__indexable good_oob(void) { + int *__indexable p = foo.array; + int index = 10; + p += index; + return p; +} + +// CHECK-LABEL: @bad_sub_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *__indexable bad_sub_one(void) { + int *__indexable p = foo.array; + int index = 1; + p -= index; + return p; +} + +// CHECK-LABEL: @bad_add_neg_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *__indexable bad_add_neg_one(void) { + int *__indexable p = foo.array; + int index = -1; + p += index; + return p; +} + +// CHECK-LABEL: @bad_add_one_sub_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *__indexable bad_add_one_sub_one(void) { + int *__indexable p = foo.array; + int index = 1; + p += index; + p -= index; + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-trivial-O2.c new file mode 100644 index 0000000000000..e0e05973e6c9b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith-trivial-O2.c @@ -0,0 +1,168 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static struct { + int _a[10]; + int array[3]; + int _b[10]; +} foo; + +// CHECK-LABEL: @good_add_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_add_one(void) { + int *__indexable p = foo.array; + int index = 1; + (void)(p + index); +} + +// CHECK-LABEL: @good_add_one_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_add_one_assign(void) { + int *__indexable p = foo.array; + int index = 1; + p = p + index; +} + +// CHECK-LABEL: @good_add_one_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_add_one_deref(void) { + int *__indexable p = foo.array; + int index = 1; + (void)*(p + index); +} + +// CHECK-LABEL: @good_add_neg_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_add_neg_one(void) { + int *__indexable p = foo.array; + int index = -1; + (void)(p + index); +} + +// CHECK-LABEL: @bad_add_neg_one_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_add_neg_one_assign(void) { + int *__indexable p = foo.array; + int index = -1; + p = p + index; +} + +// CHECK-LABEL: @bad_add_neg_one_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_add_neg_one_deref(void) { + int *__indexable p = foo.array; + int index = -1; + (void)*(p + index); +} + +// CHECK-LABEL: @good_sub_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_sub_one(void) { + int *__indexable p = foo.array; + int index = 1; + (void)(p - index); +} + +// CHECK-LABEL: @bad_sub_one_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_sub_one_assign(void) { + int *__indexable p = foo.array; + int index = 1; + p = p - index; +} + +// CHECK-LABEL: @bad_sub_one_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_sub_one_deref(void) { + int *__indexable p = foo.array; + int index = 1; + (void)*(p - index); +} + +// CHECK-LABEL: @good_sub_neg_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_sub_neg_one(void) { + int *__indexable p = foo.array; + int index = -1; + (void)(p - index); +} + +// CHECK-LABEL: @good_sub_neg_one_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_sub_neg_one_assign(void) { + int *__indexable p = foo.array; + int index = -1; + p = p - index; +} + +// CHECK-LABEL: @good_sub_neg_one_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_sub_neg_one_deref(void) { + int *__indexable p = foo.array; + int index = -1; + (void)*(p - index); +} + +// CHECK-LABEL: @good_add_oob( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_add_oob(void) { + int *__indexable p = foo.array; + int index = 10; + (void)(p + index); +} + +// CHECK-LABEL: @good_add_oob_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_add_oob_assign(void) { + int *__indexable p = foo.array; + int index = 10; + p = p + index; +} + +// CHECK-LABEL: @bad_add_oob_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_add_oob_deref(void) { + int *__indexable p = foo.array; + int index = 10; + (void)*(p + index); +} diff --git a/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith.c b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith.c new file mode 100644 index 0000000000000..e88f1bfd170d1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/indexable-ptr-arith.c @@ -0,0 +1,293 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +#include + +// CHECK-O0-LABEL: @add( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TMP16]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-O0-NEXT: ret { ptr, ptr } [[TMP18]] +// +// CHECK-O2-LABEL: @add( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDXPROM]] +// CHECK-O2-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[P_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK-O2: trap: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: cont: +// CHECK-O2-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[BOUND_PTR_ARITH]], 0 +// CHECK-O2-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[P_COERCE1:%.*]], 1 +// CHECK-O2-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable add(int *__indexable p, int index) { + return p + index; +} + +// CHECK-O0-LABEL: @add2( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TMP16]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-O0-NEXT: ret { ptr, ptr } [[TMP18]] +// +// CHECK-O2-LABEL: @add2( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDXPROM]] +// CHECK-O2-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[P_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK-O2: trap: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: cont: +// CHECK-O2-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[BOUND_PTR_ARITH]], 0 +// CHECK-O2-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[P_COERCE1:%.*]], 1 +// CHECK-O2-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable add2(int *__indexable p, int index) { + return index + p; +} + +// CHECK-O0-LABEL: @sub( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[IDX_NEG:%.*]] = sub i64 0, [[IDXPROM]] +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDX_NEG]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TMP16]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-O0-NEXT: ret { ptr, ptr } [[TMP18]] +// +// CHECK-O2-LABEL: @sub( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-O2-NEXT: [[IDX_NEG:%.*]] = sub nsw i64 0, [[IDXPROM]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDX_NEG]] +// CHECK-O2-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[P_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK-O2: trap: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: cont: +// CHECK-O2-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[BOUND_PTR_ARITH]], 0 +// CHECK-O2-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[P_COERCE1:%.*]], 1 +// CHECK-O2-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable sub(int *__indexable p, int index) { + return p - index; +} + +// CHECK-O0-LABEL: @add_bidi( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[P_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: store i32 [[INDEX:%.*]], ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: ret void +// +// CHECK-O2-LABEL: @add_bidi( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX:%.*]] to i64 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[P_COERCE0:%.*]], i64 [[IDXPROM]] +// CHECK-O2-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[AGG_RESULT:%.*]], align 8 +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_RESULT]], i64 8 +// CHECK-O2-NEXT: store ptr [[P_COERCE1:%.*]], ptr [[TMP0]], align 8 +// CHECK-O2-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_RESULT]], i64 16 +// CHECK-O2-NEXT: store ptr [[P_COERCE0]], ptr [[TMP1]], align 8 +// CHECK-O2-NEXT: ret void +// +int *__bidi_indexable add_bidi(int *__indexable p, int index) { + return index + p; +} diff --git a/clang/test/BoundsSafety/CodeGen/init-global-bidi-indexable-with-null.c b/clang/test/BoundsSafety/CodeGen/init-global-bidi-indexable-with-null.c new file mode 100644 index 0000000000000..91d7073384ebe --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-bidi-indexable-with-null.c @@ -0,0 +1,13 @@ + + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int *__bidi_indexable bidi_ptr = 0; +int *__bidi_indexable bidi_ptr2 = (int *)(void *)0; + +// CHECK: %[[BSS_STRUCT_NAME:.*]] = type { ptr, ptr, ptr } +// CHECK: @bidi_ptr = {{.*}}global %[[BSS_STRUCT_NAME]] zeroinitializer +// CHECK: @bidi_ptr2 = {{.*}}global %[[BSS_STRUCT_NAME]] zeroinitializer diff --git a/clang/test/BoundsSafety/CodeGen/init-global-bidi-indexable.c b/clang/test/BoundsSafety/CodeGen/init-global-bidi-indexable.c new file mode 100644 index 0000000000000..124102956ecba --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-bidi-indexable.c @@ -0,0 +1,15 @@ + + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int array[3] = {42, 43, 44}; + +int* __bidi_indexable bidi_ptr = array; + +// CHECK: %"[[BSS_BIDI_STRUCT:.*]]" = type { ptr, ptr, ptr } + +// CHECK: [[ARRAY:.*]] = +// CHECK: {{.*}} = {{.*}} %"[[BSS_BIDI_STRUCT]]" { ptr @array, ptr inttoptr (i64 add (i64 ptrtoint (ptr [[ARRAY]] to i64), i64 12) to ptr), ptr [[ARRAY]] }, align 8 diff --git a/clang/test/BoundsSafety/CodeGen/init-global-indexable-with-null.c b/clang/test/BoundsSafety/CodeGen/init-global-indexable-with-null.c new file mode 100644 index 0000000000000..5069171d993a2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-indexable-with-null.c @@ -0,0 +1,13 @@ + + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +int* __indexable ptr = 0; +int* __indexable ptr2 = (int *)(void *)0; + +// CHECK: %[[BSS_STRUCT_NAME:.*]] = type { ptr, ptr } +// CHECK: @ptr = {{.*}}global %[[BSS_STRUCT_NAME]] zeroinitializer +// CHECK: @ptr2 = {{.*}}global %[[BSS_STRUCT_NAME]] zeroinitializer diff --git a/clang/test/BoundsSafety/CodeGen/init-global-indexable.c b/clang/test/BoundsSafety/CodeGen/init-global-indexable.c new file mode 100644 index 0000000000000..a7cb26c338553 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-indexable.c @@ -0,0 +1,16 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int array[3] = {42, 43, 44}; + +int* __indexable ptr = array; + +// CHECK: %"[[BSS_IDX_STRUCT:.*]]" = type { ptr, ptr } +// CHECK: [[ARRAY:.*]] = +// CHECK: {{.*}} = {{.*}} %"[[BSS_IDX_STRUCT]]" { ptr @array, ptr inttoptr (i64 add (i64 ptrtoint (ptr [[ARRAY]] to i64), i64 12) to ptr) }, align 8 + diff --git a/clang/test/BoundsSafety/CodeGen/init-global-single-with-null.c b/clang/test/BoundsSafety/CodeGen/init-global-single-with-null.c new file mode 100644 index 0000000000000..467a995f73819 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-single-with-null.c @@ -0,0 +1,14 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int* __single ptr = 0; +int* __single ptr2 = (int *)(void *)0; + +// CHECK: @ptr = {{.*}} ptr null +// CHECK: @ptr2 = {{.*}} ptr null + diff --git a/clang/test/BoundsSafety/CodeGen/init-global-struct-with-global.c b/clang/test/BoundsSafety/CodeGen/init-global-struct-with-global.c new file mode 100644 index 0000000000000..1abf54525a171 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-struct-with-global.c @@ -0,0 +1,24 @@ +// REQUIRES: x86-registered-target + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +struct Foo { + int *__bidi_indexable inner; + int *__indexable inner2; +}; +int globalCh; + +struct Foo global = { + .inner = &globalCh, + .inner2 = &globalCh +}; + +// CHECK: _global: +// CHECK-NEXT: .quad _globalCh +// CHECK-NEXT: .quad _globalCh+4 +// CHECK-NEXT: .quad _globalCh +// CHECK-NEXT: .quad _globalCh +// CHECK-NEXT: .quad _globalCh+4 diff --git a/clang/test/BoundsSafety/CodeGen/init-global-unsafe-forge-const.c b/clang/test/BoundsSafety/CodeGen/init-global-unsafe-forge-const.c new file mode 100644 index 0000000000000..0b34fe707f0d0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-global-unsafe-forge-const.c @@ -0,0 +1,18 @@ + + +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-iphoneos -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct s { int arr[10]; char *cp; }; + +void *g1 = __unsafe_forge_single(void *, 1); +struct s *g2 = __unsafe_forge_single(struct s*, 2); +void *__bidi_indexable g3 = __unsafe_forge_bidi_indexable(void *, 3, 10); +struct s *__bidi_indexable g4 = __unsafe_forge_bidi_indexable(void *, 4, sizeof(struct s)); + +// CHECK: @g1 = global ptr inttoptr (i64 1 to ptr), align 8 +// CHECK: @g2 = global ptr inttoptr (i64 2 to ptr), align 8 +// CHECK: @g3 = global %"__bounds_safety::wide_ptr.bidi_indexable" { ptr inttoptr (i64 3 to ptr), ptr inttoptr (i64 13 to ptr), ptr inttoptr (i64 3 to ptr) }, align 8 +// CHECK: @g4 = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" { ptr inttoptr (i64 4 to ptr), ptr inttoptr (i64 52 to ptr), ptr inttoptr (i64 4 to ptr) }, align 8 diff --git a/clang/test/BoundsSafety/CodeGen/init-local-struct-with-global.c b/clang/test/BoundsSafety/CodeGen/init-local-struct-with-global.c new file mode 100644 index 0000000000000..30918ffd5a438 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-local-struct-with-global.c @@ -0,0 +1,26 @@ +// REQUIRES: x86-registered-target + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +struct Foo { + char *__bidi_indexable inner; +}; +int globalCh; + +void bar(void) { + int localCh; + + struct Foo local = { + .inner = &globalCh + }; +} + +// CHECK: local: +// CHECK: .quad _globalCh +// CHECK: .quad _globalCh+4 +// CHECK: .quad _globalCh + + diff --git a/clang/test/BoundsSafety/CodeGen/init-local-struct-with-local.c b/clang/test/BoundsSafety/CodeGen/init-local-struct-with-local.c new file mode 100644 index 0000000000000..98ab3a68f36b8 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-local-struct-with-local.c @@ -0,0 +1,48 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Foo { + char *__bidi_indexable inner; +}; + +void bar(void) { + int localCh; + + struct Foo local = { + .inner = &localCh + }; +} + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LOCALCH:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[INNER:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[LOCAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[LOCALCH]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[LOCALCH]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[LOCALCH]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[INNER]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[INNER]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[INNER]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP6]], align 8 +// CHECK-NEXT: ret void +// diff --git a/clang/test/BoundsSafety/CodeGen/init-local-struct-with-static-local.c b/clang/test/BoundsSafety/CodeGen/init-local-struct-with-static-local.c new file mode 100644 index 0000000000000..d0c77f3dd696e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-local-struct-with-static-local.c @@ -0,0 +1,48 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Foo { + char *__bidi_indexable inner; +}; + +void bar(void) { + int localCh; + + struct Foo local = { + .inner = &localCh + }; +} + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LOCALCH:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[LOCAL:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[INNER:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[LOCAL]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[LOCALCH]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[LOCALCH]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[LOCALCH]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[INNER]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[INNER]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[INNER]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP6]], align 8 +// CHECK-NEXT: ret void +// \ No newline at end of file diff --git a/clang/test/BoundsSafety/CodeGen/init-struct-const-count-O0.c b/clang/test/BoundsSafety/CodeGen/init-struct-const-count-O0.c new file mode 100644 index 0000000000000..b744ad2bba97b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-struct-const-count-O0.c @@ -0,0 +1,2115 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Checks that are the same between -fbounds-safety-bringup-missing-checks flags +// RUN: %clang_cc1 -O0 -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=SAME %s +// RUN: %clang_cc1 -O0 -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=SAME %s + +// Checks that differ with -fbounds-safety-bringup-missing-checks +// RUN: %clang_cc1 -O0 -DCOMPOUND_LITERAL -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=LEGACY %s +// RUN: %clang_cc1 -O0 -DCOMPOUND_LITERAL -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=NEW %s +#include + + +// ============================================================================= +// __counted_by +// ============================================================================= + +struct cb { + const int count; + int* __counted_by(count) ptr; +}; + +void consume_cb(struct cb); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_cb( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// SAME-NEXT: [[ENTRY:.*]]: +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// SAME-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// SAME-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// SAME-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// SAME-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// SAME-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// SAME-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// SAME-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS27]]: +// SAME-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// SAME-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_END30]]: +// SAME-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// SAME-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// SAME-NEXT: [[PTR31:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[TMP]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// SAME-NEXT: [[TMP9:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_cb([2 x i64] [[TMP9]]) +// SAME-NEXT: ret void +// +void init_list_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// SAME-LABEL: define dso_local void @init_list_cb_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*]]: +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// SAME-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS27]]: +// SAME-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// SAME-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_END30]]: +// SAME-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// SAME-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// SAME-NEXT: [[PTR31:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[PTR]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// SAME-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_cb([2 x i64] [[TMP4]]) +// SAME-NEXT: ret void +// +void init_list_cb_bidi(int count_param, int* __bidi_indexable ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cb( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// LEGACY-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: [[TMP2:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP7:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_cb([2 x i64] [[TMP7]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cb( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0:[0-9]+]] { +// NEW-NEXT: [[ENTRY:.*]]: +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// NEW-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// NEW-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS27]]: +// NEW-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// NEW-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_END30]]: +// NEW-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// NEW-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// NEW-NEXT: [[PTR31:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[TMP]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// NEW-NEXT: [[TMP9:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_cb([2 x i64] [[TMP9]]) +// NEW-NEXT: ret void +// +void compound_literal_init_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cb_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// LEGACY-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_cb([2 x i64] [[TMP2]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cb_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*]]: +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_CB:%.*]], align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// NEW-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS27]]: +// NEW-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// NEW-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_END30]]: +// NEW-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// NEW-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// NEW-NEXT: [[PTR31:%.*]] = getelementptr inbounds nuw [[STRUCT_CB]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_cb([2 x i64] [[TMP4]]) +// NEW-NEXT: ret void +// +void compound_literal_init_cb_bidi(int count_param, int*__bidi_indexable ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +#endif + +// ============================================================================= +// __counted_by_or_null +// ============================================================================= + +struct cbon { + const int count; + int* __counted_by_or_null(count) ptr; +}; + +void consume_cbon(struct cbon); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_cbon( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// SAME-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4:![0-9]+]] +// SAME-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// SAME: [[BOUNDSCHECK_NOTNULL]]: +// SAME-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// SAME-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// SAME-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// SAME-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// SAME-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// SAME-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// SAME-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// SAME: [[BOUNDSCHECK_NULL]]: +// SAME-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// SAME-NEXT: store ptr null, ptr [[TMP7]], align 8 +// SAME-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// SAME-NEXT: store ptr null, ptr [[TMP8]], align 8 +// SAME-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// SAME-NEXT: store ptr null, ptr [[TMP9]], align 8 +// SAME-NEXT: br label %[[BOUNDSCHECK_CONT]] +// SAME: [[BOUNDSCHECK_CONT]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// SAME-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// SAME-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// SAME: [[LOR_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// SAME-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS34]]: +// SAME-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// SAME-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// SAME: [[LOR_END]]: +// SAME-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// SAME-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_END37]]: +// SAME-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// SAME-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// SAME-NEXT: [[PTR38:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[TMP]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// SAME-NEXT: [[TMP14:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_cbon([2 x i64] [[TMP14]]) +// SAME-NEXT: ret void +// +void init_list_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// SAME-LABEL: define dso_local void @init_list_cbon_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*]]: +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// SAME-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// SAME-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// SAME: [[LOR_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// SAME-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS34]]: +// SAME-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// SAME-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// SAME: [[LOR_END]]: +// SAME-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// SAME-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_END37]]: +// SAME-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// SAME-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// SAME-NEXT: [[PTR38:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[PTR]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// SAME-NEXT: [[TMP5:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_cbon([2 x i64] [[TMP5]]) +// SAME-NEXT: ret void +// +void init_list_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cbon( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// LEGACY-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: [[TMP2:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META2:![0-9]+]] +// LEGACY-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META2]] +// LEGACY: [[BOUNDSCHECK_NOTNULL]]: +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// LEGACY-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// LEGACY-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// LEGACY: [[BOUNDSCHECK_NULL]]: +// LEGACY-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr null, ptr [[TMP8]], align 8 +// LEGACY-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr null, ptr [[TMP9]], align 8 +// LEGACY-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr null, ptr [[TMP10]], align 8 +// LEGACY-NEXT: br label %[[BOUNDSCHECK_CONT]] +// LEGACY: [[BOUNDSCHECK_CONT]]: +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP11:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_cbon([2 x i64] [[TMP11]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cbon( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// NEW-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4:![0-9]+]] +// NEW-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// NEW: [[BOUNDSCHECK_NOTNULL]]: +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// NEW-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// NEW-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// NEW: [[BOUNDSCHECK_NULL]]: +// NEW-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: store ptr null, ptr [[TMP7]], align 8 +// NEW-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: store ptr null, ptr [[TMP8]], align 8 +// NEW-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: store ptr null, ptr [[TMP9]], align 8 +// NEW-NEXT: br label %[[BOUNDSCHECK_CONT]] +// NEW: [[BOUNDSCHECK_CONT]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// NEW-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// NEW-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// NEW: [[LOR_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// NEW-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS34]]: +// NEW-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// NEW-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// NEW: [[LOR_END]]: +// NEW-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// NEW-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_END37]]: +// NEW-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// NEW-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// NEW-NEXT: [[PTR38:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[TMP]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// NEW-NEXT: [[TMP14:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_cbon([2 x i64] [[TMP14]]) +// NEW-NEXT: ret void +// +void compound_literal_init_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cbon_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// LEGACY-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_cbon([2 x i64] [[TMP2]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cbon_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*]]: +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_CBON:%.*]], align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// NEW-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// NEW-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// NEW: [[LOR_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// NEW-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS34]]: +// NEW-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// NEW-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// NEW: [[LOR_END]]: +// NEW-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// NEW-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_END37]]: +// NEW-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// NEW-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// NEW-NEXT: [[PTR38:%.*]] = getelementptr inbounds nuw [[STRUCT_CBON]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP39]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_cbon([2 x i64] [[TMP5]]) +// NEW-NEXT: ret void +// +void compound_literal_init_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +#endif + +// ============================================================================= +// __sized_by +// ============================================================================= + +struct sb { + const int count; + char* __sized_by(count) ptr; +}; + +void consume_sb(struct sb); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_sb( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*]]: +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// SAME-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// SAME-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// SAME-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// SAME-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// SAME-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// SAME-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS27]]: +// SAME-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// SAME-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_END30]]: +// SAME-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// SAME-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// SAME-NEXT: [[PTR31:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[TMP]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// SAME-NEXT: [[TMP9:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_sb([2 x i64] [[TMP9]]) +// SAME-NEXT: ret void +// +void init_list_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// SAME-LABEL: define dso_local void @init_list_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*]]: +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS27]]: +// SAME-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// SAME-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// SAME: [[LAND_END30]]: +// SAME-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// SAME-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// SAME-NEXT: [[PTR31:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[PTR]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// SAME-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_sb([2 x i64] [[TMP4]]) +// SAME-NEXT: ret void +// +void init_list_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sb( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// LEGACY-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: [[TMP2:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// LEGACY-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP7:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_sb([2 x i64] [[TMP7]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sb( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*]]: +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// NEW-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS27]]: +// NEW-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP6:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// NEW-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_END30]]: +// NEW-NEXT: [[TMP7:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP6]], %[[LAND_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP7]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[TMP8:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// NEW-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP8]], i8 0, i64 4, i1 false) +// NEW-NEXT: [[PTR31:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[TMP]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// NEW-NEXT: [[TMP9:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_sb([2 x i64] [[TMP9]]) +// NEW-NEXT: ret void +// +void compound_literal_init_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sb_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// LEGACY-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_sb([2 x i64] [[TMP2]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sb_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*]]: +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_SB:%.*]], align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END30:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB17]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[CMP25:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP25]], label %[[LAND_RHS27:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS27]]: +// NEW-NEXT: [[CMP28:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LAND_RHS]] ], [ [[CMP28]], %[[LAND_RHS27]] ] +// NEW-NEXT: br label %[[LAND_END30]], !annotation [[META2]] +// NEW: [[LAND_END30]]: +// NEW-NEXT: [[TMP2:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP1]], %[[LAND_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP2]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[TMP3:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// NEW-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP3]], i8 0, i64 4, i1 false) +// NEW-NEXT: [[PTR31:%.*]] = getelementptr inbounds nuw [[STRUCT_SB]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP32]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR31]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_sb([2 x i64] [[TMP4]]) +// NEW-NEXT: ret void +// +void compound_literal_init_sb_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +#endif + +// ============================================================================= +// __sized_by_or_null +// ============================================================================= + +struct sbon { + const int count; + char* __sized_by_or_null(count) ptr; +}; + +void consume_sbon(struct sbon); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_sbon( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// SAME-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// SAME-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// SAME-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// SAME: [[BOUNDSCHECK_NOTNULL]]: +// SAME-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// SAME-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// SAME-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// SAME-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// SAME-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// SAME-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// SAME-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// SAME-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// SAME: [[BOUNDSCHECK_NULL]]: +// SAME-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// SAME-NEXT: store ptr null, ptr [[TMP7]], align 8 +// SAME-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// SAME-NEXT: store ptr null, ptr [[TMP8]], align 8 +// SAME-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// SAME-NEXT: store ptr null, ptr [[TMP9]], align 8 +// SAME-NEXT: br label %[[BOUNDSCHECK_CONT]] +// SAME: [[BOUNDSCHECK_CONT]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// SAME-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// SAME-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// SAME: [[LOR_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS34]]: +// SAME-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// SAME-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// SAME: [[LOR_END]]: +// SAME-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// SAME-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_END37]]: +// SAME-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// SAME-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// SAME-NEXT: [[PTR38:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[TMP]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// SAME-NEXT: [[TMP14:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_sbon([2 x i64] [[TMP14]]) +// SAME-NEXT: ret void +// +void init_list_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// SAME-LABEL: define dso_local void @init_list_sbon_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*]]: +// SAME-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// SAME-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// SAME-NEXT: [[C:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// SAME-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// SAME-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// SAME-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// SAME-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// SAME-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// SAME-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// SAME: [[LOR_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// SAME-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// SAME-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS34]]: +// SAME-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// SAME-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// SAME: [[LAND_END]]: +// SAME-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// SAME-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// SAME: [[LOR_END]]: +// SAME-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// SAME-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// SAME: [[LAND_END37]]: +// SAME-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// SAME-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[C]], i32 0, i32 0 +// SAME-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// SAME-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// SAME-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// SAME-NEXT: [[PTR38:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[C]], i32 0, i32 1 +// SAME-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[PTR]], i64 24, i1 false) +// SAME-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 0 +// SAME-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// SAME-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 1 +// SAME-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// SAME-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 2 +// SAME-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// SAME-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// SAME-NEXT: [[TMP5:%.*]] = load [2 x i64], ptr [[C]], align 8 +// SAME-NEXT: call void @consume_sbon([2 x i64] [[TMP5]]) +// SAME-NEXT: ret void +// +void init_list_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sbon( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// LEGACY-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: [[TMP2:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// LEGACY-NEXT: [[TMP3:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: [[TMP4:%.*]] = icmp ne ptr [[TMP2]], null, !annotation [[META2]] +// LEGACY-NEXT: br i1 [[TMP4]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META2]] +// LEGACY: [[BOUNDSCHECK_NOTNULL]]: +// LEGACY-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP3]] to i64 +// LEGACY-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 [[IDX_EXT]] +// LEGACY-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr [[TMP2]], ptr [[TMP5]], align 8 +// LEGACY-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr [[ADD_PTR]], ptr [[TMP6]], align 8 +// LEGACY-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr [[TMP2]], ptr [[TMP7]], align 8 +// LEGACY-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// LEGACY: [[BOUNDSCHECK_NULL]]: +// LEGACY-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: store ptr null, ptr [[TMP8]], align 8 +// LEGACY-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: store ptr null, ptr [[TMP9]], align 8 +// LEGACY-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: store ptr null, ptr [[TMP10]], align 8 +// LEGACY-NEXT: br label %[[BOUNDSCHECK_CONT]] +// LEGACY: [[BOUNDSCHECK_CONT]]: +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP11:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_sbon([2 x i64] [[TMP11]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sbon( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// NEW-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[TMP1:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// NEW-NEXT: [[TMP2:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: [[TMP3:%.*]] = icmp ne ptr [[TMP1]], null, !annotation [[META4]] +// NEW-NEXT: br i1 [[TMP3]], label %[[BOUNDSCHECK_NOTNULL:.*]], label %[[BOUNDSCHECK_NULL:.*]], !annotation [[META4]] +// NEW: [[BOUNDSCHECK_NOTNULL]]: +// NEW-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// NEW-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[IDX_EXT]] +// NEW-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP4]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: store ptr [[ADD_PTR]], ptr [[TMP5]], align 8 +// NEW-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: store ptr [[TMP1]], ptr [[TMP6]], align 8 +// NEW-NEXT: br label %[[BOUNDSCHECK_CONT:.*]] +// NEW: [[BOUNDSCHECK_NULL]]: +// NEW-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// NEW-NEXT: store ptr null, ptr [[TMP7]], align 8 +// NEW-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// NEW-NEXT: store ptr null, ptr [[TMP8]], align 8 +// NEW-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// NEW-NEXT: store ptr null, ptr [[TMP9]], align 8 +// NEW-NEXT: br label %[[BOUNDSCHECK_CONT]] +// NEW: [[BOUNDSCHECK_CONT]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// NEW-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// NEW-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// NEW: [[LOR_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[TMP]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS34]]: +// NEW-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP10:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// NEW-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// NEW: [[LOR_END]]: +// NEW-NEXT: [[TMP11:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP10]], %[[LAND_END]] ] +// NEW-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_END37]]: +// NEW-NEXT: [[TMP12:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[BOUNDSCHECK_CONT]] ], [ [[TMP11]], %[[LOR_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP12]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// NEW-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP13]], i8 0, i64 4, i1 false) +// NEW-NEXT: [[PTR38:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[TMP]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// NEW-NEXT: [[TMP14:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_sbon([2 x i64] [[TMP14]]) +// NEW-NEXT: ret void +// +void compound_literal_init_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sbon_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// LEGACY-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// LEGACY-NEXT: [[C:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// LEGACY-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// LEGACY-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// LEGACY-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[C]], i32 0, i32 0 +// LEGACY-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// LEGACY-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// LEGACY-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// LEGACY-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 4, i1 false) +// LEGACY-NEXT: [[PTR1:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[C]], i32 0, i32 1 +// LEGACY-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// LEGACY-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// LEGACY-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// LEGACY-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// LEGACY-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// LEGACY-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// LEGACY-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[PTR1]], align 8 +// LEGACY-NEXT: [[TMP2:%.*]] = load [2 x i64], ptr [[C]], align 8 +// LEGACY-NEXT: call void @consume_sbon([2 x i64] [[TMP2]]) +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sbon_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*]]: +// NEW-NEXT: [[COUNT_PARAM_ADDR:%.*]] = alloca i32, align 4 +// NEW-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NEW-NEXT: [[C:%.*]] = alloca [[STRUCT_SBON:%.*]], align 8 +// NEW-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP4:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: [[AGG_TEMP39:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NEW-NEXT: store i32 [[COUNT_PARAM]], ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// NEW-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT_PARAM_ADDR]], align 4 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB3]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP]], label %[[LAND_LHS_TRUE:.*]], label %[[LAND_END37:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP4]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP4]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP7]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// NEW-NEXT: [[CMP14:%.*]] = icmp ule ptr [[WIDE_PTR_LB6]], [[WIDE_PTR_PTR9]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14]], label %[[LAND_RHS:.*]], label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP15]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 8, !annotation [[META2]] +// NEW-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR17]], null, !annotation [[META2]] +// NEW-NEXT: br i1 [[TOBOOL]], label %[[LOR_RHS:.*]], label %[[LOR_END:.*]], !annotation [[META2]] +// NEW: [[LOR_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP22]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[PTR]], i64 24, i1 false), !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 0, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 1, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 2, !annotation [[META2]] +// NEW-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR27]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// NEW-NEXT: [[CMP32:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP32]], label %[[LAND_RHS34:.*]], label %[[LAND_END:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS34]]: +// NEW-NEXT: [[CMP35:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// NEW-NEXT: br label %[[LAND_END]], !annotation [[META2]] +// NEW: [[LAND_END]]: +// NEW-NEXT: [[TMP1:%.*]] = phi i1 [ false, %[[LOR_RHS]] ], [ [[CMP35]], %[[LAND_RHS34]] ] +// NEW-NEXT: br label %[[LOR_END]], !annotation [[META2]] +// NEW: [[LOR_END]]: +// NEW-NEXT: [[TMP2:%.*]] = phi i1 [ true, %[[LAND_RHS]] ], [ [[TMP1]], %[[LAND_END]] ] +// NEW-NEXT: br label %[[LAND_END37]], !annotation [[META2]] +// NEW: [[LAND_END37]]: +// NEW-NEXT: [[TMP3:%.*]] = phi i1 [ false, %[[LAND_LHS_TRUE]] ], [ false, %[[ENTRY]] ], [ [[TMP2]], %[[LOR_END]] ], !annotation [[META2]] +// NEW-NEXT: br i1 [[TMP3]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[C]], i32 0, i32 0 +// NEW-NEXT: store i32 [[TMP0]], ptr [[COUNT]], align 8 +// NEW-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[C]], i64 4 +// NEW-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP4]], i8 0, i64 4, i1 false) +// NEW-NEXT: [[PTR38:%.*]] = getelementptr inbounds nuw [[STRUCT_SBON]], ptr [[C]], i32 0, i32 1 +// NEW-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP39]], ptr align 8 [[PTR]], i64 24, i1 false) +// NEW-NEXT: [[WIDE_PTR_PTR_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 0 +// NEW-NEXT: [[WIDE_PTR_PTR41:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR40]], align 8 +// NEW-NEXT: [[WIDE_PTR_UB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 1 +// NEW-NEXT: [[WIDE_PTR_UB43:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR42]], align 8 +// NEW-NEXT: [[WIDE_PTR_LB_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP39]], i32 0, i32 2 +// NEW-NEXT: [[WIDE_PTR_LB45:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR44]], align 8 +// NEW-NEXT: store ptr [[WIDE_PTR_PTR41]], ptr [[PTR38]], align 8 +// NEW-NEXT: [[TMP5:%.*]] = load [2 x i64], ptr [[C]], align 8 +// NEW-NEXT: call void @consume_sbon([2 x i64] [[TMP5]]) +// NEW-NEXT: ret void +// +void compound_literal_init_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +#endif +//. +// SAME: [[META2]] = !{!"bounds-safety-generic"} +// SAME: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// SAME: [[META4]] = !{!"bounds-safety-check-ptr-neq-null"} +//. +// LEGACY: [[META2]] = !{!"bounds-safety-check-ptr-neq-null"} +//. +// NEW: [[META2]] = !{!"bounds-safety-generic"} +// NEW: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// NEW: [[META4]] = !{!"bounds-safety-check-ptr-neq-null"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/init-struct-const-count-O2.c b/clang/test/BoundsSafety/CodeGen/init-struct-const-count-O2.c new file mode 100644 index 0000000000000..b965bb5e7cfe6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-struct-const-count-O2.c @@ -0,0 +1,693 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// Checks that are the same between -fbounds-safety-bringup-missing-checks flags +// RUN: %clang_cc1 -O2 -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=SAME %s +// RUN: %clang_cc1 -O2 -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=SAME %s + +// Checks that differ with -fbounds-safety-bringup-missing-checks +// RUN: %clang_cc1 -O2 -DCOMPOUND_LITERAL -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=LEGACY %s +// RUN: %clang_cc1 -O2 -DCOMPOUND_LITERAL -emit-llvm -triple arm64-apple-iphoneos -fbounds-safety -fbounds-safety-bringup-missing-checks=all %s -o - | FileCheck --check-prefix=NEW %s +#include + + +// ============================================================================= +// __counted_by +// ============================================================================= + +struct cb { + const int count; + int* __counted_by(count) ptr; +}; + +void consume_cb(struct cb); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_cb( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0, !annotation [[META2:![0-9]+]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[PTR]] to i64, !annotation [[META2]] +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// SAME-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4:[0-9]+]] +// SAME-NEXT: ret void +// +void init_list_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// SAME-LABEL: define dso_local void @init_list_cb_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef readonly captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// SAME-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// SAME-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// SAME-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// SAME-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// SAME-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8:![0-9]+]] +// SAME-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// SAME-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// SAME-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// SAME-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// SAME-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10:![0-9]+]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// SAME-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_cb_bidi(int count_param, int* __bidi_indexable ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cb( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2:[0-9]+]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cb( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0, !annotation [[META2:![0-9]+]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[PTR]] to i64, !annotation [[META2]] +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// NEW-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4:[0-9]+]] +// NEW-NEXT: ret void +// +void compound_literal_init_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cb_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef readonly captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cb_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef readonly captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// NEW-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// NEW-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// NEW-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// NEW-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3:![0-9]+]] +// NEW-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8:![0-9]+]] +// NEW-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// NEW-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// NEW-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// NEW-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// NEW-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10:![0-9]+]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// NEW-NEXT: tail call void @consume_cb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_cb_bidi(int count_param, int*__bidi_indexable ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +#endif + +// ============================================================================= +// __counted_by_or_null +// ============================================================================= + +struct cbon { + const int count; + int* __counted_by_or_null(count) ptr; +}; + +void consume_cbon(struct cbon); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_cbon( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[PTR]], null, !annotation [[META11:![0-9]+]] +// SAME-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0 +// SAME-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// SAME-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// SAME-LABEL: define dso_local void @init_list_cbon_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef readonly captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// SAME-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// SAME-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// SAME-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// SAME-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// SAME-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// SAME-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS_CONT_CRIT_EDGE]]: +// SAME-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// SAME-NEXT: br label %[[CONT:.*]] +// SAME: [[LOR_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// SAME-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// SAME-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// SAME-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// SAME-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// SAME-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// SAME-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cbon( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cbon( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[PTR]], null, !annotation [[META11:![0-9]+]] +// NEW-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0 +// NEW-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// NEW-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_cbon_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef readonly captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_cbon_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef readonly captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// NEW-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// NEW-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// NEW-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// NEW-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA3]] +// NEW-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// NEW-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS_CONT_CRIT_EDGE]]: +// NEW-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// NEW-NEXT: br label %[[CONT:.*]] +// NEW: [[LOR_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// NEW-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2, !annotation [[META2]] +// NEW-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_DIV]], [[CONV]], !annotation [[META2]] +// NEW-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// NEW-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// NEW-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// NEW-NEXT: tail call void @consume_cbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +#endif + +// ============================================================================= +// __sized_by +// ============================================================================= + +struct sb { + const int count; + char* __sized_by(count) ptr; +}; + +void consume_sb(struct sb); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_sb( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0, !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[PTR]] to i64, !annotation [[META2]] +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// SAME-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// SAME-LABEL: define dso_local void @init_list_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef readonly captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// SAME-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// SAME-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// SAME-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// SAME-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA12:![0-9]+]] +// SAME-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// SAME-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// SAME-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// SAME-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// SAME-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// SAME-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sb( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sb( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0, !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[PTR]] to i64, !annotation [[META2]] +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// NEW-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sb_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef readonly captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sb_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef readonly captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// NEW-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// NEW-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// NEW-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// NEW-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA12:![0-9]+]] +// NEW-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// NEW-NEXT: [[CMP25:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// NEW-NEXT: [[CMP28:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// NEW-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP28]], [[CMP25]] +// NEW-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT:.*]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext nneg i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[SUB_PTR_RHS_CAST]], 1 +// NEW-NEXT: tail call void @consume_sb([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_sb_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +#endif + +// ============================================================================= +// __sized_by_or_null +// ============================================================================= + +struct sbon { + const int count; + char* __sized_by_or_null(count) ptr; +}; + +void consume_sbon(struct sbon); + +#ifndef COMPOUND_LITERAL + +// SAME-LABEL: define dso_local void @init_list_sbon( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[PTR]], null, !annotation [[META11]] +// SAME-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0 +// SAME-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// SAME-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// SAME-LABEL: define dso_local void @init_list_sbon_bidi( +// SAME-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef readonly captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// SAME-NEXT: [[ENTRY:.*:]] +// SAME-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// SAME-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// SAME-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// SAME-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// SAME: [[LAND_LHS_TRUE]]: +// SAME-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// SAME-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA12]] +// SAME-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// SAME-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS]]: +// SAME-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// SAME-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// SAME: [[LAND_RHS_CONT_CRIT_EDGE]]: +// SAME-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// SAME-NEXT: br label %[[CONT:.*]] +// SAME: [[LOR_RHS]]: +// SAME-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// SAME-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// SAME-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// SAME-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// SAME-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// SAME-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// SAME-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// SAME: [[TRAP]]: +// SAME-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// SAME-NEXT: unreachable, !annotation [[META2]] +// SAME: [[CONT]]: +// SAME-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// SAME-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// SAME-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// SAME-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// SAME-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// SAME-NEXT: ret void +// +void init_list_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +#else + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sbon( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sbon( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[PTR]], null, !annotation [[META11]] +// NEW-NEXT: [[CMP_NOT47:%.*]] = icmp slt i32 [[COUNT_PARAM]], 0 +// NEW-NEXT: [[CMP_NOT:%.*]] = and i1 [[CMP_NOT47]], [[DOTNOT]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[CONT:.*]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[PTR]] to i64 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// NEW-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +// LEGACY-LABEL: define dso_local void @compound_literal_init_sbon_bidi( +// LEGACY-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef readonly captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// LEGACY-NEXT: [[ENTRY:.*:]] +// LEGACY-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// LEGACY-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// LEGACY-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// LEGACY-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// LEGACY-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[TMP0]], 1 +// LEGACY-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR2]] +// LEGACY-NEXT: ret void +// +// NEW-LABEL: define dso_local void @compound_literal_init_sbon_bidi( +// NEW-SAME: i32 noundef [[COUNT_PARAM:%.*]], ptr noundef readonly captures(none) [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { +// NEW-NEXT: [[ENTRY:.*:]] +// NEW-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR]], align 8 +// NEW-NEXT: [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// NEW-NEXT: [[AGG_TEMP1_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_PTR_SROA_IDX]], align 8 +// NEW-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_1_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP_NOT]], label %[[TRAP:.*]], label %[[LAND_LHS_TRUE:.*]], !annotation [[META2]] +// NEW: [[LAND_LHS_TRUE]]: +// NEW-NEXT: [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// NEW-NEXT: [[AGG_TEMP4_SROA_1_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_PTR_SROA_IDX]], align 8, !tbaa [[TBAA12]] +// NEW-NEXT: [[CMP14_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP4_SROA_1_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META2]] +// NEW-NEXT: br i1 [[CMP14_NOT]], label %[[TRAP]], label %[[LAND_RHS:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS]]: +// NEW-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META2]] +// NEW-NEXT: br i1 [[TOBOOL_NOT]], label %[[LAND_RHS_CONT_CRIT_EDGE:.*]], label %[[LOR_RHS:.*]], !annotation [[META2]] +// NEW: [[LAND_RHS_CONT_CRIT_EDGE]]: +// NEW-NEXT: [[DOTPRE:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// NEW-NEXT: br label %[[CONT:.*]] +// NEW: [[LOR_RHS]]: +// NEW-NEXT: [[CONV:%.*]] = sext i32 [[COUNT_PARAM]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_1_0_COPYLOAD]] to i64, !annotation [[META2]] +// NEW-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64 +// NEW-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META8]] +// NEW-NEXT: [[CMP32:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META2]] +// NEW-NEXT: [[CMP35:%.*]] = icmp sgt i32 [[COUNT_PARAM]], -1, !annotation [[META2]] +// NEW-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP35]], [[CMP32]] +// NEW-NEXT: br i1 [[SPEC_SELECT]], label %[[CONT]], label %[[TRAP]], !prof [[PROF10]], !annotation [[META2]] +// NEW: [[TRAP]]: +// NEW-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// NEW-NEXT: unreachable, !annotation [[META2]] +// NEW: [[CONT]]: +// NEW-NEXT: [[DOTPRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[LAND_RHS_CONT_CRIT_EDGE]] ], [ [[SUB_PTR_RHS_CAST]], %[[LOR_RHS]] ] +// NEW-NEXT: [[C_SROA_0_0_INSERT_EXT:%.*]] = zext i32 [[COUNT_PARAM]] to i64 +// NEW-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [2 x i64] poison, i64 [[C_SROA_0_0_INSERT_EXT]], 0 +// NEW-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [2 x i64] [[DOTFCA_0_INSERT]], i64 [[DOTPRE_PHI]], 1 +// NEW-NEXT: tail call void @consume_sbon([2 x i64] [[DOTFCA_1_INSERT]]) #[[ATTR4]] +// NEW-NEXT: ret void +// +void compound_literal_init_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +#endif +//. +// SAME: [[META2]] = !{!"bounds-safety-generic"} +// SAME: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// SAME: [[META4]] = !{!"p1 int", [[META5:![0-9]+]], i64 0} +// SAME: [[META5]] = !{!"any pointer", [[META6:![0-9]+]], i64 0} +// SAME: [[META6]] = !{!"omnipotent char", [[META7:![0-9]+]], i64 0} +// SAME: [[META7]] = !{!"Simple C/C++ TBAA"} +// SAME: [[META8]] = !{!"bounds-safety-generic", [[META9:![0-9]+]]} +// SAME: [[META9]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// SAME: [[PROF10]] = !{!"branch_weights", i32 1048575, i32 1} +// SAME: [[META11]] = !{!"bounds-safety-check-ptr-neq-null"} +// SAME: [[TBAA12]] = !{[[META13:![0-9]+]], [[META13]], i64 0} +// SAME: [[META13]] = !{!"p1 omnipotent char", [[META5]], i64 0} +//. +// NEW: [[META2]] = !{!"bounds-safety-generic"} +// NEW: [[TBAA3]] = !{[[META4:![0-9]+]], [[META4]], i64 0} +// NEW: [[META4]] = !{!"p1 int", [[META5:![0-9]+]], i64 0} +// NEW: [[META5]] = !{!"any pointer", [[META6:![0-9]+]], i64 0} +// NEW: [[META6]] = !{!"omnipotent char", [[META7:![0-9]+]], i64 0} +// NEW: [[META7]] = !{!"Simple C/C++ TBAA"} +// NEW: [[META8]] = !{!"bounds-safety-generic", [[META9:![0-9]+]]} +// NEW: [[META9]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// NEW: [[PROF10]] = !{!"branch_weights", i32 1048575, i32 1} +// NEW: [[META11]] = !{!"bounds-safety-check-ptr-neq-null"} +// NEW: [[TBAA12]] = !{[[META13:![0-9]+]], [[META13]], i64 0} +// NEW: [[META13]] = !{!"p1 omnipotent char", [[META5]], i64 0} +//. diff --git a/clang/test/BoundsSafety/CodeGen/init-struct-global.c b/clang/test/BoundsSafety/CodeGen/init-struct-global.c new file mode 100644 index 0000000000000..801893a3ee423 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-struct-global.c @@ -0,0 +1,18 @@ +// REQUIRES: x86-registered-target + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +struct Foo { + char *__bidi_indexable inner; +}; +int globalCh; + +struct Foo global = {&globalCh}; + +// CHECK: _global: +// CHECK: .quad _globalCh +// CHECK: .quad _globalCh+4 +// CHECK: .quad _globalCh diff --git a/clang/test/BoundsSafety/CodeGen/init-struct-local.c b/clang/test/BoundsSafety/CodeGen/init-struct-local.c new file mode 100644 index 0000000000000..b45399f5d1fdc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-struct-local.c @@ -0,0 +1,22 @@ +// REQUIRES: x86-registered-target + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +struct Foo { + char *__bidi_indexable inner; +}; +int globalCh; + +void ignoreme(void) { + struct Foo local = { + .inner = &globalCh + }; +} + +// CHECK: local: +// CHECK: .quad _globalCh +// CHECK: .quad _globalCh+4 +// CHECK: .quad _globalCh diff --git a/clang/test/BoundsSafety/CodeGen/init-union-global.c b/clang/test/BoundsSafety/CodeGen/init-union-global.c new file mode 100644 index 0000000000000..9c26632f94cb0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/init-union-global.c @@ -0,0 +1,22 @@ +// REQUIRES: x86-registered-target + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -S %s -o - | FileCheck %s + +#include + +union Foo { + char *__bidi_indexable inner; + int a; + unsigned b[100]; +}; +char globalCh; + +union Foo global = { + .inner = &globalCh +}; + +// CHECK: _global: +// CHECK: .quad _globalCh +// CHECK: .quad _globalCh+1 +// CHECK: .quad _globalCh diff --git a/clang/test/BoundsSafety/CodeGen/inline-asm-external-bounds.c b/clang/test/BoundsSafety/CodeGen/inline-asm-external-bounds.c new file mode 100644 index 0000000000000..4d7c3bf1940fe --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/inline-asm-external-bounds.c @@ -0,0 +1,29 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @Test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr @.str, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr getelementptr inbounds (i8, ptr @.str, i64 5), ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr @.str, ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void asm sideeffect "test $0", "r,~{dirflag},~{fpsr},~{flags}"(ptr [[WIDE_PTR_PTR]]) #[[ATTR1:[0-9]+]], {{!srcloc ![0-9]+}} +// CHECK-NEXT: ret void +// +void Test() { + __asm__ ("test %0" ::"r" (("beef"))); +} diff --git a/clang/test/BoundsSafety/CodeGen/large-array-subscript-x86_64.c b/clang/test/BoundsSafety/CodeGen/large-array-subscript-x86_64.c new file mode 100644 index 0000000000000..b5fea2b6a9aba --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/large-array-subscript-x86_64.c @@ -0,0 +1,87 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +// XXX: Previously, rdar://132269322 was caused by incorrectly emitting 'align 16' here +// CHECK-LABEL: define dso_local float @large_array_subscript( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARR:%.*]] = alloca [80 x float], align 16 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 320, i1 false) +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [80 x float], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds float, ptr [[ARRAYDECAY]], i64 80 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr float, ptr [[ARRAYDECAY]], i64 19 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[UPPER]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP0]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[ARRAYIDX]], [[ARRAYDECAY]], !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP1]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[TMP2:%.*]] = load float, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret float [[TMP2]] +// +float large_array_subscript(void) { + float arr[80] = {0}; + return arr[19]; +} + +// CHECK-LABEL: define dso_local float @vla_subscript( +// CHECK-SAME: i32 noundef [[SIZE:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[SAVED_STACK:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[__VLA_EXPR0:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[SIZE]], ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = call ptr @llvm.stacksave.p0() +// CHECK-NEXT: store ptr [[TMP2]], ptr [[SAVED_STACK]], align 8 +// CHECK-NEXT: [[VLA:%.*]] = alloca float, i64 [[TMP1]], align 16 +// CHECK-NEXT: store i64 [[TMP1]], ptr [[__VLA_EXPR0]], align 8 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds float, ptr [[VLA]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[VLA]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[VLA]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr float, ptr [[WIDE_PTR_PTR]], i64 19 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP6]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3]], !annotation [[META2]] +// CHECK: [[TRAP]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: [[CONT]]: +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP7]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META4]] +// CHECK: [[TRAP1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META4]] +// CHECK-NEXT: unreachable, !annotation [[META4]] +// CHECK: [[CONT2]]: +// CHECK-NEXT: [[TMP8:%.*]] = load float, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[SAVED_STACK]], align 8 +// CHECK-NEXT: call void @llvm.stackrestore.p0(ptr [[TMP9]]) +// CHECK-NEXT: ret float [[TMP8]] +// +float vla_subscript(int size) { + float arr[size]; + return arr[19]; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/materialize-unsafe-forge-ptr.c b/clang/test/BoundsSafety/CodeGen/materialize-unsafe-forge-ptr.c new file mode 100644 index 0000000000000..eb90d502d822e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/materialize-unsafe-forge-ptr.c @@ -0,0 +1,123 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -emit-llvm -triple arm64 -fbounds-safety %s -o - | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -triple arm64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s -o - | FileCheck %s + +#include + +struct opaq_t; + +struct opaq_t *g; + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP22:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @g, align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i64 11 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP8]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP8]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_UB10]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END41:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP21:%.*]] = icmp ule ptr [[WIDE_PTR_LB13]], [[WIDE_PTR_PTR16]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP21]], label [[LAND_RHS:%.*]], label [[LAND_END41]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP22]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP22]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[TMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP25]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR28]], ptr [[TMP8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP25]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP25]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB32]], ptr [[TMP10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP25]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP25]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP25]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB24]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR34]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP39:%.*]] = icmp sle i64 11, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP39]], label [[LAND_RHS40:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs40: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS40]] ] +// CHECK-NEXT: br label [[LAND_END41]], {{!annotation ![0-9]+}} +// CHECK: land.end41: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP11]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP42]], ptr align 8 [[TMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR44]], ptr [[PTR]], align 8 +// CHECK-NEXT: ret void +// +void test() { + struct opaq_t *__sized_by(11) ptr = __unsafe_forge_bidi_indexable(struct opaq_t *, g, 11); +} diff --git a/clang/test/BoundsSafety/CodeGen/member-access-with-base-side-effects.c b/clang/test/BoundsSafety/CodeGen/member-access-with-base-side-effects.c new file mode 100644 index 0000000000000..ec7736f6c19f3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/member-access-with-base-side-effects.c @@ -0,0 +1,31 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -triple x86_64 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct foo { + int *__counted_by(count) p; + int count; +}; + +struct foo *bar(void); + +// CHECK-LABEL: @baz( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call ptr @bar() #[[ATTR2:[0-9]+]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[CALL]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw i8, ptr [[CALL]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[TMP0]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[ADD_PTR]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable baz(void) { + return bar()->p; +} + diff --git a/clang/test/BoundsSafety/CodeGen/member-expr.c b/clang/test/BoundsSafety/CodeGen/member-expr.c new file mode 100644 index 0000000000000..c2dd383489334 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/member-expr.c @@ -0,0 +1,999 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +struct foo { + int bar; + int baz; + int frob[10]; +}; + +void escape_ptr_single(int *__single p); +void escape_ptr_bidi(int *__bidi_indexable p); + +// CHECK-LABEL: @foo_single_bar_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 1 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_single_bar_single(struct foo *__single f) { + escape_ptr_single(&f->bar); +} + +// CHECK-LABEL: @foo_single_baz_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 1 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_single_baz_single(struct foo *__single f) { + escape_ptr_single(&f->baz); +} + +// CHECK-LABEL: @foo_single_frob_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP5:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP6:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_single_frob_single(struct foo *__single f) { + escape_ptr_single(f->frob); +} + +// CHECK-LABEL: @foo_single_frob4_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 4 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP14:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP15:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_single_frob4_single(struct foo *__single f) { + escape_ptr_single(&f->frob[4]); +} + +// CHECK-LABEL: @foo_single_bar_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 1 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_single_bar_bidi(struct foo *__single f) { + escape_ptr_bidi(&f->bar); +} + +// CHECK-LABEL: @foo_single_baz_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 1 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_single_baz_bidi(struct foo *__single f) { + escape_ptr_bidi(&f->baz); +} + +// CHECK-LABEL: @foo_single_frob_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_single_frob_bidi(struct foo *__single f) { + escape_ptr_bidi(f->frob); +} + +// CHECK-LABEL: @foo_single_frob4_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[F:%.*]], ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F_ADDR]], align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO:%.*]], ptr [[TMP0]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 4 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_single_frob4_bidi(struct foo *__single f) { + escape_ptr_bidi(&f->frob[4]); +} + +// CHECK-LABEL: @foo_bidi_bar_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont3: +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT13:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP8:%.*]] = icmp ult ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont11: +// CHECK-NEXT: [[TMP9:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT13]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont13: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR5]]) +// CHECK-NEXT: ret void +// +void foo_bidi_bar_single(struct foo *__bidi_indexable f) { + escape_ptr_single(&f->bar); +} + +// CHECK-LABEL: @foo_bidi_baz_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont3: +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT13:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP8:%.*]] = icmp ult ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont11: +// CHECK-NEXT: [[TMP9:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT13]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont13: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR5]]) +// CHECK-NEXT: ret void +// +void foo_bidi_baz_single(struct foo *__bidi_indexable f) { + escape_ptr_single(&f->baz); +} + +// CHECK-LABEL: @foo_bidi_frob_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont3: +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT13:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont11: +// CHECK-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT13]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont13: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR5]]) +// CHECK-NEXT: ret void +// +void foo_bidi_frob_single(struct foo *__bidi_indexable f) { + escape_ptr_single(f->frob); +} + +// CHECK-LABEL: @foo_bidi_frob4_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont3: +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 4 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT13:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP16:%.*]] = icmp ult ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP16]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont11: +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT13]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont13: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR5]]) +// CHECK-NEXT: ret void +// +void foo_bidi_frob4_single(struct foo *__bidi_indexable f) { + escape_ptr_single(&f->frob[4]); +} + +// CHECK-LABEL: @foo_bidi_bar_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_bidi_bar_bidi(struct foo *__bidi_indexable f) { + escape_ptr_bidi(&f->bar); +} + +// CHECK-LABEL: @foo_bidi_baz_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_bidi_baz_bidi(struct foo *__bidi_indexable f) { + escape_ptr_bidi(&f->baz); +} + +// CHECK-LABEL: @foo_bidi_frob_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_bidi_frob_bidi(struct foo *__bidi_indexable f) { + escape_ptr_bidi(f->frob); +} + +// CHECK-LABEL: @foo_bidi_frob4_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[F:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FOO:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[WIDE_PTR_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 4 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_bidi_frob4_bidi(struct foo *__bidi_indexable f) { + escape_ptr_bidi(&f->frob[4]); +} + +// CHECK-LABEL: @foo_dot_bar_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP6:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_dot_bar_single(void) { + struct foo f; + escape_ptr_single(&f.bar); +} + +// CHECK-LABEL: @foo_dot_baz_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP6:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_dot_baz_single(void) { + struct foo f; + escape_ptr_single(&f.baz); +} + +// CHECK-LABEL: @foo_dot_frob_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP4:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP5:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_dot_frob_single(void) { + struct foo f; + escape_ptr_single(f.frob); +} + +// CHECK-LABEL: @foo_dot_frob4_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 4 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP12]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: call void @escape_ptr_single(ptr noundef [[WIDE_PTR_PTR]]) +// CHECK-NEXT: ret void +// +void foo_dot_frob4_single(void) { + struct foo f; + escape_ptr_single(&f.frob[4]); +} + +// CHECK-LABEL: @foo_dot_bar_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_dot_bar_bidi(void) { + struct foo f; + escape_ptr_bidi(&f.bar); +} + +// CHECK-LABEL: @foo_dot_baz_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_dot_baz_bidi(void) { + struct foo f; + escape_ptr_bidi(&f.baz); +} + +// CHECK-LABEL: @foo_dot_frob_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_dot_frob_bidi(void) { + struct foo f; + escape_ptr_bidi(f.frob); +} + +// CHECK-LABEL: @foo_dot_frob4_bidi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 4 +// CHECK-NEXT: [[AGG_TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[FROB:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 2 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[FROB]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 4 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @escape_ptr_bidi(ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[AGG_TMP]]) +// CHECK-NEXT: ret void +// +void foo_dot_frob4_bidi(void) { + struct foo f; + escape_ptr_bidi(&f.frob[4]); +} diff --git a/clang/test/BoundsSafety/CodeGen/multiple-dependees-O2.c b/clang/test/BoundsSafety/CodeGen/multiple-dependees-O2.c new file mode 100644 index 0000000000000..a1e7368e165f9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/multiple-dependees-O2.c @@ -0,0 +1,65 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + + +// RUN: %clang_cc1 %s -O2 -fbounds-safety -emit-llvm -triple x86_64 -o - | FileCheck %s +// RUN: %clang_cc1 %s -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 -o - | FileCheck %s + +#include + +struct T { + int cnt1; + int cnt2; + int *__counted_by(2 * cnt1 + 3 * cnt2) ptr; +}; + +// CHECK-LABEL: @TestOK( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK() { + int arr[16] = { 0 }; + struct T t; + t.cnt1 = 5; + t.cnt2 = 2; + t.ptr = arr; +} + +// CHECK-LABEL: @TestFail( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{.*}} +// CHECK-NEXT: unreachable +// +void TestFail() { + int arr[16] = { 0 }; + int n = 7; + struct T t; + t.cnt1 = n; + t.cnt2 = 1; + t.ptr = arr; +} + +// CHECK-LABEL: @TestOK2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void TestOK2() { + int arr[16] = { 0 }; + struct T t; + t.cnt1 = 7; + t.cnt2 = -1; + t.ptr = arr; +} + +// CHECK-LABEL: @TestFail2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{.*}} +// CHECK-NEXT: unreachable +// +void TestFail2() { + int arr[16] = { 0 }; + int n = 7; + struct T t; + t.cnt1 = n; + t.cnt2 = -5; + t.ptr = arr; +} diff --git a/clang/test/BoundsSafety/CodeGen/multiple-dependees.c b/clang/test/BoundsSafety/CodeGen/multiple-dependees.c new file mode 100644 index 0000000000000..7daae4a7ffbdf --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/multiple-dependees.c @@ -0,0 +1,166 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 %s -O0 -fbounds-safety -triple x86_64 -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm -o - | FileCheck %s + +#include + +struct T { + int cnt1; + int cnt2; + int *__counted_by(2 * cnt1 + 3 * cnt2) ptr; +}; + +// CHECK-LABEL: @Foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[ARR:%.*]] = alloca [16 x i32], align 16 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[T:%.*]] = alloca [[STRUCT_T:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca [[STRUCT_T]], align 8 +// CHECK-NEXT: store i32 [[IDX:%.*]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 64, i1 false) +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [16 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 16 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[CNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[T]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[CNT1]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CNT2:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[T]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[CNT2]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[PTR1:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[T]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[PTR1]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END29:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP16:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR11]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP16]], label [[LAND_RHS:%.*]], label [[LAND_END29]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP17]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB19]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR22]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27:%.*]] = icmp sle i64 16, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS28:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs28: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP3:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS28]] ] +// CHECK-NEXT: br label [[LAND_END29]], {{!annotation ![0-9]+}} +// CHECK: land.end29: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP3]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[CNT130:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[T]], i32 0, i32 0 +// CHECK-NEXT: store i32 5, ptr [[CNT130]], align 8 +// CHECK-NEXT: [[CNT231:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[T]], i32 0, i32 1 +// CHECK-NEXT: store i32 2, ptr [[CNT231]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8 +// CHECK-NEXT: [[PTR39:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[T]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[PTR39]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP41]], ptr align 8 [[T]], i64 16, i1 false) +// CHECK-NEXT: [[CNT142:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[CNT142]], align 8 +// CHECK-NEXT: [[CNT243:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[CNT243]], align 4 +// CHECK-NEXT: [[PTR44:%.*]] = getelementptr inbounds nuw [[STRUCT_T]], ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[PTR44]], align 8 +// CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 2, [[TMP5]] +// CHECK-NEXT: [[MUL45:%.*]] = mul nsw i32 3, [[TMP6]] +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[MUL]], [[MUL45]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[ADD]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP7]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR47:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR46]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP11]] to i64 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR47]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB51:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR50]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = icmp ult ptr [[TMP12]], [[WIDE_PTR_UB49]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT53:%.*]], label [[TRAP52:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap52: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont53: +// CHECK-NEXT: [[TMP14:%.*]] = icmp uge ptr [[TMP12]], [[WIDE_PTR_LB51]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT55:%.*]], label [[TRAP54:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap54: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont55: +// CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[TMP12]], align 4 +// CHECK-NEXT: ret i32 [[TMP15]] +// +int Foo(int idx) { + int arr[16] = { 0 }; + int *ptr = arr; + struct T t; + t.cnt1 = 5; + t.cnt2 = 2; + t.ptr = ptr; + + return t.ptr[idx]; +} diff --git a/clang/test/BoundsSafety/CodeGen/neon-builtin-bounds-safety-cast.c b/clang/test/BoundsSafety/CodeGen/neon-builtin-bounds-safety-cast.c new file mode 100644 index 0000000000000..816a5bb6786fb --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/neon-builtin-bounds-safety-cast.c @@ -0,0 +1,76 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -triple armv7k -target-feature +neon -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple armv7k -target-feature +neon -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[SIGN:%.*]] = alloca [64 x float], align 4 +// CHECK-NEXT: [[R:%.*]] = alloca [[STRUCT_FLOAT32X4X4_T:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 4 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 4 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 4 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 4 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_FLOAT32X4X4_T]], ptr [[R]], i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[R]], ptr [[TMP1]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 4 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[R]], ptr [[TMP3]], align 4 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 4 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 4 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 4 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 4 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP6]], align 4 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 4 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 4 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 4 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [64 x float], ptr [[SIGN]], i32 0, i32 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds float, ptr [[ARRAYDECAY]], i32 64 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP7]], align 4 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP8]], align 4 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP9]], align 4 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR11:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR10]], align 4 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB13:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR12]], align 4 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB15:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR14]], align 4 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR11]], ptr [[TMP10]], align 4 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB13]], ptr [[TMP11]], align 4 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB15]], ptr [[TMP12]], align 4 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 4 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB19:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR18]], align 4 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB21:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR20]], align 4 +// CHECK-NEXT: [[VLD4Q_V:%.*]] = call { <4 x float>, <4 x float>, <4 x float>, <4 x float> } @llvm.arm.neon.vld4.v4f32.p0(ptr [[WIDE_PTR_PTR17]], i32 1) +// CHECK-NEXT: store { <4 x float>, <4 x float>, <4 x float>, <4 x float> } [[VLD4Q_V]], ptr [[WIDE_PTR_PTR3]], align 8 +// CHECK-NEXT: ret void +// +void test() { + float sign[64]; + float32x4x4_t r; + __builtin_neon_vld4q_v(&r, sign, 41); +} diff --git a/clang/test/BoundsSafety/CodeGen/nested-struct-member-count-O2.c b/clang/test/BoundsSafety/CodeGen/nested-struct-member-count-O2.c new file mode 100644 index 0000000000000..23f3e7a6dadfa --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/nested-struct-member-count-O2.c @@ -0,0 +1,84 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --version 4 + +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Inner { + int dummy; + int len; +}; + +struct Outer { + struct Inner hdr; + char fam[__counted_by(hdr.len)]; +}; + +// CHECK-LABEL: define dso_local i8 @access( +// CHECK-SAME: ptr noundef readonly captures(address) [[BAR:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw i8, ptr [[BAR]], i64 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw i8, ptr [[BAR]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN]], align 4, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[FAM]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[FAM]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[FAM]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP1]], !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT8:%.*]], label [[TRAP:%.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: [[TMP3:%.*]] = load i8, ptr [[ARRAYIDX]], align 1, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret i8 [[TMP3]] +// +char access(struct Outer *bar, int index) { + return bar->fam[index]; +} + + + +// CHECK-LABEL: define dso_local noundef nonnull ptr @assign( +// CHECK-SAME: ptr noundef readonly captures(none) [[BAR:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[BAR]], align 8, !nonnull {{![0-9]+}}, !noundef {{![0-9]+}} +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_BAR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BAR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_2_0_BAR_SROA_IDX]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[TMP0]], !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], !annotation {{![0-9]+}} +// CHECK-NEXT: unreachable, !annotation {{![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_BAR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[BAR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_BAR_SROA_IDX]], align 8, !tbaa {{![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP1_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP1_SROA_3_0_COPYLOAD]], [[AGG_TEMP1_SROA_0_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT18:%.*]], label [[TRAP]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: cont18: +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sgt i32 [[LEN]], -1, !annotation {{![0-9]+}} +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[AGG_TEMP1_SROA_2_0_COPYLOAD]] to i64, !annotation {{![0-9]+}} +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation {{![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext nneg i32 [[LEN]] to i64, !annotation {{![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp uge i64 [[FLEX_AVAIL_COUNT]], [[FLEX_COUNT_INTPTR]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND49:%.*]] = select i1 [[FLEX_COUNT_MINUS]], i1 [[FLEX_COUNT_CHECK]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_2_0_COPYLOAD]], !annotation {{![0-9]+}} +// CHECK-NEXT: [[OR_COND60:%.*]] = select i1 [[OR_COND49]], i1 [[TMP3]], i1 false, !annotation {{![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND60]], label [[BOUNDSCHECK_CONT:%.*]], label [[TRAP]], !prof {{![0-9]+}}, !annotation {{![0-9]+}} +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[LEN31:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], i64 4 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN31]], align 4, !tbaa {{![0-9]+}} +// CHECK-NEXT: ret ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] +// +struct Outer * assign(void * __bidi_indexable bar, int len) { + struct Outer * __single s = (struct Outer *) bar; + s->hdr.len = len; + return s; +} diff --git a/clang/test/BoundsSafety/CodeGen/nested-struct-member-count.c b/clang/test/BoundsSafety/CodeGen/nested-struct-member-count.c new file mode 100644 index 0000000000000..b6be10a157cc2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/nested-struct-member-count.c @@ -0,0 +1,271 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct Inner { + int dummy; + int len; +}; + +struct Outer { + struct Inner hdr; + char fam[__counted_by(hdr.len)]; +}; + +// CHECK-LABEL: define dso_local i8 @access( +// CHECK-SAME: ptr noundef [[BAR:%.*]], i32 noundef [[INDEX:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BAR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[INDEX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[BAR]], ptr [[BAR_ADDR]], align 8 +// CHECK-NEXT: store i32 [[INDEX]], ptr [[INDEX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BAR_ADDR]], align 8 +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM]], i64 0, i64 0 +// CHECK-NEXT: [[FAM2:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY3:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM2]], i64 0, i64 0 +// CHECK-NEXT: [[HDR:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_INNER:%.*]], ptr [[HDR]], i32 0, i32 1 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY3]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[INDEX_ADDR]], align 4 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP9]] to i64 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR5]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[TMP10]], [[WIDE_PTR_UB7]], !annotation [[META2:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP11]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[TMP10]], [[WIDE_PTR_LB]], !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT9:%.*]], label [[TRAP8:%.*]], !annotation [[META3]] +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont9: +// CHECK-NEXT: [[TMP13:%.*]] = load i8, ptr [[TMP10]], align 1 +// CHECK-NEXT: ret i8 [[TMP13]] +// +char access(struct Outer *bar, int index) { + return bar->fam[index]; +} + + +// CHECK-LABEL: define dso_local ptr @assign( +// CHECK-SAME: ptr noundef [[BAR:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BAR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[S:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: store ptr [[BAR]], ptr [[BAR_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[BAR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK:%.*]] = icmp ne ptr [[WIDE_PTR_PTR5]], null, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK]], label [[FLEX_BASE_NONNULL:%.*]], label [[CONT28:%.*]], !annotation [[META4]] +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_OUTER:%.*]], ptr [[WIDE_PTR_PTR5]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[WIDE_PTR_PTR5]], [[TMP4]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META5]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP10]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr [[STRUCT_OUTER]], ptr [[WIDE_PTR_PTR12]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[TMP6]], [[WIDE_PTR_UB14]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP7]], label [[CONT18:%.*]], label [[TRAP17:%.*]], !annotation [[META2]] +// CHECK: trap17: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont18: +// CHECK-NEXT: [[TMP8:%.*]] = icmp ule ptr [[WIDE_PTR_LB16]], [[WIDE_PTR_PTR12]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP8]], label [[CONT20:%.*]], label [[TRAP19:%.*]], !annotation [[META3]] +// CHECK: trap19: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont20: +// CHECK-NEXT: [[FAM:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[WIDE_PTR_PTR12]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM]], i64 0, i64 0 +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sle i32 0, [[TMP3]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label [[CONT22:%.*]], label [[TRAP21:%.*]], !annotation [[META6]] +// CHECK: trap21: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: cont22: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[ARRAYDECAY]], [[WIDE_PTR_UB7]], !annotation [[META7:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT24:%.*]], label [[TRAP23:%.*]], !annotation [[META7]] +// CHECK: trap23: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META7]] +// CHECK-NEXT: unreachable, !annotation [[META7]] +// CHECK: cont24: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR5]], [[WIDE_PTR_LB9]], !annotation [[META8:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT26:%.*]], label [[TRAP25:%.*]], !annotation [[META8]] +// CHECK: trap25: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META8]] +// CHECK-NEXT: unreachable, !annotation [[META8]] +// CHECK: cont26: +// CHECK-NEXT: [[UPPER_INTPTR:%.*]] = ptrtoint ptr [[WIDE_PTR_UB7]] to i64, !annotation [[META9:![0-9]+]] +// CHECK-NEXT: [[FAM_INTPTR:%.*]] = ptrtoint ptr [[ARRAYDECAY]] to i64, !annotation [[META9]] +// CHECK-NEXT: [[FLEX_AVAIL_COUNT:%.*]] = sub nuw i64 [[UPPER_INTPTR]], [[FAM_INTPTR]], !annotation [[META9]] +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext i32 [[TMP3]] to i64, !annotation [[META9]] +// CHECK-NEXT: [[FLEX_COUNT_CHECK:%.*]] = icmp ule i64 [[FLEX_COUNT_INTPTR]], [[FLEX_AVAIL_COUNT]], !annotation [[META9]] +// CHECK-NEXT: br i1 [[FLEX_COUNT_CHECK]], label [[CONT28]], label [[TRAP27:%.*]], !annotation [[META9]] +// CHECK: trap27: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META9]] +// CHECK-NEXT: unreachable, !annotation [[META9]] +// CHECK: cont28: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP11]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT38:%.*]], !annotation [[META4]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR30]], [[WIDE_PTR_UB32]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT36:%.*]], label [[TRAP35:%.*]], !annotation [[META2]] +// CHECK: trap35: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont36: +// CHECK-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR30]], [[WIDE_PTR_LB34]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT38]], label [[TRAP37:%.*]], !annotation [[META3]] +// CHECK: trap37: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont38: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR30]], ptr [[S]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[S]], align 8 +// CHECK-NEXT: [[HDR:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP14]], i32 0, i32 0 +// CHECK-NEXT: [[LEN39:%.*]] = getelementptr inbounds nuw [[STRUCT_INNER:%.*]], ptr [[HDR]], i32 0, i32 1 +// CHECK-NEXT: store i32 [[TMP3]], ptr [[LEN39]], align 4 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[S]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = icmp ne ptr [[TMP15]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP16]], label [[BOUNDSCHECK_NOTNULL41:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META4]] +// CHECK: boundscheck.notnull41: +// CHECK-NEXT: [[FAM42:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP15]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY43:%.*]] = getelementptr inbounds [0 x i8], ptr [[FAM42]], i64 0, i64 0 +// CHECK-NEXT: [[HDR44:%.*]] = getelementptr inbounds nuw [[STRUCT_OUTER]], ptr [[TMP15]], i32 0, i32 0 +// CHECK-NEXT: [[LEN45:%.*]] = getelementptr inbounds nuw [[STRUCT_INNER]], ptr [[HDR44]], i32 0, i32 1 +// CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[LEN45]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP17]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[ARRAYDECAY43]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP20]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP23]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR47:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB51:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR50]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = icmp ne ptr [[WIDE_PTR_PTR47]], null, !annotation [[META4]] +// CHECK-NEXT: br i1 [[TMP24]], label [[BOUNDSCHECK_NOTNULL52:%.*]], label [[CONT56:%.*]], !annotation [[META4]] +// CHECK: boundscheck.notnull52: +// CHECK-NEXT: [[TMP25:%.*]] = icmp ult ptr [[WIDE_PTR_PTR47]], [[WIDE_PTR_UB49]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP25]], label [[CONT54:%.*]], label [[TRAP53:%.*]], !annotation [[META2]] +// CHECK: trap53: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont54: +// CHECK-NEXT: [[TMP26:%.*]] = icmp uge ptr [[WIDE_PTR_PTR47]], [[WIDE_PTR_LB51]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP26]], label [[CONT56]], label [[TRAP55:%.*]], !annotation [[META3]] +// CHECK: trap55: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont56: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR47]] +// +struct Outer * assign(void * __bidi_indexable bar, int len) { + struct Outer * __single s = (struct Outer *) bar; + s->hdr.len = len; + return s; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[META4]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META5]] = !{!"bounds-safety-check-one-past-end-overflow"} +// CHECK: [[META6]] = !{!"bounds-safety-check-count-negative"} +// CHECK: [[META7]] = !{!"bounds-safety-check-flexible-count-gt-bounds", !"bounds-safety-check-ptr-le-upper-bound"} +// CHECK: [[META8]] = !{!"bounds-safety-check-flexible-count-gt-bounds", !"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK: [[META9]] = !{!"bounds-safety-check-flexible-count-gt-bounds"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/noalias-attribute.c b/clang/test/BoundsSafety/CodeGen/noalias-attribute.c new file mode 100644 index 0000000000000..18a1ceca8c26e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/noalias-attribute.c @@ -0,0 +1,62 @@ + +// RUN: %clang_cc1 -O0 -triple x86_64 -std=gnu99 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -std=gnu99 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// Check if 'noalias' attribute is added to thin pointers and dropped for wide pointers. + +// __bidi_indexable + +// CHECK-DAG: declare void @bidi_indexable_callee(ptr dead_on_unwind writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8, ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8) +int *__bidi_indexable bidi_indexable_callee(int *restrict __bidi_indexable p) __attribute__((malloc)); + +// CHECK-DAG: define dso_local void @bidi_indexable_caller(ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 %agg.result, ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 %p) +int *__bidi_indexable bidi_indexable_caller(int *restrict __bidi_indexable p) { + // CHECK-DAG: call void @bidi_indexable_callee(ptr dead_on_unwind writable sret(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[_:.*]], ptr noundef byval(%"__bounds_safety::wide_ptr.bidi_indexable") align 8 [[_:.*]]) + return bidi_indexable_callee(p); +} + +// __indexable + +// CHECK-DAG: declare { ptr, ptr } @indexable_callee(ptr noundef, ptr noundef) +int *__indexable indexable_callee(int *restrict __indexable p) __attribute__((malloc)); + +// CHECK-DAG: define dso_local { ptr, ptr } @indexable_caller(ptr noundef %p.coerce0, ptr noundef %p.coerce1) +int *__indexable indexable_caller(int *restrict __indexable p) { + // CHECK-DAG: call { ptr, ptr } @indexable_callee(ptr noundef [[_:.*]], ptr noundef [[_:.*]]) + return indexable_callee(p); +} + +// __single + +// CHECK-DAG: declare noalias ptr @single_callee(ptr noundef) +int *__single single_callee(int *restrict __single p) __attribute__((malloc)); + +// CHECK-DAG: define dso_local ptr @single_caller(ptr noalias noundef %p) +int *__single single_caller(int *restrict __single p) { + // CHECK-DAG: call noalias ptr @single_callee(ptr noundef [[_:.*]]) + return single_callee(p); +} + +// __unsafe_indexable + +// CHECK-DAG: declare noalias ptr @unsafe_indexable_callee(ptr noundef) +int *__unsafe_indexable unsafe_indexable_callee(int *restrict __unsafe_indexable p) __attribute__((malloc)); + +// CHECK-DAG: define dso_local ptr @unsafe_indexable_caller(ptr noalias noundef %p) +int *__unsafe_indexable unsafe_indexable_caller(int *restrict __unsafe_indexable p) { + // CHECK-DAG: call noalias ptr @unsafe_indexable_callee(ptr noundef [[_:.*]]) + return unsafe_indexable_callee(p); +} + +// __counted_by + +// CHECK-DAG: declare noalias ptr @counted_by_callee(ptr noundef) +int *__counted_by(8) counted_by_callee(int *restrict __counted_by(8) p) __attribute__((malloc)); + +// CHECK-DAG: define dso_local ptr @counted_by_caller(ptr noalias noundef %p) +int *__counted_by(8) counted_by_caller(int *restrict __counted_by(8) p) { + // CHECK-DAG: call noalias ptr @counted_by_callee(ptr noundef [[_:.*]]) + return counted_by_callee(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/null-to-bound.c b/clang/test/BoundsSafety/CodeGen/null-to-bound.c new file mode 100644 index 0000000000000..26a3ad03a4d70 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/null-to-bound.c @@ -0,0 +1,20 @@ + + +// RUN: %clang_cc1 -O0 -triple arm64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int main() { + int *__bidi_indexable ptrBound = 0; + int *__indexable ptrArray = 0; + + return 0; +} + +// CHECK: [[BOUND_STRUCT:%.*]] = type { ptr, ptr, ptr } +// CHECK: [[ARRAY_STRUCT:%.*]] = type { ptr, ptr } +// CHECK: [[PTR_BOUND:%.*]] = alloca [[BOUND_STRUCT]], align 8 +// CHECK: [[PTR_ARRAY:%.*]] = alloca [[ARRAY_STRUCT]], align 8 +// CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[PTR_BOUND]], i8 0, i64 24, i1 false) +// CHECK: call void @llvm.memset.p0.i64(ptr align 8 [[PTR_ARRAY]], i8 0, i64 16, i1 false) diff --git a/clang/test/BoundsSafety/CodeGen/opaque-pointers-transition-convert-types.c b/clang/test/BoundsSafety/CodeGen/opaque-pointers-transition-convert-types.c new file mode 100644 index 0000000000000..f77a219562a86 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opaque-pointers-transition-convert-types.c @@ -0,0 +1,364 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple aarch64 %s -o - | FileCheck %s --check-prefix=NOPQ +// RUN: %clang_cc1 -opaque-pointers -O0 -fbounds-safety -emit-llvm -triple aarch64 %s -o - | FileCheck %s --check-prefixes=OPQ + +#include + +struct s1 { + int dummy; + _Bool flag; +}; + +// NOPQ-LABEL: @f1( +// NOPQ-NEXT: entry: +// NOPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// NOPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NOPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_S1:%.*]], ptr [[TMP0]], i32 0, i32 1 +// NOPQ-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 1 +// NOPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPQ-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// NOPQ-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPQ-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// NOPQ-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NOPQ-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NOPQ-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NOPQ-NEXT: [[TMP6:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// NOPQ-NEXT: br i1 [[TMP6]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// NOPQ: boundscheck.notnull: +// NOPQ-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// NOPQ: trap: +// NOPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: unreachable +// NOPQ: cont: +// NOPQ-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: br i1 [[TMP8]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// NOPQ: trap1: +// NOPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: unreachable +// NOPQ: cont2: +// NOPQ-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +// OPQ-LABEL: @f1( +// OPQ-NEXT: entry: +// OPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// OPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// OPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// OPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_S1:%.*]], ptr [[TMP0]], i32 0, i32 1 +// OPQ-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 1 +// OPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPQ-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// OPQ-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPQ-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// OPQ-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPQ-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// OPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// OPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// OPQ-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPQ-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// OPQ-NEXT: [[TMP6:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// OPQ-NEXT: br i1 [[TMP6]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], {{!annotation ![0-9]+}} +// OPQ: boundscheck.notnull: +// OPQ-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// OPQ-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// OPQ: trap: +// OPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// OPQ-NEXT: unreachable +// OPQ: cont: +// OPQ-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// OPQ-NEXT: br i1 [[TMP8]], label [[CONT2]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// OPQ: trap1: +// OPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// OPQ-NEXT: unreachable +// OPQ: cont2: +// OPQ-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +_Bool *f1(struct s1 *p) { + return &p->flag; +} + +// NOPQ-LABEL: @f2( +// NOPQ-NEXT: entry: +// NOPQ-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// NOPQ-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// NOPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NOPQ-NEXT: store ptr [[P:%.*]], ptr [[P_INDIRECT_ADDR]], align 8 +// NOPQ-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// NOPQ-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// NOPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NOPQ-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// NOPQ-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// NOPQ-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// NOPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// NOPQ-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NOPQ-NEXT: [[TMP2:%.*]] = icmp ult ptr [[TMP1]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// NOPQ: trap: +// NOPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: unreachable +// NOPQ: cont: +// NOPQ-NEXT: [[TMP3:%.*]] = icmp uge ptr [[TMP1]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: br i1 [[TMP3]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// NOPQ: trap1: +// NOPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// NOPQ-NEXT: unreachable +// NOPQ: cont2: +// NOPQ-NEXT: [[TMP4:%.*]] = load i8, ptr [[TMP1]], align 1 +// NOPQ-NEXT: [[TOBOOL:%.*]] = trunc i8 [[TMP4]] to i1 +// NOPQ-NEXT: ret i1 [[TOBOOL]] +// +// OPQ-LABEL: @f2( +// OPQ-NEXT: entry: +// OPQ-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// OPQ-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// OPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OPQ-NEXT: store ptr [[P:%.*]], ptr [[P_INDIRECT_ADDR]], align 8 +// OPQ-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// OPQ-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// OPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// OPQ-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// OPQ-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// OPQ-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// OPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// OPQ-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPQ-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// OPQ-NEXT: [[TMP2:%.*]] = icmp ult ptr [[TMP1]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// OPQ-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// OPQ: trap: +// OPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// OPQ-NEXT: unreachable +// OPQ: cont: +// OPQ-NEXT: [[TMP3:%.*]] = icmp uge ptr [[TMP1]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// OPQ-NEXT: br i1 [[TMP3]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// OPQ: trap1: +// OPQ-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// OPQ-NEXT: unreachable +// OPQ: cont2: +// OPQ-NEXT: [[TMP4:%.*]] = load i8, ptr [[TMP1]], align 1 +// OPQ-NEXT: [[TOBOOL:%.*]] = trunc i8 [[TMP4]] to i1 +// OPQ-NEXT: ret i1 [[TOBOOL]] +// +_Bool f2(_Bool *__bidi_indexable p, int i) { + return p[i]; +} + +// NOPQ-LABEL: @f3( +// NOPQ-NEXT: entry: +// NOPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// NOPQ-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// NOPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// NOPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: store i32 [[SIZE:%.*]], ptr [[SIZE_ADDR]], align 4 +// NOPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4 +// NOPQ-NEXT: [[CONV:%.*]] = zext i32 [[TMP1]] to i64 +// NOPQ-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[TMP0]], i64 [[CONV]] +// NOPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPQ-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// NOPQ-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPQ-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// NOPQ-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// NOPQ-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// NOPQ-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// NOPQ-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// NOPQ-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP6]], align 8 +// NOPQ-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// NOPQ-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP7]], align 8 +// NOPQ-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// NOPQ-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP8]], align 8 +// NOPQ-NEXT: ret void +// +// OPQ-LABEL: @f3( +// OPQ-NEXT: entry: +// OPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// OPQ-NEXT: [[SIZE_ADDR:%.*]] = alloca i32, align 4 +// OPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// OPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// OPQ-NEXT: store i32 [[SIZE:%.*]], ptr [[SIZE_ADDR]], align 4 +// OPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// OPQ-NEXT: [[TMP1:%.*]] = load i32, ptr [[SIZE_ADDR]], align 4 +// OPQ-NEXT: [[CONV:%.*]] = zext i32 [[TMP1]] to i64 +// OPQ-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[TMP0]], i64 [[CONV]] +// OPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPQ-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// OPQ-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPQ-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// OPQ-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPQ-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// OPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// OPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// OPQ-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPQ-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// OPQ-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// OPQ-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP6]], align 8 +// OPQ-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// OPQ-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP7]], align 8 +// OPQ-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// OPQ-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP8]], align 8 +// OPQ-NEXT: ret void +// +_Bool *__bidi_indexable f3(_Bool *p, unsigned size) { + return __unsafe_forge_bidi_indexable(_Bool *, p, size); +} + +// NOPQ-LABEL: @f4( +// NOPQ-NEXT: entry: +// NOPQ-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// NOPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// NOPQ-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// NOPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// NOPQ-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// NOPQ: terminated_by.loop_cond: +// NOPQ-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// NOPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// NOPQ-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i8, ptr [[TMP1]], align 1 +// NOPQ-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i8 [[TERMINATED_BY_ELEM]], 0 +// NOPQ-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// NOPQ: terminated_by.loop_cont: +// NOPQ-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// NOPQ-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// NOPQ-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// NOPQ: terminated_by.loop_end: +// NOPQ-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// NOPQ-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TMP2]] +// NOPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// NOPQ-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// NOPQ-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// NOPQ-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP4]], align 8 +// NOPQ-NEXT: [[TMP5:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// NOPQ-NEXT: ret [2 x i64] [[TMP5]] +// +// OPQ-LABEL: @f4( +// OPQ-NEXT: entry: +// OPQ-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// OPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// OPQ-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// OPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// OPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// OPQ-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// OPQ-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// OPQ: terminated_by.loop_cond: +// OPQ-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// OPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// OPQ-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i8, ptr [[TMP1]], align 1 +// OPQ-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i8 [[TERMINATED_BY_ELEM]], 0 +// OPQ-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// OPQ: terminated_by.loop_cont: +// OPQ-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// OPQ-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// OPQ-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// OPQ: terminated_by.loop_end: +// OPQ-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// OPQ-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TMP2]] +// OPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// OPQ-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// OPQ-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// OPQ-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP4]], align 8 +// OPQ-NEXT: [[TMP5:%.*]] = load [2 x i64], ptr [[RETVAL]], align 8 +// OPQ-NEXT: ret [2 x i64] [[TMP5]] +// +_Bool *__indexable f4(_Bool *__null_terminated p) { + return __null_terminated_to_indexable(p); +} + +// NOPQ-LABEL: @f5( +// NOPQ-NEXT: entry: +// NOPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// NOPQ-NEXT: [[L:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// NOPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// NOPQ-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 1 +// NOPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[L]], i32 0, i32 0 +// NOPQ-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// NOPQ-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[L]], i32 0, i32 1 +// NOPQ-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// NOPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[L]], i32 0, i32 2 +// NOPQ-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// NOPQ-NEXT: ret void +// +// OPQ-LABEL: @f5( +// OPQ-NEXT: entry: +// OPQ-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// OPQ-NEXT: [[L:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OPQ-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// OPQ-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// OPQ-NEXT: [[UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 1 +// OPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[L]], i32 0, i32 0 +// OPQ-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// OPQ-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[L]], i32 0, i32 1 +// OPQ-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// OPQ-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[L]], i32 0, i32 2 +// OPQ-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// OPQ-NEXT: ret void +// +void f5(_Bool *p) { + _Bool *l = p; +} + +// no trap +// NOPQ-LABEL: @f6( +// NOPQ-NEXT: entry: +// NOPQ-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// NOPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// NOPQ-NEXT: store [2 x i64] [[P_COERCE:%.*]], ptr [[P]], align 8 +// NOPQ-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 16, i1 false) +// NOPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// NOPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// NOPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// NOPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// NOPQ-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// NOPQ-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// NOPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// NOPQ-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// NOPQ-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// NOPQ-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// NOPQ-NEXT: ret void +// +// OPQ-LABEL: @f6( +// OPQ-NEXT: entry: +// OPQ-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// OPQ-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// OPQ-NEXT: store [2 x i64] [[P_COERCE:%.*]], ptr [[P]], align 8 +// OPQ-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 16, i1 false) +// OPQ-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPQ-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// OPQ-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPQ-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// OPQ-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT:%.*]], i32 0, i32 0 +// OPQ-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// OPQ-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 1 +// OPQ-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// OPQ-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_RESULT]], i32 0, i32 2 +// OPQ-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// OPQ-NEXT: ret void +// +_Bool *__bidi_indexable f6(_Bool *__indexable p) { + return p; +} + diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-missed-binop-overflow.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-missed-binop-overflow.c new file mode 100644 index 0000000000000..98ecbba52ed3f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-missed-binop-overflow.c @@ -0,0 +1,863 @@ + + +// RUN: %clang_cc1 -fbounds-safety -Os %s -triple arm64-apple-iphoneos -emit-llvm -o %t-Os.s -opt-record-file %t-Os.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-Os.opt.yaml --check-prefixes OPT-REM %s +// XFAIL: true +// FIXME: rdar://137714109 + +#include + +static inline int *__bidi_indexable is_counted_by(int * __counted_by(n) ptr, unsigned n) { + return ptr; +} + +void test_shl(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], 2 * N); + for(int i= 0 ; i < N; i++) { + t[2*i] = 1; + } +} + +void test_mul(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], 5 * N); + for(int i= 0 ; i < N; i++) { + t[2*i] = 1; + } +} + +void test_add(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], 2 + N); + for(int i= 0 ; i < N; i++) { + t[i] = 1; + } +} + +void test_sub(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], N - 2); + for(int i= 0 ; i < N; i++) { + t[i] = 1; + } +} + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 12, Column: 0 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 12, Column: 0 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '3' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 12, Column: 0 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 12, Column: 0 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 12, Column: 0 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 13, Column: 12 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 13, Column: 35 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''shl'')' +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 13, Column: 33 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 13, Column: 26 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 15, Column: 13 } +// OPT-REM-NEXT: Function: test_shl +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '5' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: other (LLVM IR 'shl') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 19, Column: 0 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 19, Column: 0 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '3' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 19, Column: 0 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 19, Column: 0 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 19, Column: 0 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 20, Column: 12 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 20, Column: 35 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''mul'')' +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 20, Column: 33 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 20, Column: 26 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 22, Column: 13 } +// OPT-REM-NEXT: Function: test_mul +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '5' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: other (LLVM IR 'shl') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 26, Column: 0 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 26, Column: 0 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 26, Column: 0 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 26, Column: 0 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 26, Column: 0 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 27, Column: 12 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 27, Column: 35 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''add'')' +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 27, Column: 33 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 27, Column: 26 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: test_add +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 33, Column: 0 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 33, Column: 0 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 33, Column: 0 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 33, Column: 0 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 33, Column: 0 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 34, Column: 12 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 34, Column: 35 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''add'')' +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 34, Column: 33 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 34, Column: 26 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-binop-overflow.c', +// OPT-REM-NEXT: Line: 36, Column: 11 } +// OPT-REM-NEXT: Function: test_sub +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-missed-ptr-induction.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-missed-ptr-induction.c new file mode 100644 index 0000000000000..c6601febc38ef --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-missed-ptr-induction.c @@ -0,0 +1,903 @@ + + +// RUN: %clang_cc1 -fbounds-safety -Os %s -triple arm64-apple-iphoneos -emit-llvm -o %t-Os.s -opt-record-file %t-Os.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-Os.opt.yaml --check-prefixes OPT-REM %s +// XFAIL: true +// FIXME: rdar://137714109 + +#include +static inline int *__bidi_indexable is_counted_by(int * __counted_by(n) ptr, unsigned n) { + return ptr; +} + +void ptr_induction_different_step_sign(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], N); + int *t_start = t; + for(int i = N - 1; i >= 0; i--) { + *t_start = 1; + t_start += 1; + } +} + +void ptr_induction_different_step_sign_2(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], N); + int *t_end = t + N - 1; + for(int i = 0; i < N; i++) { + *t_end = 1; + t_end -= 1; + } +} + +void ptr_induction_different_step_size(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], 2 * N); + for(int i = 0 ; i < N; i++) { + *t = 1; + t += 2; + } +} + +void ptr_induction_different_step_size2(int* __indexable A, int N) { + int *t = is_counted_by(&A[0], 3 * N); + for(int i = 0 ; i < N; i+=2) { + *t = 1; + t += 3; + } +} + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 11, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 11, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 11, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 11, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 11, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-phi-step-size +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 11, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 12, Column: 12 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 12, Column: 33 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 12, Column: 26 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 15, Column: 6 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-missed-optimization-phi-step-size, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: 'Cannot remove bound checks because the pointer induction variable and loop counter don''t have the same step size. Consider rewriting the loop counter to have the same step size as the pointer induction variable to help the optimizer remove the access bound checks' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 20, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 20, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 20, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 20, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 20, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-phi-step-size +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 20, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 21, Column: 12 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 21, Column: 33 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 21, Column: 26 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 24, Column: 6 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_sign_2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-missed-optimization-phi-step-size, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: 'Cannot remove bound checks because the pointer induction variable and loop counter don''t have the same step size. Consider rewriting the loop counter to have the same step size as the pointer induction variable to help the optimizer remove the access bound checks' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 29, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 29, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 29, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 29, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 29, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-phi-step-size +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 29, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 30, Column: 12 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 30, Column: 35 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''shl'')' +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 30, Column: 33 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 30, Column: 26 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 32, Column: 6 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-missed-optimization-phi-step-size, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: 'Cannot remove bound checks because the pointer induction variable and loop counter don''t have the same step size. Consider rewriting the loop counter to have the same step size as the pointer induction variable to help the optimizer remove the access bound checks' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 37, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '9' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 37, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 37, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 37, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 37, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-missed-optimization-phi-step-size +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 37, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '13' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 38, Column: 12 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ugt (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 38, Column: 35 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''mul'')' +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 38, Column: 33 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 38, Column: 26 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-missed-optimization-nuw +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "other (LLVM IR 'sub')\nother (LLVM IR 'ashr')" +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: Check can not be removed because the arithmetic operation might wrap in the unsigned sense. Optimize the check by adding conditions to check for overflow before doing the operation +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-missed-ptr-induction.c', +// OPT-REM-NEXT: Line: 40, Column: 6 } +// OPT-REM-NEXT: Function: ptr_induction_different_step_size2 +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-missed-optimization-phi-step-size, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-EMPTY: +// OPT-REM-NEXT: - String: "Missed Optimization Info\n" +// OPT-REM-NEXT: - String: 'Cannot remove bound checks because the pointer induction variable and loop counter don''t have the same step size. Consider rewriting the loop counter to have the same step size as the pointer induction variable to help the optimizer remove the access bound checks' +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-ptr-conversion-O0.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-ptr-conversion-O0.c new file mode 100644 index 0000000000000..fef4dd541ada6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-ptr-conversion-O0.c @@ -0,0 +1,318 @@ + + +// RUN: %clang_cc1 -triple x86_64-apple-macos -Wno-bounds-safety-init-list -fbounds-safety -O0 %s -emit-llvm -o %t-O0.s -opt-record-file %t-O0.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O0.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O0.opt.yaml --check-prefixes OPT-REM %s + +#include + +int main(int argc, char **argv) { + int len = 0; + char *__single __counted_by(len) dcp = *argv; + char *__single tp = dcp; +} + +// IR: define{{.*}} i32 @main(i32 noundef %argc, ptr noundef %argv) +// IR: store i32 %argc, ptr %[[ARGC_ALLOCA:[a-z0-9.]+]] +// IR: store ptr %argv, ptr %[[ARGV_ALLOCA:[a-z0-9.]+]] +// IR: %[[ARGV:[a-z0-9.]+]] = load ptr, ptr %[[ARGV_ALLOCA]]{{.*}} +// IR: %[[ARGV_DEREF:[a-z0-9.]+]] = load ptr, ptr %[[ARGV]]{{.*}} +// IR: %[[LEN_DEREF:[a-z0-9.]+]] = load i32, ptr %{{.*}}, !dbg ![[LOC_11_20:[0-9]+]] +// IR: %[[LEN_EXT:[a-z0-9.]+]] = sext i32 %[[LEN_DEREF]] to i64, !dbg ![[LOC_11_20]] +// IR: icmp ule ptr %[[ARGV_DEREF]], {{.*}}, !dbg ![[LOC_11_37:[0-9]+]] +// IR: br i1 {{.*}}, label %[[LABEL_CONT:[a-z0-9.]+]], label %[[LABEL_TRAP_RES:[a-z0-9.]+]], !dbg ![[LOC_11_37]] +// ... +// IR: [[LABEL_CONT]]: +// ... +// IR: icmp ule ptr {{.*}}, %[[ARGV_DEREF]], !dbg ![[LOC_11_44:[0-9]+]] +// IR: br i1 %{{.*}}, label %[[LABEL_CONT2:.+]], label %[[LABEL_TRAP_RES]], !dbg ![[LOC_11_44]] + +// IR: [[LABEL_CONT2]]: +// ... +// IR: icmp sle i64 %[[LEN_EXT]], {{.*}}, !dbg ![[LOC_11_44]] +// IR: br i1 %{{.*}}, label %[[LABEL_CONT3:.+]], label %[[LABEL_TRAP_RES2:.+]], !dbg ![[LOC_11_44]] + +// IR: [[LABEL_CONT3]]: +// IR: %[[LEN_CHECK_RES:[a-z0-9_]+]] = icmp sle i64 0, %[[LEN_EXT]], !dbg ![[LOC_11_44]] +// IR: br label %[[LABEL_TRAP_RES2]] + +// IR: [[LABEL_TRAP_RES2]]: +// IR: %[[TRAP_RES2:[a-z0-9_]+]] = phi i1 [ false, %[[LABEL_CONT2]] ], [ %[[LEN_CHECK_RES]], %[[LABEL_CONT3]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]] +// IR: br label %[[LABEL_TRAP_RES]] + +// IR: [[LABEL_TRAP_RES]]: +// IR: %[[TRAP_RES:[a-z0-9_]+]] = phi i1 [ false, %[[LABEL_CONT]] ], [ false, %entry ], [ %[[TRAP_RES2]], %[[LABEL_TRAP_RES2]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT:[0-9]+]] +// IR: br i1 %[[TRAP_RES]], label %[[LABEL_CONT4:[a-z0-9.]+]], label %[[LABEL_TRAP:[a-z0-9.]+]], !dbg ![[LOC_11_44]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR: [[LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_11_44:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_11_44]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR: [[LABEL_CONT4]]: +// IR: %[[NULL_CHECK_RES:[a-z0-9_]+]] = icmp ne ptr %[[WIDE_PTR:[a-z0-9_.]+]], null, !dbg ![[LOC_TMP2:[0-9]+]], !annotation ![[ANNOT1:[0-9]+]] +// IR: br i1 %[[NULL_CHECK_RES]], label %[[LABEL_CONT5:[a-z0-9.]+]], label %[[LABEL_END:[a-z0-9.]+]], !dbg ![[LOC_TMP:[0-9]+]], !annotation ![[ANNOT2:[0-9]+]] + +// IR: [[LABEL_CONT5]]: +// IR: %[[LT_CHECK_RES:[a-z0-9_]+]] = icmp ult ptr %[[WIDE_PTR]], {{.*}}, !dbg ![[LOC_TMP]], !annotation ![[ANNOT3:[0-9]+]] +// IR: br i1 %[[LT_CHECK_RES]], label %[[LABEL_CONT6:[a-z0-9.]+]], label %[[LABEL_TRAP2:[a-z0-9.]+]], !dbg ![[LOC_TMP]], !prof ![[PROFILE_METADATA]], !annotation ![[ANNOT3]] + +// IR: [[LABEL_TRAP2]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC2:[0-9]+]], !annotation ![[ANNOT_TRAP:[0-9]+]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC2]], !annotation ![[ANNOT_TRAP]] + +// IR: [[LABEL_CONT6]]: +// IR: %[[GE_CHECK_RES:[a-z0-9_]+]] = icmp uge ptr %[[WIDE_PTR]], {{.*}}, !dbg ![[LOC_TMP]], !annotation ![[ANNOT4:[0-9]+]] +// IR: br i1 %[[GE_CHECK_RES]], label %[[LABEL_CONT7:[a-z0-9.]+]], label %[[LABEL_TRAP3:[a-z0-9.]+]], !dbg ![[LOC_TMP]], !prof ![[PROFILE_METADATA]], !annotation ![[ANNOT4]] + +// IR: [[LABEL_END]]: + +// IR-DAG: ![[LOC_11_44]] = !DILocation(line: 11, column: 44 +// IR-DAG: ![[TRAP_LOC_11_44]] = !DILocation(line: 0, scope: ![[TRAP_INFO_11_44:[0-9]+]], inlinedAt: ![[LOC_11_44]]) +// IR-DAG: ![[TRAP_INFO_11_44]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$" +// +// IR-DAG: ![[LOC_11_20]] = !DILocation(line: 11, column: 20 + +// IR-DAG: ![[TRAP_LOC_MISSING]] = !DILocation(line: 0, scope: ![[MAIN_SCOPE:[0-9]+]]) +// IR-DAG: ![[MAIN_SCOPE]] = distinct !DISubprogram(name: "main", {{.*}} line: 9, {{.*}} scopeLine: 9 + +// IR-DAG: ![[ANNOT_CONV_TO_COUNT]] = !{!"bounds-safety-generic"} +// IR-DAG: ![[ANNOT_TRAP]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '43' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-neq-null +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '53' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 11, Column: 44 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '38' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'sub') +// OPT-REM-NEXT: cmp sle (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp sle (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''phi'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 12, Column: 25 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-neq-null, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ne (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-ptr-conversion-O2.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-ptr-conversion-O2.c new file mode 100644 index 0000000000000..fdda5f4c54263 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/bounds-safety-ptr-conversion-O2.c @@ -0,0 +1,147 @@ +// REQUIRES: system-darwin + +// RUN: %clang_cc1 -triple x86_64-apple-macos -Wno-bounds-safety-init-list -fbounds-safety -O2 %s -emit-llvm -o %t-O2.s -opt-record-file %t-O2.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O2.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O2.opt.yaml --check-prefixes OPT-REM %s + +#include + +int main(int argc, char **argv) { + int len = 0; + char *__single __counted_by(len) dcp = *argv; + char *__single tp = dcp; +} + +// IR: define{{.*}} i32 @main(i32 noundef %argc, ptr noundef readonly captures(none) %argv) +// IR: %[[ARGV_DEREF:[a-z0-9.]+]] = load ptr, ptr %argv, align 8, !dbg ![[LOC_11_44:[0-9]+]] +// IR: %[[NULL_CHECK:[a-z0-9.]+]] = icmp eq ptr %[[ARGV_DEREF]], null, !dbg ![[LOC_12_25:[0-9]+]], !annotation ![[NULL_CHECK_ANNOT:[0-9]+]] +// IR: br i1 %[[NULL_CHECK]], label %[[LABEL_CONT:[a-z0-9.]+]], label %[[LABEL_TRAP_RES:[a-z0-9.]+]], !dbg ![[LOC_12_25]], !annotation ![[NULL_CHECK_ANNOT]] + +// IR: [[LABEL_TRAP_RES]]: +// IR: tail call void @llvm.ubsantrap(i8 25) #2, !dbg ![[TRAP_LOC_MISSING:[0-9]+]], !annotation ![[ALL_TRAPS_ANNOT:[0-9]+]] +// IR: unreachable, !dbg ![[TRAP_LOC_MISSING]] + +// IR: [[LABEL_CONT]]: +// IR: ret i32 0, !dbg ![[LOC_13_1:[0-9]+]] + +// IR-DAG: ![[LOC_11_44]] = !DILocation(line: 11, column: 44 +// +// IR-DAG: ![[LOC_12_25]] = !DILocation(line: 12, column: 25 + +// IR-DAG: ![[TRAP_LOC_MISSING]] = !DILocation(line: 0, scope: ![[MAIN_SCOPE:[0-9]+]]) +// IR-DAG: ![[MAIN_SCOPE]] = distinct !DISubprogram(name: "main", {{.*}} line: 9, {{.*}} scopeLine: 9 + +// TODO: The annotations on the branch and trap instruction should match (rdar://109089053) +// IR-DAG: ![[ALL_TRAPS_ANNOT]] = !{!"bounds-safety-generic", !"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// IR-DAG: ![[NULL_CHECK_ANNOT]] = !{!"bounds-safety-check-ptr-neq-null"} + +// opt-rem checks auto-generated by gen-opt-remarks-check-lines.py + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-neq-null +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 12, Column: 25 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-neq-null +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "cmp eq (LLVM IR 'icmp')\ncond branch (LLVM IR 'br')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}bounds-safety-ptr-conversion-O2.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic, bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O0.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O0.c new file mode 100644 index 0000000000000..1741e2a00aaa9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O0.c @@ -0,0 +1,397 @@ + + +// RUN: %clang_cc1 -fbounds-safety -O0 %s -emit-llvm -o %t-O0.s -opt-record-file %t-O0.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O0.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O0.opt.yaml --check-prefixes OPT-REM %s + +#include + +void foo(int* __counted_by(n) array, unsigned n, unsigned idx) { + array[idx] = 42; +} + +int main(int argc, char **argv) { + unsigned n = argc; + int * a; + foo(a, n, argc - 1); +} + +// IR-LABEL: @foo +// ... +// IR: icmp ult {{.*}} !dbg ![[LOC_10_16:[0-9]+]], !annotation ![[ANNOT_LT_UB:[0-9]+]] +// IR: br i1 %{{[0-9]+}}, label %[[FOO_LABEL_CONT:[a-z0-9]+]], label %[[FOO_LABEL_TRAP:[a-z0-9]+]], !dbg ![[LOC_10_16]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation ![[ANNOT_LT_UB]] +// ... +// IR: [[FOO_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[LT_TRAP_LOC_10_16:[0-9]+]], !annotation ![[ANNOT_LT_UB]] +// IR-NEXT: unreachable, !dbg ![[LT_TRAP_LOC_10_16]], !annotation ![[ANNOT_LT_UB]] +// ... +// IR: [[FOO_LABEL_CONT]]: +// IR: icmp uge {{.*}} !dbg ![[LOC_10_16]], !annotation ![[ANNOT_GE_LB:[0-9]+]] +// IR: br i1 %{{[0-9]+}}, label %{{[a-z0-9]+}}, label %[[FOO_LABEL_TRAP2:[a-z0-9]+]], !dbg ![[LOC_10_16]], !prof ![[PROFILE_METADATA]], !annotation ![[ANNOT_GE_LB]] +// ... +// IR: [[FOO_LABEL_TRAP2]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[GE_TRAP_LOC_10_16:[0-9]+]], !annotation ![[ANNOT_GE_LB]] +// IR-NEXT: unreachable, !dbg ![[GE_TRAP_LOC_10_16]], !annotation ![[ANNOT_GE_LB]] + +// IR-LABEL: @main +// IR: entry +// ... +// IR-DAG: call void @llvm.memset{{.*}}, !annotation ![[ANNOT_AUTO_INIT:[0-9]+]] +// IR: icmp ule {{.*}} !dbg ![[LOC_16_5:[0-9]+]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT:[a-z0-9.]+]], label %[[MAIN_LABEL_TRAP_RES:[a-z0-9.]+]], !dbg ![[LOC_16_5]] + +// IR: [[MAIN_LABEL_CONT]]: +// ... +// IR: icmp ule {{.*}} !dbg ![[LOC_16_5]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT2:[a-z0-9.]+]], label %[[MAIN_LABEL_TRAP_RES]], !dbg ![[LOC_16_5]] + +// IR: [[MAIN_LABEL_CONT2]]: +// ... +// IR: %[[WIDTH_CHECK_RES:[a-z0-9_]+]] = icmp ule {{.*}} !dbg ![[LOC_16_5]] +// IR: br label %[[MAIN_LABEL_TRAP_RES]] + +// IR: [[MAIN_LABEL_TRAP_RES]]: +// IR: %[[TRAP_RES:[a-z0-9_]+]] = phi i1 [ false, %[[MAIN_LABEL_CONT]] ], [ false, %entry ], [ %[[WIDTH_CHECK_RES]], %[[MAIN_LABEL_CONT2]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT:[0-9]+]] +// IR: br i1 %[[TRAP_RES]], label {{.*}}, label %[[MAIN_LABEL_TRAP:[a-z0-9.]+]], !dbg ![[LOC_16_5]], !prof ![[PROFILE_METADATA]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR: [[MAIN_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_16_5:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_16_5]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR-DAG: ![[ANNOT_CONV_TO_COUNT]] = !{!"bounds-safety-generic"} +// IR-DAG: ![[ANNOT_AUTO_INIT]] = !{!"bounds-safety-zero-init"} + +// IR-DAG: ![[LOC_10_16]] = !DILocation(line: 10, column: 16{{.*}}) +// IR-DAG: ![[LT_TRAP_LOC_10_16]] = !DILocation(line: 0, scope: ![[LT_TRAP_INFO_10_16:[0-9]+]], inlinedAt: ![[LOC_10_16]]) +// IR-DAG: ![[LT_TRAP_INFO_10_16]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds" +// IR-DAG: ![[GE_TRAP_LOC_10_16]] = !DILocation(line: 0, scope: ![[GE_TRAP_INFO_10_16:[0-9]+]], inlinedAt: ![[LOC_10_16]]) +// IR-DAG: ![[GE_TRAP_INFO_10_16]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds" +// +// IR-DAG: ![[LOC_16_5]] = !DILocation(line: 16, column: 5 +// IR-DAG: ![[TRAP_LOC_16_5]] = !DILocation(line: 0, scope: ![[TRAP_INFO_16_5:[0-9]+]], inlinedAt: ![[LOC_16_5]]) +// IR-DAG: ![[TRAP_INFO_16_5]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$" + +// IR-DAG: ![[TRAP_LOC_MISSING]] = !DILocation(line: 0, scope: ![[MAIN_SCOPE:[0-9]+]]) +// IR-DAG: ![[MAIN_SCOPE]] = distinct !DISubprogram(name: "main", {{.*}} line: 13, {{.*}} scopeLine: 13 + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '8' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 10, Column: 16 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: - String: | +// OPT-REM: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-zero-init +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '69' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '70' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 15, Column: 11 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-zero-init +// OPT-REM-NEXT: - String: | +// OPT-REM: instructions: +// OPT-REM-NEXT: - String: 'call (LLVM IR ''call'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 16, Column: 9 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '58' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'sub') +// OPT-REM-NEXT: other (LLVM IR 'sdiv') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 16, Column: 5 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 16, Column: 12 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM: instructions: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM: instructions: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''phi'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O2.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O2.c new file mode 100644 index 0000000000000..521d644d04033 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-argc-O2.c @@ -0,0 +1,203 @@ + + +// RUN: %clang_cc1 -fbounds-safety -O2 %s -emit-llvm -o %t-O2.s -opt-record-file %t-O2.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O2.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O2.opt.yaml --check-prefixes OPT-REM %s + +#include + +void foo(int* __counted_by(n) array, unsigned n, unsigned idx) { + array[idx] = 42; +} + +int main(int argc, char **argv) { + unsigned n = argc; + int * a; + foo(a, n, argc - 1); +} + +// IR-LABEL: @foo +// IR: entry +// IR-NEXT: %[[COUNT_EXT:[a-z0-9.]+]] = zext i32 {{.*}} to i64, !dbg ![[LOC_10_5:[0-9]+]] +// IR-NEXT: %[[UPPER_BOUND:[a-z0-9.]+]] = getelementptr inbounds nuw i32, ptr %[[ARR:[a-z0-9.]+]], i64 %[[COUNT_EXT]], !dbg ![[LOC_10_5]] +// IR-NEXT: %[[IDX_EXT:[a-z0-9.]+]] = zext i32 {{.*}} to i64, !dbg ![[LOC_10_16:[0-9]+]] +// IR-NEXT: %[[PTR:[a-z0-9.]+]] = getelementptr i32, ptr %[[ARR]], i64 %[[IDX_EXT]], !dbg ![[LOC_10_16]] +// IR-NEXT: %[[UPPER_CHECK:[a-z0-9.]+]] = icmp ult ptr %[[PTR]], %[[UPPER_BOUND]], !dbg ![[LOC_10_16]], !annotation ![[ANNOT_LT_UB:[0-9]+]] +// IR-NEXT: %[[LOWER_CHECK:[a-z0-9.]+]] = icmp uge ptr %[[PTR]], %[[ARR]], !dbg ![[LOC_10_16]], !annotation ![[ANNOT_GE_LB:[0-9]+]] +// TODO: The condition and branch should also be ANNOT_LT_UB_OR_GT_LB (rdar://109089053) +// IR-NEXT: %[[COND:[a-z0-9.]+]] = and i1 %[[UPPER_CHECK]], %[[LOWER_CHECK]], !dbg ![[LOC_10_16]], !annotation ![[ANNOT_GE_LB]] +// IR-NEXT: br i1 %{{[a-z.0-9]+}}, label %[[FOO_LABEL_CONT:[a-z0-9]+]], label %[[FOO_LABEL_TRAP:[a-z0-9]+]], !dbg ![[LOC_10_16]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation ![[ANNOT_LT_UB]] +// ... +// IR: [[FOO_LABEL_TRAP]]: +// IR-NEXT: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[LT_TRAP_LOC_10_16:[0-9]+]], !annotation ![[ANNOT_LT_UB_OR_GE_LB:[0-9]+]] +// ... +// IR: [[FOO_LABEL_CONT]]: +// IR-NEXT: store {{.*}} !dbg ![[LOC_10_16]] +// IR-NEXT: ret + +// IR-LABEL: @main +// IR: entry +// IR-NEXT: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_16_5:[0-9]+]] + +// IR-DAG: ![[LOC_10_5]] = !DILocation(line: 10, column: 5 +// IR-DAG: ![[LOC_10_16]] = !DILocation(line: 10, column: 16 +// IR-DAG: ![[ANNOT_LT_UB]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// IR-DAG: ![[ANNOT_GE_LB]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// IR-DAG: ![[ANNOT_LT_UB_OR_GE_LB]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} + +// opt-rem checks autogenerated by gen-opt-remarks-check-lines.py + +// OPT-REM: --- !Passed +// OPT-REM-NEXT: Pass: inline +// OPT-REM-NEXT: Name: Inlined +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 16, Column: 5 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: '''' +// OPT-REM-NEXT: - Callee: foo +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: - String: ''' inlined into ''' +// OPT-REM-NEXT: - Caller: main +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: - String: '''' +// OPT-REM-NEXT: - String: ' with ' +// OPT-REM-NEXT: - String: '(cost=' +// OPT-REM-NEXT: - Cost: '-15' +// OPT-REM-NEXT: - String: ', threshold=' +// OPT-REM-NEXT: - Threshold: '225' +// OPT-REM-NEXT: - String: ')' +// OPT-REM-NEXT: - String: ' at callsite ' +// OPT-REM-NEXT: - String: main +// OPT-REM-NEXT: - String: ':' +// OPT-REM-NEXT: - Line: '3' +// OPT-REM-NEXT: - String: ':' +// OPT-REM-NEXT: - Column: '5' +// OPT-REM-NEXT: - String: ';' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 10, Column: 16 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: trap (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'unreachable') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-argc-O2.c', +// OPT-REM-NEXT: Line: 16, Column: 5 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM: instructions: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''unreachable'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O0.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O0.c new file mode 100644 index 0000000000000..95b104d1ae7dc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O0.c @@ -0,0 +1,413 @@ + + +// RUN: %clang_cc1 -fbounds-safety -O0 %s -emit-llvm -o %t-O0.s -opt-record-file %t-O0.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O0.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O0.opt.yaml --check-prefixes OPT-REM %s + +#include + +void foo(int* __counted_by(n) array, unsigned n) { + array[6] = 42; +} + +int main() { + unsigned n = 5; + int * a; + foo(a, n); +} + +// IR-LABEL: @foo +// ... +// IR: icmp ult {{.*}} !dbg ![[LOC_10_14:[0-9]+]], !annotation ![[ANNOT_LT_UB:[0-9]+]] +// IR: br i1 %{{[0-9]+}}, label %[[FOO_LABEL_CONT:[a-z0-9]+]], label %[[FOO_LABEL_TRAP:[a-z0-9]+]], !dbg ![[LOC_10_14]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation ![[ANNOT_LT_UB]] +// ... +// IR: [[FOO_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[LT_TRAP_LOC_10_14:[0-9]+]], !annotation ![[ANNOT_LT_UB]] +// IR-NEXT: unreachable, !dbg ![[LT_TRAP_LOC_10_14]], !annotation ![[ANNOT_LT_UB]] +// ... +// IR: [[FOO_LABEL_CONT]]: +// IR: br i1 %{{[0-9]+}}, label %{{[a-z0-9]+}}, label %[[FOO_LABEL_TRAP2:[a-z0-9]+]], !dbg ![[LOC_10_14]], !prof ![[PROFILE_METADATA]], !annotation ![[ANNOT_GE_LB:[0-9]+]] +// ... +// IR: [[FOO_LABEL_TRAP2]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[GT_TRAP_LOC_10_14:[0-9]+]], !annotation ![[ANNOT_GE_LB]] +// IR-NEXT: unreachable, !dbg ![[GT_TRAP_LOC_10_14]], !annotation ![[ANNOT_GE_LB]] + + +// IR-LABEL: @main +// IR: entry +// ... +// IR-DAG: call void @llvm.memset{{.*}}, !annotation ![[ANNOT_AUTO_INIT:[0-9]+]] +// IR: icmp ule {{.*}} !dbg ![[LOC_16_5:[0-9]+]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT:[a-z0-9.]+]], label %[[MAIN_LABEL_TRAP_RES:[a-z0-9.]+]], !dbg ![[LOC_16_5]] + +// IR: [[MAIN_LABEL_CONT]]: +// ... +// IR: icmp ule {{.*}} !dbg ![[LOC_16_5]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT2:[a-z0-9.]+]], label %[[MAIN_LABEL_TRAP_RES]], !dbg ![[LOC_16_5]] + +// IR: [[MAIN_LABEL_CONT2]]: +// ... +// IR: %[[WIDTH_CHECK_RES:[a-z0-9_]+]] = icmp ule {{.*}} !dbg ![[LOC_16_5]] +// IR: br label %[[MAIN_LABEL_TRAP_RES]] + +// IR: [[MAIN_LABEL_TRAP_RES]]: +// IR: %[[TRAP_RES:[a-z0-9_]+]] = phi i1 [ false, %[[MAIN_LABEL_CONT]] ], [ false, %entry ], [ %[[WIDTH_CHECK_RES]], %[[MAIN_LABEL_CONT2]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT:[0-9]+]] +// IR: br i1 %[[TRAP_RES]], label {{.*}}, label %[[MAIN_LABEL_TRAP:[a-z0-9.]+]], !dbg ![[LOC_16_5]], !prof ![[PROFILE_METADATA]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR: [[MAIN_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_16_5:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_16_5]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR-DAG: ![[ANNOT_CONV_TO_COUNT]] = !{!"bounds-safety-generic"} +// IR-DAG: ![[ANNOT_AUTO_INIT]] = !{!"bounds-safety-zero-init"} + +// IR-DAG: ![[LOC_10_14]] = !DILocation(line: 10, column: 14{{.*}}) +// IR-DAG: ![[LT_TRAP_LOC_10_14]] = !DILocation(line: 0, scope: ![[LT_TRAP_INFO_10_14:[0-9]+]], inlinedAt: ![[LOC_10_14]]) +// IR-DAG: ![[LT_TRAP_INFO_10_14]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds" +// IR-DAG: ![[GT_TRAP_LOC_10_14]] = !DILocation(line: 0, scope: ![[GT_TRAP_INFO_10_14:[0-9]+]], inlinedAt: ![[LOC_10_14]]) +// IR-DAG: ![[GT_TRAP_INFO_10_14]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds" +// IR-DAG: ![[LOC_16_5]] = !DILocation(line: 16, column: 5 +// IR-DAG: ![[TRAP_LOC_16_5]] = !DILocation(line: 0, scope: ![[TRAP_INFO_16_5:[0-9]+]], inlinedAt: ![[LOC_16_5]]) +// IR-DAG: ![[TRAP_INFO_16_5]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$" + +// IR-DAG: ![[TRAP_LOC_MISSING]] = !DILocation(line: 0, scope: ![[MAIN_SCOPE:[0-9]+]]) +// IR-DAG: ![[MAIN_SCOPE]] = distinct !DISubprogram(name: "main", {{.*}} line: 13, {{.*}} scopeLine: 13 + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '8' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 10, Column: 14 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-zero-init +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '69' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '70' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 15, Column: 11 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-zero-init +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: 'call (LLVM IR ''call'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 16, Column: 9 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '58' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'store') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'sub') +// OPT-REM-NEXT: other (LLVM IR 'sdiv') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 16, Column: 5 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 16, Column: 12 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''zext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''phi'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O2.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O2.c new file mode 100644 index 0000000000000..736aef8740092 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-bounds-const-O2.c @@ -0,0 +1,157 @@ + + +// RUN: %clang_cc1 -fbounds-safety -O2 %s -emit-llvm -o %t-O2.s -opt-record-file %t-O2.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O2.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O2.opt.yaml --check-prefixes OPT-REM %s + +#include + +void foo(int* __counted_by(n) array, unsigned n) { + array[6] = 42; +} + +int main() { + unsigned n = 5; + int * a; + foo(a, n); +} + +// IR-LABEL: @foo +// ... +// IR-DAG: icmp ult ptr {{.*}}, !dbg {{.+}}, !annotation ![[ANNOT_LT_UB:[0-9]+]] +// IR-DAG: icmp uge ptr {{%.*}}, !dbg {{.+}}, !annotation ![[ANNOT_GE_LB:[0-9]+]] +// IR: [[OR_COND:%.*]] = and i1 %{{[a-z0-9]+}}, %{{[a-z0-9]+}}, !dbg {{.+}}, !annotation ![[ANNOT_GE_LB]] +// IR: br i1 [[OR_COND]], label %{{[a-z0-9]+}}, label %[[FOO_LABEL_TRAP:[a-z0-9]+]], !dbg {{.+}}, !annotation ![[ANNOT_LT_UB]] +// ... +// IR: [[FOO_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[LOC_10_14:[0-9]+]], !annotation ![[ANNOT_LT_UB_AND_GE_LB:[0-9]+]] +// IR-NEXT: unreachable, !dbg ![[LOC_10_14]], !annotation ![[ANNOT_LT_UB_AND_GE_LB]] + + +// IR-LABEL: @main +// ... +// IR: tail call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[LOC_0:[0-9]+]], !annotation ![[ANNOT_LE_UB_AND_CONV_TO_COUNT:[0-9]+]] +// IR-NEXT: unreachable, !dbg ![[LOC_0]], !annotation ![[ANNOT_LE_UB_AND_CONV_TO_COUNT]] + + +// IR-DAG: ![[ANNOT_LT_UB]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// IR-DAG: ![[ANNOT_GE_LB]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// IR-DAG: ![[ANNOT_LT_UB_AND_GE_LB]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +// IR-DAG: ![[ANNOT_LE_UB_AND_CONV_TO_COUNT]] = !{!"bounds-safety-generic"} + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-lt-upper-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '4' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 10, Column: 14 } +// OPT-REM-NEXT: Function: foo +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '6' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-check-ptr-lt-upper-bound, bounds-safety-check-ptr-ge-lower-bound +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ult (LLVM IR 'icmp') +// OPT-REM-NEXT: cmp uge (LLVM IR 'icmp') +// OPT-REM-NEXT: and (LLVM IR 'and') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: trap (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'unreachable') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 13, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-bounds-const-O2.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-O2.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-O2.c new file mode 100644 index 0000000000000..a521a26fb0900 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-O2.c @@ -0,0 +1,78 @@ + + +// RUN: %clang_cc1 -fbounds-safety -O2 %s -emit-llvm -o %t-O2.s -opt-record-file %t-O2.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O2.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O2.opt.yaml --check-prefixes OPT-REM %s + +#include + +int main(int argc, char **argv) { + int count_const = 0; + int * __counted_by(count_const) buff_const = 0; + count_const = 5; + buff_const = buff_const; + + int count_rt = argc; + int * __counted_by(count_rt) buff_rt = 0; + count_rt = argc; + buff_rt = buff_rt; +} + +// IR: define{{.*}} i32 @main +// IR-NEXT: entry: +// IR-NEXT: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[LOC_0:[0-9]+]], !annotation ![[TRAP_REASON:[0-9]+]] +// IR-NEXT: unreachable, !dbg ![[LOC_0]], !annotation ![[TRAP_REASON]] + + +// IR-DAG: ![[TRAP_REASON]] = !{!"bounds-safety-generic"} + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-O2.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-O2.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-argc-O0.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-argc-O0.c new file mode 100644 index 0000000000000..43b92bad4365a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-argc-O0.c @@ -0,0 +1,335 @@ + + +// RUN: %clang_cc1 -triple x86_64-apple-macos -fbounds-safety -O0 %s -emit-llvm -o %t-O0.s -opt-record-file %t-O0.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O0.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O0.opt.yaml --check-prefixes OPT-REM %s + +#include + +int main(int argc, char **argv) { + int count_rt = argc; + int * __counted_by(count_rt) buff_rt = 0; + count_rt = argc; + buff_rt = buff_rt; +} + +// IR: define{{.*}} i32 @main(i32 noundef %argc, ptr noundef %argv) +// IR: store i32 %argc, ptr %[[ARGC_ALLOCA:[a-z0-9.]+]] +// IR: %[[ARGC_LOAD1:[a-z0-9.]+]] = load i32, ptr %[[ARGC_ALLOCA]], align 4, !dbg ![[LOC_10_20:[0-9]+]] +// IR: store i32 %[[ARGC_LOAD1]], ptr %[[COUNT_RT_ALLOCA:[a-z0-9._]+]], align 4, !dbg ![[LOC_10_9:[0-9]+]] +// IR: %[[COUNT_RT_LOAD:[a-z0-9._]+]] = load i32, ptr %[[COUNT_RT_ALLOCA]], align 4, !dbg ![[LOC_12_16:[0-9]+]] +// IR: icmp eq i32 %[[COUNT_RT_LOAD]], 0, !dbg ![[LOC_11_44:[0-9]+]], !annotation ![[NEW_COUNT_POSITIVE:[a-z0-9]+]] +// IR: br i1 {{.*}}, label %[[LABEL_CONT:cont]], label %[[LABEL_TRAP:trap]], !dbg ![[LOC_11_44]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation ![[NEW_COUNT_POSITIVE]] +// ... +// IR: [[LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_11_44:[0-9]+]], !annotation ![[NEW_COUNT_POSITIVE]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_11_44]], !annotation ![[NEW_COUNT_POSITIVE]] +// ... +// IR: [[LABEL_CONT]]: +// ... +// IR: %[[ARGC_LOAD2:[a-z0-9.]+]] = load i32, ptr %[[ARGC_ALLOCA]], align 4, !dbg ![[LOC_12_16:[0-9]+]] +// IR: icmp ule {{.*}} !dbg ![[LOC_12_14:[0-9]+]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT:.+]], label %[[MAIN_LABEL_TRAP_RES:.+]], !dbg ![[LOC_12_14]] + +// IR: [[MAIN_LABEL_CONT]]: +// ... +// IR: icmp ule {{.*}} !dbg ![[LOC_12_14]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT2:.+]], label %[[MAIN_LABEL_TRAP_RES]], !dbg ![[LOC_12_5:[0-9]+]] + +// IR: [[MAIN_LABEL_CONT2]]: +// ... +// IR: %[[WIDTH_CHECK_RES:[a-z0-9_]+]] = icmp sle {{.*}} !dbg ![[LOC_12_5]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_EMPTY:.+]], label %[[MAIN_LABEL_TRAP_RES2:.*]], !dbg ![[LOC_12_5]] + +// IR: [[MAIN_LABEL_EMPTY]]: +// IR: %[[ARGC_CMP:[a-z0-9_]+]] = icmp sle i32 0, %[[ARGC_LOAD2]], !dbg ![[LOC_12_5]] +// IR: br label %[[MAIN_LABEL_TRAP_RES2]] + +// IR: [[MAIN_LABEL_TRAP_RES2]]: +// IR: %[[TRAP_RES2:[a-z0-9_]+]] = phi i1 [ false, %[[MAIN_LABEL_CONT2]] ], [ %[[ARGC_CMP]], %[[MAIN_LABEL_EMPTY]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]] +// IR: br label %[[MAIN_LABEL_TRAP_RES]] + +// IR: [[MAIN_LABEL_TRAP_RES]]: +// IR: %[[TRAP_RES:[a-z0-9_]+]] = phi i1 [ false, %[[MAIN_LABEL_CONT]] ], [ false, %[[LABEL_CONT]] ], [ %[[TRAP_RES2]], %[[MAIN_LABEL_TRAP_RES2]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT:[0-9]+]] +// IR: br i1 %[[TRAP_RES]], label {{.*}}, label %[[MAIN_LABEL_TRAP:[a-z0-9.]+]], !dbg ![[LOC_12_5]], !prof ![[PROFILE_METADATA]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR: [[MAIN_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_12_14:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_12_14]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR-DAG: ![[LOC_10_9]] = !DILocation(line: 10, column: 9 +// IR-DAG: ![[LOC_10_20]] = !DILocation(line: 10, column: 20 +// IR-DAG: ![[LOC_11_44]] = !DILocation(line: 11, column: 44 +// IR-DAG: ![[LOC_12_5]] = !DILocation(line: 12, column: 5 +// IR-DAG: ![[LOC_12_16]] = !DILocation(line: 12, column: 16 + +// IR-DAG: ![[LOC_12_14]] = !DILocation(line: 12, column: 14 +// IR-DAG: ![[TRAP_LOC_12_14]] = !DILocation(line: 0, scope: ![[TRAP_INFO_BNDS_CHECK_FAILED:[0-9]+]], inlinedAt: ![[LOC_12_5]]) +// IR-DAG: ![[TRAP_INFO_BNDS_CHECK_FAILED]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$" +// IR-DAG: ![[TRAP_LOC_11_44]] = !DILocation(line: 0, scope: ![[TRAP_INFO_BNDS_CHECK_FAILED]], inlinedAt: ![[LOC_11_44]]) + +// IR-DAG: ![[TRAP_LOC_MISSING]] = !DILocation(line: 0, scope: ![[MAIN_SCOPE:[0-9]+]]) +// IR-DAG: ![[MAIN_SCOPE]] = distinct !DISubprogram(name: "main", {{.*}} line: 9, {{.*}} scopeLine: 9 + +// IR-DAG: ![[NEW_COUNT_POSITIVE]] = !{!"bounds-safety-generic"} + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '53' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '53' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 11, Column: 11 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''load'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 11, Column: 44 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "cmp eq (LLVM IR 'icmp')\ncond branch (LLVM IR 'br')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 15 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '24' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 12, Column: 5 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '15' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'sub') +// OPT-REM-NEXT: other (LLVM IR 'sdiv') +// OPT-REM-NEXT: cmp sle (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp sle (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 12, Column: 14 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '3' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 12, Column: 16 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''sext'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''phi'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-argc-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-const-O0.c b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-const-O0.c new file mode 100644 index 0000000000000..ef7259dd97a38 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/opt-remarks/ptr-count-assignment-const-O0.c @@ -0,0 +1,302 @@ + + +// RUN: %clang_cc1 -triple x86_64-apple-macos -fbounds-safety -O0 %s -emit-llvm -o %t-O0.s -opt-record-file %t-O0.opt.yaml -opt-record-format yaml +// RUN: FileCheck --input-file %t-O0.s --check-prefixes IR %s +// RUN: FileCheck --input-file %t-O0.opt.yaml --check-prefixes OPT-REM %s + +#include + +int main(void) { + int count_const = 0; + int * __counted_by(count_const) buff_const = 0; + count_const = 5; + buff_const = buff_const; +} + +// IR: define{{.*}} i32 @main{{.*}} +// IR: load {{.*}} !dbg ![[LOC_11_11:[0-9]+]] +// IR: icmp eq {{.*}} 0, !dbg ![[LOC_11_37:[0-9]+]], !annotation ![[NEW_COUNT_POSITIVE:[a-z0-9]+]] +// IR: br i1 {{.*}}, label %[[LABEL_CONT:cont]], label %[[LABEL_TRAP:trap]], !dbg ![[LOC_11_37]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation ![[NEW_COUNT_POSITIVE]] +// ... +// IR: [[LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_11_37:[0-9]+]], !annotation ![[NEW_COUNT_POSITIVE]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_11_37]], !annotation ![[NEW_COUNT_POSITIVE]] +// ... +// IR: [[LABEL_CONT]]: +// ... +// IR: icmp ule {{.*}} !dbg ![[LOC_16_5:[0-9]+]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT:.+]], label %[[MAIN_LABEL_TRAP_RES:.+]], !dbg ![[LOC_16_5]] + +// IR: [[MAIN_LABEL_CONT]]: +// ... +// IR: icmp ule {{.*}} !dbg ![[LOC_16_5]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_CONT2:.+]], label %[[MAIN_LABEL_TRAP_RES]], !dbg ![[LOC_12_5:[0-9]+]] + +// IR: [[MAIN_LABEL_CONT2]]: +// ... +// IR: %[[WIDTH_CHECK_RES:[a-z0-9_]+]] = icmp sle {{.*}} !dbg ![[LOC_12_5]] +// IR: br i1 %{{.*}}, label %[[MAIN_LABEL_EMPTY:.+]], label %[[MAIN_LABEL_TRAP_RES2:.*]], !dbg ![[LOC_12_5]] + +// IR: [[MAIN_LABEL_EMPTY]]: +// IR: br label %[[MAIN_LABEL_TRAP_RES2]] + +// IR: [[MAIN_LABEL_TRAP_RES2]]: +// IR: %[[TRAP_RES2:[a-z0-9_]+]] = phi i1 [ false, %[[MAIN_LABEL_CONT2]] ], [ true, %[[MAIN_LABEL_EMPTY]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]] +// IR: br label %[[MAIN_LABEL_TRAP_RES]] + +// IR: [[MAIN_LABEL_TRAP_RES]]: +// IR: %[[TRAP_RES:[a-z0-9_]+]] = phi i1 [ false, %[[MAIN_LABEL_CONT]] ], [ false, %[[LABEL_CONT]] ], [ %[[TRAP_RES2]], %[[MAIN_LABEL_TRAP_RES2]] ], !dbg ![[TRAP_LOC_MISSING:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT:[0-9]+]] +// IR: br i1 %[[TRAP_RES]], label {{.*}}, label %[[MAIN_LABEL_TRAP:[a-z0-9.]+]], !dbg ![[LOC_12_5]], !prof ![[PROFILE_METADATA]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR: [[MAIN_LABEL_TRAP]]: +// IR: call void @llvm.ubsantrap(i8 25) #{{[0-9]+}}, !dbg ![[TRAP_LOC_16_5:[0-9]+]], !annotation ![[ANNOT_CONV_TO_COUNT]] +// IR-NEXT: unreachable, !dbg ![[TRAP_LOC_16_5]], !annotation ![[ANNOT_CONV_TO_COUNT]] + +// IR-DAG: ![[LOC_16_5]] = !DILocation(line: 12, column: 17 +// IR-DAG: ![[TRAP_LOC_16_5]] = !DILocation(line: 0, scope: ![[TRAP_INFO_16_5:[0-9]+]], inlinedAt: ![[LOC_12_5]]) +// IR-DAG: ![[TRAP_INFO_16_5]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$" + +// IR-DAG: ![[TRAP_LOC_MISSING]] = !DILocation(line: 0, scope: ![[MAIN_SCOPE:[0-9]+]]) +// IR-DAG: ![[MAIN_SCOPE]] = distinct !DISubprogram(name: "main", {{.*}} line: 9, {{.*}} scopeLine: 9 + +// IR-DAG: ![[NEW_COUNT_POSITIVE]] = !{!"bounds-safety-generic"} +// IR-DAG: ![[LOC_12_5]] = !DILocation(line: 12, column: 5 + +// opt-remarks tests generated using `gen-opt-remarks-check-lines.py` + +// OPT-REM: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '51' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-generic +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: AnnotationSummary +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 9, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Annotated ' +// OPT-REM-NEXT: - count: '51' +// OPT-REM-NEXT: - String: ' instructions with ' +// OPT-REM-NEXT: - type: bounds-safety-total-summary +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 11, Column: 11 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''load'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 11, Column: 50 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "cmp eq (LLVM IR 'icmp')\ncond branch (LLVM IR 'br')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 13, Column: 18 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '24' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: call (LLVM IR 'call') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 12, Column: 5 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '14' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: other (LLVM IR 'getelementptr') +// OPT-REM-NEXT: other (LLVM IR 'load') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'ptrtoint') +// OPT-REM-NEXT: other (LLVM IR 'sub') +// OPT-REM-NEXT: other (LLVM IR 'sdiv') +// OPT-REM-NEXT: cmp sle (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 12, Column: 17 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '3' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: cond branch (LLVM IR 'br') +// OPT-REM-NEXT: cmp ule (LLVM IR 'icmp') +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '1' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: '' +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: 'other (LLVM IR ''phi'')' +// OPT-REM-NEXT: ... + +// OPT-REM-NEXT: --- !Analysis +// OPT-REM-NEXT: Pass: annotation-remarks +// OPT-REM-NEXT: Name: BoundsSafetyCheck +// OPT-REM-NEXT: DebugLoc: { File: '{{.*}}ptr-count-assignment-const-O0.c', +// OPT-REM-NEXT: Line: 0, Column: 0 } +// OPT-REM-NEXT: Function: main +// OPT-REM-NEXT: Args: +// OPT-REM-NEXT: - String: 'Inserted ' +// OPT-REM-NEXT: - count: '2' +// OPT-REM-NEXT: - String: ' LLVM IR instruction' +// OPT-REM-NEXT: - String: s +// OPT-REM-NEXT: - String: "\n" +// OPT-REM-NEXT: - String: "used for:\n" +// OPT-REM-NEXT: - String: bounds-safety-generic +// OPT-REM-NEXT: - String: | +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: {{^[ ]+$}} +// OPT-REM-NEXT: instructions: +// OPT-REM-NEXT: - String: "trap (LLVM IR 'call')\nother (LLVM IR 'unreachable')" +// OPT-REM-NEXT: ... + +// OPT-REM-NOT: --- !Analysis diff --git a/clang/test/BoundsSafety/CodeGen/pass-addr-of-array-subscript-O2.c b/clang/test/BoundsSafety/CodeGen/pass-addr-of-array-subscript-O2.c new file mode 100644 index 0000000000000..edf66d90e2ef5 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/pass-addr-of-array-subscript-O2.c @@ -0,0 +1,71 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -triple arm64e-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK +// RUN: %clang_cc1 -O2 -triple arm64e-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK + +#include + +void bar(void *__sized_by(len) buf, int len); + +static void foo(int *__counted_by(len) elems, int len, int idx) { + bar(&elems[idx], sizeof(elems[idx])); +} + +// CHECK-LABEL: @oob( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void oob(void) { + int arr[10]; + foo(arr, 10, 10); +} + +// CHECK-LABEL: @oob2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[ADD_PTR_I:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH_I:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP_NOT_I:%.*]] = icmp ule ptr [[BOUND_PTR_ARITH_I]], [[ADD_PTR_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP34_NOT_I:%.*]] = icmp ule ptr [[ARR]], [[BOUND_PTR_ARITH_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_NOT76_I:%.*]] = and i1 [[CMP_NOT_I]], [[CMP34_NOT_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_NOT76_I]], label [[FOO_EXIT:%.*]], label [[TRAP_I:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: foo.exit: +// CHECK-NEXT: call void @bar(ptr noundef [[BOUND_PTR_ARITH_I]], i32 noundef 4) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void oob2(void) { + int arr[10]; + foo(arr, 10, -1); +} + + +// CHECK-LABEL: @oob3( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void oob3(void) { + int arr[10]; + foo(arr, 10, 11); +} + +// CHECK-LABEL: @good( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: [[BOUND_PTR_ARITH_I:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 36, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @bar(ptr noundef nonnull [[BOUND_PTR_ARITH_I]], i32 noundef 4) #[[ATTR6]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void good(void) { + int arr[10]; + foo(arr, 10, 9); +} diff --git a/clang/test/BoundsSafety/CodeGen/pass-addr-of-array-subscript.c b/clang/test/BoundsSafety/CodeGen/pass-addr-of-array-subscript.c new file mode 100644 index 0000000000000..b4dd972bdf053 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/pass-addr-of-array-subscript.c @@ -0,0 +1,182 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple arm64e-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK +// RUN: %clang_cc1 -O0 -triple arm64e-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK + +#include + +void bar(void *__sized_by(len) buf, int len); + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ELEMS_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP68:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[ELEMS:%.*]], ptr [[ELEMS_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: store i32 [[IDX:%.*]], ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ELEMS_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP17]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB11]], ptr [[TMP18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR4]], [[WIDE_PTR_PTR13]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END67:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB20]], ptr [[TMP19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP34:%.*]] = icmp ule ptr [[WIDE_PTR_PTR22]], [[WIDE_PTR_PTR29]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP34]], label [[LAND_RHS:%.*]], label [[LAND_END67]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB38]], ptr [[TMP20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[TMP21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB44]], ptr [[TMP23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[TMP24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB56]], ptr [[TMP25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB58]], ptr [[TMP26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR60]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP65:%.*]] = icmp sle i64 4, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP65]], label [[LAND_RHS66:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs66: +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP27:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS66]] ] +// CHECK-NEXT: br label [[LAND_END67]], {{!annotation ![0-9]+}} +// CHECK: land.end67: +// CHECK-NEXT: [[TMP28:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP27]], [[LAND_END]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP28]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP68]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP68]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP68]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP68]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK-NEXT: call void @bar(ptr noundef [[WIDE_PTR_PTR70]], i32 noundef 4) +// CHECK-NEXT: ret void +// +void foo(int *__counted_by(len) elems, int len, int idx) { + bar(&elems[idx], sizeof(elems[idx])); +} diff --git a/clang/test/BoundsSafety/CodeGen/pointer-bounds.c b/clang/test/BoundsSafety/CodeGen/pointer-bounds.c new file mode 100644 index 0000000000000..664a4636f7db6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/pointer-bounds.c @@ -0,0 +1,63 @@ + + +// RUN: %clang_cc1 %s -O2 -fbounds-safety -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -o - | FileCheck %s + +#include + +int garray[10]; +int *const __bidi_indexable lower = &garray[0]; +int *const __bidi_indexable mid = &garray[5]; +int *const __bidi_indexable upper = &garray[10]; + +#define ASSERT(X) if (!(X)) __builtin_trap(); + +int testGarray() { + ASSERT(__ptr_lower_bound(lower) == lower); + ASSERT(__ptr_lower_bound(mid) == lower); + ASSERT(__ptr_lower_bound(upper) == lower); + + ASSERT(__ptr_upper_bound(lower) == upper); + ASSERT(__ptr_upper_bound(mid) == upper); + ASSERT(__ptr_upper_bound(upper) == upper); + return 0; +} +// CHECK-LABEL: @testGarray +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 + +int testArray() { + int array[10]; + int *lower = &array[0]; + int *mid = &array[5]; + int *upper = &array[10]; + ASSERT(__ptr_lower_bound(lower) == lower); + ASSERT(__ptr_lower_bound(mid) == lower); + ASSERT(__ptr_lower_bound(upper) == lower); + + ASSERT(__ptr_upper_bound(lower) == upper); + ASSERT(__ptr_upper_bound(mid) == upper); + ASSERT(__ptr_upper_bound(upper) == upper); + return 0; +} +// CHECK-LABEL: @testArray +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 + +int testStaticArray() { + static int array[10]; + static int *const __bidi_indexable lower = &array[0]; + static int *const __bidi_indexable mid = &array[5]; + static int *const __bidi_indexable upper = &array[10]; + ASSERT(__ptr_lower_bound(lower) == lower); + ASSERT(__ptr_lower_bound(mid) == lower); + ASSERT(__ptr_lower_bound(upper) == lower); + + ASSERT(__ptr_upper_bound(lower) == upper); + ASSERT(__ptr_upper_bound(mid) == upper); + ASSERT(__ptr_upper_bound(upper) == upper); + return 0; +} +// CHECK-LABEL: @testStaticArray +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 0 diff --git a/clang/test/BoundsSafety/CodeGen/pointer-to-integer-cast.c b/clang/test/BoundsSafety/CodeGen/pointer-to-integer-cast.c new file mode 100644 index 0000000000000..69ca866183478 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/pointer-to-integer-cast.c @@ -0,0 +1,63 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK %s + +#include +#include + +char buf[42]; + +// CHECK: @buf_ptr = global i64 ptrtoint (ptr @buf to i64), align 8 +uintptr_t buf_ptr = (uintptr_t)buf; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[S_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[S_PTR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[I_PTR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[BI_PTR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[I]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[I_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[I]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[I_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: store ptr [[S:%.*]], ptr [[S_ADDR]], align 8 +// CHECK-NEXT: br label [[_S:%.*]] +// CHECK: _s: +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[S_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = ptrtoint ptr [[TMP2]] to i64 +// CHECK-NEXT: store i64 [[TMP3]], ptr [[S_PTR]], align 8 +// CHECK-NEXT: br label [[_I:%.*]] +// CHECK: _i: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[I]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64 +// CHECK-NEXT: store i64 [[TMP4]], ptr [[I_PTR]], align 8 +// CHECK-NEXT: br label [[_BI:%.*]] +// CHECK: _bi: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[BI:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR3]] to i64 +// CHECK-NEXT: store i64 [[TMP5]], ptr [[BI_PTR]], align 8 +// CHECK-NEXT: ret void +// +void foo(int *__single s, int *__indexable i, int *__bidi_indexable bi) { +_s:; + uintptr_t s_ptr = (uintptr_t)s; +_i:; + uintptr_t i_ptr = (uintptr_t)i; +_bi:; + uintptr_t bi_ptr = (uintptr_t)bi; +} diff --git a/clang/test/BoundsSafety/CodeGen/ptr-arith-O2.c b/clang/test/BoundsSafety/CodeGen/ptr-arith-O2.c new file mode 100644 index 0000000000000..cf1ac5d2170d0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ptr-arith-O2.c @@ -0,0 +1,58 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -triple arm64e -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple arm64e -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct T { + char *ptr; + int i; + long l; +}; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[LEN:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDX_NEG:%.*]] = sub nsw i64 0, [[IDX_EXT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[IDX_NEG]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH5]], [[ADD_PTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH5]], [[BUF]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT6:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont6: +// CHECK-NEXT: store i32 3, ptr [[BOUND_PTR_ARITH5]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void foo(int *buf __counted_by(len), int len) { + int *ptr = len + buf; + ptr = buf - len; + *ptr = 3;; +} + +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A:%.*]] = alloca [10 x i32], align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[A]]) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: [[ADD_PTR3_I:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 40 +// CHECK-NEXT: [[BOUND_PTR_ARITH5_I:%.*]] = getelementptr i8, ptr [[A]], i64 -40 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH5_I]], [[ADD_PTR3_I]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH5_I]], [[A]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_I:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_I]], label [[FOO_EXIT:%.*]], label [[TRAP_I:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap.i: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: foo.exit: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[A]]) #[[ATTR4]] +// CHECK-NEXT: ret i32 0 +// +int main() { + int a[10]; + foo(a, 10); + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ptr-arith.c b/clang/test/BoundsSafety/CodeGen/ptr-arith.c new file mode 100644 index 0000000000000..b27efdfda205c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ptr-arith.c @@ -0,0 +1,143 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct T { + char *ptr; + int i; + long l; +}; + +// +__attribute__((noinline)) +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP7]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT2:%.*]] = sext i32 [[TMP17]] to i64 +// CHECK-NEXT: [[ADD_PTR3:%.*]] = getelementptr inbounds i32, ptr [[TMP16]], i64 [[IDX_EXT2]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR3]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[IDXPROM4:%.*]] = sext i32 [[TMP15]] to i64 +// CHECK-NEXT: [[IDX_NEG:%.*]] = sub i64 0, [[IDXPROM4]] +// CHECK-NEXT: [[BOUND_PTR_ARITH5:%.*]] = getelementptr i32, ptr [[TMP22]], i64 [[IDX_NEG]] +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH5]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-NEXT: [[TMP28:%.*]] = load ptr, ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP28]], ptr [[TMP29]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP30]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP31:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP31]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont7: +// CHECK-NEXT: store i32 3, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-NEXT: ret void +// +void foo(int *buf __counted_by(len), int len) { + int *ptr = len + buf; + ptr = buf - len; + *ptr = 3;; +} + +// +// XXX: Room for optimizing O0 codegen + + +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[A:%.*]] = alloca [10 x i32], align 16 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[A]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR]], i32 noundef 10) +// CHECK-NEXT: ret i32 0 +// +int main() { + int a[10]; + foo(a, 10); + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/ptrauth_unsafe_forge_terminated.c b/clang/test/BoundsSafety/CodeGen/ptrauth_unsafe_forge_terminated.c new file mode 100644 index 0000000000000..c64d0a6e9e84d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ptrauth_unsafe_forge_terminated.c @@ -0,0 +1,58 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ --version 3 + +// RUN: %clang_cc1 -triple arm64e -fbounds-safety -fptrauth-returns -fptrauth-calls -emit-llvm %s -o - | FileCheck --check-prefix NO_FPTR_DISC %s +// RUN: %clang_cc1 -triple arm64e -fbounds-safety -fptrauth-returns -fptrauth-calls -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix NO_FPTR_DISC %s +// RUN: %clang_cc1 -triple arm64e -fbounds-safety -fptrauth-function-pointer-type-discrimination -fptrauth-returns -fptrauth-calls -emit-llvm %s -o - | FileCheck --check-prefix FPTR_DISC %s +// RUN: %clang_cc1 -triple arm64e -fbounds-safety -fptrauth-function-pointer-type-discrimination -fptrauth-returns -fptrauth-calls -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix FPTR_DISC %s +// RUN: %clang_cc1 -triple arm64e -fptrauth-function-pointer-type-discrimination -fptrauth-returns -fptrauth-calls -emit-llvm %s -o - | FileCheck --check-prefix FPTR_DISC_NO_BOUNDS_SAFETY %s +// RUN: %clang_cc1 -triple arm64e -fptrauth-function-pointer-type-discrimination -fptrauth-returns -fptrauth-calls -x objective-c -emit-llvm %s -o - | FileCheck --check-prefix FPTR_DISC_NO_BOUNDS_SAFETY %s + + + + +#include +#include + +typedef void(*f_t)(void); + +f_t __unsafe_indexable bar(void); + +// NO_FPTR_DISC-LABEL: define dso_local ptr @foo +// NO_FPTR_DISC-SAME: () #[[ATTR0:[0-9]+]] { +// NO_FPTR_DISC-NEXT: entry: +// NO_FPTR_DISC-NEXT: [[CALL:%.*]] = call ptr @bar() +// NO_FPTR_DISC-NEXT: ret ptr [[CALL]] +// +// FPTR_DISC-LABEL: define dso_local ptr @foo +// FPTR_DISC-SAME: () #[[ATTR0:[0-9]+]] { +// FPTR_DISC-NEXT: entry: +// FPTR_DISC-NEXT: [[CALL:%.*]] = call ptr @bar() +// FPTR_DISC-NEXT: [[TMP0:%.*]] = icmp ne ptr [[CALL]], null +// FPTR_DISC-NEXT: br i1 [[TMP0]], label [[RESIGN_NONNULL:%.*]], label [[RESIGN_CONT:%.*]] +// FPTR_DISC: resign.nonnull: +// FPTR_DISC-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[CALL]] to i64 +// FPTR_DISC-NEXT: [[TMP2:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[TMP1]], i32 0, i64 18983, i32 0, i64 0) +// FPTR_DISC-NEXT: [[TMP3:%.*]] = inttoptr i64 [[TMP2]] to ptr +// FPTR_DISC-NEXT: br label [[RESIGN_CONT]] +// FPTR_DISC: resign.cont: +// FPTR_DISC-NEXT: [[TMP4:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[TMP3]], [[RESIGN_NONNULL]] ] +// FPTR_DISC-NEXT: ret ptr [[TMP4]] +// +// FPTR_DISC_NO_BOUNDS_SAFETY-LABEL: define dso_local ptr @foo +// FPTR_DISC_NO_BOUNDS_SAFETY-SAME: () #[[ATTR0:[0-9]+]] { +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: entry: +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: [[CALL:%.*]] = call ptr @bar() +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: [[TMP0:%.*]] = icmp ne ptr [[CALL]], null +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: br i1 [[TMP0]], label [[RESIGN_NONNULL:%.*]], label [[RESIGN_CONT:%.*]] +// FPTR_DISC_NO_BOUNDS_SAFETY: resign.nonnull: +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[CALL]] to i64 +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: [[TMP2:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[TMP1]], i32 0, i64 18983, i32 0, i64 0) +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: [[TMP3:%.*]] = inttoptr i64 [[TMP2]] to ptr +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: br label [[RESIGN_CONT]] +// FPTR_DISC_NO_BOUNDS_SAFETY: resign.cont: +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: [[TMP4:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[TMP3]], [[RESIGN_NONNULL]] ] +// FPTR_DISC_NO_BOUNDS_SAFETY-NEXT: ret ptr [[TMP4]] +// +const char *foo(void) { + return __unsafe_forge_null_terminated(char *, bar()); +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations-constexpr-geps.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-constexpr-geps.c new file mode 100644 index 0000000000000..f6cc6280b5ef1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-constexpr-geps.c @@ -0,0 +1,575 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" + +// +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +// XFAIL: !rdar109424213 + +#include +#include + +int a[4]; +int b[4]; +int c[4]; +int d[4]; + +// CHECK-LABEL: @read_from_global_array_can_remove_checks( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @a, align 16, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr getelementptr inbounds ([4 x i32], ptr @a, i64 0, i64 1), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD_1:%.*]] = add nsw i32 [[TMP1]], [[TMP0]] +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr getelementptr inbounds ([4 x i32], ptr @a, i64 0, i64 2), align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD_2:%.*]] = add nsw i32 [[TMP2]], [[ADD_1]] +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr getelementptr inbounds ([4 x i32], ptr @a, i64 0, i64 3), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD_3:%.*]] = add nsw i32 [[TMP3]], [[ADD_2]] +// CHECK-NEXT: ret i32 [[ADD_3]] +// +int read_from_global_array_can_remove_checks() { + int res = 0; + for (unsigned char i = 0; i < 4; i++) { + res += a[i]; + } + return res; +} + +// CHECK-LABEL: @read_from_global_array_trap_last_iter( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +int read_from_global_array_trap_last_iter() { + int res = 0; + for (unsigned char i = 0; i < 5; i++) { + res += a[i]; + } + return res; +} + +// CHECK-LABEL: @read_from_global_array_cannot_rename( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP7_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP7_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: [[RES_0_LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: ret i32 [[RES_0_LCSSA]] +// CHECK: for.body: +// CHECK-NEXT: [[RES_09:%.*]] = phi i32 [ [[ADD]], [[CONT2]] ], [ 0, [[ENTRY]] ] +// CHECK-NEXT: [[I_08:%.*]] = phi i8 [ [[INC:%.*]], [[CONT2]] ], [ 0, [[ENTRY]] ] +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i8 [[I_08]] to i64 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr @a, i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], getelementptr inbounds ([4 x i32], ptr @a, i64 1, i64 0), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], @a, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD]] = add nsw i32 [[TMP3]], [[RES_09]] +// CHECK-NEXT: [[INC]] = add nuw nsw i8 [[I_08]], 1 +// CHECK-NEXT: [[CONV:%.*]] = zext i8 [[INC]] to i32 +// CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[CONV]], [[N]] +// CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP]], {{!llvm.loop ![0-9]+}} +// +int read_from_global_array_cannot_rename(unsigned n) { + int res = 0; + for (unsigned char i = 0; i < n; i++) { + res += a[i]; + } + return res; +} + +typedef struct { + uint16_t len; + uint16_t offset; + uint8_t payload[__counted_by(offset + len)]; +} hdr_t; + +// CHECK-LABEL: @concat_to_separate_clobals( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PAYLOAD:%.*]] = getelementptr inbounds [[STRUCT_HDR_T:%.*]], ptr [[P_BUF:%.*]], i64 0, i32 2 +// CHECK-NEXT: [[OFFSET:%.*]] = getelementptr inbounds [[STRUCT_HDR_T]], ptr [[P_BUF]], i64 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[OFFSET]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV:%.*]] = zext i16 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[P_BUF]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV3:%.*]] = zext i16 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[CONV3]], [[CONV]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[PAYLOAD]], i64 [[ADD]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 7 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[ADD_PTR]] to i64 +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ule ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[BOUND_PTR_ARITH]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP38:%.*]] = icmp sgt i64 [[SUB_PTR_SUB]], 3 +// CHECK-NEXT: [[OR_COND156:%.*]] = select i1 [[CMP14_NOT]], i1 [[CMP38]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND156]], label [[CONT:%.*]], label [[TRAP:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR50:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 11 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = load i8, ptr [[TMP2]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59:%.*]] = zext i8 [[TMP3]] to i32 +// CHECK-NEXT: store i32 [[CONV59]], ptr @a, align 16, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i8, ptr [[TMP4]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV79:%.*]] = zext i8 [[TMP5]] to i32 +// CHECK-NEXT: store i32 [[CONV79]], ptr @b, align 16, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 9 +// CHECK-NEXT: [[TMP7:%.*]] = load i8, ptr [[TMP6]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV100:%.*]] = zext i8 [[TMP7]] to i32 +// CHECK-NEXT: store i32 [[CONV100]], ptr @c, align 16, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = load i8, ptr [[BOUND_PTR_ARITH]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV121:%.*]] = zext i8 [[TMP8]] to i32 +// CHECK-NEXT: store i32 [[CONV121]], ptr @d, align 16, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_1:%.*]] = icmp ule ptr [[ADD_PTR50]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_1:%.*]] = ptrtoint ptr [[ADD_PTR50]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_1:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_1]] +// CHECK-NEXT: [[CMP38_1:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_1]], 3 +// CHECK-NEXT: [[OR_COND156_1:%.*]] = select i1 [[CMP14_NOT_1]], i1 [[CMP38_1]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND156_1]], label [[CONT_1:%.*]], label [[TRAP]] +// CHECK: cont.1: +// CHECK-NEXT: [[ADD_PTR50_1:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 15 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 14 +// CHECK-NEXT: [[TMP10:%.*]] = load i8, ptr [[TMP9]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_1:%.*]] = zext i8 [[TMP10]] to i32 +// CHECK-NEXT: store i32 [[CONV59_1]], ptr getelementptr inbounds ([4 x i32], ptr @a, i64 0, i64 1), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 12 +// CHECK-NEXT: [[TMP12:%.*]] = load i8, ptr [[TMP11]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV79_1:%.*]] = zext i8 [[TMP12]] to i32 +// CHECK-NEXT: store i32 [[CONV79_1]], ptr getelementptr inbounds ([4 x i32], ptr @b, i64 0, i64 1), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 13 +// CHECK-NEXT: [[TMP14:%.*]] = load i8, ptr [[TMP13]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV100_1:%.*]] = zext i8 [[TMP14]] to i32 +// CHECK-NEXT: store i32 [[CONV100_1]], ptr getelementptr inbounds ([4 x i32], ptr @c, i64 0, i64 1), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP15:%.*]] = load i8, ptr [[ADD_PTR50]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV121_1:%.*]] = zext i8 [[TMP15]] to i32 +// CHECK-NEXT: store i32 [[CONV121_1]], ptr getelementptr inbounds ([4 x i32], ptr @d, i64 0, i64 1), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_2:%.*]] = icmp ule ptr [[ADD_PTR50_1]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_2:%.*]] = ptrtoint ptr [[ADD_PTR50_1]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_2:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_2]] +// CHECK-NEXT: [[CMP38_2:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_2]], 3 +// CHECK-NEXT: [[OR_COND156_2:%.*]] = select i1 [[CMP14_NOT_2]], i1 [[CMP38_2]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND156_2]], label [[CONT_2:%.*]], label [[TRAP]] +// CHECK: cont.2: +// CHECK-NEXT: [[ADD_PTR50_2:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 19 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 18 +// CHECK-NEXT: [[TMP17:%.*]] = load i8, ptr [[TMP16]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_2:%.*]] = zext i8 [[TMP17]] to i32 +// CHECK-NEXT: store i32 [[CONV59_2]], ptr getelementptr inbounds ([4 x i32], ptr @a, i64 0, i64 2), align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 16 +// CHECK-NEXT: [[TMP19:%.*]] = load i8, ptr [[TMP18]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV79_2:%.*]] = zext i8 [[TMP19]] to i32 +// CHECK-NEXT: store i32 [[CONV79_2]], ptr getelementptr inbounds ([4 x i32], ptr @b, i64 0, i64 2), align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 17 +// CHECK-NEXT: [[TMP21:%.*]] = load i8, ptr [[TMP20]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV100_2:%.*]] = zext i8 [[TMP21]] to i32 +// CHECK-NEXT: store i32 [[CONV100_2]], ptr getelementptr inbounds ([4 x i32], ptr @c, i64 0, i64 2), align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP22:%.*]] = load i8, ptr [[ADD_PTR50_1]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV121_2:%.*]] = zext i8 [[TMP22]] to i32 +// CHECK-NEXT: store i32 [[CONV121_2]], ptr getelementptr inbounds ([4 x i32], ptr @d, i64 0, i64 2), align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_3:%.*]] = icmp ule ptr [[ADD_PTR50_2]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_3:%.*]] = ptrtoint ptr [[ADD_PTR50_2]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_3:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_3]] +// CHECK-NEXT: [[CMP38_3:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_3]], 3 +// CHECK-NEXT: [[OR_COND156_3:%.*]] = select i1 [[CMP14_NOT_3]], i1 [[CMP38_3]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND156_3]], label [[CONT_3:%.*]], label [[TRAP]] +// CHECK: cont.3: +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 22 +// CHECK-NEXT: [[TMP24:%.*]] = load i8, ptr [[TMP23]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_3:%.*]] = zext i8 [[TMP24]] to i32 +// CHECK-NEXT: store i32 [[CONV59_3]], ptr getelementptr inbounds ([4 x i32], ptr @a, i64 0, i64 3), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 20 +// CHECK-NEXT: [[TMP26:%.*]] = load i8, ptr [[TMP25]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV79_3:%.*]] = zext i8 [[TMP26]] to i32 +// CHECK-NEXT: store i32 [[CONV79_3]], ptr getelementptr inbounds ([4 x i32], ptr @b, i64 0, i64 3), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 21 +// CHECK-NEXT: [[TMP28:%.*]] = load i8, ptr [[TMP27]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV100_3:%.*]] = zext i8 [[TMP28]] to i32 +// CHECK-NEXT: store i32 [[CONV100_3]], ptr getelementptr inbounds ([4 x i32], ptr @c, i64 0, i64 3), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP29:%.*]] = load i8, ptr [[ADD_PTR50_2]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV121_3:%.*]] = zext i8 [[TMP29]] to i32 +// CHECK-NEXT: store i32 [[CONV121_3]], ptr getelementptr inbounds ([4 x i32], ptr @d, i64 0, i64 3), align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void concat_to_separate_clobals(hdr_t *p_buf) { + uint8_t *params = p_buf->payload + 3; + for (unsigned char i = 0; i < 4; i++) { + uint8_t *__counted_by(4) p = params; + a[i] = p[3]; // checks not removed for p[] + b[i] = p[1]; // checks not removed for b[] and p[] + c[i] = p[2]; // checks not removed for c[] and p[] + d[i] = p[0]; // checks not removed for d[] + params += 4; + } +} + +struct arrays { + int a[4]; + int b[4]; + int c[4]; + int d[4]; +}; + +// CHECK-LABEL: @concat_to_arrays_struct_can_remove_arrays_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PAYLOAD:%.*]] = getelementptr inbounds [[STRUCT_HDR_T:%.*]], ptr [[P_BUF:%.*]], i64 0, i32 2 +// CHECK-NEXT: [[OFFSET:%.*]] = getelementptr inbounds [[STRUCT_HDR_T]], ptr [[P_BUF]], i64 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[OFFSET]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV:%.*]] = zext i16 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[P_BUF]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV3:%.*]] = zext i16 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[CONV3]], [[CONV]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[PAYLOAD]], i64 [[ADD]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 7 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[ADD_PTR]] to i64 +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ule ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[BOUND_PTR_ARITH]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP38:%.*]] = icmp sgt i64 [[SUB_PTR_SUB]], 3 +// CHECK-NEXT: [[OR_COND166:%.*]] = select i1 [[CMP14_NOT]], i1 [[CMP38]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166]], label [[CONT138:%.*]], label [[TRAP:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont138: +// CHECK-NEXT: [[UPPER106:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS:%.*]], ptr [[ARRAYS:%.*]], i64 0, i32 3 +// CHECK-NEXT: [[UPPER83:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2 +// CHECK-NEXT: [[B:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 1 +// CHECK-NEXT: [[ADD_PTR50:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 11 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = load i8, ptr [[TMP2]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59:%.*]] = zext i8 [[TMP3]] to i32 +// CHECK-NEXT: store i32 [[CONV59]], ptr [[ARRAYS]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i8, ptr [[TMP4]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80:%.*]] = zext i8 [[TMP5]] to i32 +// CHECK-NEXT: store i32 [[CONV80]], ptr [[B]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 9 +// CHECK-NEXT: [[TMP7:%.*]] = load i8, ptr [[TMP6]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103:%.*]] = zext i8 [[TMP7]] to i32 +// CHECK-NEXT: store i32 [[CONV103]], ptr [[UPPER83]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = load i8, ptr [[BOUND_PTR_ARITH]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126:%.*]] = zext i8 [[TMP8]] to i32 +// CHECK-NEXT: store i32 [[CONV126]], ptr [[UPPER106]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_1:%.*]] = icmp ule ptr [[ADD_PTR50]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_1:%.*]] = ptrtoint ptr [[ADD_PTR50]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_1:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_1]] +// CHECK-NEXT: [[CMP38_1:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_1]], 3 +// CHECK-NEXT: [[OR_COND166_1:%.*]] = select i1 [[CMP14_NOT_1]], i1 [[CMP38_1]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166_1]], label [[CONT138_1:%.*]], label [[TRAP]] +// CHECK: cont138.1: +// CHECK-NEXT: [[ADD_PTR50_1:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 15 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 14 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 1 +// CHECK-NEXT: [[TMP11:%.*]] = load i8, ptr [[TMP9]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_1:%.*]] = zext i8 [[TMP11]] to i32 +// CHECK-NEXT: store i32 [[CONV59_1]], ptr [[TMP10]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 12 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 1, i64 1 +// CHECK-NEXT: [[TMP14:%.*]] = load i8, ptr [[TMP12]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80_1:%.*]] = zext i8 [[TMP14]] to i32 +// CHECK-NEXT: store i32 [[CONV80_1]], ptr [[TMP13]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2, i64 1 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 13 +// CHECK-NEXT: [[TMP17:%.*]] = load i8, ptr [[TMP16]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103_1:%.*]] = zext i8 [[TMP17]] to i32 +// CHECK-NEXT: store i32 [[CONV103_1]], ptr [[TMP15]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3, i64 1 +// CHECK-NEXT: [[TMP19:%.*]] = load i8, ptr [[ADD_PTR50]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126_1:%.*]] = zext i8 [[TMP19]] to i32 +// CHECK-NEXT: store i32 [[CONV126_1]], ptr [[TMP18]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_2:%.*]] = icmp ule ptr [[ADD_PTR50_1]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_2:%.*]] = ptrtoint ptr [[ADD_PTR50_1]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_2:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_2]] +// CHECK-NEXT: [[CMP38_2:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_2]], 3 +// CHECK-NEXT: [[OR_COND166_2:%.*]] = select i1 [[CMP14_NOT_2]], i1 [[CMP38_2]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166_2]], label [[CONT138_2:%.*]], label [[TRAP]] +// CHECK: cont138.2: +// CHECK-NEXT: [[ADD_PTR50_2:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 19 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 18 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 2 +// CHECK-NEXT: [[TMP22:%.*]] = load i8, ptr [[TMP20]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_2:%.*]] = zext i8 [[TMP22]] to i32 +// CHECK-NEXT: store i32 [[CONV59_2]], ptr [[TMP21]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 16 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 1, i64 2 +// CHECK-NEXT: [[TMP25:%.*]] = load i8, ptr [[TMP23]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80_2:%.*]] = zext i8 [[TMP25]] to i32 +// CHECK-NEXT: store i32 [[CONV80_2]], ptr [[TMP24]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2, i64 2 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 17 +// CHECK-NEXT: [[TMP28:%.*]] = load i8, ptr [[TMP27]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103_2:%.*]] = zext i8 [[TMP28]] to i32 +// CHECK-NEXT: store i32 [[CONV103_2]], ptr [[TMP26]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3, i64 2 +// CHECK-NEXT: [[TMP30:%.*]] = load i8, ptr [[ADD_PTR50_1]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126_2:%.*]] = zext i8 [[TMP30]] to i32 +// CHECK-NEXT: store i32 [[CONV126_2]], ptr [[TMP29]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_3:%.*]] = icmp ule ptr [[ADD_PTR50_2]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_3:%.*]] = ptrtoint ptr [[ADD_PTR50_2]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_3:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_3]] +// CHECK-NEXT: [[CMP38_3:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_3]], 3 +// CHECK-NEXT: [[OR_COND166_3:%.*]] = select i1 [[CMP14_NOT_3]], i1 [[CMP38_3]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166_3]], label [[CONT138_3:%.*]], label [[TRAP]] +// CHECK: cont138.3: +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 22 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 3 +// CHECK-NEXT: [[TMP33:%.*]] = load i8, ptr [[TMP31]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_3:%.*]] = zext i8 [[TMP33]] to i32 +// CHECK-NEXT: store i32 [[CONV59_3]], ptr [[TMP32]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 20 +// CHECK-NEXT: [[TMP35:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 1, i64 3 +// CHECK-NEXT: [[TMP36:%.*]] = load i8, ptr [[TMP34]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80_3:%.*]] = zext i8 [[TMP36]] to i32 +// CHECK-NEXT: store i32 [[CONV80_3]], ptr [[TMP35]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2, i64 3 +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 21 +// CHECK-NEXT: [[TMP39:%.*]] = load i8, ptr [[TMP38]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103_3:%.*]] = zext i8 [[TMP39]] to i32 +// CHECK-NEXT: store i32 [[CONV103_3]], ptr [[TMP37]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP40:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3, i64 3 +// CHECK-NEXT: [[TMP41:%.*]] = load i8, ptr [[ADD_PTR50_2]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126_3:%.*]] = zext i8 [[TMP41]] to i32 +// CHECK-NEXT: store i32 [[CONV126_3]], ptr [[TMP40]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void concat_to_arrays_struct_can_remove_arrays_check(struct arrays *arrays, hdr_t *p_buf) { + uint8_t *params = p_buf->payload + 3; + + for (unsigned char i = 0; i < 4; i++) { + uint8_t *__counted_by(4) p = params; + arrays->a[i] = p[3]; // checks not removed for p[] + arrays->b[i] = p[1]; // checks not removed for b[] and p[] + arrays->c[i] = p[2]; // checks not removed for c[] and p[] + arrays->d[i] = p[0]; // checks not removed for d[] + params += 4; + } +} + +// CHECK-LABEL: @concat_to_arrays_struct_trap_on_last_iter( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PAYLOAD:%.*]] = getelementptr inbounds [[STRUCT_HDR_T:%.*]], ptr [[P_BUF:%.*]], i64 0, i32 2 +// CHECK-NEXT: [[OFFSET:%.*]] = getelementptr inbounds [[STRUCT_HDR_T]], ptr [[P_BUF]], i64 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[OFFSET]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV:%.*]] = zext i16 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[P_BUF]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV3:%.*]] = zext i16 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[CONV3]], [[CONV]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[PAYLOAD]], i64 [[ADD]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 7 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[ADD_PTR]] to i64 +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ule ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[BOUND_PTR_ARITH]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP38:%.*]] = icmp sgt i64 [[SUB_PTR_SUB]], 3 +// CHECK-NEXT: [[OR_COND166:%.*]] = select i1 [[CMP14_NOT]], i1 [[CMP38]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166]], label [[CONT138:%.*]], label [[TRAP:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont138: +// CHECK-NEXT: [[UPPER106:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS:%.*]], ptr [[ARRAYS:%.*]], i64 0, i32 3 +// CHECK-NEXT: [[UPPER83:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 4 +// CHECK-NEXT: [[ADD_PTR50:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 11 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 10 +// CHECK-NEXT: [[TMP3:%.*]] = load i8, ptr [[TMP2]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59:%.*]] = zext i8 [[TMP3]] to i32 +// CHECK-NEXT: store i32 [[CONV59]], ptr [[ARRAYS]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i8, ptr [[TMP4]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80:%.*]] = zext i8 [[TMP5]] to i32 +// CHECK-NEXT: store i32 [[CONV80]], ptr [[UPPER]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 9 +// CHECK-NEXT: [[TMP7:%.*]] = load i8, ptr [[TMP6]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103:%.*]] = zext i8 [[TMP7]] to i32 +// CHECK-NEXT: store i32 [[CONV103]], ptr [[UPPER83]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = load i8, ptr [[BOUND_PTR_ARITH]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126:%.*]] = zext i8 [[TMP8]] to i32 +// CHECK-NEXT: store i32 [[CONV126]], ptr [[UPPER106]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_1:%.*]] = icmp ule ptr [[ADD_PTR50]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_1:%.*]] = ptrtoint ptr [[ADD_PTR50]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_1:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_1]] +// CHECK-NEXT: [[CMP38_1:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_1]], 3 +// CHECK-NEXT: [[OR_COND166_1:%.*]] = select i1 [[CMP14_NOT_1]], i1 [[CMP38_1]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166_1]], label [[CONT138_1:%.*]], label [[TRAP]] +// CHECK: cont138.1: +// CHECK-NEXT: [[ADD_PTR50_1:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 15 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 1 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 14 +// CHECK-NEXT: [[TMP11:%.*]] = load i8, ptr [[TMP10]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_1:%.*]] = zext i8 [[TMP11]] to i32 +// CHECK-NEXT: store i32 [[CONV59_1]], ptr [[TMP9]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 12 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 5 +// CHECK-NEXT: [[TMP14:%.*]] = load i8, ptr [[TMP12]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80_1:%.*]] = zext i8 [[TMP14]] to i32 +// CHECK-NEXT: store i32 [[CONV80_1]], ptr [[TMP13]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2, i64 1 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 13 +// CHECK-NEXT: [[TMP17:%.*]] = load i8, ptr [[TMP16]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103_1:%.*]] = zext i8 [[TMP17]] to i32 +// CHECK-NEXT: store i32 [[CONV103_1]], ptr [[TMP15]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3, i64 1 +// CHECK-NEXT: [[TMP19:%.*]] = load i8, ptr [[ADD_PTR50]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126_1:%.*]] = zext i8 [[TMP19]] to i32 +// CHECK-NEXT: store i32 [[CONV126_1]], ptr [[TMP18]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_2:%.*]] = icmp ule ptr [[ADD_PTR50_1]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_2:%.*]] = ptrtoint ptr [[ADD_PTR50_1]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_2:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_2]] +// CHECK-NEXT: [[CMP38_2:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_2]], 3 +// CHECK-NEXT: [[OR_COND166_2:%.*]] = select i1 [[CMP14_NOT_2]], i1 [[CMP38_2]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166_2]], label [[CONT138_2:%.*]], label [[TRAP]] +// CHECK: cont138.2: +// CHECK-NEXT: [[ADD_PTR50_2:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 19 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 2 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 18 +// CHECK-NEXT: [[TMP22:%.*]] = load i8, ptr [[TMP21]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_2:%.*]] = zext i8 [[TMP22]] to i32 +// CHECK-NEXT: store i32 [[CONV59_2]], ptr [[TMP20]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 16 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 6 +// CHECK-NEXT: [[TMP25:%.*]] = load i8, ptr [[TMP23]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80_2:%.*]] = zext i8 [[TMP25]] to i32 +// CHECK-NEXT: store i32 [[CONV80_2]], ptr [[TMP24]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2, i64 2 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 17 +// CHECK-NEXT: [[TMP28:%.*]] = load i8, ptr [[TMP27]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103_2:%.*]] = zext i8 [[TMP28]] to i32 +// CHECK-NEXT: store i32 [[CONV103_2]], ptr [[TMP26]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3, i64 2 +// CHECK-NEXT: [[TMP30:%.*]] = load i8, ptr [[ADD_PTR50_1]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126_2:%.*]] = zext i8 [[TMP30]] to i32 +// CHECK-NEXT: store i32 [[CONV126_2]], ptr [[TMP29]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT_3:%.*]] = icmp ule ptr [[ADD_PTR50_2]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST_3:%.*]] = ptrtoint ptr [[ADD_PTR50_2]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB_3:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST_3]] +// CHECK-NEXT: [[CMP38_3:%.*]] = icmp sgt i64 [[SUB_PTR_SUB_3]], 3 +// CHECK-NEXT: [[OR_COND166_3:%.*]] = select i1 [[CMP14_NOT_3]], i1 [[CMP38_3]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166_3]], label [[CONT138_3:%.*]], label [[TRAP]] +// CHECK: cont138.3: +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 3 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 22 +// CHECK-NEXT: [[TMP33:%.*]] = load i8, ptr [[TMP32]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59_3:%.*]] = zext i8 [[TMP33]] to i32 +// CHECK-NEXT: store i32 [[CONV59_3]], ptr [[TMP31]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 20 +// CHECK-NEXT: [[TMP35:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 7 +// CHECK-NEXT: [[TMP36:%.*]] = load i8, ptr [[TMP34]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80_3:%.*]] = zext i8 [[TMP36]] to i32 +// CHECK-NEXT: store i32 [[CONV80_3]], ptr [[TMP35]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 2, i64 3 +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 21 +// CHECK-NEXT: [[TMP39:%.*]] = load i8, ptr [[TMP38]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103_3:%.*]] = zext i8 [[TMP39]] to i32 +// CHECK-NEXT: store i32 [[CONV103_3]], ptr [[TMP37]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP40:%.*]] = getelementptr [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3, i64 3 +// CHECK-NEXT: [[TMP41:%.*]] = load i8, ptr [[ADD_PTR50_2]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126_3:%.*]] = zext i8 [[TMP41]] to i32 +// CHECK-NEXT: store i32 [[CONV126_3]], ptr [[TMP40]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[TRAP]] +// +void concat_to_arrays_struct_trap_on_last_iter(struct arrays *arrays, hdr_t *p_buf) { + uint8_t *params = p_buf->payload + 3; + + for (unsigned char i = 0; i < 5; i++) { + uint8_t *__counted_by(4) p = params; + arrays->a[i] = p[3]; // checks not removed for p[] + arrays->b[i] = p[1]; // checks not removed for b[] and p[] + arrays->c[i] = p[2]; // checks not removed for c[] and p[] + arrays->d[i] = p[0]; // checks not removed for d[] + params += 4; + } +} + +// CHECK-LABEL: @concat_to_arrays_struct_cannot_remove_arrays_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PAYLOAD:%.*]] = getelementptr [[STRUCT_HDR_T:%.*]], ptr [[P_BUF:%.*]], i64 0, i32 2 +// CHECK-NEXT: [[OFFSET:%.*]] = getelementptr inbounds [[STRUCT_HDR_T]], ptr [[P_BUF]], i64 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[OFFSET]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV:%.*]] = zext i16 [[TMP0]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[P_BUF]], align 2, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV3:%.*]] = zext i16 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[CONV3]], [[CONV]] +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[PAYLOAD]], i64 [[ADD]] +// CHECK-NEXT: [[CMP168_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP168_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[P_BUF]], i64 7 +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[ADD_PTR]] to i64 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYS:%.*]], i64 4 +// CHECK-NEXT: [[UPPER83:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS:%.*]], ptr [[ARRAYS]], i64 0, i32 2 +// CHECK-NEXT: [[UPPER106:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 0, i32 3 +// CHECK-NEXT: [[UPPER129:%.*]] = getelementptr inbounds [[STRUCT_ARRAYS]], ptr [[ARRAYS]], i64 1 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[PARAMS_SROA_0_0170:%.*]] = phi ptr [ [[BOUND_PTR_ARITH]], [[FOR_BODY_LR_PH]] ], [ [[ADD_PTR50:%.*]], [[CONT138:%.*]] ] +// CHECK-NEXT: [[I_0169:%.*]] = phi i8 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INC:%.*]], [[CONT138]] ] +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp ule ptr [[PARAMS_SROA_0_0170]], [[ADD_PTR]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[PARAMS_SROA_0_0170]] to i64 +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-NEXT: [[CMP38:%.*]] = icmp sgt i64 [[SUB_PTR_SUB]], 3 +// CHECK-NEXT: [[OR_COND166:%.*]] = select i1 [[CMP14_NOT]], i1 [[CMP38]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND166]], label [[CONT:%.*]], label [[TRAP:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR50]] = getelementptr inbounds i8, ptr [[PARAMS_SROA_0_0170]], i64 4 +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i8 [[I_0169]] to i64 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[ARRAYS]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[TMP2]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[TMP2]], [[ARRAYS]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND141:%.*]] = and i1 [[TMP3]], [[TMP4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND141]], label [[CONT69:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont69: +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[PARAMS_SROA_0_0170]], i64 3 +// CHECK-NEXT: [[TMP6:%.*]] = load i8, ptr [[TMP5]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV59:%.*]] = zext i8 [[TMP6]] to i32 +// CHECK-NEXT: store i32 [[CONV59]], ptr [[TMP2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i8, ptr [[PARAMS_SROA_0_0170]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr i32, ptr [[UPPER]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP9:%.*]] = load i8, ptr [[TMP7]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV80:%.*]] = zext i8 [[TMP9]] to i32 +// CHECK-NEXT: store i32 [[CONV80]], ptr [[TMP8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr i32, ptr [[UPPER83]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP11:%.*]] = icmp ult ptr [[TMP10]], [[UPPER106]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = icmp uge ptr [[TMP10]], [[UPPER83]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND145:%.*]] = and i1 [[TMP11]], [[TMP12]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND145]], label [[CONT115:%.*]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont115: +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr i8, ptr [[PARAMS_SROA_0_0170]], i64 2 +// CHECK-NEXT: [[TMP14:%.*]] = load i8, ptr [[TMP13]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV103:%.*]] = zext i8 [[TMP14]] to i32 +// CHECK-NEXT: store i32 [[CONV103]], ptr [[TMP10]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr i32, ptr [[UPPER106]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP16:%.*]] = icmp ult ptr [[TMP15]], [[UPPER129]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP17:%.*]] = icmp uge ptr [[TMP15]], [[UPPER106]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND147:%.*]] = and i1 [[TMP16]], [[TMP17]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND147]], label [[CONT138]], label [[TRAP]], {{!annotation ![0-9]+}} +// CHECK: cont138: +// CHECK-NEXT: [[TMP18:%.*]] = load i8, ptr [[PARAMS_SROA_0_0170]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CONV126:%.*]] = zext i8 [[TMP18]] to i32 +// CHECK-NEXT: store i32 [[CONV126]], ptr [[TMP15]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INC]] = add i8 [[I_0169]], 1 +// CHECK-NEXT: [[CONV4:%.*]] = zext i8 [[INC]] to i32 +// CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[CONV4]], [[N]] +// CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP]], {{!llvm.loop ![0-9]+}} +// +void concat_to_arrays_struct_cannot_remove_arrays_check(struct arrays *arrays, hdr_t *p_buf, unsigned n) { + uint8_t *params = p_buf->payload + 3; + + for (unsigned char i = 0; i < n; i++) { + uint8_t *__counted_by(4) p = params; + arrays->a[i] = p[3]; // checks not removed for p[] + arrays->b[i] = p[1]; // checks not removed for b[] and p[] + arrays->c[i] = p[2]; // checks not removed for c[] and p[] + arrays->d[i] = p[0]; // checks not removed for d[] + params += 4; + } +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations-no-bringup-missing-checks.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-no-bringup-missing-checks.c new file mode 100644 index 0000000000000..5d3a8586f115f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-no-bringup-missing-checks.c @@ -0,0 +1,729 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" --prefix-filecheck-ir-name TMP_ + +// REQUIRES: x86-registered-target + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -fno-split-cold-code -emit-llvm %s -o - | FileCheck --check-prefix=CHECK %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -fno-split-cold-code -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK %s + +#include + +// All accesses in the loop at in bounds and can be eliminated. +// CHECK-LABEL: @loop_all_accesses_in_bounds( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP5_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP5_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 4 [[DST:%.*]], i8 0, i64 [[TMP1]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_access_by_dereference( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[CMP6:%.*]] = icmp sgt i32 [[N]], 0 +// CHECK-NEXT: br i1 [[CMP6]], label [[FOR_BODY:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: [[R_0_LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD:%.*]], [[CONT1:%.*]] ] +// CHECK-NEXT: ret i32 [[R_0_LCSSA]] +// CHECK: for.body: +// CHECK-NEXT: [[B_SROA_0_09:%.*]] = phi ptr [ [[BOUND_PTR_ARITH:%.*]], [[CONT1]] ], [ [[A]], [[ENTRY]] ] +// CHECK-NEXT: [[R_08:%.*]] = phi i32 [ [[ADD]], [[CONT1]] ], [ 0, [[ENTRY]] ] +// CHECK-NEXT: [[I_07:%.*]] = phi i32 [ [[INC:%.*]], [[CONT1]] ], [ 0, [[ENTRY]] ] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[B_SROA_0_09]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[B_SROA_0_09]], [[A]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1]], label [[TRAP:%.*]], !prof [[PROF10:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: [[BOUND_PTR_ARITH]] = getelementptr i8, ptr [[B_SROA_0_09]], i64 4 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[B_SROA_0_09]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD]] = add nsw i32 [[TMP2]], [[R_08]] +// CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_07]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC]], [[N]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +int loop_access_by_dereference(int *__counted_by(n) a, int n) { + int *b = a; + int r = 0; + for (int i = 0; i < n; ++i) { + r += *b++; + } + return r; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP5:%.*]] = icmp ult i32 [[START:%.*]], [[N:%.*]] +// CHECK-NEXT: br i1 [[CMP5]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[START]], -1 +// CHECK-NEXT: [[TMP3:%.*]] = add i32 [[N]], [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = zext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[TMP5:%.*]] = shl nuw nsw i64 [[TMP4]], 2 +// CHECK-NEXT: [[TMP6:%.*]] = add nuw nsw i64 [[TMP5]], 4 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(1) [[SCEVGEP]], i8 0, i64 [[TMP6]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds_variable_start_1(int* __counted_by(n) dst, + unsigned n, unsigned start) { + for (unsigned i = start; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_2_add( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ADD:%.*]] = add i32 [[START:%.*]], 10 +// CHECK-NEXT: [[CMP7:%.*]] = icmp ult i32 [[ADD]], [[N:%.*]] +// CHECK-NEXT: br i1 [[CMP7]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[ADD]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = add i32 [[N]], -11 +// CHECK-NEXT: [[TMP3:%.*]] = sub i32 [[TMP2]], [[START]] +// CHECK-NEXT: [[TMP4:%.*]] = zext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[TMP5:%.*]] = shl nuw nsw i64 [[TMP4]], 2 +// CHECK-NEXT: [[TMP6:%.*]] = add nuw nsw i64 [[TMP5]], 4 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(1) [[SCEVGEP]], i8 0, i64 [[TMP6]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds_variable_start_2_add(int* __counted_by(n) dst, + unsigned n, unsigned start) { + start = start + 10; + for (unsigned i = start; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_3_modulo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = urem i32 [[START:%.*]], [[N]] +// CHECK-NEXT: [[TMP2:%.*]] = zext i32 [[TMP1]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP2]], [[ENTRY:%.*]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[CMP:%.*]] = icmp samesign ult i64 [[INDVARS_IV_NEXT]], [[TMP0]] +// CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]], {{!llvm.loop ![0-9]+}} +// +void loop_all_accesses_in_bounds_variable_start_3_modulo(int* __counted_by(n) dst, + unsigned n, unsigned start) { + start = start % n; + for (unsigned i = start; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_4_div( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DIV:%.*]] = udiv i32 [[START2:%.*]], 10 +// CHECK-NEXT: [[ADD:%.*]] = add i32 [[DIV]], [[START1:%.*]] +// CHECK-NEXT: [[CMP7:%.*]] = icmp ult i32 [[ADD]], [[N:%.*]] +// CHECK-NEXT: br i1 [[CMP7]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[ADD]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[START1]], -1 +// CHECK-NEXT: [[TMP3:%.*]] = add i32 [[N]], [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = sub i32 [[TMP3]], [[DIV]] +// CHECK-NEXT: [[TMP5:%.*]] = zext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[TMP6:%.*]] = shl nuw nsw i64 [[TMP5]], 2 +// CHECK-NEXT: [[TMP7:%.*]] = add nuw nsw i64 [[TMP6]], 4 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(1) [[SCEVGEP]], i8 0, i64 [[TMP7]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds_variable_start_4_div(int* __counted_by(n) dst, + unsigned n, unsigned start1, + unsigned start2) { + start1 += (start2 / 10); + for (unsigned i = start1; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_len_signed( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP5:%.*]] = icmp sgt i32 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP5]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext nneg i32 [[LEN]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 1 +// CHECK-NEXT: store i32 [[ADD]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_all_accesses_in_bounds_len_signed(int* __counted_by(len) buf, int len) { + for (int i = 0; i < len; ++i) + buf[i] += 1; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_length_ull( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP6_NOT:%.*]] = icmp eq i64 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP6_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i64 [[LEN]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP1]]) +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[I_07:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INC:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF:%.*]], i64 [[I_07]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 1 +// CHECK-NEXT: store i32 [[ADD]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INC]] = add nuw i64 [[I_07]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INC]], [[LEN]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_all_accesses_in_bounds_length_ull(int* __counted_by(len) buf, unsigned long long len) { + for (unsigned long long i = 0; i < len; ++i) + buf[i] += 1; +} + +// The lower bound checks can be eliminated, because we know that: +// 1. computing dst + n does not wrap +// 2. i + 1 <= n from loop bound. +// +// 1. and 2. together imply that dst + i + 1 does not wrap, hence dst + i + 1 >= dst is true. + +// CHECK-LABEL: @loop_accesses_out_of_bounds_eliminate_lower_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP6_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP6_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT1:%.*]] ] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT1]], label [[TRAP:%.*]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_eliminate_lower_check(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) + dst[i+1] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_eliminate_lower_check_len_signed( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP5:%.*]] = icmp sgt i32 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP5]], label [[FOR_BODY_LR_PH:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[LEN]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[BUF:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT1:%.*]] ] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT1]], label [[TRAP:%.*]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_eliminate_lower_check_len_signed(int* __counted_by(len) buf, int len) { + for (int i = 0; i < len; ++i) + buf[i+1] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_eliminate_lower_check_len_ull( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP6_NOT:%.*]] = icmp eq i64 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP6_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i64 [[LEN]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP1]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[BUF:%.*]], i64 [[LEN]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[I_07:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[ADD:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: [[ADD]] = add nuw i64 [[I_07]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[ADD]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[ARRAYIDX]], [[BUF]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2]], label [[TRAP:%.*]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[ADD]], [[LEN]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_eliminate_lower_check_len_ull(int* __counted_by(len) buf, unsigned long long len) { + for (unsigned long long i = 0; i < len; ++i) + buf[i+1] = 0; +} + +// No checks can be eliminated, as dst + i + 2 may wrap and is out of bounds. +// CHECK-LABEL: @loop_accesses_out_of_bounds_cannot_eliminate_wrap_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[INVARIANT_GEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 8 +// CHECK-NEXT: [[CMP6_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP6_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT1:%.*]] ] +// CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[INVARIANT_GEP]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[GEP]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1]], label [[TRAP:%.*]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[GEP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_cannot_eliminate_wrap_check(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) + dst[i+2] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_signed_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[INVARIANT_GEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 8 +// CHECK-NEXT: [[CMP6:%.*]] = icmp sgt i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP6]], label [[FOR_BODY_LR_PH:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT1:%.*]] ] +// CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[INVARIANT_GEP]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[GEP]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1]], label [[TRAP:%.*]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[GEP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_signed_len(int* __counted_by(n) dst, int n) { + for (int i = 0; i < n; i += 1) + dst[i+2] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_ull_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[INVARIANT_GEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 8 +// CHECK-NEXT: [[CMP7_NOT:%.*]] = icmp eq i64 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP7_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i64 [[N]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP1]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST]], i64 [[N]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[I_08:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[ADD3:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[INVARIANT_GEP]], i64 [[I_08]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[GEP]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2]], label [[TRAP:%.*]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[GEP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD3]] = add nuw i64 [[I_08]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[ADD3]], [[N]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_ull_len(int* __counted_by(n) dst, unsigned long long n) { + for (unsigned long long i = 0; i < n; i += 1) + dst[i+2] = 0; +} + +// Both lower checks can be eliminated. +// dst + i + 1 >= dst can be eliminated because i + 1 <= n and dst + n does not wrap. +// +// dst + i + 2 >= dst can be eliminated because from the check for dst[i+1] we know: +// 1. dst + n does not wrap. +// 2. dst + i + 1 < dst + n +// +// 1. and 2. together imply dst + i + 1 does not wrap, and dst + i + 2 also +// does not (note the < in 2.). Hence dst + i + 2 >= dst is true. +// +// FIXME: Regressed at the moment, rdar://120485098. +// CHECK-LABEL: @loop_accesses_eliminate_second_lower_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[INVARIANT_GEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 8 +// CHECK-NEXT: [[CMP24_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP24_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT15:%.*]] ] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT1:%.*]], label [[TRAP:%.*]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[INVARIANT_GEP]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[GEP]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND17:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND17]], label [[CONT15]], label [[TRAP]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: cont15: +// CHECK-NEXT: store i32 0, ptr [[GEP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_eliminate_second_lower_check(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) { + dst[i+1] = 0; + dst[i+2] = 0; + } +} + +// We can eliminate the checks for dst[i+1], because the earlier checks for +// dst[i+2] make them redundant. +// CHECK-LABEL: @loop_accesses_eliminate_later_checks( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[INVARIANT_GEP:%.*]] = getelementptr i8, ptr [[DST]], i64 8 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT15:%.*]] ] +// CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[INVARIANT_GEP]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[GEP]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[GEP]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[GEP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX9:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[ARRAYIDX9]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX9]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND17:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND17]], label [[CONT15]], label [[TRAP]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: cont15: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX9]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], 2000 +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_eliminate_later_checks(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < 2000; i += 1) { + dst[i+2] = 0; + dst[i+1] = 0; + } +} + +// The checks for dst[4], dst[3] and dst[2] should be removed, because they are +// redundant after checking dst[5]. +// CHECK-LABEL: @elim_consecutive_writes( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[DST]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX7:%.*]] = getelementptr i8, ptr [[DST]], i64 20 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[ARRAYIDX7]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX7]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND50:%.*]] = and i1 [[TMP3]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND50]], label [[CONT49:%.*]], label [[TRAP]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: cont49: +// CHECK-NEXT: [[ARRAYIDX43:%.*]] = getelementptr i8, ptr [[DST]], i64 8 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(16) [[ARRAYIDX43]], i8 0, i64 16, i1 false) +// CHECK-NEXT: ret void +// +void elim_consecutive_writes(int* __counted_by(n) dst, unsigned n) { + dst[1] = 0; // Need to check for wrap and against upper bound. + dst[5] = 0; // Need to check for wrap and against upper bound. + dst[4] = 0; // No checks needed. + dst[3] = 0; // No checks needed. + dst[2] = 0; // No checks needed. +} + +// CHECK-LABEL: @elim_consecutive_writes2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[IDX:%.*]], 3 +// CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK: if.then: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i32 [[IDX]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT37:%.*]], label [[TRAP:%.*]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont37: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX7:%.*]] = getelementptr i8, ptr [[DST]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX7]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX19:%.*]] = getelementptr i8, ptr [[DST]], i64 12 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX19]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX31:%.*]] = getelementptr i8, ptr [[DST]], i64 8 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX31]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX43:%.*]] = getelementptr i8, ptr [[DST]], i64 20 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[ARRAYIDX43]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX43]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND53:%.*]] = and i1 [[TMP3]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND53]], label [[CONT49:%.*]], label [[TRAP]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: cont49: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX43]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[IF_END]] +// CHECK: if.end: +// CHECK-NEXT: ret void +// +void elim_consecutive_writes2(int* __counted_by(n) dst, unsigned n, unsigned idx) { + if (idx >= 4) { + dst[idx] = 0; // Need to check for wrap and against upper bound. + dst[4] = 0; // No checks needed. + dst[3] = 0; // No checks needed. + dst[2] = 0; // No checks needed. + dst[5] = 0; // Need to check against upper bound. + } +} + +// TODO +// CHECK-LABEL: @count_ptr_relations( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP]], label [[RETURN:%.*]], label [[LAND_RHS:%.*]] +// CHECK: land.rhs: +// CHECK-NEXT: [[SUB:%.*]] = add i32 [[N]], -1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARG:%.*]], i64 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = add nuw nsw i64 [[IDX_EXT]], 4611686018427387903 +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = and i64 [[TMP0]], 4611686018427387903 +// CHECK-NEXT: [[CMP27_NOT:%.*]] = icmp samesign ult i64 [[SUB_PTR_DIV]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27_NOT]], label [[TRAP:%.*]], label [[CONT59:%.*]], !prof [[PROF29:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont59: +// CHECK-NEXT: store i32 0, ptr [[ARG]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX53:%.*]] = getelementptr i32, ptr [[ARG]], i64 [[CONV]] +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX53]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[SUB]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP]], label [[CONT85:%.*]], !prof [[PROF31:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: cont85: +// CHECK-NEXT: store i32 1, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[SUB77:%.*]] = add i32 [[N]], -2 +// CHECK-NEXT: [[IDXPROM78:%.*]] = zext i32 [[SUB77]] to i64 +// CHECK-NEXT: [[ARRAYIDX79:%.*]] = getelementptr i32, ptr [[BOUND_PTR_ARITH]], i64 [[IDXPROM78]] +// CHECK-NEXT: store i32 1, ptr [[ARRAYIDX79]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[RETURN]] +// CHECK: return: +// CHECK-NEXT: ret void +// +void count_ptr_relations(int *__counted_by(n) arg, unsigned n) { + if (n == 0) + return; + unsigned m = n-1; + int *__counted_by(m) ptr = arg + 1; + arg[0] = 0; + arg[n-1] = 0; + ptr[0] = 1; // eliminate checks? + ptr[m-1] = 1; // eliminate checks? +} + +// CHECK-LABEL: @ptrinc0( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP5_NOT:%.*]] = icmp eq i32 [[INLEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP5_NOT]], label [[WHILE_END:%.*]], label [[WHILE_BODY_PREHEADER:%.*]] +// CHECK: while.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[INLEN]] to i64 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 1 [[BUF:%.*]], i8 0, i64 [[TMP0]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[WHILE_END]] +// CHECK: while.end: +// CHECK-NEXT: ret void +// +void ptrinc0(char *__counted_by(inLen) buf, + unsigned inLen) { + unsigned inc = 0; + char *currentBuf = buf; + while (inc < inLen) { + *currentBuf = 0; + inc++; + currentBuf++; + } +} + +// TODO rdar://75687730 +// CHECK-LABEL: @ptrinc1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[INLEN:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[BUF:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[CMP_NOT4:%.*]] = icmp eq i32 [[INLEN]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT4]], label [[WHILE_END:%.*]], label [[WHILE_BODY:%.*]] +// CHECK: while.body: +// CHECK-NEXT: [[CURRENTLEN_06:%.*]] = phi i32 [ [[DEC:%.*]], [[CONT1:%.*]] ], [ [[INLEN]], [[ENTRY:%.*]] ] +// CHECK-NEXT: [[CURRENTBUF_SROA_0_05:%.*]] = phi ptr [ [[BOUND_PTR_ARITH:%.*]], [[CONT1]] ], [ [[BUF]], [[ENTRY]] ] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[CURRENTBUF_SROA_0_05]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[CURRENTBUF_SROA_0_05]], [[BUF]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1]], label [[TRAP:%.*]], !prof [[PROF10]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i8 0, ptr [[CURRENTBUF_SROA_0_05]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DEC]] = add i32 [[CURRENTLEN_06]], -1 +// CHECK-NEXT: [[BOUND_PTR_ARITH]] = getelementptr i8, ptr [[CURRENTBUF_SROA_0_05]], i64 1 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i32 [[DEC]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[WHILE_END]], label [[WHILE_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: while.end: +// CHECK-NEXT: ret void +// +void ptrinc1(char *__counted_by(inLen) buf, + unsigned inLen) { + unsigned currentLen = inLen; + char *currentBuf = buf; + while (currentLen != 0) { + *currentBuf = 0; + currentLen--; + currentBuf++; + } +} + +// TODO: rdar://99414486 - All BoundsSafety checks should be removed. +// CHECK-LABEL: @test_reforge_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult i32 [[IDX:%.*]], [[TABLE_SIZE:%.*]] +// CHECK-NEXT: [[CMP1_NOT37:%.*]] = icmp ne i32 [[IDX]], 0 +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[CMP_NOT]], [[CMP1_NOT37]] +// CHECK-NEXT: br i1 [[OR_COND]], label [[FOR_BODY_PREHEADER:%.*]], label [[RETURN:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[IDX]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[INDVARS_IV_NEXT:%.*]] = add nuw nsw i64 [[INDVARS_IV:%.*]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[RETURN]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT]], [[FOR_COND:%.*]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TABLE:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3_NOT:%.*]] = icmp eq i32 [[TMP0]], [[VALUE:%.*]] +// CHECK-NEXT: br i1 [[CMP3_NOT]], label [[IF_THEN4:%.*]], label [[FOR_COND]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: if.then4: +// CHECK-NEXT: [[BOUND_PTR_ARITH_LE:%.*]] = getelementptr i32, ptr [[TABLE]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[IDX_EXT_LE:%.*]] = zext i32 [[TABLE_SIZE]] to i64 +// CHECK-NEXT: [[ADD_PTR_LE:%.*]] = getelementptr inbounds nuw i32, ptr [[TABLE]], i64 [[IDX_EXT_LE]] +// CHECK-NEXT: [[DOTNOT31:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH_LE]], [[TABLE]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT31]], label [[TRAP:%.*]], label [[RETURN]], !prof [[PROF29]], {{!annotation ![0-9]+}} +// CHECK: return: +// CHECK-NEXT: [[RETVAL_SROA_4_3:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[ADD_PTR_LE]], [[IF_THEN4]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: [[RETVAL_SROA_0_3:%.*]] = phi ptr [ null, [[ENTRY]] ], [ [[BOUND_PTR_ARITH_LE]], [[IF_THEN4]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[RETVAL_SROA_0_3]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[RETVAL_SROA_4_3]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable test_reforge_indexable(unsigned idx, int*__counted_by(table_size) table, + unsigned table_size, int value) { + if (idx >= table_size) + return 0; + + for (unsigned i = 0; i < idx; i++) { + int *__single value_entry = &table[i]; + if (*value_entry == value) + return &table[i]; + } + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-no-bringup-missing-checks.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-no-bringup-missing-checks.c new file mode 100644 index 0000000000000..733de4324be94 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-no-bringup-missing-checks.c @@ -0,0 +1,167 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" + +// REQUIRES: x86-registered-target +// RUN: %clang_cc1 -O2 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct struct_1 { + void *value; +}; + +// CHECK-LABEL: @access_struct_1_all_checks_removable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT22_NOT:%.*]] = icmp eq i32 [[SIZE:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT22_NOT]], label [[CLEANUP13:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[SIZE]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[INDVARS_IV_NEXT:%.*]] = add nuw nsw i64 [[INDVARS_IV:%.*]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[CLEANUP13]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT]], [[FOR_COND:%.*]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_STRUCT_1:%.*]], ptr [[SRC:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BOUND_PTR_ARITH]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3_NOT:%.*]] = icmp eq ptr [[TMP0]], [[VALUE:%.*]] +// CHECK-NEXT: br i1 [[CMP3_NOT]], label [[CLEANUP13_LOOPEXIT_SPLIT_LOOP_EXIT:%.*]], label [[FOR_COND]] +// CHECK: cleanup13.loopexit.split.loop.exit: +// CHECK-NEXT: [[BOUND_PTR_ARITH_LE:%.*]] = getelementptr [[STRUCT_STRUCT_1]], ptr [[SRC]], i64 [[INDVARS_IV]] +// CHECK-NEXT: br label [[CLEANUP13]] +// CHECK: cleanup13: +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[BOUND_PTR_ARITH_LE]], [[CLEANUP13_LOOPEXIT_SPLIT_LOOP_EXIT]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: ret ptr [[SPEC_SELECT]] +// +struct struct_1 * access_struct_1_all_checks_removable( + struct struct_1 *__counted_by(size) src, unsigned size, void *value) { + for (unsigned i = 0; i < size; i++) { + struct struct_1 *value_entry = &src[i]; + if (value_entry->value == value) + return value_entry; + } + return 0; +} + +// CHECK-LABEL: @access_struct_1_checks_needed( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[SIZE:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_STRUCT_1:%.*]], ptr [[SRC:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_COND:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[INDVARS_IV_NEXT:%.*]], [[CONT1:%.*]] ], [ 0, [[ENTRY:%.*]] ] +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[CLEANUP13:%.*]], label [[FOR_BODY:%.*]] +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_STRUCT_1]], ptr [[SRC]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[BOUND_PTR_ARITH]], i64 8 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT1]], !prof [[PROF10:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BOUND_PTR_ARITH]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3_NOT:%.*]] = icmp eq ptr [[TMP1]], [[VALUE:%.*]] +// CHECK-NEXT: br i1 [[CMP3_NOT]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[FOR_COND]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CLEANUP13]], label [[TRAP]], !prof [[PROF12:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: cleanup13: +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = phi ptr [ [[BOUND_PTR_ARITH]], [[BOUNDSCHECK_NOTNULL]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: ret ptr [[SPEC_SELECT]] +// +struct struct_1 * access_struct_1_checks_needed( + struct struct_1 *__counted_by(size) src, unsigned size, void *value) { + for (unsigned i = 0; i < size; i++) { + struct struct_1 *value_entry = &src[i+1]; + if (value_entry->value == value) + return value_entry; + } + return 0; +} + +struct struct_2 { + int a; + int b; +}; + +// CHECK-LABEL: @access_struct_2_all_checks_removable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT36_NOT:%.*]] = icmp eq i32 [[SIZE:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT36_NOT]], label [[CLEANUP24:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[SIZE]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_INC:%.*]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_STRUCT_2:%.*]], ptr [[SRC:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3:%.*]] = icmp eq i32 [[TMP0]], [[A:%.*]] +// CHECK-NEXT: br i1 [[CMP3]], label [[CONT12:%.*]], label [[FOR_INC]] +// CHECK: cont12: +// CHECK-NEXT: [[B13:%.*]] = getelementptr inbounds nuw i8, ptr [[BOUND_PTR_ARITH]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[B13]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14:%.*]] = icmp eq i32 [[TMP1]], [[B:%.*]] +// CHECK-NEXT: br i1 [[CMP14]], label [[CLEANUP24]], label [[FOR_INC]] +// CHECK: for.inc: +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[CLEANUP24]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: cleanup24: +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[BOUND_PTR_ARITH]], [[CONT12]] ], [ null, [[FOR_INC]] ] +// CHECK-NEXT: ret ptr [[SPEC_SELECT]] +// +struct struct_2 * access_struct_2_all_checks_removable( + struct struct_2 *__counted_by(size) src, unsigned size, int a, int b) { + for (unsigned i = 0; i < size; i++) { + struct struct_2 *value_entry = &src[i]; + if (value_entry->a == a && value_entry->b == b) + return value_entry; + } + return 0; +} + +typedef struct { + int f1; + int f2; + int f3; +} MyStruct; + +// All runtime checks in the loop can be removed. +// CHECK-LABEL: @array_of_structs_all_checks_removable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP29_NOT:%.*]] = icmp eq i32 [[NUMITEMS:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP29_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[NUMITEMS]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: [[RES_0_LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD21:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: ret i32 [[RES_0_LCSSA]] +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[RES_031:%.*]] = phi i32 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[ADD21]], [[FOR_BODY]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_MYSTRUCT:%.*]], ptr [[ITEMS:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[F2:%.*]] = getelementptr inbounds nuw i8, ptr [[BOUND_PTR_ARITH]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[F2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[F3:%.*]] = getelementptr inbounds nuw i8, ptr [[BOUND_PTR_ARITH]], i64 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[F3]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add i32 [[TMP0]], [[RES_031]] +// CHECK-NEXT: [[ADD20:%.*]] = add i32 [[ADD]], [[TMP1]] +// CHECK-NEXT: [[ADD21]] = add i32 [[ADD20]], [[TMP2]] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +int array_of_structs_all_checks_removable(MyStruct *__counted_by(numItems) items, + unsigned numItems) { + int res = 0; + for (unsigned i = 0; i < numItems; i++) { + const MyStruct *ptr = &items[i]; + res += ptr->f1 + ptr->f2 + ptr->f3; + } + return res; +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-with-arrays-no-bringup-missing-checks.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-with-arrays-no-bringup-missing-checks.c new file mode 100644 index 0000000000000..2d78e0e487ed8 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-with-arrays-no-bringup-missing-checks.c @@ -0,0 +1,135 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" + +// +// RUN: %clang_cc1 -O2 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct buf { + int member; + int arr[10]; +}; + +// CHECK-LABEL: @array_member_access_can_remove( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw i8, ptr [[BP:%.*]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[ARR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_1:%.*]] = getelementptr i8, ptr [[BP]], i64 8 +// CHECK-NEXT: store i32 1, ptr [[ARRAYIDX_1]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_2:%.*]] = getelementptr i8, ptr [[BP]], i64 12 +// CHECK-NEXT: store i32 2, ptr [[ARRAYIDX_2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_3:%.*]] = getelementptr i8, ptr [[BP]], i64 16 +// CHECK-NEXT: store i32 3, ptr [[ARRAYIDX_3]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_4:%.*]] = getelementptr i8, ptr [[BP]], i64 20 +// CHECK-NEXT: store i32 4, ptr [[ARRAYIDX_4]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_5:%.*]] = getelementptr i8, ptr [[BP]], i64 24 +// CHECK-NEXT: store i32 5, ptr [[ARRAYIDX_5]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_6:%.*]] = getelementptr i8, ptr [[BP]], i64 28 +// CHECK-NEXT: store i32 6, ptr [[ARRAYIDX_6]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_7:%.*]] = getelementptr i8, ptr [[BP]], i64 32 +// CHECK-NEXT: store i32 7, ptr [[ARRAYIDX_7]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_8:%.*]] = getelementptr i8, ptr [[BP]], i64 36 +// CHECK-NEXT: store i32 8, ptr [[ARRAYIDX_8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_9:%.*]] = getelementptr i8, ptr [[BP]], i64 40 +// CHECK-NEXT: store i32 9, ptr [[ARRAYIDX_9]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void array_member_access_can_remove(struct buf *bp) { + for (int i = 0; i < 10; ++i) + bp->arr[i] = i; +} + +// CHECK-LABEL: @array_member_access_trap_on_last_iter( +// CHECK-NEXT: trap: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw i8, ptr [[BP:%.*]], i64 4 +// CHECK-NEXT: store i32 0, ptr [[ARR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_1:%.*]] = getelementptr i8, ptr [[BP]], i64 8 +// CHECK-NEXT: store i32 1, ptr [[ARRAYIDX_1]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_2:%.*]] = getelementptr i8, ptr [[BP]], i64 12 +// CHECK-NEXT: store i32 2, ptr [[ARRAYIDX_2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_3:%.*]] = getelementptr i8, ptr [[BP]], i64 16 +// CHECK-NEXT: store i32 3, ptr [[ARRAYIDX_3]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_4:%.*]] = getelementptr i8, ptr [[BP]], i64 20 +// CHECK-NEXT: store i32 4, ptr [[ARRAYIDX_4]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_5:%.*]] = getelementptr i8, ptr [[BP]], i64 24 +// CHECK-NEXT: store i32 5, ptr [[ARRAYIDX_5]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_6:%.*]] = getelementptr i8, ptr [[BP]], i64 28 +// CHECK-NEXT: store i32 6, ptr [[ARRAYIDX_6]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_7:%.*]] = getelementptr i8, ptr [[BP]], i64 32 +// CHECK-NEXT: store i32 7, ptr [[ARRAYIDX_7]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_8:%.*]] = getelementptr i8, ptr [[BP]], i64 36 +// CHECK-NEXT: store i32 8, ptr [[ARRAYIDX_8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_9:%.*]] = getelementptr i8, ptr [[BP]], i64 40 +// CHECK-NEXT: store i32 9, ptr [[ARRAYIDX_9]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void array_member_access_trap_on_last_iter(struct buf *bp) { + for (int i = 0; i < 11; ++i) + bp->arr[i] = i; +} + +struct buf_var_size { + unsigned size; + int arr[__counted_by(size)]; +}; + +// CHECK-LABEL: @array_member_access_can_remove_variable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BP:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp eq i32 [[TMP0]], 0 +// CHECK-NEXT: br i1 [[CMP14_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw i8, ptr [[BP]], i64 4 +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP1:%.*]] = trunc nuw nsw i64 [[INDVARS_IV]] to i32 +// CHECK-NEXT: store i32 [[TMP1]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void array_member_access_can_remove_variable(struct buf_var_size *bp) { + for (int i = 0; i < bp->size; ++i) + bp->arr[i] = i; +} + +// CHECK-LABEL: @array_member_access_cannot_remove_variable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP12_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP12_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw i8, ptr [[BP:%.*]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[ARR]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT8:%.*]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT8]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: [[TMP3:%.*]] = trunc nuw nsw i64 [[INDVARS_IV]] to i32 +// CHECK-NEXT: store i32 [[TMP3]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void array_member_access_cannot_remove_variable(struct buf_var_size *bp, unsigned n) { + for (int i = 0; i < n; ++i) + bp->arr[i] = i; +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-with-arrays.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-with-arrays.c new file mode 100644 index 0000000000000..ea1ea095b00ce --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs-with-arrays.c @@ -0,0 +1,160 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" --prefix-filecheck-ir-name TMP_ + +// +// RUN: %clang_cc1 -O2 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct buf { + int member; + int arr[10]; +}; + +// CHECK-LABEL: @array_member_access_can_remove( +// CHECK-NEXT: cont2.9: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw i8, ptr [[BP:%.*]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[BP]], i64 8 +// CHECK-NEXT: store i32 0, ptr [[ARR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_2:%.*]] = getelementptr i8, ptr [[BP]], i64 12 +// CHECK-NEXT: store i32 2, ptr [[ARRAYIDX_2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_3:%.*]] = getelementptr i8, ptr [[BP]], i64 16 +// CHECK-NEXT: store i32 3, ptr [[ARRAYIDX_3]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_4:%.*]] = getelementptr i8, ptr [[BP]], i64 20 +// CHECK-NEXT: store i32 4, ptr [[ARRAYIDX_4]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_5:%.*]] = getelementptr i8, ptr [[BP]], i64 24 +// CHECK-NEXT: store i32 5, ptr [[ARRAYIDX_5]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_6:%.*]] = getelementptr i8, ptr [[BP]], i64 28 +// CHECK-NEXT: store i32 6, ptr [[ARRAYIDX_6]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_7:%.*]] = getelementptr i8, ptr [[BP]], i64 32 +// CHECK-NEXT: store i32 7, ptr [[ARRAYIDX_7]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_8:%.*]] = getelementptr i8, ptr [[BP]], i64 36 +// CHECK-NEXT: store i32 8, ptr [[ARRAYIDX_8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_9:%.*]] = getelementptr i8, ptr [[BP]], i64 40 +// CHECK-NEXT: store i32 9, ptr [[ARRAYIDX_9]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void array_member_access_can_remove(struct buf *bp) { + for (int i = 0; i < 10; ++i) + bp->arr[i] = i; +} + +// CHECK-LABEL: @array_member_access_trap_on_last_iter( +// CHECK-NEXT: cont2.9: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw i8, ptr [[BP:%.*]], i64 4 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[BP]], i64 44 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[BP]], i64 8 +// CHECK-NEXT: store i32 0, ptr [[ARR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: store i32 1, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_2:%.*]] = getelementptr i8, ptr [[BP]], i64 12 +// CHECK-NEXT: store i32 2, ptr [[ARRAYIDX_2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_3:%.*]] = getelementptr i8, ptr [[BP]], i64 16 +// CHECK-NEXT: store i32 3, ptr [[ARRAYIDX_3]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_4:%.*]] = getelementptr i8, ptr [[BP]], i64 20 +// CHECK-NEXT: store i32 4, ptr [[ARRAYIDX_4]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_5:%.*]] = getelementptr i8, ptr [[BP]], i64 24 +// CHECK-NEXT: store i32 5, ptr [[ARRAYIDX_5]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_6:%.*]] = getelementptr i8, ptr [[BP]], i64 28 +// CHECK-NEXT: store i32 6, ptr [[ARRAYIDX_6]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_7:%.*]] = getelementptr i8, ptr [[BP]], i64 32 +// CHECK-NEXT: store i32 7, ptr [[ARRAYIDX_7]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_8:%.*]] = getelementptr i8, ptr [[BP]], i64 36 +// CHECK-NEXT: store i32 8, ptr [[ARRAYIDX_8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_9:%.*]] = getelementptr i8, ptr [[BP]], i64 40 +// CHECK-NEXT: store i32 9, ptr [[ARRAYIDX_9]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX_10:%.*]] = getelementptr i8, ptr [[BP]], i64 44 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[BP]], i64 48, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX_10]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND_10:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND_10]], label [[CONT2_10:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2.10: +// CHECK-NEXT: store i32 10, ptr [[ARRAYIDX_10]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void array_member_access_trap_on_last_iter(struct buf *bp) { + for (int i = 0; i < 11; ++i) + bp->arr[i] = i; +} + +struct buf_var_size { + unsigned size; + int arr[__counted_by(size)]; +}; + +// rdar://128576231 +// CHECK-LABEL: @array_member_access_can_remove_variable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BP:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP16_NOT:%.*]] = icmp eq i32 [[TMP0]], 0 +// CHECK-NEXT: br i1 [[CMP16_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw i8, ptr [[BP]], i64 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[ARR]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT10:%.*]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT10]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont10: +// CHECK-NEXT: [[TMP4:%.*]] = trunc nuw nsw i64 [[INDVARS_IV]] to i32 +// CHECK-NEXT: store i32 [[TMP4]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void array_member_access_can_remove_variable(struct buf_var_size *bp) { + for (int i = 0; i < bp->size; ++i) + bp->arr[i] = i; +} + +// CHECK-LABEL: @array_member_access_cannot_remove_variable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP14_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP14_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[ARR:%.*]] = getelementptr inbounds nuw i8, ptr [[BP:%.*]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BP]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[ARR]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT9:%.*]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[ARR]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP3]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND10:%.*]] = and i1 [[TMP4]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND10]], label [[CONT9]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont9: +// CHECK-NEXT: [[TMP5:%.*]] = trunc nuw nsw i64 [[INDVARS_IV]] to i32 +// CHECK-NEXT: store i32 [[TMP5]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void array_member_access_cannot_remove_variable(struct buf_var_size *bp, unsigned n) { + for (int i = 0; i < n; ++i) + bp->arr[i] = i; +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs.c new file mode 100644 index 0000000000000..ea1de7371a32e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations-structs.c @@ -0,0 +1,167 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" + +// REQUIRES: x86-registered-target +// RUN: %clang_cc1 -O2 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct struct_1 { + void *value; +}; + +// CHECK-LABEL: @access_struct_1_all_checks_removable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT24_NOT:%.*]] = icmp eq i32 [[SIZE:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT24_NOT]], label [[CLEANUP14:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[SIZE]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[INDVARS_IV_NEXT:%.*]] = add nuw nsw i64 [[INDVARS_IV:%.*]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[CLEANUP14]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT]], [[FOR_COND:%.*]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_STRUCT_1:%.*]], ptr [[SRC:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BOUND_PTR_ARITH]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3_NOT:%.*]] = icmp eq ptr [[TMP0]], [[VALUE:%.*]] +// CHECK-NEXT: br i1 [[CMP3_NOT]], label [[CLEANUP14_LOOPEXIT_SPLIT_LOOP_EXIT:%.*]], label [[FOR_COND]] +// CHECK: cleanup14.loopexit.split.loop.exit: +// CHECK-NEXT: [[BOUND_PTR_ARITH_LE:%.*]] = getelementptr [[STRUCT_STRUCT_1]], ptr [[SRC]], i64 [[INDVARS_IV]] +// CHECK-NEXT: br label [[CLEANUP14]] +// CHECK: cleanup14: +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[BOUND_PTR_ARITH_LE]], [[CLEANUP14_LOOPEXIT_SPLIT_LOOP_EXIT]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: ret ptr [[SPEC_SELECT]] +// +struct struct_1 * access_struct_1_all_checks_removable( + struct struct_1 *__counted_by(size) src, unsigned size, void *value) { + for (unsigned i = 0; i < size; i++) { + struct struct_1 *value_entry = &src[i]; + if (value_entry->value == value) + return value_entry; + } + return 0; +} + +// CHECK-LABEL: @access_struct_1_checks_needed( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[SIZE:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_STRUCT_1:%.*]], ptr [[SRC:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_COND:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[INDVARS_IV_NEXT:%.*]], [[CONT1:%.*]] ], [ 0, [[ENTRY:%.*]] ] +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[CLEANUP14:%.*]], label [[FOR_BODY:%.*]] +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_STRUCT_1]], ptr [[SRC]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[BOUND_PTR_ARITH]], i64 8 +// CHECK-NEXT: [[DOTNOT25:%.*]] = icmp ugt ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT25]], label [[TRAP:%.*]], label [[CONT1]], !prof [[PROF10:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BOUND_PTR_ARITH]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3_NOT:%.*]] = icmp eq ptr [[TMP1]], [[VALUE:%.*]] +// CHECK-NEXT: br i1 [[CMP3_NOT]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[FOR_COND]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[BOUND_PTR_ARITH]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP]], label [[CLEANUP14]], !prof [[PROF13:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: cleanup14: +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = phi ptr [ [[BOUND_PTR_ARITH]], [[BOUNDSCHECK_NOTNULL]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: ret ptr [[SPEC_SELECT]] +// +struct struct_1 * access_struct_1_checks_needed( + struct struct_1 *__counted_by(size) src, unsigned size, void *value) { + for (unsigned i = 0; i < size; i++) { + struct struct_1 *value_entry = &src[i+1]; + if (value_entry->value == value) + return value_entry; + } + return 0; +} + +struct struct_2 { + int a; + int b; +}; + +// CHECK-LABEL: @access_struct_2_all_checks_removable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT38_NOT:%.*]] = icmp eq i32 [[SIZE:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT38_NOT]], label [[CLEANUP25:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[SIZE]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_INC:%.*]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_STRUCT_2:%.*]], ptr [[SRC:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP3:%.*]] = icmp eq i32 [[TMP0]], [[A:%.*]] +// CHECK-NEXT: br i1 [[CMP3]], label [[CONT12:%.*]], label [[FOR_INC]] +// CHECK: cont12: +// CHECK-NEXT: [[B13:%.*]] = getelementptr inbounds nuw i8, ptr [[BOUND_PTR_ARITH]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[B13]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP14:%.*]] = icmp eq i32 [[TMP1]], [[B:%.*]] +// CHECK-NEXT: br i1 [[CMP14]], label [[CLEANUP25]], label [[FOR_INC]] +// CHECK: for.inc: +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[CLEANUP25]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: cleanup25: +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[BOUND_PTR_ARITH]], [[CONT12]] ], [ null, [[FOR_INC]] ] +// CHECK-NEXT: ret ptr [[SPEC_SELECT]] +// +struct struct_2 * access_struct_2_all_checks_removable( + struct struct_2 *__counted_by(size) src, unsigned size, int a, int b) { + for (unsigned i = 0; i < size; i++) { + struct struct_2 *value_entry = &src[i]; + if (value_entry->a == a && value_entry->b == b) + return value_entry; + } + return 0; +} + +typedef struct { + int f1; + int f2; + int f3; +} MyStruct; + +// All runtime checks in the loop can be removed. +// CHECK-LABEL: @array_of_structs_all_checks_removable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP29_NOT:%.*]] = icmp eq i32 [[NUMITEMS:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP29_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[NUMITEMS]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: [[RES_0_LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD21:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: ret i32 [[RES_0_LCSSA]] +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[RES_031:%.*]] = phi i32 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[ADD21]], [[FOR_BODY]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr [[STRUCT_MYSTRUCT:%.*]], ptr [[ITEMS:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[F2:%.*]] = getelementptr inbounds nuw i8, ptr [[BOUND_PTR_ARITH]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[F2]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[F3:%.*]] = getelementptr inbounds nuw i8, ptr [[BOUND_PTR_ARITH]], i64 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[F3]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add i32 [[TMP0]], [[RES_031]] +// CHECK-NEXT: [[ADD20:%.*]] = add i32 [[ADD]], [[TMP1]] +// CHECK-NEXT: [[ADD21]] = add i32 [[ADD20]], [[TMP2]] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +int array_of_structs_all_checks_removable(MyStruct *__counted_by(numItems) items, + unsigned numItems) { + int res = 0; + for (unsigned i = 0; i < numItems; i++) { + const MyStruct *ptr = &items[i]; + res += ptr->f1 + ptr->f2 + ptr->f3; + } + return res; +} diff --git a/clang/test/BoundsSafety/CodeGen/range-check-optimizations.c b/clang/test/BoundsSafety/CodeGen/range-check-optimizations.c new file mode 100644 index 0000000000000..1ba08f310e0fa --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/range-check-optimizations.c @@ -0,0 +1,800 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!llvm.loop ![0-9]+" "#[0-9]+" --prefix-filecheck-ir-name TMP_ + +// REQUIRES: x86-registered-target + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -fno-split-cold-code -emit-llvm %s -o - | FileCheck --check-prefix=CHECK %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -fbounds-safety-bringup-missing-checks=all -fno-split-cold-code -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK %s + +#include + +// All accesses in the loop at in bounds and can be eliminated. +// CHECK-LABEL: @loop_all_accesses_in_bounds( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP7_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP7_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_PREHEADER:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 4 [[DST:%.*]], i8 0, i64 [[TMP1]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_access_by_dereference( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[CMP8:%.*]] = icmp sgt i32 [[N]], 0 +// CHECK-NEXT: br i1 [[CMP8]], label [[FOR_BODY:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: [[R_0_LCSSA:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: ret i32 [[R_0_LCSSA]] +// CHECK: for.body: +// CHECK-NEXT: [[B_SROA_0_011:%.*]] = phi ptr [ [[BOUND_PTR_ARITH:%.*]], [[CONT2]] ], [ [[A]], [[ENTRY]] ] +// CHECK-NEXT: [[R_010:%.*]] = phi i32 [ [[ADD]], [[CONT2]] ], [ 0, [[ENTRY]] ] +// CHECK-NEXT: [[I_09:%.*]] = phi i32 [ [[INC:%.*]], [[CONT2]] ], [ 0, [[ENTRY]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH]] = getelementptr i8, ptr [[B_SROA_0_011]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ule ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[B_SROA_0_011]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp uge ptr [[B_SROA_0_011]], [[A]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND3:%.*]] = and i1 [[TMP2]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND3]], label [[CONT2]], label [[TRAP:%.*]], !prof [[PROF11:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[B_SROA_0_011]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD]] = add nsw i32 [[TMP3]], [[R_010]] +// CHECK-NEXT: [[INC]] = add nuw nsw i32 [[I_09]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i32 [[INC]], [[N]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +int loop_access_by_dereference(int *__counted_by(n) a, int n) { + int *b = a; + int r = 0; + for (int i = 0; i < n; ++i) { + r += *b++; + } + return r; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP7:%.*]] = icmp ult i32 [[START:%.*]], [[N:%.*]] +// CHECK-NEXT: br i1 [[CMP7]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[START]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[START]], -1 +// CHECK-NEXT: [[TMP3:%.*]] = add i32 [[N]], [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = zext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[TMP5:%.*]] = shl nuw nsw i64 [[TMP4]], 2 +// CHECK-NEXT: [[TMP6:%.*]] = add nuw nsw i64 [[TMP5]], 4 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(1) [[SCEVGEP]], i8 0, i64 [[TMP6]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds_variable_start_1(int* __counted_by(n) dst, + unsigned n, unsigned start) { + for (unsigned i = start; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_2_add( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ADD:%.*]] = add i32 [[START:%.*]], 10 +// CHECK-NEXT: [[CMP9:%.*]] = icmp ult i32 [[ADD]], [[N:%.*]] +// CHECK-NEXT: br i1 [[CMP9]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[ADD]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = add i32 [[N]], -11 +// CHECK-NEXT: [[TMP3:%.*]] = sub i32 [[TMP2]], [[START]] +// CHECK-NEXT: [[TMP4:%.*]] = zext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[TMP5:%.*]] = shl nuw nsw i64 [[TMP4]], 2 +// CHECK-NEXT: [[TMP6:%.*]] = add nuw nsw i64 [[TMP5]], 4 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(1) [[SCEVGEP]], i8 0, i64 [[TMP6]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds_variable_start_2_add(int* __counted_by(n) dst, + unsigned n, unsigned start) { + start = start + 10; + for (unsigned i = start; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_3_modulo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = urem i32 [[START:%.*]], [[N]] +// CHECK-NEXT: [[TMP2:%.*]] = zext i32 [[TMP1]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[TMP2]], [[ENTRY:%.*]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[CMP:%.*]] = icmp samesign ult i64 [[INDVARS_IV_NEXT]], [[TMP0]] +// CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]], {{!llvm.loop ![0-9]+}} +// +void loop_all_accesses_in_bounds_variable_start_3_modulo(int* __counted_by(n) dst, + unsigned n, unsigned start) { + start = start % n; + for (unsigned i = start; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_variable_start_4_div( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DIV:%.*]] = udiv i32 [[START2:%.*]], 10 +// CHECK-NEXT: [[ADD:%.*]] = add i32 [[DIV]], [[START1:%.*]] +// CHECK-NEXT: [[CMP9:%.*]] = icmp ult i32 [[ADD]], [[N:%.*]] +// CHECK-NEXT: br i1 [[CMP9]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[ADD]] to i64 +// CHECK-NEXT: [[TMP1:%.*]] = shl nuw nsw i64 [[TMP0]], 2 +// CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[DST:%.*]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[START1]], -1 +// CHECK-NEXT: [[TMP3:%.*]] = add i32 [[N]], [[TMP2]] +// CHECK-NEXT: [[TMP4:%.*]] = sub i32 [[TMP3]], [[DIV]] +// CHECK-NEXT: [[TMP5:%.*]] = zext i32 [[TMP4]] to i64 +// CHECK-NEXT: [[TMP6:%.*]] = shl nuw nsw i64 [[TMP5]], 2 +// CHECK-NEXT: [[TMP7:%.*]] = add nuw nsw i64 [[TMP6]], 4 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(1) [[SCEVGEP]], i8 0, i64 [[TMP7]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[FOR_COND_CLEANUP]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// +void loop_all_accesses_in_bounds_variable_start_4_div(int* __counted_by(n) dst, + unsigned n, unsigned start1, + unsigned start2) { + start1 += (start2 / 10); + for (unsigned i = start1; i < n; i += 1) + dst[i] = 0; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_len_signed( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP7:%.*]] = icmp sgt i32 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP7]], label [[FOR_BODY_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext nneg i32 [[LEN]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 1 +// CHECK-NEXT: store i32 [[ADD]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_all_accesses_in_bounds_len_signed(int* __counted_by(len) buf, int len) { + for (int i = 0; i < len; ++i) + buf[i] += 1; +} + +// CHECK-LABEL: @loop_all_accesses_in_bounds_length_ull( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP8_NOT:%.*]] = icmp eq i64 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP8_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i64 [[LEN]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP1]]) +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[I_09:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INC:%.*]], [[FOR_BODY]] ] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF:%.*]], i64 [[I_09]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 1 +// CHECK-NEXT: store i32 [[ADD]], ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INC]] = add nuw i64 [[I_09]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INC]], [[LEN]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_all_accesses_in_bounds_length_ull(int* __counted_by(len) buf, unsigned long long len) { + for (unsigned long long i = 0; i < len; ++i) + buf[i] += 1; +} + +// The lower bound checks can be eliminated, because we know that: +// 1. computing dst + n does not wrap +// 2. i + 1 <= n from loop bound. +// +// 1. and 2. together imply that dst + i + 1 does not wrap, hence dst + i + 1 >= dst is true. + +// CHECK-LABEL: @loop_accesses_out_of_bounds_eliminate_lower_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP8_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP8_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2]], label [[TRAP:%.*]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_eliminate_lower_check(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) + dst[i+1] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_eliminate_lower_check_len_signed( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP7:%.*]] = icmp sgt i32 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP7]], label [[FOR_BODY_LR_PH:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[LEN]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[BUF:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2]], label [[TRAP:%.*]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_eliminate_lower_check_len_signed(int* __counted_by(len) buf, int len) { + for (int i = 0; i < len; ++i) + buf[i+1] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_eliminate_lower_check_len_ull( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP8_NOT:%.*]] = icmp eq i64 [[LEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP8_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i64 [[LEN]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP1]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[BUF:%.*]], i64 [[LEN]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[I_09:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[ADD:%.*]], [[CONT3:%.*]] ] +// CHECK-NEXT: [[ADD]] = add nuw i64 [[I_09]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[BUF]], i64 [[ADD]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], [[BUF]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND4:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND4]], label [[CONT3]], label [[TRAP:%.*]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont3: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[ADD]], [[LEN]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_eliminate_lower_check_len_ull(int* __counted_by(len) buf, unsigned long long len) { + for (unsigned long long i = 0; i < len; ++i) + buf[i+1] = 0; +} + +// No checks can be eliminated, as dst + i + 2 may wrap and is out of bounds. +// CHECK-LABEL: @loop_accesses_out_of_bounds_cannot_eliminate_wrap_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP8_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP8_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[TMP0]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i64 12, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND4:%.*]] = and i1 [[TMP4]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND4]], label [[CONT2]], label [[TRAP:%.*]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_cannot_eliminate_wrap_check(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) + dst[i+2] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_signed_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP8:%.*]] = icmp sgt i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP8]], label [[FOR_BODY_LR_PH:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext nneg i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT2:%.*]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[TMP0]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i64 12, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND4:%.*]] = and i1 [[TMP4]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND4]], label [[CONT2]], label [[TRAP:%.*]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_signed_len(int* __counted_by(n) dst, int n) { + for (int i = 0; i < n; i += 1) + dst[i+2] = 0; +} + +// CHECK-LABEL: @loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_ull_len( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP9_NOT:%.*]] = icmp eq i64 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP9_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i64 [[N]], -1 +// CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP1]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[N]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[I_010:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[ADD4:%.*]], [[CONT3:%.*]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[I_010]] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[TMP0]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i64 12, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND5:%.*]] = and i1 [[TMP4]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND5]], label [[CONT3]], label [[TRAP:%.*]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont3: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD4]] = add nuw i64 [[I_010]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[ADD4]], [[N]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_out_of_bounds_cannot_eliminate_wrap_check_ull_len(int* __counted_by(n) dst, unsigned long long n) { + for (unsigned long long i = 0; i < n; i += 1) + dst[i+2] = 0; +} + +// Both lower checks can be eliminated. +// dst + i + 1 >= dst can be eliminated because i + 1 <= n and dst + n does not wrap. +// +// dst + i + 2 >= dst can be eliminated because from the check for dst[i+1] we know: +// 1. dst + n does not wrap. +// 2. dst + i + 1 < dst + n +// +// 1. and 2. together imply dst + i + 1 does not wrap, and dst + i + 2 also +// does not (note the < in 2.). Hence dst + i + 2 >= dst is true. +// +// FIXME: Regressed at the moment, rdar://120485098. +// CHECK-LABEL: @loop_accesses_eliminate_second_lower_check( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP28_NOT:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP28_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY_LR_PH:%.*]] +// CHECK: for.body.lr.ph: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT17:%.*]] ] +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT2:%.*]], label [[TRAP:%.*]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[ARRAYIDX10:%.*]] = getelementptr i8, ptr [[TMP3]], i64 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[TMP3]], i64 12, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[ARRAYIDX10]], [[TMP4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND20:%.*]] = and i1 [[TMP5]], [[TMP6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[ARRAYIDX10]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND21:%.*]] = and i1 [[TMP7]], [[OR_COND20]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND21]], label [[CONT17]], label [[TRAP]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: cont17: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX10]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[IDX_EXT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_eliminate_second_lower_check(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < n; i += 1) { + dst[i+1] = 0; + dst[i+2] = 0; + } +} + +// We can eliminate the checks for dst[i+1], because the earlier checks for +// dst[i+2] make them redundant. +// CHECK-LABEL: @loop_accesses_eliminate_later_checks( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond.cleanup: +// CHECK-NEXT: ret void +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT17:%.*]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[TMP0]], i64 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i64 12, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND19:%.*]] = and i1 [[TMP4]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND19]], label [[CONT2:%.*]], label [[TRAP:%.*]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-NEXT: [[ARRAYIDX10:%.*]] = getelementptr i32, ptr [[DST]], i64 [[INDVARS_IV_NEXT]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[ARRAYIDX10]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[TMP5]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = icmp ule ptr [[ARRAYIDX10]], [[TMP5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND20:%.*]] = and i1 [[TMP6]], [[TMP7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = icmp uge ptr [[ARRAYIDX10]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND21:%.*]] = and i1 [[TMP8]], [[OR_COND20]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND21]], label [[CONT17]], label [[TRAP]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: cont17: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX10]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], 2000 +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// +void loop_accesses_eliminate_later_checks(int* __counted_by(n) dst, unsigned n) { + for (int i = 0; i < 2000; i += 1) { + dst[i+2] = 0; + dst[i+1] = 0; + } +} + +// The checks for dst[4], dst[3] and dst[2] should be removed, because they are +// redundant after checking dst[5]. +// CHECK-LABEL: @elim_consecutive_writes( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[DST]], i64 4 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[DST]], i64 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND55:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND55]], label [[CONT2:%.*]], label [[TRAP:%.*]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX8:%.*]] = getelementptr i8, ptr [[DST]], i64 20 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DST]], i64 24, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[ARRAYIDX8]], [[TMP4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND56:%.*]] = and i1 [[TMP6]], [[TMP5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[ARRAYIDX8]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND57:%.*]] = and i1 [[TMP7]], [[OR_COND56]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND57]], label [[CONT15:%.*]], label [[TRAP]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: cont15: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARRAYIDX8]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP]], label [[CONT54:%.*]], !prof [[PROF26:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: cont54: +// CHECK-NEXT: [[ARRAYIDX21:%.*]] = getelementptr i8, ptr [[DST]], i64 16 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX21]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX34:%.*]] = getelementptr i8, ptr [[DST]], i64 12 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX34]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: store i32 0, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void elim_consecutive_writes(int* __counted_by(n) dst, unsigned n) { + dst[1] = 0; // Need to check for wrap and against upper bound. + dst[5] = 0; // Need to check for wrap and against upper bound. + dst[4] = 0; // No checks needed. + dst[3] = 0; // No checks needed. + dst[2] = 0; // No checks needed. +} + +// CHECK-LABEL: @elim_consecutive_writes2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[IDX:%.*]], 3 +// CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK: if.then: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[DST:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[IDXPROM:%.*]] = zext i32 [[IDX]] to i64 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[DST]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARRAYIDX]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[ARRAYIDX]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp uge ptr [[ARRAYIDX]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND55:%.*]] = and i1 [[TMP3]], [[OR_COND]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND55]], label [[CONT2:%.*]], label [[TRAP:%.*]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX8:%.*]] = getelementptr i8, ptr [[DST]], i64 16 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[DST]], i64 20, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[ARRAYIDX8]], [[TMP4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND56:%.*]] = and i1 [[TMP6]], [[TMP5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND56]], label [[CONT15:%.*]], label [[TRAP]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: cont15: +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX8]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[ARRAYIDX8]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP]], label [[CONT41:%.*]], !prof [[PROF26]], {{!annotation ![0-9]+}} +// CHECK: cont41: +// CHECK-NEXT: [[ARRAYIDX21:%.*]] = getelementptr i8, ptr [[DST]], i64 12 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX21]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX34:%.*]] = getelementptr i8, ptr [[DST]], i64 8 +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX34]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i8, ptr [[DST]], i64 24, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = icmp ule ptr [[TMP7]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = icmp ule ptr [[TMP4]], [[TMP7]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND62:%.*]] = and i1 [[TMP9]], [[TMP8]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[TMP4]], [[DST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND63:%.*]] = and i1 [[TMP10]], [[OR_COND62]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND63]], label [[CONT54:%.*]], label [[TRAP]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: cont54: +// CHECK-NEXT: store i32 0, ptr [[TMP4]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[IF_END]] +// CHECK: if.end: +// CHECK-NEXT: ret void +// +void elim_consecutive_writes2(int* __counted_by(n) dst, unsigned n, unsigned idx) { + if (idx >= 4) { + dst[idx] = 0; // Need to check for wrap and against upper bound. + dst[4] = 0; // No checks needed. + dst[3] = 0; // No checks needed. + dst[2] = 0; // No checks needed. + dst[5] = 0; // Need to check against upper bound. + } +} + +// TODO +// CHECK-LABEL: @count_ptr_relations( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[N:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP]], label [[RETURN:%.*]], label [[LAND_RHS:%.*]] +// CHECK: land.rhs: +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARG:%.*]], i64 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[N]] to i64 +// CHECK-NEXT: [[SUB:%.*]] = add i32 [[N]], -1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CONV:%.*]] = zext i32 [[SUB]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = add nuw nsw i64 [[IDX_EXT]], 4611686018427387903 +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = and i64 [[TMP0]], 4611686018427387903 +// CHECK-NEXT: [[CMP27_NOT:%.*]] = icmp samesign ult i64 [[SUB_PTR_DIV]], [[CONV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27_NOT]], label [[TRAP:%.*]], label [[CONT61:%.*]], !prof [[PROF30:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont61: +// CHECK-NEXT: store i32 0, ptr [[ARG]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ARRAYIDX54:%.*]] = getelementptr i32, ptr [[ARG]], i64 [[CONV]] +// CHECK-NEXT: store i32 0, ptr [[ARRAYIDX54]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD_PTR64:%.*]] = getelementptr inbounds nuw i32, ptr [[BOUND_PTR_ARITH]], i64 [[CONV]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[ARG]], i64 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[TMP1]], [[ADD_PTR64]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp ule ptr [[BOUND_PTR_ARITH]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND93:%.*]] = and i1 [[TMP3]], [[TMP2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND93]], label [[CONT74:%.*]], label [[TRAP]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: cont74: +// CHECK-NEXT: store i32 1, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[SUB80:%.*]] = add i32 [[N]], -2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[IDXPROM81:%.*]] = zext i32 [[SUB80]] to i64 +// CHECK-NEXT: [[ARRAYIDX82:%.*]] = getelementptr i32, ptr [[BOUND_PTR_ARITH]], i64 [[IDXPROM81]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[ARRAYIDX82]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = icmp ule ptr [[TMP4]], [[ADD_PTR64]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = icmp ule ptr [[ARRAYIDX82]], [[TMP4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND95:%.*]] = and i1 [[TMP5]], [[TMP6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = icmp uge ptr [[ARRAYIDX82]], [[BOUND_PTR_ARITH]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND96:%.*]] = and i1 [[TMP7]], [[OR_COND95]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND96]], label [[CONT89:%.*]], label [[TRAP]], !prof [[PROF11]], {{!annotation ![0-9]+}} +// CHECK: cont89: +// CHECK-NEXT: store i32 1, ptr [[ARRAYIDX82]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[RETURN]] +// CHECK: return: +// CHECK-NEXT: ret void +// +void count_ptr_relations(int *__counted_by(n) arg, unsigned n) { + if (n == 0) + return; + unsigned m = n-1; + int *__counted_by(m) ptr = arg + 1; + arg[0] = 0; + arg[n-1] = 0; + ptr[0] = 1; // eliminate checks? + ptr[m-1] = 1; // eliminate checks? +} + +// CHECK-LABEL: @ptrinc0( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP5_NOT:%.*]] = icmp eq i32 [[INLEN:%.*]], 0 +// CHECK-NEXT: br i1 [[CMP5_NOT]], label [[WHILE_END:%.*]], label [[WHILE_BODY_PREHEADER:%.*]] +// CHECK: while.body.preheader: +// CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[INLEN]] to i64 +// CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 1 [[BUF:%.*]], i8 0, i64 [[TMP0]], i1 false), {{!tbaa ![0-9]+}} +// CHECK-NEXT: br label [[WHILE_END]] +// CHECK: while.end: +// CHECK-NEXT: ret void +// +void ptrinc0(char *__counted_by(inLen) buf, + unsigned inLen) { + unsigned inc = 0; + char *currentBuf = buf; + while (inc < inLen) { + *currentBuf = 0; + inc++; + currentBuf++; + } +} + +// TODO rdar://75687730 +// CHECK-LABEL: @ptrinc1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[INLEN:%.*]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[BUF:%.*]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[CMP_NOT4:%.*]] = icmp eq i32 [[INLEN]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT4]], label [[WHILE_END:%.*]], label [[WHILE_BODY:%.*]] +// CHECK: while.body: +// CHECK-NEXT: [[CURRENTLEN_06:%.*]] = phi i32 [ [[DEC:%.*]], [[CONT1:%.*]] ], [ [[INLEN]], [[ENTRY:%.*]] ] +// CHECK-NEXT: [[CURRENTBUF_SROA_0_05:%.*]] = phi ptr [ [[BOUND_PTR_ARITH:%.*]], [[CONT1]] ], [ [[BUF]], [[ENTRY]] ] +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[CURRENTBUF_SROA_0_05]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[CURRENTBUF_SROA_0_05]], [[BUF]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1]], label [[TRAP:%.*]], !prof [[PROF36:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: store i8 0, ptr [[CURRENTBUF_SROA_0_05]], align 1, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DEC]] = add i32 [[CURRENTLEN_06]], -1 +// CHECK-NEXT: [[BOUND_PTR_ARITH]] = getelementptr i8, ptr [[CURRENTBUF_SROA_0_05]], i64 1 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i32 [[DEC]], 0 +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[WHILE_END]], label [[WHILE_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: while.end: +// CHECK-NEXT: ret void +// +void ptrinc1(char *__counted_by(inLen) buf, + unsigned inLen) { + unsigned currentLen = inLen; + char *currentBuf = buf; + while (currentLen != 0) { + *currentBuf = 0; + currentLen--; + currentBuf++; + } +} + +// TODO: rdar://99414486 - All BoundsSafety checks should be removed. +// CHECK-LABEL: @test_reforge_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ult i32 [[IDX:%.*]], [[TABLE_SIZE:%.*]] +// CHECK-NEXT: [[CMP1_NOT39:%.*]] = icmp ne i32 [[IDX]], 0 +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[CMP_NOT]], [[CMP1_NOT39]] +// CHECK-NEXT: br i1 [[OR_COND]], label [[FOR_BODY_PREHEADER:%.*]], label [[RETURN:%.*]] +// CHECK: for.body.preheader: +// CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[IDX]] to i64 +// CHECK-NEXT: br label [[FOR_BODY:%.*]] +// CHECK: for.cond: +// CHECK-NEXT: [[INDVARS_IV_NEXT:%.*]] = add nuw nsw i64 [[INDVARS_IV:%.*]], 1 +// CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-NEXT: br i1 [[EXITCOND_NOT]], label [[RETURN]], label [[FOR_BODY]], {{!llvm.loop ![0-9]+}} +// CHECK: for.body: +// CHECK-NEXT: [[INDVARS_IV]] = phi i64 [ 0, [[FOR_BODY_PREHEADER]] ], [ [[INDVARS_IV_NEXT]], [[FOR_COND:%.*]] ] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TABLE:%.*]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[CMP4_NOT:%.*]] = icmp eq i32 [[TMP0]], [[VALUE:%.*]] +// CHECK-NEXT: br i1 [[CMP4_NOT]], label [[IF_THEN5:%.*]], label [[FOR_COND]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) {{#[0-9]+}}, {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: if.then5: +// CHECK-NEXT: [[BOUND_PTR_ARITH_LE:%.*]] = getelementptr i32, ptr [[TABLE]], i64 [[INDVARS_IV]] +// CHECK-NEXT: [[IDX_EXT_LE:%.*]] = zext i32 [[TABLE_SIZE]] to i64 +// CHECK-NEXT: [[ADD_PTR_LE:%.*]] = getelementptr inbounds nuw i32, ptr [[TABLE]], i64 [[IDX_EXT_LE]] +// CHECK-NEXT: [[DOTNOT33:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH_LE]], [[TABLE]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT33]], label [[TRAP:%.*]], label [[RETURN]], !prof [[PROF30]], {{!annotation ![0-9]+}} +// CHECK: return: +// CHECK-NEXT: [[RETVAL_SROA_4_3:%.*]] = phi ptr [ null, [[ENTRY:%.*]] ], [ [[ADD_PTR_LE]], [[IF_THEN5]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: [[RETVAL_SROA_0_3:%.*]] = phi ptr [ null, [[ENTRY]] ], [ [[BOUND_PTR_ARITH_LE]], [[IF_THEN5]] ], [ null, [[FOR_COND]] ] +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[RETVAL_SROA_0_3]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[RETVAL_SROA_4_3]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable test_reforge_indexable(unsigned idx, int*__counted_by(table_size) table, + unsigned table_size, int value) { + if (idx >= table_size) + return 0; + + for (unsigned i = 0; i < idx; i++) { + int *__single value_entry = &table[i]; + if (*value_entry == value) + return &table[i]; + } + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/regression-arithmetics-with-enum.c b/clang/test/BoundsSafety/CodeGen/regression-arithmetics-with-enum.c new file mode 100644 index 0000000000000..60af3a5c36758 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/regression-arithmetics-with-enum.c @@ -0,0 +1,12 @@ + + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - + +#include + +enum Enum {ZERO=0, ONE}; + +void foo(int *__bidi_indexable ptr, enum Enum e) { + *(ptr + e); +} diff --git a/clang/test/BoundsSafety/CodeGen/regression-arithmetics-with-typedefs.c b/clang/test/BoundsSafety/CodeGen/regression-arithmetics-with-typedefs.c new file mode 100644 index 0000000000000..a7ac0ca58eb36 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/regression-arithmetics-with-typedefs.c @@ -0,0 +1,12 @@ + + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - + +#include + +typedef unsigned long size_t; + +void foo(int *__bidi_indexable ptr, size_t idx) { + *(ptr + idx); +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/array-to-bound-lower.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/array-to-bound-lower.c new file mode 100644 index 0000000000000..1296026b7dbdf --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/array-to-bound-lower.c @@ -0,0 +1,134 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-ARM-O2 %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple x86_64 -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -triple arm -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-ARM-O2 %s + +#include +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-O0-NEXT: [[UPTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[BPTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[UPTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[UPTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[UPTR]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB5]], ptr [[TMP7]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[BPTR]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP10]], i64 -1 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP13]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP6]], ptr align 8 [[BPTR]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = icmp ult ptr [[WIDE_PTR_PTR8]], [[WIDE_PTR_UB10]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP18]], label [[CONT14:%.*]], label [[TRAP13:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap13: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont14: +// CHECK-O0-NEXT: [[TMP19:%.*]] = icmp uge ptr [[WIDE_PTR_PTR8]], [[WIDE_PTR_LB12]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP19]], label [[CONT16:%.*]], label [[TRAP15:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap15: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont16: +// CHECK-O0-NEXT: [[TMP20:%.*]] = load i32, ptr [[WIDE_PTR_PTR8]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP20]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR3:[0-9]+]] +// CHECK-O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 40 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i64 -4 +// CHECK-O2-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[OR_COND]], label [[CONT14:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O2: trap: +// CHECK-O2-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: cont14: +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR3]] +// CHECK-O2-NEXT: ret i32 undef +// +// CHECK-ARM-O2-LABEL: @main( +// CHECK-ARM-O2-NEXT: entry: +// CHECK-ARM-O2-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-ARM-O2-NEXT: call void @llvm.lifetime.start.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR3:[0-9]+]] +// CHECK-ARM-O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i32 40 +// CHECK-ARM-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[ARR]], i32 -4 +// CHECK-ARM-O2-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-ARM-O2-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-ARM-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-ARM-O2-NEXT: br i1 [[OR_COND]], label [[CONT14:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-ARM-O2: trap: +// CHECK-ARM-O2-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-ARM-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-ARM-O2: cont14: +// CHECK-ARM-O2-NEXT: call void @llvm.lifetime.end.p0(i64 40, ptr nonnull [[ARR]]) #[[ATTR3]] +// CHECK-ARM-O2-NEXT: ret i32 undef +// +int main() { + int arr[10]; + int *__indexable uPtr = arr; + int *__bidi_indexable bptr = uPtr; + --bptr; + return *bptr; // trap : underflow +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/array-to-bound.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/array-to-bound.c new file mode 100644 index 0000000000000..35f6ed61068f2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/array-to-bound.c @@ -0,0 +1,92 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +#include + +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR:%.*]], i64 4 +// O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[ARR]], i64 40 +// O2-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[UPPER]], {{!annotation ![0-9]+}} +// O2-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[ARR]], {{!annotation ![0-9]+}} +// O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP1]], [[TMP2]], {{!annotation ![0-9]+}} +// O2-NEXT: br i1 [[OR_COND]], label [[CONT11:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O2: trap: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// O2: cont11: +// O2-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret i32 [[TMP3]] +// +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[ARR_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[UPTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// O0-NEXT: [[BPTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// O0-NEXT: store ptr [[ARR:%.*]], ptr [[ARR_ADDR]], align 8 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[UPTR]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[UPTR]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[UPTR]], i64 16, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 0 +// O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 1 +// O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BPTR]], i32 0, i32 2 +// O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP5]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[UPTR]], i64 16, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// O0-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP6]], align 8 +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// O0-NEXT: store ptr [[WIDE_PTR_UB6]], ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// O0-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP8]], align 8 +// O0-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR8]], i64 10 +// O0-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB10]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP10]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont: +// O0-NEXT: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP11]], label [[CONT12:%.*]], label [[TRAP11:%.*]], {{!annotation ![0-9]+}} +// O0: trap11: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont12: +// O0-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// O0-NEXT: ret i32 [[TMP12]] +// +int foo(int * arr) { + int *__indexable uPtr = arr; + int *__bidi_indexable bptr = uPtr; +bingo: + return uPtr[10]; // trap : overflow +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/bound-to-array-ok.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/bound-to-array-ok.c new file mode 100644 index 0000000000000..64fa2c567c794 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/bound-to-array-ok.c @@ -0,0 +1,89 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s + +#include + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 40, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: br label [[BINGO:%.*]] +// CHECK-O0: bingo: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB6]], ptr [[TMP7]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR8]], i64 9 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB10]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP10]], label [[CONT14:%.*]], label [[TRAP13:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap13: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont14: +// CHECK-O0-NEXT: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB12]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP11]], label [[CONT16:%.*]], label [[TRAP15:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap15: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont16: +// CHECK-O0-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP12]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 0 +// +int main() { + int arr[10] = {0}; + int *__indexable p = arr; +bingo:; + return p[9]; // ok +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/bound-to-array.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/bound-to-array.c new file mode 100644 index 0000000000000..b0f510e08b7b1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/bound-to-array.c @@ -0,0 +1,86 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +#include + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[P]], i64 16, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_UB6]], ptr [[TMP7]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR8]], i64 10 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB10]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP10]], label [[CONT14:%.*]], label [[TRAP13:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap13: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont14: +// CHECK-O0-NEXT: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB12]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP11]], label [[CONT16:%.*]], label [[TRAP15:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap15: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont16: +// CHECK-O0-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP12]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int main() { + int arr[10]; + int *__indexable p = arr; + return p[10]; // trap : overflow +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound-O2.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound-O2.c new file mode 100644 index 0000000000000..fa04527da084c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound-O2.c @@ -0,0 +1,33 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -Wno-bounds-safety-init-list -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s + +#include + +// O2-LABEL: @TestFail( +// O2-NEXT: entry: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{.*}} +// O2-NEXT: unreachable +// +int TestFail() { + int len = 0; + int i = 3; + int *__single __counted_by(len) dcp = &i; + int *bp = dcp; + return *bp; // run-time trap : oob - dereference to zero-bound pointer +} + + + +// O2-LABEL: @TestOK( +// O2-NEXT: entry: +// O2-NEXT: ret void +// +void TestOK() { + int len = 0; + int i = 3; + int *__single __counted_by(len) dcp = &i; + int *bp = dcp; +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound-ptr-O2.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound-ptr-O2.c new file mode 100644 index 0000000000000..5217171ecad83 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound-ptr-O2.c @@ -0,0 +1,32 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +#include + +// CHECK-O2-LABEL: @TestFail( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{.*}} +// CHECK-O2-NEXT: unreachable +// +int TestFail() { + int array[5] = {4, 3, 2, 1, 0}; + int len = 2; + int *__counted_by(len) dcp = array; + int *bp = dcp; + return bp[2]; // oob - run-time trap +} + +// CHECK-O2-LABEL: @TestOK( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 3 +// +int TestOK() { + int array[5] = {4, 3, 2, 1, 0}; + int len = 2; + int *__counted_by(len) dcp = array; + int *bp = dcp; + return bp[1]; +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound.c new file mode 100644 index 0000000000000..737b2bc4ebf81 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-bound.c @@ -0,0 +1,109 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -Wno-bounds-safety-init-list -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +#include + +// O0-LABEL: @main( +// O0-NEXT: entry: +// O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// O0-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// O0-NEXT: [[I:%.*]] = alloca i32, align 4 +// O0-NEXT: [[DCP:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[BP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// O0-NEXT: store i32 0, ptr [[LEN]], align 4 +// O0-NEXT: store i32 3, ptr [[I]], align 4 +// O0-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[I]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: store ptr [[I]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: store ptr [[I]], ptr [[TMP3]], align 8 +// O0-NEXT: [[TMP4:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[CONV:%.*]] = sext i32 [[TMP4]] to i64 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64 +// O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64 +// O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// O0-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]] +// O0-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// O0: land.rhs: +// O0-NEXT: [[CMP5:%.*]] = icmp sle i64 0, [[CONV]] +// O0-NEXT: br label [[LAND_END]] +// O0: land.end: +// O0-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP5]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8 +// O0-NEXT: store ptr [[WIDE_PTR_PTR9]], ptr [[DCP]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: [[TMP6:%.*]] = load ptr, ptr [[DCP]], align 8 +// O0-NEXT: [[TMP7:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP7]] to i64 +// O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP6]], i64 [[IDX_EXT]] +// O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BP]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BP]], i32 0, i32 1 +// O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BP]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP6]], ptr [[TMP10]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP14]], ptr align 8 [[BP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8 +// O0-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_UB18]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP11]], label [[CONT22:%.*]], label [[TRAP21:%.*]], {{!annotation ![0-9]+}} +// O0: trap21: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont22: +// O0-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_LB20]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP12]], label [[CONT24:%.*]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// O0: trap23: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont24: +// O0-NEXT: [[TMP13:%.*]] = load i32, ptr [[WIDE_PTR_PTR16]], align 4 +// O0-NEXT: ret i32 [[TMP13]] +// +int main() { + int len = 0; + int i = 3; + int *__single __counted_by(len) dcp = &i; +bingo:; + int *bp = dcp; + return *bp; // run-time trap : oob - dereference to zero-bound pointer +} + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-thin-O2.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-thin-O2.c new file mode 100644 index 0000000000000..5b15cf0ee2af4 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-thin-O2.c @@ -0,0 +1,118 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + +// XFAIL: * +// FIXME: rdar://80820825 + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s + +#include + +// CHECK-O2-LABEL: @TestOK( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void TestOK() { + int len = 0; + int *__single __counted_by(len) dcp = 0; + int *__single tp = dcp; +} + +// CHECK-O2-LABEL: @TestOK2( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void TestOK2() { + int len = 0; + int arr[10]; + int *__single __counted_by(len) dcp = arr + 10; +} + +// CHECK-O2-LABEL: @TestFail1( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +void TestFail1() { + int len = 0; + int arr[10]; + int *__single __counted_by(len) dcp = arr + 10; + int *__single tp = dcp; // trap +} + +// CHECK-O2-LABEL: @TestFail2( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int TestFail2() { + int len = 0; + int *__single __counted_by(len) dcp = 0; + int *__single tp = dcp; + return *tp; // trap +} + +// CHECK-O2-LABEL: @TestFail3( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int TestFail3() { + int len = 0; + int *__single __counted_by(len) dcp = 0; + int *__single tp = dcp; + return tp[0]; // trap +} + +struct Data { + int *ptr; + char *cp; +}; + +// CHECK-O2-LABEL: @TestFail4( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int *TestFail4() { + int len = 0; + struct Data *__single __counted_by(len) dcp = 0; + struct Data *__single tp = dcp; + return tp->ptr; // trap +} + +// CHECK-O2-LABEL: @TestFail5( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +char TestFail5() { + int len = 0; + struct Data *__single __counted_by(len) dcp = 0; + struct Data *__single tp = dcp; + return *(tp->cp); // trap +} + +// CHECK-O2-LABEL: @TestFail6( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +char *TestFail6() { + int len = 0; + struct Data *__single __counted_by(len) dcp = 0; + struct Data *__single tp = dcp; + return tp->cp; // trap +} + +// CHECK-O2-LABEL: @TestFail7( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation !{{[0-9]+}} +// CHECK-O2-NEXT: unreachable +// +void TestFail7() { + int len = 0; + struct Data *__single __counted_by(len) dcp = 0; + struct Data *__single tp = dcp; + char **__single cp = &tp->cp; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-thin.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-thin.c new file mode 100644 index 0000000000000..0423873ff0752 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/dep-count-ptr-to-thin.c @@ -0,0 +1,116 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +#include + + +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[LEN_INIT_ADDR:%.*]] = alloca i32, align 4 +// O0-NEXT: [[I:%.*]] = alloca [3 x i32], align 4 +// O0-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// O0-NEXT: [[DCP:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TP:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store i32 [[LEN_INIT:%.*]], ptr [[LEN_INIT_ADDR]], align 4 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[I]], ptr align 4 @__const.foo.i, i64 12, i1 false) +// O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_INIT_ADDR]], align 4 +// O0-NEXT: store i32 [[TMP0]], ptr [[LEN]], align 4 +// O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [3 x i32], ptr [[I]], i64 0, i64 0 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 3 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// O0-NEXT: [[TMP4:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[CONV:%.*]] = sext i32 [[TMP4]] to i64 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64 +// O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64 +// O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// O0-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]] +// O0-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// O0: land.rhs: +// O0-NEXT: [[CMP5:%.*]] = icmp sle i64 0, [[CONV]] +// O0-NEXT: br label [[LAND_END]] +// O0: land.end: +// O0-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP5]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8 +// O0-NEXT: store ptr [[WIDE_PTR_PTR9]], ptr [[DCP]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: [[TMP6:%.*]] = load ptr, ptr [[DCP]], align 8 +// O0-NEXT: [[TMP7:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP7]] to i64 +// O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP6]], i64 [[IDX_EXT]] +// O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP6]], ptr [[TMP10]], align 8 +// O0-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8 +// O0-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR16]], null, {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP11]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT24:%.*]], {{!annotation ![0-9]+}} +// O0: boundscheck.notnull: +// O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_UB18]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP12]], label [[CONT22:%.*]], label [[TRAP21:%.*]], {{!annotation ![0-9]+}} +// O0: trap21: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont22: +// O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_LB20]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP13]], label [[CONT24]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// O0: trap23: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont24: +// O0-NEXT: store ptr [[WIDE_PTR_PTR16]], ptr [[TP]], align 8 +// O0-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TP]], align 8 +// O0-NEXT: [[TMP15:%.*]] = load i32, ptr [[TMP14]], align 4 +// O0-NEXT: ret i32 [[TMP15]] +// +int foo(int len_init) { + int i[3] = {3}; + int len = len_init; + int *__single __counted_by(len) dcp = i; +bingo:; + int *__single tp = dcp; + + return *tp; +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/dependent-count-pointer-conversion.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/dependent-count-pointer-conversion.c new file mode 100644 index 0000000000000..6049ffb46a9ce --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/dependent-count-pointer-conversion.c @@ -0,0 +1,155 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s + +#include + +// O0-LABEL: @main( +// O0-NEXT: entry: +// O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// O0-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// O0-NEXT: [[DCP:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP7:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TP:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[BP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[ARR]], ptr align 16 @__const.main.arr, i64 40, i1 false) +// O0-NEXT: store i32 5, ptr [[LEN]], align 4 +// O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[CONV:%.*]] = sext i32 [[TMP3]] to i64 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB3:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR2]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64 +// O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64 +// O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// O0-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]] +// O0-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +// O0: land.rhs: +// O0-NEXT: [[CMP5:%.*]] = icmp sle i64 0, [[CONV]] +// O0-NEXT: br label [[LAND_END]] +// O0: land.end: +// O0-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP5]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP7]], ptr align 8 [[TMP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP7]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8 +// O0-NEXT: store ptr [[WIDE_PTR_PTR9]], ptr [[DCP]], align 8 +// O0-NEXT: [[TMP5:%.*]] = load ptr, ptr [[DCP]], align 8 +// O0-NEXT: [[TMP6:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP6]] to i64 +// O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP5]], i64 [[IDX_EXT]] +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP5]], ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP8]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP5]], ptr [[TMP9]], align 8 +// O0-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP14]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8 +// O0-NEXT: [[TMP10:%.*]] = icmp ne ptr [[WIDE_PTR_PTR16]], null, {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP10]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT24:%.*]], {{!annotation ![0-9]+}} +// O0: boundscheck.notnull: +// O0-NEXT: [[TMP11:%.*]] = icmp ult ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_UB18]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP11]], label [[CONT22:%.*]], label [[TRAP21:%.*]], {{!annotation ![0-9]+}} +// O0: trap21: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont22: +// O0-NEXT: [[TMP12:%.*]] = icmp uge ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_LB20]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP12]], label [[CONT24]], label [[TRAP23:%.*]], {{!annotation ![0-9]+}} +// O0: trap23: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont24: +// O0-NEXT: store ptr [[WIDE_PTR_PTR16]], ptr [[TP]], align 8 +// O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[DCP]], align 8 +// O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[LEN]], align 4 +// O0-NEXT: [[IDX_EXT25:%.*]] = sext i32 [[TMP14]] to i64 +// O0-NEXT: [[ADD_PTR26:%.*]] = getelementptr inbounds i32, ptr [[TMP13]], i64 [[IDX_EXT25]] +// O0-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BP]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP13]], ptr [[TMP15]], align 8 +// O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BP]], i32 0, i32 1 +// O0-NEXT: store ptr [[ADD_PTR26]], ptr [[TMP16]], align 8 +// O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BP]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP13]], ptr [[TMP17]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[BP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8 +// O0-NEXT: [[TMP18:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR29]], i64 5 +// O0-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8 +// O0-NEXT: [[TMP19:%.*]] = icmp ult ptr [[TMP18]], [[WIDE_PTR_UB31]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP19]], label [[CONT35:%.*]], label [[TRAP34:%.*]], {{!annotation ![0-9]+}} +// O0: trap34: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont35: +// O0-NEXT: [[TMP20:%.*]] = icmp uge ptr [[TMP18]], [[WIDE_PTR_LB33]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP20]], label [[CONT37:%.*]], label [[TRAP36:%.*]], {{!annotation ![0-9]+}} +// O0: trap36: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont37: +// O0-NEXT: [[TMP21:%.*]] = load i32, ptr [[TMP18]], align 4 +// O0-NEXT: ret i32 [[TMP21]] +// +// O2-LABEL: @main( +// O2-NEXT: entry: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O2-NEXT: unreachable +// +int main() { + int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + int len = 5; + int *__counted_by(len) dcp = arr; + int *__single tp = dcp; // ok: check len >= 1 or dcp is null + int *bp = dcp; // construct bounds pointer based on calculated bounds +bingo:; + return bp[5]; // oob trap! +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ib-dependent-count-ptr-O2.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ib-dependent-count-ptr-O2.c new file mode 100644 index 0000000000000..dcf89e9d70689 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ib-dependent-count-ptr-O2.c @@ -0,0 +1,47 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm -mllvm -enable-constraint-elimination %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -mllvm -enable-constraint-elimination %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +// piggy-back test for -fcoverage-mapping +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -fprofile-instrument=clang -fcoverage-mapping -emit-llvm %s -o /dev/null + +#include + +// The range check can be removed because 'i >= 0 && i < len'. + +// CHECK-O2-LABEL: @foo( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[CMP6:%.*]] = icmp sgt i32 [[LEN:%.*]], 0 +// CHECK-O2-NEXT: br i1 [[CMP6]], label [[CONT1_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]] +// CHECK-O2: for.body.preheader: +// CHECK-O2-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext nneg i32 [[LEN]] to i64 +// CHECK-O2-NEXT: br label [[CONT1:%.*]] +// CHECK-O2: for.cond.cleanup: +// CHECK-O2-NEXT: ret void +// CHECK-O2: for.body: +// CHECK-O2-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[CONT1_PREHEADER]] ], [ [[INDVARS_IV_NEXT:%.*]], [[CONT1]] ] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[BUF:%.*]], i64 [[INDVARS_IV]] +// CHECK-O2-NEXT: [[TMP1:%.*]] = trunc nuw nsw i64 [[INDVARS_IV]] to i32 +// CHECK-O2-NEXT: store i32 [[TMP1]], ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// CHECK-O2-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 +// CHECK-O2-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]] +// CHECK-O2-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_COND_CLEANUP]], label [[CONT1]], !llvm.loop [[LOOP6:![0-9]+]] +// +void foo(int *__counted_by(len) buf, int len) { + for (int i = 0; i < len; ++i) + buf[i] = i; +} + +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 9 +// +int main() { + int arr[10]; + foo(arr, 10); + return arr[9]; +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-minus-assign.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-minus-assign.c new file mode 100644 index 0000000000000..ba456d6644dfd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-minus-assign.c @@ -0,0 +1,29 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int foo(int * __indexable ptr, int idx) { + ptr -= idx; + return *ptr; +} + +int main() { + int a; + int * __indexable p = &a; + + return foo(p, -1); +} + +// CHECK: define{{.*}} i32 @foo({{.*}}) +// ... +// CHECK: %[[UGE_RES:[0-9]+]] = icmp uge i64 {{%.*}}, {{%.*}}, !annotation ![[ANNOT_NEW_IDX_GE_OLD:[0-9]+]] +// CHECK: br i1 %[[UGE_RES]], label %{{[a-z0-9]+}}, label %[[LABEL_TRAP:[a-z0-9]+]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation ![[ANNOT_NEW_IDX_GE_OLD]] +// CHECK: [[LABEL_TRAP]] +// CHECK-NEXT: call void @llvm.ubsantrap{{.*}} !annotation ![[ANNOT_NEW_IDX_GE_OLD]] +// ... +// CHECK: define{{.*}} i32 @main() #0 { +// ... +// CHECK: ![[ANNOT_NEW_IDX_GE_OLD]] = !{!"bounds-safety-check-new-indexable-ptr-ge-old"} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-plus-assign-unsigned.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-plus-assign-unsigned.c new file mode 100644 index 0000000000000..ad4fc65979738 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-plus-assign-unsigned.c @@ -0,0 +1,29 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int foo(int * __indexable ptr, unsigned idx) { + ptr += idx; + return *ptr; +} + +int main() { + int a; + int * __indexable p = &a; + + return foo(p, 42); +} + +// CHECK-LABEL: @foo +// ... +// CHECK: %[[UGE_RES:[0-9]+]] = icmp uge i64 {{%.*}}, {{%.*}}, !annotation ![[ANNOT_NEW_IDX_GE_OLD:[0-9]+]] +// CHECK: br i1 %[[UGE_RES]], label %{{[a-z0-9]+}}, label %[[LABEL_TRAP:[a-z0-9]+]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation ![[ANNOT_NEW_IDX_GE_OLD]] +// CHECK: [[LABEL_TRAP]] +// CHECK-NEXT: call void @llvm.ubsantrap{{.*}} !annotation ![[ANNOT_NEW_IDX_GE_OLD]] +// ... +// CHECK-LABEL: @main +// ... +// CHECK: ![[ANNOT_NEW_IDX_GE_OLD]] = !{!"bounds-safety-check-new-indexable-ptr-ge-old"} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-plus-assign.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-plus-assign.c new file mode 100644 index 0000000000000..e22e1e143336f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/indexable-ptr-plus-assign.c @@ -0,0 +1,29 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +int foo(int * __indexable ptr, int idx) { + ptr += idx; + return *ptr; +} + +int main() { + int a; + int * __indexable p = &a; + + return foo(p, -1); +} + +// CHECK: define{{.*}} i32 @foo({{.*}}) +// ... +// CHECK: %[[UGE_RES:[0-9]+]] = icmp uge i64 {{%.*}}, {{%.*}}, !annotation ![[ANNOT_NEW_IDX_GE_OLD:[0-9]+]] +// CHECK: br i1 %[[UGE_RES]], label %{{[a-z0-9]+}}, label %[[LABEL_TRAP:[a-z0-9]+]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation ![[ANNOT_NEW_IDX_GE_OLD]] +// CHECK: [[LABEL_TRAP]] +// CHECK-NEXT: call void @llvm.ubsantrap{{.*}} !annotation ![[ANNOT_NEW_IDX_GE_OLD]] +// ... +// CHECK: define{{.*}} i32 @main() #0 { +// ... +// CHECK: ![[ANNOT_NEW_IDX_GE_OLD]] = !{!"bounds-safety-check-new-indexable-ptr-ge-old"} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/length-check-bound-check-removal.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/length-check-bound-check-removal.c new file mode 100644 index 0000000000000..8a8b032436764 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/length-check-bound-check-removal.c @@ -0,0 +1,86 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 3 + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// +#include + +typedef struct { + int *__counted_by(len) buf; + int len; +} S; + +void use(int *, int); + +// CHECK-LABEL: define dso_local range(i32 -1, 1) i32 @access1( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN]], align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP0]], 2 +// CHECK-NEXT: br i1 [[CMP]], label [[CLEANUP:%.*]], label [[CONT11:%.*]] +// CHECK: cont11: +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P]], align 8, !tbaa [[TBAA9:![0-9]+]] +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP1]], i64 4 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4, !tbaa [[TBAA10:![0-9]+]] +// CHECK-NEXT: tail call void @use(ptr noundef [[BOUND_PTR_ARITH]], i32 noundef [[TMP2]]) #[[ATTR3:[0-9]+]] +// CHECK-NEXT: br label [[CLEANUP]] +// CHECK: cleanup: +// CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ 0, [[CONT11]] ], [ -1, [[ENTRY:%.*]] ] +// CHECK-NEXT: ret i32 [[RETVAL_0]] +// +int access1(S *p) { + int *Ptr = p->buf; + int Len = p->len; + + if (Len < 2) return -1; + int v = *Ptr++; + use(Ptr, v); + return 0; +} + +// CHECK-LABEL: define dso_local range(i32 -1, 1) i32 @access2( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN]], align 8, !tbaa [[TBAA2]] +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P]], align 8, !tbaa [[TBAA9]] +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP0]], 2 +// CHECK-NEXT: br i1 [[CMP]], label [[CLEANUP:%.*]], label [[CONT11:%.*]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META11:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META11]] +// CHECK: cont11: +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[TMP1]], i64 4 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4, !tbaa [[TBAA10]] +// CHECK-NEXT: tail call void @use(ptr noundef [[BOUND_PTR_ARITH]], i32 noundef [[TMP2]]) #[[ATTR3]] +// CHECK-NEXT: [[BOUND_PTR_ARITH14:%.*]] = getelementptr i8, ptr [[TMP1]], i64 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[ADD_PTR]], !annotation [[META12:![0-9]+]] +// CHECK-NEXT: [[TMP4:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[TMP1]], !annotation [[META13:![0-9]+]] +// CHECK-NEXT: [[OR_COND35:%.*]] = and i1 [[TMP3]], [[TMP4]], !annotation [[META13]] +// CHECK-NEXT: br i1 [[OR_COND35]], label [[CONT22:%.*]], label [[TRAP:%.*]], !prof [[PROF14:![0-9]+]], !annotation [[META12]] +// CHECK: cont22: +// CHECK-NEXT: [[DOTNOT39:%.*]] = icmp eq ptr [[BOUND_PTR_ARITH14]], null, !annotation [[META15:![0-9]+]] +// CHECK-NEXT: [[TMP5:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH14]], [[ADD_PTR]], !annotation [[META12]] +// CHECK-NEXT: [[OR_COND40:%.*]] = select i1 [[DOTNOT39]], i1 true, i1 [[TMP5]], !annotation [[META12]] +// CHECK-NEXT: br i1 [[OR_COND40]], label [[CONT32:%.*]], label [[TRAP]], !prof [[PROF16:![0-9]+]], !annotation [[META15]] +// CHECK: cont32: +// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, !tbaa [[TBAA10]] +// CHECK-NEXT: tail call void @use(ptr noundef [[BOUND_PTR_ARITH14]], i32 noundef [[TMP6]]) #[[ATTR3]] +// CHECK-NEXT: br label [[CLEANUP]] +// CHECK: cleanup: +// CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ 0, [[CONT32]] ], [ -1, [[ENTRY:%.*]] ] +// CHECK-NEXT: ret i32 [[RETVAL_0]] +// +int access2(S *p) { + int *Ptr = p->buf; + int Len = p->len; + + if (Len < 2) return -1; + int v = *Ptr++; + use(Ptr, v); + v = *Ptr++; + use(Ptr, v); + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/multiple-fields-deref.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/multiple-fields-deref.c new file mode 100644 index 0000000000000..b7a6bb80ae1ff --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/multiple-fields-deref.c @@ -0,0 +1,40 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +struct foo { + int a, b, c; +}; + +// CHECK-LABEL: @sum( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[F:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_F_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[F]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_2_0_F_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_F_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[F]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_3_0_F_SROA_IDX]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 12 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[AGG_TEMP_SROA_2_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[AGG_TEMP_SROA_3_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TMP1]], i1 [[TMP2]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT19:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont19: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[B:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 4 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[B]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP4]], [[TMP3]] +// CHECK-NEXT: [[C:%.*]] = getelementptr inbounds nuw i8, ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], i64 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[C]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[ADD20:%.*]] = add nsw i32 [[ADD]], [[TMP5]] +// CHECK-NEXT: ret i32 [[ADD20]] +// +int sum(struct foo *__bidi_indexable f) { + // This should generate only one check at O2. + return f->a + f->b + f->c; +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript-deref.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript-deref.c new file mode 100644 index 0000000000000..713f084952103 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript-deref.c @@ -0,0 +1,76 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 16 +// CHECK-O0-NEXT: [[I:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 10 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[I]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[I]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[I]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: br label [[BINGO:%.*]] +// CHECK-O0: bingo: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[I]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP13]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP14]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int main() { + int arr[10]; + + int *i = &arr[10]; +bingo:; + return *i; // run-time trap : oob +} + +// + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript-noload.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript-noload.c new file mode 100644 index 0000000000000..aa04f21515d08 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript-noload.c @@ -0,0 +1,17 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +int main() { + int arr[10]; + + int *i = &arr[10]; // ok. no dereference. + + return 0; +} + +// CHECK-NOT: @llvm.ubsantrap diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript.c new file mode 100644 index 0000000000000..207a4d19c4e32 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-array-subscript.c @@ -0,0 +1,74 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -triple x86_64 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -triple x86_64 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR:%.*]], i64 4 +// O2-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[ARR]], i64 40 +// O2-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[UPPER]], {{!annotation ![0-9]+}} +// O2-NEXT: [[TMP1:%.*]] = icmp uge ptr [[ARRAYIDX]], [[ARR]], {{!annotation ![0-9]+}} +// O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// O2-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O2: trap: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// O2: cont1: +// O2-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret i32 [[TMP2]] +// +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[ARR_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[BIDI_ARR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[I:%.*]] = alloca i32, align 4 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[ARR:%.*]], ptr [[ARR_ADDR]], align 8 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARR_ADDR]], align 8 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BIDI_ARR]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BIDI_ARR]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[BIDI_ARR]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BIDI_ARR]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 10 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP4:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont: +// O0-NEXT: [[TMP5:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP5]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// O0: trap1: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont2: +// O0-NEXT: [[TMP6:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// O0-NEXT: store i32 [[TMP6]], ptr [[I]], align 4 +// O0-NEXT: [[TMP7:%.*]] = load i32, ptr [[I]], align 4 +// O0-NEXT: ret i32 [[TMP7]] +// +int foo(int * arr) { + int * bidi_arr = arr; +bingo:; + int i = bidi_arr[10]; + + return i; +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-dependent-count-ptr.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-dependent-count-ptr.c new file mode 100644 index 0000000000000..891160bf3f8ff --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-dependent-count-ptr.c @@ -0,0 +1,78 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=O0 %s +// RUN: %clang_cc1 -O2 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=O2 %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=O0 %s +// RUN: %clang_cc1 -O2 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=O2 %s + +#include + +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// O0-NEXT: store i32 [[LEN:%.*]], ptr [[LEN_ADDR]], align 4 +// O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// O0-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[TMP6:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 5 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP7:%.*]] = icmp ult ptr [[TMP6]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont: +// O0-NEXT: [[TMP8:%.*]] = icmp uge ptr [[TMP6]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP8]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// O0: trap1: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont2: +// O0-NEXT: store i32 [[TMP0]], ptr [[TMP6]], align 4 +// O0-NEXT: ret void +// +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[IDX_EXT:%.*]] = sext i32 [[LEN:%.*]] to i64 +// O2-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[BUF:%.*]], i64 [[IDX_EXT]] +// O2-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[BUF]], i64 20 +// O2-NEXT: [[TMP1:%.*]] = icmp ult ptr [[TMP0]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// O2-NEXT: [[TMP2:%.*]] = icmp uge ptr [[TMP0]], [[BUF]], {{!annotation ![0-9]+}} +// O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP2]], [[TMP1]], {{!annotation ![0-9]+}} +// O2-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O2: trap: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// O2: cont1: +// O2-NEXT: store i32 [[LEN]], ptr [[TMP0]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret void +// +void foo(int *__counted_by(len) buf, int len) { + buf[5] = len; +} + + + + + + + + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-member-expr.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-member-expr.c new file mode 100644 index 0000000000000..03bc2ee3e3a9f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/oob-member-expr.c @@ -0,0 +1,108 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// REQUIRES: system-darwin + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +struct S { + int *p; + long i; +}; + +// O2-LABEL: define dso_local void @foo( +// O2-SAME: ptr noundef readnone captures(none) [[A:%.*]], i64 noundef [[I:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// O2-NEXT: [[ENTRY:.*:]] +// O2-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// O2-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr nonnull [[S]]) #[[ATTR3:[0-9]+]] +// O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[S]], i64 16 +// O2-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[S]], i64 24 +// O2-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP1]], [[TMP0]], !annotation [[META2:![0-9]+]] +// O2-NEXT: br i1 [[DOTNOT]], label %[[TRAP:.*]], label %[[CONT9:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// O2: [[TRAP]]: +// O2-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META4:![0-9]+]] +// O2-NEXT: unreachable, !annotation [[META4]] +// O2: [[CONT9]]: +// O2-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr nonnull [[S]]) #[[ATTR3]] +// O2-NEXT: ret void +// +// O0-LABEL: define dso_local void @foo( +// O0-SAME: ptr noundef [[A:%.*]], i64 noundef [[I:%.*]]) #[[ATTR0:[0-9]+]] { +// O0-NEXT: [[ENTRY:.*:]] +// O0-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[I_ADDR:%.*]] = alloca i64, align 8 +// O0-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// O0-NEXT: [[SP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[A]], ptr [[A_ADDR]], align 8 +// O0-NEXT: store i64 [[I]], ptr [[I_ADDR]], align 8 +// O0-NEXT: [[P:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// O0-NEXT: store ptr [[TMP0]], ptr [[P]], align 8 +// O0-NEXT: [[I1:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// O0-NEXT: [[TMP1:%.*]] = load i64, ptr [[I_ADDR]], align 8 +// O0-NEXT: store i64 [[TMP1]], ptr [[I1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds i64, ptr [[TMP2]], i64 1 +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP2]], ptr [[TMP6]], align 8 +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[SP]], i32 0, i32 0 +// O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[SP]], i32 0, i32 1 +// O0-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP8]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[SP]], i32 0, i32 2 +// O0-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP9]], align 8 +// O0-NEXT: br label %[[BINGO:.*]] +// O0: [[BINGO]]: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[SP]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr [[STRUCT_S]], ptr [[WIDE_PTR_PTR4]], i64 1 +// O0-NEXT: [[TMP11:%.*]] = icmp ule ptr [[TMP10]], [[WIDE_PTR_UB6]], !annotation [[META2:![0-9]+]] +// O0-NEXT: br i1 [[TMP11]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// O0: [[TRAP]]: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// O0-NEXT: unreachable, !annotation [[META2]] +// O0: [[CONT]]: +// O0-NEXT: [[TMP12:%.*]] = icmp ule ptr [[WIDE_PTR_LB8]], [[WIDE_PTR_PTR4]], !annotation [[META4:![0-9]+]] +// O0-NEXT: br i1 [[TMP12]], label %[[CONT10:.*]], label %[[TRAP9:.*]], !prof [[PROF3]], !annotation [[META4]] +// O0: [[TRAP9]]: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// O0-NEXT: unreachable, !annotation [[META4]] +// O0: [[CONT10]]: +// O0-NEXT: [[I11:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[WIDE_PTR_PTR4]], i32 0, i32 1 +// O0-NEXT: store i64 3, ptr [[I11]], align 8 +// O0-NEXT: ret void +// +void foo(int * a, long i) { + struct S s = {a, i}; + struct S *sp = &s.i; // bad cast but we don't check +bingo:; + sp->i = 3; // run-time trap : oob +} + +//. +// O2: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// O2: [[PROF3]] = !{!"branch_weights", i32 8191, i32 -8192} +// O2: [[META4]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound"} +//. +// O0: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// O0: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// O0: [[META4]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-compound-ok.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-compound-ok.c new file mode 100644 index 0000000000000..00f1a999c2eaa --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-compound-ok.c @@ -0,0 +1,95 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name tmp1 + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s + +// O0-LABEL: @main( +// O0-NEXT: entry: +// O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// O0-NEXT: [[ARR:%.*]] = alloca [4 x i32], align 16 +// O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TMP1TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// O0-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ARR]], i8 0, i64 16, i1 false) +// O0-NEXT: br label [[TEST_START:%.*]] +// O0: test_start: +// O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [4 x i32], ptr [[ARR]], i64 0, i64 0 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 4 +// O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 4 +// O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP1TMP1]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP1TMP1]], i32 0, i32 0 +// O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// O0-NEXT: [[BOUND_PTR_ARITH2:%.*]] = getelementptr i32, ptr [[TMP13]], i64 -1 +// O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[BOUND_PTR_ARITH2]], ptr [[TMP14]], align 8 +// O0-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP1TMP1]], i32 0, i32 1 +// O0-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP1TMP1]], i32 0, i32 2 +// O0-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// O0-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// O0-NEXT: br label [[TEST_END:%.*]] +// O0: test_end: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP21:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP21]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont: +// O0-NEXT: [[TMP22:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP22]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// O0: trap3: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont4: +// O0-NEXT: [[TMP23:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// O0-NEXT: ret i32 [[TMP23]] +// +// O2-LABEL: @main( +// O2-NEXT: entry: +// O2-NEXT: ret i32 0 +// +int main() { + int arr[4] = {0}; +test_start:; + int *p = arr; + p += 4; + p -= 1; +test_end:; + return *p; // ok +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-compound.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-compound.c new file mode 100644 index 0000000000000..dcac06d8af2c1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-compound.c @@ -0,0 +1,73 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple arm64e -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple arm64e -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [4 x i32], align 4 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [4 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 4 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP13]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP14]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int main() { + int arr[4]; + int *p = arr; + p += 4; + return *p; // trap +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postdec-ok.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postdec-ok.c new file mode 100644 index 0000000000000..2da353f01cb2d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postdec-ok.c @@ -0,0 +1,76 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[TMP0:%.*]] = load i32, ptr [[A:%.*]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret i32 [[TMP0]] +// +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 -1 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P_COPY]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont: +// O0-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP14]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// O0: trap1: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont2: +// O0-NEXT: [[TMP15:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// O0-NEXT: ret i32 [[TMP15]] +// +int foo(int * a) { + int *p = a; + int *p_copy = p--; +bingo:; + return *p_copy; // ok +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postdec.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postdec.c new file mode 100644 index 0000000000000..f71f61368d6dd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postdec.c @@ -0,0 +1,86 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -triple x86_64 -O2 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -triple x86_64 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[A:%.*]], i64 4 +// O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[A]], i64 -4 +// O2-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// O2-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[A]], {{!annotation ![0-9]+}} +// O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// O2-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O2: trap: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// O2: cont1: +// O2-NEXT: [[TMP2:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret i32 [[TMP2]] +// +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 -1 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont: +// O0-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP14]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// O0: trap1: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont2: +// O0-NEXT: [[TMP15:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// O0-NEXT: ret i32 [[TMP15]] +// +int foo(int* a) { + int *p = a; +bingo:; + int *p_copy = p--; + return *p; +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postinc-ok.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postinc-ok.c new file mode 100644 index 0000000000000..0f991a96365a3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postinc-ok.c @@ -0,0 +1,77 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[TMP0:%.*]] = load i32, ptr [[A:%.*]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret i32 [[TMP0]] +// +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 1 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P_COPY]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont: +// O0-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP14]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// O0: trap1: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable +// O0: cont2: +// O0-NEXT: [[TMP15:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// O0-NEXT: ret i32 [[TMP15]] +// +int foo(int* a) { + int *p = a; + int *p_copy = p++; +bingo:; + return *p_copy; +} + + +// diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postinc.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postinc.c new file mode 100644 index 0000000000000..73711acf37eb9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-postinc.c @@ -0,0 +1,75 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[A:%.*]] = alloca [1 x i32], align 4 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[A]], i8 0, i64 4, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x i32], ptr [[A]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 1 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 1 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP13]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP14]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int main() { + int a[1] = {0}; + int *p = a; + int *p_copy = p++; + + return *p; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-predec-self.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-predec-self.c new file mode 100644 index 0000000000000..e6e7b06d5bc5e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-predec-self.c @@ -0,0 +1,87 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[A:%.*]] = alloca [1 x i32], align 4 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[A]], i8 0, i64 4, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x i32], ptr [[A]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 1 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 -1 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP13]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP14]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[A:%.*]] = alloca [1 x i32], align 4 +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[A]]) #[[ATTR3:[0-9]+]] +// CHECK-O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[A]], i64 4 +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[A]], i64 -4 +// CHECK-O2-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[A]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O2: trap: +// CHECK-O2-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK-O2: cont1: +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[A]]) #[[ATTR3]] +// CHECK-O2-NEXT: ret i32 undef +// +int main() { + int a[1] = {0}; + int *p = a; + int *p_copy = --p; + + return *p; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-predec.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-predec.c new file mode 100644 index 0000000000000..4fdccc0eb0763 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-predec.c @@ -0,0 +1,86 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefixes O0 %s + +// O2-LABEL: @foo( +// O2-NEXT: entry: +// O2-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[A:%.*]], i64 4 +// O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i8, ptr [[A]], i64 -4 +// O2-NEXT: [[TMP0:%.*]] = icmp ult ptr [[BOUND_PTR_ARITH]], [[UPPER]], {{!annotation ![0-9]+}} +// O2-NEXT: [[TMP1:%.*]] = icmp uge ptr [[BOUND_PTR_ARITH]], [[A]], {{!annotation ![0-9]+}} +// O2-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// O2-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O2: trap: +// O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// O2-NEXT: unreachable, {{!annotation ![0-9]+}} +// O2: cont1: +// O2-NEXT: [[TMP2:%.*]] = load i32, ptr [[BOUND_PTR_ARITH]], align 4, {{!tbaa ![0-9]+}} +// O2-NEXT: ret i32 [[TMP2]] +// +// O0-LABEL: @foo( +// O0-NEXT: entry: +// O0-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8 +// O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// O0-NEXT: store ptr [[A:%.*]], ptr [[A_ADDR]], align 8 +// O0-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8 +// O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// O0-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP5]], i64 -1 +// O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// O0-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// O0-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// O0-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// O0-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// O0-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// O0-NEXT: br label [[BINGO:%.*]] +// O0: bingo: +// O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P_COPY]], i64 24, i1 false) +// O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// O0-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// O0: trap: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont: +// O0-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// O0-NEXT: br i1 [[TMP14]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// O0: trap1: +// O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// O0-NEXT: unreachable, {{!annotation ![0-9]+}} +// O0: cont2: +// O0-NEXT: [[TMP15:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// O0-NEXT: ret i32 [[TMP15]] +// +int foo(int * a) { + int *p = a; + int *p_copy = --p; +bingo:; + return *p_copy; +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-preinc-self.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-preinc-self.c new file mode 100644 index 0000000000000..a54760a8cc679 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-preinc-self.c @@ -0,0 +1,77 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[A:%.*]] = alloca [1 x i32], align 4 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[A]], i8 0, i64 4, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x i32], ptr [[A]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 1 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 1 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP13]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP14]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int main() { + int a[1] = {0}; + int *p = a; + int *p_copy = ++p; + + return *p; // trap +} + + diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-preinc.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-preinc.c new file mode 100644 index 0000000000000..36ecb04827b94 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-preinc.c @@ -0,0 +1,75 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[A:%.*]] = alloca [1 x i32], align 4 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[A]], i8 0, i64 4, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [1 x i32], ptr [[A]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 1 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 1 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P_COPY]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP13]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP14:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP14]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int main() { + int a[1] = {0}; + int *p = a; + int *p_copy = ++p; + + return *p_copy; // trap +} diff --git a/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-prepost.c b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-prepost.c new file mode 100644 index 0000000000000..b9c92cf21de45 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/runtime-checks/ptr-arith-prepost.c @@ -0,0 +1,90 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[A:%.*]] = alloca [2 x i32], align 4 +// CHECK-O0-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[P_COPY:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[TMP_TMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[A]], i8 0, i64 8, i1 false) +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 2 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[P_COPY]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP4]], i64 1 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP10:%.*]] = load ptr, ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP10]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[TMP_TMP1]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[TMP13:%.*]] = load ptr, ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[BOUND_PTR_ARITH2:%.*]] = getelementptr i32, ptr [[TMP13]], i64 1 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[BOUND_PTR_ARITH2]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: [[TMP16:%.*]] = load ptr, ptr [[TMP15]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP16]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-O0-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[P]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP19]], ptr [[TMP20]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P_COPY]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP21:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP21]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP22:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP22]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap3: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont4: +// CHECK-O0-NEXT: [[TMP23:%.*]] = load i32, ptr [[WIDE_PTR_PTR]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP23]] +// +// CHECK-O2-LABEL: @main( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 0 +// +int main() { + int a[2] = {0}; + int *p = a; + int *p_copy = p++; + ++p; + + return *p_copy; +} diff --git a/clang/test/BoundsSafety/CodeGen/signed-vs-unsigned-bounds-check.c b/clang/test/BoundsSafety/CodeGen/signed-vs-unsigned-bounds-check.c new file mode 100644 index 0000000000000..72ee219e5109e --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/signed-vs-unsigned-bounds-check.c @@ -0,0 +1,18 @@ + + +// RUN: %clang_cc1 %s -O0 -fbounds-safety -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O2 -fbounds-safety -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -o - | FileCheck %s + +#include + +int f1(char *__bidi_indexable arr, char i) { + return arr[i]; + // CHECK: {{%.*}} = sext i8 {{%.*}} to i64 +} + +int f2(char *__bidi_indexable arr, unsigned char i) { + return arr[i]; + // CHECK: {{%.*}} = zext i8 {{%.*}} to i64 +} diff --git a/clang/test/BoundsSafety/CodeGen/single-null-O2.c b/clang/test/BoundsSafety/CodeGen/single-null-O2.c new file mode 100644 index 0000000000000..11fb350fd3384 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/single-null-O2.c @@ -0,0 +1,30 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 + +#include + +// CHECK-O2-LABEL: @TestOK( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 0 +// +int TestOK() { + int len = 0; + int *__single __counted_by(len) dcp = 0; + int *__single tp = dcp; + return 0; +} + +// FIXME +// CHECK-O2-LABEL: @TestTrap( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void TestTrap() { + int len = 1; + char p; + char *__counted_by(len) dcp = &p; + int *__single tp = dcp; // rdar://80816535 +} diff --git a/clang/test/BoundsSafety/CodeGen/single-null.c b/clang/test/BoundsSafety/CodeGen/single-null.c new file mode 100644 index 0000000000000..1a65f3cdc8f49 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/single-null.c @@ -0,0 +1,65 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 + +#include + +// CHECK-O0-LABEL: @main( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[LEN:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[DCP:%.*]] = alloca ptr, align 8 +// CHECK-O0-NEXT: [[TP:%.*]] = alloca ptr, align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-O0-NEXT: store i32 0, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[CMP:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: store ptr null, ptr [[DCP]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = load ptr, ptr [[DCP]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[LEN]], align 4 +// CHECK-O0-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX_EXT]] +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP6]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT4:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: boundscheck.notnull: +// CHECK-O0-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP7]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP8]], label [[CONT4]], label [[TRAP3:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap3: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont4: +// CHECK-O0-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TP]], align 8 +// CHECK-O0-NEXT: ret i32 0 +// +int main() { + int len = 0; + int *__single __counted_by(len) dcp = 0; + int *__single tp = dcp; + return 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/sized_by_or_null_call-O2.c b/clang/test/BoundsSafety/CodeGen/sized_by_or_null_call-O2.c new file mode 100644 index 0000000000000..7799147f91326 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/sized_by_or_null_call-O2.c @@ -0,0 +1,213 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 + +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s + +#include + +void foo(int *__sized_by_or_null(len) p, int len); + +// CHECK-LABEL: define dso_local void @caller_1( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @foo(ptr noundef null, i32 noundef 2) #[[ATTR5:[0-9]+]] +// CHECK-NEXT: ret void +// +void caller_1() { + foo(0, 2); +} + +// CHECK-LABEL: define dso_local void @caller_2( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @foo(ptr noundef null, i32 noundef 0) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_2() { + foo(0, 0); +} + +// rdar://118117905 +// CHECK-LABEL: define dso_local void @caller_3( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[P]], null, !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[CMP_NOT79:%.*]] = icmp slt i32 [[LEN]], 0 +// CHECK-NEXT: [[CMP_NOT:%.*]] = and i1 [[DOTNOT]], [[CMP_NOT79]], !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6:[0-9]+]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont: +// CHECK-NEXT: tail call void @foo(ptr noundef [[P]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_3(int *__sized_by_or_null(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_4( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// +void caller_4() { + int i = 0; + foo(&i, -1); +} + +// CHECK-LABEL: define dso_local void @caller_5( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[I]]) #[[ATTR5]] +// CHECK-NEXT: store i32 0, ptr [[I]], align 4, !tbaa [[TBAA4:![0-9]+]] +// CHECK-NEXT: call void @foo(ptr noundef nonnull [[I]], i32 noundef 2) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[I]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_5() { + int i = 0; + foo(&i, 2); +} + +// CHECK-LABEL: define dso_local void @caller_6( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[LEN]], 0, !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[LAND_RHS:%.*]], !annotation [[META3]] +// CHECK: land.rhs: +// CHECK-NEXT: tail call void @foo(ptr noundef [[P]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// +void caller_6(int *__sized_by(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_7( +// CHECK-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_9_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8 +// CHECK-NEXT: [[AGG_TEMP_SROA_9_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_9_0_P_SROA_IDX]], align 8 +// CHECK-NEXT: [[CMP_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], [[AGG_TEMP_SROA_9_0_COPYLOAD]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[LAND_LHS_TRUE:%.*]], !annotation [[META3]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[AGG_TEMP_SROA_17_0_P_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16 +// CHECK-NEXT: [[AGG_TEMP_SROA_17_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP_SROA_17_0_P_SROA_IDX]], align 8, !tbaa [[TBAA8:![0-9]+]] +// CHECK-NEXT: [[CMP27_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP_SROA_17_0_COPYLOAD]], [[AGG_TEMP_SROA_0_0_COPYLOAD]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP27_NOT]], label [[TRAP]], label [[LAND_RHS:%.*]], !annotation [[META3]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TOBOOL_NOT]], label [[CONT:%.*]], label [[LOR_RHS:%.*]], !annotation [[META3]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[LEN]] to i64, !annotation [[META3]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_9_0_COPYLOAD]] to i64, !annotation [[META3]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[AGG_TEMP_SROA_0_0_COPYLOAD]] to i64, !annotation [[META3]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META11:![0-9]+]] +// CHECK-NEXT: [[CMP65:%.*]] = icmp sge i64 [[SUB_PTR_SUB]], [[CONV]], !annotation [[META3]] +// CHECK-NEXT: [[CMP68:%.*]] = icmp sgt i32 [[LEN]], -1, !annotation [[META3]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP68]], [[CMP65]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label [[CONT]], label [[TRAP]], !prof [[PROF13:![0-9]+]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont: +// CHECK-NEXT: tail call void @foo(ptr noundef [[AGG_TEMP_SROA_0_0_COPYLOAD]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_7(int *__bidi_indexable p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_8( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq ptr [[P]], null, !annotation [[META3]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = icmp ult i32 [[LEN]], 5 +// CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[TOBOOL_NOT]], [[SPEC_SELECT]], !annotation [[META3]] +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF14:![0-9]+]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META3]] +// CHECK-NEXT: unreachable, !annotation [[META3]] +// CHECK: cont: +// CHECK-NEXT: tail call void @foo(ptr noundef [[P]], i32 noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_8(int *__single p, int len) { + foo(p, len); +} + +void bar(int *__sized_by(*len) *out, int *len); + +// CHECK-LABEL: define dso_local void @caller_9( +// CHECK-SAME: ptr noundef [[OUT:%.*]], ptr noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @bar(ptr noundef [[OUT]], ptr noundef [[LEN]]) #[[ATTR5]] +// CHECK-NEXT: ret void +// +void caller_9(int *__sized_by(*len) *out, int *len){ + bar(out, len); +} + +// CHECK-LABEL: define dso_local ptr @caller_10( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) local_unnamed_addr #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[COUNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[COUNT]]) #[[ATTR5]] +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 4, !annotation [[META15:![0-9]+]] +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr nonnull [[P]]) #[[ATTR5]] +// CHECK-NEXT: store ptr null, ptr [[P]], align 8, !annotation [[META15]] +// CHECK-NEXT: call void @bar(ptr noundef nonnull [[P]], ptr noundef nonnull [[COUNT]]) #[[ATTR5]] +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P]], align 8, !tbaa [[TBAA8]] +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[CMP_NOT90:%.*]] = icmp slt i32 [[TMP1]], 0, !annotation [[META3]] +// CHECK-NEXT: [[CMP_NOT:%.*]] = select i1 [[DOTNOT]], i1 [[CMP_NOT90]], i1 false, !annotation [[META3]] +// CHECK-NEXT: br i1 [[CMP_NOT]], label [[TRAP:%.*]], label [[LAND_RHS:%.*]], !annotation [[META3]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR6]], !annotation [[META16:![0-9]+]] +// CHECK-NEXT: unreachable, !annotation [[META16]] +// CHECK: land.rhs: +// CHECK-NEXT: br i1 [[DOTNOT]], label [[LOR_RHS:%.*]], label [[CONT67:%.*]], !annotation [[META3]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CMP61:%.*]] = icmp sge i32 [[TMP1]], [[LEN]], !annotation [[META3]] +// CHECK-NEXT: [[CMP64:%.*]] = icmp sgt i32 [[LEN]], -1, !annotation [[META3]] +// CHECK-NEXT: [[SPEC_SELECT:%.*]] = and i1 [[CMP64]], [[CMP61]] +// CHECK-NEXT: br i1 [[SPEC_SELECT]], label [[CONT67]], label [[TRAP]], !prof [[PROF13]], !annotation [[META3]] +// CHECK: cont67: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr nonnull [[P]]) #[[ATTR5]] +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[COUNT]]) #[[ATTR5]] +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__sized_by_or_null(len) caller_10(int len) { + int count; + int *__sized_by_or_null(count) p; + bar(&p, &count); + p = p; // workaround for missing return bounds check + count = len; + return p; +} + +//. +// CHECK: [[META2]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META3]] = !{!"bounds-safety-generic"} +// CHECK: [[TBAA4]] = !{[[META5:![0-9]+]], [[META5]], i64 0} +// CHECK: [[META5]] = !{!"int", [[META6:![0-9]+]], i64 0} +// CHECK: [[META6]] = !{!"omnipotent char", [[META7:![0-9]+]], i64 0} +// CHECK: [[META7]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TBAA8]] = !{[[META9:![0-9]+]], [[META9]], i64 0} +// CHECK: [[META9]] = !{!"p1 int", [[META10:![0-9]+]], i64 0} +// CHECK: [[META10]] = !{!"any pointer", [[META6]], i64 0} +// CHECK: [[META11]] = !{!"bounds-safety-generic", [[META12:![0-9]+]]} +// CHECK: [[META12]] = !{!"bounds-safety-missed-optimization-nsw", !"Check can not be removed because the arithmetic operation might wrap in the signed sense. Optimize the check by adding conditions to check for overflow before doing the operation"} +// CHECK: [[PROF13]] = !{!"branch_weights", i32 1048575, i32 1} +// CHECK: [[PROF14]] = !{!"branch_weights", i32 2097151, i32 1} +// CHECK: [[META15]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META16]] = !{!"bounds-safety-check-ptr-lt-upper-bound", !"bounds-safety-check-ptr-ge-lower-bound", !"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/sized_by_or_null_call.c b/clang/test/BoundsSafety/CodeGen/sized_by_or_null_call.c new file mode 100644 index 0000000000000..2c717054d57ee --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/sized_by_or_null_call.c @@ -0,0 +1,963 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-bounds-safety-init-list -emit-llvm %s -o - | FileCheck %s + +#include + +void foo(int *__sized_by_or_null(len) p, int len); + +// CHECK-LABEL: define dso_local void @caller_1( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2:![0-9]+]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @foo(ptr noundef null, i32 noundef 2) +// CHECK-NEXT: ret void +// +void caller_1() { + foo(0, 2); +} + +// CHECK-LABEL: define dso_local void @caller_2( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @foo(ptr noundef null, i32 noundef 0) +// CHECK-NEXT: ret void +// +void caller_2() { + foo(0, 0); +} + +// CHECK-LABEL: define dso_local void @caller_3( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META3:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP2]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP8]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END70:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END70]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP9]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB38]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[TMP13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB44]], ptr [[TMP15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[TMP16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB56]], ptr [[TMP17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB58]], ptr [[TMP18]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR60]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP65:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP65]], label [[LAND_RHS67:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs67: +// CHECK-NEXT: [[CMP68:%.*]] = icmp sle i32 0, [[TMP9]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP19:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP68]], [[LAND_RHS67]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP20:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP19]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END70]], !annotation [[META2]] +// CHECK: land.end70: +// CHECK-NEXT: [[TMP21:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[TMP20]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP21]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR73]], i32 noundef [[TMP9]]) +// CHECK-NEXT: ret void +// +void caller_3(int *__sized_by_or_null(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_4( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[I]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[I]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP3]], align 8 +// CHECK-NEXT: br i1 false, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR]], i32 noundef -1) +// CHECK-NEXT: ret void +// +void caller_4() { + int i = 0; + foo(&i, -1); +} + +// CHECK-LABEL: define dso_local void @caller_5( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 0, ptr [[I]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[I]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[I]], ptr [[TMP3]], align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR]], i32 noundef 2) +// CHECK-NEXT: ret void +// +void caller_5() { + int i = 0; + foo(&i, 2); +} + +// CHECK-LABEL: define dso_local void @caller_6( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP1]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END70:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END70]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP5]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB38]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB44]], ptr [[TMP11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB56]], ptr [[TMP13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB58]], ptr [[TMP14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR60]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP65:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP65]], label [[LAND_RHS67:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs67: +// CHECK-NEXT: [[CMP68:%.*]] = icmp sle i32 0, [[TMP5]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP68]], [[LAND_RHS67]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP16:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP15]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END70]], !annotation [[META2]] +// CHECK: land.end70: +// CHECK-NEXT: [[TMP17:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP16]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR73]], i32 noundef [[TMP5]]) +// CHECK-NEXT: ret void +// +void caller_6(int *__sized_by(len) p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_7( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP71:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[P]], i64 24, i1 false) +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END70:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END70]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR30]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB38]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP36]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB44]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB56]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB58]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR60]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP65:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP65]], label [[LAND_RHS67:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs67: +// CHECK-NEXT: [[CMP68:%.*]] = icmp sle i32 0, [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP10:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP68]], [[LAND_RHS67]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP11:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP10]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END70]], !annotation [[META2]] +// CHECK: land.end70: +// CHECK-NEXT: [[TMP12:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP11]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP71]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR72:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR73:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR72]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR74:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB75:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR74]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR76:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP71]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB77:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR76]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[WIDE_PTR_PTR73]], i32 noundef [[TMP0]]) +// CHECK-NEXT: ret void +// +void caller_7(int *__bidi_indexable p, int len) { + foo(p, len); +} + +// CHECK-LABEL: define dso_local void @caller_8( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP14:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP15:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB2:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_PTR]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END36:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: [[UPPER4:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP6]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER4]], ptr [[TMP7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP8]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB6:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB6]], ptr [[TMP9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB10:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB12:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP13:%.*]] = icmp ule ptr [[WIDE_PTR_PTR8]], [[TMP0]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP13]], label [[LAND_RHS:%.*]], label [[LAND_END36]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[TMP0]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP1]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[UPPER16:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1, !annotation [[META2]] +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP10]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[UPPER16]], ptr [[TMP11]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB18]], ptr [[TMP13]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP15]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR20]], ptr [[TMP14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB22]], ptr [[TMP15]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB24]], ptr [[TMP16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP14]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR26:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP14]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP14]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB30:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR26]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[TMP0]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP31:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP31]], label [[LAND_RHS33:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs33: +// CHECK-NEXT: [[CMP34:%.*]] = icmp sle i32 0, [[TMP1]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP17:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP34]], [[LAND_RHS33]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP18:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP17]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END36]], !annotation [[META2]] +// CHECK: land.end36: +// CHECK-NEXT: [[TMP19:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP18]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP19]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @foo(ptr noundef [[TMP0]], i32 noundef [[TMP1]]) +// CHECK-NEXT: ret void +// +void caller_8(int *__single p, int len) { + foo(p, len); +} + +void bar(int *__sized_by(*len) *out, int *len); + +// CHECK-LABEL: define dso_local void @caller_9( +// CHECK-SAME: ptr noundef [[OUT:%.*]], ptr noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[OUT]], ptr [[OUT_ADDR]], align 8 +// CHECK-NEXT: store ptr [[LEN]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OUT_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: call void @bar(ptr noundef [[TMP0]], ptr noundef [[TMP1]]) +// CHECK-NEXT: ret void +// +void caller_9(int *__sized_by(*len) *out, int *len){ + bar(out, len); +} + +// CHECK-LABEL: define dso_local ptr @caller_10( +// CHECK-SAME: i32 noundef [[LEN:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[COUNT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[P:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP17:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP32:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP72:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP79:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 [[LEN]], ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 4, !annotation [[META4:![0-9]+]] +// CHECK-NEXT: store ptr null, ptr [[P]], align 8, !annotation [[META4]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr ptr, ptr [[P]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[P]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[P]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr i32, ptr [[COUNT]], i64 1 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[COUNT]], ptr [[TMP7]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP8]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT4:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP9:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], !annotation [[META5:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP9]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META5]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: cont: +// CHECK-NEXT: [[TMP10:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], !annotation [[META6:![0-9]+]] +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT4]], label [[TRAP3:%.*]], !annotation [[META6]] +// CHECK: trap3: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: cont4: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP5]], ptr align 8 [[AGG_TEMP1]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR7]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP11]], label [[BOUNDSCHECK_NOTNULL12:%.*]], label [[CONT16:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull12: +// CHECK-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_UB9]], !annotation [[META5]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT14:%.*]], label [[TRAP13:%.*]], !annotation [[META5]] +// CHECK: trap13: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META5]] +// CHECK-NEXT: unreachable, !annotation [[META5]] +// CHECK: cont14: +// CHECK-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR7]], [[WIDE_PTR_LB11]], !annotation [[META6]] +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT16]], label [[TRAP15:%.*]], !annotation [[META6]] +// CHECK: trap15: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META6]] +// CHECK-NEXT: unreachable, !annotation [[META6]] +// CHECK: cont16: +// CHECK-NEXT: call void @bar(ptr noundef [[WIDE_PTR_PTR]], ptr noundef [[WIDE_PTR_PTR7]]) +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP16:%.*]] = icmp ne ptr [[TMP14]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP16]], label [[BOUNDSCHECK_NOTNULL18:%.*]], label [[BOUNDSCHECK_NULL:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull18: +// CHECK-NEXT: [[IDX_EXT:%.*]] = sext i32 [[TMP15]] to i64 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP14]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP19]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT:%.*]] +// CHECK: boundscheck.null: +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP17]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP22]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT]] +// CHECK: boundscheck.cont: +// CHECK-NEXT: [[TMP23:%.*]] = load i32, ptr [[LEN_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR21:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR20]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB23:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR22]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP19]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB25:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR24]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP26]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB28:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR27]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR21]], [[WIDE_PTR_UB28]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END69:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP29]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP29]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP32]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP32]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP39:%.*]] = icmp ule ptr [[WIDE_PTR_LB31]], [[WIDE_PTR_PTR34]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP39]], label [[LAND_RHS:%.*]], label [[LAND_END69]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR42:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR41]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB44:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR43]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP40]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB46:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR45]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[WIDE_PTR_PTR42]], null, !annotation [[META2]] +// CHECK-NEXT: br i1 [[TOBOOL]], label [[LOR_RHS:%.*]], label [[LOR_END:%.*]], !annotation [[META2]] +// CHECK: lor.rhs: +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[TMP23]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP51]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR53:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR52]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR56:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP51]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB57:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR56]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR53]], ptr [[TMP24]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB55]], ptr [[TMP25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB57]], ptr [[TMP26]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR58:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR59:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR58]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR60:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB61:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR60]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR62:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB63:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR62]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB49]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR59]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP64:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP64]], label [[LAND_RHS66:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs66: +// CHECK-NEXT: [[CMP67:%.*]] = icmp sle i32 0, [[TMP23]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP27:%.*]] = phi i1 [ false, [[LOR_RHS]] ], [ [[CMP67]], [[LAND_RHS66]] ] +// CHECK-NEXT: br label [[LOR_END]], !annotation [[META2]] +// CHECK: lor.end: +// CHECK-NEXT: [[TMP28:%.*]] = phi i1 [ true, [[LAND_RHS]] ], [ [[TMP27]], [[LAND_END]] ] +// CHECK-NEXT: br label [[LAND_END69]], !annotation [[META2]] +// CHECK: land.end69: +// CHECK-NEXT: [[TMP29:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[BOUNDSCHECK_CONT]] ], [ [[TMP28]], [[LOR_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP29]], label [[CONT71:%.*]], label [[TRAP70:%.*]], !annotation [[META2]] +// CHECK: trap70: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont71: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP72]], ptr align 8 [[AGG_TEMP17]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR73:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR74:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR73]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR75:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB76:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR75]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR77:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP72]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB78:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR77]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR74]], ptr [[P]], align 8 +// CHECK-NEXT: store i32 [[TMP23]], ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP30:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = load i32, ptr [[COUNT]], align 4 +// CHECK-NEXT: [[TMP32:%.*]] = icmp ne ptr [[TMP30]], null, !annotation [[META3]] +// CHECK-NEXT: br i1 [[TMP32]], label [[BOUNDSCHECK_NOTNULL80:%.*]], label [[BOUNDSCHECK_NULL83:%.*]], !annotation [[META3]] +// CHECK: boundscheck.notnull80: +// CHECK-NEXT: [[IDX_EXT81:%.*]] = sext i32 [[TMP31]] to i64 +// CHECK-NEXT: [[ADD_PTR82:%.*]] = getelementptr inbounds i8, ptr [[TMP30]], i64 [[IDX_EXT81]] +// CHECK-NEXT: [[TMP33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP30]], ptr [[TMP33]], align 8 +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR82]], ptr [[TMP34]], align 8 +// CHECK-NEXT: [[TMP35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP30]], ptr [[TMP35]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT84:%.*]] +// CHECK: boundscheck.null83: +// CHECK-NEXT: [[TMP36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[TMP36]], align 8 +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[TMP37]], align 8 +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 2 +// CHECK-NEXT: store ptr null, ptr [[TMP38]], align 8 +// CHECK-NEXT: br label [[BOUNDSCHECK_CONT84]] +// CHECK: boundscheck.cont84: +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR85:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR86:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR85]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR87:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB88:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR87]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR89:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP79]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB90:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR89]], align 8 +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR86]] +// +int *__sized_by_or_null(len) caller_10(int len) { + int count; + int *__sized_by_or_null(count) p; + bar(&p, &count); + p = p; // workaround for missing return bounds check + count = len; + return p; +} +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +// CHECK: [[META3]] = !{!"bounds-safety-check-ptr-neq-null"} +// CHECK: [[META4]] = !{!"bounds-safety-zero-init"} +// CHECK: [[META5]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK: [[META6]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/system-header-unsafe-main.c b/clang/test/BoundsSafety/CodeGen/system-header-unsafe-main.c new file mode 100644 index 0000000000000..e48635250c70c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/system-header-unsafe-main.c @@ -0,0 +1,130 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --functions "func" "funcInSDK" --include-generated-funcs --version 3 + +#include "../AST/SystemHeaders/include/system-header-unsafe-sys.h" + +// RUN: %clang_cc1 -fbounds-safety %s -O0 -triple arm64-apple-iphoneos -emit-llvm -o - | FileCheck %s + +void func(char * __unsafe_indexable ptr, char * __bidi_indexable bidi) { + funcInSDK(ptr, bidi); +} +// CHECK-LABEL: define dso_local void @funcInSDK +// CHECK-SAME: (ptr noundef [[PTR:%.*]], ptr noundef [[BIDI:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[BIDI_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP47:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[BIDI]], ptr [[BIDI_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BIDI]], i64 24, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP1]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END46:%.*]], !annotation [[META2]] +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP2]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END46]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP3]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[CMP44:%.*]] = icmp sle i64 5, [[SUB_PTR_SUB]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP44]], label [[LAND_RHS45:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs45: +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP4:%.*]] = phi i1 [ false, [[LAND_RHS]] ], [ true, [[LAND_RHS45]] ] +// CHECK-NEXT: br label [[LAND_END46]], !annotation [[META2]] +// CHECK: land.end46: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[TMP4]], [[LAND_END]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP47]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR49:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB51:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR50]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR52:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP47]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB53:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR52]], align 8 +// CHECK-NEXT: call void @funcWithAnnotation(ptr noundef [[TMP0]], ptr noundef [[WIDE_PTR_PTR49]]) +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: define dso_local void @func +// CHECK-SAME: (ptr noundef [[PTR:%.*]], ptr noundef [[BIDI:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[BIDI_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[BYVAL_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: store ptr [[BIDI]], ptr [[BIDI_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[BYVAL_TEMP]], ptr align 8 [[BIDI]], i64 24, i1 false) +// CHECK-NEXT: call void @funcInSDK(ptr noundef [[TMP0]], ptr noundef [[BYVAL_TEMP]]) +// CHECK-NEXT: ret void +// +//. +// CHECK: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-assign-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-assign-O2.c new file mode 100644 index 0000000000000..51af4ca84042b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-assign-O2.c @@ -0,0 +1,71 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[P]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void assign(int *__null_terminated p) { + *p = 42; +} + +// CHECK-LABEL: @compound_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], 42 +// CHECK-NEXT: store i32 [[ADD]], ptr [[P]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void compound_assign(int *__null_terminated p) { + *p += 42; +} + +// CHECK-LABEL: @subscript( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[P]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void subscript(int *__null_terminated p) { + p[0] = 42; +} + +// CHECK-LABEL: @nested_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P:%.*]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq ptr [[TMP0]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store ptr null, ptr [[P]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret void +// +void nested_assign(int *__single *__terminated_by(-1) p) { + *p = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-assign-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-assign-trivial-O2.c new file mode 100644 index 0000000000000..890da9a3077f6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-assign-trivial-O2.c @@ -0,0 +1,72 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @good_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_assign(void) { + char array[__null_terminated 2] = {1, 0}; + char *__null_terminated p = array; + *p = 42; +} + +// CHECK-LABEL: @good_compound_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_compound_assign(void) { + char array[__null_terminated 2] = {1, 0}; + char *__null_terminated p = array; + *p += 42; +} + +// CHECK-LABEL: @good_subscript( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good_subscript(void) { + char array[__null_terminated 2] = {1, 0}; + char *__null_terminated p = array; + p[0] = 42; +} + +// CHECK-LABEL: @bad_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_assign(void) { + char array[__null_terminated 2] = {1, 0}; + char *__null_terminated p = array; + p++; + *p = 42; +} + +// CHECK-LABEL: @bad_compound_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_compound_assign(void) { + char array[__null_terminated 2] = {1, 0}; + char *__null_terminated p = array; + p++; + *p += 42; +} + +// CHECK-LABEL: @bad_subscript( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad_subscript(void) { + char array[__null_terminated 2] = {1, 0}; + char *__null_terminated p = array; + p++; + p[0] = 42; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-assign.c b/clang/test/BoundsSafety/CodeGen/terminated-by-assign.c new file mode 100644 index 0000000000000..5209d7c927086 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-assign.c @@ -0,0 +1,85 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[TMP0]], align 4 +// CHECK-NEXT: ret void +// +void assign(int *__null_terminated p) { + *p = 42; +} + +// CHECK-LABEL: @compound_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP3]], 42 +// CHECK-NEXT: store i32 [[ADD]], ptr [[TMP0]], align 4 +// CHECK-NEXT: ret void +// +void compound_assign(int *__null_terminated p) { + *p += 42; +} + +// CHECK-LABEL: @subscript( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store i32 42, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret void +// +void subscript(int *__null_terminated p) { + p[0] = 42; +} + +// CHECK-LABEL: @nested_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP1]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store ptr null, ptr [[TMP0]], align 8 +// CHECK-NEXT: ret void +// +void nested_assign(int *__single *__terminated_by(-1) p) { + *p = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-O2.c new file mode 100644 index 0000000000000..e3d42603aaef4 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-O2.c @@ -0,0 +1,82 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[PTR_COERCE0:%.*]], [[PTR_COERCE1:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[TERMINATED_BY_LOOP_COND:%.*]], label [[TRAP:%.*]], !prof [[PROF3:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_CUR_0:%.*]] = phi ptr [ [[TERMINATED_BY_ONE_PAST_CUR:%.*]], [[CONT2:%.*]] ], [ [[PTR_COERCE0]], [[ENTRY:%.*]] ] +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_CUR]] = getelementptr inbounds nuw i8, ptr [[TERMINATED_BY_CUR_0]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ACCESS_NOT:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_CUR]], [[PTR_COERCE1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ACCESS_NOT]], label [[TRAP]], label [[CONT2]], !prof [[PROF6:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TERMINATED_BY_CUR_0]], align 4 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP1]], 0 +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: ret ptr [[PTR_COERCE0]] +// +int *__null_terminated indexable(int *__indexable ptr) { + return __unsafe_null_terminated_from_indexable(ptr); +} + +// CHECK-LABEL: @bidi_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_2_0_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_PTR_SROA_IDX]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp uge ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_3_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_2_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 [[TMP0]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TERMINATED_BY_LOOP_COND:%.*]], label [[TRAP:%.*]], !prof [[PROF13:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_CUR_0:%.*]] = phi ptr [ [[TERMINATED_BY_ONE_PAST_CUR:%.*]], [[CONT8:%.*]] ], [ [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[ENTRY:%.*]] ] +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_CUR]] = getelementptr inbounds nuw i8, ptr [[TERMINATED_BY_CUR_0]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ACCESS_NOT:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_CUR]], [[AGG_TEMP1_SROA_2_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ACCESS_NOT]], label [[TRAP]], label [[CONT8]], !prof [[PROF6]], {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TERMINATED_BY_CUR_0]], align 4 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP1]], 0 +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: ret ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] +// +int *__null_terminated bidi_indexable(int *__bidi_indexable ptr) { + return __unsafe_null_terminated_from_indexable(ptr); +} + +// CHECK-LABEL: @nested_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[PTR_COERCE0:%.*]], [[PTR_COERCE1:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[TERMINATED_BY_LOOP_COND:%.*]], label [[TRAP:%.*]], !prof [[PROF3]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_CUR_0:%.*]] = phi ptr [ [[TERMINATED_BY_ONE_PAST_CUR:%.*]], [[CONT2:%.*]] ], [ [[PTR_COERCE0]], [[ENTRY:%.*]] ] +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_CUR]] = getelementptr inbounds nuw i8, ptr [[TERMINATED_BY_CUR_0]], i64 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ACCESS_NOT:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_CUR]], [[PTR_COERCE1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ACCESS_NOT]], label [[TRAP]], label [[CONT2]], !prof [[PROF6]], {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[TERMINATED_BY_CUR_0]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TMP1]], inttoptr (i64 -1 to ptr) +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: ret ptr [[PTR_COERCE0]] +// +int *__single *__terminated_by(-1) nested_indexable(int *__single *__indexable ptr) { + return __unsafe_terminated_by_from_indexable(-1, ptr); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term-O2.c new file mode 100644 index 0000000000000..450c92adade7d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term-O2.c @@ -0,0 +1,116 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @indexable_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT:%.*]] = icmp ugt ptr [[PTR_COERCE0:%.*]], [[PTR_TO_TERM_COERCE0:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]], !prof [[PROF3:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr i8, ptr [[PTR_TO_TERM_COERCE0]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[PTR_TO_TERM_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[PTR_COERCE1:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT7:%.*]], label [[TRAP]], !prof [[PROF7:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: cont7: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR_TO_TERM_COERCE0]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT8:%.*]], label [[TRAP]], !prof [[PROF9:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: ret ptr [[PTR_COERCE0]] +// +int *__null_terminated indexable_indexable(int *__indexable ptr, int *__indexable ptr_to_term) { + return __unsafe_null_terminated_from_indexable(ptr, ptr_to_term); +} + +// CHECK-LABEL: @bidi_indexable_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_PTR_SROA_IDX]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_3_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[PTR_TO_TERM:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND10:%.*]] = or i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT]], [[DOTNOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND10]], label [[TRAP:%.*]], label [[CONT6:%.*]], !prof [[PROF16:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont6: +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_2_0_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr i8, ptr [[PTR_TO_TERM]], i64 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[PTR_TO_TERM]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[AGG_TEMP1_SROA_2_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT8:%.*]], label [[TRAP]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR_TO_TERM]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT9:%.*]], label [[TRAP]], !prof [[PROF9]], {{!annotation ![0-9]+}} +// CHECK: cont9: +// CHECK-NEXT: ret ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] +// +int *__null_terminated bidi_indexable_single(int *__bidi_indexable ptr, int *__single ptr_to_term) { + return __unsafe_null_terminated_from_indexable(ptr, ptr_to_term); +} + +// CHECK-LABEL: @nested_indexable_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT:%.*]] = icmp ugt ptr [[PTR_COERCE0:%.*]], [[PTR_TO_TERM_COERCE0:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT]], label [[TRAP:%.*]], label [[CONT:%.*]], !prof [[PROF3]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr i8, ptr [[PTR_TO_TERM_COERCE0]], i64 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[PTR_TO_TERM_COERCE0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[PTR_COERCE1:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT7:%.*]], label [[TRAP]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: cont7: +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_TO_TERM_COERCE0]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TMP0]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT8:%.*]], label [[TRAP]], !prof [[PROF9]], {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: ret ptr [[PTR_COERCE0]] +// +int *__single *__terminated_by(-1) nested_indexable_indexable(int *__single *__indexable ptr, int *__single *__indexable ptr_to_term) { + return __unsafe_terminated_by_from_indexable(-1, ptr, ptr_to_term); +} + +// CHECK-LABEL: @nested_bidi_indexable_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP1_SROA_0_0_COPYLOAD:%.*]] = load ptr, ptr [[PTR:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 16 +// CHECK-NEXT: [[AGG_TEMP1_SROA_3_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_3_0_PTR_SROA_IDX]], align 8, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[AGG_TEMP1_SROA_3_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT:%.*]] = icmp ugt ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]], [[PTR_TO_TERM:%.*]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND10:%.*]] = or i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR_NOT]], [[DOTNOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND10]], label [[TRAP:%.*]], label [[CONT6:%.*]], !prof [[PROF16]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont6: +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_PTR_SROA_IDX:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 8 +// CHECK-NEXT: [[AGG_TEMP1_SROA_2_0_COPYLOAD:%.*]] = load ptr, ptr [[AGG_TEMP1_SROA_2_0_PTR_SROA_IDX]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr i8, ptr [[PTR_TO_TERM]], i64 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[PTR_TO_TERM]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[AGG_TEMP1_SROA_2_0_COPYLOAD]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], i1 false, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT8:%.*]], label [[TRAP]], !prof [[PROF7]], {{!annotation ![0-9]+}} +// CHECK: cont8: +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_TO_TERM]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TMP0]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT9:%.*]], label [[TRAP]], !prof [[PROF9]], {{!annotation ![0-9]+}} +// CHECK: cont9: +// CHECK-NEXT: ret ptr [[AGG_TEMP1_SROA_0_0_COPYLOAD]] +// +int *__single *__terminated_by(-1) nested_bidi_indexable_single(int *__single *__bidi_indexable ptr, int *__single *__single ptr_to_term) { + return __unsafe_terminated_by_from_indexable(-1, ptr, ptr_to_term); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term-trivial-O2.c new file mode 100644 index 0000000000000..dd96589b3560f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term-trivial-O2.c @@ -0,0 +1,67 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include +#include + +static const struct { + int _dummy; + // We use int32_t instead of int, because we want a type whose size is greater than 1. + int32_t ints[3]; +} foo = { + 0, + {1, 2, 3}, +}; + +// CHECK-LABEL: @good( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr getelementptr inbounds nuw (i8, ptr @foo, i64 4) +// +const int32_t *__terminated_by(3) good(void) { + const int32_t *__indexable p = foo.ints; + return __unsafe_terminated_by_from_indexable(3, p, p + 2); +} + +// CHECK-LABEL: @bad_no_term( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +const int32_t *__terminated_by(3) bad_no_term(void) { + const int32_t *__indexable p = foo.ints; + return __unsafe_terminated_by_from_indexable(3, p, p); +} + +// CHECK-LABEL: @bad_ptr_to_term_oob( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +const int32_t *__terminated_by(3) bad_ptr_to_term_oob(void) { + const int32_t *__indexable p = foo.ints; + return __unsafe_terminated_by_from_indexable(3, p, p + 3); +} + +// CHECK-LABEL: @bad_ptr_to_term_oob2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +const int32_t *__terminated_by(3) bad_ptr_to_term_oob2(void) { + const int32_t *__indexable p = foo.ints; + const int32_t *__bidi_indexable q = p; + return __unsafe_terminated_by_from_indexable(3, p, q - 1); +} + +// CHECK-LABEL: @bad_ptr_to_term_oob3( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +const int32_t *__terminated_by(3) bad_ptr_to_term_oob3(void) { + const int32_t *__indexable p = foo.ints; + const int32_t *__bidi_indexable q = (const void *__bidi_indexable)p + sizeof(foo.ints) - 1; + return __unsafe_terminated_by_from_indexable(3, p, q); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term.c b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term.c new file mode 100644 index 0000000000000..3d32897e9f2ce --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-ptr-to-term.c @@ -0,0 +1,238 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @indexable_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[PTR_TO_TERM:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[PTR_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[PTR_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTR_TO_TERM]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[PTR_TO_TERM_COERCE0:%.*]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTR_TO_TERM]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[PTR_TO_TERM_COERCE1:%.*]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR_TO_TERM]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR3]], i64 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[WIDE_PTR_PTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont7: +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], label [[CONT9:%.*]], label [[TRAP8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont9: +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[WIDE_PTR_PTR3]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP4]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int *__null_terminated indexable_indexable(int *__indexable ptr, int *__indexable ptr_to_term) { + return __unsafe_null_terminated_from_indexable(ptr, ptr_to_term); +} + +// CHECK-LABEL: @bidi_indexable_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_TO_TERM_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[PTR_TO_TERM:%.*]], ptr [[PTR_TO_TERM_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_TO_TERM_ADDR]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR:%.*]] = icmp ule ptr [[WIDE_PTR_PTR3]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont7: +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr i32, ptr [[TMP3]], i64 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], label [[CONT9:%.*]], label [[TRAP8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont9: +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP4]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR3]] +// +int *__null_terminated bidi_indexable_single(int *__bidi_indexable ptr, int *__single ptr_to_term) { + return __unsafe_null_terminated_from_indexable(ptr, ptr_to_term); +} + +// CHECK-LABEL: @nested_indexable_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[PTR_TO_TERM:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[PTR_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[PTR_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTR_TO_TERM]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[PTR_TO_TERM_COERCE0:%.*]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTR_TO_TERM]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[PTR_TO_TERM_COERCE1:%.*]], ptr [[TMP3]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR_TO_TERM]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr ptr, ptr [[WIDE_PTR_PTR3]], i64 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[WIDE_PTR_PTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont7: +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], label [[CONT9:%.*]], label [[TRAP8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont9: +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TMP4]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int *__single *__terminated_by(-1) nested_indexable_indexable(int *__single *__indexable ptr, int *__single *__indexable ptr_to_term) { + return __unsafe_terminated_by_from_indexable(-1, ptr, ptr_to_term); +} + +// CHECK-LABEL: @nested_bidi_indexable_single( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR_TO_TERM_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[PTR_TO_TERM:%.*]], ptr [[PTR_TO_TERM_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR_TO_TERM_ADDR]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR:%.*]] = icmp ule ptr [[WIDE_PTR_PTR3]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_PTR_LE_TERM_PTR]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont7: +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_TERM_PTR:%.*]] = getelementptr ptr, ptr [[TMP3]], i64 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW:%.*]] = icmp ugt ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[TMP3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_OVERFLOW]], label [[CONT9:%.*]], label [[TRAP8:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap8: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont9: +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_TERM_PTR]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ONE_PAST_TERM_PTR_LE_UPPER]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap10: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont11: +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TMP4]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap12: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont13: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR3]] +// +int *__single *__terminated_by(-1) nested_bidi_indexable_single(int *__single *__bidi_indexable ptr, int *__single *__single ptr_to_term) { + return __unsafe_terminated_by_from_indexable(-1, ptr, ptr_to_term); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-trivial-O2.c new file mode 100644 index 0000000000000..4eda5ca5ab4aa --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable-trivial-O2.c @@ -0,0 +1,64 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static const struct { + int _dummy; + int ints[3]; +} foo = { + 0, + {1, 2, 3}, +}; + +// CHECK-LABEL: @good( +// CHECK-NEXT: cont8.2: +// CHECK-NEXT: ret ptr getelementptr inbounds nuw (i8, ptr @foo, i64 4) +// +const int *__terminated_by(3) good(void) { + const int *__indexable p = foo.ints; + return __unsafe_terminated_by_from_indexable(3, p); +} + +// CHECK-LABEL: @good_2( +// CHECK-NEXT: cont8.1: +// CHECK-NEXT: ret ptr getelementptr inbounds nuw (i8, ptr @foo, i64 4) +// +const int *__terminated_by(2) good_2(void) { + const int *__indexable p = foo.ints; + return __unsafe_terminated_by_from_indexable(2, p); +} + +// CHECK-LABEL: @bad_null( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +int *__null_terminated bad_null(void) { + int *__indexable p = 0; + return __unsafe_null_terminated_from_indexable(p); +} + +// CHECK-LABEL: @bad_no_term( +// CHECK-NEXT: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +const int *__null_terminated bad_no_term(void) { + const int *__indexable p = foo.ints; + return __unsafe_null_terminated_from_indexable(p); +} + +// CHECK-LABEL: @bad_bidi_lower_bound( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +const int *__terminated_by(3) bad_bidi_lower_bound(void) { + // Conversion of __bidi_indexable to __indexable should fail. + const int *__bidi_indexable p = foo.ints; + p--; + return __unsafe_terminated_by_from_indexable(3, p); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable.c b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable.c new file mode 100644 index 0000000000000..3875730293836 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-from-indexable.c @@ -0,0 +1,150 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[TERMINATED_BY_CUR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[PTR_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[PTR_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_CUR1:%.*]] = load ptr, ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_CUR:%.*]] = getelementptr inbounds i32, ptr [[TERMINATED_BY_CUR1]], i64 1 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ACCESS:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_CUR]], [[WIDE_PTR_UB]] +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ACCESS]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont3: +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[TERMINATED_BY_CUR1]], align 4 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP3]], 0 +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: store ptr [[TERMINATED_BY_ONE_PAST_CUR]], ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int *__null_terminated indexable(int *__indexable ptr) { + return __unsafe_null_terminated_from_indexable(ptr); +} + +// CHECK-LABEL: @bidi_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TERMINATED_BY_CUR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[PTR:%.*]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = icmp ult ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont7: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_CUR8:%.*]] = load ptr, ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_CUR:%.*]] = getelementptr inbounds i32, ptr [[TERMINATED_BY_CUR8]], i64 1 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ACCESS:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_CUR]], [[WIDE_PTR_UB5]] +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ACCESS]], label [[CONT10:%.*]], label [[TRAP9:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap9: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont10: +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TERMINATED_BY_CUR8]], align 4 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TMP4]], 0 +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: store ptr [[TERMINATED_BY_ONE_PAST_CUR]], ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR3]] +// +int *__null_terminated bidi_indexable(int *__bidi_indexable ptr) { + return __unsafe_null_terminated_from_indexable(ptr); +} + +// CHECK-LABEL: @nested_indexable( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[TERMINATED_BY_CUR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[PTR_COERCE0:%.*]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { ptr, ptr }, ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[PTR_COERCE1:%.*]], ptr [[TMP1]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_CUR1:%.*]] = load ptr, ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_ONE_PAST_CUR:%.*]] = getelementptr inbounds ptr, ptr [[TERMINATED_BY_CUR1]], i64 1 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_ACCESS:%.*]] = icmp ule ptr [[TERMINATED_BY_ONE_PAST_CUR]], [[WIDE_PTR_UB]] +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_ACCESS]], label [[CONT3:%.*]], label [[TRAP2:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap2: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont3: +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[TERMINATED_BY_CUR1]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TMP3]], inttoptr (i64 -1 to ptr) +// CHECK-NEXT: br i1 [[TERMINATED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: store ptr [[TERMINATED_BY_ONE_PAST_CUR]], ptr [[TERMINATED_BY_CUR]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: ret ptr [[WIDE_PTR_PTR]] +// +int *__single *__terminated_by(-1) nested_indexable(int *__single *__indexable ptr) { + return __unsafe_terminated_by_from_indexable(-1, ptr); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith-O2.c new file mode 100644 index 0000000000000..e4591b308f959 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith-O2.c @@ -0,0 +1,171 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @pre_inc( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[INCDEC_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int pre_inc(int *__null_terminated p) { + ++p; + return *p; +} + +// CHECK-LABEL: @post_inc( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[INCDEC_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int post_inc(int *__null_terminated p) { + p++; + return *p; +} + +// CHECK-LABEL: @add_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ADD_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int add_one(int *__null_terminated p) { + p = p + 1; + return *p; +} + +// CHECK-LABEL: @add_one_swap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ADD_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int add_one_swap(int *__null_terminated p) { + p = 1 + p; + return *p; +} + +// CHECK-LABEL: @assign_add_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ADD_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int assign_add_one(int *__null_terminated p) { + p += 1; + return *p; +} + +// CHECK-LABEL: @sub_minus_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ADD_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int sub_minus_one(int *__null_terminated p) { + p = p - (-1); + return *p; +} + +// CHECK-LABEL: @assign_sub_minus_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[TMP0]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ADD_PTR]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP1]] +// +int assign_sub_minus_one(int *__null_terminated p) { + p -= -1; + return *p; +} + +// CHECK-LABEL: @add_zero( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP0]] +// +int add_zero(int *__null_terminated p) { + p = p + 0; + return *p; +} + +// CHECK-LABEL: @add_zero_swap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP0]] +// +int add_zero_swap(int *__null_terminated p) { + p = 0 + p; + return *p; +} + +// CHECK-LABEL: @add_assign_zero( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP0]] +// +int add_assign_zero(int *__null_terminated p) { + p += 0; + return *p; +} + +// CHECK-LABEL: @subscript_zero( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P:%.*]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: ret i32 [[TMP0]] +// +int subscript_zero(int *__null_terminated p) { + return p[0]; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith-trivial-O2.c new file mode 100644 index 0000000000000..268323995e168 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith-trivial-O2.c @@ -0,0 +1,40 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @good( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void good(void) { + int array[__null_terminated 2] = {42, 0}; + int *__null_terminated p = array; + p++; +} + +// CHECK-LABEL: @bad( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad(void) { + int array[__null_terminated 2] = {42, 0}; + int *__null_terminated p = array; + p++; + p++; +} + +// CHECK-LABEL: @bad2( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// +void bad2(void) { + int array[__null_terminated 2] = {42, 0}; + int *__null_terminated p = array; + *p = 0; + p++; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith.c b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith.c new file mode 100644 index 0000000000000..122bb00b8a48c --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-arith.c @@ -0,0 +1,228 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @pre_inc( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int pre_inc(int *__null_terminated p) { + ++p; + return *p; +} + +// CHECK-LABEL: @post_inc( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int post_inc(int *__null_terminated p) { + p++; + return *p; +} + +// CHECK-LABEL: @add_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int add_one(int *__null_terminated p) { + p = p + 1; + return *p; +} + +// CHECK-LABEL: @add_one_swap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int add_one_swap(int *__null_terminated p) { + p = 1 + p; + return *p; +} + +// CHECK-LABEL: @assign_add_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int assign_add_one(int *__null_terminated p) { + p += 1; + return *p; +} + +// CHECK-LABEL: @sub_minus_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int sub_minus_one(int *__null_terminated p) { + p = p - (-1); + return *p; +} + +// CHECK-LABEL: @assign_sub_minus_one( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int assign_sub_minus_one(int *__null_terminated p) { + p -= -1; + return *p; +} + +// CHECK-LABEL: @add_zero( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 0 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 [[TMP2]] +// +int add_zero(int *__null_terminated p) { + p = p + 0; + return *p; +} + +// CHECK-LABEL: @add_zero_swap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 0 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 [[TMP2]] +// +int add_zero_swap(int *__null_terminated p) { + p = 0 + p; + return *p; +} + +// CHECK-LABEL: @add_assign_zero( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 0 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: ret i32 [[TMP2]] +// +int add_assign_zero(int *__null_terminated p) { + p += 0; + return *p; +} + +// CHECK-LABEL: @subscript_zero( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// CHECK-NEXT: ret i32 [[TMP1]] +// +int subscript_zero(int *__null_terminated p) { + return p[0]; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-cast-unsafe-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-cast-unsafe-O2.c new file mode 100644 index 0000000000000..c48c16be7a4c6 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-cast-unsafe-O2.c @@ -0,0 +1,22 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @unsafe_to_null_terminated( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr [[P:%.*]] +// +const char *__null_terminated unsafe_to_null_terminated(const char *__unsafe_indexable p) { + return __unsafe_forge_terminated_by(const char *, p, 0); +} + +// CHECK-LABEL: @null_terminated_to_unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret ptr [[P:%.*]] +// +const char *__unsafe_indexable null_terminated_to_unsafe(const char *__null_terminated p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-cast-unsafe.c b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-cast-unsafe.c new file mode 100644 index 0000000000000..aabaf4896619b --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-cast-unsafe.c @@ -0,0 +1,28 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @unsafe_to_null_terminated( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +const char *__null_terminated unsafe_to_null_terminated(const char *__unsafe_indexable p) { + return __unsafe_forge_terminated_by(const char *, p, 0); +} + +// CHECK-LABEL: @null_terminated_to_unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +const char *__unsafe_indexable null_terminated_to_unsafe(const char *__null_terminated p) { + return p; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-relaxed-casting.c b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-relaxed-casting.c new file mode 100644 index 0000000000000..794db108a5ee2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-ptr-relaxed-casting.c @@ -0,0 +1,109 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2 + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -Wno-error=bounds-safety-strict-terminated-by-cast -emit-llvm %s -o - | FileCheck %s + +#include + +// This should be removed by rdar://118390724 +// Until then this is here to make sure that Sema retains a valid AST when emitted as warning, so that CodeGen doesn't crash + +void foo(const char * __null_terminated); +void bar(const char * __null_terminated * __single); + +// CHECK-LABEL: define dso_local void @test +// CHECK-SAME: (ptr noundef [[SP:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[SP_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NTP:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NTP2:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NTP3:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[SPP:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[NTPP:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NTPP2:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[NTPP3:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[SP]], ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[NTP]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[NTP2]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[TMP2]]) +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: call void @foo(ptr noundef [[TMP3]]) +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[NTP]], align 8 +// CHECK-NEXT: store ptr [[TMP4]], ptr [[NTP3]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP5]], ptr [[NTP3]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[SP_ADDR]], align 8 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[NTP3]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr ptr, ptr [[SP_ADDR]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[SP_ADDR]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[SP_ADDR]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = icmp ne ptr [[WIDE_PTR_PTR]], null +// CHECK-NEXT: br i1 [[TMP11]], label [[BOUNDSCHECK_NOTNULL:%.*]], label [[CONT2:%.*]], +// CHECK: boundscheck.notnull: +// CHECK-NEXT: [[TMP12:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]] +// CHECK-NEXT: br i1 [[TMP12]], label [[CONT:%.*]], label [[TRAP:%.*]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]] +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[TMP13:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]] +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT2]], label [[TRAP1:%.*]] +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]] +// CHECK-NEXT: unreachable +// CHECK: cont2: +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[SPP]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[SPP]], align 8 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[NTPP]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[SPP]], align 8 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[NTPP2]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = load ptr, ptr [[SPP]], align 8 +// CHECK-NEXT: call void @bar(ptr noundef [[TMP16]]) +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[SPP]], align 8 +// CHECK-NEXT: call void @bar(ptr noundef [[TMP17]]) +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[NTPP]], align 8 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[NTPP3]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[SPP]], align 8 +// CHECK-NEXT: store ptr [[TMP19]], ptr [[NTPP3]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = load ptr, ptr [[SPP]], align 8 +// CHECK-NEXT: store ptr [[TMP20]], ptr [[NTPP3]], align 8 +// CHECK-NEXT: ret void +// +void test(const char * __single sp) { + const char * __null_terminated ntp = sp; + const char * __null_terminated ntp2 = (const char * __null_terminated) sp; + + foo(sp); + foo((const char * __null_terminated) sp); + + const char * __null_terminated ntp3 = ntp; + ntp3 = sp; + ntp3 = (const char * __null_terminated) sp; + + + /* --- Nested --- */ + + const char * __single * __single spp = &sp; + const char * __null_terminated * __single ntpp = spp; + const char * __null_terminated * __single ntpp2 = (const char * __null_terminated * __single) spp; + + bar(spp); + bar((const char * __null_terminated * __single) spp); + + const char * __null_terminated * __single ntpp3 = ntpp; + ntpp3 = spp; + ntpp3 = (const char * __null_terminated * __single) spp; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-term.c b/clang/test/BoundsSafety/CodeGen/terminated-by-term.c new file mode 100644 index 0000000000000..d07091f3dc6c2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-term.c @@ -0,0 +1,129 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// Check how the terminator and its load instruction are generated for different +// types, values etc. + +// CHECK-LABEL: @nul_char( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr [[TMP0]], align 1 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i8 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void nul_char(char *__null_terminated p) { + p++; +} + +// CHECK-LABEL: @nul_int( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void nul_int(int *__null_terminated p) { + p++; +} + +// CHECK-LABEL: @nul_ptr( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP1]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw ptr, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void nul_ptr(int **__null_terminated p) { + p++; +} + +// CHECK-LABEL: @neg_one_int( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], -1, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void neg_one_int(int *__terminated_by(-1) p) { + p++; +} + +// CHECK-LABEL: @neg_one_unsigned( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[TMP1]], -1, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void neg_one_unsigned(unsigned *__terminated_by(-1) p) { + p++; +} + +// CHECK-LABEL: @neg_one_ptr( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = icmp ne ptr [[TMP1]], inttoptr (i64 -1 to ptr), {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR2]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds nuw ptr, ptr [[TMP0]], i32 1 +// CHECK-NEXT: store ptr [[INCDEC_PTR]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: ret void +// +void neg_one_ptr(int **__terminated_by(-1) p) { + p++; +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-builtin-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-builtin-O2.c new file mode 100644 index 0000000000000..e6fc067c34421 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-builtin-O2.c @@ -0,0 +1,31 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @safe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call i64 @strlen(ptr noundef nonnull dereferenceable(1) [[P:%.*]]) +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 [[CALL]] +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[P]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[TERMINATED_BY_UPPER]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +const char *__indexable safe(const char *__null_terminated p) { + return __terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CALL:%.*]] = tail call i64 @strlen(ptr noundef nonnull dereferenceable(1) [[P:%.*]]) +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[P]], i64 [[CALL]] +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr i8, ptr [[TMP0]], i64 1 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[P]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[TERMINATED_BY_UPPER]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +const char *__indexable unsafe(const char *__null_terminated p) { + return __unsafe_terminated_by_to_indexable(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-builtin.c b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-builtin.c new file mode 100644 index 0000000000000..2ab629d06df94 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-builtin.c @@ -0,0 +1,45 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @safe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call i64 @strlen(ptr noundef [[TMP0]]) +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[CALL]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP3]] +// +const char *__indexable safe(const char *__null_terminated p) { + return __terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call i64 @strlen(ptr noundef [[TMP0]]) +// CHECK-NEXT: [[TMP1:%.*]] = add i64 [[CALL]], 1 +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TMP1]] +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP4]] +// +const char *__indexable unsafe(const char *__null_terminated p) { + return __unsafe_terminated_by_to_indexable(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-freestanding.c b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-freestanding.c new file mode 100644 index 0000000000000..1a09a0d2ba4f0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-freestanding.c @@ -0,0 +1,75 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -ffreestanding -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -ffreestanding -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// Make sure we don't call strlen() when -ffreestanding is set. + +// CHECK-LABEL: @safe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i8, ptr [[TMP1]], align 1 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i8 [[TERMINATED_BY_ELEM]], 0 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// CHECK-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TMP2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP5]] +// +const char *__indexable safe(const char *__null_terminated p) { + return __terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i8, ptr [[TMP1]], align 1 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i8 [[TERMINATED_BY_ELEM]], 0 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// CHECK-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = add i64 [[TMP2]], 1 +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i8, ptr [[TMP0]], i64 [[TMP3]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP6]] +// +const char *__indexable unsafe(const char *__null_terminated p) { + return __unsafe_terminated_by_to_indexable(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-loop-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-loop-O2.c new file mode 100644 index 0000000000000..63885c02190fc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-loop-O2.c @@ -0,0 +1,71 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// REQUIRES: x86-registered-target + +// Disable loop idiom recognize for wcslen() as a workaround for rdar://148775324 +// (https://github.com/llvm/llvm-project/issues/134736). +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -mllvm -disable-loop-idiom-wcslen -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -mllvm -disable-loop-idiom-wcslen -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @safe( +// CHECK-NEXT: entry: +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN_0:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[TERMINATED_BY_NEW_LEN:%.*]], [[TERMINATED_BY_LOOP_COND]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 [[TERMINATED_BY_LEN_0]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TERMINATED_BY_ELEM]], 0 +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN]] = add i64 [[TERMINATED_BY_LEN_0]], 1 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[TERMINATED_BY_LEN_0]] +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[P]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[TMP1]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable safe(int *__null_terminated p) { + return __terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN_0:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[TERMINATED_BY_NEW_LEN:%.*]], [[TERMINATED_BY_LOOP_COND]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 [[TERMINATED_BY_LEN_0]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i32, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TERMINATED_BY_ELEM]], 0 +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN]] = add i64 [[TERMINATED_BY_LEN_0]], 1 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[TERMINATED_BY_LEN_0]] +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr i8, ptr [[TMP1]], i64 4 +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[P]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[TERMINATED_BY_UPPER]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__indexable unsafe(int *__null_terminated p) { + return __unsafe_terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @nested( +// CHECK-NEXT: entry: +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN_0:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[TERMINATED_BY_NEW_LEN:%.*]], [[TERMINATED_BY_LOOP_COND]] ] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds ptr, ptr [[P:%.*]], i64 [[TERMINATED_BY_LEN_0]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load ptr, ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TERMINATED_BY_ELEM]], inttoptr (i64 -1 to ptr) +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN]] = add i64 [[TERMINATED_BY_LEN_0]], 1 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds ptr, ptr [[P]], i64 [[TERMINATED_BY_LEN_0]] +// CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue { ptr, ptr } poison, ptr [[P]], 0 +// CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue { ptr, ptr } [[DOTFCA_0_INSERT]], ptr [[TMP1]], 1 +// CHECK-NEXT: ret { ptr, ptr } [[DOTFCA_1_INSERT]] +// +int *__single *__indexable nested(int *__single *__terminated_by(-1) p) { + return __terminated_by_to_indexable(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-loop.c b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-loop.c new file mode 100644 index 0000000000000..f5881073a08fa --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-loop.c @@ -0,0 +1,106 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @safe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TERMINATED_BY_ELEM]], 0 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// CHECK-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TMP2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP5]] +// +int *__indexable safe(int *__null_terminated p) { + return __terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load i32, ptr [[TMP1]], align 4 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq i32 [[TERMINATED_BY_ELEM]], 0 +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// CHECK-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = add i64 [[TMP2]], 1 +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds i32, ptr [[TMP0]], i64 [[TMP3]] +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP6]] +// +int *__indexable unsafe(int *__null_terminated p) { + return __unsafe_terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @nested( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable.0", align 8 +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[TERMINATED_BY_LEN:%.*]] = alloca i64, align 8 +// CHECK-NEXT: store ptr [[P:%.*]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i64 0, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND:%.*]] +// CHECK: terminated_by.loop_cond: +// CHECK-NEXT: [[TERMINATED_BY_LEN1:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[TERMINATED_BY_LEN1]] +// CHECK-NEXT: [[TERMINATED_BY_ELEM:%.*]] = load ptr, ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TERMINTED_BY_CHECK_TERMINATOR:%.*]] = icmp eq ptr [[TERMINATED_BY_ELEM]], inttoptr (i64 -1 to ptr) +// CHECK-NEXT: br i1 [[TERMINTED_BY_CHECK_TERMINATOR]], label [[TERMINATED_BY_LOOP_END:%.*]], label [[TERMINATED_BY_LOOP_CONT:%.*]] +// CHECK: terminated_by.loop_cont: +// CHECK-NEXT: [[TERMINATED_BY_NEW_LEN:%.*]] = add i64 [[TERMINATED_BY_LEN1]], 1 +// CHECK-NEXT: store i64 [[TERMINATED_BY_NEW_LEN]], ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: br label [[TERMINATED_BY_LOOP_COND]] +// CHECK: terminated_by.loop_end: +// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TERMINATED_BY_LEN]], align 8 +// CHECK-NEXT: [[TERMINATED_BY_UPPER:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[TMP2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.0", ptr [[RETVAL]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable.0", ptr [[RETVAL]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TERMINATED_BY_UPPER]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load { ptr, ptr }, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret { ptr, ptr } [[TMP5]] +// +int *__single *__indexable nested(int *__single *__terminated_by(-1) p) { + return __terminated_by_to_indexable(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-trivial-O2.c new file mode 100644 index 0000000000000..e2084ffe984d0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/terminated-by-to-indexable-trivial-O2.c @@ -0,0 +1,59 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +static const int ints[__null_terminated 2] = {42, 0}; +static const char *__null_terminated chars = "Hello"; + +// CHECK-LABEL: @good_ints( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr @ints, ptr getelementptr inbounds nuw (i8, ptr @ints, i64 4) } +// +const int *__indexable good_ints(void) { + return __terminated_by_to_indexable(ints); +} + +// CHECK-LABEL: @good_ints_unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr @ints, ptr getelementptr inbounds nuw (i8, ptr @ints, i64 8) } +// +const int *__indexable good_ints_unsafe(void) { + return __unsafe_terminated_by_to_indexable(ints); +} + +// CHECK-LABEL: @good_chars( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr @.str, ptr getelementptr inbounds nuw (i8, ptr @.str, i64 5) } +// +const char *__indexable good_chars(void) { + return __terminated_by_to_indexable(chars); +} + +// CHECK-LABEL: @good_chars_unsafe( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret { ptr, ptr } { ptr @.str, ptr getelementptr inbounds nuw (i8, ptr @.str, i64 6) } +// +const char *__indexable good_chars_unsafe(void) { + return __unsafe_terminated_by_to_indexable(chars); +} + +// CHECK-LABEL: @bad_null( +// CHECK-NEXT: terminated_by.loop_end: +// CHECK-NEXT: ret { ptr, ptr } zeroinitializer +// +int *__indexable bad_null(void) { + int *__null_terminated p = 0; + return __terminated_by_to_indexable(p); +} + +// CHECK-LABEL: @bad_null_unsafe( +// CHECK-NEXT: terminated_by.loop_end: +// CHECK-NEXT: ret { ptr, ptr } zeroinitializer +// +int *__indexable bad_null_unsafe(void) { + int *__null_terminated p = 0; + return __unsafe_terminated_by_to_indexable(p); +} diff --git a/clang/test/BoundsSafety/CodeGen/trap-function-returns-O2.c b/clang/test/BoundsSafety/CodeGen/trap-function-returns-O2.c new file mode 100644 index 0000000000000..4abbf4260350a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-function-returns-O2.c @@ -0,0 +1,30 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 3 + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -ftrap-function=fb_trap -ftrap-function-returns -O2 -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 + +#include + +// X86_64-LABEL: define dso_local i32 @foo( +// X86_64-SAME: ptr noundef readonly captures(address) [[BAR:%.*]], i32 noundef [[COUNT:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: [[IDX_EXT:%.*]] = zext i32 [[COUNT]] to i64 +// X86_64-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[BAR]], i64 [[IDX_EXT]] +// X86_64-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[BAR]], i64 4 +// X86_64-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[ADD_PTR]], !annotation [[META2:![0-9]+]] +// X86_64-NEXT: br i1 [[TMP0]], label [[CONT:%.*]], label [[TRAP:%.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// X86_64: trap: +// X86_64-NEXT: tail call void @fb_trap(i8 25) #[[ATTR1:[0-9]+]], !annotation [[META2]] +// X86_64-NEXT: br label [[CONT]], !annotation [[META2]] +// X86_64: cont: +// X86_64-NEXT: [[DOTNOT:%.*]] = icmp ult ptr [[ARRAYIDX]], [[BAR]], !annotation [[META4:![0-9]+]] +// X86_64-NEXT: br i1 [[DOTNOT]], label [[TRAP1:%.*]], label [[CONT2:%.*]], !prof [[PROF5:![0-9]+]], !annotation [[META4]] +// X86_64: trap1: +// X86_64-NEXT: tail call void @fb_trap(i8 25) #[[ATTR1]], !annotation [[META4]] +// X86_64-NEXT: br label [[CONT2]], !annotation [[META4]] +// X86_64: cont2: +// X86_64-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA6:![0-9]+]] +// X86_64-NEXT: ret i32 [[TMP1]] +// +int foo(int *__counted_by(count) bar, unsigned count) { + return bar[1]; +} diff --git a/clang/test/BoundsSafety/CodeGen/trap-function-returns.c b/clang/test/BoundsSafety/CodeGen/trap-function-returns.c new file mode 100644 index 0000000000000..ae614d4d98f97 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-function-returns.c @@ -0,0 +1,53 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ --version 3 + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -ftrap-function=fb_trap -ftrap-function-returns -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 + +#include + +// X86_64-LABEL: define dso_local i32 @foo +// X86_64-SAME: (ptr noundef [[BAR:%.*]], i32 noundef [[COUNT:%.*]]) #[[ATTR0:[0-9]+]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: [[BAR_ADDR:%.*]] = alloca ptr, align 8 +// X86_64-NEXT: [[COUNT_ADDR:%.*]] = alloca i32, align 4 +// X86_64-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// X86_64-NEXT: store ptr [[BAR]], ptr [[BAR_ADDR]], align 8 +// X86_64-NEXT: store i32 [[COUNT]], ptr [[COUNT_ADDR]], align 4 +// X86_64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BAR_ADDR]], align 8 +// X86_64-NEXT: [[TMP1:%.*]] = load i32, ptr [[COUNT_ADDR]], align 4 +// X86_64-NEXT: [[IDX_EXT:%.*]] = zext i32 [[TMP1]] to i64 +// X86_64-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i32, ptr [[TMP0]], i64 [[IDX_EXT]] +// X86_64-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// X86_64-NEXT: store ptr [[TMP0]], ptr [[TMP2]], align 8 +// X86_64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// X86_64-NEXT: store ptr [[ADD_PTR]], ptr [[TMP3]], align 8 +// X86_64-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// X86_64-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// X86_64-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// X86_64-NEXT: [[TMP5:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 1 +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// X86_64-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// X86_64-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// X86_64-NEXT: [[TMP6:%.*]] = icmp ult ptr [[TMP5]], [[WIDE_PTR_UB]], !annotation [[META2:![0-9]+]] +// X86_64-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// X86_64: trap: +// X86_64-NEXT: call void @fb_trap(i8 25) #[[ATTR1:[0-9]+]], !annotation [[META2]] +// X86_64-NEXT: br label [[CONT]], !annotation [[META2]] +// X86_64: cont: +// X86_64-NEXT: [[TMP7:%.*]] = icmp uge ptr [[TMP5]], [[WIDE_PTR_LB]], !annotation [[META3:![0-9]+]] +// X86_64-NEXT: br i1 [[TMP7]], label [[CONT2:%.*]], label [[TRAP1:%.*]], !annotation [[META3]] +// X86_64: trap1: +// X86_64-NEXT: call void @fb_trap(i8 25) #[[ATTR1]], !annotation [[META3]] +// X86_64-NEXT: br label [[CONT2]], !annotation [[META3]] +// X86_64: cont2: +// X86_64-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP5]], align 4 +// X86_64-NEXT: ret i32 [[TMP8]] +// +int foo(int *__counted_by(count) bar, unsigned count) { + return bar[1]; +} +//. +// X86_64: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// X86_64: [[META3]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/trap-optnone.c b/clang/test/BoundsSafety/CodeGen/trap-optnone.c new file mode 100644 index 0000000000000..9e4c4d970f3ab --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-optnone.c @@ -0,0 +1,38 @@ + +// RUN: %clang_cc1 -O3 -fbounds-safety -triple arm64-apple-darwin -emit-llvm %s -o - | FileCheck %s + +#include + +int consume(char* __bidi_indexable d) __attribute__((optnone)) { + return d[0]; +} + +// CHECK-LABEL: consume +// CHECK: [[ADDR:%[-_.[:alnum:]]+]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[WIDE_PTR:%[-_.[:alnum:]]+]], i32 0, i32 0 +// CHECK: [[PTR:%[-_.[:alnum:]]+]] = load ptr, ptr [[ADDR]], align 8 +// CHECK: [[PTR_INDEXED:%[-_.[:alnum:]]+]] = getelementptr i8, ptr [[PTR]], i64 0 +// CHECK: [[UB_ADDR:%[-_.[:alnum:]]+]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[WIDE_PTR]], i32 0, i32 1 +// CHECK: [[UB:%[-_.[:alnum:]]+]] = load ptr, ptr [[UB_ADDR]], align 8 +// CHECK: [[LB_ADDR:%[-_.[:alnum:]]+]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[WIDE_PTR]], i32 0, i32 2 +// CHECK: [[LB:%[-_.[:alnum:]]+]] = load ptr, ptr [[LB_ADDR]], align 8 +// CHECK: [[CMP1:%[-_.[:alnum:]]+]] = icmp ult ptr [[PTR_INDEXED]], [[UB]] +// CHECK: br i1 [[CMP1]], label %[[CONT1:[-_.[:alnum:]]+]], label %[[TRAP1:[-_.[:alnum:]]+]] + +// CHECK: [[TRAP1]]: +// CHECK: call void @llvm.ubsantrap(i8 25) + +// CHECK: [[CONT1]]: +// CHECK: [[CMP2:%[-_.[:alnum:]]+]] = icmp uge ptr [[PTR_INDEXED]], [[LB]] +// CHECK: br i1 [[CMP2]], label %[[CONT2:[-_.[:alnum:]]+]], label %[[TRAP2:[-_.[:alnum:]]+]] + +// CHECK: [[TRAP2]]: +// CHECK: call void @llvm.ubsantrap(i8 25) + +int main(void) { + char *data = "ab"; + return consume(data); +} + +// CHECK-LABEL: main +// CHECK-NOT: ubsantrap + diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/bidi_to_indexable_ptr_lt_lower_bound-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/bidi_to_indexable_ptr_lt_lower_bound-O0.c new file mode 100644 index 0000000000000..1e1a13cde5af3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/bidi_to_indexable_ptr_lt_lower_bound-O0.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +int *__indexable convert(int *__bidi_indexable p) { + return p; +} + +// CHECK-LABEL: @convert( + +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp uge ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Converted __indexable pointer is below bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{.+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 7, column: 10, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/general_trap-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/general_trap-O0.c new file mode 100644 index 0000000000000..99fad1059506a --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/general_trap-O0.c @@ -0,0 +1,42 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm -triple arm64-apple-darwin %s -o %t.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t.ll %t.sep %t.ll > %t.repeated.ll +// RUN: FileCheck %s --input-file=%t.repeated.ll + +#include + +void consume(int *__counted_by(size) buf, int size); + +void use(int *__indexable buf) { + // FIXME(dliew): We should not be emitting a generic trap here. Instead + // we should be emitting much more specific trap reasons. + // rdar://100346924 + consume(buf, 2); +} + +// We effectively need to walk the IR backwards starting from the debug info +// and then locating the expected IR instructions. We don't have a good way +// of walking the file backwards other than make everything a CHECK-DAG. +// That would allow many different orderings rather than the one we want, which +// isn't ideal. Instead make the input to FileCheck be two copies of the IR so +// that we can first match the debug info and then the IR instructions. + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-generic"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: define void @use +// Note: Multiple branches use the `bounds-safety-generic` remark. They are +// different checks but they use the same opt-remark (rdar://100346924). Until +// that's fixed we need to explicitly match against them all so that we +// correctly determine the trap label. +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %{{.+}} !dbg [[SRC_LOC]], !annotation [[OPT_REMARK]] +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %{{.+}} !dbg [[SRC_LOC]], !annotation [[OPT_REMARK]] +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %{{.+}} !dbg [[SRC_LOC]], !annotation [[OPT_REMARK]] +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9.]+]], !dbg [[SRC_LOC]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/indexable_ptr_new_lt_old-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/indexable_ptr_new_lt_old-O0.c new file mode 100644 index 0000000000000..0e53a7e185b27 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/indexable_ptr_new_lt_old-O0.c @@ -0,0 +1,30 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t.ll %t.sep %t.ll > %t.repeated.ll +// RUN: FileCheck %s --input-file=%t.repeated.ll +#include + +int* shift(int* __indexable i, int offset) { + i += offset; + return i; +} + +// We effectively need to walk the IR backwards starting from the debug info +// and then locating the expected IR instructions. We don't have a good way +// of walking the file backwards other than make everything a CHECK-DAG. +// That would allow many different orderings rather than the one we want, which +// isn't ideal. Instead make the input to FileCheck be two copies of the IR so +// that we can first match the debug info and then the IR instructions. + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-check-new-indexable-ptr-ge-old"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$New lower bound less than old lower bound", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: @shift +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[SRC_LOC]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/merge_traps-O3.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/merge_traps-O3.c new file mode 100644 index 0000000000000..49685a03f8be5 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/merge_traps-O3.c @@ -0,0 +1,32 @@ + +// RUN: %clang_cc1 -O3 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefixes CHECK,CHECK-FAKE-FRAME %s + +int bad_read(int index) { + int array[] = {0, 1, 2}; + int array2[] = {1, 5, 8}; + // Placing these two accesses on different lines prevents their debug + // location from being being merged to have the same line. So they will + // end up as 0-line locations. + return array[index] + + array2[index]; +} + +// Merging of traps isn't necessarily ideal because it causes debug info to be lost +// and therefore the trap information stored there to be lost too. +// We should investigate preventing merging of traps (rdar://85946510). +// For now just check that when traps get merged we drop the -fbounds-safety trap reason +// to avoid confusing users. + +// Check that only one trap is emitted when optimized. +// CHECK--LABEL: @bad_read +// CHECK: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] +// CHECK-NOT: call void @llvm.ubsantrap(i8 25) + +// Check the merged trap as a 0-line location and scoped within the correct function +// !26 = !DILocation(line: 0, scope: !14) +// !14 = distinct !DISubprogram(name: "bad_read", scope: !15, file: +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "bad_read" + +// Make sure fake -fbounds-safety traps are not emitted. +// CHECK-FAKE-FRAME-NOT: {{![0-9]+}} = distinct !DISubprogram(name: "Bounds check failed: diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-addr_of_struct_member-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-addr_of_struct_member-O0.c new file mode 100644 index 0000000000000..fab6e75649132 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-addr_of_struct_member-O0.c @@ -0,0 +1,40 @@ + +// RUN: %clang_cc1 -DTYPE=__bidi_indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t_bidi.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t_bidi.ll %t.sep %t_bidi.ll > %t_bidi.repeated.ll +// RUN: FileCheck %s --input-file=%t_bidi.repeated.ll + +// RUN: %clang_cc1 -DTYPE=__indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t_idx.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t_idx.ll %t.sep %t_idx.ll > %t_idx.repeated.ll +// RUN: FileCheck %s --input-file=%t_idx.repeated.ll + +#include + +#ifndef TYPE +#error TYPE must be defined +#endif + +typedef struct Data { + int a; + int b; +} Data_t; + +int* TYPE operation(Data_t* TYPE d) { + return &d->a; +} + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Pointer to struct above bounds while taking address of struct member", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-DAG: [[SRC_LOC]] = !DILocation(line: 24, column: 12, scope: {{![0-9]+}}) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: @operation +// Note: We use the `OPT_REMARK` to make sure we match against the correct branch. +// Unfortunately `SRC_LOC` is not enough because it isn't unique. +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[SRC_LOC]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-cast-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-cast-O0.c new file mode 100644 index 0000000000000..af2be0ad09712 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-cast-O0.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -DTYPE=__bidi_indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -DTYPE=__indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +int* __single operation(int* TYPE i) { + return i; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp ult ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Pointer above bounds while casting", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{.+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 7, column: 12, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c new file mode 100644 index 0000000000000..9088cb3a42c54 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-array_subscript-O0.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +int operation(int index) { + int array[] = {0, 1, 2}; + return array[index]; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp ult ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK-NEXT: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 6, column: 10, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-member_access-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-member_access-O0.c new file mode 100644 index 0000000000000..836758b2ee021 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-member_access-O0.c @@ -0,0 +1,29 @@ + +// RUN: %clang_cc1 -DTYPE=__bidi_indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -DTYPE=__indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +#ifndef TYPE +#error TYPE must be defined +#endif + +typedef struct Data { + int a; + int b; +} Data_t; + +int operation(Data_t* TYPE d) { + return d->a; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp ule ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK-NEXT: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 16, column: 15, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-unary-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-unary-O0.c new file mode 100644 index 0000000000000..f296f55a5aa34 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-deref-unary-O0.c @@ -0,0 +1,24 @@ + +// RUN: %clang_cc1 -DTYPE=__bidi_indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -DTYPE=__indexable -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +#ifndef TYPE +#error TYPE must be defined +#endif + +int operation(int* TYPE i) { + return *i; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp ult ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK-NEXT: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing above bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 11, column: 12, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-terminated_by_from_indexable-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-terminated_by_from_indexable-O0.c new file mode 100644 index 0000000000000..523c5d380f5bd --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_ge_upper_bound-terminated_by_from_indexable-O0.c @@ -0,0 +1,32 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t.ll %t.sep %t.ll > %t.repeated.ll +// RUN: FileCheck %s --input-file=%t.repeated.ll +#include + +void convert(const char* __indexable str) { + const char* __null_terminated convert = __unsafe_null_terminated_from_indexable(str); +} + +// We effectively need to walk the IR backwards starting from the debug info +// and then locating the expected IR instructions. We don't have a good way +// of walking the file backwards other than make everything a CHECK-DAG. +// That would allow many different orderings rather than the one we want, which +// isn't ideal. Instead make the input to FileCheck be two copies of the IR so +// that we can first match the debug info and then the IR instructions. + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Pointer above bounds while converting __indexable to __terminated_by", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: @convert +// Note: We use the `OPT_REMARK` to make sure we match against the correct branch. +// Unfortunately `SRC_LOC` is not enough because it isn't unique. +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[SRC_LOC]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] +// CHECK: [[SRC_LOC]] = !DILocation(line: 9, column: 45, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-addr_of_struct_member-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-addr_of_struct_member-O0.c new file mode 100644 index 0000000000000..9c72984c03ca1 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-addr_of_struct_member-O0.c @@ -0,0 +1,32 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t_bidi.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t_bidi.ll %t.sep %t_bidi.ll > %t_bidi.repeated.ll +// RUN: FileCheck -v %s --input-file=%t_bidi.repeated.ll +#include + +// Don't need to test `__indexable` because there is no lower bound check. + +typedef struct Data { + int a; + int b; +} Data_t; + +int* __bidi_indexable operation(Data_t* __bidi_indexable d) { + return &d->a; +} + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Pointer to struct below bounds while taking address of struct member", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-DAG: [[SRC_LOC]] = !DILocation(line: 16, column: 12, scope: {{![0-9]+}}) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: @operation +// Note: We use the `OPT_REMARK` to make sure we match against the correct branch. +// Unfortunately `SRC_LOC` is not enough because it isn't unique. +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[SRC_LOC]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-cast-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-cast-O0.c new file mode 100644 index 0000000000000..9dbbc21406143 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-cast-O0.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +// Don't need to test `__indexable` src because there is no lower bound check. + +int* __single operation(int* __bidi_indexable i) { + return i; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp uge ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Pointer below bounds while casting", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{.+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 8, column: 12, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-array_subscript-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-array_subscript-O0.c new file mode 100644 index 0000000000000..5659a9ed17bef --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-array_subscript-O0.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +int operation(int index) { + int array[] = {0, 1, 2}; + return array[index]; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp uge ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{.+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 6, column: 10, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-member_access-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-member_access-O0.c new file mode 100644 index 0000000000000..bd888e13a6ebe --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-member_access-O0.c @@ -0,0 +1,32 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t_bidi.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t_bidi.ll %t.sep %t_bidi.ll > %t_bidi.repeated.ll +// RUN: FileCheck %s --input-file=%t_bidi.repeated.ll +#include + +typedef struct Data { + int a; + int b; +} Data_t; + +// Don't need to test `__indexable` because there is no lower bound check + +int operation(Data_t* __bidi_indexable d) { + return d->a; +} + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-DAG: [[SRC_LOC]] = !DILocation(line: 16, column: 15, scope: {{![0-9]+}}) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: @operation +// Note: We use the `OPT_REMARK` to make sure we match against the correct branch. +// Unfortunately `SRC_LOC` is not enough because it isn't unique. +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[SRC_LOC]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-unary-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-unary-O0.c new file mode 100644 index 0000000000000..99af47a74c0a2 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/ptr_lt_lower_bound-deref-unary-O0.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +// Don't need to test `__indexable` because there is no lower bound check. + +int operation(int* __bidi_indexable i) { + return *i; +} + +// CHECK-LABEL: @operation +// We don't try to match the registers used in the comparison because trying +// to match the IR is very fragile. +// CHECK: [[BRANCH_REG:%[0-9]+]] = icmp uge ptr %{{.+}}, %{{.+}}, !dbg [[LOC:![0-9]+]] +// CHECK-NEXT: br i1 [[BRANCH_REG]], label {{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Dereferencing below bounds", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[LOC]] = !DILocation(line: 8, column: 12, scope: {{![0-9]+}}) diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_from_indexable_bounds-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_from_indexable_bounds-O0.c new file mode 100644 index 0000000000000..4d7d6ab7cbfb8 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_from_indexable_bounds-O0.c @@ -0,0 +1,49 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t.ll %t.sep %t.ll > %t.repeated.ll +// RUN: FileCheck %s --input-file=%t.repeated.ll +#include + +void convert(const char* __indexable str) { + const char* __null_terminated convert = __unsafe_null_terminated_from_indexable(str, /*terminator=*/ &str[1]); +} + +// We effectively need to walk the IR backwards starting from the debug info +// and then locating the expected IR instructions. We don't have a good way +// of walking the file backwards other than make everything a CHECK-DAG. +// That would allow many different orderings rather than the one we want, which +// isn't ideal. Instead make the input to FileCheck be two copies of the IR so +// that we can first match the debug info and then the IR instructions. + +// In first copy of the file + +// CHECK-DAG: [[OPT_REMARK_0:![0-9]+]] = !{!"bounds-safety-check-terminated-by-from-indexable-ptr-le-term-ptr"} +// CHECK-DAG: [[TRAP_SCOPE_0:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Terminator pointer below bounds", scope: [[FILE_SCOPE_0:![0-9]+]], file: [[FILE_SCOPE_0]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC_0:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE_0]], inlinedAt: [[SRC_LOC_0:![0-9]+]]) + +// CHECK-DAG: [[OPT_REMARK_1:![0-9]+]] = !{!"bounds-safety-check-terminated-by-from-indexable-term-ptr-no-overflow"} +// CHECK-DAG: [[TRAP_SCOPE_1:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Terminator pointer overflows address space", scope: [[FILE_SCOPE_1:![0-9]+]], file: [[FILE_SCOPE_1]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC_1:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE_1]], inlinedAt: [[SRC_LOC_1:![0-9]+]]) + +// CHECK-DAG: [[OPT_REMARK_2:![0-9]+]] = !{!"bounds-safety-check-terminated-by-from-indexable-term-ptr-plus-one-le-upper-bound"} +// CHECK-DAG: [[TRAP_SCOPE_2:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Terminator pointer above bounds", scope: [[FILE_SCOPE_2:![0-9]+]], file: [[FILE_SCOPE_2]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC_2:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE_2]], inlinedAt: [[SRC_LOC_2:![0-9]+]]) + +// CHECK-LABEL: ; __SEPERATOR__ +// In second copy of the file +// CHECK-LABEL: @convert +// Note: We use the `OPT_REMARK` to make sure we match against the correct branch. +// Unfortunately `SRC_LOC` is not enough because it isn't unique. + +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL_0:[a-z0-9]+]], !dbg [[SRC_LOC_0]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation [[OPT_REMARK_0]] +// CHECK: [[TRAP_LABEL_0]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC_0]] + +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL_1:[a-z0-9]+]], !dbg [[SRC_LOC_1]], !prof ![[PROFILE_METADATA]], !annotation [[OPT_REMARK_1]] +// CHECK: [[TRAP_LABEL_1]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC_1]] + +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL_2:[a-z0-9]+]], !dbg [[SRC_LOC_2]], !prof ![[PROFILE_METADATA]], !annotation [[OPT_REMARK_2]] +// CHECK: [[TRAP_LABEL_2]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC_2]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_from_indexable_term-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_from_indexable_term-O0.c new file mode 100644 index 0000000000000..53bccc55b1359 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_from_indexable_term-O0.c @@ -0,0 +1,31 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o %t.ll +// RUN: echo "; __SEPERATOR__" > %t.sep +// RUN: cat %t.ll %t.sep %t.ll > %t.repeated.ll +// RUN: FileCheck %s --input-file=%t.repeated.ll +#include + +void convert(const char* __indexable str) { + const char* __null_terminated convert = __unsafe_null_terminated_from_indexable(str, /*terminator=*/ &str[1]); +} + +// We effectively need to walk the IR backwards starting from the debug info +// and then locating the expected IR instructions. We don't have a good way +// of walking the file backwards other than make everything a CHECK-DAG. +// That would allow many different orderings rather than the one we want, which +// isn't ideal. Instead make the input to FileCheck be two copies of the IR so +// that we can first match the debug info and then the IR instructions. + +// In first copy of the file +// CHECK-DAG: [[OPT_REMARK:![0-9]+]] = !{!"bounds-safety-check-terminated-by-from-indexable-term"} +// CHECK-DAG: [[TRAP_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Cannot find the terminator when converting to __terminated_by pointer from an __indexable pointer", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} +// CHECK-DAG: [[TRAP_LOC:![0-9]+]] = !DILocation(line: 0, scope: [[TRAP_SCOPE]], inlinedAt: [[SRC_LOC:![0-9]+]]) +// CHECK-LABEL: ; __SEPERATOR__ + +// In second copy of the file +// CHECK-LABEL: @convert +// Note: We use the `OPT_REMARK` to make sure we match against the correct branch. +// Unfortunately `SRC_LOC` is not enough because it isn't unique. +// CHECK: br i1 %{{.+}}, label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[SRC_LOC]], !prof ![[PROFILE_METADATA:[0-9]+]], !annotation [[OPT_REMARK]] +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC]] diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_ptr_arith-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_ptr_arith-O0.c new file mode 100644 index 0000000000000..d1731cc8a35c0 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_ptr_arith-O0.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +void iterate(const char* __null_terminated str) { + while (*str != '\0') { + ++str; + } +} + +// CHECK-LABEL: @iterate + +// CHECK: %[[CMP_REG:[a-z0-9]+]] = icmp ne i8 %{{.+}}, 0, !dbg [[LOC:![0-9]+]] +// CHECK-NEXT: br i1 %[[CMP_REG]], label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] + +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$Arithmetic on __terminated_by pointer one-past-the-end of the terminator", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} diff --git a/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_term_assign-O0.c b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_term_assign-O0.c new file mode 100644 index 0000000000000..68d4af17ae27f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/trap-reasons/terminated_by_term_assign-O0.c @@ -0,0 +1,18 @@ + +// RUN: %clang_cc1 -O0 -debug-info-kind=standalone -dwarf-version=5 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +#include + +void assign(char* __null_terminated str) { + str[0] = 'x'; +} + +// CHECK-LABEL: @assign + +// CHECK: %[[CMP_REG:[a-z0-9]+]] = icmp ne i8 %{{.+}}, 0, !dbg [[LOC:![0-9]+]] +// CHECK-NEXT: br i1 %[[CMP_REG]], label %{{.+}}, label %[[TRAP_LABEL:[a-z0-9]+]], !dbg [[LOC]] + +// CHECK: [[TRAP_LABEL]]: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) {{.*}}!dbg [[TRAP_LOC:![0-9]+]] + +// CHECK-DAG: [[TRAP_LOC]] = !DILocation(line: 0, scope: [[TRAP_SCOPE:![0-9]+]], inlinedAt: [[LOC]]) +// CHECK-DAG: [[TRAP_SCOPE]] = distinct !DISubprogram(name: "__clang_trap_msg$Bounds check failed$The terminator cannot be assigned", scope: [[FILE_SCOPE:![0-9]+]], file: [[FILE_SCOPE:![0-9]+]], type: {{![0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: {{![0-9]+}} diff --git a/clang/test/BoundsSafety/CodeGen/ubsan/ptr-arithmetic-struct.c b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-arithmetic-struct.c new file mode 100644 index 0000000000000..ce9bd904d3e8f --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-arithmetic-struct.c @@ -0,0 +1,28 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -fsanitize=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O2 -fbounds-safety -fsanitize=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O0 -fbounds-safety -fsanitize=array-bounds -fsanitize-trap=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s +// RUN: %clang_cc1 -O2 -fbounds-safety -fsanitize=array-bounds -fsanitize-trap=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds -fsanitize-trap=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds -fsanitize-trap=array-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s + +struct foo { + unsigned bar; +}; + +struct foo arr[10]; + +int v; + +int main() { + struct foo *ptr = &arr[v + 1]; +// RECOVER: call void @__ubsan_handle_out_of_bounds + +// TRAP: br {{.*}} label %[[LABEL_TRAP:[a-z0-9]+]], !prof ![[PROFILE_METADATA:[0-9]+]], !nosanitize +// TRAP: [[LABEL_TRAP]]: +// TRAP-NEXT: call void @llvm.ubsantrap(i8 {{.*}}) #{{.*}}, !nosanitize + +} diff --git a/clang/test/BoundsSafety/CodeGen/ubsan/ptr-arithmetic.c b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-arithmetic.c new file mode 100644 index 0000000000000..df017b2de65ec --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-arithmetic.c @@ -0,0 +1,24 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -fsanitize=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O2 -fbounds-safety -fsanitize=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O0 -fbounds-safety -fsanitize=array-bounds,local-bounds -fsanitize-trap=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s +// RUN: %clang_cc1 -O2 -fbounds-safety -fsanitize=array-bounds,local-bounds -fsanitize-trap=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=RECOVER %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds,local-bounds -fsanitize-trap=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=array-bounds,local-bounds -fsanitize-trap=array-bounds,local-bounds -emit-llvm %s -o - | FileCheck --check-prefix=TRAP %s + +int arr[10]; + +int v; + +int main() { + int *ptr = &arr[v]; +// RECOVER: call void @__ubsan_handle_out_of_bounds + +// TRAP: br {{.*}} label %[[LABEL_TRAP:[a-z0-9]+]], !prof ![[PROFILE_METADATA:[0-9]+]], !nosanitize +// TRAP: [[LABEL_TRAP]]: +// TRAP-NEXT: call void @llvm.ubsantrap(i8 {{.*}}) #{{.*}}, !nosanitize + +} diff --git a/clang/test/BoundsSafety/CodeGen/ubsan/ptr-nullability.c b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-nullability.c new file mode 100644 index 0000000000000..06589e5cc2ebc --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-nullability.c @@ -0,0 +1,39 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -fsanitize=nullability-arg,nullability-assign,nullability-return -fsanitize-trap=nullability-arg,nullability-assign,nullability-return -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsanitize=nullability-arg,nullability-assign,nullability-return -fsanitize-trap=nullability-arg,nullability-assign,nullability-return -emit-llvm %s -o - | FileCheck %s + +#include + +int* bidi_to_bidi(int * _Nonnull __bidi_indexable ptr, int * __bidi_indexable ptr2) { + return ptr = ptr2; +} + +// CHECK: ptr @bidi_to_bidi +// ... +// CHECK: call void @llvm.ubsantrap(i8 22) + +int* raw_to_idx(int * _Nonnull __indexable ptr, int * ptr2) { + return ptr = ptr2; +} + +// CHECK: ptr @raw_to_idx +// ... +// CHECK: call void @llvm.ubsantrap(i8 22) + +int* bidi_to_idx(int * _Nonnull __indexable ptr, int * __bidi_indexable ptr2) { + return ptr = ptr2; +} + +// CHECK: ptr @bidi_to_idx +// ... +// CHECK: call void @llvm.ubsantrap(i8 22) + + +int* idx_to_bidi(int * _Nonnull __bidi_indexable ptr, int * __indexable ptr2) { + return ptr = ptr2; +} + +// CHECK: ptr @idx_to_bidi +// ... +// CHECK: call void @llvm.ubsantrap(i8 22) diff --git a/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-local-vars.c b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-local-vars.c new file mode 100644 index 0000000000000..a1d8981b44baa --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-local-vars.c @@ -0,0 +1,13 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-int-conversion -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-int-conversion -x objective-c -fbounds-attributes-objc-experimental -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s + +#include + +int local_vars() { + int * __bidi_indexable ptr = 0; + return ptr += 1; +} + +// CHECK: call void @llvm.ubsantrap(i8 19) diff --git a/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-params.c b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-params.c new file mode 100644 index 0000000000000..13a6dc33cfb49 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/ubsan/ptr-overflow-params.c @@ -0,0 +1,14 @@ + + +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-int-conversion -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -Wno-int-conversion -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -Wno-int-conversion -x objective-c -fbounds-attributes-objc-experimental -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -Wno-int-conversion -x objective-c -fbounds-attributes-objc-experimental -fsanitize=pointer-overflow -fsanitize-trap=pointer-overflow -emit-llvm %s -o - | FileCheck %s + +#include + +int fixed_len_array(int * __bidi_indexable ptr, int k) { + return ptr += k; +} + +// CHECK: call void @llvm.ubsantrap(i8 19) diff --git a/clang/test/BoundsSafety/CodeGen/union-array-member-subscript-tbaa-trivial-O2.c b/clang/test/BoundsSafety/CodeGen/union-array-member-subscript-tbaa-trivial-O2.c new file mode 100644 index 0000000000000..8a729f4620345 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/union-array-member-subscript-tbaa-trivial-O2.c @@ -0,0 +1,36 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -triple arm64-apple-ios -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s + +#include + +struct pf_addr { + union { + short _addr16[8]; + int _addr32[4]; + } pfa; +}; + +static short foo(struct pf_addr *a, struct pf_addr *b, int *p) { + b->pfa._addr32[0] = a->pfa._addr32[0]; + + *p = 0x10000010; + short ret = b->pfa._addr16[0] + b->pfa._addr16[1]; // 0x1000 + 0x0010 = 4112 + b->pfa._addr16[0] = 3; + return ret; +} + +// CHECK-LABEL: i16 @bar( +// CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: ret i16 4112 +// +short bar(void) { + struct pf_addr a = {1, 0, 5, 0}; + struct pf_addr b = {3, 0, 7, 0}; + int *p = &b.pfa._addr32[0]; + + return foo(&a, &b, p); +} diff --git a/clang/test/BoundsSafety/CodeGen/union-array-member-subscript-tbaa.c b/clang/test/BoundsSafety/CodeGen/union-array-member-subscript-tbaa.c new file mode 100644 index 0000000000000..d597cc0132783 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/union-array-member-subscript-tbaa.c @@ -0,0 +1,26 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 + +// RUN: %clang_cc1 -fbounds-safety -Oz -disable-llvm-passes -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -Oz -disable-llvm-passes -triple arm64-apple-ios -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -Oz -disable-llvm-passes -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s + +#include + +struct pf_addr { + union { + short _addr16[8]; + int _addr32[4]; + } pfa; +}; + +void foo(struct pf_addr *up) { + up->pfa._addr32[0] = 5; + *up->pfa._addr32 = 4; +} + +// CHECK: store i32 5, ptr [[TMP1:%.*]], align 4, !tbaa ![[TBAA_9:[0-9]+]] +// CHECK: store i32 4, ptr [[WIDE_PTR_PTR:%.*]], align 4, !tbaa ![[TBAA_10:[0-9]+]] +// CHECK-DAG: ![[TBAA_4:[0-9]+]] = !{!"omnipotent char", !{{[0-9]+}}, i64 0} +// CHECK-DAG: ![[TBAA_9]] = !{![[TBAA_4]], ![[TBAA_4]], i64 0} +// CHECK-DAG: ![[TBAA_10]] = !{![[TBAA_11:[0-9]+]], ![[TBAA_11]], i64 0} +// CHECK-DAG: ![[TBAA_11]] = !{!"int", !{{[0-9]+}}, i64 0} diff --git a/clang/test/BoundsSafety/CodeGen/unique-trap-blocks-O0-O2-opt-disabled.c b/clang/test/BoundsSafety/CodeGen/unique-trap-blocks-O0-O2-opt-disabled.c new file mode 100644 index 0000000000000..6f452255250c8 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/unique-trap-blocks-O0-O2-opt-disabled.c @@ -0,0 +1,105 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --include-generated-funcs --version 5 + +// RUN: %clang_cc1 -O0 -triple arm64e-apple-ios -fbounds-safety \ +// RUN: -funique-traps -emit-llvm %s -o - \ +// RUN: | FileCheck -check-prefix=OPT0 %s +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety \ +// RUN: -funique-traps -emit-llvm %s -disable-llvm-passes -o - \ +// RUN: | FileCheck -check-prefix=OPT2 %s + +#include + +int consume(int* __bidi_indexable ptr, int idx) { + return ptr[idx]; +} +// OPT0-LABEL: define i32 @consume( +// OPT0-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0:[0-9]+]] { +// OPT0-NEXT: [[ENTRY:.*:]] +// OPT0-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// OPT0-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// OPT0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OPT0-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8 +// OPT0-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4 +// OPT0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false) +// OPT0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPT0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// OPT0-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4 +// OPT0-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// OPT0-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// OPT0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPT0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// OPT0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPT0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// OPT0-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], !annotation [[META2:![0-9]+]] +// OPT0-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF3:![0-9]+]], !annotation [[META2]] +// OPT0: [[TRAP]]: +// OPT0-NEXT: call void asm sideeffect "", "n"(i64 0), !annotation [[META2]] +// OPT0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META2]] +// OPT0-NEXT: unreachable, !annotation [[META2]] +// OPT0: [[CONT]]: +// OPT0-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META4:![0-9]+]] +// OPT0-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF3]], !annotation [[META4]] +// OPT0: [[TRAP1]]: +// OPT0-NEXT: call void asm sideeffect "", "n"(i64 1), !annotation [[META4]] +// OPT0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META4]] +// OPT0-NEXT: unreachable, !annotation [[META4]] +// OPT0: [[CONT2]]: +// OPT0-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 +// OPT0-NEXT: ret i32 [[TMP3]] +// +// +// OPT2-LABEL: define i32 @consume( +// OPT2-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[IDX:%.*]]) #[[ATTR0:[0-9]+]] { +// OPT2-NEXT: [[ENTRY:.*:]] +// OPT2-NEXT: [[PTR_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// OPT2-NEXT: [[IDX_ADDR:%.*]] = alloca i32, align 4 +// OPT2-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// OPT2-NEXT: store ptr [[PTR]], ptr [[PTR_INDIRECT_ADDR]], align 8, !tbaa [[TBAA2:![0-9]+]] +// OPT2-NEXT: store i32 [[IDX]], ptr [[IDX_ADDR]], align 4, !tbaa [[TBAA8:![0-9]+]] +// OPT2-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR]], i64 24, i1 false), !tbaa.struct [[TBAA_STRUCT10:![0-9]+]] +// OPT2-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// OPT2-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// OPT2-NEXT: [[TMP0:%.*]] = load i32, ptr [[IDX_ADDR]], align 4, !tbaa [[TBAA8]] +// OPT2-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// OPT2-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[IDXPROM]] +// OPT2-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// OPT2-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// OPT2-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// OPT2-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// OPT2-NEXT: [[TMP1:%.*]] = icmp ult ptr [[ARRAYIDX]], [[WIDE_PTR_UB]], !annotation [[META13:![0-9]+]] +// OPT2-NEXT: br i1 [[TMP1]], label %[[CONT:.*]], label %[[TRAP:.*]], !prof [[PROF14:![0-9]+]], !annotation [[META13]] +// OPT2: [[TRAP]]: +// OPT2-NEXT: call void asm sideeffect "", "n"(i64 0), !annotation [[META13]] +// OPT2-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], !annotation [[META13]] +// OPT2-NEXT: unreachable, !annotation [[META13]] +// OPT2: [[CONT]]: +// OPT2-NEXT: [[TMP2:%.*]] = icmp uge ptr [[ARRAYIDX]], [[WIDE_PTR_LB]], !annotation [[META15:![0-9]+]] +// OPT2-NEXT: br i1 [[TMP2]], label %[[CONT2:.*]], label %[[TRAP1:.*]], !prof [[PROF14]], !annotation [[META15]] +// OPT2: [[TRAP1]]: +// OPT2-NEXT: call void asm sideeffect "", "n"(i64 1), !annotation [[META15]] +// OPT2-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], !annotation [[META15]] +// OPT2-NEXT: unreachable, !annotation [[META15]] +// OPT2: [[CONT2]]: +// OPT2-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARRAYIDX]], align 4, !tbaa [[TBAA8]] +// OPT2-NEXT: ret i32 [[TMP3]] +// +//. +// OPT0: [[META2]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// OPT0: [[PROF3]] = !{!"branch_weights", i32 1048575, i32 1} +// OPT0: [[META4]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. +// OPT2: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// OPT2: [[META3]] = !{!"p2 int", [[META4:![0-9]+]], i64 0} +// OPT2: [[META4]] = !{!"any p2 pointer", [[META5:![0-9]+]], i64 0} +// OPT2: [[META5]] = !{!"any pointer", [[META6:![0-9]+]], i64 0} +// OPT2: [[META6]] = !{!"omnipotent char", [[META7:![0-9]+]], i64 0} +// OPT2: [[META7]] = !{!"Simple C/C++ TBAA"} +// OPT2: [[TBAA8]] = !{[[META9:![0-9]+]], [[META9]], i64 0} +// OPT2: [[META9]] = !{!"int", [[META6]], i64 0} +// OPT2: [[TBAA_STRUCT10]] = !{i64 0, i64 24, [[META11:![0-9]+]]} +// OPT2: [[META11]] = !{[[META12:![0-9]+]], [[META12]], i64 0} +// OPT2: [[META12]] = !{!"p1 int", [[META5]], i64 0} +// OPT2: [[META13]] = !{!"bounds-safety-check-ptr-lt-upper-bound"} +// OPT2: [[PROF14]] = !{!"branch_weights", i32 1048575, i32 1} +// OPT2: [[META15]] = !{!"bounds-safety-check-ptr-ge-lower-bound"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/unique-trap-blocks-O2.c b/clang/test/BoundsSafety/CodeGen/unique-trap-blocks-O2.c new file mode 100644 index 0000000000000..134094fed2836 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/unique-trap-blocks-O2.c @@ -0,0 +1,29 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --include-generated-funcs --version 5 + + +// Make sure that with or without `-fsplit-cold-code` that the trap blocks don't +// get outlined into their own function. Outlining trap blocks is unhelpful +// because it makes debugging harder which is the opposite of what +// `-funique-trap` is tries to do. + +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety \ +// RUN: -funique-traps -emit-llvm %s -o - -fno-split-cold-code \ +// RUN: | FileCheck %s +// RUN: %clang_cc1 -O2 -triple arm64e-apple-ios -fbounds-safety \ +// RUN: -funique-traps -emit-llvm %s -o - -fsplit-cold-code \ +// RUN: | FileCheck %s +#include + +__attribute__((always_inline)) int i_want_to_be_inlined( + int* __bidi_indexable ptr, int idx) { + return ptr[idx]; +} + +// Note: The trap counter is global to the module so even in the presence of +// inlining the counter will be unique. +int consume(int* __bidi_indexable ptr, int* __bidi_indexable ptr2, int idx) { + int other = i_want_to_be_inlined(ptr2, idx); + return ptr[idx]; +} +//// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +// CHECK: {{.*}} diff --git a/clang/test/BoundsSafety/CodeGen/unsafe_forge.c b/clang/test/BoundsSafety/CodeGen/unsafe_forge.c new file mode 100644 index 0000000000000..3b5dad1fc3e83 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/unsafe_forge.c @@ -0,0 +1,151 @@ +// REQUIRES: system-darwin + +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +typedef struct { + char str[10]; + int i; +} S; + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store i32 10, ptr [[A]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = inttoptr i32 [[TMP0]] to ptr +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr i8, ptr [[TMP1]], i64 16 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP8]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR4]], i64 16 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP9]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR4]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR6]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB8]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB10]], ptr [[TMP15]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr [[STRUCT_S:%.*]], ptr [[WIDE_PTR_PTR13]], i64 1 +// CHECK-NEXT: [[TMP17:%.*]] = icmp ule ptr [[TMP16]], [[WIDE_PTR_UB15]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP17]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP18:%.*]] = icmp ule ptr [[WIDE_PTR_LB17]], [[WIDE_PTR_PTR13]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP18]], label [[CONT19:%.*]], label [[TRAP18:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap18: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont19: +// CHECK-NEXT: [[I:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[WIDE_PTR_PTR13]], i32 0, i32 1 +// CHECK-NEXT: [[TMP19:%.*]] = load i32, ptr [[I]], align 4 +// CHECK-NEXT: ret i32 [[TMP19]] +// +int foo(void) { + int a = 10; + S *ptr = __unsafe_forge_bidi_indexable(S *, a, sizeof(S)); + ptr = __unsafe_forge_bidi_indexable(S *, ptr, sizeof(S)); + return ptr->i; +} + + + + + + + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store i32 10, ptr [[A]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = inttoptr i32 [[TMP0]] to ptr +// CHECK-NEXT: store ptr [[TMP1]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[I:%.*]] = getelementptr inbounds nuw [[STRUCT_S:%.*]], ptr [[TMP3]], i32 0, i32 1 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[I]], align 4 +// CHECK-NEXT: ret i32 [[TMP4]] +// +int bar(void) { + int a = 10; + S *__single ptr = __unsafe_forge_single(S *, a); + ptr = __unsafe_forge_single(S *, ptr); + return ptr->i; +} + +// CHECK-LABEL: @baz( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[PTR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store i32 10, ptr [[A]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[A]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = inttoptr i32 [[TMP0]] to ptr +// CHECK-NEXT: store ptr [[TMP1]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i8, ptr [[TMP2]], align 1 +// CHECK-NEXT: [[TMP4:%.*]] = icmp ne i8 [[TMP3]], 65, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[PTR]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load i8, ptr [[TMP5]], align 1 +// CHECK-NEXT: ret i8 [[TMP6]] +// +char baz(void) { + int a = 10; + char *__terminated_by('A') ptr = __unsafe_forge_terminated_by(char *, a, 'A'); + ptr = __unsafe_forge_terminated_by(char *, ptr+1, 'A'); + return *ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/unsafe_forge_constant_base-O2.c b/clang/test/BoundsSafety/CodeGen/unsafe_forge_constant_base-O2.c new file mode 100644 index 0000000000000..4e7d440041656 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/unsafe_forge_constant_base-O2.c @@ -0,0 +1,16 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -O2 -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 +#include + +void f1(void *__sized_by(size) ptr, unsigned size); + +// X86_64-LABEL: @f2( +// X86_64-NEXT: entry: +// X86_64-NEXT: tail call void @f1(ptr noundef nonnull inttoptr (i32 4096 to ptr), i32 noundef 10) #[[ATTR2:[0-9]+]] +// X86_64-NEXT: ret void +// +void f2(void) { + f1(__unsafe_forge_bidi_indexable(void *, 0x1000, 10), 10); +} diff --git a/clang/test/BoundsSafety/CodeGen/unsafe_forge_constant_base.c b/clang/test/BoundsSafety/CodeGen/unsafe_forge_constant_base.c new file mode 100644 index 0000000000000..4b501794b4e58 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/unsafe_forge_constant_base.c @@ -0,0 +1,164 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-globals all --prefix-filecheck-ir-name TMP_ --version 3 + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=X86_64 +#include + +void f1(void *__sized_by(size) ptr, unsigned size); + +//. +// X86_64: @static_single.p_compile_time_constant_single = internal constant ptr inttoptr (i64 123400004321 to ptr), align 8 +// X86_64: @static_bidi.p_compile_time_constant_bidi = internal constant %"__bounds_safety::wide_ptr.bidi_indexable.1" { ptr inttoptr (i64 12300321 to ptr), ptr inttoptr (i64 12300333 to ptr), ptr inttoptr (i64 12300321 to ptr) }, align 8 +//. +// X86_64-LABEL: define dso_local void @f2( +// X86_64-SAME: ) #[[ATTR0:[0-9]+]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// X86_64-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// X86_64-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// X86_64-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// X86_64-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// X86_64-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// X86_64-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// X86_64-NEXT: store ptr inttoptr (i32 4096 to ptr), ptr [[TMP0]], align 8 +// X86_64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// X86_64-NEXT: store ptr getelementptr (i8, ptr inttoptr (i32 4096 to ptr), i64 10), ptr [[TMP1]], align 8 +// X86_64-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// X86_64-NEXT: store ptr inttoptr (i32 4096 to ptr), ptr [[TMP2]], align 8 +// X86_64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2:![0-9]+]] +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP3]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP4]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP5]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP6]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, !annotation [[META2]] +// X86_64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_PTR14]], ptr [[TMP7]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_UB16]], ptr [[TMP8]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// X86_64-NEXT: store ptr [[WIDE_PTR_LB18]], ptr [[TMP9]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 0, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 1, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP11]], i32 0, i32 2, !annotation [[META2]] +// X86_64-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8, !annotation [[META2]] +// X86_64-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR6]] to i64, !annotation [[META2]] +// X86_64-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64, !annotation [[META2]] +// X86_64-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// X86_64-NEXT: [[CMP:%.*]] = icmp ule i64 10, [[SUB_PTR_SUB]], !annotation [[META2]] +// X86_64-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2]] +// X86_64: trap: +// X86_64-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// X86_64-NEXT: unreachable, !annotation [[META2]] +// X86_64: cont: +// X86_64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP25]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 0 +// X86_64-NEXT: [[WIDE_PTR_PTR27:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR26]], align 8 +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 1 +// X86_64-NEXT: [[WIDE_PTR_UB29:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR28]], align 8 +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP25]], i32 0, i32 2 +// X86_64-NEXT: [[WIDE_PTR_LB31:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR30]], align 8 +// X86_64-NEXT: call void @f1(ptr noundef [[WIDE_PTR_PTR27]], i32 noundef 10) +// X86_64-NEXT: ret void +// +void f2(void) { + f1(__unsafe_forge_bidi_indexable(void *, 0x1000, 10), 10); +} + +// X86_64-LABEL: define dso_local ptr @static_single( +// X86_64-SAME: ) #[[ATTR0]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: ret ptr inttoptr (i64 123400004321 to ptr) +// +unsigned int * __single static_single() { + static unsigned int * __single const p_compile_time_constant_single = __unsafe_forge_single(unsigned int*, 123400004321); + return p_compile_time_constant_single; +} +// X86_64-LABEL: define dso_local void @static_bidi( +// X86_64-SAME: ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable.1") align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT]], ptr align 8 @static_bidi.p_compile_time_constant_bidi, i64 24, i1 false) +// X86_64-NEXT: ret void +// +unsigned int * __bidi_indexable static_bidi() { + static unsigned int * __bidi_indexable const p_compile_time_constant_bidi = __unsafe_forge_bidi_indexable(unsigned int*, 12300321, 12); + return p_compile_time_constant_bidi; +} + +// X86_64-LABEL: define dso_local ptr @nonstatic_single( +// X86_64-SAME: ) #[[ATTR0]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: [[P:%.*]] = alloca ptr, align 8 +// X86_64-NEXT: store ptr inttoptr (i64 123400004321 to ptr), ptr [[P]], align 8 +// X86_64-NEXT: ret ptr inttoptr (i64 123400004321 to ptr) +// +unsigned int * __single nonstatic_single() { + unsigned int * __single const p = __unsafe_forge_single(unsigned int*, 123400004321); + return p; +} +// X86_64-LABEL: define dso_local void @nonstatic_bidi( +// X86_64-SAME: ptr dead_on_unwind noalias writable sret(%"__bounds_safety::wide_ptr.bidi_indexable.1") align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// X86_64-NEXT: entry: +// X86_64-NEXT: [[P:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// X86_64-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// X86_64-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// X86_64-NEXT: store ptr inttoptr (i32 12300321 to ptr), ptr [[TMP0]], align 8 +// X86_64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// X86_64-NEXT: store ptr getelementptr (i8, ptr inttoptr (i32 12300321 to ptr), i64 12), ptr [[TMP1]], align 8 +// X86_64-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// X86_64-NEXT: store ptr inttoptr (i32 12300321 to ptr), ptr [[TMP2]], align 8 +// X86_64-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// X86_64-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// X86_64-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// X86_64-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// X86_64-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// X86_64-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// X86_64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P]], i32 0, i32 0 +// X86_64-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// X86_64-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P]], i32 0, i32 1 +// X86_64-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// X86_64-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[P]], i32 0, i32 2 +// X86_64-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// X86_64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_RESULT]], ptr align 8 [[P]], i64 24, i1 false) +// X86_64-NEXT: ret void +// +unsigned int * __bidi_indexable nonstatic_bidi() { + unsigned int * __bidi_indexable const p = __unsafe_forge_bidi_indexable(unsigned int*, 12300321, 12); + return p; +} +//. +// X86_64: attributes #[[ATTR0]] = { noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// X86_64: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +// X86_64: attributes #[[ATTR2:[0-9]+]] = { cold noreturn nounwind } +// X86_64: attributes #[[ATTR3:[0-9]+]] = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" } +// X86_64: attributes #[[ATTR4]] = { nomerge noreturn nounwind } +//. +// X86_64: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4} +// X86_64: [[META1:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"} +// X86_64: [[META2]] = !{!"bounds-safety-generic"} +//. diff --git a/clang/test/BoundsSafety/CodeGen/vla-array-subscript-diff-O2.c b/clang/test/BoundsSafety/CodeGen/vla-array-subscript-diff-O2.c new file mode 100644 index 0000000000000..06223b0f41e42 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/vla-array-subscript-diff-O2.c @@ -0,0 +1,70 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// Showing the results are the same with or without -fbounds-safety +// RUN: %clang_cc1 -O2 -triple x86_64 -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -x objective-c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @diff_2d( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i64 13 +// +unsigned long long diff_2d(void) { + int n = 10, m = 13; + int vla[n][m]; + return vla[1] - vla[0]; +} + +// CHECK-LABEL: @diff_3d( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i64 13 +// +unsigned long long diff_3d(void) { + int n = 10, m = 13; + int vla[n][m][15]; + return vla[1] - vla[0]; +} + +// CHECK-LABEL: @diff_3d_2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i64 15 +// +unsigned long long diff_3d_2(void) { + int n = 10, m = 13, l = 15; + int vla[n][m][l]; + return vla[0][1] - vla[0][0]; +} + +// CHECK-LABEL: @diff_2d_cast( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i64 13 +// +unsigned long long diff_2d_cast(void) { + int n = 10, m = 13; + int vla[n][m]; + return (int*)vla[1] - (int*)vla[0]; +} + +// CHECK-LABEL: @diff_3d_cast( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i64 195 +// +unsigned long long diff_3d_cast(void) { + int n = 10, m = 13, l = 15; + int vla[n][m][l]; + return (int*)vla[1] - (int*)vla[0]; +} + +// CHECK-LABEL: @diff_3d_cast_2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i64 15 +// +unsigned long long diff_3d_cast_2(void) { + int n = 10, m = 13, l = 15; + int vla[n][m][l]; + return (int*)vla[0][1] - (int*)vla[0][0]; +} diff --git a/clang/test/BoundsSafety/CodeGen/vla-array-subscript-diff.c b/clang/test/BoundsSafety/CodeGen/vla-array-subscript-diff.c new file mode 100644 index 0000000000000..7ed617c636c1d --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/vla-array-subscript-diff.c @@ -0,0 +1,305 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + + +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O0 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s + +#include + +// CHECK-O0-LABEL: @diff_2d( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[M_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[N_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[SAVED_STACK:%.*]] = alloca ptr, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR0:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR1:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-O0-NEXT: store i32 [[M:%.*]], ptr [[M_ADDR]], align 4 +// CHECK-O0-NEXT: store i32 [[N:%.*]], ptr [[N_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[M_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[N_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP3:%.*]] = zext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[TMP4:%.*]] = call ptr @llvm.stacksave.p0() +// CHECK-O0-NEXT: store ptr [[TMP4]], ptr [[SAVED_STACK]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[VLA:%.*]] = alloca i32, i64 [[TMP5]], align 16 +// CHECK-O0-NEXT: store i64 [[TMP1]], ptr [[__VLA_EXPR0]], align 8 +// CHECK-O0-NEXT: store i64 [[TMP3]], ptr [[__VLA_EXPR1]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[VLA]], i64 [[TMP6]] +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP7]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = mul i64 1, [[TMP3]] +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[TMP10]] +// CHECK-O0-NEXT: [[UPPER2:%.*]] = getelementptr inbounds i32, ptr [[TMP11]], i64 [[TMP3]] +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER2]], ptr [[TMP13]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP11]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP15:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[UPPER7:%.*]] = getelementptr inbounds i32, ptr [[VLA]], i64 [[TMP15]] +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP16]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER7]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP18]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// CHECK-O0-NEXT: [[TMP19:%.*]] = mul i64 0, [[TMP3]] +// CHECK-O0-NEXT: [[TMP20:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR9]], i64 [[TMP19]] +// CHECK-O0-NEXT: [[UPPER10:%.*]] = getelementptr inbounds i32, ptr [[TMP20]], i64 [[TMP3]] +// CHECK-O0-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP20]], ptr [[TMP21]], align 8 +// CHECK-O0-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER10]], ptr [[TMP22]], align 8 +// CHECK-O0-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP20]], ptr [[TMP23]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR4]] to i64 +// CHECK-O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR12]] to i64 +// CHECK-O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// CHECK-O0-NEXT: [[TMP24:%.*]] = load ptr, ptr [[SAVED_STACK]], align 8 +// CHECK-O0-NEXT: call void @llvm.stackrestore.p0(ptr [[TMP24]]) +// CHECK-O0-NEXT: ret i64 [[SUB_PTR_DIV]] +// +unsigned long long diff_2d(int m, int n) { + int vla[m][n]; + return vla[1] - vla[0]; +} + +// CHECK-O0-LABEL: @diff_3d( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[M_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[N_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[L_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[SAVED_STACK:%.*]] = alloca ptr, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR0:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR1:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP5:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP6:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.2", align 8 +// CHECK-O0-NEXT: store i32 [[M:%.*]], ptr [[M_ADDR]], align 4 +// CHECK-O0-NEXT: store i32 [[N:%.*]], ptr [[N_ADDR]], align 4 +// CHECK-O0-NEXT: store i32 [[L:%.*]], ptr [[L_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[M_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[N_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP3:%.*]] = zext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[TMP4:%.*]] = call ptr @llvm.stacksave.p0() +// CHECK-O0-NEXT: store ptr [[TMP4]], ptr [[SAVED_STACK]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[VLA:%.*]] = alloca [15 x i32], i64 [[TMP5]], align 16 +// CHECK-O0-NEXT: store i64 [[TMP1]], ptr [[__VLA_EXPR0]], align 8 +// CHECK-O0-NEXT: store i64 [[TMP3]], ptr [[__VLA_EXPR1]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds [15 x i32], ptr [[VLA]], i64 [[TMP6]] +// CHECK-O0-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP7]], align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP8]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP9]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = mul i64 1, [[TMP3]] +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr [15 x i32], ptr [[WIDE_PTR_PTR]], i64 [[TMP10]] +// CHECK-O0-NEXT: [[UPPER2:%.*]] = getelementptr inbounds [15 x i32], ptr [[TMP11]], i64 [[TMP3]] +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER2]], ptr [[TMP13]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP11]], ptr [[TMP14]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP15:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[UPPER7:%.*]] = getelementptr inbounds [15 x i32], ptr [[VLA]], i64 [[TMP15]] +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP16]], align 8 +// CHECK-O0-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP6]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER7]], ptr [[TMP17]], align 8 +// CHECK-O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP6]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP18]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.2", ptr [[AGG_TEMP6]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR9:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR8]], align 8 +// CHECK-O0-NEXT: [[TMP19:%.*]] = mul i64 0, [[TMP3]] +// CHECK-O0-NEXT: [[TMP20:%.*]] = getelementptr [15 x i32], ptr [[WIDE_PTR_PTR9]], i64 [[TMP19]] +// CHECK-O0-NEXT: [[UPPER10:%.*]] = getelementptr inbounds [15 x i32], ptr [[TMP20]], i64 [[TMP3]] +// CHECK-O0-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP20]], ptr [[TMP21]], align 8 +// CHECK-O0-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER10]], ptr [[TMP22]], align 8 +// CHECK-O0-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP20]], ptr [[TMP23]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR12:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR11]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB14:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR13]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP5]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB16:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR15]], align 8 +// CHECK-O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR4]] to i64 +// CHECK-O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR12]] to i64 +// CHECK-O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 60 +// CHECK-O0-NEXT: [[TMP24:%.*]] = load ptr, ptr [[SAVED_STACK]], align 8 +// CHECK-O0-NEXT: call void @llvm.stackrestore.p0(ptr [[TMP24]]) +// CHECK-O0-NEXT: ret i64 [[SUB_PTR_DIV]] +// +unsigned long long diff_3d(int m, int n, int l) { + int vla[m][n][15]; + return vla[1] - vla[0]; +} + + +// CHECK-O0-LABEL: @diff_3d_2( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[M_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[N_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[L_ADDR:%.*]] = alloca i32, align 4 +// CHECK-O0-NEXT: [[SAVED_STACK:%.*]] = alloca ptr, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR0:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR1:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[__VLA_EXPR2:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.3", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.4", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.3", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.4", align 8 +// CHECK-O0-NEXT: store i32 [[M:%.*]], ptr [[M_ADDR]], align 4 +// CHECK-O0-NEXT: store i32 [[N:%.*]], ptr [[N_ADDR]], align 4 +// CHECK-O0-NEXT: store i32 [[L:%.*]], ptr [[L_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i32, ptr [[M_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load i32, ptr [[N_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP3:%.*]] = zext i32 [[TMP2]] to i64 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load i32, ptr [[L_ADDR]], align 4 +// CHECK-O0-NEXT: [[TMP5:%.*]] = zext i32 [[TMP4]] to i64 +// CHECK-O0-NEXT: [[TMP6:%.*]] = call ptr @llvm.stacksave.p0() +// CHECK-O0-NEXT: store ptr [[TMP6]], ptr [[SAVED_STACK]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[TMP8:%.*]] = mul nuw i64 [[TMP7]], [[TMP5]] +// CHECK-O0-NEXT: [[VLA:%.*]] = alloca i32, i64 [[TMP8]], align 16 +// CHECK-O0-NEXT: store i64 [[TMP1]], ptr [[__VLA_EXPR0]], align 8 +// CHECK-O0-NEXT: store i64 [[TMP3]], ptr [[__VLA_EXPR1]], align 8 +// CHECK-O0-NEXT: store i64 [[TMP5]], ptr [[__VLA_EXPR2]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[TMP10:%.*]] = mul nuw i64 [[TMP9]], [[TMP5]] +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[VLA]], i64 [[TMP10]] +// CHECK-O0-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP11]], align 8 +// CHECK-O0-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP12]], align 8 +// CHECK-O0-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP13]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP14:%.*]] = mul nuw i64 [[TMP3]], [[TMP5]] +// CHECK-O0-NEXT: [[TMP15:%.*]] = mul i64 0, [[TMP14]] +// CHECK-O0-NEXT: [[TMP16:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 [[TMP15]] +// CHECK-O0-NEXT: [[TMP17:%.*]] = mul nuw i64 [[TMP3]], [[TMP5]] +// CHECK-O0-NEXT: [[UPPER3:%.*]] = getelementptr inbounds i32, ptr [[TMP16]], i64 [[TMP17]] +// CHECK-O0-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP16]], ptr [[TMP18]], align 8 +// CHECK-O0-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER3]], ptr [[TMP19]], align 8 +// CHECK-O0-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP16]], ptr [[TMP20]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-O0-NEXT: [[TMP21:%.*]] = mul i64 1, [[TMP5]] +// CHECK-O0-NEXT: [[TMP22:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR5]], i64 [[TMP21]] +// CHECK-O0-NEXT: [[UPPER6:%.*]] = getelementptr inbounds i32, ptr [[TMP22]], i64 [[TMP5]] +// CHECK-O0-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-O0-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER6]], ptr [[TMP24]], align 8 +// CHECK-O0-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP22]], ptr [[TMP25]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR8:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR7]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP26:%.*]] = mul nuw i64 [[TMP1]], [[TMP3]] +// CHECK-O0-NEXT: [[TMP27:%.*]] = mul nuw i64 [[TMP26]], [[TMP5]] +// CHECK-O0-NEXT: [[UPPER12:%.*]] = getelementptr inbounds i32, ptr [[VLA]], i64 [[TMP27]] +// CHECK-O0-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP28]], align 8 +// CHECK-O0-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER12]], ptr [[TMP29]], align 8 +// CHECK-O0-NEXT: [[TMP30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[VLA]], ptr [[TMP30]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.4", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8 +// CHECK-O0-NEXT: [[TMP31:%.*]] = mul nuw i64 [[TMP3]], [[TMP5]] +// CHECK-O0-NEXT: [[TMP32:%.*]] = mul i64 0, [[TMP31]] +// CHECK-O0-NEXT: [[TMP33:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR14]], i64 [[TMP32]] +// CHECK-O0-NEXT: [[TMP34:%.*]] = mul nuw i64 [[TMP3]], [[TMP5]] +// CHECK-O0-NEXT: [[UPPER15:%.*]] = getelementptr inbounds i32, ptr [[TMP33]], i64 [[TMP34]] +// CHECK-O0-NEXT: [[TMP35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP33]], ptr [[TMP35]], align 8 +// CHECK-O0-NEXT: [[TMP36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER15]], ptr [[TMP36]], align 8 +// CHECK-O0-NEXT: [[TMP37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP33]], ptr [[TMP37]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.3", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR17:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR16]], align 8 +// CHECK-O0-NEXT: [[TMP38:%.*]] = mul i64 0, [[TMP5]] +// CHECK-O0-NEXT: [[TMP39:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR17]], i64 [[TMP38]] +// CHECK-O0-NEXT: [[UPPER18:%.*]] = getelementptr inbounds i32, ptr [[TMP39]], i64 [[TMP5]] +// CHECK-O0-NEXT: [[TMP40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[TMP39]], ptr [[TMP40]], align 8 +// CHECK-O0-NEXT: [[TMP41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER18]], ptr [[TMP41]], align 8 +// CHECK-O0-NEXT: [[TMP42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[TMP39]], ptr [[TMP42]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8 +// CHECK-O0-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR8]] to i64 +// CHECK-O0-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR20]] to i64 +// CHECK-O0-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]] +// CHECK-O0-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4 +// CHECK-O0-NEXT: [[TMP43:%.*]] = load ptr, ptr [[SAVED_STACK]], align 8 +// CHECK-O0-NEXT: call void @llvm.stackrestore.p0(ptr [[TMP43]]) +// CHECK-O0-NEXT: ret i64 [[SUB_PTR_DIV]] +// +unsigned long long diff_3d_2(int m, int n, int l) { + int vla[m][n][l]; + return vla[0][1] - vla[0][0]; +} diff --git a/clang/test/BoundsSafety/CodeGen/wide-array-subscript-align.c b/clang/test/BoundsSafety/CodeGen/wide-array-subscript-align.c new file mode 100644 index 0000000000000..daea9f51986b3 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-array-subscript-align.c @@ -0,0 +1,103 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -fsanitize=alignment -fsanitize-trap=alignment -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -fsanitize=alignment -fsanitize-trap=alignment -triple arm64 %s -o - | FileCheck %s + +#include + +struct bstr { + const unsigned char *__counted_by(length) data; + unsigned long length; +}; + +// CHECK-LABEL: @test( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[SN_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[SN:%.*]], ptr [[SN_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[SN_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[TMP0]] to i64, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP2:%.*]] = and i64 [[TMP1]], 7, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = icmp eq i64 [[TMP2]], 0, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP3]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!nosanitize ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 22) #[[ATTR3:[0-9]+]], {{!nosanitize ![0-9]+}} +// CHECK-NEXT: unreachable, {{!nosanitize ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[LENGTH:%.*]] = getelementptr inbounds nuw [[STRUCT_BSTR:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[TMP4:%.*]] = ptrtoint ptr [[LENGTH]] to i64, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = and i64 [[TMP4]], 7, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = icmp eq i64 [[TMP5]], 0, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!nosanitize ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 22) #[[ATTR3]], {{!nosanitize ![0-9]+}} +// CHECK-NEXT: unreachable, {{!nosanitize ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[TMP7:%.*]] = load i64, ptr [[LENGTH]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = ptrtoint ptr [[TMP0]] to i64, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = and i64 [[TMP8]], 7, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = icmp eq i64 [[TMP9]], 0, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP10]], label [[CONT4:%.*]], label [[TRAP3:%.*]], {{!nosanitize ![0-9]+}} +// CHECK: trap3: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 22) #[[ATTR3]], {{!nosanitize ![0-9]+}} +// CHECK-NEXT: unreachable, {{!nosanitize ![0-9]+}} +// CHECK: cont4: +// CHECK-NEXT: [[DATA:%.*]] = getelementptr inbounds nuw [[STRUCT_BSTR]], ptr [[TMP0]], i32 0, i32 0 +// CHECK-NEXT: [[TMP11:%.*]] = ptrtoint ptr [[DATA]] to i64, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = and i64 [[TMP11]], 7, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = icmp eq i64 [[TMP12]], 0, {{!nosanitize ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT6:%.*]], label [[TRAP5:%.*]], {{!nosanitize ![0-9]+}} +// CHECK: trap5: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 22) #[[ATTR3]], {{!nosanitize ![0-9]+}} +// CHECK-NEXT: unreachable, {{!nosanitize ![0-9]+}} +// CHECK: cont6: +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[DATA]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[TMP7]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP14]], i64 [[TMP7]] +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr i8, ptr [[WIDE_PTR_PTR]], i64 2 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = icmp ult ptr [[TMP18]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP19]], label [[CONT8:%.*]], label [[TRAP7:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap7: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont8: +// CHECK-NEXT: [[TMP20:%.*]] = icmp uge ptr [[TMP18]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP20]], label [[CONT10:%.*]], label [[TRAP9:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap9: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable +// CHECK: cont10: +// CHECK-NEXT: [[TMP21:%.*]] = load i8, ptr [[TMP18]], align 1 +// CHECK-NEXT: [[CONV:%.*]] = zext i8 [[TMP21]] to i32 +// CHECK-NEXT: [[CMP11:%.*]] = icmp eq i32 [[CONV]], 170 +// CHECK-NEXT: br i1 [[CMP11]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +// CHECK: if.then: +// CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 +// CHECK-NEXT: br label [[RETURN:%.*]] +// CHECK: if.end: +// CHECK-NEXT: store i32 1, ptr [[RETVAL]], align 4 +// CHECK-NEXT: br label [[RETURN]] +// CHECK: return: +// CHECK-NEXT: [[TMP22:%.*]] = load i32, ptr [[RETVAL]], align 4 +// CHECK-NEXT: ret i32 [[TMP22]] +// +int test(struct bstr *sn) +{ + if (sn->data[2] == 0xAA) + return 0; + return 1; +} diff --git a/clang/test/BoundsSafety/CodeGen/wide-ptr-assign-O2.c b/clang/test/BoundsSafety/CodeGen/wide-ptr-assign-O2.c new file mode 100644 index 0000000000000..fe937a5e5dc76 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-ptr-assign-O2.c @@ -0,0 +1,136 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple arm64 %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @wide_array_subscript_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void wide_array_subscript_ok() { + int *__indexable arr[5]; + int *__indexable *__indexable buf = arr; + buf[4] = 0; +} + +// CHECK-LABEL: @wide_array_subscript_trap1( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void wide_array_subscript_trap1() { + int *__indexable arr[5]; + int *__indexable *__indexable buf = arr; + buf[5] = 0; +} + +// CHECK-LABEL: @wide_array_subscript_trap2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [5 x %"__bounds_safety::wide_ptr.indexable"], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 80, ptr nonnull [[ARR]]) #[[ATTR6:[0-9]+]] +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds nuw i8, ptr [[ARR]], i64 80 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr i8, ptr [[ARR]], i64 -16 +// CHECK-NEXT: [[TMP0:%.*]] = icmp ult ptr [[ARRAYIDX]], [[UPPER]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP1:%.*]] = icmp uge ptr [[ARRAYIDX]], [[ARR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[TMP0]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[CONT1:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 80, ptr nonnull [[ARR]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void wide_array_subscript_trap2() { + int *__indexable arr[5]; + int *__indexable *buf = arr; + buf[-1] = 0; +} + +// CHECK-LABEL: @wide_array_subscript_read_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void wide_array_subscript_read_ok() { + int *__indexable arr[5]; + int *__indexable *__indexable buf = arr; + (void)buf[4]; +} + +// CHECK-LABEL: @wide_array_subscript_read_ok2( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void wide_array_subscript_read_ok2() { + int *__indexable arr[5]; + int *__indexable *__indexable buf = arr; + (void)&buf[5]; +} + +// CHECK-LABEL: @wide_array_subscript_read_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void wide_array_subscript_read_trap() { + int *__indexable arr[5]; + int *__indexable *__indexable buf = arr; + (void)buf[5]; +} + +// CHECK-LABEL: @wide_deref_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void wide_deref_ok() { + int *__indexable arr[5]; + int *__indexable *buf = arr; + *(buf + 4) = 0; +} + +// CHECK-LABEL: @wide_deref_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// +void wide_deref_trap() { + int *__indexable arr[5]; + int *__indexable *buf = arr; + *(buf + 5) = 0; +} + +struct wide_member_t { + int *__bidi_indexable ptr; +}; +// CHECK-LABEL: @wide_member_assign_ok( +// CHECK-NEXT: entry: +// CHECK-NEXT: ret void +// +void wide_member_assign_ok() { + struct wide_member_t w; + struct wide_member_t *wp = &w; + wp->ptr = 0; +} + +// CHECK-LABEL: @wide_member_assign_trap( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[W:%.*]] = alloca [[STRUCT_WIDE_MEMBER_T:%.*]], align 8 +// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull [[W]]) #[[ATTR6]] +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[W]], i64 24 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[W]], i64 48 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[TMP1]], [[TMP0]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[DOTNOT]], label [[TRAP:%.*]], label [[CONT1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont1: +// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 24, ptr nonnull [[W]]) #[[ATTR6]] +// CHECK-NEXT: ret void +// +void wide_member_assign_trap() { + struct wide_member_t w; + struct wide_member_t *wp = &w + 1; + wp->ptr = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/wide-ptr-assign.c b/clang/test/BoundsSafety/CodeGen/wide-ptr-assign.c new file mode 100644 index 0000000000000..11d39a23dbf65 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-ptr-assign.c @@ -0,0 +1,236 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple arm64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple arm64 %s -o - | FileCheck %s + +#include + +// CHECK-LABEL: @wide_array_subscript( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: store [2 x i64] [[BUF_COERCE:%.*]], ptr [[BUF]], align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[BUF]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr %"__bounds_safety::wide_ptr.indexable.0", ptr [[WIDE_PTR_PTR3]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ult ptr [[TMP4]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP6:%.*]] = icmp uge ptr [[TMP4]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont7: +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[TMP4]], i8 0, i64 16, i1 false) +// CHECK-NEXT: ret void +// +void wide_array_subscript(int *__indexable *__indexable buf, int i) { + buf[i] = 0; +} + +// CHECK-LABEL: @wide_array_subscript_load( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: store [2 x i64] [[BUF_COERCE:%.*]], ptr [[BUF]], align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[BUF]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP3]] to i64 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr %"__bounds_safety::wide_ptr.indexable.0", ptr [[WIDE_PTR_PTR3]], i64 [[IDXPROM]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = icmp ult ptr [[TMP4]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP6:%.*]] = icmp uge ptr [[TMP4]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont7: +// CHECK-NEXT: ret void +// +void wide_array_subscript_load(int *__indexable *__indexable buf, int i) { + (void)buf[i]; +} + +// CHECK-LABEL: @wide_array_subscript_addr( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: store [2 x i64] [[BUF_COERCE:%.*]], ptr [[BUF]], align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[BUF]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr %"__bounds_safety::wide_ptr.indexable.0", ptr [[TMP5]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TMP_ENSURED]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: ret void +// +void wide_array_subscript_addr(int *__indexable *__indexable buf, int i) { + (void)&buf[i]; +} + +// CHECK-LABEL: @wide_deref( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: [[I_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.indexable", align 8 +// CHECK-NEXT: store [2 x i64] [[BUF_COERCE:%.*]], ptr [[BUF]], align 8 +// CHECK-NEXT: store i32 [[I:%.*]], ptr [[I_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[BUF]], i64 16, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[TMP4]], align 8 +// CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP0]] to i64 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr %"__bounds_safety::wide_ptr.indexable.0", ptr [[TMP5]], i64 [[IDXPROM]] +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP8]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = icmp ult ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_UB5]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP14:%.*]] = icmp uge ptr [[WIDE_PTR_PTR3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP14]], label [[CONT7:%.*]], label [[TRAP6:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap6: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont7: +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[WIDE_PTR_PTR3]], i8 0, i64 16, i1 false) +// CHECK-NEXT: ret void +// +void wide_deref(int *__indexable *__indexable buf, int i) { + *(buf + i) = 0; +} + +struct wide_member_t { + int *__bidi_indexable ptr; +}; +// CHECK-LABEL: @wide_member_assign( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[W_INDIRECT_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: store ptr [[W:%.*]], ptr [[W_INDIRECT_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[W]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr [[STRUCT_WIDE_MEMBER_T:%.*]], ptr [[WIDE_PTR_PTR]], i64 1 +// CHECK-NEXT: [[TMP1:%.*]] = icmp ule ptr [[TMP0]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: [[TMP2:%.*]] = icmp ule ptr [[WIDE_PTR_LB]], [[WIDE_PTR_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP2]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap1: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont2: +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_WIDE_MEMBER_T]], ptr [[WIDE_PTR_PTR]], i32 0, i32 0 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[PTR]], i8 0, i64 24, i1 false) +// CHECK-NEXT: ret void +// +void wide_member_assign(struct wide_member_t *__bidi_indexable w) { + w->ptr = 0; +} diff --git a/clang/test/BoundsSafety/CodeGen/wide-ptr-bound-addrof-deref.c b/clang/test/BoundsSafety/CodeGen/wide-ptr-bound-addrof-deref.c new file mode 100644 index 0000000000000..32aa2efd8fec9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-ptr-bound-addrof-deref.c @@ -0,0 +1,105 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 + +// CHECK-O0-LABEL: @foo( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[PTR2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[PTR2]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR2]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 10 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp ult ptr [[TMP3]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP5:%.*]] = icmp uge ptr [[TMP3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP5]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP6]] +// +// CHECK-O2-LABEL: @foo( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int foo() { + int arr[10]; + int *ptr = arr; + int *ptr2 = &*ptr; + return ptr2[10]; +} + +// CHECK-O0-LABEL: @bar( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[PTR2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[PTR2]], ptr align 8 [[PTR]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP]], ptr align 8 [[PTR2]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR]], i64 9 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = icmp ult ptr [[TMP3]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP5:%.*]] = icmp uge ptr [[TMP3]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP5]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP3]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP6]] +// +// CHECK-O2-LABEL: @bar( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 undef +// +int bar() { + int arr[10]; + int *ptr = arr; + int *ptr2 = &*ptr; + return ptr2[9]; +} diff --git a/clang/test/BoundsSafety/CodeGen/wide-ptr-bound-deref-addrof.c b/clang/test/BoundsSafety/CodeGen/wide-ptr-bound-deref-addrof.c new file mode 100644 index 0000000000000..e1cf0a79792ad --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-ptr-bound-deref-addrof.c @@ -0,0 +1,160 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -O2 -triple arm64-apple-iphoneos -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-O2 +#include + +// +// We could have skipped '*&' entirely but this is fine too. +// CHECK-O0-LABEL: @foo( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[PTR2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i64 1 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[PTR]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[PTR]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP8]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[PTR2]], ptr align 8 [[WIDE_PTR_PTR]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[PTR2]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR5]], i64 10 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP10]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap10: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont11: +// CHECK-O0-NEXT: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP11]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap12: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont13: +// CHECK-O0-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP12]] +// +// CHECK-O2-LABEL: @foo( +// CHECK-O2-NEXT: trap: +// CHECK-O2-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-O2-NEXT: unreachable +// +int foo() { + int arr[10]; + int *__bidi_indexable ptr = arr; + int *ptr2 = *&ptr; + return ptr2[10]; +} + +// CHECK-O0-LABEL: @bar( +// CHECK-O0-NEXT: entry: +// CHECK-O0-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-O0-NEXT: [[PTR:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[PTR2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-O0-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-O0-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-O0-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-O0-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = getelementptr %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[PTR]], i64 1 +// CHECK-O0-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: store ptr [[PTR]], ptr [[TMP4]], align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: store ptr [[PTR]], ptr [[TMP6]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = icmp ult ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_UB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP7]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont: +// CHECK-O0-NEXT: [[TMP8:%.*]] = icmp uge ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_LB]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP8]], label [[CONT2:%.*]], label [[TRAP1:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap1: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont2: +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[PTR2]], ptr align 8 [[WIDE_PTR_PTR]], i64 24, i1 false) +// CHECK-O0-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[PTR2]], i64 24, i1 false) +// CHECK-O0-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-O0-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = getelementptr i32, ptr [[WIDE_PTR_PTR5]], i64 9 +// CHECK-O0-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-O0-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-O0-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-O0-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = icmp ult ptr [[TMP9]], [[WIDE_PTR_UB7]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP10]], label [[CONT11:%.*]], label [[TRAP10:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap10: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont11: +// CHECK-O0-NEXT: [[TMP11:%.*]] = icmp uge ptr [[TMP9]], [[WIDE_PTR_LB9]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: br i1 [[TMP11]], label [[CONT13:%.*]], label [[TRAP12:%.*]], {{!annotation ![0-9]+}} +// CHECK-O0: trap12: +// CHECK-O0-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR3]], {{!annotation ![0-9]+}} +// CHECK-O0-NEXT: unreachable +// CHECK-O0: cont13: +// CHECK-O0-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP9]], align 4 +// CHECK-O0-NEXT: ret i32 [[TMP12]] +// +// CHECK-O2-LABEL: @bar( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret i32 undef +// +int bar() { + int arr[10]; + int *__bidi_indexable ptr = arr; + int *ptr2 = *&ptr; + return ptr2[9]; +} diff --git a/clang/test/BoundsSafety/CodeGen/wide-ptr-init-with-incomplete-array.c b/clang/test/BoundsSafety/CodeGen/wide-ptr-init-with-incomplete-array.c new file mode 100644 index 0000000000000..8d417a222bb68 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-ptr-init-with-incomplete-array.c @@ -0,0 +1,27 @@ + + +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +#include + +extern float foo[]; +// CHECK: @foo = external global [0 x float] + +float *__indexable wide_f[] = {foo}; +// CHECK: @wide_f = global [1 x %"__bounds_safety::wide_ptr.indexable"] [%"__bounds_safety::wide_ptr.indexable" { ptr @foo, ptr @foo }] + +extern float bar[]; +float bar[] = {1, 2, 3, 4}; +// CHECK: @bar = global [4 x float] [float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00] + +float *__indexable wide_f2[] = {bar}; +// CHECK: @wide_f2 = global [1 x %"__bounds_safety::wide_ptr.indexable"] [%"__bounds_safety::wide_ptr.indexable" { ptr @bar, ptr inttoptr (i64 add (i64 ptrtoint (ptr @bar to i64), i64 16) to ptr) }] + +float *__bidi_indexable wide_f3[] = {foo, bar}; +// CHECK: @wide_f3 = global [2 x %"__bounds_safety::wide_ptr.bidi_indexable"] [%"__bounds_safety::wide_ptr.bidi_indexable" { ptr @foo, ptr @foo, ptr @foo }, %"__bounds_safety::wide_ptr.bidi_indexable" { ptr @bar, ptr inttoptr (i64 add (i64 ptrtoint (ptr @bar to i64), i64 16) to ptr), ptr @bar }] + +// CHECK-NOT: @foo +// CHECK-NOT: @wide_f +// CHECK-NOT: @bar +// CHECK-NOT: @wide_f2 +// CHECK-NOT: @wide_f3 diff --git a/clang/test/BoundsSafety/CodeGen/wide-ptr-init.c b/clang/test/BoundsSafety/CodeGen/wide-ptr-init.c new file mode 100644 index 0000000000000..66bce7b7d80ef --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-ptr-init.c @@ -0,0 +1,60 @@ + + +// RUN: %clang_cc1 -O0 -triple arm64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s + +#include + +// CHECK: %"__bounds_safety::wide_ptr.indexable" = type { ptr, ptr } +// CHECK: %"__bounds_safety::wide_ptr.bidi_indexable" = type { ptr, ptr, ptr } + +// CHECK: @i_null = global %"__bounds_safety::wide_ptr.indexable{{.*}}" zeroinitializer, align 8 +// CHECK: @bi_null = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" zeroinitializer, align 8 +int *__indexable i_null = 0; +int *__bidi_indexable bi_null = 0; + +// CHECK: @i_null_cast = global %"__bounds_safety::wide_ptr.indexable{{.*}}" zeroinitializer, align 8 +// CHECK: @bi_null_cast = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" zeroinitializer, align 8 +void *__indexable i_null_cast = (int *)0; +void *__bidi_indexable bi_null_cast = (int *)0; + +// CHECK: @array = global [10 x i32] zeroinitializer, align 4 +int array[10]; + +// CHECK: @i_array = global %"__bounds_safety::wide_ptr.indexable{{.*}}" { ptr @array, ptr inttoptr (i64 add (i64 ptrtoint (ptr @array to i64), i64 40) to ptr) }, align 8 +// CHECK: @bi_array = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" { ptr @array, ptr inttoptr (i64 add (i64 ptrtoint (ptr @array to i64), i64 40) to ptr), ptr @array }, align 8 +int *__indexable i_array = array; +int *__bidi_indexable bi_array = array; + +// CHECK: @i_array_cast = global %"__bounds_safety::wide_ptr.indexable{{.*}}" { ptr @array, ptr inttoptr (i64 add (i64 ptrtoint (ptr @array to i64), i64 40) to ptr) }, align 8 +// CHECK: @bi_array_cast = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" { ptr @array, ptr inttoptr (i64 add (i64 ptrtoint (ptr @array to i64), i64 40) to ptr), ptr @array }, align 8 +void *__indexable i_array_cast = array; +void *__bidi_indexable bi_array_cast = array; + +// CHECK: @x = global i32 0, align 4 +int x; + +// CHECK: @i_addrof = global %"__bounds_safety::wide_ptr.indexable{{.*}}" { ptr @x, ptr inttoptr (i64 add (i64 ptrtoint (ptr @x to i64), i64 4) to ptr) }, align 8 +// CHECK: @bi_addrof = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" { ptr @x, ptr inttoptr (i64 add (i64 ptrtoint (ptr @x to i64), i64 4) to ptr), ptr @x }, align 8 +int *__indexable i_addrof = &x; +int *__bidi_indexable bi_addrof = &x; + +// CHECK: @i_addrof_cast = global %"__bounds_safety::wide_ptr.indexable{{.*}}" { ptr @x, ptr inttoptr (i64 add (i64 ptrtoint (ptr @x to i64), i64 4) to ptr) }, align 8 +// CHECK: @bi_addrof_cast = global %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" { ptr @x, ptr inttoptr (i64 add (i64 ptrtoint (ptr @x to i64), i64 4) to ptr), ptr @x }, align 8 +void *__indexable i_addrof_cast = &x; +void *__bidi_indexable bi_addrof_cast = &x; + +struct foo { + int x[100]; +}; + +// CHECK: @f = global %struct.foo zeroinitializer, align 4 +struct foo f = {}; + +struct bar { + const void *__bidi_indexable p; +}; + +// CHECK: @b = global %struct.bar { %"__bounds_safety::wide_ptr.bidi_indexable{{.*}}" { ptr @f, ptr inttoptr (i64 add (i64 ptrtoint (ptr @f to i64), i64 400) to ptr), ptr @f } }, align 8 +struct bar b = { + .p = &f +}; diff --git a/clang/test/BoundsSafety/CodeGen/wide-to-counted-pointer-arg-side-effect.c b/clang/test/BoundsSafety/CodeGen/wide-to-counted-pointer-arg-side-effect.c new file mode 100644 index 0000000000000..1b5e47cd94385 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/wide-to-counted-pointer-arg-side-effect.c @@ -0,0 +1,51 @@ + +// XFAIL: * + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -emit-llvm %s -o /dev/null +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o /dev/null + +#include + +void Foo(int *__counted_by(len) buf, int len) {} + +unsigned long get_len(void *__bidi_indexable ptr); +unsigned long trap_if_bigger_than_max(unsigned long len); + + +int Test() { + int arr[10]; + Foo(arr, trap_if_bigger_than_max(get_len(arr))); + return 0; +} + +// CHECK-LABEL: @Test +// ... +// CHECK: [[RET_GET_LEN:%.*]] = call i64 @get_len +// CHECK: [[RET_TRAP_IF:%.*]] = call i64 @trap_if_bigger_than_max(i64 [[RET_GET_LEN]]), !annotation [[ANNOT_CONV_TO_COUNT:![0-9]+]] +// ... +// CHECK: [[COND_LE_UB:%.*]] = icmp ule i64 {{%.*}}, {{%.*}}, !annotation [[ANNOT_LE_UB:![0-9]+]] +// CHECK: br i1 [[COND_LE_UB]], label %[[LABEL_CONT:.*]], label %[[LABEL_TRAP:.*]], !annotation [[ANNOT_LE_UB]] + +// CHECK: [[LABEL_TRAP]]: +// CHECK: call void @llvm.ubsantrap{{.*}}, !annotation [[ANNOT_LE_UB]] +// CHECK: unreachable + +// 'RET_TRAP_IF' - the result of call 'trap_if_bigger_than_max' is used in count check here and later as argument for function call to @Foo, +// instead of being re-evaluated. +// CHECK: [[LABEL_CONT]]: +// CHECK: [[COND_CONV_TO_COUNT:%.*]] = icmp ule i64 [[RET_TRAP_IF]], {{.*}}, !annotation [[ANNOT_CONV_TO_COUNT]] +// CHECK: br i1 [[COND_CONV_TO_COUNT]], label %[[LABEL_CONT12:.*]], label %[[LABEL_TRAP11:.*]], !annotation [[ANNOT_CONV_TO_COUNT]] + +// CHECK: [[LABEL_TRAP11]]: ; preds = %[[LABEL_CONT]] +// CHECK: call void @llvm.ubsantrap{{.*}}, !annotation [[ANNOT_CONV_TO_COUNT]] +// CHECK: unreachable + +// CHECK: [[LABEL_CONT12]]: ; preds = %[[LABEL_CONT]] +// CHECK: [[ARG_CONV:%.*]] = trunc i64 [[RET_TRAP_IF]] to i32 +// CHECK: call void @Foo(i32* {{%.*}}, i32 [[ARG_CONV]]) +// CHECK: ret i32 0 + +// CHECK: [[ANNOT_CONV_TO_COUNT]] = !{!"bounds-safety-check-conversion-to-count"} +// CHECK: [[ANNOT_LE_UB]] = !{!"bounds-safety-check-ptr-le-upper-bound"} diff --git a/clang/test/BoundsSafety/CodeGen/zero-init-split-pointers.c b/clang/test/BoundsSafety/CodeGen/zero-init-split-pointers.c new file mode 100644 index 0000000000000..9488ac8cb08ce --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/zero-init-split-pointers.c @@ -0,0 +1,28 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s +#include + +struct foo { + int *__counted_by(count) ptr; + int count; + int not_initialized; +}; + + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[COUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: store i32 0, ptr [[COUNT]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[COUNT1:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 1 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COUNT1]], align 8 +// CHECK-NEXT: ret i32 [[TMP0]] +// +int bar(void) { + struct foo f; + return f.count; +} diff --git a/clang/test/BoundsSafety/CodeGen/zero-init-terminated-by.c b/clang/test/BoundsSafety/CodeGen/zero-init-terminated-by.c new file mode 100644 index 0000000000000..daabb05c7d427 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/zero-init-terminated-by.c @@ -0,0 +1,25 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" + +// RUN: %clang_cc1 -O0 -fbounds-safety -emit-llvm -triple x86_64 %s -o - | FileCheck %s +// RUN: %clang_cc1 -O0 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -triple x86_64 %s -o - | FileCheck %s + +#include + +struct foo { + int *__null_terminated ptr; + int not_initialized; +}; + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[STRUCT_FOO:%.*]], align 8 +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[PTR1:%.*]] = getelementptr inbounds nuw [[STRUCT_FOO]], ptr [[F]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR1]], align 8 +// CHECK-NEXT: ret ptr [[TMP0]] +// +int *__null_terminated bar(void) { + struct foo f; + return f.ptr; +} diff --git a/clang/test/BoundsSafety/CodeGen/zero-init.c b/clang/test/BoundsSafety/CodeGen/zero-init.c new file mode 100644 index 0000000000000..bc26054808def --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/zero-init.c @@ -0,0 +1,13 @@ + +// RUN: %clang_cc1 %s -O2 -fbounds-safety -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O2 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm -o - | FileCheck %s + +int main() { + int *ptr; + if (!ptr) + return 0; + return 1; +} + +// CHECK: ret i32 0 +// CHECK-NOT ret i32 1 \ No newline at end of file diff --git a/clang/test/BoundsSafety/CodeGen/zero-length-pointers.c b/clang/test/BoundsSafety/CodeGen/zero-length-pointers.c new file mode 100644 index 0000000000000..b35b1d12cf7a9 --- /dev/null +++ b/clang/test/BoundsSafety/CodeGen/zero-length-pointers.c @@ -0,0 +1,1298 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ + +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -emit-llvm %s -o - -O2 | FileCheck %s --check-prefix CHECK-O2 +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -emit-llvm %s -o - -O2 | FileCheck %s --check-prefix CHECK-O2 + +#include +typedef __SIZE_TYPE__ size_t; + + +void counted_by(int *__counted_by(len) p, size_t len); +// CHECK-LABEL: @test_counted_by( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP20:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP37:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP53:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP62:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP76:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB13:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB13]], ptr [[TMP4]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR15:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB17:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB19:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP20]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP20]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP27:%.*]] = icmp ule ptr [[WIDE_PTR_PTR15]], [[WIDE_PTR_PTR22]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP27]], label [[LAND_RHS:%.*]], label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR32:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB34:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP28]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB36:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP37]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR39:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB41:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP37]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB43:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR42]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR32]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR39]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP44:%.*]] = icmp ule i64 0, [[SUB_PTR_DIV]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP6:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP44]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP6]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP45]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR47:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB49:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR48]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR50:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP45]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB51:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR50]], align 8 +// CHECK-NEXT: call void @counted_by(ptr noundef [[WIDE_PTR_PTR47]], i64 noundef 0) +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP12]], i64 1 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP19]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP53]], ptr align 8 [[AGG_TEMP52]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR54:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB55:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR54]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB55]], ptr [[TMP20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR56:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR57:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR56]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR58:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB59:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR58]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR60:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP53]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB61:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR60]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP62]], ptr align 8 [[AGG_TEMP52]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR64:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR63]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB66:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR65]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR67:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP62]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB68:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR67]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST69:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR57]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST70:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR64]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB71:%.*]] = sub i64 [[SUB_PTR_LHS_CAST69]], [[SUB_PTR_RHS_CAST70]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_DIV72:%.*]] = sdiv exact i64 [[SUB_PTR_SUB71]], 4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP73:%.*]] = icmp ule i64 0, [[SUB_PTR_DIV72]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP73]], label [[CONT75:%.*]], label [[TRAP74:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap74: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont75: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP76]], ptr align 8 [[AGG_TEMP52]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR77:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP76]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR78:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR77]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR79:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP76]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB80:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR79]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR81:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP76]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB82:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR81]], align 8 +// CHECK-NEXT: call void @counted_by(ptr noundef [[WIDE_PTR_PTR78]], i64 noundef 0) +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_counted_by( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-O2-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4:[0-9]+]] +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @counted_by(ptr noundef nonnull [[ZERO]], i64 noundef 0) #[[ATTR4]] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ONE]], i64 4 +// CHECK-O2-NEXT: call void @counted_by(ptr noundef nonnull [[TMP0]], i64 noundef 0) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: ret void +// +void test_counted_by(void) { + int zero[0]; + int one; + counted_by(zero, 0); + counted_by(&one + 1, 0); +} + +void sized_by(void *__sized_by(len) p, size_t len); +// CHECK-LABEL: @test_sized_by( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP36:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP51:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP66:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP73:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP74:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP81:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP82:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP97:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.1", align 8 +// CHECK-NEXT: [[AGG_TEMP98:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP117:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB11]], ptr [[TMP6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR4]], [[WIDE_PTR_PTR13]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB20]], ptr [[TMP7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR22:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR21]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB24:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB26:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR25]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP27]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP27]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP34:%.*]] = icmp ule ptr [[WIDE_PTR_PTR22]], [[WIDE_PTR_PTR29]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP34]], label [[LAND_RHS:%.*]], label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP36]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB38:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB38]], ptr [[TMP8]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP36]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR40]], ptr [[TMP9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB44]], ptr [[TMP11]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR54]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB56]], ptr [[TMP13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB58]], ptr [[TMP14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP51]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR46]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR60]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP65:%.*]] = icmp ule i64 0, [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP15:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP65]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP15]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP66]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR67:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR68:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR67]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP66]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB70:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR71:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB72:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR71]], align 8 +// CHECK-NEXT: call void @sized_by(ptr noundef [[WIDE_PTR_PTR68]], i64 noundef 0) +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP21:%.*]] = load ptr, ptr [[TMP20]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP21]], i64 1 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP24:%.*]] = load ptr, ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP24]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP27:%.*]] = load ptr, ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP27]], ptr [[TMP28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR75:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR76:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR75]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR77:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB78:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR77]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR79:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP74]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB80:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR79]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP73]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR76]], ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP73]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB78]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP73]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB80]], ptr [[TMP31]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP82]], ptr align 8 [[AGG_TEMP73]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR83:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB84:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR83]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB84]], ptr [[TMP32]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR85:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR86:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR85]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR87:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB88:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR87]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR89:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB90:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR89]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP81]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR86]], ptr [[TMP33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP81]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB88]], ptr [[TMP34]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP81]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB90]], ptr [[TMP35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR91:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP81]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR92:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR91]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR93:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP81]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB94:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR93]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR95:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP81]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB96:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR95]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP98]], ptr align 8 [[AGG_TEMP73]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR99:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP98]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR100:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR99]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR101:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP98]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB102:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR101]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR103:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP98]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB104:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR103]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP97]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR100]], ptr [[TMP36]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP97]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB102]], ptr [[TMP37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP97]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB104]], ptr [[TMP38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR105:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP97]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR106:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR105]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR107:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP97]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB108:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR107]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR109:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.1", ptr [[AGG_TEMP97]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB110:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR109]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST111:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR92]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST112:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR106]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB113:%.*]] = sub i64 [[SUB_PTR_LHS_CAST111]], [[SUB_PTR_RHS_CAST112]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP114:%.*]] = icmp ule i64 0, [[SUB_PTR_SUB113]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP114]], label [[CONT116:%.*]], label [[TRAP115:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap115: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont116: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP117]], ptr align 8 [[AGG_TEMP73]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR118:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP117]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR119:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR118]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR120:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP117]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB121:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR120]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR122:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP117]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB123:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR122]], align 8 +// CHECK-NEXT: call void @sized_by(ptr noundef [[WIDE_PTR_PTR119]], i64 noundef 0) +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_sized_by( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-O2-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @sized_by(ptr noundef nonnull [[ZERO]], i64 noundef 0) #[[ATTR4]] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ONE]], i64 4 +// CHECK-O2-NEXT: call void @sized_by(ptr noundef nonnull [[TMP0]], i64 noundef 0) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: ret void +// +void test_sized_by(void) { + int zero[0]; + int one; + sized_by(zero, 0); + sized_by(&one + 1, 0); +} + +void ended_by(void *__ended_by(end) start, void *end); +// CHECK-LABEL: @test_ended_by( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP19:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP28:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP35:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP50:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP57:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP58:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP65:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP66:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP67:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP75:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP82:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP92:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP99:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[ARRAYDECAY4:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER5:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY4]], i64 0 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY4]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER5]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY4]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR7:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB9:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB11:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR10]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR7]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB9]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB11]], ptr [[TMP11]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR14:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB16:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB18:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP19]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB21]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP19]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR14]], [[WIDE_PTR_PTR23]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP28]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR30:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR29]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP28]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB34:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP35]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR37:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR36]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB39:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP35]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB41:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR40]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP42:%.*]] = icmp ule ptr [[WIDE_PTR_PTR30]], [[WIDE_PTR_PTR37]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP13:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP42]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP13]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP43]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR45:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR44]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR46:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB47:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR46]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR48:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP43]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB49:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR48]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP50]], ptr align 8 [[AGG_TEMP2]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR52:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP50]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB56:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR55]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR45]], ptr noundef [[WIDE_PTR_PTR52]]) +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[TMP18]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP19]], i64 1 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP22:%.*]] = load ptr, ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP22]], ptr [[TMP23]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[TMP24]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP25]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR59:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR60:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR59]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB62:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP58]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB64:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR63]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP57]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR60]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP57]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB62]], ptr [[TMP28]], align 8 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP57]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB64]], ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP67]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP67]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP30]], ptr [[TMP32]], align 8 +// CHECK-NEXT: [[TMP33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP67]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP33]], align 8 +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP67]], i32 0, i32 0 +// CHECK-NEXT: [[TMP35:%.*]] = load ptr, ptr [[TMP34]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH68:%.*]] = getelementptr i32, ptr [[TMP35]], i64 1 +// CHECK-NEXT: [[TMP36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH68]], ptr [[TMP36]], align 8 +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP67]], i32 0, i32 1 +// CHECK-NEXT: [[TMP38:%.*]] = load ptr, ptr [[TMP37]], align 8 +// CHECK-NEXT: [[TMP39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP38]], ptr [[TMP39]], align 8 +// CHECK-NEXT: [[TMP40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP67]], i32 0, i32 2 +// CHECK-NEXT: [[TMP41:%.*]] = load ptr, ptr [[TMP40]], align 8 +// CHECK-NEXT: [[TMP42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP41]], ptr [[TMP42]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR69:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR70:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR69]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR71:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB72:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR71]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR73:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP66]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB74:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR73]], align 8 +// CHECK-NEXT: [[TMP43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP65]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR70]], ptr [[TMP43]], align 8 +// CHECK-NEXT: [[TMP44:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP65]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB72]], ptr [[TMP44]], align 8 +// CHECK-NEXT: [[TMP45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP65]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB74]], ptr [[TMP45]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP75]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR76:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP75]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR77:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR76]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR78:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP75]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB79:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR78]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR80:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP75]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB81:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR80]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP82]], ptr align 8 [[AGG_TEMP65]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR83:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR84:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR83]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR85:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB86:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR85]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR87:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP82]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB88:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR87]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP89:%.*]] = icmp ule ptr [[WIDE_PTR_PTR77]], [[WIDE_PTR_PTR84]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP89]], label [[CONT91:%.*]], label [[TRAP90:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap90: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont91: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP92]], ptr align 8 [[AGG_TEMP57]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR93:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR94:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR93]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR95:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB96:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR95]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR97:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP92]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB98:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR97]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP99]], ptr align 8 [[AGG_TEMP65]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR100:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP99]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR101:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR100]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR102:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP99]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB103:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR102]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR104:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP99]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB105:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR104]], align 8 +// CHECK-NEXT: call void @ended_by(ptr noundef [[WIDE_PTR_PTR94]], ptr noundef [[WIDE_PTR_PTR101]]) +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_ended_by( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-O2-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @ended_by(ptr noundef nonnull [[ZERO]], ptr noundef nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr inbounds nuw i8, ptr [[ONE]], i64 4 +// CHECK-O2-NEXT: call void @ended_by(ptr noundef nonnull [[BOUND_PTR_ARITH]], ptr noundef nonnull [[BOUND_PTR_ARITH]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: ret void +// +void test_ended_by(void) { + int zero[0]; + int one; + ended_by(zero, zero); + ended_by(&one + 1, &one + 1); +} + +void ended_by_itself(void *__ended_by(end) end); +// CHECK-LABEL: @test_ended_by_itself( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP18:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP25:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP33:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP40:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP52:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR4:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB6:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB8:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP9]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB11:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR10]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB11]], ptr [[TMP6]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP9]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP:%.*]] = icmp ule ptr [[WIDE_PTR_PTR4]], [[WIDE_PTR_PTR13]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP18]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR20:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB22:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP18]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB24:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR23]], align 8 +// CHECK-NEXT: call void @ended_by_itself(ptr noundef [[WIDE_PTR_PTR20]]) +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP7]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP12:%.*]] = load ptr, ptr [[TMP11]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP12]], i64 1 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP15]], ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP18:%.*]] = load ptr, ptr [[TMP17]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP18]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR28:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB30:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR29]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB32:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR31]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR28]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB30]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP25]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB32]], ptr [[TMP22]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP33]], ptr align 8 [[AGG_TEMP25]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP33]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP33]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP33]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP40]], ptr align 8 [[AGG_TEMP25]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP23:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB42]], ptr [[TMP23]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR44:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB46:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR45]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP40]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB48:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR47]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP49:%.*]] = icmp ule ptr [[WIDE_PTR_PTR35]], [[WIDE_PTR_PTR44]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP49]], label [[CONT51:%.*]], label [[TRAP50:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap50: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont51: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP52]], ptr align 8 [[AGG_TEMP25]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP52]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8 +// CHECK-NEXT: call void @ended_by_itself(ptr noundef [[WIDE_PTR_PTR54]]) +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_ended_by_itself( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-O2-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @ended_by_itself(ptr noundef nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[ONE]], i64 4 +// CHECK-O2-NEXT: call void @ended_by_itself(ptr noundef nonnull [[TMP0]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr nonnull [[ONE]]) #[[ATTR4]] +// CHECK-O2-NEXT: call void @llvm.lifetime.end.p0(i64 0, ptr nonnull [[ZERO]]) #[[ATTR4]] +// CHECK-O2-NEXT: ret void +// +void test_ended_by_itself(void) { + int zero[0]; + int one; + ended_by_itself(zero); + ended_by_itself(&one + 1); +} + +struct counted_by { + int *__counted_by(len) p; + size_t len; +}; + +// CHECK-LABEL: @test_struct_counted_by( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_COUNTED_BY:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED1:%.*]] = alloca [[STRUCT_COUNTED_BY]], align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[P:%.*]] = getelementptr inbounds nuw [[STRUCT_COUNTED_BY]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[P]], align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_COUNTED_BY]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: store i64 0, ptr [[LEN]], align 8 +// CHECK-NEXT: [[P2:%.*]] = getelementptr inbounds nuw [[STRUCT_COUNTED_BY]], ptr [[AGG_TMP_ENSURED1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP3]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP8:%.*]] = load ptr, ptr [[TMP7]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP8]], i64 1 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR5:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB7:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR6]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB9:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR8]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR5]], ptr [[P2]], align 8 +// CHECK-NEXT: [[LEN10:%.*]] = getelementptr inbounds nuw [[STRUCT_COUNTED_BY]], ptr [[AGG_TMP_ENSURED1]], i32 0, i32 1 +// CHECK-NEXT: store i64 0, ptr [[LEN10]], align 8 +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_struct_counted_by( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void test_struct_counted_by(void) { + int zero[0]; + int one; + (void) (struct counted_by) {zero, 0}; + (void) (struct counted_by) {&one + 1, 0}; +} + +struct sized_by { + void *__sized_by(len) p; + size_t len; +}; + +// CHECK-LABEL: @test_struct_sized_by( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_SIZED_BY:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED8:%.*]] = alloca [[STRUCT_SIZED_BY]], align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[P:%.*]] = getelementptr inbounds nuw [[STRUCT_SIZED_BY]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[P]], align 8 +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_SIZED_BY]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: store i64 0, ptr [[LEN]], align 8 +// CHECK-NEXT: [[P9:%.*]] = getelementptr inbounds nuw [[STRUCT_SIZED_BY]], ptr [[AGG_TMP_ENSURED8]], i32 0, i32 0 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP11]], i64 1 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR13]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB15]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB17]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR19]], ptr [[P9]], align 8 +// CHECK-NEXT: [[LEN24:%.*]] = getelementptr inbounds nuw [[STRUCT_SIZED_BY]], ptr [[AGG_TMP_ENSURED8]], i32 0, i32 1 +// CHECK-NEXT: store i64 0, ptr [[LEN24]], align 8 +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_struct_sized_by( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void test_struct_sized_by(void) { + int zero[0]; + int one; + (void) (struct sized_by) {zero, 0}; + (void) (struct sized_by) {&one + 1, 0}; +} + +struct ended_by { + void *__ended_by(end) start; + void *end; +}; + +// CHECK-LABEL: @test_struct_ended_by( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_ENDED_BY:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP8:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP9:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED24:%.*]] = alloca [[STRUCT_ENDED_BY]], align 8 +// CHECK-NEXT: [[AGG_TEMP26:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP27:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP41:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP42:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP_TMP43:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[START:%.*]] = getelementptr inbounds nuw [[STRUCT_ENDED_BY]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[START]], align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_ENDED_BY]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYDECAY10:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER11:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY10]], i64 0 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY10]], ptr [[TMP6]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER11]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY10]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP9]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR13]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB15]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB17]], ptr [[TMP11]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP8]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR19]], ptr [[END]], align 8 +// CHECK-NEXT: [[START25:%.*]] = getelementptr inbounds nuw [[STRUCT_ENDED_BY]], ptr [[AGG_TMP_ENSURED24]], i32 0, i32 0 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP12]], ptr [[TMP14]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[TMP16]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP17]], i64 1 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP20:%.*]] = load ptr, ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP20]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP23:%.*]] = load ptr, ptr [[TMP22]], align 8 +// CHECK-NEXT: [[TMP24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP23]], ptr [[TMP24]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR28:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR29:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR28]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB31:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR30]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP27]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB33:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR32]], align 8 +// CHECK-NEXT: [[TMP25:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR29]], ptr [[TMP25]], align 8 +// CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB31]], ptr [[TMP26]], align 8 +// CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB33]], ptr [[TMP27]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR35:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR34]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR36:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB37:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR36]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP26]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB39:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR38]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR35]], ptr [[START25]], align 8 +// CHECK-NEXT: [[END40:%.*]] = getelementptr inbounds nuw [[STRUCT_ENDED_BY]], ptr [[AGG_TMP_ENSURED24]], i32 0, i32 1 +// CHECK-NEXT: [[TMP28:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP43]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP29]], align 8 +// CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP43]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP28]], ptr [[TMP30]], align 8 +// CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP43]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP31]], align 8 +// CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP43]], i32 0, i32 0 +// CHECK-NEXT: [[TMP33:%.*]] = load ptr, ptr [[TMP32]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH44:%.*]] = getelementptr i32, ptr [[TMP33]], i64 1 +// CHECK-NEXT: [[TMP34:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH44]], ptr [[TMP34]], align 8 +// CHECK-NEXT: [[TMP35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP43]], i32 0, i32 1 +// CHECK-NEXT: [[TMP36:%.*]] = load ptr, ptr [[TMP35]], align 8 +// CHECK-NEXT: [[TMP37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP36]], ptr [[TMP37]], align 8 +// CHECK-NEXT: [[TMP38:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP_TMP43]], i32 0, i32 2 +// CHECK-NEXT: [[TMP39:%.*]] = load ptr, ptr [[TMP38]], align 8 +// CHECK-NEXT: [[TMP40:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP39]], ptr [[TMP40]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR45:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR46:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR45]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB48:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR47]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP42]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB50:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR49]], align 8 +// CHECK-NEXT: [[TMP41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR46]], ptr [[TMP41]], align 8 +// CHECK-NEXT: [[TMP42:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB48]], ptr [[TMP42]], align 8 +// CHECK-NEXT: [[TMP43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB50]], ptr [[TMP43]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR52:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR51]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB54:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR53]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP41]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB56:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR55]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR52]], ptr [[END40]], align 8 +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_struct_ended_by( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void test_struct_ended_by(void) { + int zero[0]; + int one; + (void) (struct ended_by) {zero, zero}; + (void) (struct ended_by) {&one + 1, &one + 1}; +} + +struct ended_by_itself { + void *__ended_by(end) end; +}; + +// CHECK-LABEL: @test_struct_ended_by_itself( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ZERO:%.*]] = alloca [0 x i32], align 4 +// CHECK-NEXT: [[ONE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[AGG_TMP_ENSURED:%.*]] = alloca [[STRUCT_ENDED_BY_ITSELF:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TMP_ENSURED8:%.*]] = alloca [[STRUCT_ENDED_BY_ITSELF]], align 8 +// CHECK-NEXT: [[AGG_TEMP10:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[TMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds nuw [[STRUCT_ENDED_BY_ITSELF]], ptr [[AGG_TMP_ENSURED]], i32 0, i32 0 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [0 x i32], ptr [[ZERO]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 0 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR3:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR2]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR6:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB7:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR6]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR3]], ptr [[END]], align 8 +// CHECK-NEXT: [[END9:%.*]] = getelementptr inbounds nuw [[STRUCT_ENDED_BY_ITSELF]], ptr [[AGG_TMP_ENSURED8]], i32 0, i32 0 +// CHECK-NEXT: [[TMP6:%.*]] = getelementptr i32, ptr [[ONE]], i64 1 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP8]], align 8 +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ONE]], ptr [[TMP9]], align 8 +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP11:%.*]] = load ptr, ptr [[TMP10]], align 8 +// CHECK-NEXT: [[BOUND_PTR_ARITH:%.*]] = getelementptr i32, ptr [[TMP11]], i64 1 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[BOUND_PTR_ARITH]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[TMP13]], align 8 +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[TMP14]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[TMP]], i32 0, i32 2 +// CHECK-NEXT: [[TMP17:%.*]] = load ptr, ptr [[TMP16]], align 8 +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP17]], ptr [[TMP18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8 +// CHECK-NEXT: [[TMP19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR13]], ptr [[TMP19]], align 8 +// CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_UB15]], ptr [[TMP20]], align 8 +// CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[WIDE_PTR_LB17]], ptr [[TMP21]], align 8 +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR19:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR18]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR20:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB21:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR20]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP10]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB23:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR22]], align 8 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR19]], ptr [[END9]], align 8 +// CHECK-NEXT: ret void +// +// CHECK-O2-LABEL: @test_struct_ended_by_itself( +// CHECK-O2-NEXT: entry: +// CHECK-O2-NEXT: ret void +// +void test_struct_ended_by_itself(void) { + int zero[0]; + int one; + (void) (struct ended_by_itself) {zero}; + (void) (struct ended_by_itself) {&one + 1}; +} diff --git a/clang/test/BoundsSafety/Driver/asm_is_supported.S b/clang/test/BoundsSafety/Driver/asm_is_supported.S new file mode 100644 index 0000000000000..337a77233eae1 --- /dev/null +++ b/clang/test/BoundsSafety/Driver/asm_is_supported.S @@ -0,0 +1,4 @@ + +// Making sure the Frontend accepts the -fbounds-safety flags in assembly mode - it would otherwise produce error: unknown argument '...' + +// RUN: %clang -fsyntax-only -fbounds-safety %s diff --git a/clang/test/BoundsSafety/Driver/bounds-safety-adoption-mode.c b/clang/test/BoundsSafety/Driver/bounds-safety-adoption-mode.c new file mode 100644 index 0000000000000..6409bcc8eb23c --- /dev/null +++ b/clang/test/BoundsSafety/Driver/bounds-safety-adoption-mode.c @@ -0,0 +1,32 @@ + + +// ============================================================================= +// Adoption mode off +// ============================================================================= + +// Check adoption mode is off by default +// RUN: %clang -fbounds-safety -c %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=NO-ADOPT-CHECK %s + +// Check adoption mode is off if explicitly requested +// RUN: %clang -fbounds-safety -fno-bounds-safety-adoption-mode -c %s -### \ +// RUN: 2>&1 | FileCheck --check-prefix=NO-ADOPT-CHECK %s + +// NO-ADOPT-CHECK: -fbounds-safety +// NOT-ADOPT-CHECK-NOT: -fbounds-safety-adoption-mode + +// ============================================================================= +// Adoption mode on +// ============================================================================= + +// Check driver passes flag when adoption mode explicitly requested +// RUN: %clang -fbounds-safety -fbounds-safety-adoption-mode -c %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=ADOPT-CHECK %s + +// Check -fno- followed by -f causes -f to win +// RUN: %clang -fbounds-safety -fno-bounds-safety-adoption-mode \ +// RUN: -fbounds-safety-adoption-mode -c %s -### 2>&1 | \ +// RUN: FileCheck --check-prefix=ADOPT-CHECK %s + +// ADOPT-CHECK: -fbounds-safety +// ADOPT-CHECK: -fbounds-safety-adoption-mode diff --git a/clang/test/BoundsSafety/Driver/bounds-safety-attributes.c b/clang/test/BoundsSafety/Driver/bounds-safety-attributes.c new file mode 100644 index 0000000000000..725a3f8ac368c --- /dev/null +++ b/clang/test/BoundsSafety/Driver/bounds-safety-attributes.c @@ -0,0 +1,15 @@ + + +// ALL-NOT: unknown argument + +// RUN: %clang -c %s -### 2>&1 | not grep fexperimental-bounds-safety-attributes + +// RUN: %clang -fexperimental-bounds-safety-attributes -c %s -### 2>&1 | not grep fbounds-safety + +// RUN: %clang -fexperimental-bounds-safety-attributes -c %s -### 2>&1 | FileCheck -check-prefixes ALL,T0 %s +// T0: -fexperimental-bounds-safety-attributes + +// RUN: %clang -fbounds-safety -fexperimental-bounds-safety-attributes -c %s -### 2>&1 | FileCheck -check-prefixes ALL,T1 %s +// T1: -fexperimental-bounds-safety-attributes + +// RUN: %clang -fexperimental-bounds-safety-attributes -fno-experimental-bounds-safety-attributes -c %s -### 2>&1 | not grep fexperimental-bounds-safety-attributes diff --git a/clang/test/BoundsSafety/Driver/bounds-safety-bringup-missing-checks.c b/clang/test/BoundsSafety/Driver/bounds-safety-bringup-missing-checks.c new file mode 100644 index 0000000000000..801e76948ea92 --- /dev/null +++ b/clang/test/BoundsSafety/Driver/bounds-safety-bringup-missing-checks.c @@ -0,0 +1,101 @@ + + +// RUN: %clang -c %s -### 2>&1 | FileCheck %s --check-prefixes=EMPTY +// RUN: %clang -fbounds-safety -c %s -### 2>&1 | FileCheck %s --check-prefixes=EMPTY + +// EMPTY-NOT: -fbounds-safety-bringup-missing-checks +// EMPTY-NOT: -fno-bounds-safety-bringup-missing-checks +// EMPTY-NOT: -fbounds-safety-bringup-missing-checks=all +// EMPTY-NOT: -fno-bounds-safety-bringup-missing-checks=all +// EMPTY-NOT: -fbounds-safety-bringup-missing-checks=batch_0 +// EMPTY-NOT: -fno-bounds-safety-bringup-missing-checks=batch_0 + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks -c %s -### 2>&1 | FileCheck %s --check-prefix=DISABLED +// DISABLED: -fno-bounds-safety-bringup-missing-checks=all +// DISABLED-NOT: -fbounds-safety-bringup-missing-checks=all + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks -c %s -### 2>&1 | FileCheck %s --check-prefix=ENABLED +// ENABLED: -fbounds-safety-bringup-missing-checks=all +// ENABLED-NOT: -fno-bounds-safety-bringup-missing-checks=all + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks -fno-bounds-safety-bringup-missing-checks -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_NEG +// POS_NEG: -fbounds-safety-bringup-missing-checks=all +// POS_NEG: -fno-bounds-safety-bringup-missing-checks=all + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks -fbounds-safety-bringup-missing-checks -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_POS +// NEG_POS: -fno-bounds-safety-bringup-missing-checks=all +// NEG_POS: -fbounds-safety-bringup-missing-checks=all + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=access_size -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_ACCESS +// POS_ACCESS: -fbounds-safety-bringup-missing-checks=access_size + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=indirect_count_update -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_INDIRECT +// POS_INDIRECT: -fbounds-safety-bringup-missing-checks=indirect_count_update + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=return_size -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_RETURN +// POS_RETURN: -fbounds-safety-bringup-missing-checks=return_size + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=ended_by_lower_bound -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_ENDED_BY_LOWER +// POS_ENDED_BY_LOWER: -fbounds-safety-bringup-missing-checks=ended_by_lower_bound + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_COMPOUND_LITERAL_INIT +// POS_COMPOUND_LITERAL_INIT: -fbounds-safety-bringup-missing-checks=compound_literal_init + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=libc_attributes -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_LIBC_ATTRIBUTES +// POS_LIBC_ATTRIBUTES: -fbounds-safety-bringup-missing-checks=libc_attributes + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=all -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_ALL +// POS_ALL: -fbounds-safety-bringup-missing-checks=all + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=batch_0 -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_batch_0 +// POS_batch_0: -fbounds-safety-bringup-missing-checks=batch_0 + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=access_size -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_ACCESS +// NEG_ACCESS: -fno-bounds-safety-bringup-missing-checks=access_size + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=indirect_count_update -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_INDIRECT +// NEG_INDIRECT: -fno-bounds-safety-bringup-missing-checks=indirect_count_update + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=return_size -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_RETURN +// NEG_RETURN: -fno-bounds-safety-bringup-missing-checks=return_size + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_ENDED_BY_LOWER +// NEG_ENDED_BY_LOWER: -fno-bounds-safety-bringup-missing-checks=ended_by_lower_bound + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=compound_literal_init -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_COMPOUND_LITERAL_INIT +// NEG_COMPOUND_LITERAL_INIT: -fno-bounds-safety-bringup-missing-checks=compound_literal_init + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=libc_attributes -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_LIBC_ATTRIBUTES +// NEG_LIBC_ATTRIBUTES: -fno-bounds-safety-bringup-missing-checks=libc_attributes + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_ALL +// NEG_ALL: -fno-bounds-safety-bringup-missing-checks=all + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=batch_0 -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_batch_0 +// NEG_batch_0: -fno-bounds-safety-bringup-missing-checks=batch_0 + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all -c %s -### 2>&1 | FileCheck %s --check-prefix=POS_COMMA +// POS_COMMA: -fbounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all -c %s -### 2>&1 | FileCheck %s --check-prefix=NEG_COMMA +// NEG_COMMA: -fno-bounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all + +// RUN: %clang -fbounds-safety -fbounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all -fno-bounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all -c %s -### 2>&1 | FileCheck %s --check-prefixes=POS_NEG_COMMA +// POS_NEG_COMMA: -fbounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all +// POS_NEG_COMMA: -fno-bounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all + +// RUN: %clang -fbounds-safety -fno-bounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all -fbounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all -c %s -### 2>&1 | FileCheck %s --check-prefixes=NEG_POS_COMMA +// NEG_POS_COMMA: -fno-bounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all +// NEG_POS_COMMA: -fbounds-safety-bringup-missing-checks=access_size,indirect_count_update,return_size,ended_by_lower_bound,compound_literal_init,libc_attributes,all + +// RUN: %clang -fno-bounds-safety-bringup-missing-checks -c %s -### 2>&1 | FileCheck %s --check-prefixes=UNUSED_NEG +// UNUSED_NEG: warning: argument unused during compilation: '-fno-bounds-safety-bringup-missing-checks' + +// RUN: %clang -fbounds-safety-bringup-missing-checks -c %s -### 2>&1 | FileCheck %s --check-prefixes=UNUSED_POS +// UNUSED_POS: warning: argument unused during compilation: '-fbounds-safety-bringup-missing-checks' + +// RUN: %clang -fno-bounds-safety-bringup-missing-checks=batch_0 -c %s -### 2>&1 | FileCheck %s --check-prefixes=UNUSED_NEG_BATCH_0 +// UNUSED_NEG_BATCH_0: warning: argument unused during compilation: '-fno-bounds-safety-bringup-missing-checks=batch_0' + +// RUN: %clang -fbounds-safety-bringup-missing-checks=batch_0 -c %s -### 2>&1 | FileCheck %s --check-prefixes=UNUSED_POS_BATCH_0 +// UNUSED_POS_BATCH_0: warning: argument unused during compilation: '-fbounds-safety-bringup-missing-checks=batch_0' diff --git a/clang/test/BoundsSafety/Driver/cmdl_opts.c b/clang/test/BoundsSafety/Driver/cmdl_opts.c new file mode 100644 index 0000000000000..57ad53b511fde --- /dev/null +++ b/clang/test/BoundsSafety/Driver/cmdl_opts.c @@ -0,0 +1,8 @@ + +// Making sure the Frontend accepts the -fbounds-safety flags - it would otherwise produce error: unknown argument '...' + +// RUN: %clang -fbounds-safety -fsyntax-only %s + +// RUN: %clang -fbounds-safety -fbounds-attributes-cxx-experimental -fsyntax-only %s + +// RUN: %clang -fbounds-safety -fbounds-attributes-objc-experimental -fsyntax-only %s diff --git a/clang/test/BoundsSafety/Driver/driver.c b/clang/test/BoundsSafety/Driver/driver.c new file mode 100644 index 0000000000000..3bb1d1cfff11d --- /dev/null +++ b/clang/test/BoundsSafety/Driver/driver.c @@ -0,0 +1,59 @@ +// ALL-NOT: unknown argument + +// Warning: careful to not grep some directory in the buid +// RUN: %clang -c %s -### 2>&1 | not grep fbounds-safety + +// RUN: %clang -fbounds-safety -### %s 2>&1 | FileCheck -check-prefixes ALL,T0 %s +// T0: -fbounds-safety +// T0: -enable-constraint-elimination + +// RUN: %clang -fbounds-safety -fno-bounds-safety -c %s -### 2>&1 | not grep -e fbounds-safety -e enable-constraint-elimination + +// RUN: %clang -fno-bounds-safety -fbounds-safety -c %s -### 2>&1 | FileCheck -check-prefixes ALL,T4 %s +// T4: -fbounds-safety +// T4: -enable-constraint-elimination + +// RUN: %clang -fbounds-attributes -c %s -### 2>&1 | not grep fbounds-attributes +// RUN: %clang -fbounds-attributes -c %s -### 2>&1 | FileCheck -check-prefixes ALL,T6 %s +// T6: -fbounds-safety +// T6: -enable-constraint-elimination + +// RUN: %clang -fbounds-attributes -fno-bounds-attributes -c %s -### 2>&1 | not grep -e fbounds-safety -e enable-constraint-elimination +// RUN: %clang -fbounds-attributes -fno-bounds-attributes -c %s -### 2>&1 | not grep -e fbounds-attributes -e enable-constraint-elimination +// RUN: %clang -fbounds-attributes -fno-bounds-attributes -c %s -### 2>&1 | not grep -e fbounds-safety -e enable-constraint-elimination + + +// RUN: %clang -fbounds-safety -mllvm -enable-constraint-elimination -### %s 2>&1 | grep enable-constraint-elimination -o | wc -l | FileCheck -check-prefixes ALL,T7 %s +// T7: 1 + +// RUN: %clang -fbounds-safety -mllvm -enable-constraint-elimination=false -### %s 2>&1 | grep enable-constraint-elimination -o | wc -l | FileCheck -check-prefixes ALL,T8 %s +// T8: 1 + +// RUN: %clang -fbounds-safety -mllvm -enable-constraint-elimination=false -### %s 2>&1 | FileCheck -check-prefixes ALL,T9 %s +// T9: -enable-constraint-elimination=false + +// RUN: %clang -fbounds-safety -mllvm -enable-constraint-elimination -mllvm -enable-constraint-elimination=false -### %s 2>&1 | grep enable-constraint-elimination -o | wc -l | FileCheck -check-prefixes ALL,T10 %s +// T10: 2 + +// RUN: %clang -c %s -### 2>&1 | not grep fbounds-attributes-cxx-experimental +// RUN: %clang -fbounds-safety -c %s -### 2>&1 | not grep fbounds-attributes-cxx-experimental + +// RUN: %clang -fbounds-safety -fbounds-attributes-cxx-experimental -### %s 2>&1 | FileCheck -check-prefixes ALL,T11 %s +// T11: -fbounds-attributes-cxx-experimental + +// RUN: %clang -fbounds-safety -fbounds-attributes-cxx-experimental -fno-bounds-attributes-cxx-experimental -c %s -### 2>&1 | not grep fbounds-attributes-cxx-experimental + +// RUN: %clang -c %s -### 2>&1 | not grep fbounds-attributes-objc-experimental +// RUN: %clang -fbounds-safety -c %s -### 2>&1 | not grep fbounds-attributes-objc-experimental + +// RUN: %clang -fbounds-safety -fbounds-attributes-objc-experimental -### %s 2>&1 | FileCheck -check-prefixes ALL,T12 %s +// T12: -fbounds-attributes-objc-experimental + +// RUN: %clang -fbounds-safety -fbounds-attributes-objc-experimental -fno-bounds-attributes-objc-experimental -c %s -### 2>&1 | not grep fbounds-attributes-objc-experimental + +// RUN: %clang -fbounds-safety -c %s -### 2>&1 | FileCheck -check-prefixes ALL,T13 %s +// T13: -fbounds-safety +// T13: -enable-constraint-elimination + +// RUN: %clang -fbounds-safety -fno-bounds-safety-relaxed-system-headers -c %s -### 2>&1 | FileCheck -check-prefixes ALL,T14 %s +// T14: -fno-bounds-safety-relaxed-system-headers diff --git a/clang/test/BoundsSafety/Driver/libc_staged_bounds_safety_attributes_macro.c b/clang/test/BoundsSafety/Driver/libc_staged_bounds_safety_attributes_macro.c new file mode 100644 index 0000000000000..fce161eb6c876 --- /dev/null +++ b/clang/test/BoundsSafety/Driver/libc_staged_bounds_safety_attributes_macro.c @@ -0,0 +1,93 @@ +// ============================================================================= +// Supported target triples +// ============================================================================= + +// MACRO: -D__LIBC_STAGED_BOUNDS_SAFETY_ATTRIBUTES +// NO_MACRO-NOT: -D__LIBC_STAGED_BOUNDS_SAFETY_ATTRIBUTES + +// ios +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-ios \ +// RUN: -fno-bounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NO_MACRO %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-ios \ +// RUN: -fbounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=MACRO %s + +// macos +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-macos \ +// RUN: -fno-bounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NO_MACRO %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-macos \ +// RUN: -fbounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=MACRO %s + +// macos (legacy `darwin` os name in triple) +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-darwin \ +// RUN: -fno-bounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NO_MACRO %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-darwin \ +// RUN: -fbounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=MACRO %s + +// watchos +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-watchos \ +// RUN: -fno-bounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NO_MACRO %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-watchos \ +// RUN: -fbounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=MACRO %s + +// tvos +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-tvos \ +// RUN: -fno-bounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NO_MACRO %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-tvos \ +// RUN: -fbounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=MACRO %s + +// ============================================================================= +// Check option is included in all and option variant without `=all` suffix +// ============================================================================= + +// ios +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-ios \ +// RUN: -fno-bounds-safety-bringup-missing-checks=all \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NO_MACRO %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-ios \ +// RUN: -fno-bounds-safety-bringup-missing-checks \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NO_MACRO %s + +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-ios \ +// RUN: -fbounds-safety-bringup-missing-checks=all \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=MACRO %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-ios \ +// RUN: -fbounds-safety-bringup-missing-checks \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=MACRO %s + +// ============================================================================= +// Unsupported target triples +// ============================================================================= + +// linux +// RUN: %clang -Wno-incompatible-sysroot -target x86_64-unknown-linux \ +// RUN: -fno-bounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NO_MACRO %s +// RUN: %clang -Wno-incompatible-sysroot -target x86_64-unknown-linux \ +// RUN: -fbounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -### -fbounds-safety %s 2>&1 \ +// RUN: | FileCheck --check-prefix=NO_MACRO %s diff --git a/clang/test/BoundsSafety/Driver/only_c_is_supported.c b/clang/test/BoundsSafety/Driver/only_c_is_supported.c new file mode 100644 index 0000000000000..09219a91901d8 --- /dev/null +++ b/clang/test/BoundsSafety/Driver/only_c_is_supported.c @@ -0,0 +1,8 @@ + +// RUN: not %clang -fbounds-safety -x c++ %s 2>&1 | FileCheck %s + +// RUN: not %clang -fbounds-safety -x objective-c %s 2>&1 | FileCheck %s + +// RUN: not %clang -fbounds-safety -x objective-c++ %s 2>&1 | FileCheck %s + +// CHECK: error: -fbounds-safety is supported only for C language diff --git a/clang/test/BoundsSafety/Driver/trap-function-may-return.c b/clang/test/BoundsSafety/Driver/trap-function-may-return.c new file mode 100644 index 0000000000000..262662310ee33 --- /dev/null +++ b/clang/test/BoundsSafety/Driver/trap-function-may-return.c @@ -0,0 +1,13 @@ + + +// RUN: %clang -ftrap-function=foo -ftrap-function-returns -### %s 2>&1 | FileCheck -check-prefix SHOULD-BE-THERE %s +// RUN: %clang -ftrap-function=foo -fno-trap-function-returns -### %s 2>&1 | FileCheck -check-prefix SHOULD %s + +// RUN: %clang -ftrap-function=foo -fno-trap-function-returns -ftrap-function-returns -### %s 2>&1 | FileCheck -check-prefix SHOULD-BE-THERE %s +// RUN: %clang -ftrap-function=foo -ftrap-function-returns -fno-trap-function-returns -### %s 2>&1 | FileCheck -check-prefix SHOULD %s + +// RUN: not %clang -fsyntax-only -ftrap-function-returns %s 2>&1 > /dev/null +// RUN: %clang -fsyntax-only -fno-trap-function-returns %s 2>&1 > /dev/null + +// SHOULD-BE-THERE: -ftrap-function-returns +// SHOULD-NOT: -ftrap-function-return diff --git a/clang/test/BoundsSafety/FixIt/autobound-pointers.c b/clang/test/BoundsSafety/FixIt/autobound-pointers.c new file mode 100644 index 0000000000000..18448297769f1 --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/autobound-pointers.c @@ -0,0 +1,162 @@ + +// RUN: cp %s %t +// RUN: not %clang_cc1 -fbounds-safety -fdiagnostics-parseable-fixits -fixit -fix-what-you-can %t > %t.cc_out 2> %t.cc_out +// RUN: grep -v FIXIT-CHECK %t | FileCheck --check-prefix=FIXIT-CHECK %s +// RUN: FileCheck --check-prefix=DPF-CHECK %s --input-file=%t.cc_out + +struct foo; +#define FOOP struct foo * +typedef struct foo *foop; + +void take_single(struct foo **); + +void no_fixits(void) { + // CHECK-NOT: fix-it:"{{.+}}autobound-pointers.c":{12: + // FIXIT-CHECK: struct foo *no_fixit; + struct foo *no_fixit; // No Fix + take_single(&no_fixit); +} + +#include + +void take_indexable(struct foo *__indexable *); +void take_bidi_indexable(struct foo *__bidi_indexable *); +void take_unsafe_indexable(struct foo *__unsafe_indexable *); + +void fixits(void) { + // FIXIT-CHECK: struct foo *__single t_single; + struct foo *t_single; // Fix + // FIXIT-CHECK: struct foo *__indexable t_idx; + struct foo *t_idx; // Fix + // FIXIT-CHECK: struct foo *t_bidi; + struct foo *t_bidi; // No Fix + // FIXIT-CHECK: struct foo *__unsafe_indexable t_unsafe; + struct foo *t_unsafe; + take_single(&t_single); + take_indexable(&t_idx); + take_bidi_indexable(&t_bidi); + take_unsafe_indexable(&t_unsafe); + + // CHECK: fix-it:"{{.+}}autobound-pointers.c":{37:10-37:10}:"__single " + // CHECK: fix-it:"{{.+}}autobound-pointers.c":{37:10-37:10}:"__indexable " + // CHECK-NOT: fix-it:"{{.+}}autobound-pointers.c":{37:10-37:10}:"__bidi_indexable " + // CHECK: fix-it:"{{.+}}autobound-pointers.c":{37:10-37:10}:"__unsafe_indexable " + // FIXIT-CHECK: FOOP __single t_single_macro; + FOOP t_single_macro; // Fix + // FIXIT-CHECK: FOOP __indexable t_idx_macro; + FOOP t_idx_macro; // Fix + // FIXIT-CHECK: FOOP t_bidi_macro; + FOOP t_bidi_macro; // No Fix + // FIXIT-CHECK: FOOP __unsafe_indexable t_unsafe_macro; + FOOP t_unsafe_macro; // Fix + take_single(&t_single_macro); + take_indexable(&t_idx_macro); + take_bidi_indexable(&t_bidi_macro); + take_unsafe_indexable(&t_unsafe_macro); + + // CHECK: fix-it:"{{.+}}autobound-pointers.c":{47:10-47:10}:"__single " + // CHECK: fix-it:"{{.+}}autobound-pointers.c":{47:10-47:10}:"__indexable " + // CHECK-NOT: fix-it:"{{.+}}autobound-pointers.c":{47:10-47:10}:"__bidi_indexable " + // CHECK: fix-it:"{{.+}}autobound-pointers.c":{47:10-47:10}:"__unsafe_indexable " + // FIXIT-CHECK: foop __single take_single_td; + foop take_single_td; // Fix + // FIXIT-CHECK: foop __indexable take_idx_td; + foop take_idx_td; // Fix + // FIXIT-CHECK: foop take_bidi_td; + foop take_bidi_td; // No Fix + // FIXIT-CHECK: foop __unsafe_indexable take_unsafe_td; + foop take_unsafe_td; + take_single(&take_single_td); + take_indexable(&take_idx_td); + take_bidi_indexable(&take_bidi_td); + take_unsafe_indexable(&take_unsafe_td); + + // FIXIT-CHECK: struct foo * __single t_single2; + struct foo * t_single2; // Fix + // FIXIT-CHECK: struct foo * __indexable t_idx2; + struct foo * t_idx2; // Fix + // FIXIT-CHECK: struct foo * t_bidi2; + struct foo * t_bidi2; // No Fix + // FIXIT-CHECK: struct foo * __unsafe_indexable t_unsafe2; + struct foo * t_unsafe2; // Fix + take_single(&t_single2); + take_indexable(&t_idx2); + take_bidi_indexable(&t_bidi2); + take_unsafe_indexable(&t_unsafe2); + + // FIXIT-CHECK: struct foo* __single t_single3; + struct foo* t_single3; // Fix + // FIXIT-CHECK: struct foo* __indexable t_idx3 + struct foo* t_idx3; // Fix + // FIXIT-CHECK: struct foo* t_bidi3; + struct foo* t_bidi3; // No Fix + // FIXIT-CHECK: struct foo* __unsafe_indexable t_unsafe3; + struct foo* t_unsafe3; // Fix + take_single(&t_single3); + take_indexable(&t_idx3); + take_bidi_indexable(&t_bidi3); + take_unsafe_indexable(&t_unsafe3); +} + +void multiple_fixes_to_decl(void) { + // "DPF" checks (diagnostics-parseable-fixits) are used to check that the other fixits + // that don't get applied automatically are emitted in someway. + + // FIXIT-CHECK: struct foo* __single mftd_single; + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+2]]:17-[[@LINE+2]]:17}:"__single " + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+1]]:17-[[@LINE+1]]:17}:"__single " + struct foo* mftd_single; // Fix + // FIXIT-CHECK: struct foo* __indexable mftd_idx; + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+2]]:17-[[@LINE+2]]:17}:"__indexable " + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+1]]:17-[[@LINE+1]]:17}:"__indexable " + struct foo* mftd_idx; // Fix + // FIXIT-CHECK: struct foo* mftd_bidi; + struct foo* mftd_bidi; // No Fix + // FIXIT-CHECK: struct foo* __unsafe_indexable mftd_unsafe; + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+2]]:17-[[@LINE+2]]:17}:"__unsafe_indexable " + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+1]]:17-[[@LINE+1]]:17}:"__unsafe_indexable " + struct foo* mftd_unsafe; // Fix + // FIXIT-CHECK: struct foo* __single mftd_mixed; + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+3]]:17-[[@LINE+3]]:17}:"__single " + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+2]]:17-[[@LINE+2]]:17}:"__indexable " + // DPF-CHECK: fix-it:"{{.+}}autobound-pointers.c.tmp":{[[@LINE+1]]:17-[[@LINE+1]]:17}:"__unsafe_indexable " + struct foo* mftd_mixed; // Fix + + take_single(&mftd_single); + take_single(&mftd_single); + + take_indexable(&mftd_idx); + take_indexable(&mftd_idx); + + take_bidi_indexable(&mftd_bidi); + take_bidi_indexable(&mftd_bidi); + + take_unsafe_indexable(&mftd_unsafe); + take_unsafe_indexable(&mftd_unsafe); + + // In this case the first call is used to generate the fix-it that + // is automatically. + take_single(&mftd_mixed); + take_indexable(&mftd_mixed); + take_bidi_indexable(&mftd_mixed); + take_unsafe_indexable(&mftd_mixed); +} + +void take_counted_by(struct foo *__sized_by(*size) *, int *size); + +struct foo *glob; + +void no_fixits_2(void) { + // FIXIT-CHECK: struct foo *__bidi_indexable i; + struct foo *__bidi_indexable i; // No Fix + take_single(&i); + + struct foo *j; + int size = 0; + take_counted_by(&j, &size); + + int *k; + take_single(&k); + + struct foo *__unsafe_indexable *l = &glob; +} diff --git a/clang/test/BoundsSafety/FixIt/const_array_to_terminated_by_null.c b/clang/test/BoundsSafety/FixIt/const_array_to_terminated_by_null.c new file mode 100644 index 0000000000000..0681d252ff492 --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/const_array_to_terminated_by_null.c @@ -0,0 +1,219 @@ + +// RUN: cp %s %t +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits -verify %t +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %t > %t.cc_out 2> %t.cc_out +// RUN: FileCheck %s --input-file=%t.cc_out + +#include + +// expected-note@+1 10{{passing argument to parameter 'path' here}} +void method_with_single_const_param(const char *path); + +// expected-note@+1 10{{passing argument to parameter 'path' here}} +void method_with_single_null_terminated_param(char * __null_terminated path); + +void test_single_invocation_local_variable() { + + // expected-note@+2{{consider adding '__null_terminated' to 'array_of_chars'}} + // CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:23-[[@LINE+1]]:23}:"__null_terminated" + char array_of_chars[] = "foo"; + + // CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"const " + // expected-note@+1{{consider adding 'const' to 'array_of_chars2'}} + char array_of_chars2[] = "foo"; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[4]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars); + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[4]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars2); +} + +// expected-note@+2{{consider adding '__null_terminated' to 'array_of_chars'}} +// CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:21-[[@LINE+1]]:21}:"__null_terminated" +char array_of_chars[] = "foo"; + +// CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+2]]:22-[[@LINE+2]]:22}:"__null_terminated" +// expected-note@+1{{consider adding '__null_terminated' to 'array_of_chars2'}} +char array_of_chars2[] = "foo"; + +void test_single_invocation_global_variable() { + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[4]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars); + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[4]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars2); +} + +void test_explicit_const_size_arrays() { + // expected-note@+2{{consider adding '__null_terminated' to 'array_of_chars'}} + // CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:23-[[@LINE+1]]:23}:"__null_terminated " + char array_of_chars[20] = "foo"; + + // CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"const " + // expected-note@+1{{consider adding 'const' to 'array_of_chars2'}} + char array_of_chars2[20] = "foobar"; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[20]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars); + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[20]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars2); +} + +void test_initialized_local_arrays_without_null_termination() { + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:23-[[@LINE+1]]:23}:"__null_terminated " + char array_of_chars[] = {'f', 'o', 'o'}; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[3]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars); + + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char array_of_chars2[] = {'f', 'o', 'o', 'b', 'a', 'r'}; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[6]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars2); +} + +void test_initialized_local_arrays_with_null_termination() { + // expected-note@+2{{consider adding '__null_terminated' to 'array_of_chars'}} + // CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:23-[[@LINE+1]]:23}:"__null_terminated" + char array_of_chars[] = {'f', 'o', 'o', '\0'}; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[4]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars); + + // CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"const " + // expected-note@+1{{consider adding 'const' to 'array_of_chars2'}} + char array_of_chars2[] = {'f', 'o', 'o', 'b', 'a', 'r', '\0'}; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[7]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars2); + + char array_of_chars3[6][2] = {{'f'}, {'o'}, {'x', 'y'}, {'b', 'a'}, {'r', '\0'}}; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[6][2]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars3); + + char array_of_chars4[6][2] = {{'f'}, {'o'}, {'x', 'y'}, {'b', 'a'}, {'r', '\0'}}; + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[6][2]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars4); + +} + +typedef char my_array_t[4]; +// expected-note@+2{{consider adding '__null_terminated' to 'typedef_array1'}} +// CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__null_terminated " +my_array_t typedef_array1 = "foo"; + +// expected-note@+2{{consider adding '__null_terminated' to 'typedef_array2'}} +// CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__null_terminated " +my_array_t typedef_array2 = "bar"; + +void test_typedefed_arrays() { + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1{{passing 'my_array_t' (aka 'char[4]') to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(typedef_array1); + + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1{{passing 'my_array_t' (aka 'char[4]') to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(typedef_array2); + +} + +// expected-note@+2{{consider adding '__null_terminated' to 'array_of_chars123'}} +// CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:24-[[@LINE+1]]:24}:"__null_terminated " +char array_of_chars123[10]; + +// CHECK-DAG: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+2]]:24-[[@LINE+2]]:24}:"__null_terminated " +// expected-note@+1{{consider adding '__null_terminated' to 'array_of_chars345'}} +char array_of_chars345[6]; + +void test_uninitialized_global_arrays() { + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[10]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars123); + + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-error@+1 {{passing 'char[6]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars345); +} + +void test_no_fixits() { + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+2]]:3-[[@LINE+2]]:3}:"const " + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:30-[[@LINE+1]]:30}:"__null_terminated " + const char array_of_chars1[] = "foo"; + + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char array_of_chars2[__null_terminated] = "bar"; + + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char array_of_chars3[__null_terminated 20] = "bar"; + + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char array_of_chars4[3] = "foo"; + + // CHECK-NOT: fix-it:"{{.+}}const_array_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char array_of_chars5[3] = {}; + + // expected-warning@+1 {{passing 'const char[4]' to parameter of type 'char *__single __terminated_by(0)' (aka 'char *__single') discards qualifiers}} + method_with_single_null_terminated_param(array_of_chars1); + + method_with_single_const_param(array_of_chars1); + + method_with_single_null_terminated_param(array_of_chars2); + + method_with_single_const_param(array_of_chars2); + + method_with_single_null_terminated_param(array_of_chars3); + + method_with_single_const_param(array_of_chars3); + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[3]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars4); + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[3]' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(array_of_chars4); + + // expected-note@+3 {{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // expected-note@+2 {{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-error@+1 {{passing 'char[3]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(array_of_chars4); + +} diff --git a/clang/test/BoundsSafety/FixIt/fixit-function-decl.c b/clang/test/BoundsSafety/FixIt/fixit-function-decl.c new file mode 100644 index 0000000000000..cf8275c0e97da --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/fixit-function-decl.c @@ -0,0 +1,718 @@ + +// RUN: %clang_cc1 -fbounds-safety -fblocks -verify -verify-ignore-unexpected=note %s +// RUN: cp %s %t +// RUN: not %clang_cc1 -fbounds-safety -fixit -fix-what-you-can %t 2> /dev/null +// RUN: grep -v CHECK %t | FileCheck %s + +#include + +// CHECK: F0RT __bidi_indexable f0(void); +// CHECK: int *__bidi_indexable f0(void); +// expected-error@+3{{conflicting types for 'f0'}} +#define F0RT int * +F0RT f0(void); +int *__bidi_indexable f0(void); + +// CHECK: F1RT f1(void); +// CHECK: int *__bidi_indexable *f1(void); +// expected-error@+3{{conflicting types for 'f1'}} +#define F1RT int ** +F1RT f1(void); // no fixit: can't add bidi_indexable in the middle +int *__bidi_indexable *f1(void); + +// CHECK: int f2(void); +// CHECK: int *__bidi_indexable *f2(void); +// expected-error@+2{{conflicting types for 'f2'}} +int f2(void); // no fixit: can't change from non-pointer to pointer type +int *__bidi_indexable *f2(void); + +// CHECK: int *__bidi_indexable f3(void); +// CHECK: int *__bidi_indexable f3(void); +// expected-error@+2{{conflicting types for 'f3'}} +int *f3(void); +int *__bidi_indexable f3(void); + +// CHECK: f4rt __bidi_indexable f4(void); +// CHECK: int *__bidi_indexable f4(void); +// expected-error@+3{{conflicting types for 'f4'}} +typedef int *f4rt; +f4rt f4(void); +int *__bidi_indexable f4(void); + +// CHECK: f5rt f5(void); +// CHECK: int *__bidi_indexable *f5(void); +// expected-error@+3{{conflicting types for 'f5'}} +typedef int **f5rt; +f5rt f5(void); // no fixit: can't add bidi_indexable in the middle +int *__bidi_indexable *f5(void); + +// CHECK: void f6(int *b, int a); +// CHECK: void f6(int a, int *__counted_by(a) b); +// expected-error@+2{{conflicting types for 'f6'}} +void f6(int *b, int a); +void f6(int a, int *__counted_by(a) b); // no fixit: swapped arguments + +// CHECK: void f7(int *a); +// CHECK: void f7(int *__counted_by(b) a, int b); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f7(int *a); +void f7(int *__counted_by(b) a, int b); // no fixit: added argument + +// CHECK: void f8(int *a, int b, int c); +// CHECK: void f8(int *__counted_by(b) a, int b); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f8(int *a, int b, int c); +void f8(int *__counted_by(b) a, int b); // no fixit: removed argument + +// CHECK: void f9(int *__counted_by(b) a, int b); +// CHECK: void f9(int *__counted_by(b) a, int b); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f9(int *a, int b); +void f9(int *__counted_by(b) a, int b); + +// CHECK: void f10(int *a, int b); +// CHECK: void f10(int *__counted_by(hello) a, int hello); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f10(int *a, int b); +void f10(int *__counted_by(hello) a, int hello); // TODO: no fixit because variable name changed :( + +// CHECK: void f11(int *a, int b, int c); +// CHECK: void f11(int *__counted_by(hello + world) a, int hello, int world); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f11(int *a, int b, int c); +void f11(int *__counted_by(hello + world) a, int hello, int world); // TODO: no fixit because variable name changed :( + +// CHECK: void f12(int *__counted_by(b + c) a, int b, int c); +// CHECK: void f12(int *__counted_by(b + c) a, int b, int c); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f12(int *a, int b, int c); +void f12(int *__counted_by(b + c) a, int b, int c); + +// CHECK: int *__counted_by(count) f13(int count); +// CHECK: int *__counted_by(count) f13(int count); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +int *f13(int count); +int *__counted_by(count) f13(int count); + +// CHECK: void f14(int *__sized_by(b) a, int b); +// CHECK: void f14(int *__sized_by(b) a, int b); +// expected-error@+2{{conflicting '__sized_by' attribute with the previous function declaration}} +void f14(int *a, int b); +void f14(int *__sized_by(b) a, int b); + +// CHECK: void f15(int *__sized_by(b + c) a, int b, int c); +// CHECK: void f15(int *__sized_by(b + c) a, int b, int c); +// expected-error@+2{{conflicting '__sized_by' attribute with the previous function declaration}} +void f15(int *a, int b, int c); +void f15(int *__sized_by(b + c) a, int b, int c); + +// CHECK: int *__sized_by(count) f16(int count); +// CHECK: int *__sized_by(count) f16(int count); +// expected-error@+2{{conflicting '__sized_by' attribute with the previous function declaration}} +int *f16(int count); +int *__sized_by(count) f16(int count); + +// CHECK: void f17(int *__ended_by(b) a, int *b); +// CHECK: void f17(int *__ended_by(b) a, int *b); +// expected-error@+2{{conflicting '__ended_by' attribute with the previous function declaration}} +void f17(int *a, int *b); +void f17(int *__ended_by(b) a, int *b); + +// CHECK: int *f18(int *a); +// CHECK: typeof(f18) f18; +int *f18(int *a); +typeof(f18) f18; + +// CHECK: int *__counted_by(b) f19(int *__counted_by(b) a, int b); +// CHECK: typeof(f19) f20; +// CHECK: int *__counted_by(b) f20(int *__counted_by(b) a, int b); +// expected-error@+3{{conflicting '__counted_by' attribute with the previous function declaration}} +int *__counted_by(b) f19(int *__counted_by(b) a, int b); +typeof(f19) f20; +int *f20(int *a, int b); + +// CHECK: void f21(int a, int *__counted_by(a) b); +// CHECK: void f21(int a, int *__counted_by(a) b); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +void f21(int a, int *b); +void f21(int a, int *__counted_by(a) b); + +// CHECK: void f22(F0PT __bidi_indexable p); +// CHECK: void f22(int *__bidi_indexable p); +// expected-error@+3{{conflicting types for 'f22'}} +#define F0PT int * +void f22(F0PT p); +void f22(int *__bidi_indexable p); + +// CHECK: void f23(F1PT p); +// CHECK: void f23(int *__bidi_indexable *p); +// expected-error@+3{{conflicting types for 'f23'}} +#define F1PT int ** +void f23(F1PT p); // no fixit: can't add bidi_indexable in the middle +void f23(int *__bidi_indexable *p); + +// CHECK: void f24(int *__indexable p); +// CHECK: void f24(int *__indexable p); +// expected-error@+2{{conflicting types for 'f24'}} +void f24(int *p); +void f24(int *__indexable p); + +// CHECK: fptr_t f25; +// CHECK: void f25(int *__bidi_indexable p); +// expected-error@+3{{conflicting types for 'f25'}} +typedef void fptr_t(int *p); +fptr_t f25; +void f25(int *__bidi_indexable p); + +// CHECK: void f26(int *__bidi_indexable p); +// CHECK: void f26(int *__bidi_indexable p) { +// expected-error@+2{{conflicting types for 'f26'}} +void f26(int *p); +void f26(int *__bidi_indexable p) { + return; +} + +// CHECK: void f27(int *__bidi_indexable p); +// CHECK: void f27(int *__bidi_indexable p); +// expected-error@+2{{conflicting types for 'f27'}} +void f27(int *__bidi_indexable p); +void f27(int *p); + +// CHECK: int* __bidi_indexable f28(void); +// CHECK: int* __bidi_indexable f28(void); +// expected-error@+2{{conflicting types for 'f28'}} +int* __bidi_indexable f28(void); +int* f28(void); + +// CHECK: int* __counted_by(count) f29(int count); +// CHECK: int* __counted_by(count) f29(int count); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +int* __counted_by(count) f29(int count); +int* f29(int count); + +// CHECK: int f30(int* __bidi_indexable b); +// CHECK: int f30(int* __bidi_indexable b); +// expected-error@+2{{conflicting types for 'f30'}} +int f30(int* __bidi_indexable b); +int f30(int* b); + +// CHECK: int f31(int* __bidi_indexable); +// CHECK: int f31(int*__bidi_indexable); +// expected-error@+2{{conflicting types for 'f31'}} +int f31(int* __bidi_indexable); +int f31(int*); + +// CHECK: int f32(int *__bidi_indexable buffer); +// CHECK: int f32(int *__bidi_indexable buffer); +// expected-error@+2{{conflicting types for 'f32'}} +int f32(int *__bidi_indexable buffer); +int f32(int *buffer); + +// CHECK: int f33(int* __bidi_indexable buffer); +// CHECK: int f33(int* __bidi_indexable buffer); +// expected-error@+2{{conflicting types for 'f33'}} +int f33(int* __bidi_indexable buffer); +int f33(int* buffer); + +// CHECK: int f34(int *__counted_by(size) buffer, int size); +// CHECK: int f34(int *__counted_by(size) buffer, int size); +// expected-error@+2{{conflicting '__counted_by' attribute with the previous function declaration}} +int f34(int *__counted_by(size) buffer, int size); +int f34(int *buffer, int size); + +// CHECK: int f35(int *nullable __bidi_indexable foo); +// CHECK: int f35(int *nullable __bidi_indexable foo); +// expected-error@+3{{conflicting types for 'f35'}} +#define nullable _Nullable +int f35(int *nullable foo); +int f35(int *nullable __bidi_indexable foo); + +// CHECK: int f36(int *nullable __bidi_indexable foo); +// CHECK: int f36(int *__bidi_indexable nullable foo); +// expected-error@+3{{conflicting types for 'f36'}} +#define nullable _Nullable +int f36(int *nullable foo); +int f36(int *__bidi_indexable nullable foo); + +// CHECK: int f37(int *__bidi_indexable const foo); +// CHECK: int f37(int *const __bidi_indexable foo); +// expected-error@+2{{conflicting types for 'f37'}} +int f37(int *const foo); +int f37(int *const __bidi_indexable foo); + +// CHECK: int f38(int *__bidi_indexable const foo); +// CHECK: int f38(int *__bidi_indexable const foo); +// expected-error@+2{{conflicting types for 'f38'}} +int f38(int *const foo); +int f38(int *__bidi_indexable const foo); + +// CHECK: int f39(int *__bidi_indexable foo); +// CHECK: int f39(int *my_bidi foo); +// expected-error@+3{{conflicting types for 'f39'}} +#define my_bidi __bidi_indexable +int f39(int *foo); +int f39(int *my_bidi foo); + +// CHECK: int *__bidi_indexable f40(int *__indexable p); +// CHECK: int *__bidi_indexable f40(int *__indexable p); +// expected-error@+2{{conflicting types for 'f40'}} +int *f40(int *p); +int *__bidi_indexable f40(int *__indexable p); + +//============================================================================== +// _Nonnull attribute on pointers +// +// The expected application of fix-its below +// +// * Doesn't try to match mismatched `_Nonnull` attributes +// * Demonstrates several cases where fix-its fail to apply but should (rdar://116016096) +// * Demonstrates several cases where a fix-it is applied but creates broken code (rdar://116016096) +// +// The declarations below were programmatically generated from a +// configuration matrix of: +// +// * Attribute is on parameter or return type +// * Parameter name attribute is on is specified +// * Attribute appears only on first or second decl +// * Attribute name (__counted_by and __bidi_indexable were used below) +// * Attribute is on inner or outer pointer +// * _Nonnull is on the destination type for the fix +// * _Nonnull is on the source type which the fix is derived from +//============================================================================== + + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer, unsigned len); + +// This catches a crash (rdar://115575540) where the `_Nonnull` +// attribute wasn't handled. +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest(int ** __counted_by(len) buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest(int ** __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull buffer, unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_src(int ** __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_outer_ptr_nn_src(int ** buffer, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest(int * __counted_by(len)* buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest(int * __counted_by(len)* buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_src(int ** buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_fd_cb_inner_ptr_nn_src(int ** buffer, unsigned len); + +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +// CHECK: void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer); + +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest'}} +// CHECK: void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest(int ** __bidi_indexable buffer); +// CHECK: void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull __bidi_indexable buffer); +void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest(int ** __bidi_indexable buffer); +void f_on_param_named_param_attr_fd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull buffer); + +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_fd_b_outer_ptr_nn_src'}} +// CHECK: void f_on_param_named_param_attr_fd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +// CHECK: void f_on_param_named_param_attr_fd_b_outer_ptr_nn_src(int ** __bidi_indexable buffer); +void f_on_param_named_param_attr_fd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +void f_on_param_named_param_attr_fd_b_outer_ptr_nn_src(int ** buffer); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); +// CHECK: void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer); +void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); +void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest'}} +// CHECK: void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest(int * __bidi_indexable* buffer); +// CHECK: void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer); +void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest(int * __bidi_indexable* buffer); +void f_on_param_named_param_attr_fd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_fd_b_inner_ptr_nn_src'}} +// CHECK: void f_on_param_named_param_attr_fd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); +// CHECK: void f_on_param_named_param_attr_fd_b_inner_ptr_nn_src(int ** buffer); +void f_on_param_named_param_attr_fd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); +void f_on_param_named_param_attr_fd_b_inner_ptr_nn_src(int ** buffer); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest(int ** __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_dest(int ** __counted_by(len) buffer, unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_src(int ** __counted_by(len) buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_src(int ** buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len) buffer, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest(int * __counted_by(len)* buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_dest(int * __counted_by(len)* buffer, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_src(int ** buffer, unsigned len); +// CHECK: void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_src(int ** buffer, unsigned len); +void f_on_param_named_param_attr_sd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull buffer, unsigned len); + +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +// CHECK: void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer); +void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); + +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest'}} +// CHECK: void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull __bidi_indexable buffer); +// CHECK: void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest(int ** __bidi_indexable buffer); +void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull buffer); +void f_on_param_named_param_attr_sd_b_outer_ptr_nn_dest(int ** __bidi_indexable buffer); + +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_sd_b_outer_ptr_nn_src'}} +// CHECK: void f_on_param_named_param_attr_sd_b_outer_ptr_nn_src(int ** __bidi_indexable buffer); +// CHECK: void f_on_param_named_param_attr_sd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); +void f_on_param_named_param_attr_sd_b_outer_ptr_nn_src(int ** buffer); +void f_on_param_named_param_attr_sd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable buffer); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer); +// CHECK: void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); +void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull buffer); +void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest'}} +// CHECK: void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer); +// CHECK: void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest(int * __bidi_indexable* buffer); +void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull buffer); +void f_on_param_named_param_attr_sd_b_inner_ptr_nn_dest(int * __bidi_indexable* buffer); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_named_param_attr_sd_b_inner_ptr_nn_src'}} +// CHECK: void f_on_param_named_param_attr_sd_b_inner_ptr_nn_src(int ** buffer); +// CHECK: void f_on_param_named_param_attr_sd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); +void f_on_param_named_param_attr_sd_b_inner_ptr_nn_src(int ** buffer); +void f_on_param_named_param_attr_sd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull buffer); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); +// CHECK: void f_on_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull__counted_by(len) , unsigned len); +void f_on_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); +void f_on_param_attr_fd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull, unsigned len); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_fd_cb_outer_ptr_nn_dest(int ** __counted_by(len), unsigned len); +// CHECK: void f_on_param_attr_fd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull__counted_by(len) , unsigned len); +void f_on_param_attr_fd_cb_outer_ptr_nn_dest(int ** __counted_by(len), unsigned len); +void f_on_param_attr_fd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull, unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_fd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); +// CHECK: void f_on_param_attr_fd_cb_outer_ptr_nn_src(int **__counted_by(len), unsigned len); +void f_on_param_attr_fd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); +void f_on_param_attr_fd_cb_outer_ptr_nn_src(int **, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); +// CHECK: void f_on_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull, unsigned len); +void f_on_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); +void f_on_param_attr_fd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_fd_cb_inner_ptr_nn_dest(int * __counted_by(len)*, unsigned len); +// CHECK: void f_on_param_attr_fd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull, unsigned len); +void f_on_param_attr_fd_cb_inner_ptr_nn_dest(int * __counted_by(len)*, unsigned len); +void f_on_param_attr_fd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_fd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); +// CHECK: void f_on_param_attr_fd_cb_inner_ptr_nn_src(int **, unsigned len); +void f_on_param_attr_fd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); +void f_on_param_attr_fd_cb_inner_ptr_nn_src(int **, unsigned len); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_fd_b_outer_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); +// CHECK: void f_on_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull__bidi_indexable ); +void f_on_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); +void f_on_param_attr_fd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_fd_b_outer_ptr_nn_dest'}} +// CHECK: void f_on_param_attr_fd_b_outer_ptr_nn_dest(int ** __bidi_indexable); +// CHECK: void f_on_param_attr_fd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull__bidi_indexable ); +void f_on_param_attr_fd_b_outer_ptr_nn_dest(int ** __bidi_indexable); +void f_on_param_attr_fd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull); + +// expected-error@+4{{conflicting types for 'f_on_param_attr_fd_b_outer_ptr_nn_src'}} +// CHECK: void f_on_param_attr_fd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); +// CHECK: void f_on_param_attr_fd_b_outer_ptr_nn_src(int **__bidi_indexable); +void f_on_param_attr_fd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); +void f_on_param_attr_fd_b_outer_ptr_nn_src(int **); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_fd_b_inner_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); +// CHECK: void f_on_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull); +void f_on_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); +void f_on_param_attr_fd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_fd_b_inner_ptr_nn_dest'}} +// CHECK: void f_on_param_attr_fd_b_inner_ptr_nn_dest(int * __bidi_indexable*); +// CHECK: void f_on_param_attr_fd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull); +void f_on_param_attr_fd_b_inner_ptr_nn_dest(int * __bidi_indexable*); +void f_on_param_attr_fd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_fd_b_inner_ptr_nn_src'}} +// CHECK: void f_on_param_attr_fd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); +// CHECK: void f_on_param_attr_fd_b_inner_ptr_nn_src(int **); +void f_on_param_attr_fd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); +void f_on_param_attr_fd_b_inner_ptr_nn_src(int **); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull__counted_by(len) , unsigned len); +// CHECK: void f_on_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); +void f_on_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull, unsigned len); +void f_on_param_attr_sd_cb_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_sd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull__counted_by(len) , unsigned len); +// CHECK: void f_on_param_attr_sd_cb_outer_ptr_nn_dest(int ** __counted_by(len), unsigned len); +void f_on_param_attr_sd_cb_outer_ptr_nn_dest(int *_Nonnull*_Nonnull, unsigned len); +void f_on_param_attr_sd_cb_outer_ptr_nn_dest(int ** __counted_by(len), unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_sd_cb_outer_ptr_nn_src(int **__counted_by(len), unsigned len); +// CHECK: void f_on_param_attr_sd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); +void f_on_param_attr_sd_cb_outer_ptr_nn_src(int **, unsigned len); +void f_on_param_attr_sd_cb_outer_ptr_nn_src(int *_Nonnull*_Nonnull __counted_by(len), unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull, unsigned len); +// CHECK: void f_on_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); +void f_on_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull, unsigned len); +void f_on_param_attr_sd_cb_inner_ptr_nn_dest_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_sd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull, unsigned len); +// CHECK: void f_on_param_attr_sd_cb_inner_ptr_nn_dest(int * __counted_by(len)*, unsigned len); +void f_on_param_attr_sd_cb_inner_ptr_nn_dest(int *_Nonnull*_Nonnull, unsigned len); +void f_on_param_attr_sd_cb_inner_ptr_nn_dest(int * __counted_by(len)*, unsigned len); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: void f_on_param_attr_sd_cb_inner_ptr_nn_src(int **, unsigned len); +// CHECK: void f_on_param_attr_sd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); +void f_on_param_attr_sd_cb_inner_ptr_nn_src(int **, unsigned len); +void f_on_param_attr_sd_cb_inner_ptr_nn_src(int *_Nonnull __counted_by(len)*_Nonnull, unsigned len); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_sd_b_outer_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull__bidi_indexable ); +// CHECK: void f_on_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); +void f_on_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull); +void f_on_param_attr_sd_b_outer_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); + +// TODO(dliew): A fix-it applied here but it results in broken code (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_sd_b_outer_ptr_nn_dest'}} +// CHECK: void f_on_param_attr_sd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull__bidi_indexable ); +// CHECK: void f_on_param_attr_sd_b_outer_ptr_nn_dest(int ** __bidi_indexable); +void f_on_param_attr_sd_b_outer_ptr_nn_dest(int *_Nonnull*_Nonnull); +void f_on_param_attr_sd_b_outer_ptr_nn_dest(int ** __bidi_indexable); + +// expected-error@+4{{conflicting types for 'f_on_param_attr_sd_b_outer_ptr_nn_src'}} +// CHECK: void f_on_param_attr_sd_b_outer_ptr_nn_src(int **__bidi_indexable); +// CHECK: void f_on_param_attr_sd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); +void f_on_param_attr_sd_b_outer_ptr_nn_src(int **); +void f_on_param_attr_sd_b_outer_ptr_nn_src(int *_Nonnull*_Nonnull __bidi_indexable); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_sd_b_inner_ptr_nn_dest_nn_src'}} +// CHECK: void f_on_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull); +// CHECK: void f_on_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); +void f_on_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull*_Nonnull); +void f_on_param_attr_sd_b_inner_ptr_nn_dest_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_sd_b_inner_ptr_nn_dest'}} +// CHECK: void f_on_param_attr_sd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull); +// CHECK: void f_on_param_attr_sd_b_inner_ptr_nn_dest(int * __bidi_indexable*); +void f_on_param_attr_sd_b_inner_ptr_nn_dest(int *_Nonnull*_Nonnull); +void f_on_param_attr_sd_b_inner_ptr_nn_dest(int * __bidi_indexable*); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_param_attr_sd_b_inner_ptr_nn_src'}} +// CHECK: void f_on_param_attr_sd_b_inner_ptr_nn_src(int **); +// CHECK: void f_on_param_attr_sd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); +void f_on_param_attr_sd_b_inner_ptr_nn_src(int **); +void f_on_param_attr_sd_b_inner_ptr_nn_src(int *_Nonnull __bidi_indexable*_Nonnull); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_dest_nn_src(unsigned len); +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_dest_nn_src(unsigned len); +int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_dest_nn_src(unsigned len); +int *_Nonnull*_Nonnull f_on_ret_attr_fd_cb_outer_ptr_nn_dest_nn_src(unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: int ** __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_dest(unsigned len); +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_dest(unsigned len); +int ** __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_dest(unsigned len); +int *_Nonnull*_Nonnull f_on_ret_attr_fd_cb_outer_ptr_nn_dest(unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_src(unsigned len); +// CHECK: int ** __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_src(unsigned len); +int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_fd_cb_outer_ptr_nn_src(unsigned len); +int ** f_on_ret_attr_fd_cb_outer_ptr_nn_src(unsigned len); + +// expected-error@+4{{conflicting types for 'f_on_ret_attr_fd_b_outer_ptr_nn_dest_nn_src'}} +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_dest_nn_src(void); +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_dest_nn_src(void); +int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_dest_nn_src(void); +int *_Nonnull*_Nonnull f_on_ret_attr_fd_b_outer_ptr_nn_dest_nn_src(void); + +// expected-error@+4{{conflicting types for 'f_on_ret_attr_fd_b_outer_ptr_nn_dest'}} +// CHECK: int ** __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_dest(void); +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_dest(void); +int ** __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_dest(void); +int *_Nonnull*_Nonnull f_on_ret_attr_fd_b_outer_ptr_nn_dest(void); + +// expected-error@+4{{conflicting types for 'f_on_ret_attr_fd_b_outer_ptr_nn_src'}} +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_src(void); +// CHECK: int ** __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_src(void); +int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_fd_b_outer_ptr_nn_src(void); +int ** f_on_ret_attr_fd_b_outer_ptr_nn_src(void); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_dest_nn_src(unsigned len); +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_dest_nn_src(unsigned len); +int *_Nonnull*_Nonnull f_on_ret_attr_sd_cb_outer_ptr_nn_dest_nn_src(unsigned len); +int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_dest_nn_src(unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_dest(unsigned len); +// CHECK: int ** __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_dest(unsigned len); +int *_Nonnull*_Nonnull f_on_ret_attr_sd_cb_outer_ptr_nn_dest(unsigned len); +int ** __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_dest(unsigned len); + +// expected-error@+4{{conflicting '__counted_by' attribute with the previous function declaration}} +// CHECK: int ** __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_src(unsigned len); +// CHECK: int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_src(unsigned len); +int ** f_on_ret_attr_sd_cb_outer_ptr_nn_src(unsigned len); +int *_Nonnull*_Nonnull __counted_by(len) f_on_ret_attr_sd_cb_outer_ptr_nn_src(unsigned len); + +// expected-error@+4{{conflicting types for 'f_on_ret_attr_sd_b_outer_ptr_nn_dest_nn_src'}} +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_dest_nn_src(void); +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_dest_nn_src(void); +int *_Nonnull*_Nonnull f_on_ret_attr_sd_b_outer_ptr_nn_dest_nn_src(void); +int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_dest_nn_src(void); + +// expected-error@+4{{conflicting types for 'f_on_ret_attr_sd_b_outer_ptr_nn_dest'}} +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_dest(void); +// CHECK: int ** __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_dest(void); +int *_Nonnull*_Nonnull f_on_ret_attr_sd_b_outer_ptr_nn_dest(void); +int ** __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_dest(void); + +// expected-error@+4{{conflicting types for 'f_on_ret_attr_sd_b_outer_ptr_nn_src'}} +// CHECK: int ** __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_src(void); +// CHECK: int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_src(void); +int ** f_on_ret_attr_sd_b_outer_ptr_nn_src(void); +int *_Nonnull*_Nonnull __bidi_indexable f_on_ret_attr_sd_b_outer_ptr_nn_src(void); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_ret_attr_fd_b_inner_ptr_nn_dest_nn_src'}} +// CHECK: int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_dest_nn_src(void); +// CHECK: int *_Nonnull*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_dest_nn_src(void); +int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_dest_nn_src(void); +int *_Nonnull*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_dest_nn_src(void); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_ret_attr_fd_b_inner_ptr_nn_dest'}} +// CHECK: int * __bidi_indexable* f_on_ret_attr_fd_b_inner_ptr_nn_dest(void); +// CHECK: int *_Nonnull*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_dest(void); +int * __bidi_indexable* f_on_ret_attr_fd_b_inner_ptr_nn_dest(void); +int *_Nonnull*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_dest(void); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_ret_attr_fd_b_inner_ptr_nn_src'}} +// CHECK: int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_src(void); +// CHECK: int ** f_on_ret_attr_fd_b_inner_ptr_nn_src(void); +int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_fd_b_inner_ptr_nn_src(void); +int ** f_on_ret_attr_fd_b_inner_ptr_nn_src(void); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_ret_attr_sd_b_inner_ptr_nn_dest_nn_src'}} +// CHECK: int *_Nonnull*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_dest_nn_src(void); +// CHECK: int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_dest_nn_src(void); +int *_Nonnull*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_dest_nn_src(void); +int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_dest_nn_src(void); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_ret_attr_sd_b_inner_ptr_nn_dest'}} +// CHECK: int *_Nonnull*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_dest(void); +// CHECK: int * __bidi_indexable* f_on_ret_attr_sd_b_inner_ptr_nn_dest(void); +int *_Nonnull*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_dest(void); +int * __bidi_indexable* f_on_ret_attr_sd_b_inner_ptr_nn_dest(void); + +// TODO(dliew): A fix-it should be applied here (rdar://116016096). +// expected-error@+4{{conflicting types for 'f_on_ret_attr_sd_b_inner_ptr_nn_src'}} +// CHECK: int ** f_on_ret_attr_sd_b_inner_ptr_nn_src(void); +// CHECK: int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_src(void); +int ** f_on_ret_attr_sd_b_inner_ptr_nn_src(void); +int *_Nonnull __bidi_indexable*_Nonnull f_on_ret_attr_sd_b_inner_ptr_nn_src(void); diff --git a/clang/test/BoundsSafety/FixIt/null_terminated_bidi_indexable_conv.c b/clang/test/BoundsSafety/FixIt/null_terminated_bidi_indexable_conv.c new file mode 100644 index 0000000000000..87d125789248a --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/null_terminated_bidi_indexable_conv.c @@ -0,0 +1,290 @@ + +// RUN: cp %s %t +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits -verify %t +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %t > %t.cc_out 2> %t.cc_out +// RUN: FileCheck --check-prefix=DPF-CHECK %s --input-file=%t.cc_out + +#include +#include + +// expected-note@+1 2{{passing argument to parameter here}} +size_t my_strlen(const char* __null_terminated); +int my_memcmp(const void*__sized_by(n) s1, const void *__sized_by(n) s2, size_t n); + +typedef int proc_t; + +const char* cs_identity_get(proc_t proc); + +#define SIGNING_ID "signing_id" +#define SIGNING_ID_LEN (sizeof(SIGNING_ID) - 1) + +void call(proc_t proc) { + // expected-note@+2 2{{consider adding '__null_terminated' to 'signing_id'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:17-[[@LINE+1]]:17}:"__null_terminated " + const char *signing_id = NULL; + // ========================================================================= + // error: assigning to 'const char *__bidi_indexable' from incompatible type + // 'const char *__single __terminated_by(0)' (aka 'const char *__single') + // requires a linear search for the terminator; use + // '__terminated_by_to_indexable()' to perform this conversion explicitly + // + // **NOTE**: This is __null_terminated -> __bidi conversion + // + // --- + // Multiple choice fix-its (each to be emitted on a separate note) + // 1. Use builtin to perform conversion: + // ``` + // (signing_id = __null_terminated_to_indexable(cs_identity_get(proc))) + // ``` + // Note `__terminated_by_to_indexable` should be suggested instead if the thing we want to convert is a __terminated_by that's + // not a __null_terminated. + // + // 2. Add `__null_terminated` to the declaration of the local. + // + // ``` + // const char *__null_terminated signing_id = NULL; + // ``` + // + // Note: this breaks the call to `my_memcmp` which is why this a fix-it on a note (low confidence fix-it), + // rather than the top-level diagnostic (high-confidence fix-it). + // ========================================================================= + // expected-error@+7{{assigning to 'const char *__bidi_indexable' from incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:18-[[@LINE+5]]:18}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:39-[[@LINE+4]]:39}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:18-[[@LINE+2]]:18}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:39-[[@LINE+1]]:39}:")" + signing_id = cs_identity_get(proc); + // ========================================================================= + // error: passing 'const char *__bidi_indexable' to parameter of + // incompatible type 'const char *__single __terminated_by(0)' + // (aka 'const char *__single') is an unsafe operation; + // use '__unsafe_terminated_by_from_indexable()' or + // '__unsafe_forge_terminated_by()' to perform this conversion + // + // **NOTE**: This is __bidi conversion -> __null_terminated conversion + // --- + // Multiple choice fix-its (each to be emitted on a separate note) + // + // 1. Use O(N) search builtin + // + // my_strlen(__unsafe_null_terminated_from_indexable(signing_id)) + // + // 2. Use O(1) search builtin + // + // my_strlen(__unsafe_null_terminated_from_indexable(signing_id, <# pointer to null terminator #>)) + // + // 3. Add `__null_terminated` to the declaration of the local. + // + // ``` + // const char *__null_terminated signing_id = NULL; + // ``` + // ========================================================================= + // expected-error@+7{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:20-[[@LINE+5]]:20}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:30-[[@LINE+4]]:30}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:20-[[@LINE+2]]:20}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:30-[[@LINE+1]]:30}:", <# pointer to null terminator #>)" + if ((my_strlen(signing_id) == SIGNING_ID_LEN)) + return; + if (my_memcmp(signing_id, SIGNING_ID, SIGNING_ID_LEN) == 0) + return; + // expected-error@+9{{initializing 'const char *__bidi_indexable' with an expression of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+8{{consider adding '__null_terminated' to 'var_init'}} + // expected-note@+7{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+6]]:15-[[@LINE+6]]:15}:"__null_terminated " + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:26-[[@LINE+5]]:26}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:47-[[@LINE+4]]:47}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:26-[[@LINE+2]]:26}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:47-[[@LINE+1]]:47}:")" + const char *var_init = cs_identity_get(proc); +} + +const char *__bidi_indexable test_ret_null_to_bidi(proc_t proc) { + // expected-error@+7{{returning 'const char *__single __terminated_by(0)' (aka 'const char *__single') from a function with incompatible result type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:31-[[@LINE+4]]:31}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:31-[[@LINE+1]]:31}:")" + return cs_identity_get(proc); +} + +__ptrcheck_abi_assume_bidi_indexable() +// expected-note@+2{{consider adding '__null_terminated' to 'const char *' return type of 'test_ret_null_to_imp_bidi'}} +// DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:"__null_terminated " +const char *test_ret_null_to_imp_bidi(proc_t proc) { + // expected-error@+7{{returning 'const char *__single __terminated_by(0)' (aka 'const char *__single') from a function with incompatible result type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:31-[[@LINE+4]]:31}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:31-[[@LINE+1]]:31}:")" + return cs_identity_get(proc); +} + +__ptrcheck_abi_assume_single() + +const char *__null_terminated test_ret_bidi_to_null() { + // expected-note@+2{{consider adding '__null_terminated' to 'local'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:15-[[@LINE+1]]:15}:"__null_terminated " + const char *local = "bidi_local"; + // expected-error@+7{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:15-[[@LINE+4]]:15}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:15-[[@LINE+1]]:15}:", <# pointer to null terminator #>)" + return local; +} + +void test_cast_null_to_bidi(proc_t proc) { + // expected-error@+9{{initializing 'const char *__bidi_indexable' with an expression of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+8{{consider adding '__null_terminated' to 'cast_var'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+7]]:15-[[@LINE+7]]:15}:"__null_terminated " + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:26-[[@LINE+5]]:26}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:61-[[@LINE+4]]:61}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:26-[[@LINE+2]]:26}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:61-[[@LINE+1]]:61}:")" + const char *cast_var = (const char *)cs_identity_get(proc); +} + +void test_cast_bidi_to_null(const char *__bidi_indexable bidi_param) { + // expected-error@+7{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:46-[[@LINE+5]]:46}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:70-[[@LINE+4]]:70}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:46-[[@LINE+2]]:46}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:70-[[@LINE+1]]:70}:", <# pointer to null terminator #>)" + const char *__null_terminated null_local = (const char *)bidi_param; +} + +// Constant array in struct variant + +#define SIZE 4 +struct Foo { + // expected-note@+2{{consider adding '__null_terminated' to 'msg'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:20-[[@LINE+1]]:20}:"__null_terminated " + const char msg[SIZE]; + const char *msg_ptr; +}; + +void consumeFoo(struct Foo* f) { + // ========================================================================= + // error: passing 'const char[4]' to parameter of incompatible type + // 'const char *__single __terminated_by(0)' (aka 'const char *__single') + // is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or + // '__unsafe_forge_terminated_by()' to perform this conversion + // + // **NOTE**: This is __bidi conversion -> __null_terminated conversion + // --- + // + // Multiple choice fix-its (each to be emitted on a separate note) + // + // 1. Use O(N) search builtin + // + // if (my_strlen(__unsafe_null_terminated_from_indexable(f->msg))) + // + // 2. Use O(1) search builtin + // + // if (my_strlen(__unsafe_null_terminated_from_indexable(f->msg, <# pointer to null terminator #>))) + // + // 3. Add `__null_terminated` to the declaration of the constant array + // + // ``` + // struct Foo { + // const char msg[__null_terminated SIZE]; + // }; + // ``` + // ========================================================================= + // expected-error@+7{{passing 'const char[4]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:19-[[@LINE+5]]:19}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:25-[[@LINE+4]]:25}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:19-[[@LINE+2]]:19}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:25-[[@LINE+1]]:25}:", <# pointer to null terminator #>)" + if (my_strlen(f->msg)) { + // do something + } + // expected-error@+9{{initializing 'const char *__bidi_indexable' with an expression of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+8{{consider adding '__null_terminated' to 'var_init'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+7]]:17-[[@LINE+7]]:17}:"__null_terminated " + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:28-[[@LINE+5]]:28}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:38-[[@LINE+4]]:38}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:28-[[@LINE+2]]:28}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:38-[[@LINE+1]]:38}:")" + const char *var_init = f->msg_ptr; + // expected-error@+7{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:18-[[@LINE+5]]:18}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:26-[[@LINE+4]]:26}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:18-[[@LINE+2]]:18}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:26-[[@LINE+1]]:26}:", <# pointer to null terminator #>)" + f->msg_ptr = var_init; +} + +void call1(proc_t proc) { + const char *__bidi_indexable signing_id = NULL; + // expected-error@+7{{assigning to 'const char *__bidi_indexable' from incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:16-[[@LINE+5]]:16}:"__null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:37-[[@LINE+4]]:37}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:16-[[@LINE+2]]:16}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:37-[[@LINE+1]]:37}:")" + signing_id = cs_identity_get(proc); +} + +// expected-note@+1 {{passing argument to parameter here}} +void consume_null_param(const char * __null_terminated); +void test_implicit_bidi_to_null_param(void) { + // expected-note@+2{{consider adding '__null_terminated' to 'local'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:17-[[@LINE+1]]:17}:"__null_terminated " + const char* local; + // expected-error@+7{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:24-[[@LINE+5]]:24}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:31-[[@LINE+4]]:31}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:24-[[@LINE+2]]:24}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:31-[[@LINE+1]]:31}:", <# pointer to null terminator #>)" + consume_null_param((local)); +} + +// expected-note@+1 2{{passing argument to parameter here}} +void consume_long_null_param(long long int * __null_terminated); +void test_bitcast_bidi_to_null(void) { + // expected-note@+2 2{{consider adding '__null_terminated' to 'local'}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__null_terminated " + int* local; + // expected-error@+7{{passing 'long long *__bidi_indexable' to parameter of incompatible type 'long long *__single __terminated_by(0)' (aka 'long long *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:29-[[@LINE+5]]:29}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:51-[[@LINE+4]]:51}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:29-[[@LINE+2]]:29}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:51-[[@LINE+1]]:51}:", <# pointer to null terminator #>)" + consume_long_null_param((long long int*) local); // Fixit doesn't fix anything: rdar://122840377 + // expected-error@+7{{passing 'long long *__bidi_indexable' to parameter of incompatible type 'long long *__single __terminated_by(0)' (aka 'long long *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+5]]:29-[[@LINE+5]]:29}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+4]]:67-[[@LINE+4]]:67}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+2]]:29-[[@LINE+2]]:29}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK: fix-it:"{{.+}}null_terminated_bidi_indexable_conv.c.tmp":{[[@LINE+1]]:67-[[@LINE+1]]:67}:", <# pointer to null terminator #>)" + consume_long_null_param((long long int*)(long long int*) local); +} diff --git a/clang/test/BoundsSafety/FixIt/null_terminated_bidi_indexable_conv_return.c b/clang/test/BoundsSafety/FixIt/null_terminated_bidi_indexable_conv_return.c new file mode 100644 index 0000000000000..d65465df88459 --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/null_terminated_bidi_indexable_conv_return.c @@ -0,0 +1,178 @@ + +// RUN: cp %s %t +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits -verify %t +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %t > %t.cc_out 2> %t.cc_out +// RUN: FileCheck --check-prefix=DPF-CHECK %s --input-file=%t.cc_out + +#include + +typedef int proc_t; + +const char *cs_identity_get(proc_t proc); + +const char *test_ret_null_to_imp_bidi_ret_var(proc_t proc) { + // expected-error@+9{{initializing 'const char *__bidi_indexable' with an expression of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+8 2{{consider adding '__null_terminated' to 'ret'}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+7]]:15-[[@LINE+7]]:15}:"__null_terminated " + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:21-[[@LINE+5]]:21}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:42-[[@LINE+4]]:42}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:21-[[@LINE+2]]:21}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:42-[[@LINE+1]]:42}:")" + const char *ret = cs_identity_get(proc); + // expected-error@+7{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+6{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:13-[[@LINE+4]]:13}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_from_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:", <# pointer to null terminator #>)" + return ret; +} + +const char *test_ret_null_to_imp_bidi_ret_var_attributed(proc_t proc) { + const char *__null_terminated ret = cs_identity_get(proc); + return ret; +} + +const char *__bidi_indexable test_ret_null_to_imp_bidi_ret_var_attributed_explicit(proc_t proc) { + // expected-error@+9{{initializing 'const char *__bidi_indexable' with an expression of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+8{{consider adding '__null_terminated' to 'ret'}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+7]]:15-[[@LINE+7]]:15}:"__null_terminated " + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:21-[[@LINE+5]]:21}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:42-[[@LINE+4]]:42}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:21-[[@LINE+2]]:21}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:42-[[@LINE+1]]:42}:")" + const char *ret = cs_identity_get(proc); + return ret; +} + +__ptrcheck_abi_assume_bidi_indexable() + +// The DPF-CHECK-DAGs on `test_ret_null_to_bidi` test that all prototypes prior to its definition get fixits. +// DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:"__null_terminated " +const char *test_ret_null_to_bidi(proc_t proc); + +// DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:"__null_terminated " +const char *test_ret_null_to_bidi(proc_t proc); + +// expected-note@+2{{consider adding '__null_terminated' to 'const char *' return type of 'test_ret_null_to_bidi'}} +// DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:"__null_terminated " +const char *test_ret_null_to_bidi(proc_t proc) { + // expected-error@+7{{returning 'const char *__single __terminated_by(0)' (aka 'const char *__single') from a function with incompatible result type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:31-[[@LINE+4]]:31}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:31-[[@LINE+1]]:31}:")" + return cs_identity_get(proc); +} + +const char *test_assign_null_to_imp_bidi(const char *__null_terminated foo) { + const char *bar[42]; + // expected-error@+7{{assigning to 'const char *__bidi_indexable' from incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:12-[[@LINE+5]]:12}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:15-[[@LINE+4]]:15}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:12-[[@LINE+2]]:12}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:15-[[@LINE+1]]:15}:")" + bar[1] = foo; + return bar[1]; +} + +const char *test_assign_null_to_imp_bidi_annotate(const char *__null_terminated foo) { + const char *bar[42]; + bar[1] = __null_terminated_to_indexable(foo); + return bar[1]; +} + +// expected-note@+2{{consider adding '__null_terminated' to 'const char *' return type of 'test_ret_null_to_imp_bidi'}} +// DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:"__null_terminated " +const char *test_ret_null_to_imp_bidi(const char *__null_terminated foo) { + // expected-error@+7{{returning 'const char *__single __terminated_by(0)' (aka 'const char *__single') from a function with incompatible result type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:13-[[@LINE+4]]:13}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:")" + return foo; +} + +// FIXME: rdar://125936876 +const char *test_ret_null_to_bidi(proc_t proc); + +// expected-note@+2{{consider adding '__null_terminated' to 'const char *' return type of 'test_ret_null_to_imp_bidi_param'}} +// DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:"__null_terminated " +const char *test_ret_null_to_imp_bidi_param(const char *__null_terminated foo, const char *param) { + // expected-error@+7{{returning 'const char *__single __terminated_by(0)' (aka 'const char *__single') from a function with incompatible result type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:13-[[@LINE+4]]:13}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:13-[[@LINE+1]]:13}:")" + return foo; +} + +#define CC_TY const char* + +CC_TY __null_terminated return_macro(void); + +// expected-note@+2{{consider adding '__null_terminated' to 'const char *' return type of 'test_return_macro'}} +// DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:7-[[@LINE+1]]:7}:"__null_terminated " +CC_TY test_return_macro(void) { + const char *__null_terminated a = return_macro(); + // expected-error@+7{{returning 'const char *__single __terminated_by(0)' (aka 'const char *__single') from a function with incompatible result type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:10-[[@LINE+5]]:10}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:11-[[@LINE+4]]:11}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:10-[[@LINE+2]]:10}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:11-[[@LINE+1]]:11}:")" + return a; +} + +const char *test_ret_null_to_imp_bidi_no_fix(const char *__null_terminated foo) { + const char *bar[42]; + // expected-error@+7{{assigning to 'const char *__bidi_indexable' from incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+6{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+5]]:12-[[@LINE+5]]:12}:"__null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+4]]:15-[[@LINE+4]]:15}:")" + // expected-note@+3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+2]]:12-[[@LINE+2]]:12}:"__unsafe_null_terminated_to_indexable(" + // DPF-CHECK-DAG: fix-it:"{{.+}}null_terminated_bidi_indexable_conv_return.c.tmp":{[[@LINE+1]]:15-[[@LINE+1]]:15}:")" + bar[1] = foo; + return 0; +} + +int *test_imp_single_ret_imp_bidi(); + +int *test_imp_single_ret_imp_bidi() { + int *__single local; + return local; +} + +__ptrcheck_abi_assume_single() + +int *test_imp_single_ret() { + int *local; + return local; +} + +int *__bidi_indexable test_bidi_ret() { + // expected-note@+1{{pointer 'local' declared here}} + int *__single local; + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'local'}} + return local; +} + +int *test_imp_single_ret_bidi() { + int *__bidi_indexable local; + return local; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/FixIt/single_incomplete_type_to_indexable.c b/clang/test/BoundsSafety/FixIt/single_incomplete_type_to_indexable.c new file mode 100644 index 0000000000000..8bf6d2ae48905 --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/single_incomplete_type_to_indexable.c @@ -0,0 +1,822 @@ + +// RUN: cp %s %t +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fblocks -verify %t +// RUN: not %clang_cc1 -fbounds-safety -fixit -fix-what-you-can %t 2> /dev/null +// RUN: grep -v FIXIT-CHECK %t | FileCheck --check-prefix=FIXIT-CHECK %s +#include + +typedef struct F F_t; + +F_t* single_source(void); + +int void_ptr_single_sink(void* __single); + +// The MACRO_PTR_* macros should not be modifed by any FixIt +// FIXIT-CHECK: #define MACRO_PTR_UNSAFE_TY struct F* __unsafe_indexable +#define MACRO_PTR_UNSAFE_TY struct F* __unsafe_indexable +// FIXIT-CHECK: #define MACRO_PTR_IDX_TY struct F* __indexable +#define MACRO_PTR_IDX_TY struct F* __indexable +// FIXIT-CHECK: #define MACRO_PTR_BIDI_TY struct F* __bidi_indexable +#define MACRO_PTR_BIDI_TY struct F* __bidi_indexable +// FIXIT-CHECK: #define MACRO_PTR_SINGLE_TY struct F* __single +#define MACRO_PTR_SINGLE_TY struct F* __single +// FIXIT-CHECK: #define MACRO_PTR_TY struct F* +#define MACRO_PTR_TY struct F* +#define MACRO_TY struct F + +int opaque_init_assign(F_t* single_f, void* single_void, + void* __single explicit_single_void) { + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local' as '__single'}} + // expected-note@+2{{pointer 'oia_local' declared here}} + // FIXIT-CHECK: F_t* __single oia_local = single_f; + F_t* oia_local = single_f; // Fix + // FIXIT-CHECK: F_t* __single oia_local_nf = single_f; + F_t* __single oia_local_nf = single_f; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local2' as '__single'}} + // expected-note@+2{{pointer 'oia_local2' declared here}} + // FIXIT-CHECK: F_t* __single oia_local2 = single_source(); + F_t* oia_local2 = single_source(); // Fix + // FIXIT-CHECK: F_t* __single oia_local2_nf = single_source(); + F_t* __single oia_local2_nf = single_source(); // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local3' as '__single'}} + // expected-note@+2{{pointer 'oia_local3' declared here}} + // FIXIT-CHECK: MACRO_PTR_TY __single oia_local3 = single_f; + MACRO_PTR_TY oia_local3 = single_f; // Fix + // FIXIT-CHECK: MACRO_PTR_TY __single oia_local3_nf = single_f; + MACRO_PTR_TY __single oia_local3_nf = single_f; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local4' as '__single'}} + // expected-note@+2{{pointer 'oia_local4' declared here}} + // FIXIT-CHECK: MACRO_TY* __single oia_local4 = single_f; + MACRO_TY* oia_local4 = single_f; // Fix + // FIXIT-CHECK: MACRO_TY* __single oia_local4_nf = single_f; + MACRO_TY* __single oia_local4_nf = single_f; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local5' as '__single'}} + // expected-note@+2{{pointer 'oia_local5' declared here}} + // FIXIT-CHECK: void* __single oia_local5 = single_void; + void* oia_local5 = single_void; // Fix + // FIXIT-CHECK: void* __single oia_local5_nf = single_void; + void* __single oia_local5_nf = single_void; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local6' as '__single'}} + // expected-note@+2{{pointer 'oia_local6' declared here}} + // FIXIT-CHECK: void* __single oia_local6 = explicit_single_void; + void* oia_local6 = explicit_single_void; // Fix + // FIXIT-CHECK: void* __single oia_local6_nf = explicit_single_void; + void* __single oia_local6_nf = explicit_single_void; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local7' as '__single'}} + // expected-note@+2{{pointer 'oia_local7' declared here}} + // FIXIT-CHECK: void* _Nullable __single oia_local7 = explicit_single_void; + void* _Nullable oia_local7 = explicit_single_void; // Fix + + // No diagnostic + // FIXIT-CHECK: MACRO_PTR_SINGLE_TY oia_local8 = single_f; + MACRO_PTR_SINGLE_TY oia_local8 = single_f; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local9' as '__single'}} + // expected-note@+2{{pointer 'oia_local9' declared here}}} + // FIXIT-CHECK: MACRO_PTR_BIDI_TY oia_local9 = single_f; + MACRO_PTR_BIDI_TY oia_local9 = single_f; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oia_local10' as '__single'}} + // expected-note@+2{{pointer 'oia_local10' declared here}}} + // FIXIT-CHECK: MACRO_PTR_IDX_TY oia_local10 = single_f; + MACRO_PTR_IDX_TY oia_local10 = single_f; // No Fix + + // No diagnostic + // FIXIT-CHECK: MACRO_PTR_UNSAFE_TY oia_local11 = single_f; + MACRO_PTR_UNSAFE_TY oia_local11 = single_f; // No Fix +} + +int opaque_assign(F_t* imp_single, void* single_void, + void* __single explicit_single_void) { + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local' as '__single'}} + // expected-note@+2{{pointer 'oa_local' declared here}} + // FIXIT-CHECK: F_t* __single oa_local = 0; + F_t* oa_local = 0; // Fix + oa_local = imp_single; + // FIXIT-CHECK: F_t* __single oa_local_nf = 0; + F_t* __single oa_local_nf = 0; // No Fix + oa_local_nf = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local2' as '__single'}} + // expected-note@+2{{pointer 'oa_local2' declared here}} + // FIXIT-CHECK: F_t* __single oa_local2 = 0; + F_t* oa_local2 = 0; // Fix + oa_local2 = single_source(); + // FIXIT-CHECK: F_t* __single oa_local2_nf = 0; + F_t* __single oa_local2_nf = 0; // No Fix + oa_local2_nf = single_source(); + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local3' as '__single'}} + // expected-note@+2{{pointer 'oa_local3' declared here}} + // FIXIT-CHECK: MACRO_PTR_TY __single oa_local3 = 0; + MACRO_PTR_TY oa_local3 = 0; // Fix + oa_local3 = imp_single; + // FIXIT-CHECK: MACRO_PTR_TY __single oa_local3_nf = 0; + MACRO_PTR_TY __single oa_local3_nf = 0; // No Fix + oa_local3_nf = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local4' as '__single'}} + // expected-note@+2{{pointer 'oa_local4' declared here}} + // FIXIT-CHECK: MACRO_TY* __single oa_local4 = 0; + MACRO_TY* oa_local4 = 0; // Fix + oa_local4 = imp_single; + // FIXIT-CHECK: MACRO_TY* __single oa_local4_nf = 0; + MACRO_TY* __single oa_local4_nf = 0; // No Fix + oa_local4_nf = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local5' as '__single'}} + // expected-note@+2{{pointer 'oa_local5' declared here}} + // FIXIT-CHECK: void* __single oa_local5 = 0; + void* oa_local5 = 0; // Fix + oa_local5 = single_void; + // FIXIT-CHECK: void* __single oa_local5_nf = 0; + void* __single oa_local5_nf = 0; // No Fix + oa_local5_nf = single_void; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local6' as '__single'}} + // expected-note@+2{{pointer 'oa_local6' declared here}} + // FIXIT-CHECK: void* __single oa_local6 = 0; + void* oa_local6 = 0; // Fix + oa_local6 = explicit_single_void; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local7' as '__single'}} + // expected-note@+2{{pointer 'oa_local7' declared here}} + // FIXIT-CHECK: void* _Nullable __single oa_local7; + void* _Nullable oa_local7; + oa_local7 = explicit_single_void; // Fix + + // No diagnostic + // FIXIT-CHECK: MACRO_PTR_SINGLE_TY oa_local8; + MACRO_PTR_SINGLE_TY oa_local8; // No Fix + oa_local8 = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local9' as '__single'}} + // expected-note@+2{{pointer 'oa_local9' declared here}}} + // FIXIT-CHECK: MACRO_PTR_BIDI_TY oa_local9; + MACRO_PTR_BIDI_TY oa_local9; // No Fix + oa_local9 = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local10' as '__single'}} + // expected-note@+2{{pointer 'oa_local10' declared here}}} + // FIXIT-CHECK: MACRO_PTR_IDX_TY oa_local10; + MACRO_PTR_IDX_TY oa_local10; // No Fix + oa_local10 = imp_single; + + // No diagnostic + // FIXIT-CHECK: MACRO_PTR_UNSAFE_TY oa_local11; + MACRO_PTR_UNSAFE_TY oa_local11; // No Fix + oa_local11 = imp_single; + + // Test assignment with parentheses on LHS + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oa_local12' as '__single'}} + // expected-note@+2{{pointer 'oa_local12' declared here}} + // FIXIT-CHECK: F_t* __single oa_local12 = 0; + F_t* oa_local12 = 0; // Fix + (oa_local12) = imp_single; +} + +int opaque_multiple_assignments(F_t* imp_single) { + // expected-error-re@+6{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oma_local' as '__single'}} + // expected-note@+4{{pointer 'oma_local' declared here}} + // expected-error-re@+5{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oma_local' as '__single'}} + // expected-note@+2{{pointer 'oma_local' declared here}} + // FIXIT-CHECK: F_t* __single oma_local = 0; + F_t* oma_local = 0; // Fix + oma_local = imp_single; + oma_local = single_source(); + + // expected-error-re@+6{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oma_local3' as '__single'}} + // expected-note@+4{{pointer 'oma_local3' declared here}} + // expected-error-re@+5{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oma_local3' as '__single'}} + // expected-note@+2{{pointer 'oma_local3' declared here}} + // FIXIT-CHECK: MACRO_PTR_TY __single oma_local3 = 0; + MACRO_PTR_TY oma_local3 = 0; // Fix + oma_local3 = imp_single; + oma_local3 = single_source(); + + // expected-error-re@+6{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oma_local4' as '__single'}} + // expected-note@+4{{pointer 'oma_local4' declared here}} + // expected-error-re@+5{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oma_local4' as '__single'}} + // expected-note@+2{{pointer 'oma_local4' declared here}} + // FIXIT-CHECK: MACRO_TY* __single oma_local4 = 0; + MACRO_TY* oma_local4 = 0; // Fix + oma_local4 = imp_single; + oma_local4 = single_source(); +} + +int block_local_fn(void* __single param) { + int (^block_func)(void) = ^{ + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'block_local' as '__single'}} + // expected-note@+2{{pointer 'block_local' declared here}} + // FIXIT-CHECK: void* __single block_local = param; + void* block_local = param; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'block_local2' as '__single'}} + // expected-note@+2{{pointer 'block_local2' declared here}} + // FIXIT-CHECK: void* __single block_local2; + void* block_local2; + block_local2 = param; + + return void_ptr_single_sink(block_local); + }; + + return block_func(); +} + +__ptrcheck_abi_assume_bidi_indexable() +typedef struct StructWithBidiPtrOIFD { + // expected-note@+2{{pointer 'StructWithBidiPtrOIFD::oifd_bidi_ptr' declared here}} + // FIXIT-CHECK: void* __single oifd_bidi_ptr; + void* oifd_bidi_ptr; +} StructWithBidiPtrOIFD_t; + +typedef struct StructWithBidiPtrOIFD2 { + // expected-note@+2{{pointer 'StructWithBidiPtrOIFD2::oifd_bidi_ptr2' declared here}} + // FIXIT-CHECK: void* __single oifd_bidi_ptr2; + void* oifd_bidi_ptr2; +} StructWithBidiPtrOIFD2_t; + +typedef struct StructWithBidiPtrOAFD { + // expected-note@+2{{pointer 'StructWithBidiPtrOAFD::oafd_bidi_ptr' declared here}} + // FIXIT-CHECK: void* __single oafd_bidi_ptr; + void* oafd_bidi_ptr; +} StructWithBidiPtrOAFD_t; + +typedef struct StructWithBidiPtrOAFD2 { + // expected-note@+2{{pointer 'StructWithBidiPtrOAFD2::oafd_bidi_ptr2' declared here}} + // FIXIT-CHECK: void* __single oafd_bidi_ptr2; + void* oafd_bidi_ptr2; +} StructWithBidiPtrOAFD2_t; + +typedef struct StructWithBidiPtrOAFD3 { + // expected-note@+2{{pointer 'StructWithBidiPtrOAFD3::oafd_bidi_ptr3' declared here}} + // FIXIT-CHECK: void* __single oafd_bidi_ptr3; + void* oafd_bidi_ptr3; +} StructWithBidiPtrOAFD3_t; + +typedef struct StructWithBidiPtrOAFD4 { + // expected-note@+2{{pointer 'StructWithBidiPtrOAFD4::oafd_bidi_ptr4' declared here}} + // FIXIT-CHECK: void* __single oafd_bidi_ptr4; + void* oafd_bidi_ptr4; +} StructWithBidiPtrOAFD4_t; + +typedef struct StructWithBidiPtrOMAFD { + // expected-note@+3{{pointer 'StructWithBidiPtrOMAFD::omafd_bidi_ptr' declared here}} + // expected-note@+2{{pointer 'StructWithBidiPtrOMAFD::omafd_bidi_ptr' declared here}} + // FIXIT-CHECK: void* __single omafd_bidi_ptr; + void* omafd_bidi_ptr; +} StructWithBidiPtrOMAFD_t; + + + +__ptrcheck_abi_assume_indexable(); + +typedef struct StructWithIdxPtrOIFD { + // expected-note@+2{{pointer 'StructWithIdxPtrOIFD::oifd_idx_ptr' declared here}} + // FIXIT-CHECK: void* __single oifd_idx_ptr; + void* oifd_idx_ptr; +} StructWithIdxPtrOIFD_t; + +typedef struct StructWithIdxPtrOIFD2 { + // expected-note@+2{{pointer 'StructWithIdxPtrOIFD2::oifd_idx_ptr2' declared here}} + // FIXIT-CHECK: void* __single oifd_idx_ptr2; + void* oifd_idx_ptr2; +} StructWithIdxPtrOIFD2_t; + +typedef struct StructWithIdxPtrOAFD { + // expected-note@+2{{pointer 'StructWithIdxPtrOAFD::oafd_idx_ptr' declared here}} + // FIXIT-CHECK: void* __single oafd_idx_ptr; + void* oafd_idx_ptr; +} StructWithIdxPtrOAFD_t; + +typedef struct StructWithIdxPtrOAFD2 { + // expected-note@+2{{pointer 'StructWithIdxPtrOAFD2::oafd_idx_ptr2' declared here}} + // FIXIT-CHECK: void* __single oafd_idx_ptr2; + void* oafd_idx_ptr2; +} StructWithIdxPtrOAFD2_t; + +typedef struct StructWithIdxPtrOAFD3 { + // expected-note@+2{{pointer 'StructWithIdxPtrOAFD3::oafd_idx_ptr3' declared here}} + // FIXIT-CHECK: void* __single oafd_idx_ptr3; + void* oafd_idx_ptr3; +} StructWithIdxPtrOAFD3_t; + +typedef struct StructWithIdxPtrOAFD4 { + // expected-note@+2{{pointer 'StructWithIdxPtrOAFD4::oafd_idx_ptr4' declared here}} + // FIXIT-CHECK: void* __single oafd_idx_ptr4; + void* oafd_idx_ptr4; +} StructWithIdxPtrOAFD4_t; + +typedef struct StructWithIdxPtrOMAFD { + // expected-note@+2 2 {{pointer 'StructWithIdxPtrOMAFD::omafd_idx_ptr' declared here}} + // FIXIT-CHECK: void* __single omafd_idx_ptr; + void* omafd_idx_ptr; +} StructWithIdxPtrOMAFD_t; + + +__ptrcheck_abi_assume_single() + +typedef struct Nested_Bidi_OIFD { + StructWithBidiPtrOIFD2_t field; +} Nested_Bidi_OIFD_t; + +typedef struct Nested_Idx_OIFD { + StructWithIdxPtrOIFD2_t field; +} Nested_Idx_OIFD_t; + +void opaque_init_field_decl(void* __single explicit_single) { + + // expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOIFD::oifd_bidi_ptr' as '__single'}} + StructWithBidiPtrOIFD_t oifd_local = {.oifd_bidi_ptr = explicit_single }; + + // expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOIFD::oifd_idx_ptr' as '__single'}} + StructWithIdxPtrOIFD_t oifd_local2 = {.oifd_idx_ptr = explicit_single }; + + // expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOIFD2::oifd_bidi_ptr2' as '__single'}} + Nested_Bidi_OIFD_t oifd_local3 = {.field = {.oifd_bidi_ptr2 = explicit_single}}; + + // expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOIFD2::oifd_idx_ptr2' as '__single'}} + Nested_Idx_OIFD_t oifd_local4 = {. field = {.oifd_idx_ptr2 = explicit_single}}; +} + +typedef struct Nested_Bidi_OAFD { + StructWithBidiPtrOAFD3_t field; +} Nested_Bidi_OAFD_t; + +typedef struct Nested_Idx_OAFD { + StructWithIdxPtrOAFD3_t field; +} Nested_Idx_OAFD_t; + +void opaque_assign_field_decl(void* __single explicit_single, StructWithBidiPtrOAFD2_t* base_with_bidi_field, StructWithIdxPtrOAFD2_t* base_with_idx_field) { + StructWithBidiPtrOAFD_t oafd_local; + StructWithIdxPtrOAFD_t oafd_local2; + + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOAFD::oafd_bidi_ptr' as '__single'}} + oafd_local.oafd_bidi_ptr = explicit_single; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOAFD::oafd_idx_ptr' as '__single'}} + oafd_local2.oafd_idx_ptr = explicit_single; + + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOAFD2::oafd_bidi_ptr2' as '__single'}} + base_with_bidi_field->oafd_bidi_ptr2 = explicit_single; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOAFD2::oafd_idx_ptr2' as '__single'}} + base_with_idx_field->oafd_idx_ptr2 = explicit_single; + + Nested_Bidi_OAFD_t oafd_local3; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOAFD3::oafd_bidi_ptr3' as '__single'}} + oafd_local3.field.oafd_bidi_ptr3 = explicit_single; + + Nested_Idx_OAFD_t oafd_local4; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOAFD3::oafd_idx_ptr3' as '__single'}} + oafd_local4.field.oafd_idx_ptr3 = explicit_single; + + // Test assignment with parentheses on LHS + StructWithBidiPtrOAFD4_t oafd_local5; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOAFD4::oafd_bidi_ptr4' as '__single'}} + (oafd_local5.oafd_bidi_ptr4) = explicit_single; + + // Test assignment with parentheses on LHS + StructWithIdxPtrOAFD4_t oafd_local6; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOAFD4::oafd_idx_ptr4' as '__single'}} + (oafd_local6.oafd_idx_ptr4) = explicit_single; +} + +void opaque_multiple_assign_field_decl(void* __single explicit_single) { + StructWithBidiPtrOMAFD_t omafd_local; + StructWithIdxPtrOMAFD_t omafd_local2; + + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOMAFD::omafd_bidi_ptr' as '__single'}} + omafd_local.omafd_bidi_ptr = explicit_single; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithBidiPtrOMAFD::omafd_bidi_ptr' as '__single'}} + omafd_local.omafd_bidi_ptr = explicit_single; + + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOMAFD::omafd_idx_ptr' as '__single'}} + omafd_local2.omafd_idx_ptr = explicit_single; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithIdxPtrOMAFD::omafd_idx_ptr' as '__single'}} + omafd_local2.omafd_idx_ptr = explicit_single; +} + + +// This defined to be a compile time constant because it seems this is the only +// thing clang with -fbounds-safety allows. +#define SINGLE_SOURCE_GLOBAL ((F_t* __single) 0) + +// Assigning single_source() isn't legal here because it isn't a constant. +// FIXIT-CHECK: F_t* global_F_single = SINGLE_SOURCE_GLOBAL; +F_t* global_F_single = SINGLE_SOURCE_GLOBAL; // No Fix + +__ptrcheck_abi_assume_bidi_indexable(); + +// It's questionable if the FixIts for these global assignments are of value. +// Once the fix is made compilation will fail because `__single_source()` isn't +// a compile time constant. + +// expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi' as '__single'}} +// expected-note@+2{{pointer 'global_F_implicit_bidi' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi = single_source(); +F_t* global_F_implicit_bidi = single_source(); // Fix + +// expected-note@+2{{pointer 'global_F_implicit_bidi2' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi2; +F_t* global_F_implicit_bidi2; +void modify_global_implicit_bidi(F_t* __single explicit_single) { + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi2' as '__single'}} + global_F_implicit_bidi2 = explicit_single; +} + + +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi3; +// FIXIT-CHECK-NEXT: F_t* __single global_F_implicit_bidi3; +// FIXIT-CHECK-NEXT: F_t* __single global_F_implicit_bidi3; +// expected-note@+3{{pointer 'global_F_implicit_bidi3' declared here}} +F_t* global_F_implicit_bidi3; // Fix +F_t* global_F_implicit_bidi3; // Fix +F_t* global_F_implicit_bidi3; // Fix +void modify_global_implicit_bidi2(F_t* __single explicit_single) { + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi3' as '__single'}} + global_F_implicit_bidi3 = explicit_single; +} + +// expected-note@+2 3 {{pointer 'global_F_implicit_bidi4' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi4; +F_t* global_F_implicit_bidi4; // Fix +void modify_global_implicit_bidi_multiple_assign(F_t* __single explicit_single) { + // expected-error-re@+3{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi4' as '__single'}} + // expected-error-re@+3{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi4' as '__single'}} + // expected-error-re@+3{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi4' as '__single'}} + global_F_implicit_bidi4 = explicit_single; + global_F_implicit_bidi4 = explicit_single; + global_F_implicit_bidi4 = explicit_single; +} + +// This tests the case where a global gets a FixIt emitted on it and then it +// gets redeclared and then a FixIt gets emitted on the redeclaration. We have +// to be careful to not annotate the first declaration again. + +// expected-note@+2{{pointer 'global_F_implicit_bidi5' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi5; +F_t* global_F_implicit_bidi5; // First Decl, Fix + +void modify_global_implicit_bidi_pre_redeclare(F_t* __single explicit_single) { + // Fixes First Decl + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi5' as '__single'}} + global_F_implicit_bidi5 = explicit_single; +} + +// expected-note@+2{{pointer 'global_F_implicit_bidi5' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi5; +F_t* global_F_implicit_bidi5; // Second Decl, Fix + +void modify_global_implicit_bidi_post_redeclare(F_t* __single explicit_single) { + // Fixes Second Decl. Have to avoid fixing First Decl + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi5' as '__single'}} + global_F_implicit_bidi5 = explicit_single; +} + +// FIXME(dliew): rdar://115456779 +// This is a case where not all the redeclarations can be fixed +// expected-note@+2{{pointer 'global_F_implicit_bidi6' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_bidi6; +F_t* global_F_implicit_bidi6; // First Decl, Fix + +void modify_global_implicit_bidi_pre_redeclare2(F_t* __single explicit_single) { + // Fixes Second Decl. Have to avoid fixing First Decl + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_bidi6' as '__single'}} + global_F_implicit_bidi6 = explicit_single; +} + +// No note +// FIXIT-CHECK: F_t* global_F_implicit_bidi6; +F_t* global_F_implicit_bidi6; // Second Decl, No Fix + +__ptrcheck_abi_assume_indexable(); + +// expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx' as '__single'}} +// expected-note@+2{{pointer 'global_F_implicit_idx' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_idx = single_source(); +F_t* global_F_implicit_idx = single_source(); // Fix + +// expected-note@+2{{pointer 'global_F_implicit_idx2' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_idx2; +F_t* global_F_implicit_idx2; +void modify_global_implicit_idx(F_t* __single explicit_single) { + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx2' as '__single'}} + global_F_implicit_idx2 = explicit_single; +} + +// FIXIT-CHECK: F_t* __single global_F_implicit_idx3; +// FIXIT-CHECK-NEXT: F_t* __single global_F_implicit_idx3; +// FIXIT-CHECK-NEXT: F_t* __single global_F_implicit_idx3; +// expected-note@+3{{pointer 'global_F_implicit_idx3' declared here}} +F_t* global_F_implicit_idx3; // Fix +F_t* global_F_implicit_idx3; // Fix +F_t* global_F_implicit_idx3; // Fix +void modify_global_implicit_idx2(F_t* __single explicit_single) { + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx3' as '__single'}} + global_F_implicit_idx3 = explicit_single; +} + +// expected-note@+2 3 {{pointer 'global_F_implicit_idx4' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_idx4; +F_t* global_F_implicit_idx4; // Fix +void modify_global_implicit_idx_multiple_assign(F_t* __single explicit_single) { + // expected-error-re@+3{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx4' as '__single'}} + // expected-error-re@+3{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx4' as '__single'}} + // expected-error-re@+3{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx4' as '__single'}} + global_F_implicit_idx4 = explicit_single; + global_F_implicit_idx4 = explicit_single; + global_F_implicit_idx4 = explicit_single; +} + +// This tests the case where a global gets a FixIt emitted on it and then it +// gets redeclared and then a FixIt gets emitted on the redeclaration. We have +// to be careful to not annotate the first declaration again. + +// expected-note@+2{{pointer 'global_F_implicit_idx5' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_idx5; +F_t* global_F_implicit_idx5; // First Decl, Fix + +void modify_global_implicit_idx_pre_redeclare(F_t* __single explicit_single) { + // Fixes First Decl + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx5' as '__single'}} + global_F_implicit_idx5 = explicit_single; +} + +// expected-note@+2{{pointer 'global_F_implicit_idx5' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_idx5; +F_t* global_F_implicit_idx5; // Second Decl, Fix + +void modify_global_implicit_idx_post_redeclare(F_t* __single explicit_single) { + // Fixes Second Decl. Have to avoid fixing First Decl + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx5' as '__single'}} + global_F_implicit_idx5 = explicit_single; +} + +// FIXME(dliew): rdar://115456779 +// This is a case where not all the redeclarations can be fixed +// expected-note@+2{{pointer 'global_F_implicit_idx6' declared here}} +// FIXIT-CHECK: F_t* __single global_F_implicit_idx6; +F_t* global_F_implicit_idx6; // First Decl, Fix + +void modify_global_implicit_idx_pre_redeclare2(F_t* __single explicit_single) { + // Fixes Second Decl. Have to avoid fixing First Decl + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_implicit_idx6' as '__single'}} + global_F_implicit_idx6 = explicit_single; +} + +// No note +// FIXIT-CHECK: F_t* global_F_implicit_idx6; +F_t* global_F_implicit_idx6; // Second Decl, No Fix + + +__ptrcheck_abi_assume_single(); + +//============================================================================== +// FixIts on parameters +//============================================================================== + +__ptrcheck_abi_assume_bidi_indexable(); + +// expected-note@+2{{passing argument to parameter 'implicit_bidi' here}} +// FIXIT-CHECK: void implicit_bidi_sink(void* __single implicit_bidi); +void implicit_bidi_sink(void* implicit_bidi); // Fix + +// expected-note@+3{{pointer 'implicit_bidi2' declared here}} // This is emitted due to a FixIt needs to be attached to a diagnostic and we can't attach it to the error. +// expected-note@+2 2 {{passing argument to parameter 'implicit_bidi2' here}} +// FIXIT-CHECK: void implicit_bidi_sink2(void* __single implicit_bidi2); +void implicit_bidi_sink2(void* implicit_bidi2); // Fix + +// expected-note@+3{{pointer declared here}} // This is emitted due to a FixIt needs to be attached to a diagnostic and we can't attach it to the error. +// expected-note@+2 2 {{passing argument to parameter here}} +// FIXIT-CHECK: void implicit_bidi_sink3(void*__single); +void implicit_bidi_sink3(void*); // Fix + +__ptrcheck_abi_assume_single(); + +void single_opaque_arg_passed_to_implicit_bidi_param(void* __single p) { + // expected-error@+1{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'implicit_bidi' parameter '__single'}} + implicit_bidi_sink(p); +} + +void single_opaque_arg_passed_to_implicit_bidi_param2(void* __single p) { + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'implicit_bidi2' parameter '__single'}} + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'implicit_bidi2' parameter '__single'}} + implicit_bidi_sink2(p); + implicit_bidi_sink2(p); +} + +void single_opaque_arg_passed_to_implicit_bidi_param3(void* __single p) { + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the parameter '__single'}} + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the parameter '__single'}} + implicit_bidi_sink3(p); + implicit_bidi_sink3(p); +} + +__ptrcheck_abi_assume_indexable(); + +// expected-note@+2{{passing argument to parameter 'implicit_idx_param' here}} +// FIXIT-CHECK: void implicit_idx_sink(void* __single implicit_idx_param); +void implicit_idx_sink(void* implicit_idx_param); // Fix + +// expected-note@+3{{pointer 'implicit_idx_param2' declared here}} // This is emitted due to a FixIt needs to be attached to a diagnostic and we can't attach it to the error. +// expected-note@+2 2 {{passing argument to parameter 'implicit_idx_param2' here}} +// FIXIT-CHECK: void implicit_idx_sink2(void* __single implicit_idx_param2); +void implicit_idx_sink2(void* implicit_idx_param2); // Fix + +// expected-note@+3{{pointer declared here}} // This is emitted due to a FixIt needs to be attached to a diagnostic and we can't attach it to the error. +// expected-note@+2 2 {{passing argument to parameter here}} +// FIXIT-CHECK: void implicit_idx_sink3(void*__single); +void implicit_idx_sink3(void*); // Fix + +__ptrcheck_abi_assume_single(); + +void single_opaque_arg_passed_to_implicit_idx_param(void* __single p) { + // expected-error@+1{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__indexable'; consider making the 'implicit_idx_param' parameter '__single'}} + implicit_idx_sink(p); +} + +void single_opaque_arg_passed_to_implicit_idx_param2(void* __single p) { + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__indexable'; consider making the 'implicit_idx_param2' parameter '__single'}} + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__indexable'; consider making the 'implicit_idx_param2' parameter '__single'}} + implicit_idx_sink2(p); + implicit_idx_sink2(p); +} + +void single_opaque_arg_passed_to_implicit_idx_param3(void* __single p) { + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__indexable'; consider making the parameter '__single'}} + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__indexable'; consider making the parameter '__single'}} + implicit_idx_sink3(p); + implicit_idx_sink3(p); +} + +//============================================================================== +// No FixIts for array assignments/initialization +// +// The lack of FixIts here is just an implementation limitation. +// rdar://115201001 +//============================================================================== + +__ptrcheck_abi_assume_bidi_indexable(); + +// FIXIT-CHECK: void* global_opaque_array_with_init[] = { single_source(), 0}; +// expected-error-re@+1{{cannot initialize indexable pointer with type 'void *__bidi_indexable' from __single pointer to incomplete type 'F_t *__single' (aka 'struct F *__single'){{$}}}} +void* global_opaque_array_with_init[] = { single_source(), 0}; // No Fix + +// FIXIT-CHECK: void* global_opaque_array[4]; +void* global_opaque_array[4]; // No Fix +__ptrcheck_abi_assume_single(); + +void assign_global_opaque_array(void* __single explicit_single) { + // The suggestion to `consider` making the destination __single here is omitted + // expected-error-re@+1{{cannot assign to indexable pointer with type 'void *__bidi_indexable' from __single pointer to incomplete type 'void *__single'{{$}}}} + global_opaque_array[0] = explicit_single; +} + +void assign_local_opaque_array(void* __single explicit_single) { + void* aloa_array[2]; // Implicitly void* __single aloa_array[2]; + aloa_array[0] = explicit_single; // No diagnostic +} + +//============================================================================== +// No FixIts +// +// The lack of FixIts here is just an implementation limitation. +// rdar://114478465 +//============================================================================== + +typedef struct StructWithExplicitBidiPtrOIAEB { + // expected-note@+2{{pointer 'StructWithExplicitBidiPtrOIAEB::oiaeb_bidi_ptr' declared here}} + // FIXIT-CHECK: void* __bidi_indexable oiaeb_bidi_ptr; + void* __bidi_indexable oiaeb_bidi_ptr; // No fix +} StructWithExplicitBidiPtrOIAEB_t; + +typedef struct StructWithExplicitIdxPtrOIAEB { + // expected-note@+2{{pointer 'StructWithExplicitIdxPtrOIAEB::oiaeb_idx_ptr' declared here}} + // FIXIT-CHECK: void* __indexable oiaeb_idx_ptr; + void* __indexable oiaeb_idx_ptr; // No fix +} StructWithExplicitIdxPtrOIAEB_t; + +int opaque_init_assign_explicit_bidi(F_t* imp_single) { + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaeb_local1' as '__single'}} + // expected-note@+2{{pointer 'oiaeb_local1' declared here}} + // FIXIT-CHECK: F_t* __bidi_indexable oiaeb_local1 = imp_single; + F_t* __bidi_indexable oiaeb_local1 = imp_single; // No fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaeb_local2' as '__single'}} + // expected-note@+2{{pointer 'oiaeb_local2' declared here}} + // FIXIT-CHECK: F_t* __bidi_indexable oiaeb_local2 = single_source(); + F_t* __bidi_indexable oiaeb_local2 = single_source(); // No fix + + // expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithExplicitBidiPtrOIAEB::oiaeb_bidi_ptr' as '__single'}} + StructWithExplicitBidiPtrOIAEB_t oiaeb_local3 = {.oiaeb_bidi_ptr = imp_single}; + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaeb_local4' as '__single'}} + // expected-note@+2{{pointer 'oiaeb_local4' declared here}} + // FIXIT-CHECK: MACRO_PTR_TY __bidi_indexable oiaeb_local4 = imp_single; + MACRO_PTR_TY __bidi_indexable oiaeb_local4 = imp_single; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaeb_local5' as '__single'}} + // expected-note@+2{{pointer 'oiaeb_local5' declared here}} + // FIXIT-CHECK: MACRO_TY* __bidi_indexable oiaeb_local5 = imp_single; + MACRO_TY* __bidi_indexable oiaeb_local5 = imp_single; // No Fix +} + +typedef struct StructWithExplicitBidiPtrOAEB { + // expected-note@+2{{pointer 'StructWithExplicitBidiPtrOAEB::oaeb_bidi_ptr' declared here}} + // FIXIT-CHECK: void* __bidi_indexable oaeb_bidi_ptr; + void* __bidi_indexable oaeb_bidi_ptr; // No fix +} StructWithExplicitBidiPtrOAEB_t; + +typedef struct StructWithExplicitIdxPtrOAEI { + // expected-note@+2{{pointer 'StructWithExplicitIdxPtrOAEI::oaei_idx_ptr' declared here}} + // FIXIT-CHECK: void* __indexable oaei_idx_ptr; + void* __indexable oaei_idx_ptr; // No fix +} StructWithExplicitIdxPtrOAEI_t; + +int opaque_assign_explicit_bidi(F_t* imp_single) { + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oaeb_local1' as '__single'}} + // expected-note@+2{{pointer 'oaeb_local1' declared here}} + // FIXIT-CHECK: F_t* __bidi_indexable oaeb_local1 = 0; + F_t* __bidi_indexable oaeb_local1 = 0; // No Fix + oaeb_local1 = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oaeb_local2' as '__single'}} + // expected-note@+2{{pointer 'oaeb_local2' declared here}} + // FIXIT-CHECK: F_t* __bidi_indexable oaeb_local2 = 0; + F_t* __bidi_indexable oaeb_local2 = 0; // No Fix + oaeb_local2 = single_source(); + + StructWithExplicitBidiPtrOAEB_t oaeb_local3; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithExplicitBidiPtrOAEB::oaeb_bidi_ptr' as '__single'}} + oaeb_local3.oaeb_bidi_ptr = imp_single; +} + +int opaque_init_assign_explicit_idx(F_t* imp_single) { + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaei_local1' as '__single'}} + // expected-note@+2{{pointer 'oiaei_local1' declared here}} + // FIXIT-CHECK: F_t* __indexable oiaei_local1 = imp_single; + F_t* __indexable oiaei_local1 = imp_single; // No fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaei_local2' as '__single'}} + // expected-note@+2{{pointer 'oiaei_local2' declared here}} + // FIXIT-CHECK: F_t* __indexable oiaei_local2 = single_source(); + F_t* __indexable oiaei_local2 = single_source(); // No fix + + // expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithExplicitIdxPtrOIAEB::oiaeb_idx_ptr' as '__single'}} + StructWithExplicitIdxPtrOIAEB_t oiaei_local3 = {.oiaeb_idx_ptr = imp_single}; + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaei_local4' as '__single'}} + // expected-note@+2{{pointer 'oiaei_local4' declared here}} + // FIXIT-CHECK: MACRO_PTR_TY __indexable oiaei_local4 = imp_single; + MACRO_PTR_TY __indexable oiaei_local4 = imp_single; // No Fix + + // expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oiaei_local5' as '__single'}} + // expected-note@+2{{pointer 'oiaei_local5' declared here}} + // FIXIT-CHECK: MACRO_TY* __indexable oiaei_local5 = imp_single + MACRO_TY* __indexable oiaei_local5 = imp_single; // No Fix +} + +int opaque_assign_explicit_idx(F_t* imp_single) { + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oaei_local1' as '__single'}} + // expected-note@+2{{pointer 'oaei_local1' declared here}} + // FIXIT-CHECK: F_t* __indexable oaei_local1 = 0; + F_t* __indexable oaei_local1 = 0; // No Fix + oaei_local1 = imp_single; + + // expected-error-re@+4{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'oaei_local2' as '__single'}} + // expected-note@+2{{pointer 'oaei_local2' declared here}} + // FIXIT-CHECK: F_t* __indexable oaei_local2 = 0; + F_t* __indexable oaei_local2 = 0; // No Fix + oaei_local2 = single_source(); + + StructWithExplicitIdxPtrOAEI_t oaei_local3; + // expected-error-re@+1{{cannot assign to indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'StructWithExplicitIdxPtrOAEI::oaei_idx_ptr' as '__single'}} + oaei_local3.oaei_idx_ptr = imp_single; +} + +// expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_explicit_bidi' as '__single'}} +// expected-note@+2{{pointer 'global_F_explicit_bidi' declared here}} +// FIXIT-CHECK: F_t* __bidi_indexable global_F_explicit_bidi = single_source(); +F_t* __bidi_indexable global_F_explicit_bidi = single_source(); // No Fix + +// expected-error-re@+3{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type {{.+}}; consider declaring pointer 'global_F_explicit_idx' as '__single'}} +// expected-note@+2{{pointer 'global_F_explicit_idx' declared here}} +// FIXIT-CHECK: F_t* __indexable global_F_explicit_idx = single_source(); +F_t* __indexable global_F_explicit_idx = single_source(); // No Fix + +//============================================================================== +// No FixIts +//============================================================================== + +// This is cast so there's no destination variable to modify at the point the +// diagnostic is emitted. +F_t* foo3(F_t* imp_single) { + // expected-error-re@+2{{cannot cast from __single pointer to incomplete type {{.+}} to indexable pointer type {{.+}}}} + // FIXIT-CHECK: return (F_t* __bidi_indexable) imp_single; + return (F_t* __bidi_indexable) imp_single; // No Fix +} diff --git a/clang/test/BoundsSafety/FixIt/single_to_dynamic_count.c b/clang/test/BoundsSafety/FixIt/single_to_dynamic_count.c new file mode 100644 index 0000000000000..1a73f8f1267bd --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/single_to_dynamic_count.c @@ -0,0 +1,932 @@ + +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +#include +#include + +int global; + +void cb(int *__counted_by(count) p, int count); +void sb(void *__sized_by(size) p, int size); +void cb_multi(int *__counted_by(c1 - c2) p, int c1, int c2); +void cb_out(int *__counted_by(*count) p, int *count); +void cb_or_null(int *__counted_by_or_null(count) p, int count); +void sb_or_null(void *__sized_by_or_null(size) p, int size); + +// Check if the pointer argument has an explicit attribute. + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void explicit_parm(int *__single p1, int l1, int *__counted_by(l2) p2, int l2) { + cb(p1, l1); + cb(p2, l2); + + int k1 = l1; + int *__counted_by(k1) q1 = p1; + + int k2 = l2; + int *__counted_by(k1) q2 = p2; + + q1 = p1; + k1 = l1; + + q2 = p2; + k2 = l2; +} + +void explicit_struct(void) { + struct s { + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: + int *__single p1; + int l1; + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: + int *__counted_by(l2) p2; + int l2; + } s; + cb(s.p1, s.l1); + cb(s.p2, s.l2); + + int k1 = s.l1; + int *__counted_by(k1) q1 = s.p1; + + int k2 = s.l2; + int *__counted_by(k1) q2 = s.p2; + + q1 = s.p1; + k1 = s.l1; + + q2 = s.p2; + k2 = s.l2; +} + +// Check if the pointer types are compatible. + +void cb_i8(int8_t *__counted_by(len) p, int len); +void sb_i8(int8_t *__sized_by(size) p, int size); +void cb_or_null_i8(int8_t *__counted_by_or_null(len) p, int len); +void sb_or_null_i8(int8_t *__sized_by_or_null(size) p, int size); + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:34-[[@LINE+1]]:34}:"__counted_by(len) " +void cb_types_ok_passing(int8_t *p, int len) { + cb_i8(p, len); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:31-[[@LINE+1]]:31}:"__counted_by(len) " +void cb_types_ok_init(int8_t *p, int len) { + int l = len; + int8_t *__counted_by(l) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:33-[[@LINE+1]]:33}:"__counted_by(len) " +void cb_types_ok_assign(int8_t *p, int len) { + int l; + int8_t *__counted_by(l) q; + q = p; + l = len; +} + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void cb_types_mismatch(int16_t *p, int len) { + cb_i8(p, len); + + int l = len; + int8_t *__counted_by(l) q = p; + + q = p; + l = len; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:34-[[@LINE+1]]:34}:"__sized_by(size) " +void sb_types_ok_passing(int8_t *p, int size) { + sb_i8(p, size); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:31-[[@LINE+1]]:31}:"__sized_by(size) " +void sb_types_ok_init(int8_t *p, int size) { + int s = size; + int8_t *__sized_by(s) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:33-[[@LINE+1]]:33}:"__sized_by(size) " +void sb_types_ok_assign(int8_t *p, int size) { + int s; + int8_t *__sized_by(s) q; + q = p; + s = size; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:36-[[@LINE+1]]:36}:"__sized_by(size) " +void sb_types_ok2_passing(int16_t *p, int size) { + sb_i8(p, size); // size is in bytes, pointee size does not matter. +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:33-[[@LINE+1]]:33}:"__sized_by(size) " +void sb_types_ok2_init(int16_t *p, int size) { + int s = size; + int8_t *__sized_by(s) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:35-[[@LINE+1]]:35}:"__sized_by(size) " +void sb_types_ok2_assign(int16_t *p, int size) { + int s; + int8_t *__sized_by(s) q; + q = p; + s = size; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:42-[[@LINE+1]]:42}:"__counted_by_or_null(len) " +void cb_or_null_types_ok_passing(int8_t *p, int len) { + cb_or_null_i8(p, len); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:39-[[@LINE+1]]:39}:"__counted_by_or_null(len) " +void cb_or_null_types_ok_init(int8_t *p, int len) { + int l = len; + int8_t *__counted_by_or_null(l) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:41-[[@LINE+1]]:41}:"__counted_by_or_null(len) " +void cb_or_null_types_ok_assign(int8_t *p, int len) { + int l; + int8_t *__counted_by_or_null(l) q; + q = p; + l = len; +} + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void cb_or_null_types_mismatch(int16_t *p, int len) { + cb_or_null_i8(p, len); + + int l = len; + int8_t *__counted_by_or_null(l) q = p; + + q = p; + l = len; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:42-[[@LINE+1]]:42}:"__sized_by_or_null(size) " +void sb_or_null_types_ok_passing(int8_t *p, int size) { + sb_or_null_i8(p, size); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:39-[[@LINE+1]]:39}:"__sized_by_or_null(size) " +void sb_or_null_types_ok_init(int8_t *p, int size) { + int s = size; + int8_t *__sized_by_or_null(s) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:41-[[@LINE+1]]:41}:"__sized_by_or_null(size) " +void sb_or_null_types_ok_assign(int8_t *p, int size) { + int s; + int8_t *__sized_by_or_null(s) q; + + q = p; + s = size; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:44-[[@LINE+1]]:44}:"__sized_by_or_null(size) " +void sb_or_null_types_ok2_passing(int16_t *p, int size) { + sb_or_null_i8(p, size); // size is in bytes, pointee size does not matter. +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:41-[[@LINE+1]]:41}:"__sized_by_or_null(size) " +void sb_or_null_types_ok2_init(int16_t *p, int size) { + int s = size; + int8_t *__sized_by_or_null(s) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:43-[[@LINE+1]]:43}:"__sized_by_or_null(size) " +void sb_or_null_types_ok2_assign(int16_t *p, int size) { + int s; + int8_t *__sized_by_or_null(s) q; + + q = p; + s = size; +} + +// Check syntax of count expression. + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:29-[[@LINE+1]]:29}:"__counted_by(len + 2 * (len + 1) - 42) " +void syntax_ok_passing(int *p, int len) { + cb(p, len + 2 * (len + 1) - 42); +} + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:26-[[@LINE+1]]:26}:"__counted_by(len + 2 * (len + 1) - 42) " +void syntax_ok_init(int *p, int len) { + int l = len + 2 * (len + 1) - 42; + int *__counted_by(l) q = p; +} + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:28-[[@LINE+1]]:28}:"__counted_by(len + 2 * (len + 1) - 42) " +void syntax_ok_assign(int *p, int len) { + int l; + int *__counted_by(l) q; + + q = p; + l = len + 2 * (len + 1) - 42; +} + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void syntax_bad(int *p, int len) { + cb(p, len == 42 ? 42 : len + 1); // ?: operator is unsupported. + + int l = len == 42 ? 42 : len + 1; + int *__counted_by(l) q = p; + + q = p; + l = len == 42 ? 42 : len + 1; +} + +// Check if the decls in the count argument are parameters of the same function as the pointer argument. + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void decls_bad(int *p, int len) { + int local; + cb(p, local); + cb(p, global); + cb(p, len + local + global); + cb_multi(p, len, local); + cb_multi(p, global, len); + + int l1 = local; + int *__counted_by(l1) q2 = p; + q2 = p; + l1 = local; + + int l2 = global; + int *__counted_by(l2) q2 = p; + q2 = p; + l2 = global; + + int l3 = len + local + global; + int *__counted_by(l3) q3 = p; + q3 = p; + l3 = len + local + global; + + int l4_a = len; + int l4_b = local; + int *__counted_by(l4 - l4_b) q4 = p; + q4 = p; + len4_a = len; + len4_b = local; + + int l5_a = global; + int l5_b = len; + int *__counted_by(l5_a - l5_b) q5 = p; + q5 = p; + l5_a = global; + l5_b = len; +} + +// Check if the decls in the count argument are parameters of the same function as the pointer argument. + +void member_base_ok_passing(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(len) " + int *p; + int len; + } f; + cb(f.p, f.len); +} + +void member_base_ok_init(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(len) " + int *p; + int len; + } f; + int l = f.len; + int *__counted_by(l) q = f.p; +} + +void member_base_ok_assign(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(len) " + int *p; + int len; + } f; + int l; + int *__counted_by(l) q; + q = f.p; + l = f.len; +} + +void member_base_ok_ptr_passing(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(len) " + int *p; + int len; + } *f; + cb(f->p, f->len); +} + +void member_base_ok_ptr_init(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(len) " + int *p; + int len; + } *f; + int l = f->len; + int *__counted_by(l) q = f->p; +} + +void member_base_ok_ptr_assign(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(len) " + int *p; + int len; + } *f; + int l; + int *__counted_by(l) q; + q = f->p; + l = f->len; +} + +void member_base_ok_multi_passing(void) { + struct foo { + // TODO: Blocked on purpose until we have CodeGen tests. + // rdar://119737451 + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by((a + b) - (b - 42)) " + int *p; + int a; + int b; + } f; + cb_multi(f.p, f.a + f.b, f.b - 42); +} + +void member_base_ok_multi_init(void) { + struct foo { + // TODO: Blocked on purpose until we have CodeGen tests. + // rdar://119737451 + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by((a + b) - (b - 42)) " + int *p; + int a; + int b; + } f; + int l1 = f.a + f.b; + int l2 = f.b - 42; + int *__counted_by(l1 - l2) q = f.p; +} + +void member_base_ok_multi_assign(void) { + struct foo { + // TODO: Blocked on purpose until we have CodeGen tests. + // rdar://119737451 + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by((a + b) - (b - 42)) " + int *p; + int a; + int b; + } f; + int l1; + int l2; + int *__counted_by(l1 - l2) q; + q = f.p; + l1 = f.a + f.b; + l2 = f.b - 42; +} + +void member_base_ok_nested_passing(void) { + struct foo { + struct bar { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__counted_by(len) " + int *p; + int len; + } b; + } f; + cb(f.b.p, f.b.len); +} + +void member_base_ok_nested_init(void) { + struct foo { + struct bar { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__counted_by(len) " + int *p; + int len; + } b; + } f; + int l = f.b.len; + int *__counted_by(l) q = f.b.p; +} + +void member_base_ok_nested_assign(void) { + struct foo { + struct bar { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__counted_by(len) " + int *p; + int len; + } b; + } f; + int l; + int *__counted_by(l) q; + q = f.b.p; + l = f.b.len; +} + +void member_base_ok_ptr_nested_passing(void) { + struct foo { + struct bar { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__counted_by(len) " + int *p; + int len; + } *b; + } *f; + cb(f->b->p, f->b->len); +} + +void member_base_ok_ptr_nested_init(void) { + struct foo { + struct bar { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__counted_by(len) " + int *p; + int len; + } *b; + } *f; + int l = f->b->len; + int *__counted_by(l) q = f->b->p; +} + +void member_base_ok_ptr_nested_assign(void) { + struct foo { + struct bar { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:12-[[@LINE+1]]:12}:"__counted_by(len) " + int *p; + int len; + } *b; + } *f; + int l; + int *__counted_by(l) q; + q = f->b->p; + l = f->b->len; +} + +struct member_base_foo_passing { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_ok_from_parm_passing(struct member_base_foo_passing *f) { + cb(f->p, f->len); +} + +struct member_base_foo_init { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_ok_from_parm_init(struct member_base_foo_init *f) { + int l = f->len; + int *__counted_by(l) q = f->p; +} + +struct member_base_foo_assign { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_ok_from_parm_assign(struct member_base_foo_assign *f) { + int l; + int *__counted_by(l) q; + q = f->p; + l = f->len; +} + +struct member_base_foo2_passing { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_ok_from_parm2_passing(struct member_base_foo2_passing f) { + cb(f.p, f.len); +} + +struct member_base_foo2_init { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_ok_from_parm2_init(struct member_base_foo2_init f) { + int l = f.len; + int *__counted_by(l) q = f.p; +} + +struct member_base_foo2_assign { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_ok_from_parm2_assign(struct member_base_foo2_assign f) { + int l; + int *__counted_by(l) q; + q = f.p; + l = f.len; +} + +void member_base_bad(int parm) { + int local; + struct foo { + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: + int *p; + int len; + } f, f2; + cb(f.p, local); + cb(f.p, parm); + cb(f.p, global); + cb(f.p, f2.len); + cb_multi(f.p, f.len, f2.len); + cb_multi(f.p, f2.len, f.len); + cb_multi(f.p, f.len, local); + cb_multi(f.p, f.len, parm); + cb_multi(f.p, global, f.len); + + int l1 = local; + int *__counted_by(l1) q1 = f.p; + l1 = parm; + q1 = f.p; + + int l2 = global; + int *__counted_by(l2) q2 = f.p; + l2 = f2.len; + q2 = f.p; + + int l3_a = f.len; + int l3_b = f2.len; + int *__counted_by(l3_a - l3_b) q3 = f.p; + q3 = f.p; + l3_a = f.len; + l3_b = parm; +} + +void member_base_bad2(int parm) { + int local; + struct foo { + struct bar { + // CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: + int *p; + int len; + } b; + int size; + } f, f2; + cb(f.b.p, f.size); + cb(f.b.p, f2.b.len); + cb(f.b.p, f2.size); + cb(f.b.p, f.b.len + f2.b.len); + cb(f.b.p, f.b.len + f.size); + cb(f.b.p, f.b.len + f2.size); + cb(f.b.p, f.b.len + local); + cb(f.b.p, f.b.len + parm); + cb(f.b.p, f.b.len + global); + cb_multi(f.b.p, f.b.len, f2.b.len); + cb_multi(f.b.p, f.b.len, f.size); + + int l1 = f.size; + int *__counted_by(l1) q1 = f.b.p; + l1 = f2.b.len; + q1 = f.b.p; + + int l2 = f.b.len + f2.b.len; + int *__counted_by(l2) q2 = f.b.p; + l2 = f.b.len + parm; + q2 = f.b.p; + + int l3_a = f.b.len; + int l3_b = f2.b.len; + int *__counted_by(l3_a - l3_b) q3 = f.b.p; + q3 = f.b.p; + l3_a = f.b.len; + l3_b = f.size; +} + +struct member_base_global_foo_passing { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_global_ok_passing(void) { + struct member_base_global_foo_passing f; + cb(f.p, f.len); +} + +struct member_base_global_foo_init { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_global_ok_init(void) { + struct member_base_global_foo_init f; + int l = f.len; + int *__counted_by(l) q = f.p; +} + +struct member_base_global_foo_assign { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:8-[[@LINE+1]]:8}:"__counted_by(len) " + int *p; + int len; +}; +void member_base_global_ok_assign(void) { + struct member_base_global_foo_assign f; + int l; + int *__counted_by(l) q; + l = f.len; + q = f.p; +} + +// Check if parentheses are emitted for complex expressions, so that we can +// avoid dealing with operator precedence. + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:18-[[@LINE+1]]:18}:"__counted_by(len) " +void parens(int *p, int len) { + cb(p, len); +} + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:19-[[@LINE+1]]:19}:"__counted_by(len + len2) " +void parens2(int *p, int len, int len2) { + cb(p, len + len2); +} + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:19-[[@LINE+1]]:19}:"__counted_by(len - len2) " +void parens3(int *p, int len, int len2) { + cb_multi(p, len, len2); +} + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:19-[[@LINE+1]]:19}:"__counted_by(len - (len + len2)) " +void parens4(int *p, int len, int len2) { + cb_multi(p, len, len + len2); +} + +// TODO: Blocked on purpose until we have CodeGen tests. +// rdar://119737451 +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]:19-[[@LINE+1]]:19}:"__counted_by((len - 42) - (len + len2)) " +void parens5(int *p, int len, int len2) { + cb_multi(p, len - 42, len + len2); +} + +// Check out counts. + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:29-[[@LINE+1]]:29}:"__counted_by(*count) " +void out_to_in_passing(int *p, int *count) { + cb(p, *count); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:26-[[@LINE+1]]:26}:"__counted_by(*count) " +void out_to_in_init(int *p, int *count) { + int c = *count; + int *__counted_by(c) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:28-[[@LINE+1]]:28}:"__counted_by(*count) " +void out_to_in_assign(int *p, int *count) { + int c; + int *__counted_by(c) q; + q = p; + c = *count; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:22-[[@LINE+1]]:22}:"__counted_by(*count) " +void out_to_out(int *p, int *count) { + cb_out(p, count); +} + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void out_to_out_with_arith(int *p, int *count) { + cb_out(p, count + 1); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:21-[[@LINE+1]]:21}:"__counted_by(count) " +void in_to_out(int *p, int count) { + cb_out(p, &count); +} + +// CHECK-NOT: fix-it:{{.+}}:{[[@LINE+1]]: +void in_to_out_with_arith(int *p, int count) { + cb_out(p, &count + 1); +} + +// Check *_or_null() variant. + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:33-[[@LINE+1]]:33}:"__counted_by_or_null(count) " +void to_cb_or_null_passing(int *p, int count) { + cb_or_null(p, count); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:30-[[@LINE+1]]:30}:"__counted_by_or_null(count) " +void to_cb_or_null_init(int *p, int count) { + int c = count; + int *__counted_by_or_null(c) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:32-[[@LINE+1]]:32}:"__counted_by_or_null(count) " +void to_cb_or_null_assign(int *p, int count) { + int c; + int *__counted_by_or_null(c) q; + q = p; + c = count; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:33-[[@LINE+1]]:33}:"__sized_by_or_null(size) " +void to_sb_or_null_passing(int *p, int size) { + sb_or_null(p, size); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:30-[[@LINE+1]]:30}:"__sized_by_or_null(size) " +void to_sb_or_null_init(int *p, int size) { + int s = size; + int *__sized_by_or_null(s) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:32-[[@LINE+1]]:32}:"__sized_by_or_null(size) " +void to_sb_or_null_assign(int *p, int size) { + int s; + int *__sized_by_or_null(s) q; + q = p; + s = size; +} + +// Check constant counts. + +void cb_const(int *__counted_by(16) p); +void sb_const(void *__sized_by(16) p); + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:36-[[@LINE+1]]:36}:"__counted_by(16) " +void to_cb_const_parm_passing(int *p) { + cb_const(p); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:33-[[@LINE+1]]:33}:"__counted_by(16) " +void to_cb_const_parm_init(int *p) { + int *__counted_by(16) q = p; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:35-[[@LINE+1]]:35}:"__counted_by(16) " +void to_cb_const_parm_assign(int *p) { + int *__counted_by(16) q; + q = p; +} + +void to_cb_const_struct(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(16) " + int *p; + } f; + cb_const(f.p); +} + +void to_cb_const_struct_ptr(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(16) " + int *p; + } *f; + cb_const(f->p); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:29-[[@LINE+1]]:29}:"__sized_by(16) " +void to_sb_const_parm(void *p) { + sb_const(p); +} + +void to_sb_const_struct(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:11-[[@LINE+1]]:11}:"__sized_by(16) " + void *p; + } f; + sb_const(f.p); +} + +void to_sb_const_struct_ptr(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:11-[[@LINE+1]]:11}:"__sized_by(16) " + void *p; + } *f; + sb_const(f->p); +} + +// Check macros. + +// TODO: We should suggest __counted_by(COUNT)/__sized_by(COUNT) instead. +// rdar://119737647 + +#define COUNT 16 + +void cb_const_macro(int *__counted_by(COUNT) p); +void sb_const_macro(void *__sized_by(COUNT) p); + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:34-[[@LINE+1]]:34}:"__counted_by(16) " +void to_cb_const_parm_macro(int *p) { + cb_const_macro(p); +} + +void to_cb_const_struct_macro(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(16) " + int *p; + } f; + cb_const_macro(f.p); +} + +void to_cb_const_struct_macro_ptr(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:10-[[@LINE+1]]:10}:"__counted_by(16) " + int *p; + } *f; + cb_const_macro(f->p); +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:35-[[@LINE+1]]:35}:"__sized_by(16) " +void to_sb_const_parm_macro(void *p) { + sb_const_macro(p); +} + +void to_sb_const_struct_macro(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:11-[[@LINE+1]]:11}:"__sized_by(16) " + void *p; + } f; + sb_const_macro(f.p); +} + +void to_sb_const_struct_macro_ptr(void) { + struct foo { + // CHECK: fix-it:{{.+}}:{[[@LINE+1]]:11-[[@LINE+1]]:11}:"__sized_by(16) " + void *p; + } *f; + sb_const_macro(f->p); +} + +// Check init patterns. + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:23-[[@LINE+1]]:23}:"__counted_by(len) " +void init_struct(int *p, int len) { + struct foo { + int *__counted_by(l) q; + int l; + }; + struct foo f = { p, len }; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:24-[[@LINE+1]]:24}:"__counted_by(len) " +void init_struct2(int *p, int len) { + struct foo { + int *__counted_by(l) q; + int l; + } f = { p, len }; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:34-[[@LINE+1]]:34}:"__counted_by(len) " +void init_struct_designated(int *p, int len) { + struct foo { + int *__counted_by(l) q; + int l; + }; + struct foo f = { .q = p, .l = len }; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:35-[[@LINE+1]]:35}:"__counted_by(len) " +void init_struct_designated2(int *p, int len) { + struct foo { + int *__counted_by(l) q; + int l; + } f = { .q = p, .l = len }; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:30-[[@LINE+1]]:30}:"__counted_by(len) " +void init_struct_nested(int *p, int len) { + struct bar { + struct foo { + int *__counted_by(l) q; + int l; + } f; + int x; + }; + struct bar b = { { p, len }, 0 }; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:41-[[@LINE+1]]:41}:"__counted_by(len) " +void init_struct_nested_designated(int *p, int len) { + struct bar { + struct foo { + int *__counted_by(l) q; + int l; + } f; + int x; + }; + struct bar b = { .x = 0, .f = { .l = len, .q = p} }; +} + +// CHECK: fix-it:{{.+}}:{[[@LINE+1]]:22-[[@LINE+1]]:22}:"__counted_by(len) " +void init_array(int *p, int len) { + struct foo { + int *__counted_by(l) q; + int l; + }; + struct foo array[2] = { { p, len } }; +} diff --git a/clang/test/BoundsSafety/FixIt/single_to_terminated_by_null.c b/clang/test/BoundsSafety/FixIt/single_to_terminated_by_null.c new file mode 100644 index 0000000000000..781cf927bf34a --- /dev/null +++ b/clang/test/BoundsSafety/FixIt/single_to_terminated_by_null.c @@ -0,0 +1,95 @@ + +// RUN: cp %s %t +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits -verify %t +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %t > %t.cc_out 2> %t.cc_out +// RUN: FileCheck %s --input-file=%t.cc_out + +#include + +// expected-note@+1 6{{passing argument to parameter 'path' here}} +void method_with_single_const_param(const char *path); + +// expected-note@+1 5{{passing argument to parameter 'path' here}} +void method_with_single_null_terminated_param(char * __null_terminated path); + +struct Foo { +// expected-note@+4 {{consider adding '__null_terminated' to 'buf'}} +// CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+3]]:9-[[@LINE+3]]:9}:"__null_terminated " +// expected-note@+2{{consider adding 'const' to 'buf'}} +// CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char *buf; + +// expected-note@+4 {{consider adding '__null_terminated' to 'arr_in_struct'}} +// CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+3]]:9-[[@LINE+3]]:9}:"__null_terminated " +// expected-note@+2{{consider adding 'const' to 'arr_in_struct'}} +// CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char *arr_in_struct[4]; +}; + +void test_invocation_with_struct_fields(struct Foo f) { + + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(f.arr_in_struct[0]); + + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(f.arr_in_struct[0]); + + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(f.buf); + + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(f.buf); +} + +// expected-note@+2{{consider adding 'const' to 'anchor'}} +// CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+1]]:25-[[@LINE+1]]:25}:"const " +void single_invocation1(char *anchor) +{ + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(anchor); +} + +// expected-note@+2{{consider adding '__null_terminated' to 'anchor'}} +// CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+1]]:31-[[@LINE+1]]:31}:"__null_terminated " +void single_invocation2(char *anchor) +{ + //expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(anchor); +} + +void test_single_invocation_local_variable() { + // expected-note@+2 {{consider adding 'const' to 'local_arr'}} + // CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+1]]:3-[[@LINE+1]]:3}:"const " + char *local_arr[2]; + + // expected-error@+1{{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(local_arr[0]); +} + +void test_single_invocation2_local_variable() { + // expected-note@+2 {{consider adding '__null_terminated' to 'local_arr'}} + // CHECK-DAG: fix-it:"{{.+}}single_to_terminated_by_null.c.tmp":{[[@LINE+1]]:9-[[@LINE+1]]:9}:"__null_terminated " + char *local_arr[2]; + + //expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(local_arr[0]); +} + +void test_single_invocation4_local_variable() { + char * __single local_arr[2]; + + // expected-error@+1{{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(local_arr[0]); +} + +// TODO: Emit a note and a fixit here. rdar://122997544 +char * returns_single(void); + +void test_invocation_with_return_values() { + + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_const_param(returns_single()); + + // expected-error@+1 {{passing 'char *__single' to parameter of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + method_with_single_null_terminated_param(returns_single()); +} diff --git a/clang/test/BoundsSafety/Frontend/asm_is_supported.S b/clang/test/BoundsSafety/Frontend/asm_is_supported.S new file mode 100644 index 0000000000000..b657829a23a9d --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/asm_is_supported.S @@ -0,0 +1,5 @@ + + +// Making sure the Frontend accepts the -fbounds-safety flags in assembly mode - it would otherwise produce error: unknown argument '...' + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety %s diff --git a/clang/test/BoundsSafety/Frontend/bounds-safety-adoption-mode-ignored.c b/clang/test/BoundsSafety/Frontend/bounds-safety-adoption-mode-ignored.c new file mode 100644 index 0000000000000..098db97f033df --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/bounds-safety-adoption-mode-ignored.c @@ -0,0 +1,7 @@ + +// RUN: %clang_cc1 -fbounds-safety-adoption-mode -fsyntax-only %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety-adoption-mode -fbounds-safety -fno-bounds-safety -fsyntax-only %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -fno-bounds-safety -fbounds-safety-adoption-mode -fsyntax-only %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-adoption-mode -fno-bounds-safety -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: warning: -fbounds-safety-adoption-mode without -fbounds-safety is ignored diff --git a/clang/test/BoundsSafety/Frontend/bounds-safety-attributes.c b/clang/test/BoundsSafety/Frontend/bounds-safety-attributes.c new file mode 100644 index 0000000000000..53560ff1ad91c --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/bounds-safety-attributes.c @@ -0,0 +1,25 @@ + + + +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -fsyntax-only %s + +// RUN: %clang_cc1 -x c++ -fexperimental-bounds-safety-attributes -fsyntax-only %s + +// RUN: %clang_cc1 -x objective-c -fexperimental-bounds-safety-attributes -fsyntax-only %s + +// RUN: %clang_cc1 -x objective-c++ -fexperimental-bounds-safety-attributes -fsyntax-only %s + +// RUN: %clang_cc1 -fbounds-safety -fexperimental-bounds-safety-attributes -fsyntax-only %s + +// RUN: not %clang_cc1 -x c++ -fbounds-safety -fexperimental-bounds-safety-attributes -fsyntax-only %s 2>&1 | FileCheck %s + +// RUN: not %clang_cc1 -x objective-c -fbounds-safety -fexperimental-bounds-safety-attributes -fsyntax-only %s 2>&1 | FileCheck %s + +// RUN: not %clang_cc1 -x objective-c++ -fbounds-safety -fexperimental-bounds-safety-attributes -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: error: -fbounds-safety is supported only for C language + + +// RUN: not %clang_cc1 -fbounds-safety -fno-experimental-bounds-safety-attributes -fsyntax-only %s 2>&1 | FileCheck --check-prefix=CHECK-DIS %s + +// CHECK-DIS: error: -fexperimental-bounds-safety-attributes cannot be disabled when -fbounds-safety is enabled diff --git a/clang/test/BoundsSafety/Frontend/bounds-safety-bringup-missing-checks.c b/clang/test/BoundsSafety/Frontend/bounds-safety-bringup-missing-checks.c new file mode 100644 index 0000000000000..12c7e0cdf7104 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/bounds-safety-bringup-missing-checks.c @@ -0,0 +1,22 @@ + + +// RUN: not %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=none -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=POS_NONE +// RUN: not %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=none,compound_literal_init,all -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=POS_NONE + +// POS_NONE: invalid value 'none' in '-fbounds-safety-bringup-missing-checks='; did you mean '-fno-bounds-safety-bringup-missing-checks'? + +// RUN: not %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=none -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=NEG_NONE +// RUN: not %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=none,compound_literal_init,all -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=NEG_NONE + +// NEG_NONE: invalid value 'none' in '-fno-bounds-safety-bringup-missing-checks='; did you mean '-fbounds-safety-bringup-missing-checks'? + +// RUN: not %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=abc -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=POS_ABC +// RUN: not %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=abc,compound_literal_init,all -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=POS_ABC + +// POS_ABC: invalid value 'abc' in '-fbounds-safety-bringup-missing-checks=' + + +// RUN: not %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=abc -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=NEG_ABC +// RUN: not %clang_cc1 -fbounds-safety -fno-bounds-safety-bringup-missing-checks=abc,compound_literal_init,all -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=NEG_ABC + +// NEG_ABC: invalid value 'abc' in '-fno-bounds-safety-bringup-missing-checks=' diff --git a/clang/test/BoundsSafety/Frontend/cmdl_opts.c b/clang/test/BoundsSafety/Frontend/cmdl_opts.c new file mode 100644 index 0000000000000..dc5de416eb762 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/cmdl_opts.c @@ -0,0 +1,9 @@ + + +// Making sure the Frontend accepts the -fbounds-safety flags - it would otherwise produce error: unknown argument '...' + +// RUN: %clang -cc1 -fbounds-safety -fsyntax-only %s + +// RUN: %clang -cc1 -fbounds-safety -fbounds-attributes-cxx-experimental -fsyntax-only %s + +// RUN: %clang -cc1 -fbounds-safety -fbounds-attributes-objc-experimental -fsyntax-only %s diff --git a/clang/test/BoundsSafety/Frontend/cpp_experimental_is_supported.cpp b/clang/test/BoundsSafety/Frontend/cpp_experimental_is_supported.cpp new file mode 100644 index 0000000000000..d967eb95d433c --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/cpp_experimental_is_supported.cpp @@ -0,0 +1,3 @@ + + +// RUN: %clang_cc1 -fbounds-safety -fbounds-attributes-cxx-experimental -fsyntax-only %s diff --git a/clang/test/BoundsSafety/Frontend/cpp_experimental_without_bounds_safety_ignored.cpp b/clang/test/BoundsSafety/Frontend/cpp_experimental_without_bounds_safety_ignored.cpp new file mode 100644 index 0000000000000..7e61a5fc25bbe --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/cpp_experimental_without_bounds_safety_ignored.cpp @@ -0,0 +1,5 @@ + + +// RUN: %clang_cc1 -fbounds-attributes-cxx-experimental -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: warning: -fbounds-attributes-cxx-experimental without -fbounds-attributes is ignored diff --git a/clang/test/BoundsSafety/Frontend/cpp_is_unsupported.cpp b/clang/test/BoundsSafety/Frontend/cpp_is_unsupported.cpp new file mode 100644 index 0000000000000..0d96f42fc7892 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/cpp_is_unsupported.cpp @@ -0,0 +1,6 @@ + + +// RUN: not %clang_cc1 -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fbounds-attributes %s 2>&1 | FileCheck %s + +// CHECK: error: -fbounds-safety is supported only for C language diff --git a/clang/test/BoundsSafety/Frontend/forge-ptr-keyword.c b/clang/test/BoundsSafety/Frontend/forge-ptr-keyword.c new file mode 100644 index 0000000000000..8ce582ac0dfcc --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/forge-ptr-keyword.c @@ -0,0 +1,9 @@ + +// RUN: %clang_cc1 -x c -verify=c_lang %s +// RUN: %clang_cc1 -x c++ -verify=cpp_lang %s + +void Test1() { + __builtin_unsafe_forge_bidi_indexable(0, sizeof(int)); + // c_lang-error@-1 {{use of unknown builtin '__builtin_unsafe_forge_bidi_indexable'}} + // cpp_lang-error@-2 {{use of undeclared identifier '__builtin_unsafe_forge_bidi_indexable'}} +} diff --git a/clang/test/BoundsSafety/Frontend/libc_staged_bounds_safety_attributes_macro.c b/clang/test/BoundsSafety/Frontend/libc_staged_bounds_safety_attributes_macro.c new file mode 100644 index 0000000000000..bbf95b99b414c --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/libc_staged_bounds_safety_attributes_macro.c @@ -0,0 +1,84 @@ +// This is similar to the driver test of the same name but we verify that +// the macro is actually set/unset as expected. + +// ============================================================================= +// Supported target triples +// ============================================================================= + +// ios +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-ios \ +// RUN: -fno-bounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -Xclang -verify=no_macro -fbounds-safety -fsyntax-only %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-ios \ +// RUN: -fbounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -Xclang -verify=macro -fbounds-safety -fsyntax-only %s + +// macos +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-macos \ +// RUN: -fno-bounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -Xclang -verify=no_macro -fbounds-safety -fsyntax-only %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-macos \ +// RUN: -fbounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -Xclang -verify=macro -fbounds-safety -fsyntax-only %s + +// macos (legacy `darwin` os name in triple) +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-darwin \ +// RUN: -fno-bounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -Xclang -verify=no_macro -fbounds-safety -fsyntax-only %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-darwin \ +// RUN: -fbounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -Xclang -verify=macro -fbounds-safety -fsyntax-only %s + +// watchos +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-watchos \ +// RUN: -fno-bounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -Xclang -verify=no_macro -fbounds-safety -fsyntax-only %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-watchos \ +// RUN: -fbounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -Xclang -verify=macro -fbounds-safety -fsyntax-only %s + +// tvos +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-tvos \ +// RUN: -fno-bounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -Xclang -verify=no_macro -fbounds-safety -fsyntax-only %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-tvos \ +// RUN: -fbounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -Xclang -verify=macro -fbounds-safety -fsyntax-only %s + +// ============================================================================= +// Unsupported target triples +// ============================================================================= + +// linux +// RUN: %clang -Wno-incompatible-sysroot -target x86_64-unknown-linux \ +// RUN: -fno-bounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -Xclang -verify=no_macro -fbounds-safety -fsyntax-only %s +// RUN: %clang -Wno-incompatible-sysroot -target x86_64-unknown-linux \ +// RUN: -fbounds-safety-bringup-missing-checks=libc_attributes \ +// RUN: -x c -Xclang -verify=no_macro -fbounds-safety -fsyntax-only %s + +// ============================================================================= +// Check option is included in all and option variant without `=all` suffix +// ============================================================================= + +// ios +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-ios \ +// RUN: -fno-bounds-safety-bringup-missing-checks=all \ +// RUN: -x c -Xclang -verify=no_macro -fbounds-safety -fsyntax-only %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-ios \ +// RUN: -fno-bounds-safety-bringup-missing-checks \ +// RUN: -x c -Xclang -verify=no_macro -fbounds-safety -fsyntax-only %s + +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-ios \ +// RUN: -fbounds-safety-bringup-missing-checks=all \ +// RUN: -x c -Xclang -verify=macro -fbounds-safety -fsyntax-only %s +// RUN: %clang -Wno-incompatible-sysroot -target arm64-apple-ios \ +// RUN: -fbounds-safety-bringup-missing-checks \ +// RUN: -x c -Xclang -verify=macro -fbounds-safety -fsyntax-only %s + +#ifndef __LIBC_STAGED_BOUNDS_SAFETY_ATTRIBUTES +// no_macro-error@+1{{expected __LIBC_STAGED_BOUNDS_SAFETY_ATTRIBUTES macro}} +#error expected __LIBC_STAGED_BOUNDS_SAFETY_ATTRIBUTES macro +#endif + +// macro-no-diagnostics diff --git a/clang/test/BoundsSafety/Frontend/objc_experimental_is_supported.m b/clang/test/BoundsSafety/Frontend/objc_experimental_is_supported.m new file mode 100644 index 0000000000000..fb9d1d7e18197 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/objc_experimental_is_supported.m @@ -0,0 +1,2 @@ + +// RUN: %clang -cc1 -fbounds-safety -fbounds-attributes-objc-experimental -fsyntax-only %s diff --git a/clang/test/BoundsSafety/Frontend/objc_experimental_without_bounds_safety_ignored.cpp b/clang/test/BoundsSafety/Frontend/objc_experimental_without_bounds_safety_ignored.cpp new file mode 100644 index 0000000000000..1d1694bf25c6c --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/objc_experimental_without_bounds_safety_ignored.cpp @@ -0,0 +1,5 @@ + + +// RUN: %clang -cc1 -fbounds-attributes-objc-experimental -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: warning: -fbounds-attributes-objc-experimental without -fbounds-attributes is ignored diff --git a/clang/test/BoundsSafety/Frontend/objective_c_is_unsupported.m b/clang/test/BoundsSafety/Frontend/objective_c_is_unsupported.m new file mode 100644 index 0000000000000..0d96f42fc7892 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/objective_c_is_unsupported.m @@ -0,0 +1,6 @@ + + +// RUN: not %clang_cc1 -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fbounds-attributes %s 2>&1 | FileCheck %s + +// CHECK: error: -fbounds-safety is supported only for C language diff --git a/clang/test/BoundsSafety/Frontend/objective_cpp_is_unsupported.mm b/clang/test/BoundsSafety/Frontend/objective_cpp_is_unsupported.mm new file mode 100644 index 0000000000000..0d96f42fc7892 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/objective_cpp_is_unsupported.mm @@ -0,0 +1,6 @@ + + +// RUN: not %clang_cc1 -fbounds-safety %s 2>&1 | FileCheck %s +// RUN: not %clang_cc1 -fbounds-attributes %s 2>&1 | FileCheck %s + +// CHECK: error: -fbounds-safety is supported only for C language diff --git a/clang/test/BoundsSafety/Frontend/only_c_is_supported.c b/clang/test/BoundsSafety/Frontend/only_c_is_supported.c new file mode 100644 index 0000000000000..b98ccd2fd9ab7 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/only_c_is_supported.c @@ -0,0 +1,10 @@ + +// RUN: not %clang -cc1 -fbounds-safety -x c++ %s 2>&1 | FileCheck %s + +// RUN: not %clang -cc1 -fbounds-safety -x objective-c %s 2>&1 | FileCheck %s + +// RUN: not %clang -cc1 -fbounds-safety -x objective-c++ %s 2>&1 | FileCheck %s + +// RUN: not %clang -cc1 -fbounds-safety -x objective-c++ %s 2>&1 | FileCheck %s + +// CHECK: error: -fbounds-safety is supported only for C language diff --git a/clang/test/BoundsSafety/Frontend/ptrcheck-bounds-safety-disabled-expansion.c b/clang/test/BoundsSafety/Frontend/ptrcheck-bounds-safety-disabled-expansion.c new file mode 100644 index 0000000000000..0d72f40538ddc --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/ptrcheck-bounds-safety-disabled-expansion.c @@ -0,0 +1,82 @@ + + +// RUN: %clang_cc1 -dump-tokens %s 2>&1 | FileCheck %s + +#include + +int HasPtrCheck = __has_ptrcheck; +// CHECK-LABEL: identifier 'HasPtrCheck' +// CHECK: numeric_constant '0' + +struct Foo { +// CHECK-LABEL: identifier 'Foo' +// CHECK-NEXT: l_brace + int *__single f0; +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: identifier 'f0' +// CHECK-LABEL: semi + + int *__unsafe_indexable f1; +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: identifier 'f1' +// CHECK-LABEL: semi + + int *__counted_by(10) f2; +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: identifier 'f2' +// CHECK-LABEL: semi + + int *__sized_by(10) f3; +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: identifier 'f3' +// CHECK-LABEL: semi +}; +// CHECK-LABEL: r_brace +// CHECK: semi + +__ptrcheck_abi_assume_single(); +// CHECK-NEXT: semi +__ptrcheck_abi_assume_unsafe_indexable(); +// CHECK-NEXT: semi + +void code(void) { +// CHECK-LABEL: l_brace + __unsafe_forge_single(int *, 100); +// CHECK-NEXT: l_paren +// CHECK-NEXT: l_paren +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: r_paren +// CHECK-NEXT: l_paren +// CHECK: numeric_constant '100' +// CHECK-NEXT: r_paren +// CHECK-NEXT: r_paren + +// CHECK-LABEL: semi + __unsafe_forge_bidi_indexable(int *, 200, 300); +// CHECK-NEXT: l_paren +// CHECK-NEXT: l_paren +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: r_paren +// CHECK-NEXT: l_paren +// CHECK: numeric_constant '200' +// CHECK-NEXT: r_paren +// CHECK-NEXT: r_paren + +// CHECK-LABEL: semi + __unsafe_forge_terminated_by(int *, 200, 300); +// CHECK-NEXT: l_paren +// CHECK-NEXT: l_paren +// CHECK-NEXT: 'int' +// CHECK-NEXT: star +// CHECK-NEXT: r_paren +// CHECK-NEXT: l_paren +// CHECK: numeric_constant '200' +// CHECK-NEXT: r_paren +// CHECK-NEXT: r_paren +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Frontend/ptrcheck-bounds-safety-disabled.c b/clang/test/BoundsSafety/Frontend/ptrcheck-bounds-safety-disabled.c new file mode 100644 index 0000000000000..908e700613c37 --- /dev/null +++ b/clang/test/BoundsSafety/Frontend/ptrcheck-bounds-safety-disabled.c @@ -0,0 +1,5 @@ + + +// RUN: %clang -fsyntax-only %s + +#include diff --git a/clang/test/BoundsSafety/Lexer/bidi_indexable.c b/clang/test/BoundsSafety/Lexer/bidi_indexable.c new file mode 100644 index 0000000000000..77c314e40cbc7 --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/bidi_indexable.c @@ -0,0 +1,16 @@ + + +// RUN: %clang_cc1 -dump-tokens -fbounds-safety %s 2>&1 | FileCheck %s + +struct Foo { + int *__attribute__((bidi_indexable)) foo; + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: __attribute '__attribute__' + // CHECK: l_paren '(' + // CHECK: l_paren '(' + // CHECK: identifier 'bidi_indexable' + // CHECK: r_paren ')' + // CHECK: r_paren ')' + // CHECK: identifier 'foo' + // CHECK: semi ';' diff --git a/clang/test/BoundsSafety/Lexer/ended_by.c b/clang/test/BoundsSafety/Lexer/ended_by.c new file mode 100644 index 0000000000000..1c9dbd38b34f5 --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/ended_by.c @@ -0,0 +1,24 @@ + + +// RUN: %clang_cc1 -dump-tokens -fbounds-safety %s 2>&1 | FileCheck %s + +struct Foo { + int *__attribute__((ended_by(end))) start; + int *end; + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: __attribute '__attribute__' + // CHECK: l_paren '(' + // CHECK: l_paren '(' + // CHECK: identifier 'ended_by' + // CHECK: l_paren '(' + // CHECK: identifier 'end' + // CHECK: r_paren ')' + // CHECK: r_paren ')' + // CHECK: r_paren ')' + // CHECK: identifier 'start' + // CHECK: semi ';' + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: identifier 'end' + // CHECK: semi '; diff --git a/clang/test/BoundsSafety/Lexer/indexable.c b/clang/test/BoundsSafety/Lexer/indexable.c new file mode 100644 index 0000000000000..fc6fc2c75510a --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/indexable.c @@ -0,0 +1,17 @@ + + +// RUN: %clang_cc1 -dump-tokens -fbounds-safety %s 2>&1 | FileCheck %s + +struct Foo { + int *__attribute__((indexable)) foo; + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: __attribute '__attribute__' + // CHECK: l_paren '(' + // CHECK: l_paren '(' + // CHECK: identifier 'indexable' + // CHECK: r_paren ')' + // CHECK: r_paren ')' + // CHECK: identifier 'foo' + // CHECK: semi ';' +}; diff --git a/clang/test/BoundsSafety/Lexer/single.c b/clang/test/BoundsSafety/Lexer/single.c new file mode 100644 index 0000000000000..6adc6a6029b6e --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/single.c @@ -0,0 +1,16 @@ + + +// RUN: %clang_cc1 -dump-tokens -fbounds-safety %s 2>&1 | FileCheck %s + +struct Foo { + int *__attribute__((single)) foo; + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: __attribute '__attribute__' + // CHECK: l_paren '(' + // CHECK: l_paren '(' + // CHECK: identifier 'single' + // CHECK: r_paren ')' + // CHECK: r_paren ')' + // CHECK: identifier 'foo' + // CHECK: semi ';' diff --git a/clang/test/BoundsSafety/Lexer/unsafe_forge_bidi_indexable.c b/clang/test/BoundsSafety/Lexer/unsafe_forge_bidi_indexable.c new file mode 100644 index 0000000000000..835762f8800a4 --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/unsafe_forge_bidi_indexable.c @@ -0,0 +1,8 @@ + + +// RUN: %clang_cc1 -fbounds-safety -dump-tokens %s 2>&1 | FileCheck %s + +void Test() { + (void) __builtin_unsafe_forge_bidi_indexable(0, sizeof(int)); + // CHECK: __builtin_unsafe_forge_bidi_indexable '__builtin_unsafe_forge_bidi_indexable' +} diff --git a/clang/test/BoundsSafety/Lexer/unsafe_forge_single.c b/clang/test/BoundsSafety/Lexer/unsafe_forge_single.c new file mode 100644 index 0000000000000..4978ee10761a7 --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/unsafe_forge_single.c @@ -0,0 +1,9 @@ + + +// RUN: %clang_cc1 -fbounds-safety -dump-tokens %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -dump-tokens %s 2>&1 | FileCheck %s + +void Test() { + (void) __builtin_unsafe_forge_single(0); + // CHECK: __builtin_unsafe_forge_single '__builtin_unsafe_forge_single' +} diff --git a/clang/test/BoundsSafety/Lexer/unsafe_forge_terminated_by.c b/clang/test/BoundsSafety/Lexer/unsafe_forge_terminated_by.c new file mode 100644 index 0000000000000..1a14b32612c65 --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/unsafe_forge_terminated_by.c @@ -0,0 +1,17 @@ + + +// RUN: %clang_cc1 -fbounds-safety -dump-tokens %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -dump-tokens %s 2>&1 | FileCheck %s + +void Test() { + (void) __builtin_unsafe_forge_terminated_by(int*, 1, 2); + // CHECK: __builtin_unsafe_forge_terminated_by '__builtin_unsafe_forge_terminated_by' + // CHECK: l_paren '(' + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: comma ',' + // CHECK: numeric_constant '1' + // CHECK: comma ',' + // CHECK: numeric_constant '2' + // CHECK: r_paren ')' +} diff --git a/clang/test/BoundsSafety/Lexer/unsafe_indexable.c b/clang/test/BoundsSafety/Lexer/unsafe_indexable.c new file mode 100644 index 0000000000000..ff422003b4ddb --- /dev/null +++ b/clang/test/BoundsSafety/Lexer/unsafe_indexable.c @@ -0,0 +1,17 @@ + + +// RUN: %clang_cc1 -dump-tokens -fbounds-safety %s 2>&1 | FileCheck %s + +struct Foo { + int *__attribute__((unsafe_indexable)) foo; + // CHECK: int 'int' + // CHECK: star '*' + // CHECK: __attribute '__attribute__' + // CHECK: l_paren '(' + // CHECK: l_paren '(' + // CHECK: identifier 'unsafe_indexable' + // CHECK: r_paren ')' + // CHECK: r_paren ')' + // CHECK: identifier 'foo' + // CHECK: semi ';' +} diff --git a/clang/test/BoundsSafety/Macros/c99-variadic-macros.c b/clang/test/BoundsSafety/Macros/c99-variadic-macros.c new file mode 100644 index 0000000000000..4af0befca1354 --- /dev/null +++ b/clang/test/BoundsSafety/Macros/c99-variadic-macros.c @@ -0,0 +1,23 @@ + + +// RUN: %clang_cc1 -fsyntax-only -verify -pedantic %s + +// expected-no-diagnostics + +#include + +void foo(void) { + char *p; + + // This macro cannot be declared as __unsafe_null_terminated_from_indexable(P, ...), + // because then the C standard pre-C23 doesn't allow passing only 1 argument. + __unsafe_null_terminated_from_indexable((void)p); + __unsafe_null_terminated_from_indexable((void)p, trailing); + __unsafe_null_terminated_from_indexable((void)p, trailing, trailing2); + + // This macro cannot be declared as __unsafe_terminated_by_from_indexable(T, P, ...), + // because then the C standard pre-C23 doesn't allow passing only 2 arguments. + __unsafe_terminated_by_from_indexable(filler, (void)p); + __unsafe_terminated_by_from_indexable(filler, (void)p, trailing); + __unsafe_terminated_by_from_indexable(filler, (void)p, trailing, trailing2); +} diff --git a/clang/test/BoundsSafety/Modules/Inputs/count-assign/count-assign-module.h b/clang/test/BoundsSafety/Modules/Inputs/count-assign/count-assign-module.h new file mode 100644 index 0000000000000..1abf3300d76ee --- /dev/null +++ b/clang/test/BoundsSafety/Modules/Inputs/count-assign/count-assign-module.h @@ -0,0 +1,17 @@ +#include + +#ifndef COUNT_ASSIGN_MODULE_H +#define COUNT_ASSIGN_MODULE_H + +int foo(int *__counted_by(len) ptr, int len) { + int arr[10]; + ptr = arr; + len = 10; + return ptr[len-1]; +} + +void baz() { + int len; + void *__sized_by(len) ptr; +} +#endif \ No newline at end of file diff --git a/clang/test/BoundsSafety/Modules/Inputs/count-assign/module.modulemap b/clang/test/BoundsSafety/Modules/Inputs/count-assign/module.modulemap new file mode 100644 index 0000000000000..95f029e3c487c --- /dev/null +++ b/clang/test/BoundsSafety/Modules/Inputs/count-assign/module.modulemap @@ -0,0 +1,2 @@ +module ca { header "count-assign-module.h" export * } + diff --git a/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/cdefs.h b/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/cdefs.h new file mode 100644 index 0000000000000..fded5f0c9281d --- /dev/null +++ b/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/cdefs.h @@ -0,0 +1,5 @@ +#if __has_include() +#include +#else +#define __has_ptrcheck 0 +#endif diff --git a/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/module.modulemap b/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/module.modulemap new file mode 100644 index 0000000000000..718ea590ce54a --- /dev/null +++ b/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/module.modulemap @@ -0,0 +1,9 @@ +module cdefs [system] [no_undeclared_includes] { + header "cdefs.h" + export * +} + +module ptrcheck { + header "ptrcheck.h" + export * +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/ptrcheck.h b/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/ptrcheck.h new file mode 100644 index 0000000000000..b8b85a9dd5ee1 --- /dev/null +++ b/clang/test/BoundsSafety/Modules/Inputs/ptrcheck-include-from-darwin/ptrcheck.h @@ -0,0 +1 @@ +#define __has_ptrcheck 1 diff --git a/clang/test/BoundsSafety/Modules/clang-headers.c b/clang/test/BoundsSafety/Modules/clang-headers.c new file mode 100644 index 0000000000000..6cd58179aba41 --- /dev/null +++ b/clang/test/BoundsSafety/Modules/clang-headers.c @@ -0,0 +1,8 @@ + +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fmodules \ +// RUN: -fimplicit-module-maps -fmodules-cache-path=%t/module_cache -Rmodule-import -verify %s +// RUN: %clang_cc1 -fsyntax-only -fmodules \ +// RUN: -fimplicit-module-maps -fmodules-cache-path=%t/module_cache -Rmodule-import -verify %s + +#include // expected-remark-re{{importing module 'ptrcheck' from '{{.*}}.pcm'}} diff --git a/clang/test/BoundsSafety/Modules/count-assign.c b/clang/test/BoundsSafety/Modules/count-assign.c new file mode 100644 index 0000000000000..5be680d4ea02c --- /dev/null +++ b/clang/test/BoundsSafety/Modules/count-assign.c @@ -0,0 +1,27 @@ +// REQUIRES: system-darwin +// RUN: rm -rf %t +// RUN: %clang_cc1 -fbounds-safety -fmodules -fno-implicit-modules -x c -I%S/Inputs/count-assign -emit-module %S/Inputs/count-assign/module.modulemap -fmodule-name=ca -o %t/count-assign.pcm +// RUN: %clang_cc1 -fbounds-safety -fmodules -fno-implicit-modules -x c -I%S/Inputs/count-assign -ast-dump-all -o - %s -fmodule-file=%t/count-assign.pcm | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -fmodules -fno-implicit-modules -x c -I%S/Inputs/count-assign -emit-llvm -o - %s -fmodule-file=%t/count-assign.pcm | FileCheck %s -check-prefix=LLVM +#include "count-assign-module.h" + +int bar(void) { + int arr[10]; + return foo(arr, 10); +} + +// CHECK: |-FunctionDecl {{.*}} imported in ca used foo 'int (int *__single __counted_by(len), int)' +// CHECK: | |-ParmVarDecl [[PARM_DECL_PTR:0x[a-z0-9]*]] {{.*}} imported in ca used ptr 'int *__single __counted_by(len)':'int *__single' +// CHECK: | |-ParmVarDecl {{.*}} imported in ca used len 'int' +// CHECK: | | `-DependerDeclsAttr {{.*}} Implicit [[PARM_DECL_PTR]] 0 + +// CHECK: |-FunctionDecl {{.*}} imported in ca baz 'void ()' +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.*}} imported in ca used len 'int' +// CHECK: | | `-DependerDeclsAttr {{.*}} Implicit [[LOCAL_PTR_REF:0x[a-z0-9]*]] 0 +// CHECK: | `-DeclStmt +// CHECK: | `-VarDecl [[LOCAL_PTR_REF]] {{.*}} imported in ca ptr 'void *__single __sized_by(len)':'void *__single' + +// Check that we do not accidentally set nullcheck on expr loaded from pcm. +// LLVM-NOT: boundscheck.null diff --git a/clang/test/BoundsSafety/Modules/ptrcheck-include-from-darwin.m b/clang/test/BoundsSafety/Modules/ptrcheck-include-from-darwin.m new file mode 100644 index 0000000000000..10af316c1a49c --- /dev/null +++ b/clang/test/BoundsSafety/Modules/ptrcheck-include-from-darwin.m @@ -0,0 +1,10 @@ + +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs/ptrcheck-include-from-darwin/ %s -verify +// expected-no-diagnostics + +#include "cdefs.h" + +#if !__has_ptrcheck + #error __has_ptrcheck is not defined to 1 +#endif diff --git a/clang/test/BoundsSafety/PCH/bounds-safety-pointer-cast.c b/clang/test/BoundsSafety/PCH/bounds-safety-pointer-cast.c new file mode 100644 index 0000000000000..7758dacd9e4a5 --- /dev/null +++ b/clang/test/BoundsSafety/PCH/bounds-safety-pointer-cast.c @@ -0,0 +1,21 @@ + +// Test without pch. +// RUN: %clang_cc1 -fbounds-safety -include %s -fsyntax-only -verify %s + +// Test with pch. +// RUN: %clang_cc1 -fbounds-safety -emit-pch -o %t %s +// RUN: %clang_cc1 -fbounds-safety -include-pch %t -fsyntax-only -verify %s + +#ifndef HEADER +#define HEADER +int Test(int *singleArg) { + int *local = singleArg; + + return *local; +} +#else + +float Test(float); // expected-error{{conflicting types for 'Test'}} + // expected-note@-8{{previous definition is here}} + +#endif diff --git a/clang/test/BoundsSafety/PCH/count-assign-with-pch.c b/clang/test/BoundsSafety/PCH/count-assign-with-pch.c new file mode 100644 index 0000000000000..439aef0bc245a --- /dev/null +++ b/clang/test/BoundsSafety/PCH/count-assign-with-pch.c @@ -0,0 +1,30 @@ +// REQUIRES: system-darwin +// Test without pch. +// RUN: %clang_cc1 -fbounds-safety -include %s -fsyntax-only -verify %s + +// Test with pch. +// RUN: %clang_cc1 -fbounds-safety -emit-pch -o %t %s +// RUN: %clang_cc1 -fbounds-safety -include-pch %t %s -ast-dump-all 2>&1 | FileCheck %s +// expected-no-diagnostics +#include + +#ifndef HEADER +#define HEADER +int foo(int *__counted_by(len) ptr, int len) { + ptr = ptr; + len = 10; + return ptr[len-1]; +} +#else + +int main() { + int arr[10] = {0}; + return foo(arr, 10); +} + +// CHECK: |-FunctionDecl {{.*}} imported used foo 'int (int *__single __counted_by(len), int)' +// CHECK: | |-ParmVarDecl [[PTR_DECL:0x[a-z0-9]*]] {{.*}} imported used ptr 'int *__single __counted_by(len)':'int *__single' +// CHECK: | |-ParmVarDecl {{.*}} imported used len 'int' +// CHECK: | | `-DependerDeclsAttr {{.*}} Implicit [[PTR_DECL]] 0 + +#endif diff --git a/clang/test/BoundsSafety/PCH/count-fields-assign-with-pch.c b/clang/test/BoundsSafety/PCH/count-fields-assign-with-pch.c new file mode 100644 index 0000000000000..23010663a8c26 --- /dev/null +++ b/clang/test/BoundsSafety/PCH/count-fields-assign-with-pch.c @@ -0,0 +1,39 @@ + +// Test without pch. +// RUN: %clang_cc1 -fbounds-safety -include %s -fsyntax-only -verify %s + +// Test with pch. +// RUN: %clang_cc1 -fbounds-safety -emit-pch -o %t %s +// RUN: %clang_cc1 -fbounds-safety -include-pch %t -verify -ast-dump-all %s 2>&1 | FileCheck %s +// expected-no-diagnostics +#include + +#ifndef HEADER +#define HEADER +typedef struct { + int len; + int *__counted_by(len) ptr; +} S; +int foo() { + int arr[10]; + S s; + s.len = 10; + s.ptr = arr; + return s.ptr[9]; +} +#else + +int main() { + return foo(); +} + +// CHECK: |-RecordDecl [[STRUCT_DEF:0x[a-z0-9]*]] {{.*}} imported struct definition +// CHECK: | |-FieldDecl {{.*}} imported referenced len 'int' +// CHECK: | | `-DependerDeclsAttr {{.*}} Implicit [[FIELD_DECL_PTR:0x[a-z0-9]*]] 0 +// CHECK: | `-FieldDecl [[FIELD_DECL_PTR]] {{.*}} imported referenced ptr 'int *__single __counted_by(len)':'int *__single' +// CHECK: |-TypedefDecl {{.*}} imported referenced S 'struct S':'S' +// CHECK: | `-ElaboratedType {{.*}} 'struct S' sugar imported +// CHECK: | `-RecordType {{.*}} 'S' imported +// CHECK: | `-Record [[STRUCT_DEF]] + +#endif diff --git a/clang/test/BoundsSafety/PCH/count-increment-with-pch.c b/clang/test/BoundsSafety/PCH/count-increment-with-pch.c new file mode 100644 index 0000000000000..d835b53cf9406 --- /dev/null +++ b/clang/test/BoundsSafety/PCH/count-increment-with-pch.c @@ -0,0 +1,103 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py + +// Test without pch. +// RUN: %clang_cc1 -fbounds-safety -include %s -triple arm64-apple-ios -fsyntax-only -verify %s + +// Test with pch. +// RUN: %clang_cc1 -fbounds-safety -triple arm64-apple-ios -emit-pch -o %t %s +// RUN: %clang_cc1 -fbounds-safety -triple arm64-apple-ios -include-pch %t -verify -emit-llvm -O0 %s -o - | FileCheck %s +// expected-no-diagnostics +#include + +#ifndef HEADER +#define HEADER +typedef struct { + int len; + int *__counted_by(len) ptr; +} S; +int foo() { + int arr[10]; + S s = {0}; + s.ptr = arr; + s.len++; + return 0; +} +#else + +int main() { + return foo(); +} + +#endif + +// CHECK-LABEL: @foo( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[ARR:%.*]] = alloca [10 x i32], align 4 +// CHECK-NEXT: [[S:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP3:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP11:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: br i1 true, label [[CONT:%.*]], label [[TRAP:%.*]], !annotation [[META2:![0-9]+]] +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4:[0-9]+]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont: +// CHECK-NEXT: [[LEN:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: store i32 0, ptr [[LEN]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[S]], i64 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[TMP0]], i8 0, i64 4, i1 false) +// CHECK-NEXT: [[PTR:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr null, ptr [[PTR]], align 8 +// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[ARR]], i64 0, i64 0 +// CHECK-NEXT: [[UPPER:%.*]] = getelementptr inbounds i32, ptr [[ARRAYDECAY]], i64 10 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[UPPER]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[ARRAYDECAY]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[LEN1:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[LEN1]], align 8 +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP4]], 1 +// CHECK-NEXT: [[CONV:%.*]] = sext i32 [[ADD]] to i64, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP3]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 0, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 1, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_UB5:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR4]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP3]], i32 0, i32 2, !annotation [[META2]] +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_UB]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR]] to i64, !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !annotation [[META2]] +// CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = sdiv exact i64 [[SUB_PTR_SUB]], 4, !annotation [[META2]] +// CHECK-NEXT: [[CMP:%.*]] = icmp sle i64 [[CONV]], [[SUB_PTR_DIV]], !annotation [[META2]] +// CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]], !annotation [[META2]] +// CHECK: land.rhs: +// CHECK-NEXT: [[CMP7:%.*]] = icmp sle i32 0, [[ADD]], !annotation [[META2]] +// CHECK-NEXT: br label [[LAND_END]], !annotation [[META2]] +// CHECK: land.end: +// CHECK-NEXT: [[TMP5:%.*]] = phi i1 [ false, [[CONT]] ], [ [[CMP7]], [[LAND_RHS]] ], !annotation [[META2]] +// CHECK-NEXT: br i1 [[TMP5]], label [[CONT10:%.*]], label [[TRAP9:%.*]], !annotation [[META2]] +// CHECK: trap9: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR4]], !annotation [[META2]] +// CHECK-NEXT: unreachable, !annotation [[META2]] +// CHECK: cont10: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP11]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR13:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR12]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB15:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR14]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP11]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB17:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR16]], align 8 +// CHECK-NEXT: [[PTR18:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[S]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR13]], ptr [[PTR18]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[LEN1]], align 8 +// CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP6]], 1 +// CHECK-NEXT: store i32 [[INC]], ptr [[LEN1]], align 8 +// CHECK-NEXT: ret i32 0 +// diff --git a/clang/test/BoundsSafety/Profile/flexible-array-member-checks-code-coverage.c b/clang/test/BoundsSafety/Profile/flexible-array-member-checks-code-coverage.c new file mode 100644 index 0000000000000..2f13fef9e416f --- /dev/null +++ b/clang/test/BoundsSafety/Profile/flexible-array-member-checks-code-coverage.c @@ -0,0 +1,53 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ --version 3 + + +// RUN: %clang_cc1 -O2 -triple x86_64 -fbounds-safety -fprofile-instrument=clang -fcoverage-mapping -emit-llvm %s -o - | FileCheck %s + +#include + +struct s { + int count; + int fam[__counted_by(count)]; +}; + +void bar(struct s *p); + +// CHECK-LABEL: define dso_local void @foo( +// CHECK-SAME: ptr noundef [[BUF:%.*]], i32 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_foo, align 8 +// CHECK-NEXT: [[TMP0:%.*]] = add i64 [[PGOCOUNT]], 1 +// CHECK-NEXT: store i64 [[TMP0]], ptr @__profc_foo, align 8 +// CHECK-NEXT: [[IDX_EXT:%.*]] = zext i32 [[SIZE]] to i64 +// CHECK-NEXT: [[FLEX_BASE_NULL_CHECK_NOT:%.*]] = icmp eq ptr [[BUF]], null, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_BASE_NULL_CHECK_NOT]], label [[CONT40:%.*]], label [[FLEX_BASE_NONNULL:%.*]], {{!annotation ![0-9]+}} +// CHECK: flex.base.nonnull: +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[BUF]], i64 [[IDX_EXT]] +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, ptr [[BUF]], i64 4 +// CHECK-NEXT: [[DOTNOT:%.*]] = icmp ugt ptr [[BUF]], [[TMP1]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT49:%.*]] = icmp ugt ptr [[TMP1]], [[ADD_PTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[DOTNOT]], i1 true, i1 [[DOTNOT49]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND]], label [[TRAP:%.*]], label [[CONT27:%.*]], !prof [[PROF6:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: tail call void @llvm.ubsantrap(i8 25) #[[ATTR3:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont27: +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[BUF]], align 4, {{!tbaa ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_MINUS:%.*]] = icmp sgt i32 [[TMP2]], -1, {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[FLEX_COUNT_MINUS]], label [[CONT30:%.*]], label [[TRAP]], !prof [[PROF13:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: cont30: +// CHECK-NEXT: [[GEPDIFF:%.*]] = add nsw i64 [[IDX_EXT]], -4, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_AVAIL_COUNT_DIV:%.*]] = ashr exact i64 [[GEPDIFF]], 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_INTPTR:%.*]] = zext nneg i32 [[TMP2]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[FLEX_COUNT_CHECK_NOT:%.*]] = icmp ult i64 [[FLEX_AVAIL_COUNT_DIV]], [[FLEX_COUNT_INTPTR]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[DOTNOT50:%.*]] = icmp eq i32 [[SIZE]], 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[OR_COND52:%.*]] = or i1 [[DOTNOT50]], [[FLEX_COUNT_CHECK_NOT]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[OR_COND52]], label [[TRAP]], label [[CONT40]], !prof [[PROF17:![0-9]+]], {{!annotation ![0-9]+}} +// CHECK: cont40: +// CHECK-NEXT: tail call void @bar(ptr noundef [[BUF]]) #[[ATTR4:[0-9]+]] +// CHECK-NEXT: ret void +// +void foo(void *__sized_by(size) buf, unsigned size) { + struct s *p = (struct s *)buf; + bar(p); +} diff --git a/clang/test/BoundsSafety/Profile/region-counter-map.c b/clang/test/BoundsSafety/Profile/region-counter-map.c new file mode 100644 index 0000000000000..fe74078d0a0f1 --- /dev/null +++ b/clang/test/BoundsSafety/Profile/region-counter-map.c @@ -0,0 +1,170 @@ +// REQUIRES: system-darwin +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --replace-value-regex "!annotation ![0-9]+" "!tbaa ![0-9]+" "!tbaa\.struct ![0-9]+" "!nosanitize ![0-9]+" "!srcloc ![0-9]+" --prefix-filecheck-ir-name TMP_ +// RUN: %clang_cc1 -O0 -triple arm64-apple-iphoneos -fbounds-attributes -fprofile-instrument=clang -fcoverage-mapping -emit-llvm %s -o - | FileCheck %s + + +#include + +void *__sized_by(len) foo(void *__sized_by(len) buf, unsigned long long len); + + +// CHECK-LABEL: @bar( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[BUF_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[LEN_ADDR:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[AGG_TEMP:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP1:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP2:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP12:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP21:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP29:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP30:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP45:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable.0", align 8 +// CHECK-NEXT: [[AGG_TEMP46:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: [[AGG_TEMP60:%.*]] = alloca %"__bounds_safety::wide_ptr.bidi_indexable", align 8 +// CHECK-NEXT: store ptr [[BUF:%.*]], ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: store i64 [[LEN:%.*]], ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_bar, align 8 +// CHECK-NEXT: [[TMP0:%.*]] = add i64 [[PGOCOUNT]], 1 +// CHECK-NEXT: store i64 [[TMP0]], ptr @__profc_bar, align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BUF_ADDR]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[TMP2]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP]]) +// CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP1]], i64 [[TMP2]] +// CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP3]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 1 +// CHECK-NEXT: store ptr [[ADD_PTR]], ptr [[TMP4]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP]], i32 0, i32 2 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP6:%.*]] = load i64, ptr [[LEN_ADDR]], align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP1]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP1]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP2]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR3:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB4:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR3]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB4]], ptr [[TMP7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR5:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR6:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR5]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR7:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB8:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR7]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP2]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB10:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP11:%.*]] = icmp ule ptr [[WIDE_PTR_PTR]], [[WIDE_PTR_PTR6]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP11]], label [[LAND_LHS_TRUE:%.*]], label [[LAND_END:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.lhs.true: +// CHECK-NEXT: [[PGOCOUNT69:%.*]] = load i64, ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 3), align 8 +// CHECK-NEXT: [[TMP8:%.*]] = add i64 [[PGOCOUNT69]], 1 +// CHECK-NEXT: store i64 [[TMP8]], ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 3), align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP12]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB14:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB14]], ptr [[TMP9]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR16:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB18:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR19:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP12]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB20:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR19]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP21]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR22:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR23:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR22]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR24:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB25:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR24]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR26:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP21]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB27:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR26]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP28:%.*]] = icmp ule ptr [[WIDE_PTR_PTR16]], [[WIDE_PTR_PTR23]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP28]], label [[LOP_RHSCNT:%.*]], label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: lop.rhscnt: +// CHECK-NEXT: [[PGOCOUNT70:%.*]] = load i64, ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 4), align 8 +// CHECK-NEXT: [[TMP10:%.*]] = add i64 [[PGOCOUNT70]], 1 +// CHECK-NEXT: store i64 [[TMP10]], ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 4), align 8 +// CHECK-NEXT: br label [[LAND_RHS:%.*]], {{!annotation ![0-9]+}} +// CHECK: land.rhs: +// CHECK-NEXT: [[PGOCOUNT71:%.*]] = load i64, ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 1), align 8 +// CHECK-NEXT: [[TMP11:%.*]] = add i64 [[PGOCOUNT71]], 1 +// CHECK-NEXT: store i64 [[TMP11]], ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 1), align 8 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP30]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR31:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB32:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR31]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB32]], ptr [[TMP12]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR33:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR34:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR33]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR35:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB36:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR35]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR37:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP30]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB38:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR37]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP13:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR34]], ptr [[TMP13]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB36]], ptr [[TMP14]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB38]], ptr [[TMP15]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR39:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR40:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR39]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR41:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB42:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR41]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR43:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP29]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB44:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR43]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP46]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false), {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR47:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR48:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR47]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR49:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB50:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR49]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR51:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP46]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB52:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR51]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_PTR48]], ptr [[TMP16]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_UB50]], ptr [[TMP17]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: store ptr [[WIDE_PTR_LB52]], ptr [[TMP18]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR53:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 0, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_PTR54:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR53]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR55:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 1, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_UB56:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR55]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR57:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable.0", ptr [[AGG_TEMP45]], i32 0, i32 2, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[WIDE_PTR_LB58:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR57]], align 8, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR40]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[WIDE_PTR_PTR54]] to i64, {{!annotation ![0-9]+}} +// CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], {{!annotation ![0-9]+}} +// CHECK-NEXT: [[CMP59:%.*]] = icmp ule i64 [[TMP6]], [[SUB_PTR_SUB]], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[CMP59]], label [[LAND_RHSCNT:%.*]], label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.rhscnt: +// CHECK-NEXT: [[PGOCOUNT72:%.*]] = load i64, ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 2), align 8 +// CHECK-NEXT: [[TMP19:%.*]] = add i64 [[PGOCOUNT72]], 1 +// CHECK-NEXT: store i64 [[TMP19]], ptr getelementptr inbounds ([5 x i64], ptr @__profc_bar, i32 0, i32 2), align 8 +// CHECK-NEXT: br label [[LAND_END]], {{!annotation ![0-9]+}} +// CHECK: land.end: +// CHECK-NEXT: [[TMP20:%.*]] = phi i1 [ false, [[LAND_LHS_TRUE]] ], [ false, [[ENTRY:%.*]] ], [ [[CMP59]], [[LAND_RHSCNT]] ], [ [[CMP59]], [[LAND_RHS]] ], {{!annotation ![0-9]+}} +// CHECK-NEXT: br i1 [[TMP20]], label [[CONT:%.*]], label [[TRAP:%.*]], {{!annotation ![0-9]+}} +// CHECK: trap: +// CHECK-NEXT: call void @llvm.ubsantrap(i8 25) #[[ATTR7:[0-9]+]], {{!annotation ![0-9]+}} +// CHECK-NEXT: unreachable, {{!annotation ![0-9]+}} +// CHECK: cont: +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[AGG_TEMP60]], ptr align 8 [[AGG_TEMP]], i64 24, i1 false) +// CHECK-NEXT: [[WIDE_PTR_PTR_ADDR61:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP60]], i32 0, i32 0 +// CHECK-NEXT: [[WIDE_PTR_PTR62:%.*]] = load ptr, ptr [[WIDE_PTR_PTR_ADDR61]], align 8 +// CHECK-NEXT: [[WIDE_PTR_UB_ADDR63:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP60]], i32 0, i32 1 +// CHECK-NEXT: [[WIDE_PTR_UB64:%.*]] = load ptr, ptr [[WIDE_PTR_UB_ADDR63]], align 8 +// CHECK-NEXT: [[WIDE_PTR_LB_ADDR65:%.*]] = getelementptr inbounds nuw %"__bounds_safety::wide_ptr.bidi_indexable", ptr [[AGG_TEMP60]], i32 0, i32 2 +// CHECK-NEXT: [[WIDE_PTR_LB66:%.*]] = load ptr, ptr [[WIDE_PTR_LB_ADDR65]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call ptr @foo(ptr noundef [[WIDE_PTR_PTR62]], i64 noundef [[TMP6]]) +// CHECK-NEXT: [[CMP67:%.*]] = icmp sge i64 [[TMP6]], 0 +// CHECK-NEXT: call void @llvm.assume(i1 [[CMP67]]) +// CHECK-NEXT: [[ADD_PTR68:%.*]] = getelementptr inbounds nuw i8, ptr [[CALL]], i64 [[TMP6]] +// CHECK-NEXT: ret void +// +void bar(void*__sized_by(len) buf, unsigned long long len) +{ + foo(buf, len); +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/array-param-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/array-param-main.c new file mode 100644 index 0000000000000..b92d10fcfc4e4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/array-param-main.c @@ -0,0 +1,13 @@ + +#include + +// RUN: %clang_cc1 -fbounds-safety %s -I %S/include -verify=strict -fno-bounds-safety-relaxed-system-headers + +// RUN: %clang_cc1 -fbounds-safety %s -I %S/include -verify +// expected-no-diagnostics + +void foo(int * __counted_by(size) arr, int size) { + funcInSDK(size, arr); +} + + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/bounds-checks-inline.c b/clang/test/BoundsSafety/Sema/SystemHeaders/bounds-checks-inline.c new file mode 100644 index 0000000000000..718e1d18cf766 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/bounds-checks-inline.c @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -fbounds-safety %s -I %S/include -verify=legacy,legacy-incorrect,common-incorrect,common +// RUN: %clang_cc1 -fbounds-safety %s -I %S/include -verify=strict,common -fno-bounds-safety-relaxed-system-headers +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all %s -DTEST_COMPOUND_LITERALS -I %S/include -verify=incorrect,common-incorrect,common +// RUN: %clang_cc1 -fbounds-safety -fbounds-safety-bringup-missing-checks=all %s -DTEST_COMPOUND_LITERALS -I %S/include -verify=strict,common -fno-bounds-safety-relaxed-system-headers +#include diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/builtin-function-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/builtin-function-main.c new file mode 100644 index 0000000000000..7122ba9147292 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/builtin-function-main.c @@ -0,0 +1,16 @@ + +#include +#include + +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -ast-dump -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics + +char * __counted_by(len) func(char * __counted_by(len) src_str, int len) { + int len2 = 0; + char * __counted_by(len2) dst_str; + dst_str = __unsafe_forge_bidi_indexable(char*, malloc(len), len); + len2 = len; + memcpy(dst_str, src_str, len); + return dst_str; +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/array-param-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/array-param-sys.h new file mode 100644 index 0000000000000..f4e3bc90f788b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/array-param-sys.h @@ -0,0 +1,244 @@ +#include + +void extFunc(int size, int arr[size]); +void extFunc3(int size, int * __null_terminated arr); // strict-note{{passing argument to parameter 'arr' here}} + +#pragma clang system_header + +extern void extFunc2(int size, int *sizeLessArr); + +// CHECK: |-FunctionDecl [[func_extFunc:0x[^ ]+]] {{.+}} extFunc +// CHECK: | |-ParmVarDecl [[var_size:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | `-ParmVarDecl [[var_arr:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_extFunc3:0x[^ ]+]] {{.+}} extFunc3 +// CHECK: | |-ParmVarDecl [[var_size_1:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_arr_1:0x[^ ]+]] +// CHECK: |-FunctionDecl [[func_extFunc2:0x[^ ]+]] {{.+}} 'void (int, int *)' +// CHECK: | |-ParmVarDecl [[var_size_2:0x[^ ]+]] +// CHECK: | `-ParmVarDecl [[var_sizeLessArr:0x[^ ]+]] + +static inline void funcInSDK(int size, int arr[size]) { + extFunc(size, arr); + extFunc2(size, arr); +} + +// CHECK: |-FunctionDecl [[func_static:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_3:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_arr_2:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && size <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= size' +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __counted_by(size))' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_extFunc]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_1]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_2]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_3]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__bidi_indexable' +// CHECK: | `-CallExpr +// CHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *)' +// CHECK: | | `-DeclRefExpr {{.+}} [[func_extFunc2]] +// CHECK: | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_4]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_arr_2]] +// CHECK: | | `-OpaqueValueExpr [[ove_5]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_size_3]] +// CHECK: | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' + +static inline void funcInSDK2(int size, int *sizeLessArr) { + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single __counted_by(size)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcInSDK(size, sizeLessArr); +} + +// CHECK: |-FunctionDecl [[func_static_1:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_4:0x[^ ]+]] +// CHECK: | |-ParmVarDecl [[var_sizeLessArr_1:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | `-MaterializeSequenceExpr {{.+}} +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-CallExpr +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __counted_by(size))' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[func_static]] +// CHECK: | | | |-OpaqueValueExpr [[ove_6:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_7:0x[^ ]+]] {{.*}} 'int *' +// CHECK: | | |-OpaqueValueExpr [[ove_6]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_4]] +// CHECK: | | `-OpaqueValueExpr [[ove_7]] +// CHECK: | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | `-DeclRefExpr {{.+}} [[var_sizeLessArr_1]] +// CHECK: | |-OpaqueValueExpr [[ove_6]] {{.*}} 'int' +// CHECK: | `-OpaqueValueExpr [[ove_7]] {{.*}} 'int *' + +static inline void funcInSDK3(int size, int arr[size]) { + funcInSDK(size+1, arr); + // strict-error@+2{{passing 'int *__single __counted_by(size)' (aka 'int *__single') to parameter of incompatible type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // strict-note@21{{passing argument to parameter 'arr' here}} + extFunc3(size+1, arr); + // strict-error@+1{{assignment to 'size' requires corresponding assignment to 'int *__single __counted_by(size)' (aka 'int *__single') 'arr'; add self assignment 'arr = arr' if the value has not changed}} + size++; +} + +// CHECK: |-FunctionDecl [[func_static_2:0x[^ ]+]] {{.+}} static +// CHECK: | |-ParmVarDecl [[var_size_5:0x[^ ]+]] +// CHECK: | | `-DependerDeclsAttr +// CHECK: | |-ParmVarDecl [[var_arr_3:0x[^ ]+]] +// CHECK: | `-CompoundStmt +// CHECK: | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsCheckExpr {{.+}} 'arr <= __builtin_get_pointer_upper_bound(arr) && __builtin_get_pointer_lower_bound(arr) <= arr && size + 1 <= __builtin_get_pointer_upper_bound(arr) - arr && 0 <= size + 1' +// CHECK: | | | | |-CallExpr +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __counted_by(size))' +// CHECK: | | | | | | `-DeclRefExpr {{.+}} [[func_static]] +// CHECK: | | | | | |-OpaqueValueExpr [[ove_8:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9:0x[^ ]+]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | | |-OpaqueValueExpr [[ove_10:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | | | `-OpaqueValueExpr [[ove_11:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} lower +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'long' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-GetBoundExpr {{.+}} upper +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// CHECK: | | | | |-IntegerLiteral {{.+}} 0 +// CHECK: | | | | `-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_8]] +// CHECK: | | | | `-BinaryOperator {{.+}} 'int' '+' +// CHECK: | | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | | `-OpaqueValueExpr [[ove_9]] +// CHECK: | | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | | `-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: | | | | |-OpaqueValueExpr [[ove_10]] +// CHECK: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | | | | `-OpaqueValueExpr [[ove_11]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | | |-OpaqueValueExpr [[ove_10]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | `-OpaqueValueExpr [[ove_11]] {{.*}} 'int' +// CHECK: | | |-OpaqueValueExpr [[ove_8]] {{.*}} 'int' +// CHECK: | | `-OpaqueValueExpr [[ove_9]] {{.*}} 'int *__bidi_indexable' +// CHECK: | |-CallExpr +// CHECK: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int, int *__single __terminated_by(0))' +// CHECK: | | | `-DeclRefExpr {{.+}} [[func_extFunc3]] +// CHECK: | | |-BinaryOperator {{.+}} 'int' '+' +// CHECK: | | | |-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | | `-IntegerLiteral {{.+}} 1 +// CHECK: | | `-MaterializeSequenceExpr {{.+}} +// CHECK: | | |-MaterializeSequenceExpr {{.+}} +// CHECK: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | | | | |-OpaqueValueExpr [[ove_12:0x[^ ]+]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | |-BinaryOperator {{.+}} 'int *' '+' +// CHECK: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// CHECK: | | | | | | `-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | | `-OpaqueValueExpr [[ove_13:0x[^ ]+]] {{.*}} 'int' +// CHECK: | | | |-OpaqueValueExpr [[ove_12]] +// CHECK: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | | | `-DeclRefExpr {{.+}} [[var_arr_3]] +// CHECK: | | | `-OpaqueValueExpr [[ove_13]] +// CHECK: | | | `-ImplicitCastExpr {{.+}} 'int' +// CHECK: | | | `-DeclRefExpr {{.+}} [[var_size_5]] +// CHECK: | | |-OpaqueValueExpr [[ove_12]] {{.*}} 'int *__single __counted_by(size)':'int *__single' +// CHECK: | | `-OpaqueValueExpr [[ove_13]] {{.*}} 'int' +// CHECK: | `-UnaryOperator {{.+}} postfix '++' +// CHECK: | `-OpaqueValueExpr [[ove_14:0x[^ ]+]] {{.*}} lvalue + +static void tmp() { +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/bounds-checks-inline.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/bounds-checks-inline.h new file mode 100644 index 0000000000000..8bfe1138a9aa1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/bounds-checks-inline.h @@ -0,0 +1,162 @@ +// Pretend this is a system header +#pragma clang system_header +#include + +int* get_unspecified_ptr(void); +int* __unsafe_indexable get_unsafe_ptr(void); +// strict-note@+1 2{{passing argument to parameter 'ptr' here}} +void receive_cb(int* __counted_by(count) ptr, int count); + +// TODO: The `incorrect-error`s and `common-incorrect-error`s should not be emitted. +// They are a result of a bug +// and we emit the diagnostic rather than crashing (rdar://139815437). + +// FIXME: The `FIXME-common-incorrect-error`` should be emitted but aren't for +// some reason (rdar://140145190). + +inline int* __counted_by(count) inline_header_func_get_unspecified_ptr(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. + // strict-error@+2{{returning 'int *' from a function with incompatible result type 'int *__single __counted_by(count)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // incorrect-error@+1{{cannot extract the lower bound of 'int *' because it has no bounds specification}} + return get_unspecified_ptr(); +} + +inline int* __counted_by(count) inline_header_func_get_unsafe_ptr(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. + // strict-error@+2{{returning 'int *__unsafe_indexable' from a function with incompatible result type 'int *__single __counted_by(count)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // incorrect-error@+1{{cannot extract the lower bound of 'int *__unsafe_indexable' because it has no bounds specification}} + return get_unsafe_ptr(); +} + +inline int* __counted_by(count) inline_header_ret_explicit_unspecified_cast_0(int count) { + // Outside of system headers this implicit conversion **is allowed** + return (int*)0; +} + + +inline int* __counted_by(count) inline_header_ret_explicit_unsafe_indexable_cast_0(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. + return (int* __unsafe_indexable)0; +} + +inline int* __counted_by(count) inline_header_ret_0(int count) { + // Outside of system headers this implicit conversion **is allowed** + return 0; +} + +inline int* __counted_by(count) inline_header_ret_void_star_unspecified_0(int count) { + // Outside of system headers this implicit conversion **is allowed** + return (void*)0; +} + +inline int* __counted_by(count) inline_header_ret_void_star_unsafe_indexable_0(int count) { + // Outside of system headers this implicit conversion is not + // allowed but it's allowed in system headers. + return (void* __unsafe_indexable) 0; +} + +inline void inline_header_call_receive_cb_cast_0(void) { + receive_cb((int*)0, 0); +} + +inline void inline_header_call_receive_cb_cast_unspecified_ptr(void) { + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single __counted_by(count)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + receive_cb(get_unspecified_ptr(), 0); +} + +inline void inline_header_call_receive_cb_cast_unsafe_ptr(void) { + // strict-error@+1{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single __counted_by(count)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + receive_cb(get_unsafe_ptr(), 0); +} + +inline void inline_header_assign_local_cb_cast_0(void) { + int count = 0; + int* __counted_by(count) local = (int*)0; + + local = (int*)0; + count = 0; +} + + +void side_effect(void); + + +inline void inline_header_init_local_cb_unspecified_ptr(void) { + // common-error@+1{{local variable count must be declared right next to its dependent decl}} + int count = 0; + // strict-error@+3{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // common-error@+2{{local variable local must be declared right next to its dependent decl}} + // common-incorrect-error@+1{{cannot extract the lower bound of 'int *' because it has no bounds specification}} + int* __counted_by(count) local = get_unspecified_ptr(); +} + +inline void inline_header_assign_local_cb_unspecified_ptr(void) { + int count2 = 0; + int* __counted_by(count2) local2; + + side_effect(); + + // strict-error@+3{{assigning to 'int *__single __counted_by(count2)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // FIXME: The errors in the previous function stop this error from appearing for some reason. + // FIXME-common-incorrect-error@+1{{cannot extract the lower bound of 'int *' because it has no bounds specification}} + local2 = get_unspecified_ptr(); + // strict-error@+1{{assignment to 'count2' requires corresponding assignment to 'int *__single __counted_by(count2)' (aka 'int *__single') 'local2'; add self assignment 'local2 = local2' if the value has not changed}} + count2 = 0; +} + +inline void inline_header_init_local_cb_unsafe_ptr(void) { + // common-error@+1{{local variable count must be declared right next to its dependent decl}} + int count = 0; + // strict-error@+3{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // common-error@+2{{local variable local must be declared right next to its dependent decl}} + // common-incorrect-error@+1{{cannot extract the lower bound of 'int *__unsafe_indexable' because it has no bounds specification}} + int* __counted_by(count) local = get_unsafe_ptr(); +} + +inline void inline_header_assign_local_cb_unsafe_ptr(void) { + int count2 = 0; + int* __counted_by(count2) local2; + + side_effect(); + + // strict-error@+3{{assigning to 'int *__single __counted_by(count2)' (aka 'int *__single') from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // FIXME: The errors in the previous functions stop this error from appearing for some reason. + // FIXME-common-incorrect-error@+1{{cannot extract the lower bound of 'int *' because it has no bounds specification}} + local2 = get_unsafe_ptr(); + // strict-error@+1{{assignment to 'count2' requires corresponding assignment to 'int *__single __counted_by(count2)' (aka 'int *__single') 'local2'; add self assignment 'local2 = local2' if the value has not changed}} + count2 = 0; +} + +#ifdef TEST_COMPOUND_LITERALS + +struct simple_cb { + int count; + int* __counted_by(count) ptr; +}; + + +inline void inline_header_compound_literal_unspecified_ptr(struct simple_cb s, int* unspecified_ptr) { + // FIXME: This diagnostic isn't emitted when there are diagnostics from other functions. + // strict-error@+2{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // fixme-incorrect-error@+1{{cannot extract the lower bound of 'int *' because it has no bounds specification}} + s = (struct simple_cb){.count = 0, .ptr = unspecified_ptr}; +} + +inline void inline_header_compound_literal_unspecified_cast_0(struct simple_cb s) { + s = (struct simple_cb){.count = 0, .ptr = (int*)0}; +} + +inline void inline_header_compound_literal_unsafe_indexable_cast_0(struct simple_cb s) { + s = (struct simple_cb){.count = 0, .ptr = (int* __unsafe_indexable)0}; +} + +inline void inline_header_compound_literal_unsafe_indexable_ptr(struct simple_cb s, int* __unsafe_indexable unsafe_indexable_ptr) { + // strict-error@+2{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // incorrect-error@+1{{cannot extract the lower bound of 'int *__unsafe_indexable' because it has no bounds specification}} + s = (struct simple_cb){.count = 0, .ptr = unsafe_indexable_ptr}; +} + +#endif diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/builtin-function-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/builtin-function-sys.h new file mode 100644 index 0000000000000..c4e61a3fa57a6 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/builtin-function-sys.h @@ -0,0 +1,24 @@ +#pragma clang system_header + +static inline void memcpy(void *__restrict__ dst, void *__restrict__ src, int size) { + __builtin_memcpy(dst, src, size); +} + +static inline void* memcpy2(void *__restrict__ dst, void *__restrict__ src, int size) { + return __builtin_memcpy(dst, src, size); +} + +static inline void* memcpy3(void *__restrict__ dst, void *__restrict__ src, int size) { + void * tmp = __builtin_memcpy(dst, src, size); + return tmp; +} + +static inline void* malloc(int size) { + return __builtin_malloc(size); +} + +static inline void* malloc2(int size) { + void *tmp = __builtin_malloc(size); + return tmp; +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/default-attributes-in-preprocessed.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/default-attributes-in-preprocessed.h new file mode 100644 index 0000000000000..797da3ba99c7a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/default-attributes-in-preprocessed.h @@ -0,0 +1,5 @@ +#pragma once + +inline void increment_unsafe_p(int *p) { + p++; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/int-to-ptr-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/int-to-ptr-sys.h new file mode 100644 index 0000000000000..0219965228e71 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/int-to-ptr-sys.h @@ -0,0 +1,19 @@ +#include +#include + +// both-note@+2{{passing argument to parameter 'p' here}} +// strict-note@+1{{passing argument to parameter 'p' here}} +static inline int * __single funcAdopted(int * __single p) { + return p; +} + +#pragma clang system_header + +static inline int* funcSDK(intptr_t x) { + if (x % 128) + // both-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + return funcAdopted(x); + else + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return funcAdopted((int*)x); +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/static-bound-ptr-init.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/static-bound-ptr-init.h new file mode 100644 index 0000000000000..3ffbba78b919e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/static-bound-ptr-init.h @@ -0,0 +1,3 @@ +#pragma clang system_header + +const char *mock_system_func(void); \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/struct-fields-middle-man.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/struct-fields-middle-man.h new file mode 100644 index 0000000000000..12dcd459e6239 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/struct-fields-middle-man.h @@ -0,0 +1,6 @@ +#ifdef EXTINCLUDE2 +#include +#else +#include "struct-fields-ext.h" +#endif + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/struct-fields-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/struct-fields-sys.h new file mode 100644 index 0000000000000..18fdd09dcdeca --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/struct-fields-sys.h @@ -0,0 +1,101 @@ +#pragma clang system_header + +#include + +struct foo { + int *__counted_by(count) p; + int count; +}; + +struct bar { + int *__ended_by(end) p; + int *end; +}; + +static inline struct foo funcInSDK1(int *p, int count) { + //strict-error@+1{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct foo f = { p, count }; + return f; +} + +static inline struct foo funcInSDK2(int *p, int count) { + //strict-error@+1{{initializing 'int *__single __counted_by(count)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return (struct foo){ p, count }; +} + +static inline struct foo funcInSDK3(int *p, int count) { + struct foo f; + //strict-error@+1{{assigning to 'int *__single __counted_by(count)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + f.p = p; // This results in a RecoveryExpr, so later analysis cannot see the assignment. + //strict-error@+1{{assignment to 'f.count' requires corresponding assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p'; add self assignment 'f.p = f.p' if the value has not changed}} + f.count = count; + return f; +} + + +static inline struct foo funcInSDK4(int *p, int count) { + struct foo f; + //strict-error@+1{{assigning to 'int *__single __counted_by(count)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + f.p = p; + return f; +} + +static inline struct foo funcInSDK5(int *p, int count) { + struct foo f; + //strict-error@+1{{assignment to 'f.count' requires corresponding assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p'; add self assignment 'f.p = f.p' if the value has not changed}} + f.count = count; + return f; +} + +static inline struct bar funcInSDK6(int *p, int *end) { + //strict-error@+2{{initializing 'int *__single __ended_by(end)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + //strict-error@+1{{initializing 'int *__single /* __started_by(p) */ ' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct bar b = { p, end }; + return b; +} + +static inline struct bar funcInSDK7(int *p, int *end) { + //strict-error@+2{{initializing 'int *__single __ended_by(end)' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + //strict-error@+1{{initializing 'int *__single /* __started_by(p) */ ' (aka 'int *__single') with an expression of incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return (struct bar){ p, end }; +} + +static inline struct bar funcInSDK8(int *p, int *end) { + struct bar b; + //strict-error@+1{{assigning to 'int *__single __ended_by(end)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + b.p = p; + //strict-error@+1{{assigning to 'int *__single /* __started_by(p) */ ' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + b.end = end; + return b; +} + +static inline struct bar funcInSDK9(struct bar in) { + struct bar b; + //strict-error@+1{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'b.p' requires corresponding assignment to 'b.end'; add self assignment 'b.end = b.end' if the value has not changed}} + b.p = in.p; + return b; +} + +static inline struct bar funcInSDK10(struct bar in) { + struct bar b; + //strict-error@+1{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'b.end' requires corresponding assignment to 'b.p'; add self assignment 'b.p = b.p' if the value has not changed}} + b.end = in.end; + return b; +} + +static inline struct foo funcInSDK11(struct foo in) { + struct foo f; + //strict-error@+1{{assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p' requires corresponding assignment to 'f.count'; add self assignment 'f.count = f.count' if the value has not changed}} + f.p = in.p; + return f; +} + +static inline struct foo funcInSDK12(struct foo in) { + struct foo f; + //strict-error@+1{{assignment to 'f.count' requires corresponding assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'f.p'; add self assignment 'f.p = f.p' if the value has not changed}} + f.count = in.count; + return f; +} + +static tmp() { +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/system-header-func-decl.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/system-header-func-decl.h new file mode 100644 index 0000000000000..0dd43c66b1c47 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/system-header-func-decl.h @@ -0,0 +1,3 @@ +#pragma clang system_header + +int* foo(int ** fp); diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/system-header-unsafe-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/system-header-unsafe-sys.h new file mode 100644 index 0000000000000..070f52e923a24 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/system-header-unsafe-sys.h @@ -0,0 +1,13 @@ +#include + +//strict-note@+1{{passing argument to parameter 'foo' here}} +void funcWithAnnotation(char *__sized_by(4) foo, char *__sized_by(5) bar); + + +#pragma clang system_header + +void funcInSDK(char *ptr, char * __bidi_indexable bidi) { + //strict-error@+1{{passing 'char *' to parameter of incompatible type 'char *__single __sized_by(4)' (aka 'char *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcWithAnnotation(ptr, bidi); +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/typedefs-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/typedefs-sys.h new file mode 100644 index 0000000000000..31bf896e49502 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/typedefs-sys.h @@ -0,0 +1,44 @@ +#include + +typedef const char * __null_terminated my_str_explicit_t; +typedef const char * my_str_implicit_t; +typedef int * __null_terminated my_nt_int_ptr_t; +typedef int * my_int_ptr_t; + +// both-error@+1{{'__counted_by' inside typedef is only allowed for function type}} +typedef int * __counted_by(4) ivec4_t; // If this is ever allowed we need to handle it for system headers. + +#pragma clang system_header + +static inline my_str_implicit_t funcInSDK1(const char *p) { + my_str_implicit_t str = p; + return str; + return p; +} + +static inline my_str_explicit_t funcInSDK2(const char *p) { + //strict-error@+1{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + my_str_explicit_t str = p; + return str; + //strict-error@+1{{returning 'const char *' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; +} + +static inline my_nt_int_ptr_t funcInSDK3(int *p) { + //strict-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + my_nt_int_ptr_t p2 = p; + return p2; + //strict-error@+1{{returning 'int *' from a function with incompatible result type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; +} + +typedef my_int_ptr_t __null_terminated nt_local_t; + +static inline nt_local_t funcInSDK4(int *p) { + //strict-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + nt_local_t p2 = p; + return p2; + //strict-error@+1{{returning 'int *' from a function with incompatible result type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-global-ext.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-global-ext.h new file mode 100644 index 0000000000000..561dc1051d12d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-global-ext.h @@ -0,0 +1,5 @@ +#include + +extern int *__sized_by(2) sizedGlobal; +extern int *__terminated_by(2) valueTerminatedGlobal; +extern int *__bidi_indexable bidiGlobal; diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-global-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-global-sys.h new file mode 100644 index 0000000000000..3d44db2f51a52 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-global-sys.h @@ -0,0 +1,216 @@ +#include + +extern int *__sized_by(2) sizedGlobal; +extern int *__terminated_by(2) valueTerminatedGlobal; +extern int *__bidi_indexable bidiGlobal; + +// RELAXED: |-VarDecl [[var_sizedGlobal:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_bidiGlobal:0x[^ ]+]] + +// STRICT: VarDecl [[var_sizedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_bidiGlobal:0x[^ ]+]] + +#pragma clang system_header + +void funcInSDK(int * unsafePointer) { + sizedGlobal = unsafePointer; //strict-error{{assigning to 'int *__single __sized_by(2)' (aka 'int *__single') from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + valueTerminatedGlobal = unsafePointer; //strict-error{{assigning to 'int *__single __terminated_by(2)' (aka 'int *__single') from incompatible type 'int *' is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + // This should result in an unsafe BoundsSafetyPointerCast rdar://99202425 +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single __sized_by(2)':'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *' +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] + +void funcInSDK2(int * __single __terminated_by(2) safePointer) { + sizedGlobal = safePointer; //strict-error{{assigning to 'int *__single __sized_by(2)' (aka 'int *__single') from incompatible type 'int *__single __terminated_by(2)' (aka 'int *__single') requires a linear search for the terminator; use '__terminated_by_to_indexable()' to perform this conversion explicitly}} + valueTerminatedGlobal = safePointer; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// RELAXED: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single __sized_by(2)':'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +// STRICT: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// STRICT: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +void funcInSDK3(int * unsafePointer) { + unsafePointer = sizedGlobal; + unsafePointer = valueTerminatedGlobal; + unsafePointer = bidiGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | | | | |-OpaqueValueExpr [[ove_2:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// RELAXED: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// RELAXED: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | | `-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_3:0x[^ ]+]] {{.*}} 'int' +// RELAXED: | | | |-OpaqueValueExpr [[ove_2]] +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | | `-OpaqueValueExpr [[ove_3]] +// RELAXED: | | | `-IntegerLiteral {{.+}} 2 +// RELAXED: | | |-OpaqueValueExpr [[ove_2]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | `-OpaqueValueExpr [[ove_3]] {{.*}} 'int' +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// RELAXED: | `-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-MaterializeSequenceExpr {{.+}} +// STRICT: | | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// STRICT: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// STRICT: | | | |-OpaqueValueExpr [[ove]] +// STRICT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | | `-OpaqueValueExpr [[ove_1]] +// STRICT: | | | `-IntegerLiteral {{.+}} 2 +// STRICT: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] +// STRICT: | `-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +void funcInSDK4(int * __single __terminated_by(2) safePointer) { + safePointer = sizedGlobal; //strict-error{{assigning to 'int *__single __terminated_by(2)' (aka 'int *__single') from incompatible type 'int *__single __sized_by(2)' (aka 'int *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + safePointer = valueTerminatedGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// RELAXED: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// RELAXED: | | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | | | | |-OpaqueValueExpr [[ove_4:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | `-BinaryOperator {{.+}} 'char *' '+' +// RELAXED: | | | | | |-CStyleCastExpr {{.+}} 'char *' +// RELAXED: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | | `-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove_5:0x[^ ]+]] {{.*}} 'int' +// RELAXED: | | | |-OpaqueValueExpr [[ove_4]] +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | | `-OpaqueValueExpr [[ove_5]] +// RELAXED: | | | `-IntegerLiteral {{.+}} 2 +// RELAXED: | | |-OpaqueValueExpr [[ove_4]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | `-OpaqueValueExpr [[ove_5]] {{.*}} 'int' +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// STRICT: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | `-BinaryOperator {{.+}} 'int *__single __terminated_by(2)':'int *__single' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_safePointer_1]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +// MAINCHECK: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// MAINCHECK: |-ParmVarDecl [[var_unsafe:0x[^ ]+]] +// MAINCHECK: |-ParmVarDecl [[var_term:0x[^ ]+]] +// MAINCHECK: `-CompoundStmt +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __terminated_by(2))' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK2]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_term]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: `-CallExpr +// MAINCHECK: |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __terminated_by(2))' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[func_funcInSDK4]] +// MAINCHECK: `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// MAINCHECK: `-DeclRefExpr {{.+}} [[var_term]] + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-inter-sysheader-other-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-inter-sysheader-other-sys.h new file mode 100644 index 0000000000000..e0e0269f51bde --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-inter-sysheader-other-sys.h @@ -0,0 +1,9 @@ +#include + +#pragma clang system_header + +// strict-note@+1{{passing argument to parameter 'foo' here}} +void funcWithAnnotation(int *__sized_by(4) foo); +void funcWithoutAnnotation(int * foo); +extern int * __single safeGlobal; +extern int * unsafeGlobal; diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-inter-sysheader-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-inter-sysheader-sys.h new file mode 100644 index 0000000000000..ff58385741e39 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-inter-sysheader-sys.h @@ -0,0 +1,308 @@ +#include + +// RELAXED: |-FunctionDecl [[func_funcWithAnnotation:0x[^ ]+]] {{.+}} funcWithAnnotation +// RELAXED: | `-ParmVarDecl [[var_foo:0x[^ ]+]] +// RELAXED: |-FunctionDecl [[func_funcWithoutAnnotation:0x[^ ]+]] {{.+}} funcWithoutAnnotation +// RELAXED: | `-ParmVarDecl [[var_foo_1:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_safeGlobal:0x[^ ]+]] +// RELAXED: |-VarDecl [[var_unsafeGlobal:0x[^ ]+]] + +// STRICT: |-FunctionDecl [[func_funcWithAnnotation:0x[^ ]+]] {{.+}} funcWithAnnotation +// STRICT: | `-ParmVarDecl [[var_foo:0x[^ ]+]] +// STRICT: |-FunctionDecl [[func_funcWithoutAnnotation:0x[^ ]+]] {{.+}} funcWithoutAnnotation +// STRICT: | `-ParmVarDecl [[var_foo_1:0x[^ ]+]] +// STRICT: VarDecl [[var_safeGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_unsafeGlobal:0x[^ ]+]] + +#pragma clang system_header + +void funcInSDK(int * unsafePointer) { + // strict-error@+1{{assigning to 'int *__single' from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + safeGlobal = unsafePointer; + unsafeGlobal = unsafePointer; + unsafePointer = safeGlobal; + unsafePointer = unsafeGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | `-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | `-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | |-DeclRefExpr {{.+}} [[var_unsafePointer]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +void funcInSDK2(int * unsafePointer) { + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single __sized_by(4)' (aka 'int *__single') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcWithAnnotation(unsafePointer); + funcWithoutAnnotation(unsafePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-CallExpr +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(4))' +// RELAXED: | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// RELAXED: | | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(4)':'int *__single' +// RELAXED: | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *' +// RELAXED: | | | `-OpaqueValueExpr [[ove]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// RELAXED: | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *' +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +// STRICT: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | | `-RecoveryExpr +// STRICT: | | |-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] +// STRICT: | `-CallExpr +// STRICT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +// strict-note@+1{{passing argument to parameter 'safePointer' here}} +void funcInSDK3(int * __single safePointer) { + safeGlobal = safePointer; + unsafeGlobal = safePointer; + safePointer = safeGlobal; + // strict-error@+1{{assigning to 'int *__single' from incompatible type 'int *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + safePointer = unsafeGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// RELAXED: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | |-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | | |-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// RELAXED: | `-BinaryOperator {{.+}} 'int *__single' '=' +// RELAXED: | |-DeclRefExpr {{.+}} [[var_safePointer]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// STRICT: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-BinaryOperator {{.+}} 'int *__single' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_unsafeGlobal]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | |-BinaryOperator {{.+}} 'int *__single' '=' +// STRICT: | | |-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safeGlobal]] +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[var_safePointer]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafeGlobal]] + +void funcInSDK4(int * __single safePointer) { + funcWithAnnotation(safePointer); + funcWithoutAnnotation(safePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// RELAXED: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | | |-CallExpr +// RELAXED: | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(4))' +// RELAXED: | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int *__single' +// RELAXED: | | | `-OpaqueValueExpr [[ove_1]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_safePointer_1]] +// RELAXED: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int *__single' +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer_1]] + +// STRICT: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// STRICT: | |-ParmVarDecl [[var_safePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | | |-BoundsCheckExpr {{.+}} 'safePointer <= __builtin_get_pointer_upper_bound(safePointer) && __builtin_get_pointer_lower_bound(safePointer) <= safePointer && 4 <= (char *)__builtin_get_pointer_upper_bound(safePointer) - (char *)safePointer && 0 <= 4' +// STRICT: | | | | |-CallExpr +// STRICT: | | | | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single __sized_by(4))' +// STRICT: | | | | | | `-DeclRefExpr {{.+}} [[func_funcWithAnnotation]] +// STRICT: | | | | | `-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single' +// STRICT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// STRICT: | | | | |-BinaryOperator {{.+}} 'int' '&&' +// STRICT: | | | | | |-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | | `-GetBoundExpr {{.+}} upper +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | `-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | | |-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | | `-GetBoundExpr {{.+}} lower +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | `-BinaryOperator {{.+}} 'int' '&&' +// STRICT: | | | | |-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | | |-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'long' +// STRICT: | | | | | `-BinaryOperator {{.+}} 'long' '-' +// STRICT: | | | | | |-ImplicitCastExpr {{.+}} 'char *' +// STRICT: | | | | | | `-CStyleCastExpr {{.+}} 'char *__bidi_indexable' +// STRICT: | | | | | | `-GetBoundExpr {{.+}} upper +// STRICT: | | | | | | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | | | | `-DeclRefExpr {{.+}} 'int *__single' lvalue ParmVar {{.+}} 'safePointer' 'int *__single' +// STRICT: | | | | `-BinaryOperator {{.+}} 'int' '<=' +// STRICT: | | | | |-ImplicitCastExpr {{.+}} 'long' +// STRICT: | | | | | `-IntegerLiteral {{.+}} 0 +// STRICT: | | | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// STRICT: | | | |-OpaqueValueExpr [[ove]] +// STRICT: | | | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | | | `-DeclRefExpr {{.+}} [[var_safePointer_1]] +// STRICT: | | | `-OpaqueValueExpr [[ove_1]] +// STRICT: | | | `-ImplicitCastExpr {{.+}} 'long' +// STRICT: | | | `-IntegerLiteral {{.+}} 4 +// STRICT: | | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single' +// STRICT: | | `-OpaqueValueExpr [[ove_1]] {{.*}} 'long' +// STRICT: | `-CallExpr +// STRICT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | `-DeclRefExpr {{.+}} [[func_funcWithoutAnnotation]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer_1]] + +void funcInSDK5(int * unsafePointer) { + funcInSDK(unsafePointer); + // strict-error@+1{{passing 'int *' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + funcInSDK3(unsafePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_2:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-CallExpr +// RELAXED: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] + +// STRICT: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_2:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-CallExpr +// STRICT: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] +// STRICT: | `-ImplicitCastExpr {{.+}} contains-errors +// STRICT: | `-RecoveryExpr +// STRICT: | |-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer_2]] + +void funcInSDK6(int * __single safePointer) { + funcInSDK(safePointer); + funcInSDK3(safePointer); +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// RELAXED: | |-ParmVarDecl [[var_safePointer_2:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | |-CallExpr +// RELAXED: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | | `-DeclRefExpr {{.+}} [[var_safePointer_2]] +// RELAXED: | `-CallExpr +// RELAXED: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single)' +// RELAXED: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer_2]] + +// STRICT: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// STRICT: | |-ParmVarDecl [[var_safePointer_2:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | |-CallExpr +// STRICT: | | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *)' +// STRICT: | | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | | `-DeclRefExpr {{.+}} [[var_safePointer_2]] +// STRICT: | `-CallExpr +// STRICT: | |-ImplicitCastExpr {{.+}} 'void (*__single)(int *__single)' +// STRICT: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer_2]] + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-return-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-return-sys.h new file mode 100644 index 0000000000000..484efba4f7fc9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/unsafe-return-sys.h @@ -0,0 +1,177 @@ +#include + +// RELAXED: VarDecl [[var_sizedGlobal:0x[^ ]+]] +// RELAXED: VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// RELAXED: VarDecl [[var_bidiGlobal:0x[^ ]+]] + +// STRICT: VarDecl [[var_sizedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_valueTerminatedGlobal:0x[^ ]+]] +// STRICT: VarDecl [[var_bidiGlobal:0x[^ ]+]] + +#pragma clang system_header + +int * __unsafe_indexable funcInSDK(int * __unsafe_indexable unsafePointer) { + return unsafePointer; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// RELAXED: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] + +// STRICT: |-FunctionDecl [[func_funcInSDK:0x[^ ]+]] {{.+}} funcInSDK +// STRICT: | |-ParmVarDecl [[var_unsafePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer]] + +int * __unsafe_indexable funcInSDK2(int * __single __terminated_by(2) safePointer) { + return safePointer; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// RELAXED: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +// STRICT: |-FunctionDecl [[func_funcInSDK2:0x[^ ]+]] {{.+}} funcInSDK2 +// STRICT: | |-ParmVarDecl [[var_safePointer:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_safePointer]] + +int * __single funcInSDK3(int * __unsafe_indexable unsafePointer) { + return unsafePointer; // strict-error{{returning 'int *__unsafe_indexable' from a function with incompatible result type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// RELAXED: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +// STRICT: |-FunctionDecl [[func_funcInSDK3:0x[^ ]+]] {{.+}} funcInSDK3 +// STRICT: | |-ParmVarDecl [[var_unsafePointer_1:0x[^ ]+]] +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-RecoveryExpr +// STRICT: | `-DeclRefExpr {{.+}} [[var_unsafePointer_1]] + +int * __unsafe_indexable funcInSDK4(void) { + return sizedGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-MaterializeSequenceExpr {{.+}} +// RELAXED: | |-MaterializeSequenceExpr {{.+}} +// RELAXED: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | |-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// RELAXED: | | | | |-CStyleCastExpr {{.+}} 'char *' +// RELAXED: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// RELAXED: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// RELAXED: | | |-OpaqueValueExpr [[ove]] +// RELAXED: | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// RELAXED: | | `-OpaqueValueExpr [[ove_1]] +// RELAXED: | | `-IntegerLiteral {{.+}} 2 +// RELAXED: | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// RELAXED: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' + +// STRICT: |-FunctionDecl [[func_funcInSDK4:0x[^ ]+]] {{.+}} funcInSDK4 +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// STRICT: | `-MaterializeSequenceExpr {{.+}} +// STRICT: | |-MaterializeSequenceExpr {{.+}} +// STRICT: | | |-BoundsSafetyPointerPromotionExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | | | |-OpaqueValueExpr [[ove:0x[^ ]+]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | |-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | `-BinaryOperator {{.+}} 'char *' '+' +// STRICT: | | | | |-CStyleCastExpr {{.+}} 'char *' +// STRICT: | | | | | `-ImplicitCastExpr {{.+}} 'int *' +// STRICT: | | | | | `-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | | `-OpaqueValueExpr [[ove_1:0x[^ ]+]] {{.*}} 'int' +// STRICT: | | |-OpaqueValueExpr [[ove]] +// STRICT: | | | `-ImplicitCastExpr {{.+}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | | | `-DeclRefExpr {{.+}} [[var_sizedGlobal]] +// STRICT: | | `-OpaqueValueExpr [[ove_1]] +// STRICT: | | `-IntegerLiteral {{.+}} 2 +// STRICT: | |-OpaqueValueExpr [[ove]] {{.*}} 'int *__single __sized_by(2)':'int *__single' +// STRICT: | `-OpaqueValueExpr [[ove_1]] {{.*}} 'int' + +int * __unsafe_indexable funcInSDK5(void) { + return valueTerminatedGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK5:0x[^ ]+]] {{.+}} funcInSDK5 +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// STRICT: | `-DeclRefExpr {{.+}} [[var_valueTerminatedGlobal]] + +int * __unsafe_indexable funcInSDK6(void) { + return bidiGlobal; +} + +// RELAXED: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// RELAXED: | `-CompoundStmt +// RELAXED: | `-ReturnStmt +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// RELAXED: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// RELAXED: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +// STRICT: |-FunctionDecl [[func_funcInSDK6:0x[^ ]+]] {{.+}} funcInSDK6 +// STRICT: | `-CompoundStmt +// STRICT: | `-ReturnStmt +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// STRICT: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// STRICT: | `-DeclRefExpr {{.+}} [[var_bidiGlobal]] + +// MAINCHECK: `-FunctionDecl [[func_func:0x[^ ]+]] {{.+}} func +// MAINCHECK: |-ParmVarDecl [[var_unsafe:0x[^ ]+]] +// MAINCHECK: |-ParmVarDecl [[var_term:0x[^ ]+]] +// MAINCHECK: `-CompoundStmt +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(int *__unsafe_indexable)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(int *__single __terminated_by(2))' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK2]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__single __terminated_by(2)':'int *__single' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_term]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | |-ImplicitCastExpr {{.+}} 'int *__single(*__single)(int *__unsafe_indexable)' +// MAINCHECK: | | `-DeclRefExpr {{.+}} [[func_funcInSDK3]] +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[var_unsafe]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(void)' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[func_funcInSDK4]] +// MAINCHECK: |-CallExpr +// MAINCHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(void)' +// MAINCHECK: | `-DeclRefExpr {{.+}} [[func_funcInSDK5]] +// MAINCHECK: `-CallExpr +// MAINCHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable(*__single)(void)' +// MAINCHECK: `-DeclRefExpr {{.+}} [[func_funcInSDK6]] diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/include/va-list-sys.h b/clang/test/BoundsSafety/Sema/SystemHeaders/include/va-list-sys.h new file mode 100644 index 0000000000000..4e46443003dca --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/include/va-list-sys.h @@ -0,0 +1,17 @@ +#include +#include + +#pragma clang system_header + +typedef void * (*variable_length_function)(va_list args); +static inline void* call_func_internal(variable_length_function f, va_list args) { + return f(args); +} + +static inline void* call_func(variable_length_function f, ...) { + va_list ap; + + va_start(ap, f); + return call_func_internal(f, ap); +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/int-to-ptr-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/int-to-ptr-main.c new file mode 100644 index 0000000000000..4b416ac2a55c5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/int-to-ptr-main.c @@ -0,0 +1,13 @@ + +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify=both -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=both -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict,both -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict,both -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +int * func(intptr_t y) { + // both-error@+1{{returning 'int *' from a function with incompatible result type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + return funcSDK(y); +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/struct-fields.c b/clang/test/BoundsSafety/Sema/SystemHeaders/struct-fields.c new file mode 100644 index 0000000000000..dd94c99ee4bb8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/struct-fields.c @@ -0,0 +1,24 @@ + +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics + +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +void func(int * a, int * b, struct bar in, struct foo in2) { + funcInSDK1(a, *b); + funcInSDK2(a, *b); + funcInSDK3(a, *b); + funcInSDK4(a, *b); + funcInSDK5(a, *b); + funcInSDK6(a, b); + funcInSDK7(a, b); + funcInSDK8(a, b); + funcInSDK9(in); + funcInSDK10(in); + funcInSDK11(in2); + funcInSDK12(in2); +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/system-header-unsafe-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/system-header-unsafe-main.c new file mode 100644 index 0000000000000..d5878723857f0 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/system-header-unsafe-main.c @@ -0,0 +1,13 @@ + +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics + +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +void func(char * __unsafe_indexable ptr, char * __bidi_indexable bidi) { + funcInSDK(ptr, bidi); +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/typedefs.c b/clang/test/BoundsSafety/Sema/SystemHeaders/typedefs.c new file mode 100644 index 0000000000000..923feca691b4e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/typedefs.c @@ -0,0 +1,16 @@ + +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify=both -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=both -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict,both -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict,both -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +void func(const char * a, int * b) { + funcInSDK1(a); + funcInSDK2(a); + funcInSDK3(b); + funcInSDK4(b); +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-global-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-global-main.c new file mode 100644 index 0000000000000..e411067eed0ae --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-global-main.c @@ -0,0 +1,16 @@ + +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics +// +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +void func(int * __unsafe_indexable unsafe, int * __terminated_by(2) term) { + funcInSDK(unsafe); + funcInSDK2(term); + funcInSDK3(unsafe); + funcInSDK4(term); +} diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-inter-sysheader-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-inter-sysheader-main.c new file mode 100644 index 0000000000000..c8436873b56b6 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-inter-sysheader-main.c @@ -0,0 +1,17 @@ + +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics +// +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +void func(int * __unsafe_indexable unsafe, int * __single safe) { + funcInSDK(unsafe); + funcInSDK2(unsafe); + funcInSDK3(safe); + funcInSDK4(safe); +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-return-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-return-main.c new file mode 100644 index 0000000000000..1326597aaf80a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/unsafe-return-main.c @@ -0,0 +1,19 @@ + +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics +// +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify=strict -fno-bounds-safety-relaxed-system-headers -I %S/include -x objective-c -fbounds-attributes-objc-experimental + +void func(int * __unsafe_indexable unsafe, int * __terminated_by(2) term) { + funcInSDK(unsafe); + funcInSDK2(term); + funcInSDK3(unsafe); + funcInSDK4(); + funcInSDK5(); + funcInSDK6(); +} + diff --git a/clang/test/BoundsSafety/Sema/SystemHeaders/va-list-main.c b/clang/test/BoundsSafety/Sema/SystemHeaders/va-list-main.c new file mode 100644 index 0000000000000..c5673ad8d8397 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/SystemHeaders/va-list-main.c @@ -0,0 +1,14 @@ + +#include + +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include +// RUN: %clang_cc1 -fbounds-safety %s -verify -I %S/include -x objective-c -fbounds-attributes-objc-experimental +// expected-no-diagnostics +extern variable_length_function func_ptr; +typedef void * (*variable_length_function2)(va_list args); +extern variable_length_function2 func_ptr2; + +void func(char *dst_str, char *src_str, int len) { + call_func(func_ptr, dst_str, src_str, len); + call_func(func_ptr2, dst_str, src_str, len); +} diff --git a/clang/test/BoundsSafety/Sema/abi-ptr-attr-pragma-error-checks.c b/clang/test/BoundsSafety/Sema/abi-ptr-attr-pragma-error-checks.c new file mode 100644 index 0000000000000..54d56fde74bfa --- /dev/null +++ b/clang/test/BoundsSafety/Sema/abi-ptr-attr-pragma-error-checks.c @@ -0,0 +1,10 @@ + + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#pragma clang abi_ptr_attr set(single) +#pragma clang abi_ptr_attr set(indexable) +#pragma clang abi_ptr_attr set(bidi_indexable) +#pragma clang abi_ptr_attr set(unsafe_indexable) +#pragma clang abi_ptr_attr set(nullable) // expected-error{{'nullable' cannot be set as a default pointer attribute}} diff --git a/clang/test/BoundsSafety/Sema/abi-ptr-attr.c b/clang/test/BoundsSafety/Sema/abi-ptr-attr.c new file mode 100644 index 0000000000000..ba0bd7f921867 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/abi-ptr-attr.c @@ -0,0 +1,85 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -verify %s | FileCheck %s +// RUN: %clang_cc1 -ast-dump -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s | FileCheck %s +#include + +__ptrcheck_abi_assume_single() +int *FSingle(int *x) { + int **y = &x; + return *y; +} +// CHECK: |-FunctionDecl {{.+}} FSingle 'int *__single(int *__single)' +// CHECK: | |-ParmVarDecl {{.+}} used x 'int *__single' +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} used y 'int *__single*__bidi_indexable' cinit +// CHECK: | | `-UnaryOperator {{.+}} 'int *__single*__bidi_indexable' prefix '&' cannot overflow +// CHECK: | | `-DeclRefExpr {{.+}} 'int *__single' lvalue ParmVar {{.+}} 'x' 'int *__single' +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single' +// CHECK: | `-UnaryOperator {{.+}} 'int *__single' lvalue prefix '*' cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__single*__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__single*__bidi_indexable' lvalue Var {{.+}} 'y' 'int *__single*__bidi_indexable' + + + +__ptrcheck_abi_assume_indexable() +int *FIndexable(int *x) { + int **y = &x; + return *y; +} +// CHECK: |-FunctionDecl {{.+}} FIndexable 'int *__indexable(int *__indexable)' +// CHECK: | |-ParmVarDecl {{.+}} used x 'int *__indexable' +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} used y 'int *__indexable*__bidi_indexable' cinit +// CHECK: | | `-UnaryOperator {{.+}} 'int *__indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK: | | `-DeclRefExpr {{.+}} 'int *__indexable' lvalue ParmVar {{.+}} 'x' 'int *__indexable' +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__indexable' +// CHECK: | `-UnaryOperator {{.+}} 'int *__indexable' lvalue prefix '*' cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__indexable*__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__indexable*__bidi_indexable' lvalue Var {{.+}} 'y' 'int *__indexable*__bidi_indexable' + + +__ptrcheck_abi_assume_bidi_indexable() +int *FBidiIndexable(int *x) { + int **y = &x; + return *y; +} +// CHECK: |-FunctionDecl {{.+}} FBidiIndexable 'int *__bidi_indexable(int *__bidi_indexable)' +// CHECK: | |-ParmVarDecl {{.+}} used x 'int *__bidi_indexable' +// CHECK: | `-CompoundStmt +// CHECK: | |-DeclStmt +// CHECK: | | `-VarDecl {{.+}} used y 'int *__bidi_indexable*__bidi_indexable' cinit +// CHECK: | | `-UnaryOperator {{.+}} 'int *__bidi_indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK: | | `-DeclRefExpr {{.+}} 'int *__bidi_indexable' lvalue ParmVar {{.+}} 'x' 'int *__bidi_indexable' +// CHECK: | `-ReturnStmt +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable' +// CHECK: | `-UnaryOperator {{.+}} 'int *__bidi_indexable' lvalue prefix '*' cannot overflow +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__bidi_indexable*__bidi_indexable' +// CHECK: | `-DeclRefExpr {{.+}} 'int *__bidi_indexable*__bidi_indexable' lvalue Var {{.+}} 'y' 'int *__bidi_indexable*__bidi_indexable' + + + +__ptrcheck_abi_assume_unsafe_indexable() +int *FUnsafeIndexable(int *x) { + int **y = &x; + return *y; +} +// CHECK: `-FunctionDecl {{.+}} FUnsafeIndexable 'int *__unsafe_indexable(int *__unsafe_indexable)' +// CHECK: |-ParmVarDecl {{.+}} used x 'int *__unsafe_indexable' +// CHECK: `-CompoundStmt +// CHECK: |-DeclStmt +// CHECK: | `-VarDecl {{.+}} used y 'int *__unsafe_indexable*__unsafe_indexable' cinit +// CHECK: | `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable*__unsafe_indexable' +// CHECK: | `-UnaryOperator {{.+}} 'int *__unsafe_indexable*__bidi_indexable' prefix '&' cannot overflow +// CHECK: | `-DeclRefExpr {{.+}} 'int *__unsafe_indexable' lvalue ParmVar {{.+}} 'x' 'int *__unsafe_indexable' +// CHECK: `-ReturnStmt +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable' +// CHECK: `-UnaryOperator {{.+}} 'int *__unsafe_indexable' lvalue prefix '*' cannot overflow +// CHECK: `-ImplicitCastExpr {{.+}} 'int *__unsafe_indexable*__unsafe_indexable' +// CHECK: `-DeclRefExpr {{.+}} 'int *__unsafe_indexable*__unsafe_indexable' lvalue Var {{.+}} 'y' 'int *__unsafe_indexable*__unsafe_indexable' + +// expected-no-diagnostics diff --git a/clang/test/BoundsSafety/Sema/address-of-incomplete-array.c b/clang/test/BoundsSafety/Sema/address-of-incomplete-array.c new file mode 100644 index 0000000000000..8e7016b2c4118 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/address-of-incomplete-array.c @@ -0,0 +1,23 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +typedef unsigned long long size_t; +extern size_t count; +extern int glob[__attribute__((counted_by(count)))]; +extern int glob2[__attribute__((sized_by(count)))]; +// expected-error@-1 {{'sized_by' cannot apply to arrays: use 'counted_by' instead}} + +void foo(int *__attribute__((counted_by(cnt))), size_t cnt); + +void bar(void) { + foo((int *)&glob, count); + // expected-error@-1 {{cannot take address of incomplete __counted_by array}} + // expected-note@-2 {{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(count)]' (aka 'int (*)[]')}} +} + +void bar2(void) { + foo((int *)&glob2, count); + // expected-warning@-1 {{count value is not statically known: passing 'int *__single' to parameter of type 'int *__single __counted_by(cnt)' (aka 'int *__single') is invalid for any count other than 0 or 1}} + // expected-note@-2 {{count passed here}} +} diff --git a/clang/test/BoundsSafety/Sema/address-taken-dynamic-count-decls.c b/clang/test/BoundsSafety/Sema/address-taken-dynamic-count-decls.c new file mode 100644 index 0000000000000..57b9a7eb30052 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/address-taken-dynamic-count-decls.c @@ -0,0 +1,106 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(len) buf; + int len; +}; + +struct T { + int *__counted_by(len + 1) buf; + int len; +}; + +struct U { + int *__counted_by(len) buf; + int *__counted_by(len) buf2; + int len; +}; + +struct V { + int len; + int buf[__counted_by(len)]; // expected-note 8{{referred to by count parameter here}} +}; + +int arr[10]; + +// expected-note@+1{{passing argument to parameter 'out_buf' here}} +void foo(int *out_len, int *__counted_by(*out_len) * out_buf) { + *out_len = 9; + *out_buf = arr; + return; +} + +void bar(int *fake_out_len, int **fake_out_buf) { + *fake_out_buf = arr; + *fake_out_len = 12; + return; +} + +void * baz(struct V *v) { + int *__single ptr_to_len = &v->len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + int **ptr_to_buf = &v->buf; // expected-warning{{incompatible pointer types initializing 'int *__single*__bidi_indexable' with an expression of type 'int (*__bidi_indexable)[__counted_by(len)]' (aka 'int (*__bidi_indexable)[]')}} + // expected-error@-1{{cannot take address of incomplete __counted_by array}} + // expected-note@-2{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + int **ptr_ptr_to_len = &ptr_to_len; + ptr_to_buf = &v->buf; // expected-warning{{incompatible pointer types assigning to 'int *__single*__bidi_indexable' from 'int (*__bidi_indexable)[__counted_by(len)]' (aka 'int (*__bidi_indexable)[]')}} + // expected-error@-1{{cannot take address of incomplete __counted_by array}} + // expected-note@-2{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + *ptr_to_len = &v->len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + *ptr_to_len = &(*v).len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + *ptr_to_len = 100; + + foo(&v->len, &v->buf); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + // expected-error@-1{{cannot take address of incomplete __counted_by array}} + // expected-note@-2{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + int local_len = 10; + foo(&local_len, &v->buf); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + // expected-error@-1{{cannot take address of incomplete __counted_by array}} + // expected-warning@-2{{incompatible pointer types passing 'int (*__bidi_indexable)[__counted_by(len)]' (aka 'int (*__bidi_indexable)[]') to parameter of type 'int *__single __counted_by(*out_len)*__single' (aka 'int *__single*__single')}} + // expected-note@-3{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + bar(&v->len, &v->buf); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + // expected-error@-1{{cannot take address of incomplete __counted_by array}} + // expected-note@-2{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + (void) &v->len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + (void) &v->buf; // expected-error{{cannot take address of incomplete __counted_by array}} + // expected-note@-1{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + (void) &(v->len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + (void) &(v->buf); // expected-error{{cannot take address of incomplete __counted_by array}} + // expected-note@-1{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + return &v->len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + return &v->buf; // expected-error{{cannot take address of incomplete __counted_by array}} + // expected-note@-1{{remove '&' to get address as 'int *' instead of 'int (*)[__counted_by(len)]'}} + return &(v->buf+2); // expected-error{{cannot take the address of an rvalue of type 'int *__bidi_indexable'}} +} + +int main() { + struct S s = {0}; + // expected-error@+1{{initializing 't.buf' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct T t = {0}; + struct U u = {0}; + + int local_len = 10; + int *__single ptr_to_len = &s.len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **ptr_to_buf = &s.buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **ptr_ptr_to_len = &ptr_to_len; + ptr_to_buf = &s.buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + *ptr_to_len = &s.len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + struct S *struct_ptr = &s; + *ptr_to_len = &struct_ptr->len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + *ptr_to_len = &(*struct_ptr).len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + + *ptr_to_len = 100; + + foo(&s.len, &s.buf); + foo(&local_len, &s.buf); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + foo(&t.len, &t.buf); // expected-error{{incompatible count expression '*out_len' vs. 'len + 1' in argument to function}} + // expected-error@+1{{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + foo(&u.len, &u.buf); + bar(&s.len, &s.buf); // expected-error{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/address-taken-dynamic-range-decls.c b/clang/test/BoundsSafety/Sema/address-taken-dynamic-range-decls.c new file mode 100644 index 0000000000000..dc270db11bf0c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/address-taken-dynamic-range-decls.c @@ -0,0 +1,57 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +struct S { + int *__ended_by(end) start; + int *end; +}; + +struct U { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + int *end; +}; + +void fun_out_end(int **out_end, int *__ended_by(*out_end) * out_start); +void fun_out_seq(int **out_end, + int *__ended_by(*out_iter) * out_start, + int *__ended_by(*out_end) * out_iter); + +void fun_in_start_out_end(int *__ended_by(*out_end) start, int **out_end) { + // expected-error@+1{{parameter 'start' with '__ended_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + fun_out_end(out_end, &start); +} + +void fun_no_out_end(int **no_out_end, int **no_out_start); + +void test() { + struct S s = {0}; + struct U u = {0}; + + int *__single local_end = 0; + int **__single ptr_to_end = &s.end; // expected-error{{field referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **ptr_to_start = &s.start; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int ***ptr_ptr_to_end = &ptr_to_end; + ptr_to_end = &s.end; // expected-error{{field referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + *ptr_to_start = &s.start; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + struct S *struct_ptr = &s; + *ptr_to_end = &struct_ptr->end; // expected-error{{field referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + *ptr_to_end = &(*struct_ptr).end; // expected-error{{field referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + + *ptr_to_end = 0; + + fun_out_end(&s.end, &s.start); + // expected-error@+1{{type of 'local_end', 'int *__single', is incompatible with parameter of type 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single')}} + fun_out_end(&local_end, &s.start); + // expected-error@+1{{passing address of 'end' as an indirect parameter; must also pass 'iter' or its address because the type of 'iter', 'int *__single __ended_by(end) /* __started_by(start) */ ' (aka 'int *__single'), refers to 'end'}} + fun_out_end(&u.end, &u.start); + fun_out_seq(&u.end, &u.start, &u.iter); + // expected-error@+1{{type of 'start', 'int *__single __ended_by(iter)' (aka 'int *__single'), is incompatible with parameter of type 'int *__single /* __started_by(*out_iter) */ ' (aka 'int *__single')}} + fun_out_seq(&u.start, &u.iter, &u.end); + // expected-error@+1{{passing address of 'start' as an indirect parameter; must also pass 'iter' or its address because the type of 'start', 'int *__single __ended_by(iter)' (aka 'int *__single'), refers to 'iter'}} + fun_out_seq(&s.end, &u.start, &s.start); + // expected-error@+1{{type of 'end', 'int *__single /* __started_by(start) */ ' (aka 'int *__single'), is incompatible with parameter of type 'int *__single*__single'}} + fun_no_out_end(&s.end, &s.start); +} diff --git a/clang/test/BoundsSafety/Sema/alloc-sized-calloc-side-effect.c b/clang/test/BoundsSafety/Sema/alloc-sized-calloc-side-effect.c new file mode 100644 index 0000000000000..d951ad543ddb9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/alloc-sized-calloc-side-effect.c @@ -0,0 +1,16 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +void *my_calloc(int count, int size) __attribute__((alloc_size(1,2))); +int get_len(); + +int foo() { + int a = 10; + int b = sizeof(int); + int *ptr = my_calloc(get_len(), b); + ptr = my_calloc(a, get_len()); + return ptr[10]; +} + +// expected-no-diagnostics \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-calls.c b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-calls.c new file mode 100644 index 0000000000000..26ed121a72a78 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-calls.c @@ -0,0 +1,76 @@ + + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include + +void param_with_count(int *__counted_by(len - 2) buf, int len); + +void call_param_with_count(void) { + // expected-error@+1{{negative count value of -2 for 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single')}} + param_with_count(0, 0); + param_with_count(0, 2); + // expected-error@+1{{passing null to parameter 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single') with count value of 1 always fails}} + param_with_count(0, 3); + + // expected-note@+1{{'arr' declared here}} + int arr[10] = {0}; + param_with_count(arr, 12); + // expected-error@+1{{negative count value of -2 for 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single')}} + param_with_count(arr, 0); + // expected-error@+1{{passing array 'arr' (which has 10 elements) to parameter 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single') with count value of 11 always fails}} + param_with_count(arr, 13); + param_with_count(arr, 2); +} + +void param_with_count_size(int *__counted_by(size * count) buf, int size, int count); + +void call_param_with_count_size(void) { + // expected-error@+1{{negative count value of -1 for 'buf' of type 'int *__single __counted_by(size * count)' (aka 'int *__single')}} + param_with_count_size(0, -1, 1); + param_with_count_size(0, 0, 0); + // expected-error@+1{{passing null to parameter 'buf' of type 'int *__single __counted_by(size * count)' (aka 'int *__single') with count value of 6 always fails}} + param_with_count_size(0, 3, 2); + + // expected-note@+1{{'arr' declared here}} + int arr[10] = {0}; + param_with_count_size(arr, 2, 5); + // expected-error@+1{{negative count value of -1 for 'buf' of type 'int *__single __counted_by(size * count)' (aka 'int *__single')}} + param_with_count_size(arr, 1, -1); + // expected-error@+1{{passing array 'arr' (which has 10 elements) to parameter 'buf' of type 'int *__single __counted_by(size * count)' (aka 'int *__single') with count value of 15 always fails}} + param_with_count_size(arr, 3, 5); + param_with_count_size(arr, 0, 0); +} + +// params sharing count but different expr and the callers +void param_with_shared_size(void *__sized_by(size - 1) buf1, void *__sized_by(size - 2) buf2, int size); + +void call_param_with_shared_size(void) { + // expected-error@+1{{negative size value of -1 for 'buf1' of type 'void *__single __sized_by(size - 1)' (aka 'void *__single')}} + param_with_shared_size(0, 0, 0); + // expected-error@+1{{negative size value of -1 for 'buf2' of type 'void *__single __sized_by(size - 2)' (aka 'void *__single')}} + param_with_shared_size(0, 0, 1); + // expected-error@+1{{passing null to parameter 'buf1' of type 'void *__single __sized_by(size - 1)' (aka 'void *__single') with size value of 1 always fails}} + param_with_shared_size(0, 0, 2); + // expected-error@+1{{passing null to parameter 'buf1' of type 'void *__single __sized_by(size - 1)' (aka 'void *__single') with size value of 9 always fails}} + param_with_shared_size(0, 0, 10); + + // expected-note@+1{{'arr1' declared here}} + char arr1[9] = {0}; + char arr2[8] = {0}; + // expected-error@+1{{negative size value of -1 for 'buf1' of type 'void *__single __sized_by(size - 1)' (aka 'void *__single')}} + param_with_shared_size(arr1, arr2, 0); + // expected-error@+1{{negative size value of -1 for 'buf2' of type 'void *__single __sized_by(size - 2)' (aka 'void *__single')}} + param_with_shared_size(arr1, arr2, 1); + param_with_shared_size(arr1, arr2, 2); + param_with_shared_size(arr1, arr2, 10); + // expected-error@+1{{passing array 'arr1' (which has 9 bytes) to parameter 'buf1' of type 'void *__single __sized_by(size - 1)' (aka 'void *__single') with size value of 11 always fails}} + param_with_shared_size(arr1, arr2, 12); +} + +// returns with count and the callers +void *__sized_by(count * size) return_with_count_size(int count, int size); + +void call_return_with_count_size(void) { + // FIXME: rdar://103368466 + void *buf = return_with_count_size(-1, 1); +} diff --git a/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-globals.c b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-globals.c new file mode 100644 index 0000000000000..417a6aeabe003 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-globals.c @@ -0,0 +1,77 @@ + + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include + +int len; +int *__counted_by(len - 2) buf; +// expected-error@-1{{negative count value of -2 for 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single')}} +int *__counted_by_or_null(len - 2) buf_n; + +int len2 = 2; +int *__counted_by(len2 - 2) buf2; +int *__counted_by_or_null(len2 - 2) buf2_n; + +int len3 = 0; +int *__counted_by(len3 - 2) buf3; +// expected-error@-1{{negative count value of -2 for 'buf3' of type 'int *__single __counted_by(len3 - 2)' (aka 'int *__single')}} +int *__counted_by_or_null(len3 - 2) buf3_n; + +int len4 = 0; +int *__counted_by(len4 + 2) buf4; +// expected-error@-1{{implicitly initializing 'buf4' of type 'int *__single __counted_by(len4 + 2)' (aka 'int *__single') and count value of 2 with null always fails}} +int *__counted_by_or_null(len4 + 2) buf4_n; + +int len5 = 0; +int *__counted_by(len5 + 2) buf5 = 0; +// expected-error@-1{{initializing 'buf5' of type 'int *__single __counted_by(len5 + 2)' (aka 'int *__single') and count value of 2 with null always fails}} +int *__counted_by_or_null(len5 + 2) buf5_n = 0; + +int len5_1; +int *__counted_by(len5_1 + 2) buf5_1 = 0; +// expected-error@-1{{initializing 'buf5_1' of type 'int *__single __counted_by(len5_1 + 2)' (aka 'int *__single') and count value of 2 with null always fails}} +int *__counted_by_or_null(len5_1 + 2) buf5_1_n = 0; + +int *len6; +int *__counted_by(*len6) buf6; +// expected-error@-1{{dereference operator in '__counted_by' is only allowed for function parameters}} +int *__counted_by_or_null(*len6) buf6_n; +// expected-error@-1{{dereference operator in '__counted_by_or_null' is only allowed for function parameters}} + +int *len6_1; +int *__counted_by(*len6_1 + 2) buf6_1; +// expected-error@-1{{invalid argument expression to bounds attribute}} +int *__counted_by_or_null(*len6_1 + 2) buf6_1_n; +// expected-error@-1{{invalid argument expression to bounds attribute}} + +int len7_1; +int len7_2; +int *__counted_by(len7_1 * 2 + len7_2 * 4) buf7; +int *__counted_by_or_null(len7_1 * 2 + len7_2 * 4) buf7_n; + +int len8_1; +int len8_2; +int *__counted_by(len8_1 * 2 + len8_2 * 4 + 1) buf8; +// expected-error@-1{{implicitly initializing 'buf8' of type 'int *__single __counted_by(len8_1 * 2 + len8_2 * 4 + 1)' (aka 'int *__single') and count value of 1 with null always fails}} +int *__counted_by_or_null(len8_1 * 2 + len8_2 * 4 + 1) buf8_n; + +int len9_1 = 1; +int len9_2 = 3; +int *__counted_by(len9_1 * len9_2) buf9; +// expected-error@-1{{implicitly initializing 'buf9' of type 'int *__single __counted_by(len9_1 * len9_2)' (aka 'int *__single') and count value of 3 with null always fails}} +int *__counted_by_or_null(len9_1 * len9_2) buf9_n; + +int len10_1; +int len10_2 = 2; +int *__counted_by(len10_1 * len10_2) buf10; +int *__counted_by_or_null(len10_1 * len10_2) buf10_n; + +int len11_1; +int len11_2 = 2; +int *__counted_by(len11_1 * len11_2 + 1) buf11; +// expected-error@-1{{implicitly initializing 'buf11' of type 'int *__single __counted_by(len11_1 * len11_2 + 1)' (aka 'int *__single') and count value of 1 with null always fails}} +int *__counted_by_or_null(len11_1 * len11_2 + 1) buf11_n; + +// don't complain about extern variables +extern int ext_len; +extern int *__counted_by(ext_len - 2) ext_buf; diff --git a/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-locals.c b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-locals.c new file mode 100644 index 0000000000000..69663b139cf91 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-locals.c @@ -0,0 +1,215 @@ + + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include + + +void test_counted_by(void) { + int len; + int *__counted_by(len - 2) buf; + // expected-error@-1{{negative count value of -2 for 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single')}} + + int len2 = 2; + int *__counted_by(len2 - 2) buf2; + + int len3 = 0; + int *__counted_by(len3 - 2) buf3; + // expected-error@-1{{negative count value of -2 for 'buf3' of type 'int *__single __counted_by(len3 - 2)' (aka 'int *__single')}} + + int len4 = 0; + int *__counted_by(len4 + 2) buf4; + // expected-error@-1{{implicitly initializing 'buf4' of type 'int *__single __counted_by(len4 + 2)' (aka 'int *__single') and count value of 2 with null always fails}} + + int len5 = 0; + int *__counted_by(len5 + 2) buf5 = 0; + // expected-error@-1{{initializing 'buf5' of type 'int *__single __counted_by(len5 + 2)' (aka 'int *__single') and count value of 2 with null always fails}} + + int len5_1; + int *__counted_by(len5_1 + 2) buf5_1 = 0; + // expected-error@-1{{initializing 'buf5_1' of type 'int *__single __counted_by(len5_1 + 2)' (aka 'int *__single') and count value of 2 with null always fails}} + + int *len6; + int *__counted_by(*len6) buf6; + // expected-error@-1{{dereference operator in '__counted_by' is only allowed for function parameters}} + + int *len6_1; + int *__counted_by(*len6_1 + 2) buf6_1; + // expected-error@-1{{invalid argument expression to bounds attribute}} + + int len7_1; + int len7_2; + int *__counted_by(len7_1 * 2 + len7_2 * 4) buf7; + + int len8_1; + int len8_2; + int *__counted_by(len8_1 * 2 + len8_2 * 4 + 1) buf8; + // expected-error@-1{{implicitly initializing 'buf8' of type 'int *__single __counted_by(len8_1 * 2 + len8_2 * 4 + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + + int len9_1 = 1; + int len9_2 = 3; + int *__counted_by(len9_1 * len9_2) buf9; + // expected-error@-1{{implicitly initializing 'buf9' of type 'int *__single __counted_by(len9_1 * len9_2)' (aka 'int *__single') and count value of 3 with null always fails}} + + int len10_1; + int len10_2 = 2; + int *__counted_by(len10_1 * len10_2) buf10; + + int len11_1; + int len11_2 = 2; + int *__counted_by(len11_1 * len11_2 + 1) buf11; + // expected-error@-1{{implicitly initializing 'buf11' of type 'int *__single __counted_by(len11_1 * len11_2 + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + + // don't complain about extern variables + extern int ext_len; + extern int *__counted_by(ext_len - 2) ext_buf; +} + +void test_counted_by_or_null(void) { + int arr[2] = {1,2}; // expected-note{{'arr' declared here}} + int len; + int *__counted_by_or_null(len - 2) buf = arr; + // expected-error@-1{{negative count value of -2 for 'buf' of type 'int *__single __counted_by_or_null(len - 2)' (aka 'int *__single')}} + + int len2 = 2; + int *__counted_by_or_null(len2 - 2) buf2 = arr; + + int len3 = 0; + int *__counted_by_or_null(len3 + 2) buf3 = arr; + + int *len4; + int *__counted_by_or_null(*len4) buf4 = arr; + // expected-error@-1{{dereference operator in '__counted_by_or_null' is only allowed for function parameters}} + + int *len4_1; + int *__counted_by_or_null(*len4_1 + 2) buf4_1 = arr; + // expected-error@-1{{invalid argument expression to bounds attribute}} + + int len5_1; + int len5_2; + int *__counted_by_or_null(len5_1 * 2 + len5_2 * 4) buf5 = arr; + // expected-warning@-1{{possibly initializing 'buf5' of type 'int *__single __counted_by_or_null(len5_1 * 2 + len5_2 * 4)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + + int len6_1; + int len6_2; + int *__counted_by_or_null(len6_1 * 2 + len6_2 * 4 - 1) buf6 = arr; + // expected-error@-1{{negative count value of -1 for 'buf6' of type 'int *__single __counted_by_or_null(len6_1 * 2 + len6_2 * 4 - 1)' (aka 'int *__single')}} + + int len7_1 = 1; + int len7_2 = 3; + int *__counted_by_or_null(len7_1 * len7_2) buf7 = arr; + // expected-error@-1{{initializing 'buf7' of type 'int *__single __counted_by_or_null(len7_1 * len7_2)' (aka 'int *__single') and count value of 3 with array 'arr' (which has 2 elements) always fails}} + + int len8_1; + int len8_2 = 2; + int *__counted_by_or_null(len8_1 * len8_2) buf8 = arr; + // expected-warning@-1{{possibly initializing 'buf8' of type 'int *__single __counted_by_or_null(len8_1 * len8_2)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + + int len9_1; + int len9_2 = 2; + int *__counted_by_or_null(len9_1 * len9_2 + 1) buf9 = arr; +} + +void test_sized_by(void) { + int len; + void *__sized_by(len - 2) buf; + // expected-error@-1{{negative size value of -2 for 'buf' of type 'void *__single __sized_by(len - 2)' (aka 'void *__single')}} + + int len2 = 2; + void *__sized_by(len2 - 2) buf2; + + int len3 = 0; + void *__sized_by(len3 - 2) buf3; + // expected-error@-1{{negative size value of -2 for 'buf3' of type 'void *__single __sized_by(len3 - 2)' (aka 'void *__single')}} + + int len4 = 0; + void *__sized_by(len4 + 2) buf4; + // expected-error@-1{{implicitly initializing 'buf4' of type 'void *__single __sized_by(len4 + 2)' (aka 'void *__single') and size value of 2 with null always fails}} + + int len5 = 0; + void *__sized_by(len5 + 2) buf5 = 0; + // expected-error@-1{{initializing 'buf5' of type 'void *__single __sized_by(len5 + 2)' (aka 'void *__single') and size value of 2 with null always fails}} + + int len5_1; + void *__sized_by(len5_1 + 2) buf5_1 = 0; + // expected-error@-1{{initializing 'buf5_1' of type 'void *__single __sized_by(len5_1 + 2)' (aka 'void *__single') and size value of 2 with null always fails}} + + int *len6; + void *__sized_by(*len6) buf6; + // expected-error@-1{{dereference operator in '__sized_by' is only allowed for function parameters}} + + int *len6_1; + void *__sized_by(*len6_1 + 2) buf6_1; + // expected-error@-1{{invalid argument expression to bounds attribute}} + + int len7_1; + int len7_2; + void *__sized_by(len7_1 * 2 + len7_2 * 4) buf7; + + int len8_1; + int len8_2; + void *__sized_by(len8_1 * 2 + len8_2 * 4 + 1) buf8; + // expected-error@-1{{implicitly initializing 'buf8' of type 'void *__single __sized_by(len8_1 * 2 + len8_2 * 4 + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + + int len9_1 = 1; + int len9_2 = 3; + void *__sized_by(len9_1 * len9_2) buf9; + // expected-error@-1{{implicitly initializing 'buf9' of type 'void *__single __sized_by(len9_1 * len9_2)' (aka 'void *__single') and size value of 3 with null always fails}} + + int len10_1; + int len10_2 = 2; + void *__sized_by(len10_1 * len10_2) buf10; + + int len11_1; + int len11_2 = 2; + void *__sized_by(len11_1 * len11_2 + 1) buf11; + // expected-error@-1{{implicitly initializing 'buf11' of type 'void *__single __sized_by(len11_1 * len11_2 + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + + // don't complain about extern variables + extern int ext_len2; + extern void *__sized_by(ext_len2 - 2) ext_buf2; +} + +void test_sized_by_or_null(void) { + int arr[2] = {1,2}; + int len; + int *__sized_by_or_null(len - 2) buf = arr; + // expected-error@-1{{negative size value of -2 for 'buf' of type 'int *__single __sized_by_or_null(len - 2)' (aka 'int *__single')}} + + int len2 = 2; + int *__sized_by_or_null(len2 - 2) buf2 = arr; + + int len3 = 0; + int *__sized_by_or_null(len3 + 2) buf3 = arr; + + int *len4; + int *__sized_by_or_null(*len4) buf4 = arr; + // expected-error@-1{{dereference operator in '__sized_by_or_null' is only allowed for function parameters}} + + int *len4_1; + int *__sized_by_or_null(*len4_1 + 2) buf4_1 = arr; + // expected-error@-1{{invalid argument expression to bounds attribute}} + + int len5_1; + int len5_2; + int *__sized_by_or_null(len5_1 * 2 + len5_2 * 4) buf5 = arr; + // expected-warning@-1{{possibly initializing 'buf5' of type 'int *__single __sized_by_or_null(len5_1 * 2 + len5_2 * 4)' (aka 'int *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + + int len6_1; + int len6_2; + int *__sized_by_or_null(len6_1 * 2 + len6_2 * 4 - 1) buf6 = arr; + // expected-error@-1{{negative size value of -1 for 'buf6' of type 'int *__single __sized_by_or_null(len6_1 * 2 + len6_2 * 4 - 1)' (aka 'int *__single')}} + + int len7_1 = 1; + int len7_2 = 3; + int *__sized_by_or_null(len7_1 * len7_2) buf7 = arr; + + int len8_1; + int len8_2 = 2; + int *__sized_by_or_null(len8_1 * len8_2) buf8 = arr; + // expected-warning@-1{{possibly initializing 'buf8' of type 'int *__single __sized_by_or_null(len8_1 * len8_2)' (aka 'int *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + + int len9_1; + int len9_2 = 2; + int *__sized_by_or_null(len9_1 * len9_2 + 1) buf9 = arr; +} + diff --git a/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-params-assign.c b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-params-assign.c new file mode 100644 index 0000000000000..9ad48af930f48 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-params-assign.c @@ -0,0 +1,150 @@ + + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include +#include + + +/* -------------------- + counted_by + -------------------- */ +void param_with_count(int *__counted_by(len - 2) buf, int len) { + // expected-error@+1{{negative count value of -2 for 'buf' of type 'int *__single __counted_by(len - 2)' (aka 'int *__single')}} + buf = 0; + len = 0; +} + +void inout_count(int *__counted_by(*len) buf, int *len); + +void inout_count_buf(int *__counted_by(*len) *buf, int *len); + +void pass_argument_to_inout_count_buf(int *__counted_by(len - 1) buf, int len) { + // expected-error@+1{{parameter 'buf' with '__counted_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_count_buf(&buf, &len); + // expected-error@+1{{incompatible count expression '*len' vs. 'len - 1' in argument to function}} + inout_count(buf, &len); + + int arr[10]; + int len2 = 9; + int *__counted_by(len2 + 1) buf2 = arr; + // expected-error@+1{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + inout_count_buf(&buf2, &len2); + // expected-error@+1{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + inout_count(buf2, &len2); + + int len3 = 1; + int *__counted_by(len3 - 1) buf3 = arr; + // expected-error@+2{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + // expected-error@+1{{passing address of 'buf2' as an indirect parameter; must also pass 'len2' or its address because the type of 'buf2', 'int *__single __counted_by(len2 + 1)' (aka 'int *__single'), refers to 'len2'}} + inout_count_buf(&buf2, &len3); + // expected-error@+2{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + // expected-error@+1{{passing address of 'len3' as an indirect parameter; must also pass 'buf3' or its address because the type of 'buf3', 'int *__single __counted_by(len3 - 1)' (aka 'int *__single'), refers to 'len3'}} + inout_count(buf2, &len3); + // expected-error@+1{{incompatible count expression '*len' vs. 'len3 - 1' in argument to function}} + inout_count(buf3, &len3); +} + +/* -------------------- + sized_by + -------------------- */ +void param_with_size(int *__sized_by(len - 2) buf, int len) { + // expected-error@+1{{negative size value of -2 for 'buf' of type 'int *__single __sized_by(len - 2)' (aka 'int *__single')}} + buf = 0; + len = 0; +} + +void inout_size(int *__sized_by(*len) buf, int *len); + +void inout_size_buf(int *__sized_by(*len) *buf, int *len); + +void pass_argument_to_inout_size_buf(int *__sized_by(len - 1) buf, int len) { + // expected-error@+1{{parameter 'buf' with '__sized_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_size_buf(&buf, &len); + // expected-error@+1{{incompatible count expression '*len' vs. 'len - 1' in argument to function}} + inout_size(buf, &len); + + int arr[10]; + int len2 = 9; + int *__sized_by(len2 + 1) buf2 = arr; + // expected-error@+1{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + inout_size_buf(&buf2, &len2); + // expected-error@+1{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + inout_size(buf2, &len2); + + int len3 = 1; + int *__sized_by(len3 - 1) buf3 = arr; + // expected-error@+2{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + // expected-error@+1{{passing address of 'buf2' as an indirect parameter; must also pass 'len2' or its address because the type of 'buf2', 'int *__single __sized_by(len2 + 1)' (aka 'int *__single'), refers to 'len2'}} + inout_size_buf(&buf2, &len3); + // expected-error@+2{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + // expected-error@+1{{passing address of 'len3' as an indirect parameter; must also pass 'buf3' or its address because the type of 'buf3', 'int *__single __sized_by(len3 - 1)' (aka 'int *__single'), refers to 'len3'}} + inout_size(buf2, &len3); + // expected-error@+1{{incompatible count expression '*len' vs. 'len3 - 1' in argument to function}} + inout_size(buf3, &len3); +} + +/* -------------------- + counted_by_or_null + -------------------- */ +void inout_count_nullable(int *__counted_by_or_null(*len) buf, int *len); + +void inout_count_nullable_buf(int *__counted_by_or_null(*len) *buf, int *len); + +void pass_argument_to_inout_count_nullable_buf(int *__counted_by_or_null(len - 1) buf, int len) { + // expected-error@+1{{parameter 'buf' with '__counted_by_or_null' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_count_buf(&buf, &len); + // expected-error@+1{{incompatible count expression '*len' vs. 'len - 1' in argument to function}} + inout_count_nullable(buf, &len); + + int arr[10]; + int len2 = 9; + int *__counted_by_or_null(len2 + 1) buf2 = arr; + // expected-error@+1{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + inout_count_buf(&buf2, &len2); + // expected-error@+1{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + inout_count_nullable(buf2, &len2); + + int len3 = 1; + int *__counted_by_or_null(len3 - 1) buf3 = arr; + // expected-error@+2{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + // expected-error@+1{{passing address of 'buf2' as an indirect parameter; must also pass 'len2' or its address because the type of 'buf2', 'int *__single __counted_by_or_null(len2 + 1)' (aka 'int *__single'), refers to 'len2'}} + inout_count_buf(&buf2, &len3); + // expected-error@+2{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + // expected-error@+1{{passing address of 'len3' as an indirect parameter; must also pass 'buf3' or its address because the type of 'buf3', 'int *__single __counted_by_or_null(len3 - 1)' (aka 'int *__single'), refers to 'len3'}} + inout_count_nullable(buf2, &len3); + // expected-error@+1{{incompatible count expression '*len' vs. 'len3 - 1' in argument to function}} + inout_count_nullable(buf3, &len3); +} + +/* -------------------- + sized_by_or_null + -------------------- */ +void inout_size_nullable(int *__sized_by_or_null(*len) buf, int *len); + +void inout_size_nullable_buf(int *__sized_by_or_null(*len) *buf, int *len); + +void pass_argument_to_inout_size_nullable_buf(int *__sized_by_or_null(len - 1) buf, int len) { + // expected-error@+1{{parameter 'buf' with '__sized_by_or_null' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_size_nullable_buf(&buf, &len); + // expected-error@+1{{incompatible count expression '*len' vs. 'len - 1' in argument to function}} + inout_size_nullable(buf, &len); + + int arr[10]; + int len2 = 9; + int *__sized_by_or_null(len2 + 1) buf2 = arr; + // expected-error@+1{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + inout_size_nullable_buf(&buf2, &len2); + // expected-error@+1{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + inout_size_nullable(buf2, &len2); + + int len3 = 1; + int *__sized_by_or_null(len3 - 1) buf3 = arr; + // expected-error@+2{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + // expected-error@+1{{passing address of 'buf2' as an indirect parameter; must also pass 'len2' or its address because the type of 'buf2', 'int *__single __sized_by_or_null(len2 + 1)' (aka 'int *__single'), refers to 'len2'}} + inout_size_nullable_buf(&buf2, &len3); + // expected-error@+2{{incompatible count expression '*len' vs. 'len2 + 1' in argument to function}} + // expected-error@+1{{passing address of 'len3' as an indirect parameter; must also pass 'buf3' or its address because the type of 'buf3', 'int *__single __sized_by_or_null(len3 - 1)' (aka 'int *__single'), refers to 'len3'}} + inout_size_nullable(buf2, &len3); + // expected-error@+1{{incompatible count expression '*len' vs. 'len3 - 1' in argument to function}} + inout_size_nullable(buf3, &len3); +} diff --git a/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-params.c b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-params.c new file mode 100644 index 0000000000000..8ab2a9fd20a96 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-counted-by-params.c @@ -0,0 +1,43 @@ + + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include +#include + +void param_with_count(int *__counted_by(len - 2) buf, size_t len); + +void param_with_count_size(int *__counted_by(len * size) buf, size_t len, size_t size); + +void *__sized_by(size * len) return_count_size(size_t len, size_t size); + +void *__sized_by(size * len) *return_count_size_ptr(size_t len, size_t size); +// expected-error@-1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} + +void *__sized_by(size * *len) return_inout_count_size_ptr(size_t *len, size_t size); +// expected-error@-1{{invalid argument expression to bounds attribute}} + +void *__sized_by(*len) return_inout_count(size_t *len); + +void *__sized_by(*len + 1) return_inout_count_1(size_t *len); +// expected-error@-1{{invalid argument expression to bounds attribute}} + +void *__sized_by_or_null(*len + 1) return_inout_count_2(size_t *len); +// expected-error@-1{{invalid argument expression to bounds attribute}} + +int *__counted_by_or_null(*len + 1) return_inout_count_3(size_t *len); +// expected-error@-1{{invalid argument expression to bounds attribute}} + +void *__sized_by(*len) *return_inout_count_ptr(size_t *len); +// expected-error@-1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} + +void *__sized_by(*len + 1) *return_inout_count_1_ptr(size_t *len); +// expected-error@-1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} + +void inout_buf(int *__counted_by(len * size) *buf, size_t len, size_t size); + +void inout_buf_count(int *__counted_by(*len + 1) *buf, size_t *len); +// expected-error@-1{{invalid argument expression to bounds attribute}} + +void inout_count(int *__counted_by(*len - 2) buf, size_t *len); +// expected-error@-1{{invalid argument expression to bounds attribute}} + diff --git a/clang/test/BoundsSafety/Sema/arithmetic-ops-in-sized-by-globals.c b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-sized-by-globals.c new file mode 100644 index 0000000000000..5c9be5b940f52 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/arithmetic-ops-in-sized-by-globals.c @@ -0,0 +1,77 @@ + + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include + +int len; +void *__sized_by(len - 2) buf; +// expected-error@-1{{negative size value of -2 for 'buf' of type 'void *__single __sized_by(len - 2)' (aka 'void *__single')}} +void *__sized_by_or_null(len - 2) buf_n; + +int len2 = 2; +void *__sized_by(len2 - 2) buf2; +void *__sized_by_or_null(len2 - 2) buf2_n; + +int len3 = 0; +void *__sized_by(len3 - 2) buf3; +// expected-error@-1{{negative size value of -2 for 'buf3' of type 'void *__single __sized_by(len3 - 2)' (aka 'void *__single')}} +void *__sized_by_or_null(len3 - 2) buf3_n; + +int len4 = 0; +void *__sized_by(len4 + 2) buf4; +// expected-error@-1{{implicitly initializing 'buf4' of type 'void *__single __sized_by(len4 + 2)' (aka 'void *__single') and size value of 2 with null always fails}} +void *__sized_by_or_null(len4 + 2) buf4_n; + +int len5 = 0; +void *__sized_by(len5 + 2) buf5 = 0; +// expected-error@-1{{initializing 'buf5' of type 'void *__single __sized_by(len5 + 2)' (aka 'void *__single') and size value of 2 with null always fails}} +void *__sized_by_or_null(len5 + 2) buf5_n = 0; + +int len5_1; +void *__sized_by(len5_1 + 2) buf5_1 = 0; +// expected-error@-1{{initializing 'buf5_1' of type 'void *__single __sized_by(len5_1 + 2)' (aka 'void *__single') and size value of 2 with null always fails}} +void *__sized_by_or_null(len5_1 + 2) buf5_1_n = 0; + +int *len6; +void *__sized_by(*len6) buf6; +// expected-error@-1{{dereference operator in '__sized_by' is only allowed for function parameters}} +void *__sized_by_or_null(*len6) buf6_n; +// expected-error@-1{{dereference operator in '__sized_by_or_null' is only allowed for function parameters}} + +int *len6_1; +void *__sized_by(*len6_1 + 2) buf6_1; +// expected-error@-1{{invalid argument expression to bounds attribute}} +void *__sized_by_or_null(*len6_1 + 2) buf6_1_n; +// expected-error@-1{{invalid argument expression to bounds attribute}} + +int len7_1; +int len7_2; +void *__sized_by(len7_1 * 2 + len7_2 * 4) buf7; +void *__sized_by_or_null(len7_1 * 2 + len7_2 * 4) buf7_n; + +int len8_1; +int len8_2; +void *__sized_by(len8_1 * 2 + len8_2 * 4 + 1) buf8; +// expected-error@-1{{implicitly initializing 'buf8' of type 'void *__single __sized_by(len8_1 * 2 + len8_2 * 4 + 1)' (aka 'void *__single') and size value of 1 with null always fails}} +void *__sized_by_or_null(len8_1 * 2 + len8_2 * 4 + 1) buf8_n; + +int len9_1 = 1; +int len9_2 = 3; +void *__sized_by(len9_1 * len9_2) buf9; +// expected-error@-1{{implicitly initializing 'buf9' of type 'void *__single __sized_by(len9_1 * len9_2)' (aka 'void *__single') and size value of 3 with null always fails}} +void *__sized_by_or_null(len9_1 * len9_2) buf9_n; + +int len10_1; +int len10_2 = 2; +void *__sized_by(len10_1 * len10_2) buf10; +void *__sized_by_or_null(len10_1 * len10_2) buf10_n; + +int len11_1; +int len11_2 = 2; +void *__sized_by(len11_1 * len11_2 + 1) buf11; +// expected-error@-1{{implicitly initializing 'buf11' of type 'void *__single __sized_by(len11_1 * len11_2 + 1)' (aka 'void *__single') and size value of 1 with null always fails}} +void *__sized_by_or_null(len11_1 * len11_2 + 1) buf11_n; + +// don't complain about extern variables +extern int ext_len; +extern int *__sized_by(ext_len - 2) ext_buf; diff --git a/clang/test/BoundsSafety/Sema/array-as-count.c b/clang/test/BoundsSafety/Sema/array-as-count.c new file mode 100644 index 0000000000000..f91e953a724f4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/array-as-count.c @@ -0,0 +1,66 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void ok_counted_by(int buf[__counted_by(count)], int count); +void ok_sized_by(int buf[__sized_by(size)], int size); + +void ok_constant_array(void) { + int foo[4]; +} + +void ok_variable_array(void) { + int count = 4; + int foo[count]; +} + + +// this is OK: `buf` becomes indexable +__ptrcheck_abi_assume_indexable() +void ok_abi_indexable(int buf[]); + + +// this is not OK: `buf` becomes single +__ptrcheck_abi_assume_single() +typedef int incomplete_int_array_t[]; + +void fail_typedef_decay_to_single(incomplete_int_array_t array); // expected-error{{parameter of array type 'incomplete_int_array_t' (aka 'int[]') decays to a __single pointer, and will not allow arithmetic}} \ + expected-note{{add a count attribute within the declarator brackets or convert the parameter to a pointer with a count or size attribute}} + +void fail_decay_to_single(int buf[]); // expected-error{{parameter of array type 'int[]' decays to a __single pointer, and will not allow arithmetic}} \ + expected-note{{add a count attribute within the declarator brackets or convert the parameter to a pointer with a count or size attribute}} + +void fail_fixed_size(int buf[__counted_by(count) 4], int count); // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} +void fail_variable_size(int count, int buf[__counted_by(count) count]); // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} + +void fail_local_array_constant(void) { + int foo[__counted_by(4)] = {1, 2, 3, 4}; // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} +} + +void fail_local_array_variable(void) { + int count = 4; + int foo[__counted_by(4) count]; // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} +} + +struct fail_struct_fixed_size { + int count; + int elems[__counted_by(count) 5]; // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} +}; + +struct fail_struct_sized_by { + int count; + int elems[__sized_by(count)]; // expected-error{{'__sized_by' cannot apply to arrays: use 'counted_by' instead}} +}; + +// It's not possible to create a struct field with a variable-length array +// inside with clang, so this case isn't tested. (GCC lets you do that using a +// local struct with a field sized by a local variable, but Clang has a +// diagnostic that says this will "never be supported".) + +// These are OK: +typedef int (__array_decay_discards_count_in_parameters int_array_but_decays_t)[10]; +void ok_typedef_count_discarded(int_array_but_decays_t array); +void ok_count_discarded(int (__array_decay_discards_count_in_parameters array)[5]); +void ok_count_discarded_count_attr(int_array_but_decays_t __counted_by(count) array, int count); diff --git a/clang/test/BoundsSafety/Sema/array-parameter-in-system-header.c b/clang/test/BoundsSafety/Sema/array-parameter-in-system-header.c new file mode 100644 index 0000000000000..ee512c9d80986 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/array-parameter-in-system-header.c @@ -0,0 +1,8 @@ + +// RUN: %clang_cc1 -isystem %S/mock-sdk -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -isystem %S/mock-sdk -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// expected-no-diagnostics + +#include +#include diff --git a/clang/test/BoundsSafety/Sema/array-pointer.c b/clang/test/BoundsSafety/Sema/array-pointer.c new file mode 100644 index 0000000000000..d5621d02d0b41 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/array-pointer.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int foo(void) { + int array[3] = {1, 2, 3}; + int(*ap)[3] = &array; + return (*ap)[0]; +} + +int bar(void) { + int array[3] = {1, 2, 3}; + int *p = array; + // expected-warning@+1{{incompatible pointer types initializing 'int (*__bidi_indexable)[3]' with an expression of type 'int *__bidi_indexable'}} + int(*ap)[3] = p; + return (*ap)[0]; +} diff --git a/clang/test/BoundsSafety/Sema/atomic-ops-c11-casts.c b/clang/test/BoundsSafety/Sema/atomic-ops-c11-casts.c new file mode 100644 index 0000000000000..5612cab8870bc --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-ops-c11-casts.c @@ -0,0 +1,47 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void test(void) { + // expected-error@+4{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr1 <- val1 + int *_Atomic __single ptr1; + int *__unsafe_indexable val1; + __c11_atomic_store(&ptr1, val1, __ATOMIC_SEQ_CST); + + // expected-error@+4{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr2 <- val2 + int *_Atomic __single ptr2; + int *__unsafe_indexable val2; + __c11_atomic_exchange(&ptr2, val2, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__single*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*'; use explicit cast to perform this conversion}} + // expected3 <- ptr3 + int *_Atomic __unsafe_indexable ptr3; + int *__single expected3; + int *__unsafe_indexable desired3; + __c11_atomic_compare_exchange_strong(&ptr3, &expected3, desired3, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr4 <- desired4 + int *_Atomic __single ptr4; + int *__single expected4; + int *__unsafe_indexable desired4; + __c11_atomic_compare_exchange_strong(&ptr4, &expected4, desired4, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__single*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*'; use explicit cast to perform this conversion}} + // expected5 <- ptr5 + int *_Atomic __unsafe_indexable ptr5; + int *__single expected5; + int *__unsafe_indexable desired5; + __c11_atomic_compare_exchange_weak(&ptr5, &expected5, desired5, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr6 <- desired6 + int *_Atomic __single ptr6; + int *__single expected6; + int *__unsafe_indexable desired6; + __c11_atomic_compare_exchange_weak(&ptr6, &expected6, desired6, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/Sema/atomic-ops-c11-system.h b/clang/test/BoundsSafety/Sema/atomic-ops-c11-system.h new file mode 100644 index 0000000000000..b8d537ec8c90c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-ops-c11-system.h @@ -0,0 +1,46 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#pragma clang system_header + +#include + +void unspecified(void) { + int x; + int *_Atomic p = &x; + int *q; + + p++; + p--; + ++p; + --p; + + // expected-error@+1{{invalid operands to binary expression ('_Atomic(int *)' and 'int'}} + p += 42; + // expected-error@+1{{invalid operands to binary expression ('_Atomic(int *)' and 'int'}} + p -= 42; + + q = &p[42]; + + __c11_atomic_init(&p, &x); + q = __c11_atomic_load(&p, __ATOMIC_SEQ_CST); + __c11_atomic_store(&p, q, __ATOMIC_SEQ_CST); + + q = __c11_atomic_exchange(&p, q, __ATOMIC_SEQ_CST); + __c11_atomic_compare_exchange_strong(&p, &q, q, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __c11_atomic_compare_exchange_weak(&p, &q, q, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + q = __c11_atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + q = __c11_atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *) *__bidi_indexable' invalid}} + __c11_atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *) *__bidi_indexable' invalid}} + __c11_atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *) *__bidi_indexable' invalid}} + __c11_atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer or supported floating point type ('_Atomic(int *) *__bidi_indexable' invalid}} + __c11_atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer or supported floating point type ('_Atomic(int *) *__bidi_indexable' invalid}} + __c11_atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/Sema/atomic-ops-c11.c b/clang/test/BoundsSafety/Sema/atomic-ops-c11.c new file mode 100644 index 0000000000000..b94981999d56b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-ops-c11.c @@ -0,0 +1,92 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include "atomic-ops-c11-system.h" + +void unsafe_indexable(void) { + int x; + int *_Atomic __unsafe_indexable p = &x; + int *__unsafe_indexable q; + + p++; + p--; + ++p; + --p; + + // expected-error@+1{{invalid operands to binary expression ('_Atomic(int *__unsafe_indexable)' and 'int'}} + p += 42; + // expected-error@+1{{invalid operands to binary expression ('_Atomic(int *__unsafe_indexable)' and 'int'}} + p -= 42; + + q = &p[42]; + + __c11_atomic_init(&p, &x); + q = __c11_atomic_load(&p, __ATOMIC_SEQ_CST); + __c11_atomic_store(&p, q, __ATOMIC_SEQ_CST); + + q = __c11_atomic_exchange(&p, q, __ATOMIC_SEQ_CST); + __c11_atomic_compare_exchange_strong(&p, &q, q, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __c11_atomic_compare_exchange_weak(&p, &q, q, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + q = __c11_atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + q = __c11_atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *__unsafe_indexable) *__bidi_indexable' invalid}} + __c11_atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *__unsafe_indexable) *__bidi_indexable' invalid}} + __c11_atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *__unsafe_indexable) *__bidi_indexable' invalid}} + __c11_atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer or supported floating point type ('_Atomic(int *__unsafe_indexable) *__bidi_indexable' invalid}} + __c11_atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer or supported floating point type ('_Atomic(int *__unsafe_indexable) *__bidi_indexable' invalid}} + __c11_atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void single(void) { + int x; + int *_Atomic __single p = &x; + int *__single q; + + // expected-error-re@+1{{pointer arithmetic on single pointer 'p' is out of {{bounds$}}}} + p++; + // expected-error-re@+1{{pointer arithmetic on single pointer 'p' is out of {{bounds$}}}} + p--; + // expected-error-re@+1{{pointer arithmetic on single pointer 'p' is out of {{bounds$}}}} + ++p; + // expected-error-re@+1{{pointer arithmetic on single pointer 'p' is out of {{bounds$}}}} + --p; + + // expected-error@+1{{invalid operands to binary expression ('_Atomic(int *__single)' and 'int'}} + p += 42; + // expected-error@+1{{invalid operands to binary expression ('_Atomic(int *__single)' and 'int'}} + p -= 42; + + // expected-error@+1{{array subscript on single pointer 'p' must use a constant index of 0 to be in bounds}} + q = &p[42]; + + __c11_atomic_init(&p, &x); + q = __c11_atomic_load(&p, __ATOMIC_SEQ_CST); + __c11_atomic_store(&p, q, __ATOMIC_SEQ_CST); + + q = __c11_atomic_exchange(&p, q, __ATOMIC_SEQ_CST); + __c11_atomic_compare_exchange_strong(&p, &q, q, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __c11_atomic_compare_exchange_weak(&p, &q, q, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('_Atomic(int *__single) *__bidi_indexable' invalid)}} + q = __c11_atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('_Atomic(int *__single) *__bidi_indexable' invalid)}} + q = __c11_atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *__single) *__bidi_indexable' invalid}} + __c11_atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *__single) *__bidi_indexable' invalid}} + __c11_atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer ('_Atomic(int *__single) *__bidi_indexable' invalid}} + __c11_atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer or supported floating point type ('_Atomic(int *__single) *__bidi_indexable' invalid}} + __c11_atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to atomic integer or supported floating point type ('_Atomic(int *__single) *__bidi_indexable' invalid}} + __c11_atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/Sema/atomic-ops-gnu-casts.c b/clang/test/BoundsSafety/Sema/atomic-ops-gnu-casts.c new file mode 100644 index 0000000000000..c18473f865a2a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-ops-gnu-casts.c @@ -0,0 +1,73 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void test(void) { + // expected-error@+4{{passing 'int *__single*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*'; use explicit cast to perform this conversion}} + // ret1 <- ptr1 + int *__unsafe_indexable ptr1; + int *__single ret1; + __atomic_load(&ptr1, &ret1, __ATOMIC_SEQ_CST); + + // expected-error@+4{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr2 <- val2 + int *__single ptr2; + int *__unsafe_indexable val2; + __atomic_store_n(&ptr2, val2, __ATOMIC_SEQ_CST); + + // expected-error@+4{{passing 'int *__unsafe_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single*__bidi_indexable'; use explicit cast to perform this conversion}} + // ptr3 <- val3 + int *__single ptr3; + int *__unsafe_indexable val3; + __atomic_store(&ptr3, &val3, __ATOMIC_SEQ_CST); + + // expected-error@+4{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr4 <- val4 + int *__single ptr4; + int *__unsafe_indexable val4; + __atomic_exchange_n(&ptr4, val4, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__unsafe_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single*'; use explicit cast to perform this conversion}} + // ptr5 <- val5 + int *__single ptr5; + int *__unsafe_indexable val5; + int *__single ret5; + __atomic_exchange(&ptr5, &val5, &ret5, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__single*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*__bidi_indexable'; use explicit cast to perform this conversion}} + // ret6 <- ptr6 + int *__unsafe_indexable ptr6; + int *__unsafe_indexable val6; + int *__single ret6; + __atomic_exchange(&ptr6, &val6, &ret6, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__single*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*'; use explicit cast to perform this conversion}} + // expected7 <- ptr7 + int *__unsafe_indexable ptr7; + int *__single expected7; + int *__unsafe_indexable desired7; + __atomic_compare_exchange_n(&ptr7, &expected7, desired7, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'int *__single' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // ptr8 <- desired8 + int *__single ptr8; + int *__single expected8; + int *__unsafe_indexable desired8; + __atomic_compare_exchange_n(&ptr8, &expected8, desired8, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__single*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*'; use explicit cast to perform this conversion}} + // expected9 <- ptr9 + int *__unsafe_indexable ptr9; + int *__single expected9; + int *__unsafe_indexable desired9; + __atomic_compare_exchange(&ptr9, &expected9, &desired9, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+5{{passing 'int *__unsafe_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single*__bidi_indexable'; use explicit cast to perform this conversion}} + // ptr10 <- desired10 + int *__single ptr10; + int *__single expected10; + int *__unsafe_indexable desired10; + __atomic_compare_exchange(&ptr10, &expected10, &desired10, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/Sema/atomic-ops-gnu-system.h b/clang/test/BoundsSafety/Sema/atomic-ops-gnu-system.h new file mode 100644 index 0000000000000..c93aa8f70e4c8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-ops-gnu-system.h @@ -0,0 +1,61 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +#pragma clang system_header + +void unspecified(void) { + int x; + int * p = &x; + int * q; + int * r; + + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int **__bidi_indexable' invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int **__bidi_indexable' invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int **__bidi_indexable' invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int **__bidi_indexable' invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int **__bidi_indexable' invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/Sema/atomic-ops-gnu.c b/clang/test/BoundsSafety/Sema/atomic-ops-gnu.c new file mode 100644 index 0000000000000..f77354902a4cf --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-ops-gnu.c @@ -0,0 +1,585 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include "atomic-ops-gnu-system.h" + +void unsafe_indexable(void) { + int x; + int *__unsafe_indexable p = &x; + int *__unsafe_indexable q; + int *__unsafe_indexable r; + + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__unsafe_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void single(void) { + int x; + int *__single p = &x; + int *__single q; + int *__single r; + + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single*__bidi_indexable' invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single*__bidi_indexable' invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single*__bidi_indexable' invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single*__bidi_indexable' invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single*__bidi_indexable' invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void indexable(void) { + int x; + int *__indexable p = &x; + int *__indexable q; + int *__indexable r; + + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__indexable' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__indexable*__bidi_indexable' invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void bidi_indexable(void) { + int x; + int *__bidi_indexable p = &x; + int *__bidi_indexable q; + int *__bidi_indexable r; + + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__bidi_indexable' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__bidi_indexable*__bidi_indexable' invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void counted_by(void) { + int x; + int *__counted_by(1) p = &x; + int *__counted_by(1) q = &x; + int *__counted_by(1) r = &x; + + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void sized_by(void) { + int x; + int *__sized_by(4) p = &x; + int *__sized_by(4) q = &x; + int *__sized_by(4) r = &x; + + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void counted_by_or_null(void) { + int x; + int *__counted_by_or_null(1) p = &x; + int *__counted_by_or_null(1) q = &x; + int *__counted_by_or_null(1) r = &x; + + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__counted_by_or_null' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __counted_by_or_null(1)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void sized_by_or_null(void) { + int x; + int *__sized_by_or_null(4) p = &x; + int *__sized_by_or_null(4) q = &x; + int *__sized_by_or_null(4) r = &x; + + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__sized_by_or_null' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __sized_by_or_null(4)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} + +void ended_by(int * x, int * y, int * z, int * __ended_by(x) p, int * __ended_by(y) q, int *__ended_by(z) r) { + // expected-error@+2{{assignment to 'int *__single __ended_by(y)' (aka 'int *__single') 'q' requires corresponding assignment to 'y'; add self assignment 'y = y' if the value has not changed}} + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + q = __atomic_load_n(&p, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + __atomic_load(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + __atomic_store_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + __atomic_store(&p, &q, __ATOMIC_SEQ_CST); + + // expected-error@+2{{assignment to 'int *__single __ended_by(y)' (aka 'int *__single') 'q' requires corresponding assignment to 'y'; add self assignment 'y = y' if the value has not changed}} + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + q = __atomic_exchange_n(&p, q, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + __atomic_exchange(&p, &q, &r, __ATOMIC_SEQ_CST); + + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + __atomic_compare_exchange_n(&p, &q, r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + // expected-error@+1{{atomic operation on '__ended_by' pointer is not yet supported}} + __atomic_compare_exchange(&p, &q, &r, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_add_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_add(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_sub_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic arithmetic operation must be a pointer to '__unsafe_indexable' pointer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_sub(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_and_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_and(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_or_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_or(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_xor_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_xor(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_nand_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_nand(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_min_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_min(&p, 42, __ATOMIC_SEQ_CST); + + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_max_fetch(&p, 42, __ATOMIC_SEQ_CST); + // expected-error@+1{{address argument to atomic operation must be a pointer to integer or supported floating point type ('int *__single __ended_by(x)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') invalid)}} + __atomic_fetch_max(&p, 42, __ATOMIC_SEQ_CST); +} diff --git a/clang/test/BoundsSafety/Sema/atomic-types-auto-bound.c b/clang/test/BoundsSafety/Sema/atomic-types-auto-bound.c new file mode 100644 index 0000000000000..940fa9ea26517 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-types-auto-bound.c @@ -0,0 +1,28 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void test(void) { + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *_Atomic p1; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *) p2; + + // The nested pointers should be __single. + int *_Atomic *_Atomic __unsafe_indexable p3; + _Atomic(int *) *_Atomic __unsafe_indexable p4; + + // There shouldn't be an error about __bidi_indexable, since the attribute + // is replaced by __single when __counted_by is handled. + int len; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + int *_Atomic __counted_by(len) p5; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + int *__counted_by(len) _Atomic p6; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *__counted_by(len)) p7; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *) __counted_by(len) p8; +} diff --git a/clang/test/BoundsSafety/Sema/atomic-types.c b/clang/test/BoundsSafety/Sema/atomic-types.c new file mode 100644 index 0000000000000..6a0ba701962ad --- /dev/null +++ b/clang/test/BoundsSafety/Sema/atomic-types.c @@ -0,0 +1,665 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// __unsafe_indexable + +struct unsafe_indexable_struct { + int *_Atomic __unsafe_indexable p1; + int *__unsafe_indexable _Atomic p2; + _Atomic(int *__unsafe_indexable) p3; + _Atomic(int *) __unsafe_indexable p4; +}; + +void unsafe_indexable_local(void) { + int *_Atomic __unsafe_indexable p1; + int *__unsafe_indexable _Atomic p2; + _Atomic(int *__unsafe_indexable) p3; + _Atomic(int *) __unsafe_indexable p4; + + int *_Atomic __unsafe_indexable *_Atomic __unsafe_indexable p5; + int *__unsafe_indexable _Atomic *_Atomic __unsafe_indexable p6; + _Atomic(int *__unsafe_indexable) *_Atomic __unsafe_indexable p7; + _Atomic(int *) __unsafe_indexable *_Atomic __unsafe_indexable p8; +} + +int *_Atomic __unsafe_indexable unsafe_indexable_decl_ret1(void); +int *__unsafe_indexable _Atomic unsafe_indexable_decl_ret2(void); +_Atomic(int *__unsafe_indexable) unsafe_indexable_decl_ret3(void); +_Atomic(int *) __unsafe_indexable unsafe_indexable_decl_ret4(void); + +// expected-warning@+1{{non-void function does not return a value}} +int *_Atomic __unsafe_indexable unsafe_indexable_def_ret1(void) {} +// expected-warning@+1{{non-void function does not return a value}} +int *__unsafe_indexable _Atomic unsafe_indexable_def_ret2(void) {} +// expected-warning@+1{{non-void function does not return a value}} +_Atomic(int *__unsafe_indexable) unsafe_indexable_def_ret3(void) {} +// expected-warning@+1{{non-void function does not return a value}} +_Atomic(int *) __unsafe_indexable unsafe_indexable_def_ret4(void) {} + +void unsafe_indexable_decl_p1(int *_Atomic __unsafe_indexable p); +void unsafe_indexable_decl_p2(int *__unsafe_indexable _Atomic p); +void unsafe_indexable_decl_p3(_Atomic(int *__unsafe_indexable) p); +void unsafe_indexable_decl_p4(_Atomic(int *) __unsafe_indexable p); + +void unsafe_indexable_def_p1(int *_Atomic __unsafe_indexable p) {} +void unsafe_indexable_def_p2(int *__unsafe_indexable _Atomic p) {} +void unsafe_indexable_def_p3(_Atomic(int *__unsafe_indexable) p) {} +void unsafe_indexable_def_p4(_Atomic(int *) __unsafe_indexable p) {} + +// __single + +struct single_struct { + int *_Atomic __single p1; + int *__single _Atomic p2; + _Atomic(int *__single) p3; + _Atomic(int *) __single p4; +}; + +void single_local(void) { + int *_Atomic __single p1; + int *__single _Atomic p2; + _Atomic(int *__single) p3; + _Atomic(int *) __single p4; + + int *_Atomic __single *_Atomic __single p5; + int *__single _Atomic *_Atomic __single p6; + _Atomic(int *__single) *_Atomic __single p7; + _Atomic(int *) __single *_Atomic __single p8; +} +int *_Atomic __single single_decl_ret1(void); +int *__single _Atomic single_decl_ret2(void); +_Atomic(int *__single) single_decl_ret3(void); +_Atomic(int *) __single single_decl_ret4(void); + +// expected-warning@+1{{non-void function does not return a value}} +int *_Atomic __single single_def_ret1(void) {} +// expected-warning@+1{{non-void function does not return a value}} +int *__single _Atomic single_def_ret2(void) {} +// expected-warning@+1{{non-void function does not return a value}} +_Atomic(int *__single) single_def_ret3(void) {} +// expected-warning@+1{{non-void function does not return a value}} +_Atomic(int *) __single single_def_ret4(void) {} + +void single_decl_p1(int *_Atomic __single p); +void single_decl_p2(int *__single _Atomic p); +void single_decl_p3(_Atomic(int *__single) p); +void single_decl_p4(_Atomic(int *) __single p); + +void single_def_p1(int *_Atomic __single p) {} +void single_def_p2(int *__single _Atomic p) {} +void single_def_p3(_Atomic(int *__single) p) {} +void single_def_p4(_Atomic(int *) __single p) {} + +// __indexable + +struct indexable_struct { + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *_Atomic __indexable p1; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *__indexable _Atomic p2; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *__indexable) p3; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *) __indexable p4; +}; + +void indexable_local(void) { + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *_Atomic __indexable p1; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *__indexable _Atomic p2; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *__indexable) p3; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *) __indexable p4; + + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *_Atomic __indexable *_Atomic __unsafe_indexable p5; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *__indexable _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *__indexable) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *) __indexable *_Atomic __unsafe_indexable p8; + + // expected-error@+2{{_Atomic on '__indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *_Atomic __indexable *_Atomic __indexable p9; + // expected-error@+2{{_Atomic on '__indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + int *__indexable _Atomic *_Atomic __indexable p10; + // expected-error@+2{{_Atomic on '__indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *__indexable) *_Atomic __indexable p11; + // expected-error@+2{{_Atomic on '__indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *) __indexable *_Atomic __indexable p12; + + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int *_Nullable __indexable) p13; + // expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} + _Atomic(int * _Nullable) __indexable p14; +} + +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +int *_Atomic __indexable indexable_decl_ret1(void); +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +int *__indexable _Atomic indexable_decl_ret2(void); +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +_Atomic(int *__indexable) indexable_decl_ret3(void); +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +_Atomic(int *) __indexable indexable_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +int *_Atomic __indexable indexable_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +int *__indexable _Atomic indexable_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +_Atomic(int *__indexable) indexable_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +_Atomic(int *) __indexable indexable_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_decl_p1(int *_Atomic __indexable p); +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_decl_p2(int *__indexable _Atomic p); +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_decl_p3(_Atomic(int *__indexable) p); +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_decl_p4(_Atomic(int *) __indexable p); + +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_def_p1(int *_Atomic __indexable p) {} +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_def_p2(int *__indexable _Atomic p) {} +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_def_p3(_Atomic(int *__indexable) p) {} +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +void indexable_def_p4(_Atomic(int *) __indexable p) {} + +// __bidi_indexable + +struct bidi_indexable_struct { + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *_Atomic __bidi_indexable p1; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *__bidi_indexable _Atomic p2; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *__bidi_indexable) p3; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *) __bidi_indexable p4; +}; + +void bidi_indexable_local(void) { + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *_Atomic __bidi_indexable p1; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *__bidi_indexable _Atomic p2; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *__bidi_indexable) p3; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *) __bidi_indexable p4; + + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *_Atomic __bidi_indexable *_Atomic __unsafe_indexable p5; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *__bidi_indexable _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *__bidi_indexable) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *) __bidi_indexable *_Atomic __unsafe_indexable p8; + + // expected-error@+2{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *_Atomic __bidi_indexable *_Atomic __bidi_indexable p9; + // expected-error@+2{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + int *__bidi_indexable _Atomic *_Atomic __bidi_indexable p10; + // expected-error@+2{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *__bidi_indexable) *_Atomic __bidi_indexable p11; + // expected-error@+2{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} + _Atomic(int *) __bidi_indexable *_Atomic __bidi_indexable p12; +} + +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +int *_Atomic __bidi_indexable bidi_indexable_decl_ret1(void); +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +int *__bidi_indexable _Atomic bidi_indexable_decl_ret2(void); +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +_Atomic(int *__bidi_indexable) bidi_indexable_decl_ret3(void); +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +_Atomic(int *) __bidi_indexable bidi_indexable_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +int *_Atomic __bidi_indexable bidi_indexable_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +int *__bidi_indexable _Atomic bidi_indexable_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +_Atomic(int *__bidi_indexable) bidi_indexable_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +_Atomic(int *) __bidi_indexable bidi_indexable_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_decl_p1(int *_Atomic __bidi_indexable p); +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_decl_p2(int *__bidi_indexable _Atomic p); +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_decl_p3(_Atomic(int *__bidi_indexable) p); +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_decl_p4(_Atomic(int *) __bidi_indexable p); + +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_def_p1(int *_Atomic __bidi_indexable p) {} +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_def_p2(int *__bidi_indexable _Atomic p) {} +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_def_p3(_Atomic(int *__bidi_indexable) p) {} +// expected-error@+1{{_Atomic on '__bidi_indexable' pointer is not yet supported}} +void bidi_indexable_def_p4(_Atomic(int *) __bidi_indexable p) {} + +// __counted_by + +struct counted_by_struct { + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + int *_Atomic __counted_by(16) p1; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + int *__counted_by(16) _Atomic p2; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *__counted_by(16)) p3; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *) __counted_by(16) p4; +}; + +void counted_by_local(void) { + int len; + + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + int *_Atomic __counted_by(len) p1; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + int *__counted_by(len) _Atomic p2; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *__counted_by(len)) p3; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *) __counted_by(len) p4; + + // expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *_Atomic __counted_by(len) * _Atomic __unsafe_indexable p5; + // expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__counted_by(len) _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *__counted_by(len)) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} + _Atomic(int *) __counted_by(len) * _Atomic __unsafe_indexable p8; +} + +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +int *_Atomic __counted_by(16) counted_by_decl_ret1(void); +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +int *__counted_by(16) _Atomic counted_by_decl_ret2(void); +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}}c +_Atomic(int *__counted_by(16)) counted_by_decl_ret3(void); +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +_Atomic(int *) __counted_by(16) counted_by_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +int *_Atomic __counted_by(16) counted_by_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +int *__counted_by(16) _Atomic counted_by_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +_Atomic(int *__counted_by(16)) counted_by_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +_Atomic(int *) __counted_by(16) counted_by_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_decl_p1(int *_Atomic __counted_by(16) p); +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_decl_p2(int *__counted_by(16) _Atomic p); +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_decl_p3(_Atomic(int *__counted_by(16)) p); +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_decl_p4(_Atomic(int *) __counted_by(16) p); + +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_def_p1(int *_Atomic __counted_by(16) p) {} +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_def_p2(int *__counted_by(16) _Atomic p) {} +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_def_p3(_Atomic(int *__counted_by(16)) p) {} +// expected-error@+1{{_Atomic on '__counted_by' pointer is not yet supported}} +void counted_by_def_p4(_Atomic(int *) __counted_by(16) p) {} + +// __sized_by + +struct sized_by_struct { + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + int *_Atomic __sized_by(16) p1; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + int *__sized_by(16) _Atomic p2; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + _Atomic(int *__sized_by(16)) p3; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + _Atomic(int *) __sized_by(16) p4; +}; + +void sized_by_local(void) { + int size; + + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + int *_Atomic __sized_by(size) p1; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + int *__sized_by(size) _Atomic p2; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + _Atomic(int *__sized_by(size)) p3; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + _Atomic(int *) __sized_by(size) p4; + + // expected-error@+1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *_Atomic __sized_by(size) * _Atomic __unsafe_indexable p5; + // expected-error@+1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__sized_by(size) _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + _Atomic(int *__sized_by(size)) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} + _Atomic(int *) __sized_by(size) * _Atomic __unsafe_indexable p8; +} + +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +int *_Atomic __sized_by(16) sized_by_decl_ret1(void); +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +int *__sized_by(16) _Atomic sized_by_decl_ret2(void); +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +_Atomic(int *__sized_by(16)) sized_by_decl_ret3(void); +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +_Atomic(int *) __sized_by(16) sized_by_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +int *_Atomic __sized_by(16) sized_by_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +int *__sized_by(16) _Atomic sized_by_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +_Atomic(int *__sized_by(16)) sized_by_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +_Atomic(int *) __sized_by(16) sized_by_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_decl_p1(int *_Atomic __sized_by(16) p); +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_decl_p2(int *__sized_by(16) _Atomic p); +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_decl_p3(_Atomic(int *__sized_by(16)) p); +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_decl_p4(_Atomic(int *) __sized_by(16) p); + +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_def_p1(int *_Atomic __sized_by(16) p) {} +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_def_p2(int *__sized_by(16) _Atomic p) {} +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_def_p3(_Atomic(int *__sized_by(16)) p) {} +// expected-error@+1{{_Atomic on '__sized_by' pointer is not yet supported}} +void sized_by_def_p4(_Atomic(int *) __sized_by(16) p) {} + +// __counted_by_or_null + +struct counted_by_or_null_struct { + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + int *_Atomic __counted_by_or_null(16) p1; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + int *__counted_by_or_null(16) _Atomic p2; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + _Atomic(int *__counted_by_or_null(16)) p3; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + _Atomic(int *) __counted_by_or_null(16) p4; +}; + +void counted_by_or_null_local(void) { + int len; + + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + int *_Atomic __counted_by_or_null(len) p1; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + int *__counted_by_or_null(len) _Atomic p2; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + _Atomic(int *__counted_by_or_null(len)) p3; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + _Atomic(int *) __counted_by_or_null(len) p4; + + // expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + int *_Atomic __counted_by_or_null(len) * _Atomic __unsafe_indexable p5; + // expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + int *__counted_by_or_null(len) _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + _Atomic(int *__counted_by_or_null(len)) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} + _Atomic(int *) __counted_by_or_null(len) * _Atomic __unsafe_indexable p8; +} + +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +int *_Atomic __counted_by_or_null(16) counted_by_or_null_decl_ret1(void); +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +int *__counted_by_or_null(16) _Atomic counted_by_or_null_decl_ret2(void); +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}}c +_Atomic(int *__counted_by_or_null(16)) counted_by_or_null_decl_ret3(void); +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +_Atomic(int *) __counted_by_or_null(16) counted_by_or_null_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +int *_Atomic __counted_by_or_null(16) counted_by_or_null_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +int *__counted_by_or_null(16) _Atomic counted_by_or_null_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +_Atomic(int *__counted_by_or_null(16)) counted_by_or_null_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +_Atomic(int *) __counted_by_or_null(16) counted_by_or_null_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_decl_p1(int *_Atomic __counted_by_or_null(16) p); +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_decl_p2(int *__counted_by_or_null(16) _Atomic p); +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_decl_p3(_Atomic(int *__counted_by_or_null(16)) p); +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_decl_p4(_Atomic(int *) __counted_by_or_null(16) p); + +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_def_p1(int *_Atomic __counted_by_or_null(16) p) {} +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_def_p2(int *__counted_by_or_null(16) _Atomic p) {} +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_def_p3(_Atomic(int *__counted_by_or_null(16)) p) {} +// expected-error@+1{{_Atomic on '__counted_by_or_null' pointer is not yet supported}} +void counted_by_or_null_def_p4(_Atomic(int *) __counted_by_or_null(16) p) {} + +// __sized_by_or_null + +struct sized_by_or_null_struct { + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + int *_Atomic __sized_by_or_null(16) p1; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + int *__sized_by_or_null(16) _Atomic p2; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + _Atomic(int *__sized_by_or_null(16)) p3; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + _Atomic(int *) __sized_by_or_null(16) p4; +}; + +void sized_by_or_null_local(void) { + int size; + + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + int *_Atomic __sized_by_or_null(size) p1; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + int *__sized_by_or_null(size) _Atomic p2; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + _Atomic(int *__sized_by_or_null(size)) p3; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + _Atomic(int *) __sized_by_or_null(size) p4; + + // expected-error@+1{{'__sized_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + int *_Atomic __sized_by_or_null(size) * _Atomic __unsafe_indexable p5; + // expected-error@+1{{'__sized_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + int *__sized_by_or_null(size) _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + _Atomic(int *__sized_by_or_null(size)) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} + _Atomic(int *) __sized_by_or_null(size) * _Atomic __unsafe_indexable p8; +} + +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +int *_Atomic __sized_by_or_null(16) sized_by_or_null_decl_ret1(void); +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +int *__sized_by_or_null(16) _Atomic sized_by_or_null_decl_ret2(void); +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +_Atomic(int *__sized_by_or_null(16)) sized_by_or_null_decl_ret3(void); +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +_Atomic(int *) __sized_by_or_null(16) sized_by_or_null_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +int *_Atomic __sized_by_or_null(16) sized_by_or_null_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +int *__sized_by_or_null(16) _Atomic sized_by_or_null_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +_Atomic(int *__sized_by_or_null(16)) sized_by_or_null_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +_Atomic(int *) __sized_by_or_null(16) sized_by_or_null_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_decl_p1(int *_Atomic __sized_by_or_null(16) p); +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_decl_p2(int *__sized_by_or_null(16) _Atomic p); +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_decl_p3(_Atomic(int *__sized_by_or_null(16)) p); +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_decl_p4(_Atomic(int *) __sized_by_or_null(16) p); + +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_def_p1(int *_Atomic __sized_by_or_null(16) p) {} +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_def_p2(int *__sized_by_or_null(16) _Atomic p) {} +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_def_p3(_Atomic(int *__sized_by_or_null(16)) p) {} +// expected-error@+1{{_Atomic on '__sized_by_or_null' pointer is not yet supported}} +void sized_by_or_null_def_p4(_Atomic(int *) __sized_by_or_null(16) p) {} + +// __ended_by + +void ended_by_params( + int * pEnd, + + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + int *_Atomic __ended_by(pEnd) p1, + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + int *__ended_by(pEnd) _Atomic p2, + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + _Atomic(int *__ended_by(pEnd)) p3, + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + _Atomic(int *) __ended_by(pEnd) p4, + + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + int *_Atomic __ended_by(pEnd) * _Atomic __unsafe_indexable p5, + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + int *__ended_by(pEnd) _Atomic *_Atomic __unsafe_indexable p6, + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + _Atomic(int *__ended_by(pEnd)) *_Atomic __unsafe_indexable p7, + // expected-error@+1{{_Atomic on '__ended_by' pointer is not yet supported}} + _Atomic(int *) __ended_by(pEnd) * _Atomic __unsafe_indexable p8, + + int *_Atomic p9, + // expected-error@+1{{_Atomic on 'end' pointer is not yet supported}} + int * __ended_by(p9) pStart, + int *_Atomic * _Atomic p10, + // expected-error@+1{{_Atomic on 'end' pointer is not yet supported}} + int * __ended_by(p10) pStart2, + int * * _Atomic p11, + // expected-error@+1{{_Atomic on 'end' pointer is not yet supported}} + int * __ended_by(p11) pStart3, + int * _Atomic * p12, + int * * __ended_by(p12) pStart4 +); + +// __terminated_by + +struct terminated_by_struct { + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *_Atomic __null_terminated __single p1; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *__null_terminated __single _Atomic p2; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *__null_terminated __single) p3; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *) __null_terminated __single p4; +}; + +void terminated_by_local(void) { + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *_Atomic __null_terminated __single p1; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *__null_terminated __single _Atomic p2; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *__null_terminated __single) p3; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *) __null_terminated __single p4; + + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *_Atomic __null_terminated __single *_Atomic __unsafe_indexable p5; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *__null_terminated __single _Atomic *_Atomic __unsafe_indexable p6; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *__null_terminated __single) *_Atomic __unsafe_indexable p7; + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *) __null_terminated __single *_Atomic __unsafe_indexable p8; + + // expected-error@+2{{_Atomic on '__terminated_by' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *_Atomic __null_terminated __single *_Atomic __null_terminated __single p9; + // expected-error@+2{{_Atomic on '__terminated_by' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + int *__null_terminated __single _Atomic *_Atomic __null_terminated __single p10; + // expected-error@+2{{_Atomic on '__terminated_by' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *__null_terminated __single) *_Atomic __null_terminated __single p11; + // expected-error@+2{{_Atomic on '__terminated_by' pointer is not yet supported}} + // expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} + _Atomic(int *) __null_terminated __single *_Atomic __null_terminated __single p12; +} + +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +int *_Atomic __null_terminated __single terminated_by_decl_ret1(void); +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +int *__null_terminated __single _Atomic terminated_by_decl_ret2(void); +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +_Atomic(int *__null_terminated __single) terminated_by_decl_ret3(void); +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +_Atomic(int *) __null_terminated __single terminated_by_decl_ret4(void); + +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +int *_Atomic __null_terminated __single terminated_by_def_ret1(void) {} +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +int *__null_terminated __single _Atomic terminated_by_def_ret2(void) {} +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +_Atomic(int *__null_terminated __single) terminated_by_def_ret3(void) {} +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +_Atomic(int *) __null_terminated __single terminated_by_def_ret4(void) {} + +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_decl_p1(int *_Atomic __null_terminated __single p); +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_decl_p2(int *__null_terminated __single _Atomic p); +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_decl_p3(_Atomic(int *__null_terminated __single) p); +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_decl_p4(_Atomic(int *) __null_terminated __single p); + +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_def_p1(int *_Atomic __null_terminated __single p) {} +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_def_p2(int *__null_terminated __single _Atomic p) {} +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_def_p3(_Atomic(int *__null_terminated __single) p) {} +// expected-error@+1{{_Atomic on '__terminated_by' pointer is not yet supported}} +void terminated_by_def_p4(_Atomic(int *) __null_terminated __single p) {} diff --git a/clang/test/BoundsSafety/Sema/auto-bound-const-char-pointer-quals.c b/clang/test/BoundsSafety/Sema/auto-bound-const-char-pointer-quals.c new file mode 100644 index 0000000000000..099ac119b7e0f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/auto-bound-const-char-pointer-quals.c @@ -0,0 +1,24 @@ + + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// expected-no-diagnostics + +#include + +// pass_dynamic_object_size attribute only applies to constant pointer +// arguments, make sure that after handling __null_terminated the pointer +// remains const. + +unsigned long my_strlen_c(const char *const s __attribute__((pass_dynamic_object_size(0)))) { + return __builtin_strlen(s); +} + +unsigned long my_strlen_c_nt(const char *const __null_terminated s __attribute__((pass_dynamic_object_size(0)))) { + return __builtin_strlen(s); +} + +unsigned long my_strlen_nt_c(const char *__null_terminated const s __attribute__((pass_dynamic_object_size(0)))) { + return __builtin_strlen(s); +} diff --git a/clang/test/BoundsSafety/Sema/auto-bound-decl-remaining-in-declcontext.c b/clang/test/BoundsSafety/Sema/auto-bound-decl-remaining-in-declcontext.c new file mode 100644 index 0000000000000..a4e07f857ce66 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/auto-bound-decl-remaining-in-declcontext.c @@ -0,0 +1,8 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wuninitialized -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wuninitialized -verify %s + +inline void *a() { + void *b; // expected-note{{initialize the variable 'b' to silence this warning}} + return b; // expected-warning{{variable 'b' is uninitialized when used here}} +} diff --git a/clang/test/BoundsSafety/Sema/auto-type-attribute-only-mode.c b/clang/test/BoundsSafety/Sema/auto-type-attribute-only-mode.c new file mode 100644 index 0000000000000..1b9df35b41973 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/auto-type-attribute-only-mode.c @@ -0,0 +1,14 @@ + + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +void foo(int *__counted_by(len) ptr, int len) { + __auto_type p = ptr; // expected-error{{passing '__counted_by' pointer as __auto_type initializer is not yet supported}} +} diff --git a/clang/test/BoundsSafety/Sema/auto-type-from-bound-pointers.c b/clang/test/BoundsSafety/Sema/auto-type-from-bound-pointers.c new file mode 100644 index 0000000000000..afb1dfedc0881 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/auto-type-from-bound-pointers.c @@ -0,0 +1,116 @@ + + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct SequencePtrs { + char *__ended_by(iter) start; + char *__ended_by(end) iter; + char *end; +}; + +void TestEndedBy(struct SequencePtrs *sp) { + __auto_type local_start = sp->start; // expected-error{{passing '__ended_by' pointer as __auto_type initializer is not yet supported}} + __auto_type local_iter = sp->iter; // expected-error{{passing '__ended_by' pointer as __auto_type initializer is not yet supported}} + __auto_type local_end = sp->end; // expected-error{{passing end pointer as __auto_type initializer is not yet supported}} +} + +void TestCountedBy(int *__counted_by(len) ptr, int len) { + __auto_type local_counted_ptr = ptr; // expected-error{{passing '__counted_by' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len = len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len_addrof = &len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} +} + +void TestSizedBy(void *__sized_by(len) ptr, unsigned len) { + __auto_type local_counted_ptr = ptr; // expected-error{{passing '__sized_by' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len = len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{pointer with '__sized_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len_addrof = &len; // expected-error{{variable referred to by '__sized_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} +} + +void TestOutCountedBy(int *__counted_by(*len) *ptr, int *len) { + __auto_type local_counted_ptr = ptr; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len = len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + __auto_type local_counted_ptr_deref = *ptr; // expected-error{{passing '__counted_by' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len_deref = *len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} +} + +void TestOutSizedBy(void *__sized_by(*len) *ptr, unsigned *len) { + __auto_type local_counted_ptr = ptr; // expected-error{{pointer with '__sized_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len = len; // expected-error{{variable referred to by '__sized_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + __auto_type local_counted_ptr_deref = *ptr; // expected-error{{passing '__sized_by' pointer as __auto_type initializer is not yet supported}} +} + +void TestCountedByOrNull(int *__counted_by_or_null(len) ptr, int len) { + __auto_type local_counted_ptr = ptr; // expected-error{{passing '__counted_by_or_null' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len = len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{pointer with '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len_addrof = &len; // expected-error{{variable referred to by '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} +} + +void TestSizedByOrNull(void *__sized_by_or_null(len) ptr, unsigned len) { + __auto_type local_counted_ptr = ptr; // expected-error{{passing '__sized_by_or_null' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len = len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{pointer with '__sized_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len_addrof = &len; // expected-error{{variable referred to by '__sized_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} +} + +void TestOutCountedByOrNull(int *__counted_by_or_null(*len) *ptr, int *len) { + __auto_type local_counted_ptr = ptr; // expected-error{{pointer with '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len = len; // expected-error{{variable referred to by '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + __auto_type local_counted_ptr_deref = *ptr; // expected-error{{passing '__counted_by_or_null' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len_deref = *len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{pointer with '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} +} + +void TestOutSizedByOrNull(void *__sized_by_or_null(*len) *ptr, unsigned *len) { + __auto_type local_counted_ptr = ptr; // expected-error{{pointer with '__sized_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len = len; // expected-error{{variable referred to by '__sized_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + __auto_type local_counted_ptr_deref = *ptr; // expected-error{{passing '__sized_by_or_null' pointer as __auto_type initializer is not yet supported}} +} + +void TestOutCountedByArray(int (*ptr)[__counted_by(*len)], int *len) { // expected-error{{pointer to incomplete __counted_by array type 'int[]' not allowed; did you mean to use a nested pointer type?}} + __auto_type local_counted_ptr = ptr; // expected-error{{array with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + __auto_type local_len = len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable}} + __auto_type local_counted_ptr_deref = *ptr; // expected-error{{passing '__counted_by' pointer as __auto_type initializer is not yet supported}} + __auto_type local_len_deref = *len; + __auto_type local_counted_ptr_addrof = &ptr; // expected-error{{array with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} +} + + +struct CountedStruct { + char * __counted_by(n) buf; + int n; +}; + +void foo(struct CountedStruct * __single p) { + __auto_type ebuf = p->buf; // expected-error{{passing '__counted_by' pointer as __auto_type initializer is not yet supported}} + __auto_type *__single eptr = &(p->buf); // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} +} + +void baz(void * __unsafe_indexable p); + +void bar(char * p) { + char * implicit_bidi = p; + baz( + (void*)({ + __auto_type pointer_to_bidi = &implicit_bidi; + *pointer_to_bidi; + }) + ); +} + +struct FAMStruct { + int n; + char buf[__counted_by(n)]; +}; + +void foo_fam(struct FAMStruct * __single p) { + __auto_type ebuf = p->buf; // expected-error{{passing '__counted_by' pointer as __auto_type initializer is not yet supported}} + __auto_type *__single eptr = &(p->buf); // expected-error{{cannot take address of incomplete __counted_by array}} + // expected-note@-1{{remove '&' to get address as 'char *' instead of 'char (*)[__counted_by(n)]'}} +} diff --git a/clang/test/BoundsSafety/Sema/auto-type-from-xnu.c b/clang/test/BoundsSafety/Sema/auto-type-from-xnu.c new file mode 100644 index 0000000000000..a7cc819d05de2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/auto-type-from-xnu.c @@ -0,0 +1,22 @@ + + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// expected-no-diagnostics + +#include + +typedef struct foo { + int field; +} *__single foo_t; + +extern void takes_foo(foo_t foo); + +struct foo array_of_one[1]; + +void +call_takes_foo(void) +{ + __auto_type foo = array_of_one; + takes_foo(foo); +} diff --git a/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-escape.c b/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-escape.c new file mode 100644 index 0000000000000..3dc73e0ff6960 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-escape.c @@ -0,0 +1,45 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int *__counted_by(n) cb_in(int n) { + int *n1 = &n; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int n2 = n; +} + +int *__counted_by(*n) cb_out(int *n) { + int **n1 = &n; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *n2 = n; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int n3 = *n; +} + +void *__sized_by(n) sb_in(int n) { + int *n1 = &n; // expected-error{{variable referred to by '__sized_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int n2 = n; +} + +int *__counted_by_or_null(n) cbn_in(int n) { + int *n1 = &n; // expected-error{{variable referred to by '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int n2 = n; +} + +void *__sized_by_or_null(n) sbn_in(int n) { + int *n1 = &n; // expected-error{{variable referred to by '__sized_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int n2 = n; +} + +int *__ended_by(end) eb_in(int *end) { + int **e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *e2 = end; + int e3 = *end; +} + +int *__ended_by(*end) eb_out(int **end) { + int ***e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **e2 = end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *e3 = *end; + int e4 = **end; +} diff --git a/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-immutable-dependent-param.c b/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-immutable-dependent-param.c new file mode 100644 index 0000000000000..53852622d2e19 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-immutable-dependent-param.c @@ -0,0 +1,142 @@ + +// TODO: We should get the same diagnostics with/without return_size (rdar://138982703) + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,legacy %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,legacy %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s + +#include + +void i(int); +void pi(int *); +void ppi(int **); +void pppi(int ***); + +// __counted_by() + +void cb_in_in(int count, int *__counted_by(count) ptr); +void cb_in_out(int count, int *__counted_by(count) *ptr); +void cb_out_in(int *count, int *__counted_by(*count) ptr); +void cb_out_out(int *count, int *__counted_by(*count) *ptr); + +int *__counted_by(count) ret_cb_in(int count); +int *__counted_by(*count) ret_cb_out(int *count); + +int *__counted_by(count) mixed_simple(int count, int *__counted_by(count) ptr); +int *__counted_by(*count) mixed_inout_count(int *count, int *__counted_by(*count) ptr); +int *__counted_by(*count) mixed_out_ptr(int *count, int *__counted_by(*count) *ptr); +int *__counted_by(*count) mixed_inout_count_out_ptr(int *count, int *__counted_by(*count) ptr, int *__counted_by(*count) *out_ptr); + +int *__counted_by(count) test_cb_in(int count) { + int c; + int *__counted_by(c) p; + + count = 42; // rs-error{{parameter 'count' is implicitly read-only due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + + i(count); + pi(&count); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + + cb_in_in(count, p); + cb_in_out(count, &p); // expected-error{{passing address of 'p' as an indirect parameter; must also pass 'c' or its address because the type of 'p', 'int *__single __counted_by(c)' (aka 'int *__single'), refers to 'c'}} + cb_out_in(&count, p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + // legacy-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single'}} + cb_out_out(&count, &p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + + (void)ret_cb_in(count); + (void)ret_cb_out(&count); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + + (void)mixed_simple(count, p); + (void)mixed_inout_count(&count, p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + // legacy-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single'}} + (void)mixed_out_ptr(&count, &p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} + // legacy-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single'}} + (void)mixed_inout_count_out_ptr(&count, p, &p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_in' ('int *__single __counted_by(count)' (aka 'int *__single'))}} +} + +int *__counted_by(*count) test_cb_out(int *count) { + int c; + int *__counted_by(c) p; + + *count = 42; + count = p; // expected-error{{not allowed to change out parameter used as dependent count expression of other parameter or return type}} + + i(*count); + pi(count); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_out' ('int *__single __counted_by(*count)' (aka 'int *__single'))}} + ppi(&count); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_out' ('int *__single __counted_by(*count)' (aka 'int *__single'))}} + + cb_in_in(*count, p); + cb_in_out(*count, &p); // expected-error{{passing address of 'p' as an indirect parameter; must also pass 'c' or its address because the type of 'p', 'int *__single __counted_by(c)' (aka 'int *__single'), refers to 'c'}} + cb_out_in(count, p); + // legacy-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single'}} + cb_out_out(count, &p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_out' ('int *__single __counted_by(*count)' (aka 'int *__single'))}} + + (void)ret_cb_in(*count); + (void)ret_cb_out(count); + + (void)mixed_simple(*count, p); + (void)mixed_inout_count(count, p); + // legacy-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single'}} + (void)mixed_out_ptr(count, &p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_out' ('int *__single __counted_by(*count)' (aka 'int *__single'))}} + // legacy-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single'}} + (void)mixed_inout_count_out_ptr(count, p, &p); // rs-error{{parameter 'count' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__counted_by' attribute in the return type of 'test_cb_out' ('int *__single __counted_by(*count)' (aka 'int *__single'))}} +} + +// Other variants of __counted_by() +// The logic is the same as for __counted_by(), so test only a few cases. + +int *__counted_by_or_null(count) test_cbn_in(int count) { + count = 42; // rs-error{{parameter 'count' is implicitly read-only due to being used by the '__counted_by_or_null' attribute in the return type of 'test_cbn_in' ('int *__single __counted_by_or_null(count)' (aka 'int *__single'))}} +} + +void *__sized_by(size) test_sb_in(int size) { + size = 42; // rs-error{{parameter 'size' is implicitly read-only due to being used by the '__sized_by' attribute in the return type of 'test_sb_in' ('void *__single __sized_by(size)' (aka 'void *__single'))}} +} + +void *__sized_by_or_null(size) test_sbn_in(int size) { + size = 42; // rs-error{{parameter 'size' is implicitly read-only due to being used by the '__sized_by_or_null' attribute in the return type of 'test_sbn_in' ('void *__single __sized_by_or_null(size)' (aka 'void *__single'))}} +} + +// __ended_by() + +void eb_in(int *__ended_by(end) start, int *end); +void eb_out(int *__ended_by(*end) start, int **end); + +int *__ended_by(end) ret_eb_in(int *end); +int *__ended_by(*end) ret_eb_out(int **end); + +int *__ended_by(end) test_eb_in(int *end) { + int *p; + + *end = 42; + end = p; // rs-error{{parameter 'end' is implicitly read-only due to being used by the '__ended_by' attribute in the return type of 'test_eb_in' ('int *__single __ended_by(end)' (aka 'int *__single'))}} + + i(*end); + pi(end); + ppi(&end); // rs-error{{parameter 'end' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__ended_by' attribute in the return type of 'test_eb_in' ('int *__single __ended_by(end)' (aka 'int *__single'))}} + + eb_in(p, end); + eb_out(p, &end); // expected-error{{type of 'end', 'int *__single', is incompatible with parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single')}} + + (void)ret_eb_in(end); + (void)ret_eb_out(&end); // rs-error{{parameter 'end' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__ended_by' attribute in the return type of 'test_eb_in' ('int *__single __ended_by(end)' (aka 'int *__single'))}} +} + +int *__ended_by(*end) test_eb_out(int **end) { + int *__single p; + + **end = 42; + *end = p; + end = &p; // rs-error{{parameter 'end' is implicitly read-only due to being used by the '__ended_by' attribute in the return type of 'test_eb_out' ('int *__single __ended_by(*end)' (aka 'int *__single'))}} + + i(**end); + pi(*end); + ppi(end); // rs-error{{parameter 'end' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__ended_by' attribute in the return type of 'test_eb_out' ('int *__single __ended_by(*end)' (aka 'int *__single'))}} + pppi(&end); // rs-error{{parameter 'end' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__ended_by' attribute in the return type of 'test_eb_out' ('int *__single __ended_by(*end)' (aka 'int *__single'))}} + + eb_in(p, *end); + eb_out(p, end); // expected-error{{type of 'end', 'int *__single*__single', is incompatible with parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single')}} + + (void)ret_eb_in(*end); + (void)ret_eb_out(end); // rs-error{{parameter 'end' is implicitly read-only and cannot be passed as an indirect argument due to being used by the '__ended_by' attribute in the return type of 'test_eb_out' ('int *__single __ended_by(*end)' (aka 'int *__single'))}} +} diff --git a/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-missing.c b/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-missing.c new file mode 100644 index 0000000000000..887b27ad2a2bc --- /dev/null +++ b/clang/test/BoundsSafety/Sema/bounds-attributed-in-return-missing.c @@ -0,0 +1,26 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +// The warnings should be always errors (rdar://136173954). + +// expected-warning@+1{{non-void function does not return a value}} +int *__counted_by(42) missing(void) {} + +// expected-warning@+1{{non-void function does not return a value}} +int *__counted_by(count) missing2(int count) {} + +int *__counted_by(42) mismatch(void) { + return; // expected-error{{non-void function 'mismatch' should return a value}} +} + +int *__counted_by(count) mismatch2(int count) { + return; // expected-error{{non-void function 'mismatch2' should return a value}} +} diff --git a/clang/test/BoundsSafety/Sema/bounds-safety-attr-ignored.c b/clang/test/BoundsSafety/Sema/bounds-safety-attr-ignored.c new file mode 100644 index 0000000000000..fef7d4a5cfc44 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/bounds-safety-attr-ignored.c @@ -0,0 +1,40 @@ + +// RUN: %clang_cc1 -x c -verify -verify=c %s +// RUN: %clang_cc1 -x c++ -verify=expected -verify=cxx %s +// RUN: %clang_cc1 -x objective-c -verify -verify=c %s +// RUN: %clang_cc1 -x objective-c++ -verify=expected -verify=cxx %s + +int *__attribute__((bidi_indexable)) gbi; // expected-warning{{attribute ignored}} +int *__attribute__((unsafe_indexable)) gus; // expected-warning{{attribute ignored}} + +struct S1 { + // FIXME: c++ drops counted_by entirely because of late parsing but that's okay for now. + int *__attribute__((counted_by(len))) ptr; // expected-error{{undeclared identifier 'len'}} + int len; +}; + +struct S2 { + int len; + // cxx-error@+1{{invalid use of non-static data member 'len'}} + int *__attribute__((counted_by(len))) ptr; // ok +}; + +int foo1(int *__attribute__((counted_by(len))) ptr, int len) { // expected-error{{use of undeclared identifier 'len'}} + int i; + // cxx-error@+2{{cannot initialize a variable of type}} + // c-error@+1{{incompatible integer to pointer conversion initializing}} + int *__attribute__((indexable)) pi = i; // expected-warning{{attribute ignored}} + int *__attribute__((single)) ps; // expected-warning{{attribute ignored}} + return 0; +} + +// cxx-warning@+1{{'counted_by' attribute ignored}} +int foo2(int len, int *__attribute__((counted_by(len))) ptr); // c-error{{counted_by attribute only applies to non-static data members}} + +// c-error@+2{{sized_by attribute only applies to non-static data members}} +// cxx-warning@+1{{'sized_by' attribute ignored}} +void bar1(int size, void *__attribute__((sized_by(size))) ptr); +void bar2(void *__attribute__((sized_by(size))) ptr, int size); // expected-error{{use of undeclared identifier 'size'}} + +void baz1(void *end, void *__attribute__((ended_by(end))) start); // expected-warning{{'ended_by' attribute ignored}} +void baz2(void *__attribute__((ended_by(end))) start, void *end); // expected-error{{use of undeclared identifier 'end'}} diff --git a/clang/test/BoundsSafety/Sema/bounds-safety-call-param-warning-regressions.c b/clang/test/BoundsSafety/Sema/bounds-safety-call-param-warning-regressions.c new file mode 100644 index 0000000000000..5734a984134fb --- /dev/null +++ b/clang/test/BoundsSafety/Sema/bounds-safety-call-param-warning-regressions.c @@ -0,0 +1,47 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#include +#include + +#ifndef __bidi_indexable +#define __bidi_indexable +#endif + +typedef struct { + uint8_t dummy; + uint32_t value; +} __attribute__((packed)) packed_t; + +void external(void * __sized_by(size), int size); + +void test(void) { + packed_t s; + const int size = 4; + external(&s.value, size); + void *local = &s.value; +} + +void test_explicit_cast_single(void) { + packed_t s; + const int size = 4; + external((uint32_t * __single) &s.value, size); // expected-warning{{taking address of packed member 'value' of class or structure 'packed_t' may result in an unaligned pointer value}} + void *local = (uint32_t * __bidi_indexable)(uint32_t * __single) &s.value; // expected-warning{{taking address of packed member 'value' of class or structure 'packed_t' may result in an unaligned pointer value}} +} + +void test_explicit_cast_bidi(void) { + packed_t s; + const int size = 4; + external((uint32_t * __bidi_indexable) &s.value, size); // expected-warning{{taking address of packed member 'value' of class or structure 'packed_t' may result in an unaligned pointer value}} + void *local = (uint32_t * __bidi_indexable) &s.value; // expected-warning{{taking address of packed member 'value' of class or structure 'packed_t' may result in an unaligned pointer value}} +} + +void test_explicit_cast_sized(void) { + packed_t s; + const int size = 4; + external((uint32_t * __sized_by(size)) &s.value, size); // expected-warning{{taking address of packed member 'value' of class or structure 'packed_t' may result in an unaligned pointer value}} + void *local = (uint32_t * __sized_by(size)) &s.value; // expected-warning{{taking address of packed member 'value' of class or structure 'packed_t' may result in an unaligned pointer value}} + external((void * __sized_by(size)) &s.value, size); + local = (void * __sized_by(size)) &s.value; +} diff --git a/clang/test/BoundsSafety/Sema/builtin-memcpy-count-annotation.c b/clang/test/BoundsSafety/Sema/builtin-memcpy-count-annotation.c new file mode 100644 index 0000000000000..1037d8c01dcc8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/builtin-memcpy-count-annotation.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-note@+1{{consider adding '__sized_by(10)' to 'dst'}} +void foo(char *dst, char *src) { + // expected-error@+1{{passing 'char *__single' with pointee of size 1 to parameter of type 'void *__single __sized_by(function-parameter-0-2)' (aka 'void *__single') with size value of 10 always fails}} + __builtin_memcpy(dst, src, 10); +} diff --git a/clang/test/BoundsSafety/Sema/check-format-arguments.c b/clang/test/BoundsSafety/Sema/check-format-arguments.c new file mode 100644 index 0000000000000..3ebb30f379c08 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/check-format-arguments.c @@ -0,0 +1,24 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify -Wformat-nonliteral %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify -Wformat-nonliteral %s + +#include +#include + +#define __printflike(__fmt,__varargs) __attribute__((__format__ (__printf__, __fmt, __varargs))) + +void __printflike(1, 0) foo(const char *__null_terminated, va_list); + +void __printflike(2, 3) bar(const char *__unsafe_indexable p1, const char *__unsafe_indexable p2, ...) { + va_list variadicArgs; + va_start(variadicArgs, p2); + + foo(__unsafe_forge_null_terminated(const char *, p2), variadicArgs); + foo(__unsafe_forge_null_terminated(const char *, p2+1), variadicArgs); + foo(__unsafe_forge_null_terminated(const char *, "Hello, %s!\n"), variadicArgs); + + foo(__unsafe_forge_null_terminated(const char *, 2), variadicArgs); // expected-warning{{format string is not a string literal}} + foo(__unsafe_forge_null_terminated(const char *, p1), variadicArgs); // expected-warning{{format string is not a string literal}} + + va_end(variadicArgs); +} diff --git a/clang/test/BoundsSafety/Sema/complex-typespecs-with-bounds.c b/clang/test/BoundsSafety/Sema/complex-typespecs-with-bounds.c new file mode 100644 index 0000000000000..50cc6f3b7f3b8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/complex-typespecs-with-bounds.c @@ -0,0 +1,120 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// Tests the correctness of applying bounds attributes to complex type specifiers +// as well as to what extent other attributes (represented by _Nullable) are retained. + +#include "complex-typespecs-with-bounds.h" +#include + +void typeoftypes() { + typeof((long * _Nullable) 0) __single p1; + typeof(typeof(bar) *) __single p2; +} + +struct S { + // expected-note@+1 2{{pointer 'S::f1' declared here}} + char * _Nullable f1; +}; + +void typeofexprs(struct S s) { + // expected-error@+1{{'__single' attribute only applies to pointer arguments}} + typeof(foo) __single p1; + // expected-error@+1{{initializing 'typeof (foo())' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(foo()) __single p2 = foo(); + typeof(&foo) __single p3 = &foo; + // expected-error@+1{{function pointers cannot be indexable}} + typeof(&foo) __bidi_indexable p4; + typeof(&foo) __unsafe_indexable p5 = &foo; + + // expected-error@+1{{initializing 'typeof (bar)' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(bar) __single p6 = bar; + typeof(&bar) __single p7 = &bar; + typeof(bar) * __single p8 = &bar; + // expected-error@+1{{initializing 'typeof (bar[2]) *__single' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(bar[2]) * __single p9 = bar; + // expected-error@+1{{initializing 'typeof (&bar[2])' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(&bar[2]) __single p10 = bar; + // expected-error@+1{{initializing 'typeof (&*bar)' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(&*bar) __single p11 = &*bar; + + // expected-warning@+1{{initializing type 'typeof (s.f1)' (aka 'char *__bidi_indexable') with an expression of type 'char *__single _Nullable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'S::f1'}} + typeof(s.f1) __bidi_indexable p12 = s.f1; + // expected-warning@+1{{initializing type 'typeof (*s.f1) *__bidi_indexable' (aka 'char *__bidi_indexable') with an expression of type 'char *__single _Nullable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'S::f1'}} + typeof(*s.f1) * __bidi_indexable p13 = s.f1; + typeof(&*s.f1) __unsafe_indexable p14 = s.f1; +} + +typedef typeof(*bar) my_t; +typedef typeof(bar) my_ptr_t; +typedef typeof(*bar) * my_manual_ptr_t; + +void typedefs_of_typeof() { + // expected-error@+1{{initializing 'my_t *__single' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + my_t * __single p1 = bar; + // expected-error@+1{{initializing 'char *__single _Nullable' with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + my_ptr_t __single p2 = bar; + // expected-error@+1{{initializing 'my_manual_ptr_t __single' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + my_manual_ptr_t __single p3 = bar; + // expected-error@+1{{initializing 'my_manual_ptr_t __bidi_indexable' (aka 'char *__bidi_indexable') with an expression of incompatible type 'char *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + my_manual_ptr_t __bidi_indexable p4 = bar; + my_manual_ptr_t __unsafe_indexable p5 = bar; + // expected-error@+1{{assigning to 'my_manual_ptr_t __bidi_indexable' (aka 'char *__bidi_indexable') from incompatible type 'my_manual_ptr_t __unsafe_indexable' (aka 'char *__unsafe_indexable') casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + p4 = p5; +} + +void autotypes(void * void_param) { + // this could probably be made to work in theory, but it can always be worked around by simply adding a '*' + __auto_type __single p1 = bar; // expected-error{{bounds attribute '__single' cannot be applied to an undeduced type}} + __auto_type * __single p2 = bar; // expected-error{{initializing 'char *__single' with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + __auto_type * __single p3 = &*bar; // expected-error{{initializing 'char *__single' with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + + // check that an undeduced void pointee doesn't get around errors regarding type size + __auto_type * __bidi_indexable p4 = void_ptr; // expected-error{{initializing 'void *__bidi_indexable' with an expression of incompatible type 'void *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // expected-note@+1{{pointer 'p5' declared here}} + __auto_type * __bidi_indexable p5 = void_param; // expected-error{{cannot initialize indexable pointer with type 'void *__bidi_indexable' from __single pointer to incomplete type 'void *__single'; consider declaring pointer 'p5' as '__single'}} +} + +void typeofexpr_typeofexpr() { + typeof(bar) p1; + // expected-error@+1{{initializing 'typeof (p1)' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(p1) __single p2 = bar; +} + +void typeofexpr_typeoftype_typeofexpr() { + typeof(typeof(bar)) p1; + // expected-error@+1{{initializing 'typeof (p1)' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(p1) __single p2 = bar; +} + +void typeof_autotype1() { + __auto_type p1 = bar; + // expected-error@+1{{bounds attribute '__single' cannot be applied to attributed type 'char * _Nullable' in this context due to the surrounding 'typeof' specifier}} + typeof(p1) __single p2 = bar; + // expected-error@+1{{bounds attribute '__bidi_indexable' cannot be applied to attributed type 'char * _Nullable' in this context due to the surrounding 'typeof' specifier}} + typeof(p1) __bidi_indexable p3 = bar; + // expected-error@+1{{bounds attribute '__unsafe_indexable' cannot be applied to attributed type 'char * _Nullable' in this context due to the surrounding 'typeof' specifier}} + typeof(p1) __unsafe_indexable p4 = bar; + // expected-error@+1{{bounds attribute '__indexable' cannot be applied to attributed type 'char * _Nullable' in this context due to the surrounding 'typeof' specifier}} + typeof(p1) __indexable p5 = bar; +} + +void typeof_autotype2() { + __auto_type * p1 = bar; + // expected-error@+1{{initializing 'typeof (p1)' (aka 'char *__single') with an expression of incompatible type 'char * _Nullable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(p1) __single p2 = bar; +} + +void typeof_autotype3() { + __auto_type p1 = bare; + // expected-error@+1{{initializing 'typeof (p1)' (aka 'char *__single') with an expression of incompatible type 'char *' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + typeof(p1) __single p2 = bare; +} + + +// check that we don't emit the same error twice +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +_Atomic(int * _Nullable) __attribute__((address_space(2))) __indexable global1; +// expected-error@+1{{_Atomic on '__indexable' pointer is not yet supported}} +int * _Atomic __attribute__((address_space(2))) __indexable global2; diff --git a/clang/test/BoundsSafety/Sema/complex-typespecs-with-bounds.h b/clang/test/BoundsSafety/Sema/complex-typespecs-with-bounds.h new file mode 100644 index 0000000000000..11d3afb2a1003 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/complex-typespecs-with-bounds.h @@ -0,0 +1,8 @@ +#pragma clang system_header +// This is a system header for historical reasons that no longer apply. +// It still silences unrelated warnings in this header, so I'm leaving it. + +char * _Nullable foo(); +char * _Nullable bar; +char * bare; +void * void_ptr; diff --git a/clang/test/BoundsSafety/Sema/compound-literal-counted_by.c b/clang/test/BoundsSafety/Sema/compound-literal-counted_by.c new file mode 100644 index 0000000000000..ce000936742b7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/compound-literal-counted_by.c @@ -0,0 +1,436 @@ + +// TODO: We should get the same diagnostics with/without compound_literal_init (rdar://138982703) +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +#include + +int side_effect(void); +int get_count(void); +char* get_single_ptr(); +struct cb_with_other_data { + int count; + char* __counted_by(count) buf; + int other; +}; +void consume_cb_with_other_data(struct cb_with_other_data); + +struct NestedCB { + struct cb_with_other_data inner; + int count; + char* __counted_by(count) buf; + int other; +}; + +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cb_with_other_data) == sizeof(struct no_attr_with_other_data), "size mismatch"); + +union TransparentUnion { + struct cb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + + +void no_diag(void) { + // No diagnostics + consume_cb_with_other_data((struct cb_with_other_data){ + 0x0, + 0x0, + 0x0 + }); +} + +struct cb_with_other_data global_no_diags = (struct cb_with_other_data) { + 0x0, + 0x0, + 0x0 +}; + + +struct cb_with_other_data field_initializers_with_side_effects(struct cb_with_other_data* s) { + *s = (struct cb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + struct cb_with_other_data s2 = (struct cb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + consume_cb_with_other_data((struct cb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect()} + ); + consume_cb_with_other_data((struct cb_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0} + ); + + (void) (struct cb_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0}; + + // no diags for structs without attributes + struct no_attrs { + int count; + char* buf; + }; + (void) (struct no_attrs) { side_effect(), 0x0}; + + // Nested + struct Contains_cb_with_other_data { + struct cb_with_other_data s; + int other; + }; + (void)(struct Contains_cb_with_other_data) { + .s = { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + }, + .other = 0x0 + }; + + // Nested CompoundLiteralExpr + (void)(struct NestedCB) { + // expected-warning@+1{{initializer '(struct cb_with_other_data){.count = 0, .buf = 0, .other = side_effect()}' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .inner = (struct cb_with_other_data) { + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminat}} + .other = side_effect() + }, + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect() + }; + + // Test array initializer list that initializes structs + (void)(struct cb_with_other_data[]){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {0, 0x0, side_effect()}, + {0, 0x0, 0x0} + }; + + union UnionWith_cb_with_other_data { + struct cb_with_other_data u; + int other; + }; + + (void)(union UnionWith_cb_with_other_data) { + { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + }; + + // Call a function that takes a transparent union + receive_transparent_union( + // Test very "untransparent" + (union TransparentUnion) {.cb = + { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + } + ); + // Call using + receive_transparent_union( + // Transparent + (struct cb_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + ); + + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + return (struct cb_with_other_data) { 0, 0x0, side_effect()}; +} + +struct cb_with_other_data global_cb_with_other_data_side_effect_init = + (struct cb_with_other_data) { + .buf = 0x0, + .count = 0, + .other = side_effect() // both-error{{initializer element is not a compile-time constant}} +}; + + +struct cb_with_other_data side_effects_in_ptr(struct cb_with_other_data* s) { + *s = (struct cb_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + consume_cb_with_other_data((struct cb_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0} + ); + + (void) (struct cb_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + (void)(struct NestedCB) { + .inner = (struct cb_with_other_data) { + .count = 0, + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }, + .count = 0, + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }; + + return (struct cb_with_other_data ){ + 0, + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; +} + +struct cb_with_other_data side_effects_in_count(struct cb_with_other_data* s) { + *s = (struct cb_with_other_data){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + consume_cb_with_other_data((struct cb_with_other_data){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0} + ); + + (void) (struct cb_with_other_data){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + (void)(struct NestedCB) { + .inner = (struct cb_with_other_data) { + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }, + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }; + + return (struct cb_with_other_data ){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; +} + +// To keep this test case small just test the `*s = CompoundLiteralExpr` variant +// in the remaining test cases. + +void constant_neg_count(struct cb_with_other_data* s) { + *s = (struct cb_with_other_data){ + -1, + // expected-error@+1{{negative count value of -1 for 'char *__single __counted_by(count)' (aka 'char *__single')}} + 0x0, + 0x0 + }; +} + +struct cb_with_other_data global_cb_with_other_data_constant_neg_count = + (struct cb_with_other_data) { + // expected-error@+1{{negative count value of -1 for 'global_cb_with_other_data_constant_neg_count.buf' of type 'char *__single __counted_by(count)' (aka 'char *__single')}} + .buf = 0x0, + .count = -1, + .other = 0x0 +}; + +struct NestedCB global_nested_cb_constant_neg_count = (struct NestedCB) { + .inner = (struct cb_with_other_data) { + .count = -1, + // expected-error@+1{{negative count value of -1 for 'global_nested_cb_constant_neg_count.inner.buf' of type 'char *__single __counted_by(count)' (aka 'char *__single')}} + .buf = 0x0, + .other = 0x0 + }, + .count = -1, + // expected-error@+1{{negative count value of -1 for 'global_nested_cb_constant_neg_count.buf' of type 'char *__single __counted_by(count)' (aka 'char *__single')}} + .buf = 0x0, + .other = 0 +}; + +void constant_pos_count_nullptr(struct cb_with_other_data* s) { + *s = (struct cb_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __counted_by(count)' (aka 'char *__single') and count value of 100 with null always fails}} + 0x0, + 0x0 + }; +} + +struct cb_with_other_data global_cb_with_other_data_constant_pos_count_nullptr = + (struct cb_with_other_data) { + // expected-error@+1{{initializing 'global_cb_with_other_data_constant_pos_count_nullptr.buf' of type 'char *__single __counted_by(count)' (aka 'char *__single') and count value of 100 with null always fails}} + .buf = 0x0, + .count = 100, + .other = 0x0 +}; + +struct NestedCB global_nested_cb_constant_post_count_nullptr = (struct NestedCB) { + .inner = (struct cb_with_other_data) { + .count = 100, + // expected-error@+1{{initializing 'global_nested_cb_constant_post_count_nullptr.inner.buf' of type 'char *__single __counted_by(count)' (aka 'char *__single') and count value of 100 with null always fails}} + .buf = 0x0, + .other = 0x0 + }, + .count = 100, + // expected-error@+1{{initializing 'global_nested_cb_constant_post_count_nullptr.buf' of type 'char *__single __counted_by(count)' (aka 'char *__single') and count value of 100 with null always fails}} + .buf = 0x0, + .other = 0 +}; + +void bad_arr_size(struct cb_with_other_data* s) { + char arr[3]; // expected-note{{'arr' declared here}} + *s = (struct cb_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __counted_by(count)' (aka 'char *__single') and count value of 100 with array 'arr' (which has 3 elements) always fails}} + arr, + 0x0 + }; +} + +// Can't refer to non constant expressions at file scope so the bad `count` +// diagnostic doesn't get emitted. +char global_arr[3] = {0}; +struct cb_with_other_data global_cb_with_other_data_bad_arr_size = + (struct cb_with_other_data) { + .buf = global_arr, // both-error{{initializer element is not a compile-time constant}} + .count = 100, + .other = 0x0 +}; + +void bad_count_and_single_ptr_src(struct cb_with_other_data* s, char* ptr) { // expected-note{{consider adding '__counted_by(100)' to 'ptr'}} + *s = (struct cb_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __counted_by(count)' (aka 'char *__single') and count value of 100 with 'char *__single' always fails}} + ptr, + 0x0 + }; +} + +// Can't refer to non constant expressions at file scope so the bad `count` +// diagnostic doesn't get emitted. +char* global_ptr; +struct cb_with_other_data global_cb_with_other_data_bad_count_single_ptr = + (struct cb_with_other_data) { + .buf = global_ptr , // both-error{{initializer element is not a compile-time constant}} + .count = 100, + .other = 0x0 +}; + +void bad_implicit_zero_count(struct cb_with_other_data* s, char*__bidi_indexable ptr, struct NestedCB* ncb) { + *s = (struct cb_with_other_data){ + // expected-warning@+1{{possibly initializing 'char *__single __counted_by(count)' (aka 'char *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + .buf = ptr + }; + + *ncb = (struct NestedCB) { + // expected-warning@+1{{possibly initializing 'char *__single __counted_by(count)' (aka 'char *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + .buf = ptr, + .inner = (struct cb_with_other_data) { + // expected-warning@+1{{possibly initializing 'char *__single __counted_by(count)' (aka 'char *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + .buf = ptr + } + }; +} + +void unknown_count_and_single_ptr_src(struct cb_with_other_data* s, char* ptr, struct NestedCB* ncb) { + int count = get_count(); + *s = (struct cb_with_other_data){ + count, // expected-note{{count initialized here}} + // expected-warning@+1{{count value is not statically known: initializing 'char *__single __counted_by(count)' (aka 'char *__single') with 'char *__single' is invalid for any count other than 0 or 1}} + ptr, + 0x0 + }; + + *ncb = (struct NestedCB) { + .inner = (struct cb_with_other_data) { + // expected-warning@+1{{count value is not statically known: initializing 'char *__single __counted_by(count)' (aka 'char *__single') with 'char *__single' is invalid for any count other than 0 or 1}} + .buf = ptr, + .count = count // expected-note{{count initialized here}} + }, + // expected-warning@+1{{count value is not statically known: initializing 'char *__single __counted_by(count)' (aka 'char *__single') with 'char *__single' is invalid for any count other than 0 or 1}} + .buf = ptr, + .count = count // expected-note{{count initialized here}} + }; +} + +void var_init_from_compound_literal_with_side_effect(char*__bidi_indexable ptr) { + // FIXME: This diagnostic is misleading. The actually restriction is the + // compound literal can't contain side-effects but the diagnostic talks about + // "non-constant array". If the side-effect (call to `get_count()`) is removed + // then this error goes away even though the compound literal is still + // non-constant due to initializing from `ptr`. + // both-error@+1{{cannot initialize array of type 'struct cb_with_other_data[]' with non-constant array of type 'struct cb_with_other_data[2]'}} + struct cb_with_other_data arr[] = (struct cb_with_other_data[]){ + {.buf = ptr, .count = 0x0, .other = 0x0}, + // expected-warning@+1{{initializer 'get_count()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {.buf = ptr, .count = 0x0, .other = get_count()}, + }; +} + +void call_null_ptr_cb(int new_count) { + consume_cb_with_other_data((struct cb_with_other_data) { + .buf = (char*)0, + .count = new_count + }); + consume_cb_with_other_data((struct cb_with_other_data) { + .buf = (void*)0, + .count = new_count + }); + consume_cb_with_other_data((struct cb_with_other_data) { + .buf = ((char*)(void*)(char*)0), + .count = new_count + }); +} diff --git a/clang/test/BoundsSafety/Sema/compound-literal-counted_by_or_null.c b/clang/test/BoundsSafety/Sema/compound-literal-counted_by_or_null.c new file mode 100644 index 0000000000000..508bd058bdc4d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/compound-literal-counted_by_or_null.c @@ -0,0 +1,417 @@ + +// TODO: We should get the same diagnostics with/without compound_literal_init (rdar://138982703) +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +#include + +int side_effect(void); +int get_count(void); +char* get_single_ptr(); +struct cbon_with_other_data { + int count; + char* __counted_by_or_null(count) buf; + int other; +}; +void consume_cbon_with_other_data(struct cbon_with_other_data); + +struct NestedCBON { + struct cbon_with_other_data inner; + int count; + char* __counted_by_or_null(count) buf; + int other; +}; + +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct cbon_with_other_data) == sizeof(struct no_attr_with_other_data), "size mismatch"); + +union TransparentUnion { + struct cbon_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + + +void no_diag(void) { + // No diagnostics + consume_cbon_with_other_data((struct cbon_with_other_data){ + 0x0, + 0x0, + 0x0 + }); +} + +struct cbon_with_other_data global_no_diags = (struct cbon_with_other_data) { + 0x0, + 0x0, + 0x0 +}; + +struct cbon_with_other_data field_initializers_with_side_effects(struct cbon_with_other_data* s) { + *s = (struct cbon_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + struct cbon_with_other_data s2 = (struct cbon_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + consume_cbon_with_other_data((struct cbon_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect()} + ); + consume_cbon_with_other_data((struct cbon_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0} + ); + + (void) (struct cbon_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0}; + + // no diags for structs without attributes + struct no_attrs { + int count; + char* buf; + }; + (void) (struct no_attrs) { side_effect(), 0x0}; + + // Nested + struct Contains_cbon_with_other_data { + struct cbon_with_other_data s; + int other; + }; + (void)(struct Contains_cbon_with_other_data) { + .s = { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + }, + .other = 0x0 + }; + + // Nested CompoundLiteralExpr + (void)(struct NestedCBON) { + // expected-warning@+1{{initializer '(struct cbon_with_other_data){.count = 0, .buf = 0, .other = side_effect()}' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .inner = (struct cbon_with_other_data) { + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminat}} + .other = side_effect() + }, + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect() + }; + + // Test array initializer list that initializes structs + (void)(struct cbon_with_other_data[]){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {0, 0x0, side_effect()}, + {0, 0x0, 0x0} + }; + + union UnionWith_cbon_with_other_data { + struct cbon_with_other_data u; + int other; + }; + + (void)(union UnionWith_cbon_with_other_data) { + { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + }; + + // Call a function that takes a transparent union + receive_transparent_union( + // Test very "untransparent" + (union TransparentUnion) {.cb = + { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + } + ); + // Call using + receive_transparent_union( + // Transparent + (struct cbon_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + ); + + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + return (struct cbon_with_other_data) { 0, 0x0, side_effect()}; +} + +struct cbon_with_other_data global_cbon_with_other_data_side_effect_init = + (struct cbon_with_other_data) { + .buf = 0x0, + .count = 0, + .other = side_effect() // both-error{{initializer element is not a compile-time constant}} +}; + + +struct cbon_with_other_data side_effects_in_ptr(struct cbon_with_other_data* s) { + *s = (struct cbon_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + consume_cbon_with_other_data((struct cbon_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0} + ); + + (void) (struct cbon_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + (void)(struct NestedCBON) { + .inner = (struct cbon_with_other_data) { + .count = 0, + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }, + .count = 0, + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }; + + return (struct cbon_with_other_data ){ + 0, + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; +} + +struct cbon_with_other_data side_effects_in_count(struct cbon_with_other_data* s) { + *s = (struct cbon_with_other_data){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + consume_cbon_with_other_data((struct cbon_with_other_data){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0} + ); + + (void) (struct cbon_with_other_data){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + (void)(struct NestedCBON) { + .inner = (struct cbon_with_other_data) { + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }, + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }; + + return (struct cbon_with_other_data ){ + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; +} + +// To keep this test case small just test the `*s = CompoundLiteralExpr` variant +// in the remaining test cases. + +void constant_neg_count(struct cbon_with_other_data* s) { + *s = (struct cbon_with_other_data){ + -1, + 0x0, // ok + 0x0 + }; +} + +struct cbon_with_other_data global_cbon_with_other_data_constant_neg_count = + (struct cbon_with_other_data) { + .buf = 0x0, // ok + .count = -1, + .other = 0x0 +}; + +struct NestedCBON global_nested_cbon_constant_neg_count = (struct NestedCBON) { + .inner = (struct cbon_with_other_data) { + .count = -1, + .buf = 0x0, // OK + .other = 0x0 + }, + .count = -1, + .buf = 0x0, + .other = 0 +}; + +void constant_pos_count_nullptr(struct cbon_with_other_data* s) { + *s = (struct cbon_with_other_data){ + 100, + 0x0, // OK + 0x0 + }; +} + +struct cbon_with_other_data global_cbon_with_other_data_constant_pos_count_nullptr = + (struct cbon_with_other_data) { + .buf = 0x0, // ok + .count = 100, + .other = 0x0 +}; + +struct NestedCBON global_nested_cbon_constant_post_count_nullptr = (struct NestedCBON) { + .inner = (struct cbon_with_other_data) { + .count = 100, + .buf = 0x0, // OK + .other = 0x0 + }, + .count = 100, + .buf = 0x0, // OK + .other = 0 +}; + +void bad_arr_size(struct cbon_with_other_data* s) { + char arr[3]; // expected-note{{'arr' declared here}} + *s = (struct cbon_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') and count value of 100 with array 'arr' (which has 3 elements) always fails}} + arr, + 0x0 + }; +} + +// Can't refer to non constant expressions at file scope so the bad `count` +// diagnostic doesn't get emitted. +char global_arr[3] = {0}; +struct cbon_with_other_data global_cbon_with_other_data_bad_arr_size = + (struct cbon_with_other_data) { + .buf = global_arr, // both-error{{initializer element is not a compile-time constant}} + .count = 100, + .other = 0x0 +}; + +void bad_count_and_single_ptr_src(struct cbon_with_other_data* s, char* ptr) { + *s = (struct cbon_with_other_data){ + 100, + // No diag because `ptr` might be null + ptr, + 0x0 + }; +} + +void bad_implicit_zero_count(struct cbon_with_other_data* s, char*__bidi_indexable ptr, struct NestedCBON* ncbon) { + *s = (struct cbon_with_other_data){ + // expected-warning@+1{{possibly initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + .buf = ptr + }; + + *ncbon = (struct NestedCBON) { + // expected-warning@+1{{possibly initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + .buf = ptr, + .inner = (struct cbon_with_other_data) { + // expected-warning@+1{{possibly initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + .buf = ptr + } + }; +} + +void unknown_count_and_single_ptr_src(struct cbon_with_other_data* s, char* ptr, struct NestedCBON* ncbon) { + int count = get_count(); + *s = (struct cbon_with_other_data){ + count, // expected-note{{count initialized here}} + // expected-warning@+1{{count value is not statically known: initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') with 'char *__single' is invalid for any count other than 0 or 1}} + ptr, + 0x0 + }; + + *ncbon = (struct NestedCBON) { + .inner = (struct cbon_with_other_data) { + // expected-warning@+1{{count value is not statically known: initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') with 'char *__single' is invalid for any count other than 0 or 1}} + .buf = ptr, + .count = count // expected-note{{count initialized here}} + }, + // expected-warning@+1{{count value is not statically known: initializing 'char *__single __counted_by_or_null(count)' (aka 'char *__single') with 'char *__single' is invalid for any count other than 0 or 1}} + .buf = ptr, + .count = count // expected-note{{count initialized here}} + }; +} + +void var_init_from_compound_literal_with_side_effect(char*__bidi_indexable ptr) { + // FIXME: This diagnostic is misleading. The actually restriction is the + // compound literal can't contain side-effects but the diagnostic talks about + // "non-constant array". If the side-effect (call to `get_count()`) is removed + // then this error goes away even though the compound literal is still + // non-constant due to initializing from `ptr`. + // both-error@+1{{cannot initialize array of type 'struct cbon_with_other_data[]' with non-constant array of type 'struct cbon_with_other_data[2]'}} + struct cbon_with_other_data arr[] = (struct cbon_with_other_data[]){ + {.buf = ptr, .count = 0x0, .other = 0x0}, + // expected-warning@+1{{initializer 'get_count()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {.buf = ptr, .count = 0x0, .other = get_count()}, + }; +} + +void call_null_ptr_cbon(int new_count) { + consume_cbon_with_other_data((struct cbon_with_other_data) { + .buf = (char*)0, + .count = new_count + }); + consume_cbon_with_other_data((struct cbon_with_other_data) { + .buf = (void*)0, + .count = new_count + }); + consume_cbon_with_other_data((struct cbon_with_other_data) { + .buf = ((char*)(void*)(char*)0), + .count = new_count + }); +} diff --git a/clang/test/BoundsSafety/Sema/compound-literal-ended_by.c b/clang/test/BoundsSafety/Sema/compound-literal-ended_by.c new file mode 100644 index 0000000000000..ab95be87121db --- /dev/null +++ b/clang/test/BoundsSafety/Sema/compound-literal-ended_by.c @@ -0,0 +1,324 @@ + +// TODO: We should get the same diagnostics with/without compound_literal_init (rdar://138982703) +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +#include + +int side_effect(void); +int get_count(void); +char* get_single_ptr(); +struct eb_with_other_data { + char* __ended_by(end) start; + char* end; + int other; +}; +void consume_eb_with_other_data(struct eb_with_other_data); + +struct NestedEB { + struct eb_with_other_data inner; + char* end; + char* __ended_by(end) start; + int other; +}; + +struct no_attr_with_other_data { + char* end; + char* start; + int other; +}; +_Static_assert(sizeof(struct eb_with_other_data) == sizeof(struct no_attr_with_other_data), "size mismatch"); + +union TransparentUnion { + struct eb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + + +void no_diag(void) { + // No diagnostics + consume_eb_with_other_data((struct eb_with_other_data){ + 0x0, + 0x0, + 0x0 + }); +} + +struct eb_with_other_data global_no_diags = (struct eb_with_other_data) { + // FIXME: This should be allowed. rdar://81135826 + 0x0, // both-error{{initializer element is not a compile-time constant}} + 0x0, + 0x0 +}; + +struct eb_with_other_data field_initializers_with_side_effects(struct eb_with_other_data* s) { + *s = (struct eb_with_other_data){ + 0x0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + struct eb_with_other_data s2 = (struct eb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + consume_eb_with_other_data((struct eb_with_other_data){ + 0x0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect()} + ); + consume_eb_with_other_data((struct eb_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .start = 0x0, + .end = 0x0} + ); + + (void) (struct eb_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .start = 0x0, + .end = 0x0}; + + // no diags for structs without attributes + struct no_attrs { + char* start; + char* end; + int other; + }; + (void) (struct no_attrs) { 0x0, 0x0, side_effect()}; + + // Nested + struct Contains_eb_with_other_data { + struct eb_with_other_data s; + int other; + }; + (void)(struct Contains_eb_with_other_data) { + .s = { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .start = 0x0, + .end = 0x0 + }, + .other = 0x0 + }; + + // Nested CompoundLiteralExpr + (void)(struct NestedEB) { + // expected-warning@+1{{initializer '(struct eb_with_other_data){.start = 0, .end = 0, .other = side_effect()}' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .inner = (struct eb_with_other_data) { + .start = 0x0, + .end = 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminat}} + .other = side_effect() + }, + .start = 0x0, + .end = 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect() + }; + + // Test array initializer list that initializes structs + (void)(struct eb_with_other_data[]){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {0x0, 0x0, side_effect()}, + {0x0, 0x0, 0x0} + }; + + union UnionWith_eb_with_other_data { + struct eb_with_other_data u; + int other; + }; + + (void)(union UnionWith_eb_with_other_data) { + { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .start = 0x0, + .end = 0x0 + } + }; + + // Call a function that takes a transparent union + receive_transparent_union( + // Test very "untransparent" + (union TransparentUnion) {.cb = + { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .start = 0x0, + .end = 0x0 + } + } + ); + // Call using + receive_transparent_union( + // Transparent + (struct eb_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .start = 0x0, + .end = 0x0 + } + ); + + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + return (struct eb_with_other_data) { 0x0, 0x0, side_effect()}; +}; + +struct eb_with_other_data global_eb_with_other_data_side_effect_init = + (struct eb_with_other_data) { + // FIXME: Location of first non-constant is wrong (rdar://81135826) + .start = 0x0, // both-error{{initializer element is not a compile-time constant}} + .end = 0x0, + .other = side_effect() +}; + +struct eb_with_other_data side_effects_in_ptrs(struct eb_with_other_data* s) { + *s = (struct eb_with_other_data){ + get_single_ptr(), // expected-error{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + get_single_ptr(), // expected-error{{initalizer for end pointer with side effects is not yet supported}} + 0x0 + }; + + consume_eb_with_other_data( + (struct eb_with_other_data){ + get_single_ptr(), // expected-error{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + get_single_ptr(), // expected-error{{initalizer for end pointer with side effects is not yet supported}} + 0x0 + } + ); + + (void)(struct eb_with_other_data){ + get_single_ptr(), // expected-error{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + get_single_ptr(), // expected-error{{initalizer for end pointer with side effects is not yet supported}} + 0x0 + }; + + (void)(struct NestedEB) { + .inner = (struct eb_with_other_data) { + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + .start = get_single_ptr(), + // expected-error@+1{{initalizer for end pointer with side effects is not yet supported}} + .end = get_single_ptr(), + .other = 0 + }, + // expected-error@+1{{initalizer for end pointer with side effects is not yet supported}} + .end = get_single_ptr(), + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + .start = get_single_ptr(), + .other = 0 + }; + + return (struct eb_with_other_data){ + get_single_ptr(), // expected-error{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + get_single_ptr(), // expected-error{{initalizer for end pointer with side effects is not yet supported}} + 0x0 + }; +} + +// To keep this test case small just test the `*s = CompoundLiteralExpr` variant +// in the remaining test cases. + +void partial_init_start(struct eb_with_other_data* s, char* start, struct NestedEB* neb) { + // expected-warning@+1{{implicitly initializing field 'end' of type 'char *__single /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + *s = (struct eb_with_other_data){ .start = start }; + + *neb = (struct NestedEB) { + .inner = (struct eb_with_other_data) { + .start = start + },// expected-warning{{implicitly initializing field 'end' of type 'char *__single /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + .start = start + }; // expected-warning{{implicitly initializing field 'end' of type 'char *__single /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} +} + + +// Partial init diagnostic isn't emitted because `global_start_ptr` isn't constant +char* global_start_ptr; +struct eb_with_other_data global_eb_partial_init = + (struct eb_with_other_data) { + .other = 0x0, + .start = global_start_ptr // both-error{{initializer element is not a compile-time constant}} +}; +struct eb_with_other_data global_eb_partial_init2 = + (struct eb_with_other_data) { + .other = 0x0, + // FIXME: This should be treated as a constant (rdar://81135826) + .start = __unsafe_forge_bidi_indexable(char*, 0x1, 4) // both-error{{initializer element is not a compile-time constant}} +}; + + +void end_is_null(struct eb_with_other_data* s, char* start, struct NestedEB* neb) { + // expected-warning@+1{{initializing field 'end' of type 'char *__single /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + *s = (struct eb_with_other_data){ .start = start, .end = 0x0 }; + + *neb = (struct NestedEB) { + .inner = (struct eb_with_other_data) { + .start = start, + // expected-warning@+1{{nitializing field 'end' of type 'char *__single /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + .end = 0x0 + }, + .start = start, + // expected-warning@+1{{nitializing field 'end' of type 'char *__single /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + .end = 0x0 + }; +} + +void partial_init_ended(struct eb_with_other_data* s, char* end, struct NestedEB* neb) { + // expected-warning@+1{{implicitly initializing field 'start' of type 'char *__single __ended_by(end)' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + *s = (struct eb_with_other_data){ .end = end }; + + + *neb = (struct NestedEB) { + .inner = (struct eb_with_other_data) { + .end = end + }, // expected-warning{{implicitly initializing field 'start' of type 'char *__single __ended_by(end)' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + .end = end + }; // expected-warning{{implicitly initializing field 'start' of type 'char *__single __ended_by(end)' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} +} + +void start_is_null(struct eb_with_other_data* s, char* end) { + // expected-warning@+1{{initializing field 'start' of type 'char *__single __ended_by(end)' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + *s = (struct eb_with_other_data){ .start = 0x0, .end = end}; +} + + +void partial_init_other(struct eb_with_other_data* s, struct NestedEB* neb) { + // OK + *s = (struct eb_with_other_data){ .other = 0x0 }; + *neb = (struct NestedEB) { + .inner = (struct eb_with_other_data) {.other = 0x0}, + .other = 0x0 + }; +} + +void implicit_init_all(struct eb_with_other_data* s, struct NestedEB* neb) { + // OK + *s = (struct eb_with_other_data){0}; + *neb = (struct NestedEB) {0}; + *neb = (struct NestedEB) { + .inner = (struct eb_with_other_data) {0} + }; +} + +void var_init_from_compound_literal_with_side_effect(char*__bidi_indexable ptr) { + // FIXME: This diagnostic is misleading. The actually restriction is the + // compound literal can't contain side-effects but the diagnostic talks about + // "non-constant array". If the side-effect (call to `get_count()`) is removed + // then this error goes away even though the compound literal is still + // non-constant due to initializing from `ptr`. + // both-error@+1{{cannot initialize array of type 'struct eb_with_other_data[]' with non-constant array of type 'struct eb_with_other_data[2]'}} + struct eb_with_other_data arr[] = (struct eb_with_other_data[]){ + {.start = ptr, .end = ptr + 1, .other = 0x0}, + // expected-warning@+1{{initializer 'get_count()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {.start = ptr, .end = ptr + 1, .other = get_count()}, + }; +} diff --git a/clang/test/BoundsSafety/Sema/compound-literal-sized_by.c b/clang/test/BoundsSafety/Sema/compound-literal-sized_by.c new file mode 100644 index 0000000000000..545114e587391 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/compound-literal-sized_by.c @@ -0,0 +1,432 @@ + +// TODO: We should get the same diagnostics with/without compound_literal_init (rdar://138982703) +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +#include + +int side_effect(void); +int get_count(void); +char* get_single_ptr(); +struct sb_with_other_data { + int count; + char* __sized_by(count) buf; + int other; +}; +void consume_sb_with_other_data(struct sb_with_other_data); + +struct NestedSB { + struct sb_with_other_data inner; + int count; + char* __sized_by(count) buf; + int other; +}; + +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sb_with_other_data) == sizeof(struct no_attr_with_other_data), "size mismatch"); + +union TransparentUnion { + struct sb_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + + +void no_diag(void) { + // No diagnostics + consume_sb_with_other_data((struct sb_with_other_data){ + 0x0, + 0x0, + 0x0 + }); +} + +struct sb_with_other_data global_no_diags = (struct sb_with_other_data) { + 0x0, + 0x0, + 0x0 +}; + + +struct sb_with_other_data field_initializers_with_side_effects(struct sb_with_other_data* s) { + *s = (struct sb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + struct sb_with_other_data s2 = (struct sb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + consume_sb_with_other_data((struct sb_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect()} + ); + consume_sb_with_other_data((struct sb_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0} + ); + + (void) (struct sb_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0}; + + // no diags for structs without attributes + struct no_attrs { + int count; + char* buf; + }; + (void) (struct no_attrs) { side_effect(), 0x0}; + + // Nested + struct Contains_sb_with_other_data { + struct sb_with_other_data s; + int other; + }; + (void)(struct Contains_sb_with_other_data) { + .s = { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + }, + .other = 0x0 + }; + + // Nested CompoundLiteralExpr + (void)(struct NestedSB) { + // expected-warning@+1{{initializer '(struct sb_with_other_data){.count = 0, .buf = 0, .other = side_effect()}' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .inner = (struct sb_with_other_data) { + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminat}} + .other = side_effect() + }, + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect() + }; + + // Test array initializer list that initializes structs + (void)(struct sb_with_other_data[]){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {0, 0x0, side_effect()}, + {0, 0x0, 0x0} + }; + + union UnionWith_sb_with_other_data { + struct sb_with_other_data u; + int other; + }; + + (void)(union UnionWith_sb_with_other_data) { + { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + }; + + // Call a function that takes a transparent union + receive_transparent_union( + // Test very "untransparent" + (union TransparentUnion) {.cb = + { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + } + ); + // Call using + receive_transparent_union( + // Transparent + (struct sb_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + ); + + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + return (struct sb_with_other_data) { 0, 0x0, side_effect()}; +} + +struct sb_with_other_data global_sb_with_other_data_side_effect_init = + (struct sb_with_other_data) { + .buf = 0x0, + .count = 0, + .other = side_effect() // both-error{{initializer element is not a compile-time constant}} +}; + + +struct sb_with_other_data side_effects_in_ptr(struct sb_with_other_data* s) { + *s = (struct sb_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + consume_sb_with_other_data((struct sb_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0} + ); + + (void) (struct sb_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + (void)(struct NestedSB) { + .inner = (struct sb_with_other_data) { + .count = 0, + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }, + .count = 0, + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }; + + return (struct sb_with_other_data ){ + 0, + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; +} + +struct sb_with_other_data side_effects_in_count(struct sb_with_other_data* s) { + *s = (struct sb_with_other_data){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + consume_sb_with_other_data((struct sb_with_other_data){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0} + ); + + (void) (struct sb_with_other_data){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + (void)(struct NestedSB) { + .inner = (struct sb_with_other_data) { + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }, + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }; + + return (struct sb_with_other_data ){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; +} + + +// To keep this test case small just test the `*s = CompoundLiteralExpr` variant +// in the remaining test cases. + +void constant_neg_count(struct sb_with_other_data* s) { + *s = (struct sb_with_other_data){ + -1, + // expected-error@+1{{negative size value of -1 for 'char *__single __sized_by(count)' (aka 'char *__single')}} + 0x0, + 0x0 + }; +} + +struct sb_with_other_data global_sb_with_other_data_constant_neg_count = + (struct sb_with_other_data) { + // expected-error@+1{{negative size value of -1 for 'global_sb_with_other_data_constant_neg_count.buf' of type 'char *__single __sized_by(count)' (aka 'char *__single')}} + .buf = 0x0, + .count = -1, + .other = 0x0 +}; + +struct NestedSB global_nested_sb_constant_neg_count = (struct NestedSB) { + .inner = (struct sb_with_other_data) { + .count = -1, + // expected-error@+1{{negative size value of -1 for 'global_nested_sb_constant_neg_count.inner.buf' of type 'char *__single __sized_by(count)' (aka 'char *__single')}} + .buf = 0x0, + .other = 0x0 + }, + .count = -1, + // expected-error@+1{{negative size value of -1 for 'global_nested_sb_constant_neg_count.buf' of type 'char *__single __sized_by(count)' (aka 'char *__single')}} + .buf = 0x0, + .other = 0 +}; + + +void constant_pos_count_nullptr(struct sb_with_other_data* s) { + *s = (struct sb_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __sized_by(count)' (aka 'char *__single') and size value of 100 with null always fails}} + 0x0, + 0x0 + }; +} + + +struct sb_with_other_data global_sb_with_other_data_constant_pos_count_nullptr = + (struct sb_with_other_data) { + // expected-error@+1{{initializing 'global_sb_with_other_data_constant_pos_count_nullptr.buf' of type 'char *__single __sized_by(count)' (aka 'char *__single') and size value of 100 with null always fails}} + .buf = 0x0, + .count = 100, + .other = 0x0 +}; + +struct NestedSB global_nested_sb_constant_pos_count_nullptr = (struct NestedSB) { + .inner = (struct sb_with_other_data) { + .count = 100, + // expected-error@+1{{initializing 'global_nested_sb_constant_pos_count_nullptr.inner.buf' of type 'char *__single __sized_by(count)' (aka 'char *__single') and size value of 100 with null always fails}} + .buf = 0x0, + .other = 0x0 + }, + .count = 100, + // expected-error@+1{{initializing 'global_nested_sb_constant_pos_count_nullptr.buf' of type 'char *__single __sized_by(count)' (aka 'char *__single') and size value of 100 with null always fails}} + .buf = 0x0, + .other = 0 +}; + + +void bad_arr_size(struct sb_with_other_data* s) { + char arr[3]; // expected-note{{'arr' declared here}} + *s = (struct sb_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __sized_by(count)' (aka 'char *__single') and size value of 100 with array 'arr' (which has 3 bytes) always fails}} + arr, + 0x0 + }; +} + +// Can't refer to non constant expressions at file scope so the bad `count` +// diagnostic doesn't get emitted. +char global_arr[3] = {0}; +struct sb_with_other_data global_sb_with_other_data_bad_arr_size = + (struct sb_with_other_data) { + .buf = global_arr, // both-error{{initializer element is not a compile-time constant}} + .count = 100, + .other = 0x0 +}; + +void bad_count_and_single_ptr_src(struct sb_with_other_data* s, char* ptr) { // expected-note{{consider adding '__sized_by(100)' to 'ptr'}} + *s = (struct sb_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __sized_by(count)' (aka 'char *__single') and size value of 100 with 'char *__single' and pointee of size 1 always fails}} + ptr, + 0x0 + }; +} + + +void bad_implicit_zero_count(struct sb_with_other_data* s, char*__bidi_indexable ptr, struct NestedSB* nsb) { + *s = (struct sb_with_other_data){ + // expected-warning@+1{{'char *__single __sized_by(count)' (aka 'char *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + .buf = ptr + }; + + *nsb = (struct NestedSB) { + // expected-warning@+1{{possibly initializing 'char *__single __sized_by(count)' (aka 'char *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + .buf = ptr, + .inner = (struct sb_with_other_data) { + // expected-warning@+1{{possibly initializing 'char *__single __sized_by(count)' (aka 'char *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + .buf = ptr + } + }; +} + + +void unknown_count_and_single_ptr_src(struct sb_with_other_data* s, char* ptr, struct NestedSB* nsb) { + int count = get_count(); + *s = (struct sb_with_other_data){ + count, // expected-note{{size initialized here}} + // expected-warning@+1{{size value is not statically known: initializing 'char *__single __sized_by(count)' (aka 'char *__single') with 'char *__single' is invalid for any size greater than 1}} + ptr, + 0x0 + }; + + *nsb = (struct NestedSB) { + .inner = (struct sb_with_other_data) { + // expected-warning@+1{{size value is not statically known: initializing 'char *__single __sized_by(count)' (aka 'char *__single') with 'char *__single' is invalid for any size greater than 1}} + .buf = ptr, + .count = count // expected-note{{size initialized here}} + }, + // expected-warning@+1{{size value is not statically known: initializing 'char *__single __sized_by(count)' (aka 'char *__single') with 'char *__single' is invalid for any size greater than 1}} + .buf = ptr, + .count = count // expected-note{{size initialized here}} + }; +} + +void var_init_from_compound_literal_with_side_effect(char*__bidi_indexable ptr) { + // FIXME: This diagnostic is misleading. The actually restriction is the + // compound literal can't contain side-effects but the diagnostic talks about + // "non-constant array". If the side-effect (call to `get_count()`) is removed + // then this error goes away even though the compound literal is still + // non-constant due to initializing from `ptr`. + // both-error@+1{{cannot initialize array of type 'struct sb_with_other_data[]' with non-constant array of type 'struct sb_with_other_data[2]'}} + struct sb_with_other_data arr[] = (struct sb_with_other_data[]){ + {.buf = ptr, .count = 0x0, .other = 0x0}, + // expected-warning@+1{{initializer 'get_count()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {.buf = ptr, .count = 0x0, .other = get_count()}, + }; +} + +void call_null_ptr_sb(int new_count) { + consume_sb_with_other_data((struct sb_with_other_data) { + .buf = (char*)0, + .count = new_count + }); + consume_sb_with_other_data((struct sb_with_other_data) { + .buf = (void*)0, + .count = new_count + }); + consume_sb_with_other_data((struct sb_with_other_data) { + .buf = ((char*)(void*)(char*)0), + .count = new_count + }); +} diff --git a/clang/test/BoundsSafety/Sema/compound-literal-sized_by_or_null.c b/clang/test/BoundsSafety/Sema/compound-literal-sized_by_or_null.c new file mode 100644 index 0000000000000..fec49814d610c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/compound-literal-sized_by_or_null.c @@ -0,0 +1,425 @@ + +// TODO: We should get the same diagnostics with/without compound_literal_init (rdar://138982703) +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,both -fbounds-safety-bringup-missing-checks=compound_literal_init %s +#include + +int side_effect(void); +int get_count(void); +char* get_single_ptr(); +struct sbon_with_other_data { + int count; + char* __sized_by_or_null(count) buf; + int other; +}; +void consume_sbon_with_other_data(struct sbon_with_other_data); + +struct NestedSBON { + struct sbon_with_other_data inner; + int count; + char* __sized_by_or_null(count) buf; + int other; +}; + +struct no_attr_with_other_data { + int count; + char* buf; + int other; +}; +_Static_assert(sizeof(struct sbon_with_other_data) == sizeof(struct no_attr_with_other_data), "size mismatch"); + +union TransparentUnion { + struct sbon_with_other_data cb; + struct no_attr_with_other_data no_cb; +} __attribute__((__transparent_union__)); + +void receive_transparent_union(union TransparentUnion); + + +void no_diag(void) { + // No diagnostics + consume_sbon_with_other_data((struct sbon_with_other_data){ + 0x0, + 0x0, + 0x0 + }); +} + +struct sbon_with_other_data global_no_diags = (struct sbon_with_other_data) { + 0x0, + 0x0, + 0x0 +}; + +struct sbon_with_other_data field_initializers_with_side_effects(struct sbon_with_other_data* s) { + *s = (struct sbon_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + struct sbon_with_other_data s2 = (struct sbon_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect() + }; + + consume_sbon_with_other_data((struct sbon_with_other_data){ + 0, + 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + side_effect()} + ); + consume_sbon_with_other_data((struct sbon_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0} + ); + + (void) (struct sbon_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0}; + + // no diags for structs without attributes + struct no_attrs { + int count; + char* buf; + }; + (void) (struct no_attrs) { side_effect(), 0x0}; + + // Nested + struct Contains_sbon_with_other_data { + struct sbon_with_other_data s; + int other; + }; + (void)(struct Contains_sbon_with_other_data) { + .s = { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + }, + .other = 0x0 + }; + + // Nested CompoundLiteralExpr + (void)(struct NestedSBON) { + // expected-warning@+1{{initializer '(struct sbon_with_other_data){.count = 0, .buf = 0, .other = side_effect()}' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .inner = (struct sbon_with_other_data) { + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminat}} + .other = side_effect() + }, + .count = 0, + .buf = 0x0, + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect() + }; + + // Test array initializer list that initializes structs + (void)(struct sbon_with_other_data[]){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {0, 0x0, side_effect()}, + {0, 0x0, 0x0} + }; + + union UnionWith_sbon_with_other_data { + struct sbon_with_other_data u; + int other; + }; + + (void)(union UnionWith_sbon_with_other_data) { + { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + }; + + // Call a function that takes a transparent union + receive_transparent_union( + // Test very "untransparent" + (union TransparentUnion) {.cb = + { + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + } + ); + // Call using + receive_transparent_union( + // Transparent + (struct sbon_with_other_data){ + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + .other = side_effect(), + .buf = 0x0, + .count = 0 + } + ); + + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + return (struct sbon_with_other_data) { 0, 0x0, side_effect()}; +} + +struct sbon_with_other_data global_sbon_with_other_data_side_effect_init = + (struct sbon_with_other_data) { + .buf = 0x0, + .count = 0, + .other = side_effect() // both-error{{initializer element is not a compile-time constant}} +}; + + +struct sbon_with_other_data side_effects_in_ptr(struct sbon_with_other_data* s) { + *s = (struct sbon_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + consume_sbon_with_other_data((struct sbon_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0} + ); + + (void) (struct sbon_with_other_data){ + 0, + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; + + (void)(struct NestedSBON) { + .inner = (struct sbon_with_other_data) { + .count = 0, + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }, + .count = 0, + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + .buf = get_single_ptr(), + .other = 0 + }; + + return (struct sbon_with_other_data ){ + 0, + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + get_single_ptr(), + 0x0 + }; +} + +struct sbon_with_other_data side_effects_in_count(struct sbon_with_other_data* s) { + *s = (struct sbon_with_other_data){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + consume_sbon_with_other_data((struct sbon_with_other_data){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0} + ); + + (void) (struct sbon_with_other_data){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; + + (void)(struct NestedSBON) { + .inner = (struct sbon_with_other_data) { + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }, + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + .count = get_count(), + .buf = 0x0, + .other = 0 + }; + + return (struct sbon_with_other_data ){ + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + get_count(), + 0x0, + 0x0 + }; +} + + +// To keep this test case small just test the `*s = CompoundLiteralExpr` variant +// in the remaining test cases. + +void constant_neg_count(struct sbon_with_other_data* s) { + *s = (struct sbon_with_other_data){ + -1, + // ok + 0x0, + 0x0 + }; +} + +struct sbon_with_other_data global_sbon_with_other_data_constant_neg_count = + (struct sbon_with_other_data) { + .buf = 0x0, // ok + .count = -1, + .other = 0x0 +}; + +struct NestedSBON global_nested_sbon_constant_neg_count = (struct NestedSBON) { + .inner = (struct sbon_with_other_data) { + .count = -1, + .buf = 0x0, // OK + .other = 0x0 + }, + .count = -1, + .buf = 0x0, // OK + .other = 0 +}; + + + +void constant_pos_count_nullptr(struct sbon_with_other_data* s) { + *s = (struct sbon_with_other_data){ + 100, + // ok + 0x0, + 0x0 + }; +} + +struct sbon_with_other_data global_sbon_with_other_data_constant_pos_count_nullptr = + (struct sbon_with_other_data) { + .buf = 0x0, // ok + .count = 100, + .other = 0x0 +}; + +struct NestedSBON global_nested_sbon_constant_pos_count_nullptr = (struct NestedSBON) { + .inner = (struct sbon_with_other_data) { + .count = 100, + .buf = 0x0, // OK + .other = 0x0 + }, + .count = 100, + .buf = 0x0, // OK + .other = 0 +}; + + +void bad_arr_size(struct sbon_with_other_data* s) { + char arr[3]; // expected-note{{'arr' declared here}} + *s = (struct sbon_with_other_data){ + 100, + // expected-error@+1{{initializing 'char *__single __sized_by_or_null(count)' (aka 'char *__single') and size value of 100 with array 'arr' (which has 3 bytes) always fails}} + arr, + 0x0 + }; +} + +// Can't refer to non constant expressions at file scope so the bad `count` +// diagnostic doesn't get emitted. +char global_arr[3] = {0}; +struct sbon_with_other_data global_sbon_with_other_data_bad_arr_size = + (struct sbon_with_other_data) { + .buf = global_arr, // both-error{{initializer element is not a compile-time constant}} + .count = 100, + .other = 0x0 +}; + + +void bad_count_and_single_ptr_src(struct sbon_with_other_data* s, char* ptr) { + *s = (struct sbon_with_other_data){ + 100, + ptr, + 0x0 + }; +} + + +void bad_implicit_zero_count(struct sbon_with_other_data* s, char*__bidi_indexable ptr, struct NestedSBON* nsbon) { + *s = (struct sbon_with_other_data){ + // expected-warning@+1{{'char *__single __sized_by_or_null(count)' (aka 'char *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + .buf = ptr + }; + + *nsbon = (struct NestedSBON) { + // expected-warning@+1{{possibly initializing 'char *__single __sized_by_or_null(count)' (aka 'char *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + .buf = ptr, + .inner = (struct sbon_with_other_data) { + // expected-warning@+1{{possibly initializing 'char *__single __sized_by_or_null(count)' (aka 'char *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + .buf = ptr + } + }; +} + + +void unknown_count_and_single_ptr_src(struct sbon_with_other_data* s, char* ptr, struct NestedSBON* nsbon) { + int count = get_count(); + *s = (struct sbon_with_other_data){ + count, // expected-note{{size initialized here}} + // expected-warning@+1{{size value is not statically known: initializing 'char *__single __sized_by_or_null(count)' (aka 'char *__single') with 'char *__single' is invalid for any size greater than 1}} + ptr, + 0x0 + }; + + *nsbon = (struct NestedSBON) { + .inner = (struct sbon_with_other_data) { + // expected-warning@+1{{size value is not statically known: initializing 'char *__single __sized_by_or_null(count)' (aka 'char *__single') with 'char *__single' is invalid for any size greater than 1 unless the pointer is null}} + .buf = ptr, + .count = count // expected-note{{size initialized here}} + }, + // expected-warning@+1{{size value is not statically known: initializing 'char *__single __sized_by_or_null(count)' (aka 'char *__single') with 'char *__single' is invalid for any size greater than 1 unless the pointer is null}} + .buf = ptr, + .count = count // expected-note{{size initialized here}} + }; +} + +void var_init_from_compound_literal_with_side_effect(char*__bidi_indexable ptr) { + // FIXME: This diagnostic is misleading. The actually restriction is the + // compound literal can't contain side-effects but the diagnostic talks about + // "non-constant array". If the side-effect (call to `get_count()`) is removed + // then this error goes away even though the compound literal is still + // non-constant due to initializing from `ptr`. + // both-error@+1{{cannot initialize array of type 'struct sbon_with_other_data[]' with non-constant array of type 'struct sbon_with_other_data[2]'}} + struct sbon_with_other_data arr[] = (struct sbon_with_other_data[]){ + {.buf = ptr, .count = 0x0, .other = 0x0}, + // expected-warning@+1{{initializer 'get_count()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + {.buf = ptr, .count = 0x0, .other = get_count()}, + }; +} + +void call_null_ptr_sbon(int new_count) { + consume_sbon_with_other_data((struct sbon_with_other_data) { + .buf = (char*)0, + .count = new_count + }); + consume_sbon_with_other_data((struct sbon_with_other_data) { + .buf = (void*)0, + .count = new_count + }); + consume_sbon_with_other_data((struct sbon_with_other_data) { + .buf = ((char*)(void*)(char*)0), + .count = new_count + }); +} diff --git a/clang/test/BoundsSafety/Sema/conflicting-group-assign-in-basic-block.c b/clang/test/BoundsSafety/Sema/conflicting-group-assign-in-basic-block.c new file mode 100644 index 0000000000000..a6e1ea33769f2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/conflicting-group-assign-in-basic-block.c @@ -0,0 +1,70 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct CountedByData { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +void TestCountedBy(struct CountedByData *d) { + int arr[10]; + d->bp = &arr[1]; // expected-note{{previously assigned here}} + d->bp2 = arr; // expected-note{{previously assigned here}} + d->l = 9; // expected-note{{previously assigned here}} + + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'bp' must be simplified; keep only one of the assignments}} + d->bp = d->bp; + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'bp2' must be simplified; keep only one of the assignments}} + d->bp2 = &arr[0]; + // expected-error@+1{{multiple consecutive assignments to a dynamic count 'l' must be simplified; keep only one of the assignments}} + d->l = d->l; +} + +void AnyCall(void); + +void TestCountedByCallInBetween(struct CountedByData *d) { + int arr[10]; + d->bp = &arr[1]; + d->bp2 = arr; + d->l = 9; + + AnyCall(); + + d->bp = d->bp; + d->bp2 = &arr[0]; + d->l = d->l; +} + +void TestCountedByMultipleInstances(struct CountedByData *d1, struct CountedByData *d2) { + int arr[10]; + d1->bp = &arr[1]; + d1->bp2 = arr; + d1->l = 9; + + d2->bp = d1->bp; + d2->bp2 = &arr[0]; + d2->l = d1->l; +} + +struct EndedByData { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + int *end; +}; + +void TestEndedBy(struct EndedByData *d) { + int arr[10]; + d->start = arr + 1; // expected-note{{previously assigned here}} + // expected-error@+1{{multiple consecutive assignments to a ranged pointer 'start' must be simplified; keep only one of the assignments}} + d->start = arr; + d->iter = arr; // expected-note{{previously assigned here}} + d->end = arr + 10; // expected-note{{previously assigned here}} + // expected-error@+1{{multiple consecutive assignments to a ranged pointer 'end' must be simplified; keep only one of the assignments}} + d->end = &arr[0] + 5; + // expected-error@+1{{multiple consecutive assignments to a ranged pointer 'iter' must be simplified; keep only one of the assignments}} + d->iter = &arr[0] + 6; +} diff --git a/clang/test/BoundsSafety/Sema/constant-eval-cast-flexible-array-member.c b/clang/test/BoundsSafety/Sema/constant-eval-cast-flexible-array-member.c new file mode 100644 index 0000000000000..7b53767714694 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/constant-eval-cast-flexible-array-member.c @@ -0,0 +1,19 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int len; + unsigned fam[__counted_by(len)]; +}; + +struct S *g_s = (struct S *)(unsigned char[1]){}; // expected-error{{initializer element is not a compile-time constant}} + +int f_s(struct S *); + +void foo(void) { + int result = f_s((struct S *)(unsigned char[1]){}); // ok +} diff --git a/clang/test/BoundsSafety/Sema/constant-eval-cast-single.c b/clang/test/BoundsSafety/Sema/constant-eval-cast-single.c new file mode 100644 index 0000000000000..370d4b7084e72 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/constant-eval-cast-single.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s + +#include +#include + +int32_t *__single p = (int32_t *__single)(int64_t *__single)0; + +// expected-error@+1{{initializer element is not a compile-time constant}} +int32_t *__single q = (int32_t *__single)(int16_t *__single)0; diff --git a/clang/test/BoundsSafety/Sema/constant-eval-count-pointer.c b/clang/test/BoundsSafety/Sema/constant-eval-count-pointer.c new file mode 100644 index 0000000000000..ad1e91c8eba98 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/constant-eval-count-pointer.c @@ -0,0 +1,13 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental %s + +#include + +void foo(long len, int *buf __counted_by(len)) { +// This is a reduced test that exercises BoundsSafetyPointerCast +// in ExprConstant's pointer constant evaluation when source +// pointer type is counted, invoked from pointer arithmetic +// overflow check. + buf + 0; +} diff --git a/clang/test/BoundsSafety/Sema/constant-eval-count-static-init-over.c b/clang/test/BoundsSafety/Sema/constant-eval-count-static-init-over.c new file mode 100644 index 0000000000000..0625178e8e150 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/constant-eval-count-static-init-over.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +typedef struct { + unsigned int *__counted_by(length) data; + int length; +} Item; + +// expected-note@+1{{'_oidRsa' declared here}} +static unsigned int _oidRsa[] = { 0, 1, 2 }; +// expected-error@+1{{initializing 'oidRsa.data' of type 'unsigned int *__single __counted_by(length)' (aka 'unsigned int *__single') and count value of 4 with array '_oidRsa' (which has 3 elements) always fails}} +const Item oidRsa = { _oidRsa, sizeof(_oidRsa)/sizeof(int) + 1 }; + +int main() { + return oidRsa.data[3]; +} diff --git a/clang/test/BoundsSafety/Sema/constant-forge-ptr-expr.c b/clang/test/BoundsSafety/Sema/constant-forge-ptr-expr.c new file mode 100644 index 0000000000000..4be0c0d0078e8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/constant-forge-ptr-expr.c @@ -0,0 +1,72 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int arr[4]; +int len = 4; +int *__bidi_indexable ptrBidi = &((int*)__unsafe_forge_bidi_indexable(int *, 0, 16))[10]; +int *__bidi_indexable ptrBidi2 = __unsafe_forge_bidi_indexable(int *, arr + 2, 2); +int *__bidi_indexable ptrBidi3 = __unsafe_forge_bidi_indexable(int *, 8000, len); // expected-error{{initializer element is not a compile-time constant}} +int *__indexable ptrArr = &((int*)__unsafe_forge_bidi_indexable(int *, 0, 10))[-1]; // expected-error{{initializer element is not a compile-time constant}} +int *__indexable ptrArr2 = &((int*)__unsafe_forge_bidi_indexable(int *, 0, 10))[1]; +int *__indexable ptrArr3 = &((int*)__unsafe_forge_bidi_indexable(int *, 0, 10))[2]; // expected-error{{initializer element is not a compile-time constant}} +// XXX: can't work until __unsafe_forge_single has a size +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wbounds-attributes-implicit-conversion-single-to-explicit-indexable" +int *__indexable ptrArr4 = __unsafe_forge_single(int *, 10); // expected-error{{initializer element is not a compile-time constant}} +#pragma clang diagnostic pop +int *__single ptrSingle = __unsafe_forge_bidi_indexable(int *, arr, 1); // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle2 = __unsafe_forge_bidi_indexable(int *, 0, 0); +int *__single ptrSingle12 = __unsafe_forge_bidi_indexable(int *, 0, 4); +int *__single ptrSingle3 = __unsafe_forge_bidi_indexable(int *, 2, 4) + 4; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle4 = __unsafe_forge_bidi_indexable(int *, 2, 5) + 4; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle5 = __unsafe_forge_bidi_indexable(int *, 2, 7) + 4; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle11 = __unsafe_forge_bidi_indexable(int *, 2, 8) + 4; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle6 = __unsafe_forge_single(int *, 0); +int *__single ptrSingle7 = &((int*)__unsafe_forge_bidi_indexable(int *, arr, 16))[1]; +int *__single ptrSingle8 = &((int*)__unsafe_forge_bidi_indexable(int *, arr, 16))[-2]; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle9 = __unsafe_forge_bidi_indexable(int *, &arr[4], 8) + 4; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle10 = __unsafe_forge_bidi_indexable(int *, &arr[4], 8) + 8; // expected-error{{initializer element is not a compile-time constant}} +int *__single ptrSingle13 = __unsafe_forge_single(int *, arr); +int *__terminated_by(5) ptrTerminated = __unsafe_forge_terminated_by(int *, arr, 5); +int *__terminated_by(5) ptrTerminated2 = __unsafe_forge_terminated_by(int *, arr, 6); // expected-error{{pointers with incompatible terminators initializing 'int *__single __terminated_by(5)' (aka 'int *__single') with an expression of incompatible type 'int *__single __terminated_by(6)' (aka 'int *__single')}} +int *__terminated_by(5) ptrTerminated3 = __unsafe_forge_terminated_by(int *, 7, 5); +// The attribute on cast is missing macro identifer info. +// expected-error@+2{{'__terminated_by__' attribute requires an integer constant}} +// expected-error@+1{{initializing 'int *__single __terminated_by(4)' (aka 'int *__single') with an expression of incompatible type 'int *__single' is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} +int *__terminated_by(4) ptrTerminated4 = __unsafe_forge_terminated_by(int *, arr, len); +int *__null_terminated ptrNullTerminated = __unsafe_forge_null_terminated(int *, arr); +// expected-error@+2{{'__terminated_by__' attribute requires an integer constant}} +// expected-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} +int *__terminated_by(0) ptrTerminated5 = __unsafe_forge_terminated_by(int *, 0, arr); +int *__terminated_by(0) ptrTerminated6 = __unsafe_forge_terminated_by(int *, 0, 0); +// expected-error@+3{{initializer element is not a compile-time constant}} +// expected-error@+2{{'__terminated_by__' attribute requires an integer constant}} +// expected-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} +int *__terminated_by(0) ptrTerminated7 = __unsafe_forge_terminated_by(int *, 1, arr); + +// rdar://84175702 +char *c = __unsafe_forge_bidi_indexable(char *, "a", 3); // expected-error{{initializer element is not a compile-time constant}} + +// expected-error@+3{{initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} +// expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} +// expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} +char *__null_terminated ptrNt1 = __unsafe_forge_bidi_indexable(char *, arr, 10); + +// expected-error@+3{{initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} +// expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} +// expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} +char *__null_terminated ptrNt2 = __unsafe_forge_bidi_indexable(char *, arr+12, 10); + +// expected-error@+2{{initializer element is not a compile-time constant}} +// expected-error@+1{{initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} +char *__null_terminated ptrNt3 = __unsafe_forge_single(char *, arr+12); + +// expected-error@+1{{initializer element is not a compile-time constant}} +char *__null_terminated ptrNt4 = __unsafe_null_terminated_from_indexable(__unsafe_forge_bidi_indexable(char *, arr+12, 10)); + +char *__null_terminated ptrNt5 = __unsafe_forge_null_terminated(char *, __unsafe_forge_bidi_indexable(char *, arr+12, 10)); + +char *__null_terminated ptrNt6 = __unsafe_forge_null_terminated(char *, __unsafe_forge_single(char *, arr+2)); diff --git a/clang/test/BoundsSafety/Sema/count-attr-assigns-nested-struct.c b/clang/test/BoundsSafety/Sema/count-attr-assigns-nested-struct.c new file mode 100644 index 0000000000000..7418f82a6ee34 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-assigns-nested-struct.c @@ -0,0 +1,23 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int d; + struct { + int *__counted_by(l) bp; + int *bp2; + int l; + } nested; +}; + +int main() { + struct S s; + + int arr[10]; + s.nested.bp = arr; // expected-error{{assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 's.nested.bp' requires corresponding assignment to 's.nested.l'; add self assignment 's.nested.l = s.nested.l' if the value has not changed}} + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/count-attr-assigns.c b/clang/test/BoundsSafety/Sema/count-attr-assigns.c new file mode 100644 index 0000000000000..065516c889cb7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-assigns.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2; + int l; +}; + +int main() { + struct S s; + + int arr[10]; + s.bp = arr; // expected-error{{assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 's.bp' requires corresponding assignment to 's.l'; add self assignment 's.l = s.l' if the value has not changed}} + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/count-attr-constant-initializer.c b/clang/test/BoundsSafety/Sema/count-attr-constant-initializer.c new file mode 100644 index 0000000000000..24e9de731206f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-constant-initializer.c @@ -0,0 +1,57 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-note@+1{{'ints' declared here}} +int ints[16]; +// expected-note@+1{{'chars' declared here}} +char chars[16]; + +struct CountInt { + int *__counted_by(len) p; + unsigned len; +}; + +// ok +struct CountInt ci1 = {.p = ints, .len = sizeof(ints) / sizeof(int)}; + +// ok +struct CountInt ci2 = {.p = ints, .len = sizeof(ints) / sizeof(int) - 1}; + +// expected-error@+1{{initializing 'ci3.p' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 17 with array 'ints' (which has 16 elements) always fails}} +struct CountInt ci3 = {.p = ints, .len = sizeof(ints) / sizeof(int) + 1}; + +// expected-error@+2{{initializing 'ci4.p' of type 'int *__single __counted_by(len)' (aka 'int *__single') and size value of 64 with array 'chars' (which has 16 bytes) always fails}} +// expected-warning@+1{{incompatible pointer types initializing 'int *__single __counted_by(len)' (aka 'int *__single') with an expression of type 'char[16]'}} +struct CountInt ci4 = {.p = chars, .len = sizeof(chars)}; + +// expected-warning@+1{{incompatible pointer types initializing 'int *__single __counted_by(len)' (aka 'int *__single') with an expression of type 'char[16]'}} +struct CountInt ci5 = {.p = chars, .len = sizeof(chars) / sizeof(int)}; + +struct CountChar { + char *__counted_by(len) p; + unsigned len; +}; + +// ok +struct CountChar cc1 = {.p = chars, .len = sizeof(chars)}; + +// expected-warning@+1{{incompatible pointer types initializing 'char *__single __counted_by(len)' (aka 'char *__single') with an expression of type 'int[16]'}} +struct CountChar cc2 = {.p = ints, .len = sizeof(ints) / sizeof(int)}; + +// expected-warning@+1{{incompatible pointer types initializing 'char *__single __counted_by(len)' (aka 'char *__single') with an expression of type 'int[16]'}} +struct CountChar cc3 = {.p = ints, .len = sizeof(ints)}; + +struct NestedCount { + char buf[16]; + + struct { + char *__counted_by(len) p; + unsigned len; + } nested; +}; + +// ok +struct NestedCount ni1 = {.nested = {.p = ni1.buf, .len = sizeof(ni1.buf)}}; diff --git a/clang/test/BoundsSafety/Sema/count-attr-fields-assign-addrof-deref.c b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-addrof-deref.c new file mode 100644 index 0000000000000..e40af359c9ff5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-addrof-deref.c @@ -0,0 +1,15 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +int foo(int *__counted_by(*out_len)* out_ptr, int *out_len) { + int arr[10]; + *&*(&*out_ptr) = arr; + *out_len = 10; + if (*out_len == 10) { + *&*out_ptr = &arr[0] + 1; // expected-error{{assignment to 'int *__single __counted_by(*out_len)' (aka 'int *__single') '*out_ptr' requires corresponding assignment to '*out_len'; add self assignment '*out_len = *out_len' if the value has not changed}} + } + return 0; +} + diff --git a/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-one-buf-nested-struct.c b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-one-buf-nested-struct.c new file mode 100644 index 0000000000000..6b71c17b09044 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-one-buf-nested-struct.c @@ -0,0 +1,33 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +struct S { + int haha; + struct { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; + } nested; +}; + +int foo(); + +int main () { + int arr[16] = {0}; + // expected-error@+1{{implicitly initializing 's.nested.bp2' of type 'int *__single __counted_by(l + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct S s; + s.nested.bp = &arr[3]; + s.nested.bp2 = &arr[3]; + s.nested.l = 9; + // run-time check here + + for (int i = 0; i < s.nested.l; ++i) + s.nested.bp[i] = i; + + s.nested.l = 10; // expected-error{{assignment to 's.nested.l' requires corresponding assignment to 'int *__single __counted_by(l + 1)' (aka 'int *__single') 's.nested.bp2'; add self assignment 's.nested.bp2 = s.nested.bp2' if the value has not changed}} + s.nested.bp = s.nested.bp; + + return s.nested.bp[9]; +} diff --git a/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-one-buf.c b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-one-buf.c new file mode 100644 index 0000000000000..45051f59e4f10 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-one-buf.c @@ -0,0 +1,29 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo(); + +int Test(struct S s) { + int arr[16] = {0}; + s.bp = &arr[3]; + s.bp2 = &arr[3]; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + s.l = 10; // expected-error{{assignment to 's.l' requires corresponding assignment to 'int *__single __counted_by(l + 1)' (aka 'int *__single') 's.bp2'; add self assignment 's.bp2 = s.bp2' if the value has not changed}} + s.bp = s.bp; + + return s.bp[9]; +} diff --git a/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-only-nested-struct.c b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-only-nested-struct.c new file mode 100644 index 0000000000000..fdd477b576328 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-only-nested-struct.c @@ -0,0 +1,34 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + struct { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; + } nested; +}; + +int foo(); + +int main () { + int arr[10]; + // expected-error@+1{{implicitly initializing 's.nested.bp2' of type 'int *__single __counted_by(l + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct S s; + s.nested.bp = &arr[1]; + s.nested.bp2 = arr; + s.nested.l = 9; + // run-time check here + + for (int i = 0; i < s.nested.l; ++i) + s.nested.bp[i] = i; + + // expected-error@+2{{assignment to 's.nested.l' requires corresponding assignment to 'int *__single __counted_by(l + 1)' (aka 'int *__single') 's.nested.bp2'; add self assignment 's.nested.bp2 = s.nested.bp2' if the value has not changed}} + // expected-error@+1{{assignment to 's.nested.l' requires corresponding assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 's.nested.bp'; add self assignment 's.nested.bp = s.nested.bp' if the value has not changed}} + s.nested.l = 11; + + return s.nested.bp2[11] + s.nested.bp[10]; +} diff --git a/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-only.c b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-only.c new file mode 100644 index 0000000000000..c23f687b85f5c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-check-len-only.c @@ -0,0 +1,30 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo(); + +int bar (struct S s) { + int arr[10]; + s.bp = &arr[1]; + s.bp2 = arr; + s.l = 9; + // run-time check here + + for (int i = 0; i < s.l; ++i) + s.bp[i] = i; + + // expected-error@+2{{assignment to 's.l' requires corresponding assignment to 'int *__single __counted_by(l + 1)' (aka 'int *__single') 's.bp2'; add self assignment 's.bp2 = s.bp2' if the value has not changed}} + // expected-error@+1{{assignment to 's.l' requires corresponding assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 's.bp'; add self assignment 's.bp = s.bp' if the value has not changed}} + s.l = 11; + + return s.bp2[11] + s.bp[10]; +} diff --git a/clang/test/BoundsSafety/Sema/count-attr-fields-assign-parens.c b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-parens.c new file mode 100644 index 0000000000000..cd2a5c72b3765 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attr-fields-assign-parens.c @@ -0,0 +1,26 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int foo (struct S s) { + int arr[10]; + struct S *sp = &s; + ((struct S*)sp)->bp = &arr[1]; + ((struct S*)sp)->bp2 = arr; + ((struct S*)sp)->l = 9; + + if (s.l == 8) { + // 'bp2' is also associated with 'l' but the compiler complains once at the first assignment of the group + ((struct S*)sp)->bp = &arr[1]; // expected-error{{assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 'sp->bp' requires corresponding assignment to 'sp->l'; add self assignment 'sp->l = sp->l' if the value has not changed}} + ((struct S*)sp)->bp2 = arr; + } + + return s.bp2[8] + s.bp[7]; +} diff --git a/clang/test/BoundsSafety/Sema/count-attrs.c b/clang/test/BoundsSafety/Sema/count-attrs.c new file mode 100644 index 0000000000000..09ee14c1087e8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-attrs.c @@ -0,0 +1,53 @@ + + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +// RUN: %clang_cc1 -fsyntax-only -verify -fexperimental-bounds-safety-attributes -x c %s +// RUN: %clang_cc1 -fsyntax-only -verify -fexperimental-bounds-safety-attributes -x c++ %s +// RUN: %clang_cc1 -fsyntax-only -verify -fexperimental-bounds-safety-attributes -x objective-c %s +// RUN: %clang_cc1 -fsyntax-only -verify -fexperimental-bounds-safety-attributes -x objective-c++ %s + +#include + +void foo(int *__counted_by(len + 1) buf, int len); +// expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} +void bar(int *__counted_by(len) *buf __counted_by(len + 2), int len); +// expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} +void baz(int *__counted_by(len) *__counted_by(len+2) buf, int len); + +void byte_foo(int *__sized_by(len + 1) buf, int len); +// expected-error@+1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} +void byte_bar(int *__sized_by(len) *buf __sized_by(len + 2), int len); +// expected-error@+1{{'__sized_by' attribute on nested pointer type is only allowed on indirect parameters}} +void byte_baz(int *__sized_by(len) *__sized_by(len+2) buf, int len); +void count_vla(int len, int buf[len + 1]); + +void frob(int *__sized_by(len) buf, int len) { + // expected-error-re@+1{{__typeof__ on an expression of type 'int *{{.*}}__sized_by(len)' (aka 'int *{{.*}}') is not yet supported}} + __typeof__(buf) buf2; +} + +void nicate(int *__sized_by(0) buf) { + __typeof__(buf) buf2; // OK +} + +void foo_or_null(int *__counted_by_or_null(len + 1) buf, int len); +// expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +void bar_or_null(int *__counted_by_or_null(len) *buf __counted_by_or_null(len + 2), int len); +// expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +void baz_or_null(int *__counted_by_or_null(len) *__counted_by_or_null(len+2) buf, int len); + +void byte_foo_or_null(int *__sized_by_or_null(len + 1) buf, int len); +// expected-error@+1{{'__sized_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +void byte_bar_or_null(int *__sized_by_or_null(len) *buf __sized_by_or_null(len + 2), int len); +// expected-error@+1{{'__sized_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +void byte_baz_or_null(int *__sized_by_or_null(len) *__sized_by_or_null(len+2) buf, int len); +void count_vla_or_null(int len, int buf[len + 1]); + +void frob_or_null(int *__sized_by_or_null(len) buf, int len) { + // expected-error-re@+1{{__typeof__ on an expression of type 'int *{{.*}}__sized_by_or_null(len)' (aka 'int *{{.*}}') is not yet supported}} + __typeof__(buf) buf2; +} + +void nicate_or_null(int *__sized_by_or_null(0) buf) { + __typeof__(buf) buf2; // OK +} diff --git a/clang/test/BoundsSafety/Sema/count-bound-attrs.c b/clang/test/BoundsSafety/Sema/count-bound-attrs.c new file mode 100644 index 0000000000000..137806490ab7b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-bound-attrs.c @@ -0,0 +1,34 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void Test(void) { + int len; + int *__bidi_indexable __counted_by(len) boundCount; // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} + int len2; + int *__single __counted_by(len2) thinCount; + int len3; + int *__indexable __counted_by(len3) arrayCount; // expected-error{{pointer cannot be '__counted_by' and '__indexable' at the same time}} + int len5; + int *__unsafe_indexable __counted_by(len5) unsafeCount; + int len6; + int *__counted_by(len6) justCount; + int len7; + int *__counted_by(len7) __counted_by(len7+1) thinCountCount; // expected-error{{pointer cannot have more than one count attribute}} + + int len8; + int *__counted_by(len8) __single countThin; + int len9; + int *__counted_by(len9) __bidi_indexable countBound; // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} + int len10; + int *__counted_by(len10) __indexable countArray; // expected-error{{pointer cannot be '__counted_by' and '__indexable' at the same time}} + int len12; + int *__counted_by(len12) __unsafe_indexable countUnsafe; + + int len13; + void *__counted_by(len13) countVoid; // expected-error{{cannot apply '__counted_by' attribute to 'void *' because 'void' has unknown size; did you mean to use '__sized_by' instead?}} + int len14; + void *__sized_by(len14) byteCountVoid; +} diff --git a/clang/test/BoundsSafety/Sema/count-pointer-flexible-mix.c b/clang/test/BoundsSafety/Sema/count-pointer-flexible-mix.c new file mode 100644 index 0000000000000..516aa6e438f4e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/count-pointer-flexible-mix.c @@ -0,0 +1,341 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Mix { + int buf_count; + int *__counted_by(buf_count) buf; + int flex_count; + int fam[__counted_by(flex_count)]; +}; + +void assign_to_buf_count1(struct Mix *p) { + p->buf_count = 0; + p->buf = 0; +} + + +void assign_to_buf_count2(struct Mix *p) { + p->buf_count = 0; // expected-error{{assignment to 'p->buf_count' requires corresponding assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->buf'; add self assignment 'p->buf = p->buf' if the value has not changed}} +} + +void assign_to_flex_count1(struct Mix *p) { + p->flex_count = 0; // expected-error{{assignment to 'p->flex_count' requires an immediately preceding assignment to 'p' with a wide pointer}} +} + +void assign_to_flex_count2(struct Mix *p, void *__bidi_indexable in_p, int in_count) { + p = in_p; + p->flex_count = in_count; +} + +void assign_to_mix1(struct Mix *p, void *__bidi_indexable in_p, int in_count) { + p = in_p; + p->flex_count = in_count; + p->buf_count = 0; + p->buf = 0; +} + +void assign_to_mix2(struct Mix *p, void *__bidi_indexable in_p, int in_count) { + p = in_p; + p->flex_count = in_count; + p->buf_count = 0; // expected-error{{assignment to 'p->buf_count' requires corresponding assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->buf'; add self assignment 'p->buf = p->buf' if the value has not changed}} +} + +void assign_to_mix3(struct Mix *p, void *__bidi_indexable in_p, int in_count) { + p = in_p; + p->buf = 0; // expected-error{{assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->buf' requires corresponding assignment to 'p->buf_count'; add self assignment 'p->buf_count = p->buf_count' if the value has not changed}} + // Below is error because the assign to p is interrupted by p->buf = 0; + p->flex_count = in_count; // expected-error{{assignment to 'p->flex_count' requires an immediately preceding assignment to 'p' with a wide pointer}} +} + +void assign_to_mix4(struct Mix *p, void *__bidi_indexable in_p, int in_count) { + p = in_p; + p->buf_count = 0; // expected-error{{assignment to 'p->buf_count' requires corresponding assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->buf'; add self assignment 'p->buf = p->buf' if the value has not changed}} + // Below is error because the assign to p is interrupted by p->count = 0; + p->flex_count = in_count; // expected-error{{assignment to 'p->flex_count' requires an immediately preceding assignment to 'p' with a wide pointer}} +} + + +struct MixInner { + int buf_count; + int *__counted_by(buf_count) buf; + int flex_count; +}; + +struct MixOuter { + struct MixInner header; + int fam[__counted_by(header.flex_count)]; +}; + + +struct MixOuterSharedCount { + struct MixInner header; + int fam[__counted_by(header.buf_count)]; +}; + +void assign_to_inner_buf_count1(struct MixInner *p) { + p->buf_count = 0; // expected-error{{assignment to 'p->buf_count' requires corresponding assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->buf'; add self assignment 'p->buf = p->buf' if the value has not changed}} +} + +void assign_to_inner_buf_count2(struct MixInner *p) { + p->buf_count = 0; + p->buf = 0; +} + +void assign_to_header_buf_count1(struct MixOuter *p) { + p->header.buf_count = 0; // expected-error{{assignment to 'p->header.buf_count' requires corresponding assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->header.buf'; add self assignment 'p->header.buf = p->header.buf' if the value has not changed}} +} + +void assign_to_header_buf_count2(struct MixOuter *p) { + p->header.buf_count = 0; + p->header.buf = 0; +} + +void assign_to_header_flex_count(struct MixOuter *p) { + p->header.flex_count = 0; // expected-error{{assignment to 'p->header.flex_count' requires an immediately preceding assignment to 'p' with a wide pointer}} +} + +void assign_to_header_shared_count1(struct MixOuterSharedCount *p) { + // expected-error@+1{{assignment to 'p->header.buf_count' requires an immediately preceding assignment to 'p' with a wide pointer}} + p->header.buf_count = 0; // expected-error{{assignment to 'p->header.buf_count' requires corresponding assignment to 'int *__single __counted_by(buf_count)' (aka 'int *__single') 'p->header.buf'; add self assignment 'p->header.buf = p->header.buf' if the value has not changed}} +} + +void assign_to_header_shared_count2(struct MixOuterSharedCount *p) { + p->header.buf_count = 0; // expected-error{{assignment to 'p->header.buf_count' requires an immediately preceding assignment to 'p' with a wide pointer}} + p->header.buf = 0; +} + +struct Indirect { + int len1; + int len2; + int * __counted_by(len1) p1; + int * __counted_by(len1 + len2) p2; + int fam[__counted_by(len2)]; +}; + +void assign_indirect_group(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + struct Indirect * __single ptr = p; + ptr->len1 = len1; + ptr->len2 = len2; + ptr->p1 = p1; + ptr->p2 = p2; +} + +void assign_indirect_group_scramble(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + struct Indirect * __single ptr = p; + ptr->p1 = p1; + ptr->p2 = p2; + ptr->len1 = len1; + ptr->len2 = len2; +} + +void assign_indirect_group_scramble2(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + struct Indirect * __single ptr = p; + ptr->len1 = len1; + ptr->p1 = p1; + ptr->len2 = len2; + ptr->p2 = p2; +} + +void assign_indirect_group_len1_missing(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + struct Indirect * __single ptr = p; + ptr->len2 = len2; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'ptr->p1' requires corresponding assignment to 'ptr->len1'; add self assignment 'ptr->len1 = ptr->len1' if the value has not changed}} + ptr->p1 = p1; + ptr->p2 = p2; +} + +void assign_indirect_group_len1_p1_missing(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1 + len2) p2) { + struct Indirect * __single ptr = p; + ptr->len2 = len2; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'ptr->p2' requires corresponding assignment to 'ptr->len1'; add self assignment 'ptr->len1 = ptr->len1' if the value has not changed}} + ptr->p2 = p2; +} + +void assign_indirect_group_p1_missing(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1 + len2) p2) { + struct Indirect * __single ptr = p; + // expected-error@+1{{assignment to 'ptr->len1' requires corresponding assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'ptr->p1'; add self assignment 'ptr->p1 = ptr->p1' if the value has not changed}} + ptr->len1 = len1; + ptr->len2 = len2; + ptr->p2 = p2; +} + +void assign_indirect_p2_missing(struct Indirect * __bidi_indexable p, int len1, int len2, int * __counted_by(len1) p1) { + struct Indirect * __single ptr = p; + // expected-error@+1{{assignment to 'ptr->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'ptr->p2'; add self assignment 'ptr->p2 = ptr->p2' if the value has not changed}} + ptr->len1 = len1; + ptr->len2 = len2; + ptr->p1 = p1; +} + +void assign_indirect_p2_len2_missing(struct Indirect * __bidi_indexable p, int len1, int * __counted_by(len1) p1) { + struct Indirect * __single ptr = p; + // expected-error@+1{{assignment to 'ptr->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'ptr->p2'; add self assignment 'ptr->p2 = ptr->p2' if the value has not changed}} + ptr->len1 = len1; + ptr->p1 = p1; +} + +void assign_indirect_p2_len2_len1_missing(struct Indirect * __bidi_indexable p, int len1, int * __counted_by(len1) p1) { + struct Indirect * __single ptr = p; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'ptr->p1' requires corresponding assignment to 'ptr->len1'; add self assignment 'ptr->len1 = ptr->len1' if the value has not changed}} + ptr->p1 = p1; +} + +void update_indirect_group(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + // expected-note@+1{{group of dependent field assignments starts here, place assignment to 'p' before it}} + p->len1 = len1; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; + p->p1 = p1; + p->p2 = p2; +} + +void update_indirect_group_scramble(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + // expected-note@+1{{group of dependent field assignments starts here, place assignment to 'p' before it}} + p->p1 = p1; + p->p2 = p2; + p->len1 = len1; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; +} + +void update_indirect_group_scramble2(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + // expected-note@+1{{group of dependent field assignments starts here, place assignment to 'p' before it}} + p->len1 = len1; + p->p1 = p1; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; + p->p2 = p2; +} + +void update_indirect_group_len1_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + // expected-error@+1{{assignment to 'p->len2' requires an immediately preceding assignment to 'p' with a wide pointer}} + p->len2 = len2; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'p->p1' requires corresponding assignment to 'p->len1'; add self assignment 'p->len1 = p->len1' if the value has not changed}} + p->p1 = p1; + p->p2 = p2; +} + +void update_indirect_group_len1_p1_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1 + len2) p2) { + // expected-error@+1{{assignment to 'p->len2' requires an immediately preceding assignment to 'p' with a wide pointer}} + p->len2 = len2; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2' requires corresponding assignment to 'p->len1'; add self assignment 'p->len1 = p->len1' if the value has not changed}} + p->p2 = p2; +} + +void update_indirect_group_p1_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1 + len2) p2) { + // expected-note@+2{{group of dependent field assignments starts here, place assignment to 'p' before it}} + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'p->p1'; add self assignment 'p->p1 = p->p1' if the value has not changed}} + p->len1 = len1; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; + p->p2 = p2; +} + +void update_indirect_p2_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1) { + // expected-note@+2{{group of dependent field assignments starts here, place assignment to 'p' before it}} + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2'; add self assignment 'p->p2 = p->p2' if the value has not changed}} + p->len1 = len1; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; + p->p1 = p1; +} + +void update_indirect_p2_len2_missing(struct Indirect * __single p, int len1, int * __counted_by(len1) p1) { + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2'; add self assignment 'p->p2 = p->p2' if the value has not changed}} + p->len1 = len1; + p->p1 = p1; +} + +void update_indirect_p2_len2_len1_missing(struct Indirect * __single p, int len1, int * __counted_by(len1) p1) { + // expected-error@+1{{assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'p->p1' requires corresponding assignment to 'p->len1'; add self assignment 'p->len1 = p->len1' if the value has not changed}} + p->p1 = p1; +} + + +void self_assign_update_indirect_group(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + p = p; + p->len1 = len1; + p->len2 = len2; + p->p1 = p1; + p->p2 = p2; +} + +void self_assign_update_indirect_group_scramble(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + // expected-note@+1{{group of dependent field assignments starts here, place assignment to 'p' before it}} + p->len1 = len1; + p = p; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; + p->p1 = p1; + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + p->p2 = p2; +} + +void self_assign_update_indirect_group_scramble2(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + // expected-note@+1{{group of dependent field assignments starts here, place assignment to 'p' before it}} + p->p1 = p1; + p->p2 = p2; + p = p; + p->len1 = len1; + // expected-error@+2{{assignments to dependent variables should not have side effects between them}} + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; +} + +void self_assign_update_indirect_group_len1_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1, int * __counted_by(len1 + len2) p2) { + p = p; + p->len2 = len2; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'p->p1' requires corresponding assignment to 'p->len1'; add self assignment 'p->len1 = p->len1' if the value has not changed}} + p->p1 = p1; + p->p2 = p2; +} + +void self_assign_update_indirect_group_len1_p1_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1 + len2) p2) { + p = p; + p->len2 = len2; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2' requires corresponding assignment to 'p->len1'; add self assignment 'p->len1 = p->len1' if the value has not changed}} + p->p2 = p2; +} + +void self_assign_update_indirect_group_p1_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1 + len2) p2) { + p = p; + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'p->p1'; add self assignment 'p->p1 = p->p1' if the value has not changed}} + p->len1 = len1; + p->len2 = len2; + p->p2 = p2; +} + +void self_assign_update_indirect_p2_missing(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1) { + p = p; + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2'; add self assignment 'p->p2 = p->p2' if the value has not changed}} + p->len1 = len1; + p->len2 = len2; + p->p1 = p1; +} + +void self_assign_update_indirect_p2_missing_scramble(struct Indirect * __single p, int len1, int len2, int * __counted_by(len1) p1) { + // expected-note@+2{{group of dependent field assignments starts here, place assignment to 'p' before it}} + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2'; add self assignment 'p->p2 = p->p2' if the value has not changed}} + p->len1 = len1; + // expected-error@+1{{assignment to 'p->len2' requires an assignment to 'p' with a wide pointer immediately preceding the group of dependent field assignments}} + p->len2 = len2; + p = p; + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + p->p1 = p1; +} + +void self_assign_update_indirect_p2_len2_missing(struct Indirect * __single p, int len1, int * __counted_by(len1) p1) { + p = p; + // expected-error@+1{{assignment to 'p->len1' requires corresponding assignment to 'int *__single __counted_by(len1 + len2)' (aka 'int *__single') 'p->p2'; add self assignment 'p->p2 = p->p2' if the value has not changed}} + p->len1 = len1; + p->p1 = p1; +} + +void self_assign_update_indirect_p2_len2_len1_missing(struct Indirect * __single p, int len1, int * __counted_by(len1) p1) { + p = p; + // expected-error@+1{{assignment to 'int *__single __counted_by(len1)' (aka 'int *__single') 'p->p1' requires corresponding assignment to 'p->len1'; add self assignment 'p->len1 = p->len1' if the value has not changed}} + p->p1 = p1; +} diff --git a/clang/test/BoundsSafety/Sema/counted-by-in-nested-unnamed-non-anon-struct.c b/clang/test/BoundsSafety/Sema/counted-by-in-nested-unnamed-non-anon-struct.c new file mode 100644 index 0000000000000..2d5dbd7f65f3c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-in-nested-unnamed-non-anon-struct.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// expected-no-diagnostics + +#include + +struct parent { + struct { + union { + struct { + int *__counted_by(len) entries; + int len; + }; + int *other; + }; + } s; +}; diff --git a/clang/test/BoundsSafety/Sema/counted-by-member-expr-cxx.cpp b/clang/test/BoundsSafety/Sema/counted-by-member-expr-cxx.cpp new file mode 100644 index 0000000000000..e427d6adb8f92 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-member-expr-cxx.cpp @@ -0,0 +1,67 @@ +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s + +#include +#include + +typedef struct T { + size_t count; +} T; + +class C { + T t; + T *tp; + size_t count; + int * __counted_by(count) p; // implicit `this->count` is ok + int * __counted_by(this->count) q; // explicit `this->count` is ok + int * __counted_by(t.count) x; // expected-error{{invalid argument expression to bounds attribute}} expected-note{{nested struct member in count parameter only supported for flexible array members}} + int * __counted_by(tp->count) y; // expected-error{{invalid argument expression to bounds attribute}} expected-note{{nested struct member in count parameter only supported for flexible array members}} +}; + +// test for simple flexible array members: +typedef struct flexible { + size_t count; + int elems[__counted_by(count)]; +} flex_t; + +class FAM { + size_t count; + int fam[__counted_by(count)]; +public: + FAM() {}; +}; + +class FAM_DOT { + T t; + int fam[__counted_by(t.count)]; // dot-expressions in counted-by is ok for FAMs +}; + +class FAM_ARROW { + T *tp; + int fam[__counted_by(tp->count)]; // expected-error{{arrow notation not allowed for struct member in count parameter}} +}; + +class FAM_THIS_ARROW_ARROW { + T *tp; + int fam[__counted_by(this->tp->count)]; // expected-error{{arrow notation not allowed for struct member in count parameter}} +}; + +class FAM_THIS_ARROW_DOT { + T t; + int fam[__counted_by(this->t.count)]; // dot-expressions in counted-by is ok for FAMs +}; + +class FAM_ARITHMETIC { + int count; + int offset; + int fam[__counted_by(count - offset)]; // ok +}; + +class FAM_THIS_PTR_ARITHMETIC { + int count; + int fam[__counted_by((this + 1)->count)]; // expected-error{{arrow notation not allowed for struct member in count parameter}} +}; + +class FAM_THIS_PTR_DEREFERENCE { + int count; + int fam[__counted_by((*this).count)]; // expected-error{{invalid argument expression to bounds attribute}} +}; diff --git a/clang/test/BoundsSafety/Sema/counted-by-nested-assignments.c b/clang/test/BoundsSafety/Sema/counted-by-nested-assignments.c new file mode 100644 index 0000000000000..42b140c6a8873 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-nested-assignments.c @@ -0,0 +1,17 @@ + + +// RUN: %clang_cc1 -fsyntax-only -verify=with-checks -fbounds-safety -fbounds-safety-bringup-missing-checks=indirect_count_update %s +// RUN: %clang_cc1 -fsyntax-only -verify=without-checks -fbounds-safety -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s +#include + +// without-checks-no-diagnostics + +void foo(int *__counted_by(count) x, int count) { + // with-checks-error@+1{{assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'x' requires corresponding assignment to 'count'; add self assignment 'count = count' if the value has not changed}} + *x++ = 0; +} + +void bar(int *__counted_by(count) x, int count) { + // with-checks-error@+1{{assignment to 'int *__single __counted_by(count)' (aka 'int *__single') 'x' requires corresponding assignment to 'count'; add self assignment 'count = count' if the value has not changed}} + *(x = x+1) = 0; +} diff --git a/clang/test/BoundsSafety/Sema/counted-by-nullability-attrs.c b/clang/test/BoundsSafety/Sema/counted-by-nullability-attrs.c new file mode 100644 index 0000000000000..6ae90f9750f04 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-nullability-attrs.c @@ -0,0 +1,73 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +void funcAttr1(int * __counted_by_or_null(len) p, int len) __attribute__((nonnull(1))); // expected-error{{cannot combine '__counted_by_or_null' and 'nonnull'; did you mean '__counted_by' instead?}} +void funcAttr2(int * __counted_by_or_null(len) p, int len) __attribute__((nonnull)); // expected-error{{cannot combine '__counted_by_or_null' and 'nonnull'; did you mean '__counted_by' instead?}} +__attribute__((nonnull)) void funcAttr3(int * __counted_by_or_null(len) p, int len); // expected-error{{cannot combine '__counted_by_or_null' and 'nonnull'; did you mean '__counted_by' instead?}} +int * __counted_by_or_null(len) funcAttr4(int len) __attribute__((nonnull)); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} +__attribute__((nonnull)) int * __counted_by_or_null(len) funcAttr5(int len); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} +int * __attribute__((nonnull)) __counted_by_or_null(len) funcAttr6(int len); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} + +void funcAttr7(int * __sized_by_or_null(len) p, int len) __attribute__((nonnull(1))); // expected-error{{cannot combine '__sized_by_or_null' and 'nonnull'; did you mean '__sized_by' instead?}} +void funcAttr8(int * __sized_by_or_null(len) p, int len) __attribute__((nonnull)); // expected-error{{cannot combine '__sized_by_or_null' and 'nonnull'; did you mean '__sized_by' instead?}} +__attribute__((nonnull)) void funcAttr9(int * __sized_by_or_null(len) p, int len); // expected-error{{cannot combine '__sized_by_or_null' and 'nonnull'; did you mean '__sized_by' instead?}} +int * __sized_by_or_null(len) funcAttr10(int len) __attribute__((nonnull)); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} +__attribute__((nonnull)) int * __sized_by_or_null(len) funcAttr11(int len); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} +int * __attribute__((nonnull)) __sized_by_or_null(len) funcAttr12(int len); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} + +void funcAttr13(int * __counted_by(len) p, int len) __attribute__((nonnull(1))); +void funcAttr14(int * __counted_by(len) p, int len) __attribute__((nonnull)); +__attribute__((nonnull)) void funcAttr15(int * __counted_by(len) p, int len); +int * __counted_by(len) funcAttr16(int len) __attribute__((nonnull)); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} +__attribute__((nonnull)) int * __counted_by(len) funcAttr17(int len); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} +int * __attribute__((nonnull)) __counted_by(len) funcAttr18(int len); // expected-warning{{'nonnull' attribute applied to function with no pointer arguments}} + +int * __counted_by_or_null(len) returnAttr1(int len) __attribute__((returns_nonnull)); // expected-error{{cannot combine '__counted_by_or_null' and 'returns_nonnull'; did you mean '__counted_by' instead?}} +__attribute__((returns_nonnull)) int * __counted_by_or_null(len) returnAttr2(int len); // expected-error{{cannot combine '__counted_by_or_null' and 'returns_nonnull'; did you mean '__counted_by' instead?}} +int * __attribute__((returns_nonnull)) __counted_by_or_null(len) returnAttr3(int len); // expected-error{{cannot combine '__counted_by_or_null' and 'returns_nonnull'; did you mean '__counted_by' instead?}} + +int * __sized_by_or_null(len) returnAttr4(int len) __attribute__((returns_nonnull)); // expected-error{{cannot combine '__sized_by_or_null' and 'returns_nonnull'; did you mean '__sized_by' instead?}} +__attribute__((returns_nonnull)) int * __sized_by_or_null(len) returnAttr5(int len); // expected-error{{cannot combine '__sized_by_or_null' and 'returns_nonnull'; did you mean '__sized_by' instead?}} +int * __attribute__((returns_nonnull)) __sized_by_or_null(len) returnAttr6(int len); // expected-error{{cannot combine '__sized_by_or_null' and 'returns_nonnull'; did you mean '__sized_by' instead?}} + +int * __counted_by(len) returnAttr7(int len) __attribute__((returns_nonnull)); +__attribute__((returns_nonnull)) int * __counted_by(len) returnAttr8(int len); +int * __attribute__((returns_nonnull)) __counted_by(len) returnAttr9(int len); + +void paramAttr1(int * __attribute((nonnull)) __counted_by_or_null(len) p, int len); // expected-error{{cannot combine '__counted_by_or_null' and 'nonnull'; did you mean '__counted_by' instead?}} +void paramAttr2(int * __counted_by_or_null(len) p __attribute((nonnull)), int len); // expected-error{{cannot combine '__counted_by_or_null' and 'nonnull'; did you mean '__counted_by' instead?}} +void paramAttr3(int * __counted_by_or_null(len) __attribute((nonnull)) p, int len); // expected-error{{cannot combine '__counted_by_or_null' and 'nonnull'; did you mean '__counted_by' instead?}} + +void paramAttr4(int * __attribute((nonnull)) __sized_by_or_null(len) p, int len); // expected-error{{cannot combine '__sized_by_or_null' and 'nonnull'; did you mean '__sized_by' instead?}} +void paramAttr5(int * __sized_by_or_null(len) p __attribute((nonnull)), int len); // expected-error{{cannot combine '__sized_by_or_null' and 'nonnull'; did you mean '__sized_by' instead?}} +void paramAttr6(int * __sized_by_or_null(len) __attribute((nonnull)) p, int len); // expected-error{{cannot combine '__sized_by_or_null' and 'nonnull'; did you mean '__sized_by' instead?}} + +void paramAttr7(int * __attribute((nonnull)) __counted_by(len) p, int len); +void paramAttr8(int * __counted_by(len) p __attribute((nonnull)), int len); +void paramAttr9(int * __counted_by(len) __attribute((nonnull)) p, int len); + +void paramType1(int * _Nonnull __counted_by_or_null(len) p, int len); // expected-warning{{combining '__counted_by_or_null' and '_Nonnull'; did you mean '__counted_by' instead?}} +void paramType2(int * __counted_by_or_null(len) _Nonnull p, int len); // expected-warning{{combining '__counted_by_or_null' and '_Nonnull'; did you mean '__counted_by' instead?}} + +void paramType3(int * _Nonnull __sized_by_or_null(len) p, int len); // expected-warning{{combining '__sized_by_or_null' and '_Nonnull'; did you mean '__sized_by' instead?}} +void paramType4(int * __sized_by_or_null(len) _Nonnull p, int len); // expected-warning{{combining '__sized_by_or_null' and '_Nonnull'; did you mean '__sized_by' instead?}} + +void paramType5(int * _Nonnull __counted_by(len) p, int len); +void paramType6(int * __counted_by(len) _Nonnull p, int len); + +void paramType7(int * __counted_by(4) _Nullable p); // expected-warning{{combining '__counted_by' with non-zero count (which cannot be null) and '_Nullable'; did you mean '__counted_by_or_null' instead?}} +void paramType8(int * __counted_by_or_null(4) _Nullable p); +void paramType9(int * __sized_by(4) _Nullable p); // expected-warning{{combining '__sized_by' with non-zero size (which cannot be null) and '_Nullable'; did you mean '__sized_by_or_null' instead?}} + +void paramType10(int * _Nullable __counted_by(4) p); // expected-warning{{combining '__counted_by' with non-zero count (which cannot be null) and '_Nullable'; did you mean '__counted_by_or_null' instead?}} +void paramType11(int * _Nullable __counted_by_or_null(4) p); +void paramType12(int * _Nullable __sized_by(4) p); // expected-warning{{combining '__sized_by' with non-zero size (which cannot be null) and '_Nullable'; did you mean '__sized_by_or_null' instead?}} + diff --git a/clang/test/BoundsSafety/Sema/counted-by-or-null-array-fixit.c b/clang/test/BoundsSafety/Sema/counted-by-or-null-array-fixit.c new file mode 100644 index 0000000000000..548758d62f8d8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-or-null-array-fixit.c @@ -0,0 +1,52 @@ + +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s --implicit-check-not="fix-it" +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fdiagnostics-parseable-fixits %s 2>&1| FileCheck %s --implicit-check-not="fix-it" + +#include + +struct my_struct { + int len; + int *__counted_by_or_null(len) tmp; + // CHECK: fix-it:{{.*}}:{[[@LINE+1]]:13-[[@LINE+1]]:33}:"__counted_by" + int fam[__counted_by_or_null(len)]; // expected-error{{flexible array members cannot be null; did you mean __counted_by instead?}} +}; + +struct constant_sized_inner_arr_2d_struct { + int len; + // CHECK: fix-it:{{.*}}:{[[@LINE+1]]:13-[[@LINE+1]]:33}:"__counted_by" + int fam[__counted_by_or_null(len)][10]; // expected-error{{flexible array members cannot be null; did you mean __counted_by instead?}} +}; + +// expected-error@+3{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +// expected-error@+2{{cannot apply '__counted_by_or_null' attribute to 'int (*)[][size]' because 'int[][size]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +// CHECK: fix-it:{{.*}}:{[[@LINE+1]]:52-[[@LINE+1]]:72}:"__sized_by_or_null" +void counted_nested_unsized_array(int size, int (* __counted_by_or_null(size) param)[__counted_by_or_null(10)][size]); + +void local_array() { + int local_buf[__counted_by_or_null(29)] = {}; // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} + int local_sized_buf[__sized_by_or_null(29)] = {}; // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} + + int local_buf_no_init[__counted_by_or_null(31)]; // expected-error{{definition of variable with array type needs an explicit size or an initializer}} + int local_sized_buf_no_init[__sized_by_or_null(31)]; // expected-error{{definition of variable with array type needs an explicit size or an initializer}} +} + +int global_buf[__counted_by_or_null(5)] = {}; // expected-error{{arrays with an explicit size decay to counted pointers and cannot also have a count attribute}} + +// CHECK: fix-it:{{.*}}:{[[@LINE+1]]:40-[[@LINE+1]]:60}:"__counted_by" +void counted_pointer_to_array(int (*p)[__counted_by_or_null(len)], int len); // expected-error{{array objects cannot be null; did you mean __counted_by instead}} +// CHECK: fix-it:{{.*}}:{[[@LINE+1]]:38-[[@LINE+1]]:56}:"__sized_by" +void sized_pointer_to_array(int (*p)[__sized_by_or_null(size)], int size); // expected-error{{array objects cannot be null; did you mean __sized_by instead}} + +extern int len; +// CHECK: fix-it:{{.*}}:{[[@LINE+1]]:16-[[@LINE+1]]:36}:"__counted_by" +extern int arr[__counted_by_or_null(len)]; // expected-error{{array objects cannot be null; did you mean __counted_by instead}} +extern int arr2[__counted_by(len)]; +extern int (*arr3)[__counted_by_or_null(len)]; // expected-error{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + +void local_extern() { + extern int len2; + extern int arr4[__counted_by(len2)]; + // CHECK: fix-it:{{.*}}:{[[@LINE+1]]:21-[[@LINE+1]]:41}:"__counted_by" + extern int arr5[__counted_by_or_null(len2)]; // expected-error{{array objects cannot be null; did you mean __counted_by instead}} +} diff --git a/clang/test/BoundsSafety/Sema/counted-by-or-null-array.c b/clang/test/BoundsSafety/Sema/counted-by-or-null-array.c new file mode 100644 index 0000000000000..818f20db723ca --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-or-null-array.c @@ -0,0 +1,55 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct my_struct { + int len; + int *__counted_by_or_null(len) tmp; + int fam[__counted_by_or_null(len)]; // expected-error{{flexible array members cannot be null; did you mean __counted_by instead?}} +}; + +struct struct_2d { + int len; + int fam[__counted_by_or_null(len)][__counted_by_or_null(len)]; // expected-error{{array has incomplete element type 'int[]'}} +}; + +struct sized_2d_struct { + int len; + int fam[__sized_by_or_null(len)][__counted_by_or_null(len)]; // expected-error{{array has incomplete element type 'int[]'}} +}; + +int global_len; +struct variably_sized_inner_arr_2d_struct { + int len; + int fam[__counted_by_or_null(len)][global_len]; // expected-error{{fields must have a constant size: 'variable length array in structure' extension will never be supported}} +}; + +struct constant_sized_inner_arr_2d_struct { + int len; + int fam[__counted_by_or_null(len)][10]; // expected-error{{flexible array members cannot be null; did you mean __counted_by instead}} +}; + +struct constant_sized_outer_arr_2d_struct { + int len; + int fam[10][__counted_by_or_null(len)]; // expected-error{{array has incomplete element type 'int[]'}} +}; + +// expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +void pointers_to_array_params(int size, int (* __sized_by_or_null(size) param)[__counted_by_or_null(10)][size], int len, int arr[__counted_by_or_null(11)][len]); + +void counted_unsized_array(int size, int (*param)[__counted_by_or_null(10)][__counted_by_or_null(size)]); // expected-error{{array has incomplete element type 'int[]'}} + +// expected-error@+2{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'int (*)[][size]' because 'int[][size]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +void counted_nested_unsized_array(int size, int (* __counted_by_or_null(size) param)[__counted_by_or_null(10)][size]); + +void counted_decayed_nested(int len, int arr[__counted_by_or_null(11)][__counted_by_or_null(len)]); // expected-error{{array has incomplete element type 'int[]'}} + +void counted_decayed_of_variably_sized_array(int len, int arr[__counted_by_or_null(11)][len]); + +// expected-warning@+1{{tentative array definition assumed to have one element}} +int global_buf[__counted_by_or_null(5)]; +int global_2D_buf[__sized_by(7)][__counted_by_or_null(13)]; // expected-error{{array has incomplete element type 'int[]'}} +int global_2D_buf_const_sized_outer[7][__counted_by_or_null(13)]; // expected-error{{array has incomplete element type 'int[]'}} diff --git a/clang/test/BoundsSafety/Sema/counted-by-or-null.c b/clang/test/BoundsSafety/Sema/counted-by-or-null.c new file mode 100644 index 0000000000000..05b71f7acc441 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-or-null.c @@ -0,0 +1,58 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include + +void foo(int * __counted_by_or_null(2) p); + +void bar(int * __counted_by_or_null(1) p) { + foo(p); +} + +void assign_null_constant_count() { + int * __counted_by_or_null(2) p = NULL; + foo(p); +} + +void assign_null_dynamic_count(int count) { + int c = count; + int * __counted_by_or_null(c) p = NULL; + foo(p); +} + +int * __counted_by_or_null(c) quux(int c); +void side_effect(); + +void reassign_coupled_decls() { + int c = 3; + int * __counted_by_or_null(c) p; + p = quux(c); + c = c; + + side_effect(); + + p = quux(4); + c = 4; + + side_effect(); + + int * __counted_by_or_null(5) tmp = quux(5); + c = 5; + p = tmp; + + side_effect(); + + p = quux(6); // expected-note{{previously assigned here}} + // expected-error@+2{{assignment to 'int *__single __counted_by_or_null(c)' (aka 'int *__single') 'p' requires corresponding assignment to 'c'; add self assignment 'c = c' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + p = quux(6); // expected-error{{assignments to dependent variables should not have side effects between them}} + + side_effect(); + + c = 7; // expected-note{{previously assigned here}} + // expected-error@+2{{assignment to 'c' requires corresponding assignment to 'int *__single __counted_by_or_null(c)' (aka 'int *__single') 'p'; add self assignment 'p = p' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count 'c' must be simplified; keep only one of the assignments}} + c = 7; +} diff --git a/clang/test/BoundsSafety/Sema/counted-by-pointer-as-length.c b/clang/test/BoundsSafety/Sema/counted-by-pointer-as-length.c new file mode 100644 index 0000000000000..57d67d293ea33 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-pointer-as-length.c @@ -0,0 +1,16 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +void foo(void) { + // expected-error@+1{{'__counted_by' attribute requires an integer type argument}} + int *__counted_by(bar) bar; + int *p = bar; +} diff --git a/clang/test/BoundsSafety/Sema/counted-by-ptr-arith-constant-count.c b/clang/test/BoundsSafety/Sema/counted-by-ptr-arith-constant-count.c new file mode 100644 index 0000000000000..dac087ae6a718 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-ptr-arith-constant-count.c @@ -0,0 +1,766 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,legacy %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fbounds-safety-bringup-missing-checks=indirect_count_update -verify=expected,legacy,extra %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -verify=expected,legacy,cli %s +#include + +void consume_cb(int* __counted_by(3) p); +void consume_cbon(int* __counted_by_or_null(3) p); + +struct cb { + int count; + int* __counted_by(count) buf; +}; + +struct cbon { + int count; + int* __counted_by_or_null(count) buf; +}; + +void side_effect(void); + +int global_arr [2] = {0}; +// expected-note@+2 4{{__counted_by attribute is here}} +// extra-note@+1 2{{__counted_by attribute is here}} +int*__counted_by(2) global_cb = global_arr; +// expected-note@+2 4{{__counted_by_or_null attribute is here}} +// extra-note@+1 2{{__counted_by_or_null attribute is here}} +int*__counted_by_or_null(2) global_cbon = global_arr; + +const int const_size = 2; +// expected-note@+2 4{{__counted_by attribute is here}} +// extra-note@+1 2{{__counted_by attribute is here}} +int*__counted_by(const_size) global_cb_const_qual_count = global_arr; +// expected-note@+2 4{{__counted_by_or_null attribute is here}} +// extra-note@+1 2{{__counted_by_or_null attribute is here}} +int*__counted_by_or_null(const_size) global_cbon_const_qual_count = global_arr; + +// expected-note@+2 4{{__counted_by attribute is here}} +// extra-note@+1 2{{__counted_by attribute is here}} +int*__counted_by(1+1) global_cb_opo = global_arr; +// expected-note@+2 4{{__counted_by_or_null attribute is here}} +// extra-note@+1 2{{__counted_by_or_null attribute is here}} +int*__counted_by_or_null(1+1) global_cbon_opo = global_arr; + +// legacy-note@+3 1{{__counted_by attribute is here}} +// expected-note@+2 8{{__counted_by attribute is here}} +// extra-note@+1 3{{__counted_by attribute is here}} +int* __counted_by(2) test_cb(int* __counted_by(3) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__counted_by attribute is here}} + // extra-note@+1 2{{__counted_by attribute is here}} + int* __counted_by(2) local_cb = p; + local_cb = p; // OK + side_effect(); + ++local_cb; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cb++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + --local_cb; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local_cb--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local_cb += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cb -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_cb++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++local_cb = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *--local_cb = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *local_cb-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local = local_cb + 1; // OK because `local_cb` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_cb++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + ++global_cb; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cb--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + --global_cb; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + global_cb += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cb -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_cb++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++global_cb = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *global_cb-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *--global_cb = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + global_cb = global_cb + 1; // OK because `global_cb` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + consume_cb(++p); // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + struct cb S = { + .count = 2, + // legacy-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + S = (struct cb){.buf = p++}; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} +} + +// legacy-note@+3 1{{__counted_by attribute is here}} +// expected-note@+2 8{{__counted_by attribute is here}} +// extra-note@+1 3{{__counted_by attribute is here}} +int* __counted_by(2) test_cb_constant_fold_count(int* __counted_by(2+1) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__counted_by attribute is here}} + // extra-note@+1 2{{__counted_by attribute is here}} + int* __counted_by(1+1) local_cb = p; + local_cb = p; // OK + side_effect(); + ++local_cb; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cb++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + --local_cb; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local_cb--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local_cb += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cb -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_cb++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++local_cb = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *--local_cb = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *local_cb-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local = local_cb + 1; // OK because `local_cb` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_cb_opo++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + ++global_cb_opo; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cb_opo--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + --global_cb_opo; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + global_cb_opo += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cb_opo -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_cb_opo++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++global_cb_opo = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *global_cb_opo-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *--global_cb_opo = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + global_cb_opo = global_cb_opo + 1; // OK because `global_cb` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + consume_cb(++p); // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + struct cb S = { + .count = 2, + // legacy-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + S = (struct cb){.buf = p++}; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count of 3 always traps}} +} + +// legacy-note@+3 1{{__counted_by attribute is here}} +// expected-note@+2 8{{__counted_by attribute is here}} +// extra-note@+1 3{{__counted_by attribute is here}} +int* __counted_by(size) test_cb_const_qualified_size(const int size, int* __counted_by(size) p) { + int* local; + // Modify local var + const int local_size = 2; + // expected-note@+2 4{{__counted_by attribute is here}} + // extra-note@+1 2{{__counted_by attribute is here}} + int* __counted_by(local_size) local_cb = p; + side_effect(); + local_cb = p; // OK + + side_effect(); + ++local_cb; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cb++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + --local_cb; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local_cb--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + local_cb += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cb -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_cb++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++local_cb = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *--local_cb = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *local_cb-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + local = local_cb + 1; // OK because `local_cb` gets promoted to a __bidi_indexable first. + side_effect(); + + // Modify global + global_cb_const_qual_count++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + ++global_cb_const_qual_count; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + --global_cb_const_qual_count; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + global_cb_const_qual_count += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cb_const_qual_count-= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_cb_const_qual_count++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++global_cb_const_qual_count = 1; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *global_cb_const_qual_count-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *--global_cb_const_qual_count = 1; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + global_cb_const_qual_count = global_cb_const_qual_count + 1; // OK because `global_cb` gets promoted to a __bidi_indexable first. + + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__counted_by' attributed pointer with constant count 'size' always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__counted_by' attributed pointer with constant count 'size' always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} + + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + + side_effect(); + + consume_cb(++p); // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + struct cb S = { + .count = 2, + // legacy-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + }; + // cli-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + S = (struct cb){.buf = p++}; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__counted_by' attributed pointer with constant count 'size' always traps}} +} + +// legacy-note@+3 1{{__counted_by_or_null attribute is here}} +// expected-note@+2 8{{__counted_by_or_null attribute is here}} +// extra-note@+1 3{{__counted_by_or_null attribute is here}} +int* __counted_by_or_null(2) test_cbon(int* __counted_by_or_null(3) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__counted_by_or_null attribute is here}} + // extra-note@+1 2{{__counted_by_or_null attribute is here}} + int* __counted_by_or_null(2) local_cbon = p; + local_cbon = p; // OK + side_effect(); + ++local_cbon; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cbon++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + --local_cbon; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local_cbon--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local_cbon += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cbon -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_cbon++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++local_cbon = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *--local_cbon = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *local_cbon-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local = local_cbon + 1; // OK because `local_cbon` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_cbon++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + ++global_cbon; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cbon--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + --global_cbon; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + global_cbon += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cbon -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_cbon++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++global_cbon = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *global_cbon-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *--global_cbon = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + global_cbon = global_cbon + 1; // OK because `global_cbon` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + consume_cbon(++p); // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + struct cbon S = { + .count = 2, + // legacy-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + S = (struct cbon){.buf = p++}; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} +} + +// legacy-note@+3 1{{__counted_by_or_null attribute is here}} +// expected-note@+2 8{{__counted_by_or_null attribute is here}} +// extra-note@+1 3{{__counted_by_or_null attribute is here}} +int* __counted_by_or_null(2) test_cbon_constant_fold_count(int* __counted_by_or_null(2+1) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__counted_by_or_null attribute is here}} + // extra-note@+1 2{{__counted_by_or_null attribute is here}} + int* __counted_by_or_null(1+1) local_cbon = p; + local_cbon = p; // OK + side_effect(); + ++local_cbon; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cbon++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + --local_cbon; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local_cbon--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local_cbon += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cbon -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_cbon++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++local_cbon = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *--local_cbon = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *local_cbon-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local = local_cbon + 1; // OK because `local_cbon` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_cbon_opo++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + ++global_cbon_opo; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cbon_opo--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + --global_cbon_opo; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + global_cbon_opo += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cbon_opo -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_cbon_opo++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++global_cbon_opo = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *global_cbon_opo-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *--global_cbon_opo = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + global_cbon_opo = global_cbon_opo + 1; // OK because `global_cbon` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + consume_cbon(++p); // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + struct cbon S = { + .count = 2, + // legacy-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + S = (struct cbon){.buf = p++}; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 3 always traps}} +} + +// legacy-note@+3 1{{__counted_by_or_null attribute is here}} +// expected-note@+2 8{{__counted_by_or_null attribute is here}} +// extra-note@+1 3{{__counted_by_or_null attribute is here}} +int* __counted_by_or_null(size) test_cbon_const_qualified_size(const int size, int* __counted_by_or_null(size) p) { + int* local; + // Modify local var + const int local_size = 2; + // expected-note@+2 4{{__counted_by_or_null attribute is here}} + // extra-note@+1 2{{__counted_by_or_null attribute is here}} + int* __counted_by_or_null(local_size) local_cbon = p; + side_effect(); + local_cbon = p; // OK + + side_effect(); + ++local_cbon; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cbon++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + --local_cbon; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local_cbon--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + local_cbon += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + local_cbon -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_cbon++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++local_cbon = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *--local_cbon = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *local_cbon-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + local = local_cbon + 1; // OK because `local_cbon` gets promoted to a __bidi_indexable first. + side_effect(); + + // Modify global + global_cbon_const_qual_count++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + ++global_cbon_const_qual_count; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + --global_cbon_const_qual_count; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + global_cbon_const_qual_count += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + global_cbon_const_qual_count-= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_cbon_const_qual_count++ = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + *++global_cbon_const_qual_count = 1; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count of 2 always traps}} + side_effect(); + + *global_cbon_const_qual_count-- = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *--global_cbon_const_qual_count = 1; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + global_cbon_const_qual_count = global_cbon_const_qual_count + 1; // OK because `global_cbon` gets promoted to a __bidi_indexable first. + + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} + + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + + side_effect(); + + consume_cbon(++p); // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + struct cbon S = { + .count = 2, + // legacy-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + }; + // cli-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + S = (struct cbon){.buf = p++}; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__counted_by_or_null' attributed pointer with constant count 'size' always traps}} +} + +// Warning diagnostic tests + +void downgrade_to_warning(int* __counted_by(4) ptr) { // expected-note{{__counted_by attribute is here}} +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wbounds-safety-externally-counted-ptr-arith-constant-count" + ++ptr; // expected-warning{{positive pointer arithmetic on '__counted_by' attributed pointer with constant count of 4 always traps}} +#pragma clang diagnostic pop +} + +void downgrade_to_ignored(int* __counted_by(4) ptr) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wbounds-safety-externally-counted-ptr-arith-constant-count" + ++ptr; // ok +} diff --git a/clang/test/BoundsSafety/Sema/counted-by-redecl.c b/clang/test/BoundsSafety/Sema/counted-by-redecl.c new file mode 100644 index 0000000000000..c8dfe4fd3174e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted-by-redecl.c @@ -0,0 +1,32 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s + +#include + +int len_a; +// expected-note@+1{{'a' declared here}} +int * __counted_by(len_a) a; +// expected-error@+1{{conflicting '__counted_by_or_null' attribute with the previous variable declaration}} +int * __counted_by_or_null(len_a) a; + +int len_b; +// expected-note@+1{{'b' declared here}} +int * __counted_by_or_null(len_b) b; +// expected-error@+1{{conflicting '__counted_by' attribute with the previous variable declaration}} +int * __counted_by(len_b) b; + +int len_c; +// expected-note@+1{{'c' declared here}} +int * __sized_by(len_c) c; +// expected-error@+1{{conflicting '__sized_by_or_null' attribute with the previous variable declaration}} +int * __sized_by_or_null(len_c) c; + +int len_d; +// expected-note@+1{{'d' declared here}} +int * __sized_by_or_null(len_d) d; +// expected-error@+1{{conflicting '__sized_by' attribute with the previous variable declaration}} +int * __sized_by(len_d) d; diff --git a/clang/test/BoundsSafety/Sema/counted_by_constants.c b/clang/test/BoundsSafety/Sema/counted_by_constants.c new file mode 100644 index 0000000000000..9287321a36e62 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_constants.c @@ -0,0 +1,313 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +// const arguments +unsigned global_count = 2; +const unsigned const_global_count = 0; +extern const unsigned extern_const_global_count; + +enum { + enum_count = 10 +}; + +struct struct_enum_count { + int *__counted_by(enum_count) buf; +}; + +struct struct_const_global_count { + int *__counted_by(const_global_count) buf; +}; + +void fun_const_global_count(int *__counted_by(const_global_count) arg); + +void test_local_const_global_count() { + int *__counted_by(const_global_count) local; +} + +struct struct_extern_const_global_count { + int *__counted_by(extern_const_global_count) buf; +}; + +void fun_extern_const_global_count(int *__counted_by(extern_const_global_count) arg); + +void test_local_extern_const_global_count() { + int *__counted_by(extern_const_global_count) local; +} + +// data const arguments +unsigned data_const_global_count __unsafe_late_const; + +struct struct_data_const_global_count { + int *__counted_by(data_const_global_count) buf; +}; + +int fun_data_const_global_count(int *__counted_by(data_const_global_count) arg); + +void test_local_data_const_global_count() { + int *__counted_by(data_const_global_count) local; +} + +// const function calls +#define __pure2 __attribute__((const)) + +__pure2 unsigned fun_const_no_argument(); + +struct struct_consteval_function_call_count { + int *__counted_by(fun_const_no_argument()) buf; +}; + +void fun_consteval_function_call_count(int *__counted_by(fun_const_no_argument()) arg); + +void test_local_consteval_function_call_count() { + int *__counted_by(fun_const_no_argument()) local; +} + +// expected-warning@+1{{'const' attribute on function returning 'void'; attribute ignored}} +__pure2 void fun_const_void(); +struct struct_void_function_call_count { + // expected-error@+1{{'__counted_by' attribute requires an integer type argument}} + int *__counted_by(fun_const_void()) buf; + // expected-error@+1{{'__sized_by' attribute requires an integer type argument}} + int *__sized_by(fun_const_void()) buf2; + // expected-error@+1{{'__counted_by_or_null' attribute requires an integer type argument}} + int *__counted_by_or_null(fun_const_void()) buf3; + // expected-error@+1{{'__sized_by_or_null' attribute requires an integer type argument}} + int *__sized_by_or_null(fun_const_void()) buf4; +}; + +// expected-note@+1 12{{'fun_no_argument' declared here}} +unsigned fun_no_argument(); + +struct struct_function_call_count { + // expected-error@+1{{argument of '__counted_by' attribute can only reference function with 'const' attribute}} + int *__counted_by(fun_no_argument()) buf; + // expected-error@+1{{argument of '__sized_by' attribute can only reference function with 'const' attribute}} + int *__sized_by(fun_no_argument()) buf2; + // expected-error@+1{{argument of '__counted_by_or_null' attribute can only reference function with 'const' attribute}} + int *__counted_by_or_null(fun_no_argument()) buf3; + // expected-error@+1{{argument of '__sized_by_or_null' attribute can only reference function with 'const' attribute}} + int *__sized_by_or_null(fun_no_argument()) buf4; +}; + +// expected-error@+1{{argument of '__counted_by' attribute can only reference function with 'const' attribute}} +void fun_function_call_count(int *__counted_by(fun_no_argument()) arg); +// expected-error@+1{{argument of '__sized_by' attribute can only reference function with 'const' attribute}} +void fun_function_call_size(int *__sized_by(fun_no_argument()) arg); +// expected-error@+1{{argument of '__counted_by_or_null' attribute can only reference function with 'const' attribute}} +void fun_function_call_count_null(int *__counted_by_or_null(fun_no_argument()) arg); +// expected-error@+1{{argument of '__sized_by_or_null' attribute can only reference function with 'const' attribute}} +void fun_function_call_size_null(int *__sized_by_or_null(fun_no_argument()) arg); + +void test_local_function_call_count() { + // expected-error@+1{{argument of '__counted_by' attribute can only reference function with 'const' attribute}} + int *__counted_by(fun_no_argument()) local; + // expected-error@+1{{argument of '__sized_by' attribute can only reference function with 'const' attribute}} + int *__sized_by(fun_no_argument()) local2; + // expected-error@+1{{argument of '__counted_by_or_null' attribute can only reference function with 'const' attribute}} + int *__counted_by_or_null(fun_no_argument()) local3; + // expected-error@+1{{argument of '__sized_by_or_null' attribute can only reference function with 'const' attribute}} + int *__sized_by_or_null(fun_no_argument()) local4; +} + + +// const function calls with argument +__pure2 int fun_const_with_argument(int arg); +__pure2 long fun_const_with_argument2(int arg0, const void *arg1); + + +struct struct_const_function_argument_count { + int *__counted_by(fun_const_with_argument(2)) buf; +}; + +void fun_const_function_argument_count(int *__counted_by(fun_const_with_argument(4)) arg); + +void test_local_const_function_argument_count() { + int *__counted_by(fun_const_with_argument(0)) local; +} + +struct struct_const_function_argument_count2 { + int field; + // expected-error@+1{{argument of function call 'fun_const_with_argument(field)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument(field)) buf; + // expected-error@+1{{argument of function call 'fun_const_with_argument(field)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument(field)) buf2; + // expected-error@+1{{argument of function call 'fun_const_with_argument(field)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument(field)) buf3; + // expected-error@+1{{argument of function call 'fun_const_with_argument(field)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument(field)) buf4; + const int const_field; + // expected-error@+1{{argument of function call 'fun_const_with_argument(const_field)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument(const_field)) buf5; + // expected-error@+1{{argument of function call 'fun_const_with_argument(const_field)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument(const_field)) buf6; + // expected-error@+1{{argument of function call 'fun_const_with_argument(const_field)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument(const_field)) buf7; + // expected-error@+1{{argument of function call 'fun_const_with_argument(const_field)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument(const_field)) buf8; + + int *__counted_by(fun_const_with_argument(const_global_count)) buf9; + int *__sized_by(fun_const_with_argument(const_global_count)) buf10; + int *__counted_by_or_null(fun_const_with_argument(const_global_count)) buf11; + int *__sized_by_or_null(fun_const_with_argument(const_global_count)) buf12; + + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument(data_const_global_count)) buf13; + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument(data_const_global_count)) buf14; + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument(data_const_global_count)) buf15; + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument(data_const_global_count)) buf16; +}; + +// expected-error@+2{{argument of function call 'fun_const_with_argument(global_count)' in '__counted_by' attribute is not a constant expression}} +void fun_const_function_argument_count2( + int *__counted_by(fun_const_with_argument(global_count)) arg); +// expected-error@+2{{argument of function call 'fun_const_with_argument(global_count)' in '__sized_by' attribute is not a constant expression}} +void fun_const_function_argument_size2( + int *__sized_by(fun_const_with_argument(global_count)) arg); +// expected-error@+2{{argument of function call 'fun_const_with_argument(global_count)' in '__counted_by_or_null' attribute is not a constant expression}} +void fun_const_function_argument_count_null2( + int *__counted_by_or_null(fun_const_with_argument(global_count)) arg); +// expected-error@+2{{argument of function call 'fun_const_with_argument(global_count)' in '__sized_by_or_null' attribute is not a constant expression}} +void fun_const_function_argument_size_null2( + int *__sized_by_or_null(fun_const_with_argument(global_count)) arg); + +void fun_const_function_argument_count3( + int *__counted_by(fun_const_with_argument(const_global_count)) arg); +void fun_const_function_argument_size3( + int *__sized_by(fun_const_with_argument(const_global_count)) arg); +void fun_const_function_argument_count_null3( + int *__counted_by_or_null(fun_const_with_argument(const_global_count)) arg); +void fun_const_function_argument_size_null3( + int *__sized_by_or_null(fun_const_with_argument(const_global_count)) arg); + +// expected-error@+2{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__counted_by' attribute is not a constant expression}} +void fun_const_function_argument_count4( + int *__counted_by(fun_const_with_argument(data_const_global_count)) arg); +// expected-error@+2{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__sized_by' attribute is not a constant expression}} +void fun_const_function_argument_size4( + int *__sized_by(fun_const_with_argument(data_const_global_count)) arg); +// expected-error@+2{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__counted_by_or_null' attribute is not a constant expression}} +void fun_const_function_argument_count_null4( + int *__counted_by_or_null(fun_const_with_argument(data_const_global_count)) arg); +// expected-error@+2{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__sized_by_or_null' attribute is not a constant expression}} +void fun_const_function_argument_size_null4( + int *__sized_by_or_null(fun_const_with_argument(data_const_global_count)) arg); + +void test_local_const_function_argument_count2(int arg) { + // expected-error@+1{{argument of function call 'fun_const_with_argument(arg)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument(arg)) local; +} +void test_local_const_function_argument_size2(int arg) { + // expected-error@+1{{argument of function call 'fun_const_with_argument(arg)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument(arg)) local; +} +void test_local_const_function_argument_count_null2(int arg) { + // expected-error@+1{{argument of function call 'fun_const_with_argument(arg)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument(arg)) local; +} +void test_local_const_function_argument_size_null2(int arg) { + // expected-error@+1{{argument of function call 'fun_const_with_argument(arg)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument(arg)) local; +} + +void test_local_const_function_argument_count3() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(global_count)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument(global_count)) local; +} +void test_local_const_function_argument_size3() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(global_count)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument(global_count)) local; +} +void test_local_const_function_argument_count_null3() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(global_count)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument(global_count)) local; +} +void test_local_const_function_argument_size_null3() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(global_count)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument(global_count)) local; +} + +void test_local_const_function_argument_count4() { + int *__counted_by(fun_const_with_argument(const_global_count)) local; +} +void test_local_const_function_argument_size4() { + int *__sized_by(fun_const_with_argument(const_global_count)) local; +} +void test_local_const_function_argument_count_null4() { + int *__counted_by_or_null(fun_const_with_argument(const_global_count)) local; +} +void test_local_const_function_argument_size_null4() { + int *__sized_by_or_null(fun_const_with_argument(const_global_count)) local; +} + +void test_local_const_function_argument_count5() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument(data_const_global_count)) local; +} +void test_local_const_function_argument_size5() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument(data_const_global_count)) local; +} +void test_local_const_function_argument_count_null5() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument(data_const_global_count)) local; +} +void test_local_const_function_argument_size_null5() { + // expected-error@+1{{argument of function call 'fun_const_with_argument(data_const_global_count)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument(data_const_global_count)) local; +} + +struct struct_const_function_argument2_count { + int field0; + void *field1; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(field0, field1)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument2(field0, field1)) buf; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(field0, field1)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument2(field0, field1)) buf2; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(field0, field1)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument2(field0, field1)) buf3; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(field0, field1)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument2(field0, field1)) buf4; + + const int const_field0; + const void *const_field1; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, field1)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument2(const_field0, field1)) buf5; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, field1)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument2(const_field0, field1)) buf6; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, field1)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument2(const_field0, field1)) buf7; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, field1)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument2(const_field0, field1)) buf8; + + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, const_field1)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument2(const_field0, const_field1)) buf9; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, const_field1)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument2(const_field0, const_field1)) buf10; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, const_field1)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument2(const_field0, const_field1)) buf11; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_field0, const_field1)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument2(const_field0, const_field1)) buf12; + + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_global_count, const_field1)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument2(const_global_count, const_field1)) buf13; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_global_count, const_field1)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument2(const_global_count, const_field1)) buf14; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_global_count, const_field1)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument2(const_global_count, const_field1)) buf15; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(const_global_count, const_field1)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument2(const_global_count, const_field1)) buf16; + + // expected-error@+1{{argument of function call 'fun_const_with_argument2(data_const_global_count, const_field1)' in '__counted_by' attribute is not a constant expression}} + int *__counted_by(fun_const_with_argument2(data_const_global_count, const_field1)) buf17; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(data_const_global_count, const_field1)' in '__sized_by' attribute is not a constant expression}} + int *__sized_by(fun_const_with_argument2(data_const_global_count, const_field1)) buf18; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(data_const_global_count, const_field1)' in '__counted_by_or_null' attribute is not a constant expression}} + int *__counted_by_or_null(fun_const_with_argument2(data_const_global_count, const_field1)) buf19; + // expected-error@+1{{argument of function call 'fun_const_with_argument2(data_const_global_count, const_field1)' in '__sized_by_or_null' attribute is not a constant expression}} + int *__sized_by_or_null(fun_const_with_argument2(data_const_global_count, const_field1)) buf20; +}; diff --git a/clang/test/BoundsSafety/Sema/counted_by_data_const_init.c b/clang/test/BoundsSafety/Sema/counted_by_data_const_init.c new file mode 100644 index 0000000000000..916b1e2c36c14 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_data_const_init.c @@ -0,0 +1,102 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +// data const arguments +unsigned data_const_global_count __unsafe_late_const; +unsigned global_count; +const unsigned const_global_count; + +enum { enum_count = 10, }; + +struct struct_enum_count { + int *__counted_by(enum_count) buf; +}; + +void test_enum_count(struct struct_enum_count *sp, int *__bidi_indexable arg) { + sp->buf = arg; +} + +struct struct_data_const_global_count { + int *__counted_by(data_const_global_count) buf; +}; + +struct struct_global_count { + // expected-error@+1{{count expression on struct field may only reference other fields of the same struct}} + int *__counted_by(global_count) buf; +}; + +struct struct_const_global_count { + int *__counted_by(const_global_count) buf; +}; + +unsigned data_const_global_count_flex __unsafe_late_const; + +struct struct_data_const_global_count_flex { + int dummy; + int flex[__counted_by(data_const_global_count_flex)]; +}; + +struct struct_global_count_flex { + int dummy; + // expected-error@+1{{count expression on struct field may only reference other fields of the same struct}} + int flex[__counted_by(global_count)]; +}; +struct struct_const_global_count_flex { + int dummy; + int flex[__counted_by(const_global_count)]; +}; + +void fun_update_data_const_global_count_flex(unsigned count) { + data_const_global_count_flex = 100; +} + +void fun_const_global_count(int *__counted_by(data_const_global_count) arg) { + int arr[10]; + arg = arr; +} + +// expected-error@+1{{count expression in function declaration may only reference parameters of that function}} +void fun_global_count(int *__counted_by(global_count) arg); + +// expected-error@+1{{count expression in function declaration may only reference parameters of that function}} +void fun_global_array_count(int arg[__counted_by(global_count)]); + +void fun_data_const_init() { + data_const_global_count = 10; +} + +void test_local_data_const_global_count(int *__bidi_indexable arg) { + int *__counted_by(data_const_global_count) local; + local = arg; +} + +void test_local_global_count(int *__bidi_indexable arg) { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by(global_count) local; +} + +void test_data_const_struct_init() { + struct struct_data_const_global_count s; // better be an error +} + +void test_data_const_struct_init2(int *__indexable arg) { + struct struct_data_const_global_count s = { arg }; +} + +void test_data_const_struct_init3(int *__indexable arg) { + struct struct_data_const_global_count s; // better be an error + s.buf = arg; +} + +void test_data_const_struct_init4(void *__bidi_indexable arg) { + struct struct_data_const_global_count *sp = + (struct struct_data_const_global_count *)arg; +} + +void test_global_const_struct_init(struct struct_const_global_count *sp, + int *__bidi_indexable arg) { + sp->buf = arg; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/counted_by_global_assign.c b/clang/test/BoundsSafety/Sema/counted_by_global_assign.c new file mode 100644 index 0000000000000..733c8c1081b72 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_global_assign.c @@ -0,0 +1,80 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +int len; +int *__counted_by(len) ptr; + +void test_len_assign() { + len = 0; + // expected-error@-1{{assignment to 'len' requires corresponding assignment to 'int *__single __counted_by(len)' (aka 'int *__single') 'ptr'; add self assignment 'ptr = ptr' if the value has not changed}} +} + +void test_ptr_assign(int *__bidi_indexable arg) { + ptr = arg; + // expected-error@-1{{assignment to 'int *__single __counted_by(len)' (aka 'int *__single') 'ptr' requires corresponding assignment to 'len'; add self assignment 'len = len' if the value has not changed}} +} + +void test_assign(int *__bidi_indexable arg) { + len = 1; + ptr = arg; +} + +extern int extlen; +extern int *__counted_by(extlen) extptr; + +void test_extlen_assign() { + extlen = 0; + // expected-error@-1{{assignment to 'extlen' requires corresponding assignment to 'int *__single __counted_by(extlen)' (aka 'int *__single') 'extptr'; add self assignment 'extptr = extptr' if the value has not changed}} +} + +void test_extptr_assign(int *__bidi_indexable arg) { + extptr = arg; + // expected-error@-1{{assignment to 'int *__single __counted_by(extlen)' (aka 'int *__single') 'extptr' requires corresponding assignment to 'extlen'; add self assignment 'extlen = extlen' if the value has not changed}} +} + +void test_ext_assign(int *__bidi_indexable arg) { + extlen = 1; + extptr = arg; +} + +int shared_len = 0; +int *__counted_by(shared_len) ptr2; +char *__counted_by(shared_len) ptr3; + +void test_shared_len_assign() { + // expected-error@+2{{assignment to 'shared_len' requires corresponding assignment to 'int *__single __counted_by(shared_len)' (aka 'int *__single') 'ptr2'; add self assignment 'ptr2 = ptr2' if the value has not changed}} + // expected-error@+1{{assignment to 'shared_len' requires corresponding assignment to 'char *__single __counted_by(shared_len)' (aka 'char *__single') 'ptr3'; add self assignment 'ptr3 = ptr3' if the value has not changed}} + shared_len = 0; +} + +void test_ptr2_assign(int *__bidi_indexable arg) { + // expected-error@+1{{assignment to 'int *__single __counted_by(shared_len)' (aka 'int *__single') 'ptr2' requires corresponding assignment to 'shared_len'; add self assignment 'shared_len = shared_len' if the value has not changed}} + ptr2 = arg; +} + +void test_ptr3_assign(char *__bidi_indexable arg) { + // expected-error@+1{{assignment to 'char *__single __counted_by(shared_len)' (aka 'char *__single') 'ptr3' requires corresponding assignment to 'shared_len'; add self assignment 'shared_len = shared_len' if the value has not changed}} + ptr3 = arg; +} + +void test_shared_len_ptr2_assign(int *__bidi_indexable arg) { + // expected-error@+1{{assignment to 'shared_len' requires corresponding assignment to 'char *__single __counted_by(shared_len)' (aka 'char *__single') 'ptr3'; add self assignment 'ptr3 = ptr3' if the value has not changed}} + shared_len = 1; + ptr2 = arg; +} + +void test_shared_len_ptr3_assign(char *__bidi_indexable arg) { + // expected-error@+1{{assignment to 'shared_len' requires corresponding assignment to 'int *__single __counted_by(shared_len)' (aka 'int *__single') 'ptr2'; add self assignment 'ptr2 = ptr2' if the value has not changed}} + shared_len = 1; + ptr3 = arg; +} + + +void test_shared_assign(int *__bidi_indexable arg1, + char *__bidi_indexable arg2) { + shared_len = 1; + ptr3 = arg2; + ptr2 = arg1; +} diff --git a/clang/test/BoundsSafety/Sema/counted_by_incdec.c b/clang/test/BoundsSafety/Sema/counted_by_incdec.c new file mode 100644 index 0000000000000..0d3da07e1b68a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_incdec.c @@ -0,0 +1,160 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +void Foo(int *__counted_by(*len) *ptr, int *len) { + (*ptr)++; // expected-note{{previously assigned here}} + // expected-error@+2{{assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*ptr' requires corresponding assignment to '*len'; add self assignment '*len = *len' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'ptr' must be simplified; keep only one of the assignments}} + ++(*ptr); +} + +void FooOrNull(int *__counted_by_or_null(*len) *ptr, int *len) { + (*ptr)++; // expected-note{{previously assigned here}} + // expected-error@+2{{assignment to 'int *__single __counted_by_or_null(*len)' (aka 'int *__single') '*ptr' requires corresponding assignment to '*len'; add self assignment '*len = *len' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'ptr' must be simplified; keep only one of the assignments}} + ++(*ptr); +} + +void Bar(int *__counted_by(*len) *ptr, int *len) { + *ptr = 0; + (*len)++; +} + +void BarOrNull(int *__counted_by_or_null(*len) *ptr, int *len) { + *ptr = 0; + (*len)++; +} + +void TestPtrPostIncrement(int *__counted_by(*len) *ptr, int *len) { + (*ptr)++; + (*len)--; +} + +void TestPtrPostIncrementOrNull(int *__counted_by_or_null(*len) *ptr, int *len) { + (*ptr)++; + (*len)--; +} + +void TestLenPostIncrementOrNull(int *__counted_by_or_null(*len) ptr, int *len) { + ptr = ptr; + (*len)++; // expected-error{{incrementing '*len' without updating 'ptr' always traps}} rdar://116470062 +} + +void TestLenPostIncrement(int *__counted_by(*len) ptr, int *len) { + ptr = ptr; + (*len)++; // expected-error{{incrementing '*len' without updating 'ptr' always traps}} +} + +void TestPtrPreDecrement(int *__counted_by(*len) *ptr, int *len) { + --(*ptr); // expected-error{{negative pointer arithmetic on '__counted_by' pointer always traps}} +} + +void TestPtrPreDecrementOrNull(int *__counted_by_or_null(*len) *ptr, int *len) { + --(*ptr); // expected-error{{negative pointer arithmetic on '__counted_by_or_null' pointer always traps}} +} + +typedef struct { + char *__counted_by(len1 + len2) buf; + unsigned len1; + unsigned len2; +} S; + +void TestMultipleCounts1(S *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2 = sp->len2; + sp->len1++; +} + +void TestMultipleCounts2(S *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2++; + sp->len1 = sp->len1; +} + +void TestMultipleCounts3(S *sp) { + sp->buf = sp->buf; + sp->len2++; // expected-error{{incrementing 'sp->len2' without updating 'sp->buf' always traps}} + sp->len1 = sp->len1; +} + +typedef struct { + char *__counted_by_or_null(len1 + len2) buf; + unsigned len1; + unsigned len2; +} SOrNull; + +void TestMultipleCounts1OrNull(SOrNull *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2 = sp->len2; + sp->len1++; +} + +void TestMultipleCounts2OrNull(S *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2++; + sp->len1 = sp->len1; +} + +void TestMultipleCounts3OrNull(S *sp) { + sp->buf = sp->buf; + sp->len2++; // expected-error{{incrementing 'sp->len2' without updating 'sp->buf' always traps}} + sp->len1 = sp->len1; +} + +typedef struct { + char *__counted_by(len) buf; + unsigned len; +} T; + +void Baz(T *tp) { + tp->buf = tp->buf; + tp->len++; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} +} + +void Qux(T *tp) { + ++tp->len; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} + tp->buf = tp->buf; +} + +void Quux(T *tp) { + tp->buf = tp->buf; + tp->len--; +} + +void Quuz(T *tp) { + tp->len--; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __counted_by(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} + +void Corge(T *tp) { + tp->len+=2; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __counted_by(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} + +typedef struct { + char *__counted_by_or_null(len) buf; + unsigned len; +} TOrNull; + +void BazOrNull(TOrNull *tp) { + tp->buf = tp->buf; + tp->len++; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} +} + +void QuxOrNull(TOrNull *tp) { + ++tp->len; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} + tp->buf = tp->buf; +} + +void QuuxOrNull(TOrNull *tp) { + tp->buf = tp->buf; + tp->len--; +} + +void QuuzOrNull(TOrNull *tp) { + tp->len--; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __counted_by_or_null(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} + +void CorgeOrNull(TOrNull *tp) { + tp->len+=2; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __counted_by_or_null(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} diff --git a/clang/test/BoundsSafety/Sema/counted_by_semantic_checks.c b/clang/test/BoundsSafety/Sema/counted_by_semantic_checks.c new file mode 100644 index 0000000000000..53a0c81ab98ec --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_semantic_checks.c @@ -0,0 +1,32 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +struct T1 { + int len; + int len2; + int *__counted_by(len) __counted_by(len+1) buf; // expected-error{{pointer cannot have more than one count attribute}} + int *__counted_by(len + len2) buf2; + int *__bidi_indexable __counted_by(len) buf3; // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} + int *__counted_by(len) __bidi_indexable buf4; // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} +}; + +// expected-note@+1{{'foo' declared here}} +int foo(void); + +struct T2 { + float flen; + int *__counted_by(flen) buf1; // expected-error{{attribute requires an integer type argument}} + int *__counted_by(foo()) buf2; // expected-error{{argument of '__counted_by' attribute can only reference function with 'const' attribute}} +}; + +int glen; + +int Test(int *__counted_by(glen) ptr); // expected-error{{count expression in function declaration may only reference parameters of that function}} + +int *__bidi_indexable __counted_by(glen) glob_buf1; // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} +int *__counted_by(glen) __bidi_indexable glob_buf2; // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} + +void foo1(int *__bidi_indexable __counted_by(glen) glob_buf1); // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} +void foo2(int *__counted_by(glen) __bidi_indexable glob_buf2); // expected-error{{pointer cannot be '__counted_by' and '__bidi_indexable' at the same time}} diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_array.c b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_array.c new file mode 100644 index 0000000000000..1dc360cb6fe96 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_array.c @@ -0,0 +1,120 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +// Test incomplete arrays + +//============================================================================== +// Extern declaration that is later redefined +//============================================================================== + + +extern int external_arr_len; +// expected-error@+1{{cannot apply '__counted_by' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by' instead?}} +extern char (* __counted_by(external_arr_len) incompleteArrayPtr)[]; // expected-note{{'incompleteArrayPtr' declared here}} +void use_incompleteArrayPtr_when_incomplete(void) { + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + char x = incompleteArrayPtr[0][0]; +} + +// Provide a complete definition with the array size defined +int external_arr_len; +// expected-error@+1{{conflicting '__counted_by' attribute with the previous variable declaration}} +char (* __counted_by(external_arr_len) incompleteArrayPtr)[4]; + +void use_incompleteArrayPtr_when_complete(void) { + char x = incompleteArrayPtr[0][0]; // OK? +} + +int external_arr_len2; +char (* __counted_by(external_arr_len2) CompleteArrayPtr)[4]; // OK + +//============================================================================== +// Global vars +//============================================================================== + +int global_arr_len; +// expected-error@+1{{cannot apply '__counted_by' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by' instead?}} +char (* __counted_by(global_arr_len) GlobalCBIncompleteArrayPtr)[]; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +char (* __counted_by_or_null(global_arr_len) GlobalCBONIncompleteArrayPtr)[]; + +char (* __counted_by(global_arr_len) GlobalCBCompleteArrayPtr)[2]; // OK +char (* __counted_by_or_null(global_arr_len) GlobalCBONCompleteArrayPtr)[2]; // OK + +char (* __counted_by(0) GlobalCBCompleteArrayPtrZeroCount)[2]; // OK +// expected-error@+1{{implicitly initializing 'GlobalCBCompleteArrayPtrOneCount' of type 'char (*__single __counted_by(1))[2]' (aka 'char (*__single)[2]') and count value of 1 with null always fails}} +char (* __counted_by(1) GlobalCBCompleteArrayPtrOneCount)[2]; + +//============================================================================== +// Local vars +//============================================================================== + +void local_cb(void) { + int size = 0; + // expected-error@+1{{cannot apply '__counted_by' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by' instead?}} + char (* __counted_by(size) local_implicit_init)[]; + + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + local_implicit_init[0]; + + int size2 = 0; + // expected-error@+1{{cannot apply '__counted_by' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by' instead?}} + char (* __counted_by(size2) local_explicit_init)[] = 0x0; +} + +void local_cbon(void) { + int size = 0; + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} + char (* __counted_by_or_null(size) local_implicit_init)[]; + + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + local_implicit_init[0]; + + int size2 = 0; + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} + char (* __counted_by_or_null(size2) local_explicit_init)[] = 0x0; +} + +//============================================================================== +// Parameters +//============================================================================== + +// expected-error@+1{{cannot apply '__counted_by' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by' instead?}} +void decl_param_cb(char (* __counted_by(size) p)[], int size) {} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +void decl_param_cbon(char (* __counted_by_or_null(size) p)[], int size) {} + +// expected-error@+1{{cannot apply '__counted_by' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by' instead?}} +void access_param_cb(char (* __counted_by(size) p)[], int size) { + void* local = p; + + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + p[0]; + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + p[0][0]; + + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + &p[0]; + + p = 0; // OK + size = 0; // OK +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'char (*)[]' because 'char[]' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +void access_param_cbon(char (* __counted_by_or_null(size) p)[], int size) { + // This doesn't show up as an error because error recovery for treats `p` as + // having type `char (*__single __sized_by_or_null(size))[]` + void* local = p; + + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + p[0]; + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + p[0][0]; + + // expected-error@+1{{subscript of pointer to incomplete type 'char[]'}} + &p[0]; + + p = 0; // OK + size = 0; +} diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_enum.c b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_enum.c new file mode 100644 index 0000000000000..b56f461c9eb60 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_enum.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +// Test incomplete enums + +extern int external_enum_len; +typedef enum incomplete_enum incomplete_enum_t; // expected-note 2{{consider providing a complete definition for 'enum incomplete_enum'}} +extern incomplete_enum_t* __counted_by(external_enum_len) incompleteEnumPtr; // OK +extern enum incomplete_enum* __counted_by(external_enum_len) incompleteEnumPtr2; // OK + +int global_enum_len; +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBEnumPtrImplicitInit' with type 'enum incomplete_enum *__single __counted_by(global_enum_len)' (aka 'enum incomplete_enum *__single') because the pointee type 'enum incomplete_enum' is incomplete; consider providing a complete definition for 'enum incomplete_enum' before this definition or using the '__sized_by' attribute}} +enum incomplete_enum* __counted_by(global_enum_len) GlobalCBEnumPtrImplicitInit; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONEnumPtrImplicitInit' with type 'enum incomplete_enum *__single __counted_by_or_null(global_enum_len)' (aka 'enum incomplete_enum *__single') because the pointee type 'enum incomplete_enum' is incomplete; consider providing a complete definition for 'enum incomplete_enum' before this definition or using the '__sized_by_or_null' attribute}} +enum incomplete_enum* __counted_by_or_null(global_enum_len) GlobalCBONEnumPtrImplicitInit; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + + +// Unions are handled like structs for the diagnostics so the testing for structs +// should mean testing other combinations should be unnecessary. diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct.c b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct.c new file mode 100644 index 0000000000000..4076f36f1f48b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct.c @@ -0,0 +1,1444 @@ + +// TODO: We should get the same diagnostics with/without return_size (rdar://138982703) +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s +#include +// Test diagnostics on _counted_by(_or_null) pointers with an incomplete struct +// pointee type. + +// NOTE: For a typedef the source location is of the underlying type instead of +// the typedef. This seems like the right behavior because the typedef isn't the +// forward declaration, `struct IncompleteStructTy` is. +// +// expected-note@+1 62{{consider providing a complete definition for 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy')}} +struct IncompleteStructTy; // expected-note 153{{consider providing a complete definition for 'struct IncompleteStructTy'}} + +typedef struct IncompleteStructTy Incomplete_Struct_t; + +//------------------------------------------------------------------------------ +// Attribute on parameters +//------------------------------------------------------------------------------ + +// On declarations its ok to use the attribute +void no_consume_ok( + struct IncompleteStructTy* __counted_by(size) cb, + struct IncompleteStructTy* __counted_by_or_null(size) cbon, + int size); // OK + +// Using the attribute on parameters on a function **definition** is not allowed. +void no_consume_ok( + // expected-error@+1{{cannot apply '__counted_by' attribute to parameter 'cb' with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} + struct IncompleteStructTy* __counted_by(size) cb, // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to parameter 'cbon' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} + struct IncompleteStructTy* __counted_by_or_null(size) cbon, // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + int size) { + +} + +void no_consume_ok_unnamed_param( + struct IncompleteStructTy* __counted_by(size), + struct IncompleteStructTy* __counted_by_or_null(size), + int size); // OK + +void no_consume_ok_unnamed_param( + // expected-error@+2{{cannot apply '__counted_by' attribute to parameter with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-warning@+1{{omitting the parameter name in a function definition is a C23 extension}} + struct IncompleteStructTy* __counted_by(size), // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + // expected-error@+2{{cannot apply '__counted_by_or_null' attribute to parameter with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-warning@+1{{omitting the parameter name in a function definition is a C23 extension}} + struct IncompleteStructTy* __counted_by_or_null(size), // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + int size) { + +} + +void consume_cb(struct IncompleteStructTy* __counted_by(size_cb), int size_cb); +void consume_cbon(struct IncompleteStructTy* __counted_by_or_null(size_cbon), int size_cbon); + +void consume_param_read_write( + // expected-error@+1{{cannot apply '__counted_by' attribute to parameter 'cb' with type 'struct IncompleteStructTy *__single __counted_by(size_cb)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} + struct IncompleteStructTy* __counted_by(size_cb) cb, // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to parameter 'cbon' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size_cbon)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} + struct IncompleteStructTy* __counted_by_or_null(size_cbon) cbon, // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + int size_cb, + int size_cbon) { + + // There shouldn't be diagnostics on the uses because the parameters are marked + // as invalid. + + // Read + struct IncompleteStructTy* local = cb; + local = cb; + local =&cb[1]; + consume_cb(cb); + + // Write + cb = 0x0; + // TODO: This diagnostic should not be firing. rdar://133001202 + // expected-error@+1{{assignment to 'size_cb' requires corresponding assignment to 'struct IncompleteStructTy *__single __counted_by(size_cb)' (aka 'struct IncompleteStructTy *__single') 'cb'; add self assignment 'cb = cb' if the value has not changed}} + size_cb = 0; + + + // Read + struct IncompleteStructTy* local2 = cbon; + local2 = cbon; + local2 =&cbon[1]; + consume_cbon(cbon); + + // Write + cbon = 0x0; + // TODO: This diagnostic should not be firing. rdar://133001202 + // expected-error@+1{{assignment to 'size_cbon' requires corresponding assignment to 'struct IncompleteStructTy *__single __counted_by_or_null(size_cbon)' (aka 'struct IncompleteStructTy *__single') 'cbon'; add self assignment 'cbon = cbon' if the value has not changed}} + size_cbon = 0; +} + +// These errors seem prevent emitting any further diagnostics about the attributes. +void no_consume_default_assign( + // expected-error@+1{{C does not support default arguments}} + struct IncompleteStructTy* __counted_by(size) cb = 0x0, + // expected-error@+1{{C does not support default arguments}} + struct IncompleteStructTy* __counted_by_or_null(size) cbon = 0x0, + int size) { + +} + +//------------------------------------------------------------------------------ +// Attribute on parameters with nested attributes +//------------------------------------------------------------------------------ +void consume_param_nested( + struct IncompleteStructTy* __counted_by(size1)* cb, // expected-note 3{{consider using '__sized_by' instead of '__counted_by'}} + struct IncompleteStructTy* __counted_by_or_null(size2)* cbon, // expected-note 3{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + int size1, int size2) { + + // Surprisingly `&cb[0]` doesn't count as a use. + struct IncompleteStructTy** local = &cb[0]; + + // expected-error@+1{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + local = cb; + + // expected-error@+1{{cannot assign to object with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(size1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + *cb = 0x0; + size1 = 0; + + // expected-error@+1{{cannot use 'cb[0]' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(size1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + void* read_cb = cb[0]; + // expected-error@+1{{cannot use '*cb' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(size1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_cb(*cb, size1); + + // expected-error@+1{{not allowed to change out parameter with dependent count}} + cb = 0x0; + + // Surprisingly `&cbon[0]` doesn't count as a use. + struct IncompleteStructTy** local2 = &cbon[0]; + // expected-error@+1{{pointer with '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + local = cbon; + + // expected-error@+1{{cannot assign to object with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(size2)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + *cbon = 0x0; + size2 = 0; + + // expected-error@+1{{cannot use 'cbon[0]' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(size2)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + void* read_cbon = cbon[0]; + // expected-error@+1{{cannot use '*cbon' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(size2)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_cbon(*cbon, size2); + + // expected-error@+1{{not allowed to change out parameter with dependent count}} + cbon = 0x0; +} + +//------------------------------------------------------------------------------ +// Attribute on return type of called function +//------------------------------------------------------------------------------ + +// expected-note@+1{{consider using '__sized_by' instead of '__counted_by'}} +struct IncompleteStructTy* __counted_by(size) ret_cb_IncompleteStructTy(int size); // OK +// expected-note@+1{{consider using '__sized_by' instead of '__counted_by'}} +Incomplete_Struct_t* __counted_by(size) ret_cb_IncompleteStructTy_typedef(int size); // OK +// expected-note@+1{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +struct IncompleteStructTy* __counted_by_or_null(size) ret_cbon_IncompleteStructTy(int size); // OK +// expected-note@+1{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +Incomplete_Struct_t* __counted_by_or_null(size) ret_cbon_IncompleteStructTy_typedef(int size); // OK + +// expected-note@+1{{consider using '__sized_by' instead of '__counted_by'}} +struct IncompleteStructTy* __counted_by(1) ret_cb_IncompleteStructTy_const_count_one(void); // OK +// expected-note@+1{{consider using '__sized_by' instead of '__counted_by'}} +Incomplete_Struct_t* __counted_by(1) ret_cb_IncompleteStructTy_typedef_const_count_one(void); // OK +// expected-note@+1{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +struct IncompleteStructTy* __counted_by_or_null(1) ret_cbon_IncompleteStructTy_const_count_one(void); // OK +// expected-note@+1{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +Incomplete_Struct_t* __counted_by_or_null(1) ret_cbon_IncompleteStructTy_typedef_const_count_one(void); // OK + +void call_fn_returns_incomplete_pointee(void) { + int size = 0; + // expected-error@+1{{cannot call 'ret_cb_IncompleteStructTy' with '__counted_by' attributed return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + ret_cb_IncompleteStructTy(size); + // expected-error@+1{{cannot call 'ret_cb_IncompleteStructTy_typedef' with '__counted_by' attributed return type 'Incomplete_Struct_t *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + ret_cb_IncompleteStructTy_typedef(size); + // expected-error@+1{{cannot call 'ret_cbon_IncompleteStructTy' with '__counted_by_or_null' attributed return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + ret_cbon_IncompleteStructTy(size); + // expected-error@+1{{cannot call 'ret_cbon_IncompleteStructTy_typedef' with '__counted_by_or_null' attributed return type 'Incomplete_Struct_t *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + ret_cbon_IncompleteStructTy_typedef(size); + + // expected-error@+1{{cannot call 'ret_cb_IncompleteStructTy_const_count_one' with '__counted_by' attributed return type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + ret_cb_IncompleteStructTy_const_count_one(); + // expected-error@+1{{cannot call 'ret_cb_IncompleteStructTy_typedef_const_count_one' with '__counted_by' attributed return type 'Incomplete_Struct_t *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + ret_cb_IncompleteStructTy_typedef_const_count_one(); + // expected-error@+1{{cannot call 'ret_cbon_IncompleteStructTy_const_count_one' with '__counted_by_or_null' attributed return type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + ret_cbon_IncompleteStructTy_const_count_one(); + // expected-error@+1{{cannot call 'ret_cbon_IncompleteStructTy_typedef_const_count_one' with '__counted_by_or_null' attributed return type 'Incomplete_Struct_t *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + ret_cbon_IncompleteStructTy_typedef_const_count_one(); +} + +//------------------------------------------------------------------------------ +// Attribute on return type in function declaration +//------------------------------------------------------------------------------ + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by(size) // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} + consume_param_and_return_cb(int size) { + // expected-error@+1{{cannot return '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + return 0x0; +} + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by(size) // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + consume_param_and_return_cb_missing_return(int size) { + // missing return statement +} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by_or_null(size) // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + consume_param_and_return_cbon_missing_return(int size) { + // missing return statement +} + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by(size) // expected-note 3{{consider using '__sized_by' instead of '__counted_by'}} + consume_param_and_return_cb_multiple_returns(int size) { + if (size == 0) + // expected-error@+1{{cannot return '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + return 0x0; + + // expected-error@+1{{cannot return '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + return 0x0; +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by_or_null(size) // expected-note 3{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + consume_param_and_return_cbon_multiple_returns(int size) { + if (size == 0) + // expected-error@+1{{cannot return '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + return 0x0; + + // expected-error@+1{{cannot return '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + return 0x0; +} + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by(1) // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} + consume_param_and_return_cb_const_count_1(int size) { + // rs-error@+2{{returning null from a function with result type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 always fails}} + // expected-error@+1{{cannot return '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + return 0x0; +} + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by(size) // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} +consume_param_and_return_cb_single_forge(int size) { + // rs-warning@+2{{count value is not statically known: returning 'struct IncompleteStructTy *__single' from a function with result type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') is invalid for any count other than 0 or 1}} + // expected-error@+1{{cannot return '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + return __unsafe_forge_single(struct IncompleteStructTy*, 0x0); +} + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by(size) // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} + consume_param_and_return_cb_single_forge_bidi(int size) { + // expected-error@+1{{cannot return '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + return __unsafe_forge_bidi_indexable(struct IncompleteStructTy*, 0x0, 4); +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by_or_null(size) // expected-note 2{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + consume_param_and_return_cbon(int size) { + // TODO: We should consider allowing this because the assignment of nullptr + // means the type size isn't needed (rdar://129424354). + // expected-error@+1{{cannot return '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + return 0x0; +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by_or_null(size) // expected-note 2{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +consume_param_and_return_cbon_single_forge(int size) { + // rs-warning@+2{{count value is not statically known: returning 'struct IncompleteStructTy *__single' from a function with result type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') is invalid for any count other than 0 or 1 unless the pointer is null}} + // expected-error@+1{{cannot return '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + return __unsafe_forge_single(struct IncompleteStructTy*, 0x0); +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by_or_null(size) // expected-note 2{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + consume_param_and_return_cbon_single_forge_bidi(int size) { + // expected-error@+1{{cannot return '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + return __unsafe_forge_bidi_indexable(struct IncompleteStructTy*, 0x0, 4); +} + +// Test typedef as the incomplete pointee type +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'Incomplete_Struct_t *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +Incomplete_Struct_t* __counted_by(size) // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} + consume_param_and_return_cb_typedef(int size) { + // expected-error@+1{{cannot return '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + return 0x0; +} + +// Check Incomplete type diagnostic and bad conversion diagnostics both emitted on return + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by(size) // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} + consume_param_and_return_cb_bad_conversion(int size) { + // expected-error@+2{{cannot return '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + return 0x1; +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by_or_null(size) // expected-note 2{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + consume_param_and_return_cbon_bad_conversion(int size) { + // expected-error@+2{{cannot return '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + return 0x1; +} + +//------------------------------------------------------------------------------ +// Pass Arguments to parameters with attribute. +//------------------------------------------------------------------------------ +void consume_incomplete_cb(struct IncompleteStructTy* __counted_by(size) c, int size); // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +void consume_incomplete_cb_unnamed(struct IncompleteStructTy* __counted_by(size), int size); // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +typedef void consume_incomplete_cb_t(struct IncompleteStructTy* __counted_by(size) c, int size); // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + +void call_consume_incomplete_cb(consume_incomplete_cb_t indirect_call) { + // expected-error@+1{{cannot pass argument to parameter 'c' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_incomplete_cb(__unsafe_forge_single(struct IncompleteStructTy*, 0x4), 1); + // expected-error@+1{{cannot pass argument to parameter with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_incomplete_cb_unnamed(__unsafe_forge_single(struct IncompleteStructTy*, 0x4), 1); + + // expected-error@+1{{cannot pass argument to parameter with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + indirect_call(__unsafe_forge_single(struct IncompleteStructTy*, 0x4), 1); +} + +void consume_incomplete_cb_const_count_1(struct IncompleteStructTy* __counted_by(1) c); // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +void consume_incomplete_cb_unnamed_const_count_1(struct IncompleteStructTy* __counted_by(1)); // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +typedef void consume_incomplete_cb_const_count_1_t(struct IncompleteStructTy* __counted_by(1) c); // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + + +void call_consume_incomplete_cb_const_count_1(consume_incomplete_cb_const_count_1_t indirect_call) { + // expected-error@+1{{cannot pass argument to parameter 'c' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_incomplete_cb_const_count_1(__unsafe_forge_single(struct IncompleteStructTy*, 0x4)); + // expected-error@+1{{cannot pass argument to parameter with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_incomplete_cb_unnamed_const_count_1(__unsafe_forge_single(struct IncompleteStructTy*, 0x4)); + + // expected-error@+1{{cannot pass argument to parameter with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + indirect_call(__unsafe_forge_single(struct IncompleteStructTy*, 0x4)); +} + +void consume_incomplete_cbon(struct IncompleteStructTy* __counted_by_or_null(size) c, int size); // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +void consume_incomplete_cbon_unnamed(struct IncompleteStructTy* __counted_by_or_null(size), int size); // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +typedef void consume_incomplete_cbon_t(struct IncompleteStructTy* __counted_by_or_null(size) c, int size); // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + +void call_consume_incomplete_cbon(consume_incomplete_cbon_t indirect_call) { + // expected-error@+1{{cannot pass argument to parameter 'c' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_incomplete_cbon(__unsafe_forge_single(struct IncompleteStructTy*, 0x4), 1); + // expected-error@+1{{cannot pass argument to parameter with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_incomplete_cbon_unnamed(__unsafe_forge_single(struct IncompleteStructTy*, 0x4), 1); + + // expected-error@+1{{cannot pass argument to parameter with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + indirect_call(__unsafe_forge_single(struct IncompleteStructTy*, 0x4), 1); +} + + +// expected-error@+1{{cannot apply '__counted_by' attribute to parameter 'c' with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +void wrap_consume_incomplete_cb(struct IncompleteStructTy* __counted_by(size) c, // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + int size, consume_incomplete_cb_t indirect_call) { + // TODO: We should consider allowing this case. rdar://132031085 + // + // This case technically doesn't require any bounds-checks because: + // + // 1. We assume the attribute on `c` is correct because we assume that the + // caller to `wrap_consume_incomplete_cb` already performed bounds-checks. + // 2. uses of `c` and `size` in this function just pass them along to + // functions with the same kind of count expression. This means the bounds + // checks performed at the call to `wrap_consume_incomplete_cb` being true + // imply that all the bounds checks that would be performed here should + // pass. One exception to this would be if the call to + // `wrap_consume_incomplete_cb` was performed from non-bounds-safety code. + // + consume_incomplete_cb(c, size); + consume_incomplete_cb_unnamed(c, size); + indirect_call(c, size); +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to parameter 'c' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} +void wrap_consume_incomplete_cbon(struct IncompleteStructTy* __counted_by_or_null(size) c, // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + int size, consume_incomplete_cbon_t indirect_call) { + // TODO: We should consider allowing this case. rdar://132031085 + // + // This case technically doesn't require any bounds-checks because: + // + // 1. We assume the attribute on `c` is correct because we assume that the + // caller to `wrap_consume_incomplete_cb` already performed bounds-checks. + // 2. uses of `c` and `size` in this function just pass them along to + // functions with the same kind of count expression. This means the bounds + // checks performed at the call to `wrap_consume_incomplete_cb` being true + // imply that all the bounds checks that would be performed here should + // pass. One exception to this would be if the call to + // `wrap_consume_incomplete_cb` was performed from non-bounds-safety code. + // + consume_incomplete_cbon(c, size); + consume_incomplete_cbon_unnamed(c, size); + indirect_call(c, size); +} + +//------------------------------------------------------------------------------ +// Passing to Parameters with attributes on nested pointer +//------------------------------------------------------------------------------ +void consume_incomplete_cb_nested(struct IncompleteStructTy* __counted_by(*size)* out, int* size); +void consume_incomplete_cb_unnamed_nested(struct IncompleteStructTy* __counted_by(*size)*, int* size); +typedef void consume_incomplete_cb_nested_t(struct IncompleteStructTy* __counted_by(*size)* c, int* size); + +struct PtrAndCountCB { + int size; + struct IncompleteStructTy* __counted_by(size) ptr; +}; + +extern int cb_global_count; +extern struct IncompleteStructTy* __counted_by(cb_global_count) cb_global; + +void call_consume_incomplete_cb_nested(consume_incomplete_cb_nested_t indirect_call, struct PtrAndCountCB* ptr_and_count) { + // Note: + // * Uses of `&cb_global` `&(ptr_and_count->ptr)` currently don't generate errors + // because only the outer most + // pointer is checked. + // * Calls to the functions don't generate errors because only the outer most + // pointer is checked in the parameter types. + // + // This is ok because currently we don't checks at call sites to functions + // with indirect __counted_by parameters. Therefore the size of + // `struct IncompleteStructTy` isn't needed. + consume_incomplete_cb_nested(&cb_global, &cb_global_count); + consume_incomplete_cb_unnamed_nested(&cb_global, &cb_global_count); + indirect_call(&cb_global, &cb_global_count); + consume_incomplete_cb_nested(&(ptr_and_count->ptr), &(ptr_and_count->size)); // OK + consume_incomplete_cb_unnamed_nested(&(ptr_and_count->ptr), &(ptr_and_count->size)); // OK + indirect_call(&(ptr_and_count->ptr), &(ptr_and_count->size)); // OK +} + +void consume_incomplete_cbon_nested(struct IncompleteStructTy* __counted_by_or_null(*size)* out, int* size); +void consume_incomplete_cbon_unnamed_nested(struct IncompleteStructTy* __counted_by_or_null(*size)*, int* size); +typedef void consume_incomplete_cbon_nested_t(struct IncompleteStructTy* __counted_by_or_null(*size)* c, int* size); + +extern int cbon_global_count; +extern struct IncompleteStructTy* __counted_by_or_null(cbon_global_count) cbon_global; + +struct PtrAndCountCBON { + int size; + struct IncompleteStructTy* __counted_by_or_null(size) ptr; +}; + +void call_consume_incomplete_cbon_nested(consume_incomplete_cbon_nested_t indirect_call, struct PtrAndCountCBON* ptr_and_count) { + // Note: + // * Uses of `&cbon_global` `&(ptr_and_count->ptr)` currently don't generate errors + // because only the outer most + // pointer is checked. + // * Calls to the functions don't generate errors because only the outer most + // pointer is checked in the parameter types. + // + // This is ok because currently we don't checks at call sites to functions + // with indirect __counted_by parameters. Therefore the size of + // `struct IncompleteStructTy` isn't needed. + consume_incomplete_cbon_nested(&cbon_global, &cbon_global_count); + consume_incomplete_cbon_unnamed_nested(&cbon_global, &cbon_global_count); + indirect_call(&cbon_global, &cbon_global_count); + consume_incomplete_cbon_nested(&(ptr_and_count->ptr), &(ptr_and_count->size)); // OK + consume_incomplete_cbon_unnamed_nested(&(ptr_and_count->ptr), &(ptr_and_count->size)); // OK + indirect_call(&(ptr_and_count->ptr), &(ptr_and_count->size)); // OK +} + +//------------------------------------------------------------------------------ +// __counted_by/__counted_by_or_null on struct members +//------------------------------------------------------------------------------ + +struct BuffersCBTyNotUsed { + int count; + struct IncompleteStructTy* __counted_by(count) buffer; // OK + Incomplete_Struct_t* __counted_by(count) buffer_typedef; // OK +}; +struct BuffersCBTy { + int count; + int count_typedef; + struct IncompleteStructTy* __counted_by(count) buffer; // expected-note 21{{consider using '__sized_by' instead of '__counted_by'}} + Incomplete_Struct_t* __counted_by(count_typedef) buffer_typedef; // expected-note 21{{consider using '__sized_by' instead of '__counted_by'}} +}; + +void side_effect(void); + +void AssignToBuffersCBTy(struct BuffersCBTy* b) { + // Check that the diagnostic about missing assignment to the count also shows + + // expected-error@+2{{cannot assign to 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{assignment to 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') 'b->buffer' requires corresponding assignment to 'b->count'; add self assignment 'b->count = b->count' if the value has not changed}} + b->buffer = 0x0; + side_effect(); + // expected-error@+2{{cannot assign to 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + // expected-error@+1{{assignment to 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') 'b->buffer_typedef' requires corresponding assignment to 'b->count_typedef'; add self assignment 'b->count_typedef = b->count_typedef' if the value has not changed}} + b->buffer_typedef = 0x0; + + // Diagnostic about missing assignment to count should not appear. + side_effect(); + b->count = 0; + // expected-error@+1{{cannot assign to 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + b->buffer = 0x0; + side_effect(); + // expected-error@+1{{cannot assign to 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + b->buffer_typedef = 0x0; + b->count_typedef = 0; +} + + +struct IncompleteStructTy* ReturnBufferCBTyMember(struct BuffersCBTy* b) { + // expected-error@+1{{cannot use 'b->buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + return b->buffer; +} + +Incomplete_Struct_t* ReturnBufferCBTyMemberTypeDef(struct BuffersCBTy* b) { + // expected-error@+1{{cannot use 'b->buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + return b->buffer_typedef; +} + +struct BuffersCBONTyNotUsed { + int count; + struct IncompleteStructTy* __counted_by_or_null(count) buffer; // OK + Incomplete_Struct_t* __counted_by_or_null(count) buffer_typedef; // OK +}; +struct BuffersCBONTy { + int count; + int count_typedef; + struct IncompleteStructTy* __counted_by_or_null(count) buffer; // expected-note 21{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + Incomplete_Struct_t* __counted_by_or_null(count_typedef) buffer_typedef; // expected-note 21{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +}; + +void AssignToBuffersCBONTy(struct BuffersCBONTy* b) { + // Check that the diagnostic about missing assignment to the count also shows + + // expected-error@+2{{cannot assign to 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{assignment to 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') 'b->buffer' requires corresponding assignment to 'b->count'; add self assignment 'b->count = b->count' if the value has not changed}} + b->buffer = 0x0; + side_effect(); + // expected-error@+2{{cannot assign to 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + // expected-error@+1{{assignment to 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') 'b->buffer_typedef' requires corresponding assignment to 'b->count_typedef'; add self assignment 'b->count_typedef = b->count_typedef' if the value has not changed}} + b->buffer_typedef = 0x0; + + // Diagnostic about missing assignment to count should not appear. + side_effect(); + b->count = 0; + // expected-error@+1{{cannot assign to 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + b->buffer = 0x0; + side_effect(); + // expected-error@+1{{cannot assign to 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + b->buffer_typedef = 0x0; + b->count_typedef = 0; +} + +struct IncompleteStructTy* ReturnBufferCBONTyMember(struct BuffersCBONTy* b) { + // expected-error@+1{{cannot use 'b->buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + return b->buffer; +} + +Incomplete_Struct_t* ReturnBufferCBONTyMemberTypeDef(struct BuffersCBONTy* b) { + // expected-error@+1{{cannot use 'b->buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + return b->buffer_typedef; +} + +//------------------------------------------------------------------------------ +// Initialization of struct members with counted_by/counted_by_or_null +//------------------------------------------------------------------------------ + +// TODO: We should consider allowing implicit and explicit zero initialization +// of __counted_by_or_null pointers. rdar://129424354 + +struct BufferCBNonZeroConstCountTy { + int extra_field; + struct IncompleteStructTy* __counted_by(1) ptr; // expected-note 4{{consider using '__sized_by' instead of '__counted_by'}} +}; + +struct BufferCBNonZeroConstCountFlippedFieldOrderTy { + struct IncompleteStructTy* __counted_by(1) ptr; // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} + int extra_field; +}; + +struct BufferCBNonZeroDynCountTy { + unsigned int count; + struct IncompleteStructTy* __counted_by(count+1) ptr; // expected-note 5{{consider using '__sized_by' instead of '__counted_by'}} +}; + +union BufferCBOrOther { + struct BuffersCBTy buf; + int other; +}; + +void InitBuffersCBTy(int size) { + // Designated initializers + struct BuffersCBTy desig_init_0 = { + .count = size, + .count_typedef = size, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + .buffer = 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + .buffer_typedef = 0x0 + }; + + struct BuffersCBTy desig_init_1 = { + // .count and .count_typedef not explicitly initialized but are implicitly zero initialized + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + .buffer = 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + .buffer_typedef = 0x0 + }; + + struct BuffersCBTy desig_init_partial = { + .count = size, + .count_typedef = size, + // .buffer and .buffer_typedef are not explicit initialized but are implicitly zero initialized + }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + struct BuffersCBTy implicit_all_zero_init = {0}; // Implicit field init + // expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + // non-designated initializer + struct BuffersCBTy non_design_init_0 = { + 0, + 0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + 0x0 + }; + + struct BuffersCBTy non_design_init_1 = { 0, 0 }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + struct BuffersCBTy desig_init_invalid_count = { + .count = 1, + // expected-error@+2{{cannot initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{initializing 'desig_init_invalid_count.buffer' of type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + .buffer = 0x0 + }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + struct BuffersCBTy desig_init_invalid_count_partial = { + .count = 1 + }; + // expected-error@-1{{implicitly initializing 'desig_init_invalid_count_partial.buffer' of type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-3{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + struct BuffersCBTy non_desig_init_invalid_count = { + 1, + 0, + // expected-error@+2{{cannot initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{initializing 'non_desig_init_invalid_count.buffer' of type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + 0x0 + }; + + + struct BuffersCBTy non_desig_init_invalid_count_partial = {1}; + // expected-error@-1{{implicitly initializing 'non_desig_init_invalid_count_partial.buffer' of type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-3{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + // Cases where zero-init would create an invalid count + struct BufferCBNonZeroConstCountTy design_init_const_count = { + // expected-error@+2{{cannot initialize 'BufferCBNonZeroConstCountTy::ptr' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{initializing 'design_init_const_count.ptr' of type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + .ptr = 0x0, + .extra_field = 0 + }; + + struct BufferCBNonZeroConstCountTy design_init_const_count_partial_explicit = { + // .ptr is implicitly zero initialized + .extra_field = 0x0 + }; + // expected-error@-1{{implicitly initializing 'design_init_const_count_partial_explicit.ptr' of type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + // expected-error@-2{{cannot implicitly initialize 'BufferCBNonZeroConstCountTy::ptr' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + + struct BufferCBNonZeroConstCountTy implicit_all_zero_init_const_count = {0}; + // expected-error@-1{{implicitly initializing 'implicit_all_zero_init_const_count.ptr' of type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + // expected-error@-2{{cannot implicitly initialize 'BufferCBNonZeroConstCountTy::ptr' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + + // When the ptr comes first it's seen as an explicit assignment when we write ` = {0}` so we get the incomplete pointee type error diagnostic + // expected-error@+2{{cannot initialize 'BufferCBNonZeroConstCountFlippedFieldOrderTy::ptr' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{initializing 'implicit_all_zero_init_const_count_ptr_init.ptr' of type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + struct BufferCBNonZeroConstCountFlippedFieldOrderTy implicit_all_zero_init_const_count_ptr_init = {0}; + + struct BufferCBNonZeroDynCountTy design_init_non_zero_dyn_count = { + .count = 0x0, + // expected-error@+2{{cannot initialize 'BufferCBNonZeroDynCountTy::ptr' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{initializing 'design_init_non_zero_dyn_count.ptr' of type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + .ptr = 0x0 + }; + + struct BufferCBNonZeroDynCountTy design_init_non_zero_dyn_count_partial_init = { + // count is implicitly zero + // expected-error@+2{{cannot initialize 'BufferCBNonZeroDynCountTy::ptr' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{initializing 'design_init_non_zero_dyn_count_partial_init.ptr' of type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + .ptr = 0x0 + }; + + struct BufferCBNonZeroDynCountTy design_init_non_zero_dyn_count_partial_init2 = { + // ptr is implicitly zero initialized + .count = 0x0 + }; + // expected-error@-1{{implicitly initializing 'design_init_non_zero_dyn_count_partial_init2.ptr' of type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + // expected-error@-2{{cannot implicitly initialize 'BufferCBNonZeroDynCountTy::ptr' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + + struct BufferCBNonZeroDynCountTy implicit_all_zero_init_non_zero_dyn_count = {0}; + // expected-error@-1{{implicitly initializing 'implicit_all_zero_init_non_zero_dyn_count.ptr' of type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + // expected-error@-2{{cannot implicitly initialize 'BufferCBNonZeroDynCountTy::ptr' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + + struct BufferCBNonZeroDynCountTy non_desig_init_non_zero_dyn_count = { + 0, + // expected-error@+2{{cannot initialize 'BufferCBNonZeroDynCountTy::ptr' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{initializing 'non_desig_init_non_zero_dyn_count.ptr' of type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + 0 + }; + + // Struct inside a union + union BufferCBOrOther UnionDesignInitOther = {.other = 0x0 }; + union BufferCBOrOther UnionZeroInit = {0}; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + union BufferCBOrOther UnionDesignInitBufZeroInitStructFields = {.buf = {0}}; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + union BufferCBOrOther UnionDesignInitBufDesignInitStructFields = {.buf = {.count = 0, .buffer = 0x0}}; + // expected-error@-1{{cannot initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +} + +struct BuffersCBTy GlobalBuffersCBTy_design_init = { + .count = 0, + .count_typedef = 0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + .buffer = 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + .buffer_typedef = 0 +}; + +struct BuffersCBTy GlobalBuffersCBTy_non_design_init = { + 0, + 0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + 0, + // expected-error@+1{{cannot initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + 0 +}; + +struct BuffersCBTy GlobalBuffersCBTy_design_init_partial = { + .count = 0, + .count_typedef = 0 + // buffer and buffer_typedef are implicitly zero initialized +}; +// expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +// expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + +struct BuffersCBTy GlobalBuffersCBTy_non_design_init_partial = { + 0, + 0, + // buffer and buffer_typedef are implicitly zero initialized +}; +// expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +// expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + +struct BuffersCBTy GlobalBuffersCBTy_all_zero_init = {0}; +// expected-error@-1{{cannot implicitly initialize 'BuffersCBTy::buffer' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +// expected-error@-2{{cannot implicitly initialize 'BuffersCBTy::buffer_typedef' with '__counted_by' attributed type 'Incomplete_Struct_t *__single __counted_by(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + +// expected-error@+2{{cannot initialize 'BufferCBNonZeroConstCountFlippedFieldOrderTy::ptr' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +// expected-error@+1{{initializing 'GlobalBuffersCBTy_implicit_all_zero_init_const_count_ptr_init.ptr' of type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} +struct BufferCBNonZeroConstCountFlippedFieldOrderTy GlobalBuffersCBTy_implicit_all_zero_init_const_count_ptr_init = {0}; + +struct BufferCBNonZeroConstCountTy GlobalBuffersCBTy_const_non_zero_count = { + // expected-error@+2{{cannot initialize 'BufferCBNonZeroConstCountTy::ptr' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{initializing 'GlobalBuffersCBTy_const_non_zero_count.ptr' of type 'struct IncompleteStructTy *__single __counted_by(1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} + .ptr = 0x0 +}; + +// counted_by_or_null variants + + +union BufferCBONOrOther { + struct BuffersCBONTy buf; + int other; +}; + +struct BufferCBONNonZeroConstCountTy { + int extra_field; + struct IncompleteStructTy* __counted_by_or_null(1) ptr; // expected-note 4{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +}; + +struct BufferCBONNonZeroConstCountFlippedFieldOrderTy { + struct IncompleteStructTy* __counted_by_or_null(1) ptr; // expected-note 2{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + int extra_field; +}; + +struct BufferCBONNonZeroDynCountTy { + unsigned int count; + struct IncompleteStructTy* __counted_by_or_null(count+1) ptr; // expected-note 5{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +}; + +void InitBuffersCBONTy(int size) { + // Designated initializers + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy desig_init_0 = { + .count = size, + .count_typedef = size, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + .buffer = 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + .buffer_typedef = 0x0 + }; + + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy desig_init_1 = { + // .count and .count_typedef not explicitly initialized but are implicitly zero initialized + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + .buffer = 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + .buffer_typedef = 0x0 + }; + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy desig_init_partial = { + .count = size, + .count_typedef = size, + // .buffer and .buffer_typedef are not explicit initialized but are implicitly zero initialized + }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy implicit_all_zero_init = {0}; // Implicit field init + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + // non-designated initializer + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy non_design_init_0 = { + 0, + 0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + 0x0 + }; + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy non_design_init_1 = { 0, 0 }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + // TODO: Explicit and implicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy desig_init_invalid_count = { + .count = 1, + // expected-error@+2{{cannot initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // + .buffer = 0x0 + }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy desig_init_explicit_non_zero_count_partial = { + .count = 1 + }; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy non_desig_init_invalid_count = { + 1, + 0, + // expected-error@+2{{cannot initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // + 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + 0x0 + }; + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BuffersCBONTy non_desig_init_non_zerocount_partial = {1}; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + // Cases where zero-init would create an invalid count + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroConstCountTy design_init_const_count = { + // expected-error@+2{{cannot initialize 'BufferCBONNonZeroConstCountTy::ptr' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // + .ptr = 0x0, + .extra_field = 0 + }; + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroConstCountTy design_init_const_count_partial_explicit = { + // .ptr is implicitly zero initialized + .extra_field = 0x0 + }; + // expected-error@-1{{cannot implicitly initialize 'BufferCBONNonZeroConstCountTy::ptr' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroConstCountTy implicit_all_zero_init_const_count = {0}; + // expected-error@-1{{cannot implicitly initialize 'BufferCBONNonZeroConstCountTy::ptr' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + // When the ptr comes first it's seen as an explicit assignment when we write ` = {0}` so we get the incomplete pointee type error diagnostic + // expected-error@+2{{cannot initialize 'BufferCBONNonZeroConstCountFlippedFieldOrderTy::ptr' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // + struct BufferCBONNonZeroConstCountFlippedFieldOrderTy implicit_all_zero_init_const_count_ptr_init = {0}; + + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroDynCountTy design_init_non_zero_dyn_count = { + .count = 0x0, + // expected-error@+2{{cannot initialize 'BufferCBONNonZeroDynCountTy::ptr' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // + .ptr = 0x0 + }; + + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroDynCountTy design_init_non_zero_dyn_count_partial_init = { + // count is implicitly zero + // expected-error@+2{{cannot initialize 'BufferCBONNonZeroDynCountTy::ptr' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // + .ptr = 0x0 + }; + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroDynCountTy design_init_non_zero_dyn_count_partial_init2 = { + // ptr is implicitly zero initialized + .count = 0x0 + }; + // expected-error@-1{{cannot implicitly initialize 'BufferCBONNonZeroDynCountTy::ptr' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + + // TODO: Implicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroDynCountTy implicit_all_zero_init_non_zero_dyn_count = {0}; + // expected-error@-1{{cannot implicitly initialize 'BufferCBONNonZeroDynCountTy::ptr' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + + // TODO: Explicit 0x0 initialization should be allowed. rdar://129424354 + struct BufferCBONNonZeroDynCountTy non_desig_init_non_zero_dyn_count = { + 0, + // expected-error@+2{{cannot initialize 'BufferCBONNonZeroDynCountTy::ptr' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count + 1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // + 0 + }; + + // Struct inside a union + union BufferCBONOrOther UnionDesignInitOther = {.other = 0x0 }; + union BufferCBONOrOther UnionZeroInit = {0}; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + union BufferCBONOrOther UnionDesignInitBufZeroInitStructFields = {.buf = {0}}; + // expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + union BufferCBONOrOther UnionDesignInitBufDesignInitStructFields = {.buf = {.count = 0, .buffer = 0x0}}; + // expected-error@-1{{cannot initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +} + +// XXX + +struct BuffersCBONTy GlobalBuffersCBONTy_design_init = { + .count = 0, + .count_typedef = 0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + .buffer = 0x0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + .buffer_typedef = 0 +}; + +struct BuffersCBONTy GlobalBuffersCBONTy_non_design_init = { + 0, + 0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + 0, + // expected-error@+1{{cannot initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + 0 +}; + +struct BuffersCBONTy GlobalBuffersCBONTy_design_init_partial = { + .count = 0, + .count_typedef = 0 + // buffer and buffer_typedef are implicitly zero initialized +}; +// expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +// expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + +struct BuffersCBONTy GlobalBuffersCBONTy_non_design_init_partial = { + 0, + 0, + // buffer and buffer_typedef are implicitly zero initialized +}; +// expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +// expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + +struct BuffersCBONTy GlobalBuffersCBONTy_all_zero_init = {0}; +// expected-error@-1{{cannot implicitly initialize 'BuffersCBONTy::buffer' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +// expected-error@-2{{cannot implicitly initialize 'BuffersCBONTy::buffer_typedef' with '__counted_by_or_null' attributed type 'Incomplete_Struct_t *__single __counted_by_or_null(count_typedef)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} + + +// expected-error@+2{{cannot initialize 'BufferCBONNonZeroConstCountFlippedFieldOrderTy::ptr' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +// +struct BufferCBONNonZeroConstCountFlippedFieldOrderTy GlobalBuffersCBONTy_implicit_all_zero_init_const_count_ptr_init = {0}; + +struct BufferCBONNonZeroConstCountTy GlobalBuffersCBONTy_const_non_zero_count = { + // expected-error@+2{{cannot initialize 'BufferCBONNonZeroConstCountTy::ptr' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(1)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // + .ptr = 0x0 +}; + +//------------------------------------------------------------------------------ +// Local __counted_by variables +//------------------------------------------------------------------------------ +void local_cb_init_and_assign(int s) { + int size = s; + + // expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'local_init' with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + struct IncompleteStructTy* __counted_by(size) local_init = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + local_init = 0x0; + + int implicit_size = s; + // expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'implicit_init' with type 'struct IncompleteStructTy *__single __counted_by(implicit_size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + struct IncompleteStructTy* __counted_by(implicit_size) implicit_init; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + implicit_init = 0x0; +} + +void local_cb_init_and_assign_constant_count(void) { + // Check we also emit diagnostics about assigning nullptr to `__counted_by(X)` where X > 0 + // + // expected-error@+2{{cannot apply '__counted_by' attribute to variable definition 'local_init' with type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{initializing 'local_init' of type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') and count value of 5 with null always fails}} + struct IncompleteStructTy* __counted_by(5) local_init = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + local_init = 0x0; // Diagnostic suppressed because the VarDecl is invalid + + // There should be no diagnostic about assigning nullptr + // TODO: We should consider allowing this given that the type size isn't + // really needed when the count is 0 (rdar://129424147). + // expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'local_init_zero' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + struct IncompleteStructTy* __counted_by(0) local_init_zero = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + local_init_zero = 0x0; // Diagnostic suppressed because the VarDecl is invalid + + // expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'local_init2' with type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + struct IncompleteStructTy* __counted_by(5) local_init2 = __unsafe_forge_bidi_indexable(struct IncompleteStructTy*, 0x4, 4); // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + local_init2 = __unsafe_forge_bidi_indexable(struct IncompleteStructTy*, 0x4, 4); // Diagnostic suppressed because the VarDecl is invalid +} + +void local_cbon_init_and_assign(int s) { + int size = s; + + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'local_init' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + struct IncompleteStructTy* __counted_by_or_null(size) local_init = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + local_init = 0x0; + + int implicit_size = s; + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'implicit_init' with type 'struct IncompleteStructTy *__single __counted_by_or_null(implicit_size)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + struct IncompleteStructTy* __counted_by_or_null(implicit_size) implicit_init; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + implicit_init = 0x0; +} + +void local_cbon_init_and_assign_constant_count(void) { + // TODO: We should consider allowing this because the assignment of nullptr + // means the type size isn't needed (rdar://129424354). + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'local_init' with type 'struct IncompleteStructTy *__single __counted_by_or_null(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + struct IncompleteStructTy* __counted_by_or_null(5) local_init = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + local_init = 0x0; // Diagnostic suppressed because the VarDecl is invalid + + // There should be no diagnostic about assigning nullptr + // TODO: We should consider allowing this given that the type size isn't + // really needed when the count is 0 (rdar://129424147). + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'local_init_zero' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + struct IncompleteStructTy* __counted_by_or_null(0) local_init_zero = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + local_init_zero = 0x0; // Diagnostic suppressed because the VarDecl is invalid + + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'local_init2' with type 'struct IncompleteStructTy *__single __counted_by_or_null(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + struct IncompleteStructTy* __counted_by_or_null(5) local_init2 = __unsafe_forge_bidi_indexable(struct IncompleteStructTy*, 0x4, 4); // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + local_init2 = __unsafe_forge_bidi_indexable(struct IncompleteStructTy*, 0x4, 4); // Diagnostic suppressed because the VarDecl is invalid +} + +//------------------------------------------------------------------------------ +// Global __counted_by variables +//------------------------------------------------------------------------------ +// NOTE: Tentative definitions are mostly tested in `counted_by_type_incomplete_completable_struct_tentative_defs.c`. + +extern int external_count; +// expected-note@+1 3{{consider using '__sized_by' instead of '__counted_by'}} +extern struct IncompleteStructTy* __counted_by(external_count) GlobalCBPtrToIncompleteTy; // OK +extern Incomplete_Struct_t* __counted_by(external_count) GlobalCBPtrToIncompleteTyTypeDef; // OK + +void use_GlobalCBPtrToIncompleteTy(void) { + // expected-error@+2{{cannot assign to 'GlobalCBPtrToIncompleteTy' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(external_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{assignment to 'struct IncompleteStructTy *__single __counted_by(external_count)' (aka 'struct IncompleteStructTy *__single') 'GlobalCBPtrToIncompleteTy' requires corresponding assignment to 'external_count'; add self assignment 'external_count = external_count' if the value has not changed}} + GlobalCBPtrToIncompleteTy = 0x0; + // expected-error@+1{{cannot use 'GlobalCBPtrToIncompleteTy' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(external_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + GlobalCBPtrToIncompleteTy[0] = 0; + // expected-error@+1{{cannot use 'GlobalCBPtrToIncompleteTy' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(external_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_incomplete_cb(GlobalCBPtrToIncompleteTy, external_count); +} + +static int global_count; +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBPtrImplicitInit' with type 'struct IncompleteStructTy *__single __counted_by(global_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +static struct IncompleteStructTy* __counted_by(global_count) GlobalCBPtrImplicitInit; // expected-note 4{{consider using '__sized_by' instead of '__counted_by'}} +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBPtrImplicitInitTypeDef' with type 'Incomplete_Struct_t *__single __counted_by(global_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +static Incomplete_Struct_t* __counted_by(global_count) GlobalCBPtrImplicitInitTypeDef; // expected-note {{consider using '__sized_by' instead of '__counted_by'}} + +void use_GlobalCBPtrImplicitInit(void) { + // expected-error@+2{{cannot assign to 'GlobalCBPtrImplicitInit' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(global_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{assignment to 'struct IncompleteStructTy *__single __counted_by(global_count)' (aka 'struct IncompleteStructTy *__single') 'GlobalCBPtrImplicitInit' requires corresponding assignment to 'global_count'; add self assignment 'global_count = global_count' if the value has not changed}} + GlobalCBPtrImplicitInit = 0x0; + // expected-error@+1{{cannot use 'GlobalCBPtrImplicitInit' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(global_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + GlobalCBPtrImplicitInit[0] = 0; + // expected-error@+1{{cannot use 'GlobalCBPtrImplicitInit' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(global_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_incomplete_cb(GlobalCBPtrImplicitInit, global_count); +} + +int global_count_non_static = 0; +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInit' with type 'struct IncompleteStructTy *__single __counted_by(global_count_non_static)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by(global_count_non_static) GlobalCBPtrExplicitInit = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitTypeDef' with type 'Incomplete_Struct_t *__single __counted_by(global_count_non_static)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +Incomplete_Struct_t* __counted_by(global_count_non_static) GlobalCBPtrExplicitInitTypeDef = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + +void use_GlobalCBPtrExplicitInit(void) { + // No diagnostics because the VarDecl is marked as invalid at this point + GlobalCBPtrExplicitInit = 0x0; + GlobalCBPtrExplicitInit[0] = 0; + consume_incomplete_cb(GlobalCBPtrExplicitInit, global_count_non_static); +} + +// This is very unidiomatic C but it seems to be legal. +// expected-warning@+1{{'extern' variable has an initializer}} +extern int global_count_extern = 0; +// expected-error@+2{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitExtern' with type 'struct IncompleteStructTy *__single __counted_by(global_count_extern)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +// expected-warning@+1 2{{'extern' variable has an initializer}} TODO: This shouldn't be emitted twice. rdar://133001618 +extern struct IncompleteStructTy* __counted_by(global_count_extern) GlobalCBPtrExplicitInitExtern = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +// expected-error@+2{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitTypeDefExtern' with type 'Incomplete_Struct_t *__single __counted_by(global_count_extern)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +// expected-warning@+1 2{{'extern' variable has an initializer}} TODO: This shouldn't be emitted twice. rdar://133001618 +extern Incomplete_Struct_t* __counted_by(global_count_extern) GlobalCBPtrExplicitInitTypeDefExtern = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + +// TODO: We should consider allowing this given that the pointee type size isn't +// really needed when the count is 0 (rdar://129424147) +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBPtrImplicitInitConstantZeroCount' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by(0) GlobalCBPtrImplicitInitConstantZeroCount; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBPtrImplicitInitConstantZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +Incomplete_Struct_t* __counted_by(0) GlobalCBPtrImplicitInitConstantZeroCountTypeDef; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitConstantZeroCount' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by(0) GlobalCBPtrExplicitInitConstantZeroCount = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitConstantZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +Incomplete_Struct_t* __counted_by(0) GlobalCBPtrExplicitInitConstantZeroCountTypeDef = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + +// expected-error@+2{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBPtrImplicitInitConstantNonZeroCount' with type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +// expected-error@+1{{implicitly initializing 'GlobalCBPtrImplicitInitConstantNonZeroCount' of type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') and count value of 5 with null always fails}} +struct IncompleteStructTy* __counted_by(5) GlobalCBPtrImplicitInitConstantNonZeroCount; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +// expected-error@+2{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBPtrImplicitInitConstantNonZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +// expected-error@+1{{implicitly initializing 'GlobalCBPtrImplicitInitConstantNonZeroCountTypeDef' of type 'Incomplete_Struct_t *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') and count value of 5 with null always fails}} +Incomplete_Struct_t* __counted_by(5) GlobalCBPtrImplicitInitConstantNonZeroCountTypeDef; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +// expected-error@+2{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitConstantNonZeroCount' with type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +// expected-error@+1{{initializing 'GlobalCBPtrExplicitInitConstantNonZeroCount' of type 'struct IncompleteStructTy *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') and count value of 5 with null always fails}} +struct IncompleteStructTy* __counted_by(5) GlobalCBPtrExplicitInitConstantNonZeroCount = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +// expected-error@+2{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExplicitInitConstantNonZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +// expected-error@+1{{initializing 'GlobalCBPtrExplicitInitConstantNonZeroCountTypeDef' of type 'Incomplete_Struct_t *__single __counted_by(5)' (aka 'struct IncompleteStructTy *__single') and count value of 5 with null always fails}} +Incomplete_Struct_t* __counted_by(5) GlobalCBPtrExplicitInitConstantNonZeroCountTypeDef = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + +//------------------------------------------------------------------------------ +// Global __counted_by_or_null variables +//------------------------------------------------------------------------------ +// NOTE: Tentative definitions are mostly tested in `counted_by_type_incomplete_completable_struct_tentative_defs.c`. + +extern int external_count_cbon; +extern int external_count_cbon_typedef; +// expected-note@+1 3{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +extern struct IncompleteStructTy* __counted_by_or_null(external_count_cbon) GlobalCBONPtrToIncompleteTy; // OK +extern Incomplete_Struct_t* __counted_by_or_null(external_count_cbon_typedef) GlobalCBONPtrToIncompleteTyTypeDef; // OK + +void use_GlobalCBONPtrToIncompleteTy(void) { + // expected-error@+2{{cannot assign to 'GlobalCBONPtrToIncompleteTy' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(external_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{assignment to 'struct IncompleteStructTy *__single __counted_by_or_null(external_count_cbon)' (aka 'struct IncompleteStructTy *__single') 'GlobalCBONPtrToIncompleteTy' requires corresponding assignment to 'external_count_cbon'; add self assignment 'external_count_cbon = external_count_cbon' if the value has not changed}} + GlobalCBONPtrToIncompleteTy = 0x0; + // expected-error@+1{{cannot use 'GlobalCBONPtrToIncompleteTy' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(external_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + GlobalCBONPtrToIncompleteTy[0] = 0; + // expected-error@+1{{cannot use 'GlobalCBONPtrToIncompleteTy' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(external_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_incomplete_cbon(GlobalCBONPtrToIncompleteTy, external_count); +} + + + +static int global_count_cbon; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONPtrImplicitInit' with type 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +static struct IncompleteStructTy* __counted_by_or_null(global_count_cbon) GlobalCBONPtrImplicitInit; // expected-note 4{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONPtrImplicitInitTypeDef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(global_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +static Incomplete_Struct_t* __counted_by_or_null(global_count_cbon) GlobalCBONPtrImplicitInitTypeDef; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + + + +void use_GlobalCBONPtrImplicitInit(void) { + // expected-error@+2{{cannot assign to 'GlobalCBONPtrImplicitInit' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-error@+1{{assignment to 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon)' (aka 'struct IncompleteStructTy *__single') 'GlobalCBONPtrImplicitInit' requires corresponding assignment to 'global_count_cbon'; add self assignment 'global_count_cbon = global_count_cbon' if the value has not changed}} + GlobalCBONPtrImplicitInit = 0x0; + // expected-error@+1{{cannot use 'GlobalCBONPtrImplicitInit' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + GlobalCBONPtrImplicitInit[0] = 0; + // expected-error@+1{{cannot use 'GlobalCBONPtrImplicitInit' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_incomplete_cbon(GlobalCBONPtrImplicitInit, global_count_cbon); +} + + + +int global_count_cbon_non_static = 0; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInit' with type 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon_non_static)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by_or_null(global_count_cbon_non_static) GlobalCBONPtrExplicitInit = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitTypeDef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(global_count_cbon_non_static)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +Incomplete_Struct_t* __counted_by_or_null(global_count_cbon_non_static) GlobalCBONPtrExplicitInitTypeDef = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + +void use_GlobalCBONPtrExplicitInit(void) { + // No diagnostics because the VarDecl is marked as invalid at this point + GlobalCBONPtrExplicitInit = 0x0; + GlobalCBONPtrExplicitInit[0] = 0; + consume_incomplete_cbon(GlobalCBONPtrExplicitInit, global_count_cbon_non_static); +} + + + +// This is very unidiomatic C but it seems to be legal. +// expected-warning@+1{{'extern' variable has an initializer}} +extern int global_count_cbon_extern = 0; +// expected-error@+2{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitExtern' with type 'struct IncompleteStructTy *__single __counted_by_or_null(global_count_cbon_extern)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +// expected-warning@+1 2{{'extern' variable has an initializer}} TODO: This shouldn't be emitted twice. rdar://133001618 +extern struct IncompleteStructTy* __counted_by_or_null(global_count_cbon_extern) GlobalCBONPtrExplicitInitExtern = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +// expected-error@+2{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitTypeDefExtern' with type 'Incomplete_Struct_t *__single __counted_by_or_null(global_count_cbon_extern)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +// expected-warning@+1 2{{'extern' variable has an initializer}} TODO: This shouldn't be emitted twice. rdar://133001618 +extern Incomplete_Struct_t* __counted_by_or_null(global_count_cbon_extern) GlobalCBONPtrExplicitInitTypeDefExtern = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + + +// TODO: We should consider allowing this given that the pointee type size isn't +// really needed when the count is 0 (rdar://129424147) +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONPtrImplicitInitConstantZeroCount' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by_or_null(0) GlobalCBONPtrImplicitInitConstantZeroCount; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONPtrImplicitInitConstantZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +Incomplete_Struct_t* __counted_by_or_null(0) GlobalCBONPtrImplicitInitConstantZeroCountTypeDef; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitConstantZeroCount' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by_or_null(0) GlobalCBONPtrExplicitInitConstantZeroCount = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitConstantZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +Incomplete_Struct_t* __counted_by_or_null(0) GlobalCBONPtrExplicitInitConstantZeroCountTypeDef = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + +// Unlike `__counted_by` assigning 0x0 (implicitly or explicitly) is allowed for `__counted_by_or_null` +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONPtrImplicitInitConstantNonZeroCount' with type 'struct IncompleteStructTy *__single __counted_by_or_null(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by_or_null(5) GlobalCBONPtrImplicitInitConstantNonZeroCount; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONPtrImplicitInitConstantNonZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +Incomplete_Struct_t* __counted_by_or_null(5) GlobalCBONPtrImplicitInitConstantNonZeroCountTypeDef; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitConstantNonZeroCount' with type 'struct IncompleteStructTy *__single __counted_by_or_null(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} +struct IncompleteStructTy* __counted_by_or_null(5) GlobalCBONPtrExplicitInitConstantNonZeroCount = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExplicitInitConstantNonZeroCountTypeDef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(5)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete}} +Incomplete_Struct_t* __counted_by_or_null(5) GlobalCBONPtrExplicitInitConstantNonZeroCountTypeDef = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + +//------------------------------------------------------------------------------ +// No explicit forward decl +//------------------------------------------------------------------------------ + +// expected-note@+1 2{{consider providing a complete definition for 'NoExplicitForwardDecl_t' (aka 'struct NoExplicitForwardDecl')}} +typedef struct NoExplicitForwardDecl NoExplicitForwardDecl_t; + +extern NoExplicitForwardDecl_t* __counted_by(0) NoExplicitForwardDeclGlobalCBPtr; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +void consume_NoExplicitForwardDeclGlobalCBPtr(void) { + // expected-error@+1{{cannot assign to 'NoExplicitForwardDeclGlobalCBPtr' with '__counted_by' attributed type 'NoExplicitForwardDecl_t *__single __counted_by(0)' (aka 'struct NoExplicitForwardDecl *__single') because the pointee type 'NoExplicitForwardDecl_t' (aka 'struct NoExplicitForwardDecl') is incomplete}} + NoExplicitForwardDeclGlobalCBPtr = 0x0; +} + +extern NoExplicitForwardDecl_t* __counted_by_or_null(0) NoExplicitForwardDeclGlobalCBONPtr; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +void consume_NoExplicitForwardDeclGlobalCBONPtr(void) { + // expected-error@+1{{cannot assign to 'NoExplicitForwardDeclGlobalCBONPtr' with '__counted_by_or_null' attributed type 'NoExplicitForwardDecl_t *__single __counted_by_or_null(0)' (aka 'struct NoExplicitForwardDecl *__single') because the pointee type 'NoExplicitForwardDecl_t' (aka 'struct NoExplicitForwardDecl') is incomplete}} + NoExplicitForwardDeclGlobalCBONPtr = 0x0; +} + +//------------------------------------------------------------------------------ +// Array element initialization +// +// Currently this appears to be forbidden +//------------------------------------------------------------------------------ + +void array_elts_init_cb(void) { + int size; + // expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} + struct IncompleteStructTy*__counted_by(size) arr[2] = { 0x0, 0x0 }; +} + +void array_elts_init_cbon(void) { + int size; + // expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + struct IncompleteStructTy*__counted_by_or_null(size) arr[2] = { 0x0, 0x0 }; +} + +//------------------------------------------------------------------------------ +// Casting +//------------------------------------------------------------------------------ +// TODO: These should cause errors to be emitted. +// rdar://131621712 +void explicit_cast_cb_to_single(struct IncompleteStructTy* p) { + struct IncompleteStructTy* __single tmp = + (struct IncompleteStructTy* __counted_by(1)) p; +} + +void explicit_cast_cbon_to_single(struct IncompleteStructTy* p) { + struct IncompleteStructTy* __single tmp = + (struct IncompleteStructTy* __counted_by_or_null(1)) p; +} + + +void explicit_cast_cb_to_bidi(struct IncompleteStructTy* p) { + // TODO: This diagnostic is misleading. It says __single but it should probably be `__counted_by(2)`. rdar://133002045 + // expected-error@+1{{cannot initialize indexable pointer with type 'struct IncompleteStructTy *__bidi_indexable' from __single pointer to incomplete type 'struct IncompleteStructTy *__single'; consider declaring pointer 'local_bidi' as '__single'}} + struct IncompleteStructTy* local_bidi = (struct IncompleteStructTy* __counted_by(2)) p; // expected-note{{pointer 'local_bidi' declared here}} +} + +void explicit_cast_cbon_to_bidi(struct IncompleteStructTy* p) { + // TODO: This diagnostic is misleading. It says __single but it should probably be `__counted_by(2)`. rdar://133002045 + // expected-error@+1{{cannot initialize indexable pointer with type 'struct IncompleteStructTy *__bidi_indexable' from __single pointer to incomplete type 'struct IncompleteStructTy *__single'; consider declaring pointer 'local_bidi' as '__single'}} + struct IncompleteStructTy* local_bidi = (struct IncompleteStructTy* __counted_by_or_null(2)) p; // expected-note{{pointer 'local_bidi' declared here}} +} + +//------------------------------------------------------------------------------ +// Completing the pointee type allows usage +//------------------------------------------------------------------------------ + +// expected-note@+1 20{{consider providing a complete definition for 'struct IncompleteLaterCompletedStructTy'}} +struct IncompleteLaterCompletedStructTy; + +// Confirm using the type is an error at this point +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'GlobalCBPtrExpectErr' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} +struct IncompleteLaterCompletedStructTy*__counted_by(0) GlobalCBPtrExpectErr = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +struct IncompleteLaterCompletedStructTy*__counted_by(0) GlobalCBPtrTentativeDefUseWillErr; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'GlobalCBONPtrExpectErr' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} +struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) GlobalCBONPtrExpectErr = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) GlobalCBONPtrTentativeDefUseWillErr; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + +struct StructPtrIncompleteLaterCompleted { + int count; + struct IncompleteLaterCompletedStructTy*__counted_by(count) ptr; // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} +}; +struct StructCBONPtrIncompleteLaterCompleted { + int count; + struct IncompleteLaterCompletedStructTy*__counted_by_or_null(count) ptr; // expected-note 2{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +}; + +void consume_IncompleteLaterCompletedStructTy(struct IncompleteLaterCompletedStructTy*__counted_by(0) p); // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +void consume_CBON_IncompleteLaterCompletedStructTy(struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) p); // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + +struct IncompleteLaterCompletedStructTy*__counted_by(0) ret_IncompleteLaterCompletedStructTy(void); // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) ret_CBON_IncompleteLaterCompletedStructTy(void); // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + +// expected-error@+1{{cannot apply '__counted_by' attribute to return type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') on a function definition because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} +struct IncompleteLaterCompletedStructTy*__counted_by(0) test_cb_expect_err( // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} + // expected-error@+1{{cannot apply '__counted_by' attribute to parameter 'param' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') on a function definition because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + struct IncompleteLaterCompletedStructTy*__counted_by(0) param) { // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + // expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'local' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + struct IncompleteLaterCompletedStructTy*__counted_by(0) local; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + + // expected-error@+1{{cannot assign to 'GlobalCBPtrTentativeDefUseWillErr' with '__counted_by' attributed type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + GlobalCBPtrTentativeDefUseWillErr = 0; + + // expected-error@+1{{cannot initialize 'StructPtrIncompleteLaterCompleted::ptr' with '__counted_by' attributed type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(count)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + struct StructPtrIncompleteLaterCompleted tmp = { .count = 0, .ptr = 0x0 }; + + struct StructPtrIncompleteLaterCompleted tmp2; + // expected-error@+1{{cannot use 'tmp2.ptr' with '__counted_by' attributed type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(count)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + consume_IncompleteLaterCompletedStructTy(tmp2.ptr); + + // expected-error@+1{{cannot pass argument to parameter 'p' with '__counted_by' attributed type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + consume_IncompleteLaterCompletedStructTy(0x0); + + // expected-error@+1{{cannot call 'ret_IncompleteLaterCompletedStructTy' with '__counted_by' attributed return type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + ret_IncompleteLaterCompletedStructTy(); + + // expected-error@+1{{cannot return '__counted_by' attributed type 'struct IncompleteLaterCompletedStructTy *__single __counted_by(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + return 0x0; +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to return type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') on a function definition because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} +struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) test_cbon_expect_err( // expected-note 2{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to parameter 'param' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') on a function definition because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) param) { // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'local' with type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) local; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + + // expected-error@+1{{cannot assign to 'GlobalCBONPtrTentativeDefUseWillErr' with '__counted_by_or_null' attributed type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + GlobalCBONPtrTentativeDefUseWillErr = 0; + + // expected-error@+1{{cannot initialize 'StructCBONPtrIncompleteLaterCompleted::ptr' with '__counted_by_or_null' attributed type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + struct StructCBONPtrIncompleteLaterCompleted tmp = { .count = 0, .ptr = 0x0 }; + + struct StructCBONPtrIncompleteLaterCompleted tmp2; + // expected-error@+1{{cannot use 'tmp2.ptr' with '__counted_by_or_null' attributed type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(count)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + consume_CBON_IncompleteLaterCompletedStructTy(tmp2.ptr); + + // expected-error@+1{{cannot pass argument to parameter 'p' with '__counted_by_or_null' attributed type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + consume_CBON_IncompleteLaterCompletedStructTy(0x0); + + // expected-error@+1{{cannot call 'ret_CBON_IncompleteLaterCompletedStructTy' with '__counted_by_or_null' attributed return type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + ret_CBON_IncompleteLaterCompletedStructTy(); + + // expected-error@+1{{cannot return '__counted_by_or_null' attributed type 'struct IncompleteLaterCompletedStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteLaterCompletedStructTy *__single') because the pointee type 'struct IncompleteLaterCompletedStructTy' is incomplete}} + return 0x0; +} + + +// Now complete the type and confirm it can be used +struct IncompleteLaterCompletedStructTy { + int field; +}; + +struct IncompleteLaterCompletedStructTy*__counted_by(0) GlobalCBPtrExpectNoErr = 0x0; + +struct IncompleteLaterCompletedStructTy*__counted_by(0) test_cb_expect_no_err( + struct IncompleteLaterCompletedStructTy*__counted_by(0) param) { + struct IncompleteLaterCompletedStructTy*__counted_by(0) local; + + GlobalCBPtrTentativeDefUseWillErr = 0; + + struct StructPtrIncompleteLaterCompleted tmp = { .count = 0, .ptr = 0x0 }; + struct StructPtrIncompleteLaterCompleted tmp2; + consume_IncompleteLaterCompletedStructTy(tmp2.ptr); + + consume_IncompleteLaterCompletedStructTy(0x0); + ret_IncompleteLaterCompletedStructTy(); + + return 0x0; +} + +struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) test_cbon_expect_no_err( + struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) param) { + struct IncompleteLaterCompletedStructTy*__counted_by_or_null(0) local; + + GlobalCBONPtrTentativeDefUseWillErr = 0; + + struct StructCBONPtrIncompleteLaterCompleted tmp = { .count = 0, .ptr = 0x0 }; + struct StructCBONPtrIncompleteLaterCompleted tmp2; + consume_CBON_IncompleteLaterCompletedStructTy(tmp2.ptr); + + consume_CBON_IncompleteLaterCompletedStructTy(0x0); + ret_CBON_IncompleteLaterCompletedStructTy(); + + return 0x0; +} diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct_blocks.c b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct_blocks.c new file mode 100644 index 0000000000000..d5bcb1a9a28c4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct_blocks.c @@ -0,0 +1,87 @@ + +// RUN: %clang_cc1 -fsyntax-only -fblocks -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fblocks -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +//------------------------------------------------------------------------------ +// Test diagnostics on _counted_by(_or_null) pointers with an incomplete struct +// pointee type on block parameters/return type +//------------------------------------------------------------------------------ + +struct IncompleteStructTy; // expected-note 6{{consider providing a complete definition for 'struct IncompleteStructTy'}} + +//------------------------------------------------------------------------------ +// Attribute on block parameters +//------------------------------------------------------------------------------ + +typedef void(^cb_block_fn_t)(struct IncompleteStructTy* __counted_by(size), int size); // OK +typedef void(^cbon_block_fn_t)(struct IncompleteStructTy* __counted_by_or_null(size), int size); // OK + +void consume_cb(struct IncompleteStructTy* __counted_by(size), int size); +void consume_cbon(struct IncompleteStructTy* __counted_by_or_null(size), int size); + +void use_block_params_cb(void) { + // expected-error@+1{{cannot apply '__counted_by' attribute to parameter 'buf' with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} + cb_block_fn_t f_named = ^(struct IncompleteStructTy* __counted_by(size) buf, int size) { // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + // Uses don't generate diagnostics because the parameter is treated as invalid. + buf = 0x0; + buf[0] = 0; + struct IncompleteStructTy* block_local = buf; + consume_cb(buf); + }; + + // expected-error@+2{{cannot apply '__counted_by' attribute to parameter with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-warning@+1{{omitting the parameter name in a function definition is a C23 extension}} + cb_block_fn_t f_unnamed = ^(struct IncompleteStructTy* __counted_by(size), int size) { // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + }; + + void (^f_var_no_typedef_decl)(struct IncompleteStructTy* __counted_by(size), int size) = + // expected-error@+1{{cannot apply '__counted_by' attribute to parameter 'buf' with type 'struct IncompleteStructTy *__single __counted_by(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} + ^(struct IncompleteStructTy* __counted_by(size) buf, int size) { // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + // Uses don't generate diagnostics because the parameter is treated as invalid. + buf = 0x0; + buf[0] = 0; + struct IncompleteStructTy* block_local = buf; + consume_cb(buf); + }; +} + +void use_block_params_cbon(void) { + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to parameter 'buf' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} + cbon_block_fn_t f_named = ^(struct IncompleteStructTy* __counted_by_or_null(size) buf, int size) { // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + // Uses don't generate diagnostics because the parameter is treated as invalid. + buf = 0x0; + buf[0] = 0; + struct IncompleteStructTy* block_local = buf; + consume_cb(buf); + }; + + // expected-error@+2{{cannot apply '__counted_by_or_null' attribute to parameter with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} + // expected-warning@+1{{omitting the parameter name in a function definition is a C23 extension}} + cbon_block_fn_t f_unnamed = ^(struct IncompleteStructTy* __counted_by_or_null(size), int size) { // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + }; + + void (^f_var_no_typedef_decl)(struct IncompleteStructTy* __counted_by_or_null(size), int size) = + // expected-error@+1{{cannot apply '__counted_by_or_null' attribute to parameter 'buf' with type 'struct IncompleteStructTy *__single __counted_by_or_null(size)' (aka 'struct IncompleteStructTy *__single') on a function definition because the pointee type 'struct IncompleteStructTy' is incomplete}} + ^(struct IncompleteStructTy* __counted_by_or_null(size) buf, int size) { // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + // Uses don't generate diagnostics because the parameter is treated as invalid. + buf = 0x0; + buf[0] = 0; + struct IncompleteStructTy* block_local = buf; + consume_cb(buf); + }; +} + +//------------------------------------------------------------------------------ +// Attribute on block return type +//------------------------------------------------------------------------------ + +// TODO: We should probably lift this restriction. rdar://132927574 +// expected-error@+1{{'__counted_by' inside typedef is only allowed for function type}} +typedef struct IncompleteStructTy* __counted_by(size)(^cb_block_ret_fn_t)(int size); + +// Don't test this because it causes clang to crash. rdar://132927229 +// void try_block_ret(void) { +// struct IncompleteStructTy*__counted_by(size) (^f_var_no_typedef_decl)(int size) = ^ struct IncompleteStructTy*__counted_by(size) (int size) { + +// }; +// } diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct_tentative_defs.c b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct_tentative_defs.c new file mode 100644 index 0000000000000..10abf1674da4e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_incomplete_completable_struct_tentative_defs.c @@ -0,0 +1,288 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +// Test diagnostics on _counted_by(_or_null) pointers with an incomplete struct +// pointee type **and** that involve tentative definitions. + +// NOTE: For a typedef the source location is of the underlying type instead of +// the typedef. This seems like the right behavior because the typedef isn't the +// forward declaration, `struct IncompleteStructTy` is. +// +// expected-note@+2 2{{consider providing a complete definition for 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy')}} +// expected-note@+1 30{{consider providing a complete definition for 'struct IncompleteStructTy'}} +struct IncompleteStructTy; + +//------------------------------------------------------------------------------ +// Only one tentative definition +//------------------------------------------------------------------------------ + +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'cb_t_dfn_one' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_one; // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'cbon_t_dfn_one' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_one; // expected-note 2{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + +void use_cb_t_dfn_one(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_one' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cb_t_dfn_one = 0x0; +} + +void use_cbon_t_dfn_one(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_one' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cbon_t_dfn_one = 0x0; +} + +//------------------------------------------------------------------------------ +// Two tentative definitions +//------------------------------------------------------------------------------ + +// We only error on the last one which is treated as a definition once the whole +// TU has been processed. + +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_two; // OK - tentative definition acts like declaration +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'cb_t_dfn_two' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_two; // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} + +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_two; // OK - tentative definition acts like declaration +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'cbon_t_dfn_two' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_two; // expected-note 2{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + +void use_cb_t_dfn_two(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_two' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cb_t_dfn_two = 0x0; +} + +void use_cbon_t_dfn_two(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_two' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cbon_t_dfn_two = 0x0; +} + +//------------------------------------------------------------------------------ +// Definition followed by tentative definition +//------------------------------------------------------------------------------ + +// NOTE: The diagnostic about initializing `cb_t_dfn_after_def` is suppressed. + +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'cb_t_dfn_after_def' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_after_def = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_after_def; // OK - tentative definition acts like declaration +// expected-note@+1{{consider using '__sized_by' instead of '__counted_by'}} +extern struct IncompleteStructTy*__counted_by(0) cb_t_dfn_after_def; // OK - declaration + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'cbon_t_dfn_after_def' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_after_def = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_after_def; // OK - tentative definition acts like declaration +// expected-note@+1{{__counted_by_or_null}} +extern struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_after_def; // OK - declaration + +void use_cb_t_dfn_after_def(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_after_def' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cb_t_dfn_after_def = 0x0; +} + +void use_cbon_t_dfn_after_def(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_after_def' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cbon_t_dfn_after_def = 0x0; +} + +//------------------------------------------------------------------------------ +// Definition preceeded by tentative definition +//------------------------------------------------------------------------------ + +// NOTE: The diagnostic about initializing `cb_t_dfn_after_def` is suppressed. +// NOTE: This test case is the **only** case where diagnostics about variable use +// are suppressed due the variable decl being invalid. So it does more testing +// than other `use_*` functions in this file. + +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_before_def; // OK - tentative definition acts like declaration +// expected-note@+1 2{{consider using '__sized_by' instead of '__counted_by'}} +extern struct IncompleteStructTy*__counted_by(0) cb_t_dfn_before_def; // OK - declaration + +void consume_cb_const_count_zero(struct IncompleteStructTy*__counted_by(0) p); + +void use_cb_t_dfn_before_def_before_def(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_before_def' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cb_t_dfn_before_def = 0x0; + + // expected-error@+1{{cannot use 'cb_t_dfn_before_def' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_cb_const_count_zero(cb_t_dfn_before_def); +} + +// expected-error@+1{{cannot apply '__counted_by' attribute to variable definition 'cb_t_dfn_before_def' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteStructTy*__counted_by(0) cb_t_dfn_before_def = 0x0; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + +void use_cb_t_dfn_before_def_after_def(void) { + // no error here. At this point `cb_t_dfn_before_def` has been marked as invalid + // because we've seen a definition and have marked the variable as invalid. + cb_t_dfn_before_def = 0x0; + consume_cb_const_count_zero(cb_t_dfn_before_def); +} + +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_before_def; // OK - tentative definition acts like declaration +// expected-note@+1 2{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} +extern struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_before_def; // OK - declaration + +void consume_cbon_const_count_zero(struct IncompleteStructTy*__counted_by_or_null(0) p); + +void use_cbon_t_dfn_before_def_before_def(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_before_def' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cbon_t_dfn_before_def = 0x0; + + // expected-error@+1{{cannot use 'cbon_t_dfn_before_def' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + consume_cbon_const_count_zero(cbon_t_dfn_before_def); +} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to variable definition 'cbon_t_dfn_before_def' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_before_def = 0x0; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + +void use_cbon_t_dfn_before_def_after_def(void) { + // no error here. At this point `cbon_t_dfn_before_def` has been marked as invalid + // because we've seen a definition and have marked the variable as invalid. + cbon_t_dfn_before_def = 0x0; + + consume_cbon_const_count_zero(cbon_t_dfn_before_def); +} + +//------------------------------------------------------------------------------ +// Tentative definition due to `static` keyword +//------------------------------------------------------------------------------ + +static struct IncompleteStructTy*__counted_by(0) cb_t_dfn_static_def; // OK - tentative definition acts like declaration +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'cb_t_dfn_static_def' with type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +static struct IncompleteStructTy*__counted_by(0) cb_t_dfn_static_def; // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} + +static struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_static_def; // OK - tentative definition acts like declaration +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'cbon_t_dfn_static_def' with type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +static struct IncompleteStructTy*__counted_by_or_null(0) cbon_t_dfn_static_def; // expected-note 2{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + +void use_cb_t_dfn_static_def(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_static_def' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cb_t_dfn_static_def = 0x0; +} + +void use_cbon_t_dfn_static_def(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_static_def' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cbon_t_dfn_static_def = 0x0; +} + +//------------------------------------------------------------------------------ +// Tentative definition and non-const count +//------------------------------------------------------------------------------ + +static int cb_t_dfn_static_non_const_count_count; +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'cb_t_dfn_static_non_const_count' with type 'struct IncompleteStructTy *__single __counted_by(cb_t_dfn_static_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +static struct IncompleteStructTy*__counted_by(cb_t_dfn_static_non_const_count_count) cb_t_dfn_static_non_const_count; // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} + +static int cbon_t_dfn_static_non_const_count_count; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'cbon_t_dfn_static_non_const_count' with type 'struct IncompleteStructTy *__single __counted_by_or_null(cbon_t_dfn_static_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +static struct IncompleteStructTy*__counted_by_or_null(cbon_t_dfn_static_non_const_count_count) cbon_t_dfn_static_non_const_count; // expected-note 2{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + + +int cb_t_dfn_non_const_count_count; +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'cb_t_dfn_non_const_count' with type 'struct IncompleteStructTy *__single __counted_by(cb_t_dfn_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by' attribute}} +struct IncompleteStructTy*__counted_by(cb_t_dfn_non_const_count_count) cb_t_dfn_non_const_count; // expected-note 2{{consider using '__sized_by' instead of '__counted_by'}} + +int cbon_t_dfn_non_const_count_count; +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'cbon_t_dfn_non_const_count' with type 'struct IncompleteStructTy *__single __counted_by_or_null(cbon_t_dfn_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete; consider providing a complete definition for 'struct IncompleteStructTy' before this definition or using the '__sized_by_or_null' attribute}} +struct IncompleteStructTy*__counted_by_or_null(cbon_t_dfn_non_const_count_count) cbon_t_dfn_non_const_count; // expected-note 2{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + +void use_cb_t_dfn_static_non_const_count(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_static_non_const_count' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(cb_t_dfn_static_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cb_t_dfn_static_non_const_count = 0x0; + cb_t_dfn_static_non_const_count_count = 0; +} + +void use_cbon_t_dfn_static_non_const_count(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_static_non_const_count' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(cbon_t_dfn_static_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cbon_t_dfn_static_non_const_count = 0x0; + cbon_t_dfn_static_non_const_count_count = 0; +} + +void use_cb_t_dfn_non_const_count(void) { + // expected-error@+1{{cannot assign to 'cb_t_dfn_non_const_count' with '__counted_by' attributed type 'struct IncompleteStructTy *__single __counted_by(cb_t_dfn_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cb_t_dfn_non_const_count = 0x0; + cb_t_dfn_non_const_count_count = 0; +} + +void use_cbon_t_dfn_non_const_count(void) { + // expected-error@+1{{cannot assign to 'cbon_t_dfn_non_const_count' with '__counted_by_or_null' attributed type 'struct IncompleteStructTy *__single __counted_by_or_null(cbon_t_dfn_non_const_count_count)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'struct IncompleteStructTy' is incomplete}} + cbon_t_dfn_non_const_count = 0x0; + cbon_t_dfn_non_const_count_count = 0; +} + +//------------------------------------------------------------------------------ +// Only one tentative definition on typedef +//------------------------------------------------------------------------------ + +typedef struct IncompleteStructTy Incomplete_Struct_t; + +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'cb_t_dfn_one_typedef' with type 'Incomplete_Struct_t *__single __counted_by(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by' attribute}} +Incomplete_Struct_t*__counted_by(0) cb_t_dfn_one_typedef; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} + +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'cbon_t_dfn_one_typedef' with type 'Incomplete_Struct_t *__single __counted_by_or_null(0)' (aka 'struct IncompleteStructTy *__single') because the pointee type 'Incomplete_Struct_t' (aka 'struct IncompleteStructTy') is incomplete; consider providing a complete definition for 'Incomplete_Struct_t' before this definition or using the '__sized_by_or_null' attribute}} +Incomplete_Struct_t*__counted_by_or_null(0) cbon_t_dfn_one_typedef; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + + +//------------------------------------------------------------------------------ +// Tentative definition using incomplete pointee that is later completed +//------------------------------------------------------------------------------ + +struct IncompleteStructLaterCompletedTy; + +// This is a tentative definition where the pointee type is incomplete. However, +// by the end of the translation unit (when we do the check) the +// pointee type has now become complete so the error diagnostic is not emitted. +struct IncompleteStructLaterCompletedTy* __counted_by(0) cb_t_dfn_later_completed; + +struct IncompleteStructLaterCompletedTy { + int field; +}; + +//------------------------------------------------------------------------------ +// Tentative definition of struct with field using __counted_by or +// __counted_by_or_null with an incomplete pointee type +//------------------------------------------------------------------------------ +struct BuffersCBTy { + int count; + int count_typedef; + struct IncompleteStructTy* __counted_by(count) buffer; + Incomplete_Struct_t* __counted_by(count_typedef) buffer_typedef; +}; + +struct BuffersCBONTy { + int count; + int count_typedef; + struct IncompleteStructTy* __counted_by_or_null(count) buffer; + Incomplete_Struct_t* __counted_by_or_null(count_typedef) buffer_typedef; +}; + +// TODO: Technically this is ok because zero initialization doesn't require +// a check and therefor doesn't need the pointee type size. However, it's +// inconsistent to disallow assignment via `= {0}` but allow it for tentative +// definitions that get zero initialized. rdar://133573722 +struct BuffersCBTy GlobalBuffersCBTy_implicit_init; +struct BuffersCBONTy GlobalBuffersCBONTy_implicit_init; + +struct BuffersNonZeroCountCBTy { + int count; + int count_typedef; + struct IncompleteStructTy* __counted_by(count+1) buffer; + Incomplete_Struct_t* __counted_by(count_typedef+1) buffer_typedef; +}; + +struct BuffersNonZeroCountCBONTy { + int count; + int count_typedef; + struct IncompleteStructTy* __counted_by_or_null(count+1) buffer; + Incomplete_Struct_t* __counted_by_or_null(count_typedef+1) buffer_typedef; +}; + +// TODO: It's inconsistent to disallow assignment via `= {0}` but allow it for +// tentative definitions that get zero initialized. rdar://133573722 +// +// expected-error@+2{{implicitly initializing 'GlobalBuffersNonZeroCountCBTy_implicit_init.buffer' of type 'struct IncompleteStructTy *__single __counted_by(count + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} +// expected-error@+1{{implicitly initializing 'GlobalBuffersNonZeroCountCBTy_implicit_init.buffer_typedef' of type 'Incomplete_Struct_t *__single __counted_by(count_typedef + 1)' (aka 'struct IncompleteStructTy *__single') and count value of 1 with null always fails}} +struct BuffersNonZeroCountCBTy GlobalBuffersNonZeroCountCBTy_implicit_init; +struct BuffersNonZeroCountCBONTy GlobalBuffersNonZeroCountCBONTy_implicit_init; // OK diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_unknown_completable_union.c b/clang/test/BoundsSafety/Sema/counted_by_type_unknown_completable_union.c new file mode 100644 index 0000000000000..3a8e13550166b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_unknown_completable_union.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +// Test incomplete unions + +extern int external_union_len; +typedef union incomplete_union incomplete_union_t; // expected-note 2{{consider providing a complete definition for 'union incomplete_union'}} +extern incomplete_union_t* __counted_by(external_union_len) incompleteUnionPtr; // OK +extern union incomplete_union* __counted_by(external_union_len) incompleteUnionPtr2; // OK + +int global_union_len; +// expected-error@+1{{cannot apply '__counted_by' attribute to tentative variable definition 'GlobalCBUnionPtrImplicitInit' with type 'union incomplete_union *__single __counted_by(global_union_len)' (aka 'union incomplete_union *__single') because the pointee type 'union incomplete_union' is incomplete; consider providing a complete definition for 'union incomplete_union' before this definition or using the '__sized_by' attribute}} +union incomplete_union* __counted_by(global_union_len) GlobalCBUnionPtrImplicitInit; // expected-note{{consider using '__sized_by' instead of '__counted_by'}} +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to tentative variable definition 'GlobalCBONUnionPtrImplicitInit' with type 'union incomplete_union *__single __counted_by_or_null(global_union_len)' (aka 'union incomplete_union *__single') because the pointee type 'union incomplete_union' is incomplete; consider providing a complete definition for 'union incomplete_union' before this definition or using the '__sized_by_or_null' attribute}} +union incomplete_union* __counted_by_or_null(global_union_len) GlobalCBONUnionPtrImplicitInit; // expected-note{{consider using '__sized_by_or_null' instead of '__counted_by_or_null'}} + +// Unions are handled like structs for the diagnostics so the testing for structs +// should mean testing other combinations should be unnecessary. diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_unknown_size.c b/clang/test/BoundsSafety/Sema/counted_by_type_unknown_size.c new file mode 100644 index 0000000000000..2ec4579ff01da --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_unknown_size.c @@ -0,0 +1,60 @@ + +// RUN: %clang_cc1 -triple arm64-apple-macos -target-feature +sve -fsyntax-only -fbounds-safety -verify %s +// RUN: not %clang_cc1 -triple arm64-apple-macos -target-feature +sve -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-macos -target-feature +sve -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: not %clang_cc1 -triple arm64-apple-macos -target-feature +sve -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +#include + +int len; + +//------------------------------------------------------------------------------ +// void pointer +//------------------------------------------------------------------------------ +// CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:7-[[@LINE+2]]:19}:"__sized_by" +// expected-error@+1{{cannot apply '__counted_by' attribute to 'void *' because 'void' has unknown size; did you mean to use '__sized_by' instead?}} +void *__counted_by(len) voidPtr; + +// CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:7-[[@LINE+2]]:27}:"__sized_by_or_null" +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'void *' because 'void' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +void *__counted_by_or_null(len) voidPtrOrNull; + +//------------------------------------------------------------------------------ +// Function pointer +//------------------------------------------------------------------------------ +typedef int (*fptr_t)(int*); + +// CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:8-[[@LINE+2]]:20}:"__sized_by" +// expected-error@+1{{cannot apply '__counted_by' attribute to 'int (*)(int *__single)' because 'int (int *__single)' has unknown size; did you mean to use '__sized_by' instead?}} +fptr_t __counted_by(len) fPtr; + +// CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:8-[[@LINE+2]]:28}:"__sized_by_or_null" +// expected-error@+1{{cannot apply '__counted_by_or_null' attribute to 'int (*)(int *__single)' because 'int (int *__single)' has unknown size; did you mean to use '__sized_by_or_null' instead?}} +fptr_t __counted_by_or_null(len) fPtrOrNull; + + +//------------------------------------------------------------------------------ +// variable length array +//------------------------------------------------------------------------------ +struct vla { + int size; + char data[__counted_by(size)]; +}; +typedef struct vla vla_t; + +// CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:8-[[@LINE+2]]:20}:"__sized_by" +// expected-error@+1{{cannot apply '__counted_by' attribute to 'vla_t *' (aka 'struct vla *') because 'vla_t' (aka 'struct vla') has unknown size; did you mean to use '__sized_by' instead?}} +vla_t* __counted_by(len) vlaPtr; + +// CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:13-[[@LINE+2]]:25}:"__sized_by" +// expected-error@+1{{cannot apply '__counted_by' attribute to 'struct vla *' because 'struct vla' has unknown size; did you mean to use '__sized_by' instead?}} +struct vla* __counted_by(len) vlaPtr2; + +//------------------------------------------------------------------------------ +// builtins +//------------------------------------------------------------------------------ +#ifdef __aarch64__ + // CHECK: fix-it:"{{.+}}":{[[@LINE+2]]:17-[[@LINE+2]]:29}:"__sized_by" + // expected-error@+1{{cannot apply '__counted_by' attribute to '__SVInt8_t *' because '__SVInt8_t' has unknown size; did you mean to use '__sized_by' instead?}} + __SVInt8_t* __counted_by(len) countSVInt8; +#endif diff --git a/clang/test/BoundsSafety/Sema/counted_by_type_unknown_size_no_fixit.c b/clang/test/BoundsSafety/Sema/counted_by_type_unknown_size_no_fixit.c new file mode 100644 index 0000000000000..c7a83f52b0ec8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/counted_by_type_unknown_size_no_fixit.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +#include + +int len; + +// Using attribute directly should not suggest a fix-it. +// expected-error-re@+1{{cannot apply '__counted_by' attribute to 'void *' because 'void' has unknown size{{$}}}} +void *__attribute__((__counted_by__(len))) voidPtr; + +// Using a custom macro that wraps the attribute should not suggest a fix-it. +// expected-error-re@+2{{cannot apply '__counted_by' attribute to 'void *' because 'void' has unknown size{{$}}}} +#define my_custom_counted_by(X) __attribute__((__counted_by__(X))) +void * my_custom_counted_by(len) voidPtr2; + +// CHECK-NOT: fix-it:"{{.+}}": diff --git a/clang/test/BoundsSafety/Sema/crash-shared-ptr-as-dep-count.c b/clang/test/BoundsSafety/Sema/crash-shared-ptr-as-dep-count.c new file mode 100644 index 0000000000000..a04bf3e0752e3 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/crash-shared-ptr-as-dep-count.c @@ -0,0 +1,13 @@ + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void foo(void) { + int n; + int* n_ptr1 = &n; + int* n_ptr2 = &n; + int *__counted_by(*n_ptr1) local_buf1; // expected-error{{dereference operator in '__counted_by' is only allowed for function parameters}} + int *__counted_by(*n_ptr2) local_buf2; // expected-error{{dereference operator in '__counted_by' is only allowed for function parameters}} +} diff --git a/clang/test/BoundsSafety/Sema/ct-constant-indexable-to-single.c b/clang/test/BoundsSafety/Sema/ct-constant-indexable-to-single.c new file mode 100644 index 0000000000000..d8675f07ef84f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ct-constant-indexable-to-single.c @@ -0,0 +1,33 @@ + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +int p; +// expected-warning@+1{{incompatible pointer types initializing 'int *__single' with an expression of type 'char *__bidi_indexable'}} +int *q1 = ((char*)&p) + 1; // expected-error{{initializer element is not a compile-time constant}} +int *q2 = &p; +int *q3 = &p + 1; // expected-error{{initializer element is not a compile-time constant}} + +int arr[10]; +int *q4 = arr; +int *q5 = arr + 9; +int *q6 = arr + 10; // expected-error{{initializer element is not a compile-time constant}} + +extern int iarr[]; +// expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} +int *q7 = iarr; // expected-error{{initializer element is not a compile-time constant}} + +extern int iarr_cnt[__counted_by(10)]; +int *q8 = iarr_cnt; +int *q9 = &iarr_cnt[9]; +int *q10 = iarr_cnt + 10; // expected-error{{initializer element is not a compile-time constant}} + +int g_val; +// expected-warning@+1{{array with '__counted_by' and the argument of the attribute should be defined in the same translation unit}} +extern int iarr_cnt_val[__counted_by(g_val)]; +int *q11 = iarr_cnt_val; // expected-error{{initializer element is not a compile-time constant}} + +char c; +// expected-warning@+1{{incompatible pointer types initializing 'int *__single' with an expression of type 'char *__bidi_indexable'}} +int *q12 = &c; // expected-error{{initializer element is not a compile-time constant}} diff --git a/clang/test/BoundsSafety/Sema/decl-refs-in-count-exprs.c b/clang/test/BoundsSafety/Sema/decl-refs-in-count-exprs.c new file mode 100644 index 0000000000000..ba1954bf4b5b7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/decl-refs-in-count-exprs.c @@ -0,0 +1,45 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int glen; + +struct T { + char *buf __counted_by(glen); // expected-error{{count expression on struct field may only reference other fields of the same struct}} + int len; +}; + +struct T_flex_1 { + int dummy; + int fam[__counted_by(glen)]; // expected-error{{count expression on struct field may only reference other fields of the same struct}} +}; + + +int glen2; +struct T_flex_2 { + int dummy; + int fam[__counted_by(glen2)]; // expected-error{{count expression on struct field may only reference other fields of the same struct}} +}; + +void Test () { + int len; + struct S { + int *__counted_by(len) buf; // expected-error{{count expression on struct field may only reference other fields of the same struct}} + int *__counted_by(glen) buf2; // expected-error{{count expression on struct field may only reference other fields of the same struct}} + }; + + struct S_flex_1 { + int dummy; + int fam[__counted_by(len)]; // expected-error{{count expression on struct field may only reference other fields of the same struct}} + }; + + struct S_flex_2 { + int dummy; + int fam[__counted_by(glen)]; // expected-error{{count expression on struct field may only reference other fields of the same struct}} + }; +} + +void foo(int *__counted_by(glen) buf); // expected-error{{count expression in function declaration may only reference parameters of that function}} +void bar(int p[__counted_by(glen)]); // expected-error{{count expression in function declaration may only reference parameters of that function}} diff --git a/clang/test/BoundsSafety/Sema/decls-with-count-attr-assignments.c b/clang/test/BoundsSafety/Sema/decls-with-count-attr-assignments.c new file mode 100644 index 0000000000000..dc309ee255fdb --- /dev/null +++ b/clang/test/BoundsSafety/Sema/decls-with-count-attr-assignments.c @@ -0,0 +1,32 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(l) bp; + int *bp2 __counted_by(l+1); + int l; +}; + +int side_effect(int *ptr); + +int Test (int *__counted_by(n) buf, int n, struct S s) { + // expected-note@+1{{'buf' has been assigned here}} + buf = 0; + // expected-error@+1{{cannot reference 'buf' after it is changed during consecutive assignments}} + n = side_effect(buf); // expected-error{{assignments to dependent variables should not have side effects between them}} + + s.bp = 0; + // expected-note@+1{{previously assigned here}} + s.l = side_effect(s.bp2); + // expected-error@+2{{assignment to 's.l' requires corresponding assignment to 'int *__single __counted_by(l + 1)' (aka 'int *__single') 's.bp2'; add self assignment 's.bp2 = s.bp2' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count 'l' must be simplified; keep only one of the assignments}} + s.l = 0; // expected-error{{assignments to dependent variables should not have side effects between them}} + + n = side_effect(buf); // no error the side effect (RHS) happens before the assignment + buf = 0; + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/dependent-count-arithmetic-check.c b/clang/test/BoundsSafety/Sema/dependent-count-arithmetic-check.c new file mode 100644 index 0000000000000..0ac8c3a4fbca4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dependent-count-arithmetic-check.c @@ -0,0 +1,27 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +int foo(int *__counted_by(*out_n + 1) *out_buf, int *out_n); // expected-error{{invalid argument expression to bounds attribute}} + +int bar(int *__counted_by(*out_n) *out_buf, int *out_n); + + +struct T { + int *__counted_by(l) buf; + int l; +}; + + +int main() { + int n = -1; + int *__counted_by(n + 1) buf; + // XXX: this error message is misleading because `foo` had an attribute error and + // thus the function parameter does not have the proper attribute to check. + // expected-error@+1{{passing address of 'n' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + foo(&buf, &n); + + bar(&buf, &n); // expected-error{{incompatible count expression '*out_n' vs. 'n + 1' in argument to function}} + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/dependent-count-as-out-params.c b/clang/test/BoundsSafety/Sema/dependent-count-as-out-params.c new file mode 100644 index 0000000000000..ed1f89e9fd95a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dependent-count-as-out-params.c @@ -0,0 +1,82 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + + +#include + +int side_effect(int *buf); + +int foo(int *__counted_by(*len) *buf, int *len) { + int arr[10]; + // expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__counted_by(*len) *p; + int *__counted_by(*len) q; // expected-error{{dereference operator in '__counted_by' is only allowed for function parameters}} + int *other_len; + int **normal_p = buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + len = other_len; // expected-error{{not allowed to change out parameter used as dependent count expression of other parameter}} + buf = normal_p; // expected-error{{not allowed to change out parameter with dependent count}} + *buf = arr; + *len = side_effect(*buf); // expected-error{{assignments to dependent variables should not have side effects between them}} + side_effect(*buf); + *len = side_effect(*buf); + *buf = arr; + return 0; +} + +int test_inbuf_outlen(int *__counted_by(*len) buf, int *len) { + int arr[10]; + // expected-error@+2{{parameter 'buf' with '__counted_by' attribute depending on an indirect count is implicitly read-only}} + // expected-error@+1{{assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') 'buf' requires corresponding assignment to '*len'; add self assignment '*len = *len' if the value has not changed}} + buf = arr; + side_effect(0); + *len = 10; + return 0; +} + +int test_outbuf_inlen(int *__counted_by(len) *buf, int len) { + int arr[10]; + // expected-error@+1{{assignment to 'len' requires corresponding assignment to 'int *__single __counted_by(len)' (aka 'int *__single') '*buf'; add self assignment '*buf = *buf' if the value has not changed}} + len = 10; + side_effect(0); + // expected-error@+1{{assignment to 'int *__single __counted_by(len)' (aka 'int *__single') '*buf' requires corresponding assignment to 'len'; add self assignment 'len = len' if the value has not changed}} + *buf = arr; + return 0; +} + +int foo_nullable(int *__counted_by_or_null(*len) *buf, int *len) { + int arr[10]; + // expected-error@+1{{'__counted_by_or_null' attribute on nested pointer type is only allowed on indirect parameters}} + int *__counted_by_or_null(*len) *p; + int *__counted_by_or_null(*len) q; // expected-error{{dereference operator in '__counted_by_or_null' is only allowed for function parameters}} + int *other_len; + int **normal_p = buf; // expected-error{{pointer with '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + len = other_len; // expected-error{{not allowed to change out parameter used as dependent count expression of other parameter}} + buf = normal_p; // expected-error{{not allowed to change out parameter with dependent count}} + *buf = arr; + *len = side_effect(*buf); // expected-error{{assignments to dependent variables should not have side effects between them}} + side_effect(*buf); + *len = side_effect(*buf); + *buf = arr; + return 0; +} + +int test_inbuf_outlen_nullable(int *__counted_by_or_null(*len) buf, int *len) { + int arr[10]; + // expected-error@+2{{parameter 'buf' with '__counted_by_or_null' attribute depending on an indirect count is implicitly read-only}} + // expected-error@+1{{assignment to 'int *__single __counted_by_or_null(*len)' (aka 'int *__single') 'buf' requires corresponding assignment to '*len'; add self assignment '*len = *len' if the value has not changed}} + buf = arr; + side_effect(0); + *len = 10; + return 0; +} + +int test_outbuf_inlen_nullable(int *__counted_by_or_null(len) *buf, int len) { + int arr[10]; + // expected-error@+1{{assignment to 'len' requires corresponding assignment to 'int *__single __counted_by_or_null(len)' (aka 'int *__single') '*buf'; add self assignment '*buf = *buf' if the value has not changed}} + len = 10; + side_effect(0); + // expected-error@+1{{assignment to 'int *__single __counted_by_or_null(len)' (aka 'int *__single') '*buf' requires corresponding assignment to 'len'; add self assignment 'len = len' if the value has not changed}} + *buf = arr; + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/dependent-count-casts.c b/clang/test/BoundsSafety/Sema/dependent-count-casts.c new file mode 100644 index 0000000000000..8c6ef17338e0c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dependent-count-casts.c @@ -0,0 +1,25 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + + +#include +// rdar://70688465 +int foo(int *__counted_by(len) *buf, int len) { + int **local_buf = buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + + return 0; +} + +int bar(int *__counted_by_or_null(len) *buf, int len) { + int **local_buf = buf; // expected-error{{pointer with '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + + return 0; +} + +int main() { + int *__single buf; + foo(&buf, 10); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + bar(&buf, 10); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/dependent-count-out-param-check.c b/clang/test/BoundsSafety/Sema/dependent-count-out-param-check.c new file mode 100644 index 0000000000000..0a0def792edf2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dependent-count-out-param-check.c @@ -0,0 +1,18 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +int foo(int *__counted_by(*out_n) *out_buf, int *out_n, int *no_out_n); + + +int main() { + int n; + int *__counted_by(n) buf; + int *p = 0; + int *__single plain_buf; + foo(&buf, &n, p); + foo(&buf, p, &n); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + foo(&plain_buf, &n, p); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/dependent-group-assign-order.c b/clang/test/BoundsSafety/Sema/dependent-group-assign-order.c new file mode 100644 index 0000000000000..e5eb46ca04c22 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dependent-group-assign-order.c @@ -0,0 +1,158 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct CountedByData { + unsigned len; + char *__counted_by(len) buf; +}; + +void TestOrderImplOK(struct CountedByData *data) { + data->len -= 10; + data->buf += 10; +} + + +void TestOrderImplOK2(struct CountedByData *data) { + data->len = 4; + data->buf = data->buf + 10; +} + +void TestOrderOK(struct CountedByData *data) { + data->buf += 10; + data->len -= 10; +} + +void TestOrderRefOK(struct CountedByData *data) { + data->buf = data->buf + 1; + data->len -= 1; +} + +void TestOutParamOrderImplOK(char *__sized_by(*outLen) *outBuf, unsigned *outLen) { + *outLen = 10; + *outBuf += 1; +} + +void *__sized_by(l) alloc(unsigned long long l); + +void TestOrderPtrPromoteFail1(struct CountedByData *data, unsigned new_len) { + data->len = new_len; + data->buf = alloc(new_len); // expected-error{{assignments to dependent variables should not have side effects between them}} +} + +void TestOrderPtrPromoteFail2(struct CountedByData *data, unsigned new_len) { + data->len = new_len; // expected-note{{'data->len' has been assigned here}} + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + data->buf = alloc(data->len); // expected-error{{cannot reference 'data->len' after it is changed during consecutive assignments}} +} + +void TestOrderPtrPromoteOK(struct CountedByData *data, unsigned new_len) { + data->buf = alloc(new_len); + data->len = new_len; +} + +unsigned glen; +char *__counted_by(glen) gbuf; + +void TestOrderGlobalPtrPromoteFail1(unsigned new_len) { + glen = new_len; + gbuf = alloc(new_len); // expected-error{{assignments to dependent variables should not have side effects between them}} +} + +void TestOrderGlobalPtrPromoteFail2(unsigned new_len) { + glen = new_len; // expected-note{{'glen' has been assigned here}} + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + gbuf = alloc(glen); // expected-error{{cannot reference 'glen' after it is changed during consecutive assignments}} +} + +void TestOrderGlobalPtrPromoteOK(unsigned new_len) { + gbuf = alloc(new_len); + glen = new_len; +} + +void TestOrderParmPtrPromoteFail1(unsigned new_len, + char *__counted_by(len) buf, unsigned len) { + len = new_len; + buf = alloc(new_len); // expected-error{{assignments to dependent variables should not have side effects between them}} +} + +void TestOrderParmPtrPromoteFail2(unsigned new_len, + char *__counted_by(len) buf, unsigned len) { + len = new_len; // expected-note{{'len' has been assigned here}} + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + buf = alloc(len); // expected-error{{cannot reference 'len' after it is changed during consecutive assignments}} +} + +void TestOrderParmPtrPromoteOK(unsigned new_len, + char *__counted_by(len) buf, unsigned len) { + buf = alloc(new_len); + len = new_len; +} + +void TestOrderLocalPtrPromoteFail1(unsigned new_len) { + unsigned len; + char *__counted_by(len) buf; + len = new_len; + buf = alloc(new_len); // expected-error{{assignments to dependent variables should not have side effects between them}} +} + +void TestOrderLocalPtrPromoteFail2(unsigned new_len) { + unsigned len; + char *__counted_by(len) buf; + // expected-note@+1{{'len' has been assigned here}} + len = new_len; + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + buf = alloc(len); // expected-error{{cannot reference 'len' after it is changed during consecutive assignments}} +} + +void TestOrderLocalPtrPromoteOK(unsigned new_len) { + unsigned len; + char *__counted_by(len) buf; + buf = alloc(new_len); + len = new_len; +} + + +struct CountedByData2 { + unsigned len; + char *__counted_by(len) buf; + char *__counted_by(len) buf2; +}; + +void TestOrderRefOK2(struct CountedByData2 *data) { + data->buf2 = data->buf; // old data->buf with old data->len + data->buf = data->buf + 1; + data->len -= 1; +} + +void TestOrderRefFail(struct CountedByData2 *data) { + data->buf = data->buf + 1; // expected-note{{'data->buf' has been assigned here}} + data->buf2 = data->buf; // expected-error{{cannot reference 'data->buf' after it is changed during consecutive assignments}} + data->len -= 1; +} + +void TestOrderRefFail2(struct CountedByData2 *data) { + data->len = 10; // expected-note{{'data->len' has been assigned here}} + data->buf = data->buf + data->len; // expected-error{{cannot reference 'data->len' after it is changed during consecutive assignments}} + data->buf2 = data->buf2 + 1; +} + +struct EndedByData { + char *__ended_by(iter) start; + char *__ended_by(end) iter; + char *end; +}; + +void TestEndedByImplOK(struct EndedByData *data) { + data->end -= 1; + data->iter += 1; + data->start = data->start; +} + +void TestEndedByOK(struct EndedByData *data) { + data->iter += 1; + data->end -= 1; + data->start = data->start; +} diff --git a/clang/test/BoundsSafety/Sema/disallow-union-count.c b/clang/test/BoundsSafety/Sema/disallow-union-count.c new file mode 100644 index 0000000000000..036cee5c5e00a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/disallow-union-count.c @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +union S { + int *p1 __counted_by(count); + // expected-error@-1 {{cannot use '__counted_by' on union fields}} + int count; + int *p2 __ended_by(end); + // expected-error@-1 {{cannot use '__ended_by' on union fields}} + int *end; + int *p3 __sized_by(size); + // expected-error@-1 {{cannot use '__sized_by' on union fields}} + int size; + struct + { + int *p1 __counted_by(count); + int count; + int *p2 __ended_by(end); + int *end; + int *p3 __sized_by(size); + int size; + } nested; + + struct + { + int *p4 __counted_by(count2); + int count2; + int *p5 __ended_by(end2); + int *end2; + int *p6 __sized_by(size2); + int size2; + } /* anonymous */; +}; diff --git a/clang/test/BoundsSafety/Sema/dynamic-bound-calls-stress.c b/clang/test/BoundsSafety/Sema/dynamic-bound-calls-stress.c new file mode 100644 index 0000000000000..ae65e89e1e45e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-bound-calls-stress.c @@ -0,0 +1,156 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +// expected-note@+8{{passing argument to parameter 'out_start' here}} +// expected-note@+7{{passing argument to parameter 'out_start' here}} +// expected-note@+6{{passing argument to parameter 'out_start' here}} +// expected-note@+5{{passing argument to parameter 'out_start' here}} +// expected-note@+4{{passing argument to parameter 'out_end' here}} +// expected-note@+3{{passing argument to parameter 'out_end' here}} +// expected-note@+2{{passing argument to parameter 'out_end' here}} +// expected-note@+1{{passing argument to parameter 'out_end' here}} +void callee_out_ranges(int *__ended_by(*out_end) *out_start, int **out_end); + +void caller_out_ranges_wrong_args(int *__ended_by(*out_end) *out_start, int **out_end) { + callee_out_ranges(out_start, out_end); + // XXX: rdar://97038292 + // expected-warning@+2{{incompatible pointer types passing 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single') to parameter of type 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{type of 'out_end', 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'), is incompatible with parameter of type 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single')}} + callee_out_ranges(out_start, *out_end); + // expected-warning@+3{{incompatible pointer types passing 'int *__single __ended_by(*out_end)' (aka 'int *__single') to parameter of type 'int *__single __ended_by(*out_end)*__single' (aka 'int *__single*__single'); remove *}} + // expected-warning@+2{{incompatible pointer types passing 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single') to parameter of type 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{type of 'out_start', 'int *__single __ended_by(*out_end)*__single' (aka 'int *__single*__single'), is incompatible with parameter of type 'int *__single __ended_by(*out_end)' (aka 'int *__single')}} + callee_out_ranges(*out_start, *out_end); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __ended_by(*out_end)' (aka 'int *__single') to parameter of type 'int *__single __ended_by(*out_end)*__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{type of 'out_start', 'int *__single __ended_by(*out_end)*__single' (aka 'int *__single*__single'), is incompatible with parameter of type 'int *__single __ended_by(*out_end)' (aka 'int *__single')}} + callee_out_ranges(*out_start, out_end); + // expected-error@+1{{type of 'out_end', 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single'), is incompatible with parameter of type 'int *__single __ended_by(*out_end)' (aka 'int *__single')}} + callee_out_ranges(out_end, out_start); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __ended_by(*out_end)' (aka 'int *__single') to parameter of type 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{type of 'out_end', 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single'), is incompatible with parameter of type 'int *__single __ended_by(*out_end)' (aka 'int *__single')}} + callee_out_ranges(out_end, *out_start); + // expected-warning@+3{{incompatible pointer types passing 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single') to parameter of type 'int *__single __ended_by(*out_end)*__single' (aka 'int *__single*__single'); remove *}} + // expected-warning@+2{{incompatible pointer types passing 'int *__single __ended_by(*out_end)' (aka 'int *__single') to parameter of type 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{type of 'out_end', 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'), is incompatible with parameter of type 'int *__single __ended_by(*out_end)' (aka 'int *__single')}} + callee_out_ranges(*out_end, *out_start); + // expected-warning@+2{{incompatible pointer types passing 'int *__single /* __started_by(*out_start) */ ' (aka 'int *__single') to parameter of type 'int *__single __ended_by(*out_end)*__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{type of 'out_end', 'int *__single /* __started_by(*out_start) */ *__single' (aka 'int *__single*__single'), is incompatible with parameter of type 'int *__single __ended_by(*out_end)' (aka 'int *__single')}} + callee_out_ranges(*out_end, out_start); +} + +// expected-note@+8{{passing argument to parameter 'start' here}} +// expected-note@+7{{passing argument to parameter 'start' here}} +// expected-note@+6{{passing argument to parameter 'start' here}} +// expected-note@+5{{passing argument to parameter 'start' here}} +// expected-note@+4{{passing argument to parameter 'end' here}} +// expected-note@+3{{passing argument to parameter 'end' here}} +// expected-note@+2{{passing argument to parameter 'end' here}} +// expected-note@+1{{passing argument to parameter 'end' here}} +void callee_in_ranges(int *__ended_by(end) start, int *end); + +void caller_in_ranges_wrong_args(int *__ended_by(end) start, int *end) { + callee_in_ranges(start, end); + // expected-warning@+3{{incompatible pointer types passing 'int *__single __ended_by(end)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single __ended_by(end)' (aka 'int *__single'); remove &}} + // expected-warning@+2{{incompatible pointer types passing 'int *__single /* __started_by(start) */ *__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single'); remove &}} + // expected-error@+1{{type of 'start', 'int *__single __ended_by(end)' (aka 'int *__single'), is incompatible with parameter of type 'int *__single __ended_by(end)' (aka 'int *__single')}} + callee_in_ranges(&start, &end); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __ended_by(end)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single __ended_by(end)' (aka 'int *__single'); remove &}} + // expected-error@+1{{type of 'start', 'int *__single __ended_by(end)' (aka 'int *__single'), is incompatible with parameter of type 'int *__single __ended_by(end)' (aka 'int *__single')}} + callee_in_ranges(&start, end); + // expected-warning@+2{{incompatible pointer types passing 'int *__single /* __started_by(start) */ *__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single'); remove &}} + // expected-error@+1{{type of 'end', 'int *__single /* __started_by(start) */ ' (aka 'int *__single'), is incompatible with parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single')}} + callee_in_ranges(start, &end); + callee_in_ranges(end, start); + // expected-warning@+3{{incompatible pointer types passing 'int *__single /* __started_by(start) */ *__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single __ended_by(end)' (aka 'int *__single'); remove &}} + // expected-warning@+2{{incompatible pointer types passing 'int *__single __ended_by(end)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single'); remove &}} + // expected-error@+1{{type of 'end', 'int *__single /* __started_by(start) */ ' (aka 'int *__single'), is incompatible with parameter of type 'int *__single __ended_by(end)' (aka 'int *__single')}} + callee_in_ranges(&end, &start); + // expected-warning@+2{{incompatible pointer types passing 'int *__single /* __started_by(start) */ *__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single __ended_by(end)' (aka 'int *__single'); remove &}} + // expected-error@+1{{type of 'end', 'int *__single /* __started_by(start) */ ' (aka 'int *__single'), is incompatible with parameter of type 'int *__single __ended_by(end)' (aka 'int *__single')}} + callee_in_ranges(&end, start); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __ended_by(end)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single'); remove &}} + // expected-error@+1{{type of 'start', 'int *__single __ended_by(end)' (aka 'int *__single'), is incompatible with parameter of type 'int *__single /* __started_by(start) */ ' (aka 'int *__single')}} + callee_in_ranges(end, &start); +} +// expected-note@+9{{passing argument to parameter 'out_count' here}} +// expected-note@+8{{passing argument to parameter 'out_count' here}} +// expected-note@+7{{passing argument to parameter 'out_count' here}} +// expected-note@+6{{passing argument to parameter 'out_buf' here}} +// expected-note@+5{{passing argument to parameter 'out_buf' here}} +// expected-note@+4{{passing argument to parameter 'out_buf' here}} +// expected-note@+3{{passing argument to parameter 'out_buf' here}} +// expected-note@+2{{passing argument to parameter 'out_buf' here}} +// expected-note@+1{{passing argument to parameter 'out_buf' here}} +void callee_out_count_buf(int *__counted_by(*out_count) *out_buf, int *out_count); + +void caller_out_count_wrong_args(int *__counted_by(*out_count) *out_buf, int *out_count) { + callee_out_count_buf(out_buf, out_count); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_out_count_buf(out_buf, *out_count); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __counted_by(*out_count)' (aka 'int *__single') to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_out_count_buf(*out_buf, *out_count); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __counted_by(*out_count)' (aka 'int *__single') to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single'); remove *}} + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single')}} + callee_out_count_buf(*out_buf, out_count); + // expected-warning@+3{{incompatible pointer types passing 'int *__single' to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single'); take the address with &}} + // expected-warning@+2{{incompatible pointer types passing 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single') to parameter of type 'int *__single'; dereference with *}} + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single')}} + callee_out_count_buf(out_count, out_buf); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_out_count_buf(*out_count, out_buf); + // expected-warning@+2{{incompatible pointer types passing 'int *__single' to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single'); take the address with &}} + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single __counted_by(*out_count)*__single' (aka 'int *__single*__single')}} + callee_out_count_buf(out_count, *out_buf); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_out_count_buf(*out_count, *out_buf); +} + +// expected-note@+8{{passing argument to parameter 'count' here}} +// expected-note@+7{{passing argument to parameter 'count' here}} +// expected-note@+6{{passing argument to parameter 'count' here}} +// expected-note@+5{{passing argument to parameter 'buf' here}} +// expected-note@+4{{passing argument to parameter 'buf' here}} +// expected-note@+3{{passing argument to parameter 'buf' here}} +// expected-note@+2{{passing argument to parameter 'buf' here}} +// expected-note@+1{{passing argument to parameter 'buf' here}} +void callee_in_count_buf(int *__counted_by(count) buf, int count); + +void caller_in_count_wrong_args(int *__counted_by(count) buf, int count) { + callee_in_count_buf(buf, count); + // expected-warning@+3{{incompatible pointer types passing 'int *__single __counted_by(count)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single __counted_by(count)' (aka 'int *__single'); remove &}} + // expected-error@+2{{incompatible pointer to integer conversion passing 'int *__bidi_indexable' to parameter of type 'int'; remove &}} + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single __counted_by(count)' (aka 'int *__single')}} + callee_in_count_buf(&buf, &count); + // expected-warning@+2{{incompatible pointer types passing 'int *__single __counted_by(count)*__bidi_indexable' (aka 'int *__single*__bidi_indexable') to parameter of type 'int *__single __counted_by(count)' (aka 'int *__single'); remove &}} + // expected-error@+1{{incompatible dynamic count pointer argument to parameter of type 'int *__single __counted_by(count)' (aka 'int *__single')}} + callee_in_count_buf(&buf, count); + // expected-error@+2{{incompatible pointer to integer conversion passing 'int *__bidi_indexable' to parameter of type 'int'; remove &}} + // expected-error@+1{{passing address of 'count' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + callee_in_count_buf(buf, &count); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_in_count_buf(count, buf); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_in_count_buf(count, *buf); + // expected-error@+2{{incompatible pointer to integer conversion passing 'int *__single __counted_by(count)' (aka 'int *__single') to parameter of type 'int'; dereference with *}} + // expected-error@+1{{passing address of 'count' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + callee_in_count_buf(&count, buf); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + callee_in_count_buf(count, &buf); + // expected-error@+1{{passing address of 'count' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + callee_in_count_buf(&count, *buf); +} + +// XXX: rdar://97041755 +void callee_vargs(int, ...); + +void caller_vargs_with_out_count(int *__counted_by(count) buf, int count) { + callee_vargs(1, &buf); + callee_vargs(1, &count); + callee_vargs(2, &buf, &count); + callee_vargs(2, buf, &count); + callee_vargs(2, &buf, count); + callee_vargs(2, buf, count); +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-bound-escape.c b/clang/test/BoundsSafety/Sema/dynamic-bound-escape.c new file mode 100644 index 0000000000000..374bfcaf45b83 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-bound-escape.c @@ -0,0 +1,147 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// __counted_by + +void cb_in_in(int *__counted_by(len) buf, int len) { + int **b1 = &buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b2 = buf; + int b3 = *buf; + + int *l1 = &len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int l2 = len; +} + +void cb_in_out(int *__counted_by(*len) buf, int *len) { + int **b1 = &buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b2 = buf; + int b3 = *buf; + + int **l1 = &len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *l2 = len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int l3 = *len; +} + +void cb_out_in(int *__counted_by(len) * buf, int len) { + int ***b1 = &buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b2 = buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b3 = *buf; + int b4 = **buf; + + int *l1 = &len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int l2 = len; +} + +void cb_out_out(int *__counted_by(*len) * buf, int *len) { + int ***b1 = &buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b2 = buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b3 = *buf; + int b4 = **buf; + + int **l1 = &len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *l2 = len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int l3 = *len; +} + +void ptr_cb_in_in(int **__counted_by(len) buf, int len) { + int ***b1 = &buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b2 = buf; + int *b3 = *buf; + int b4 = **buf; +} + +void ptr_cb_out_in(int **__counted_by(len) * buf, int len) { + int ****b1 = &buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int ***b2 = buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b3 = *buf; + int *b4 = **buf; + int b5 = ***buf; +} + +// __sized_by +// Just check if the diagnostic says __sized_by instead of __counted_by. + +void sb_in_in(int *__sized_by(size) buf, int size) { + int **b1 = &buf; // expected-error{{pointer with '__sized_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b2 = buf; + int b3 = *buf; + + int *s1 = &size; // expected-error{{variable referred to by '__sized_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int s2 = size; +} + +// __ended_by + +void eb_in_in(int *__ended_by(end) buf, int *end) { + int **b1 = &buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b2 = buf; + int b3 = *buf; + + int **e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *e2 = end; + int e3 = *end; +} + +void eb_in_out(int *__ended_by(*end) buf, int **end) { + int **b1 = &buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b2 = buf; + int b3 = *buf; + + int ***e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **e2 = end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *e3 = *end; + int e4 = **end; +} + +void eb_out_in(int *__ended_by(end) * buf, int *end) { + int ***b1 = &buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b2 = buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b3 = *buf; + int b4 = **buf; + + int **e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *e2 = end; + int e3 = *end; +} + +void eb_out_out(int *__ended_by(*end) * buf, int **end) { + int ***b1 = &buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b2 = buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int *b3 = *buf; + int b4 = **buf; + + int ***e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **e2 = end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int *e3 = *end; + int e4 = **end; +} + +void ptr_eb_in_in(int **__ended_by(end) buf, int **end) { + int ***b1 = &buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b2 = buf; + int *b3 = *buf; + int b4 = **buf; + + int ***e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **e2 = end; + int *e3 = *end; + int e4 = **end; +} + +void ptr_eb_out_out(int **__ended_by(*end) * buf, int ***end) { + int ****b1 = &buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int ***b2 = buf; // expected-error{{pointer with '__ended_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **b3 = *buf; + int *b4 = **buf; + int b5 = ***buf; + + int ****e1 = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int ***e2 = end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **e3 = *end; + int *e4 = **end; + int e5 = ***end; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-bound-init-list-side-effect.c b/clang/test/BoundsSafety/Sema/dynamic-bound-init-list-side-effect.c new file mode 100644 index 0000000000000..c0fe050bc313d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-bound-init-list-side-effect.c @@ -0,0 +1,179 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int getlen(); +int side_effect(); +void *__bidi_indexable getptr(); + +struct CountedByData { + int *__counted_by(len + 1) ptr; + int len; +}; + +struct SizedByData { + void *__sized_by(len) ptr; + unsigned len; +}; + +struct CountedByOrNullData { + int *__counted_by_or_null(len + 1) ptr; + int len; +}; + +struct SizedByOrNullData { + void *__sized_by_or_null(len) ptr; + unsigned len; +}; + +struct RangedData { + int *__ended_by(iter) start; + int *__ended_by(end) iter; + void *end; +}; + +struct CountedBySizedByDataMix { + int *__counted_by(len + 1) ptr; + int *__sized_by(len) ptr2; + int len; +}; + +struct InnerStruct { + int value; +}; +struct CountedByDataWithSubStruct { + int *__counted_by(len + 1) ptr; + int len; + struct InnerStruct inner; +}; + +struct CountedByDataWithSubStructAtStart { + struct InnerStruct inner; + int *__counted_by(len + 1) ptr; + int len; +}; + +struct CountedByDataWithOtherField { + int *__counted_by(len + 1) ptr; + int len; + int other_data; +}; + + + +int g[10]; + +void TestCountedBy(void) { + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByData c1 = { g, getlen() }; + // expected-error@+2{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByData c2 = { getptr(), getlen() }; + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + struct CountedByData c3 = { getptr(), 0 }; + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + struct CountedByData c4 = { getptr() }; + // expected-error@+1{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + struct CountedByData c5 = { .ptr = getptr() }; + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByData c6 = { .len = getlen() }; + // expected-error@+2{{initalizer for '__counted_by' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByData c7 = { .ptr = getptr(), .len = getlen() }; + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedBySizedByDataMix c8 = { g, g, getlen() }; + + int* ptr; + struct CountedByDataWithSubStruct c9 = {ptr, 4, {0}}; // OK + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + struct CountedByDataWithSubStruct c10 = {ptr, 4, {side_effect()}}; + struct CountedByDataWithSubStructAtStart c11 = {{0}, ptr, 4}; // OK + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + struct CountedByDataWithSubStructAtStart c12 = {{side_effect()}, ptr, 4}; + struct CountedByDataWithOtherField c13 = {ptr, 4, 0}; // OK + // expected-warning@+1{{initializer 'side_effect()' has a side effect; this may lead to an unexpected result because the evaluation order of initialization list expressions is indeterminate}} + struct CountedByDataWithOtherField c14 = {ptr, 4, side_effect()}; +} + +void TestSizedBy(void) { + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByData s1 = { g, getlen() }; + // expected-error@+2{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByData s2 = { getptr(), getlen() }; + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + struct SizedByData s3 = { getptr(), 0 }; + // expected-warning@+2{{possibly initializing 's4.ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + struct SizedByData s4 = { getptr() }; + // expected-warning@+2{{possibly initializing 's5.ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + // expected-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + struct SizedByData s5 = { .ptr = getptr() }; + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByData s6 = { .len = getlen() }; + // expected-error@+2{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByData s7 = { .ptr = getptr(), .len = getlen() }; +} + +void TestCountedByOrNull(void) { + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByOrNullData c1 = { g, getlen() }; + // expected-error@+2{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByOrNullData c2 = { getptr(), getlen() }; + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + struct CountedByOrNullData c3 = { getptr(), 0 }; + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + struct CountedByOrNullData c4 = { getptr() }; + // expected-error@+1{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + struct CountedByOrNullData c5 = { .ptr = getptr() }; + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByOrNullData c6 = { .len = getlen() }; + // expected-error@+2{{initalizer for '__counted_by_or_null' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for count with side effects is not yet supported}} + struct CountedByOrNullData c7 = { .ptr = getptr(), .len = getlen() }; +} + +void TestSizedByOrNull(void) { + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByOrNullData s1 = { g, getlen() }; + // expected-error@+2{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByOrNullData s2 = { getptr(), getlen() }; + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + struct SizedByOrNullData s3 = { getptr(), 0 }; + // expected-warning@+2{{possibly initializing 's4.ptr' of type 'void *__single __sized_by_or_null(len)' (aka 'void *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + struct SizedByOrNullData s4 = { getptr() }; + // expected-warning@+2{{possibly initializing 's5.ptr' of type 'void *__single __sized_by_or_null(len)' (aka 'void *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + // expected-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + struct SizedByOrNullData s5 = { .ptr = getptr() }; + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByOrNullData s6 = { .len = getlen() }; + // expected-error@+2{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for size with side effects is not yet supported}} + struct SizedByOrNullData s7 = { .ptr = getptr(), .len = getlen() }; +} + + +void TestRanged(void) { + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + struct RangedData r1 = { getptr(), g, g }; + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + struct RangedData r2 = { g, getptr(), g }; + // expected-error@+1{{initalizer for end pointer with side effects is not yet supported}} + struct RangedData r3 = { g, g, getptr() }; + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + struct RangedData r4 = { .start = getptr(), .iter = g, .end = g }; + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + struct RangedData r5 = { .start = g, .iter = getptr(), .end = g }; + // expected-error@+1{{initalizer for end pointer with side effects is not yet supported}} + struct RangedData r6 = { .start = g, .iter = g, .end = getptr() }; + // expected-error@+3{{initalizer for end pointer with side effects is not yet supported}} + // expected-error@+2{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + // expected-error@+1{{initalizer for '__ended_by' pointer with side effects is not yet supported}} + struct RangedData r7 = { getptr(), getptr(), getptr() }; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-bound-pointer-member-side-effects.c b/clang/test/BoundsSafety/Sema/dynamic-bound-pointer-member-side-effects.c new file mode 100644 index 0000000000000..119fa5d503332 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-bound-pointer-member-side-effects.c @@ -0,0 +1,39 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct CountedByStruct { + int len; + int *__counted_by(len) buf; +}; + +struct EndedByStruct { + int *end; + int *__ended_by(end) iter; + int *__ended_by(iter) start; +}; + +struct CountedByStruct RetCountedByVal(void); +struct CountedByStruct *RetCountedByPtr(void); +struct EndedByStruct RetEndedByVal(void); +struct EndedByStruct *RetEndedByPtr(void); + +void Test(void) { + int *b1 = RetCountedByVal().buf; // ok + int *b2 = RetCountedByPtr()->buf; // ok + int l1 = RetCountedByVal().len; // ok + int l2 = RetCountedByPtr()->len; // ok + + int *b3 = RetEndedByVal().end; // ok + int *b4 = RetEndedByVal().iter; // ok + int *b5 = RetEndedByVal().start; // ok + + int *b6 = RetEndedByPtr()->end; // ok + int *b7 = RetEndedByPtr()->iter; // ok + int *b8 = RetEndedByPtr()->start; // ok + +} + +// expected-no-diagnostics diff --git a/clang/test/BoundsSafety/Sema/dynamic-bounds-assignment-extern.c b/clang/test/BoundsSafety/Sema/dynamic-bounds-assignment-extern.c new file mode 100644 index 0000000000000..b5233bf47e1dd --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-bounds-assignment-extern.c @@ -0,0 +1,26 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +struct s_t; +extern unsigned size; +extern struct s_t * __sized_by(size) buf; + +unsigned size; +struct s_t *__sized_by(size) buf; +// expected-error@+1{{assignment to 'struct s_t *__single __sized_by(size)' (aka 'struct s_t *__single') 'buf' requires corresponding assignment to 'size'; add self assignment 'size = size' if the value has not changed}} +void assign_to_sized_by(struct s_t *__indexable p) { buf = p; } + +struct s_t; +extern struct s_t *end; +extern struct s_t * __ended_by(end) start; + +struct s_t *end; +struct s_t *__ended_by(end) start; + +// expected-error@+1{{assignment to 'struct s_t *__single __ended_by(end)' (aka 'struct s_t *__single') 'start' requires corresponding assignment to 'end'; add self assignment 'end = end' if the value has not changed}} +void assign_to_ended_by(struct s_t *__indexable p) { start = p; } +// expected-error@+1{{assignment to 'struct s_t *__single __ended_by(end)' (aka 'struct s_t *__single') 'end' requires corresponding assignment to 'start'; add self assignment 'start = start' if the value has not changed}} +void assign_to_end(struct s_t *__indexable p) { end = p; } diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-arg-ooe-side-effect.c b/clang/test/BoundsSafety/Sema/dynamic-count-arg-ooe-side-effect.c new file mode 100644 index 0000000000000..61556c6eb3e04 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-arg-ooe-side-effect.c @@ -0,0 +1,43 @@ + + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void Foo(int *__counted_by(len) buf, char *dummy, int len) {} +void Bar(int len, char *dummy, int *__counted_by(len) buf) {} +void Baz(int *__counted_by(*out_len)* out_buf, char *dummy, int *out_len) {} +void Qux(int *out_len, char *dummy, int *__counted_by(*out_len)* out_buf) {} + +unsigned long get_len(void *__bidi_indexable ptr); +unsigned long trap_if_bigger_than_max(unsigned long len); + +char *side_effect_cp(); +int *__bidi_indexable side_effect_ip(); + +int Test() { + int arr[10]; + char *cp; + int len; + + Foo(arr, cp, 10); + Foo(side_effect_ip(), side_effect_cp(), 10); // ok + + Foo(side_effect_ip(), cp, len); // ok + + Foo(arr, side_effect_cp(), len); // ok + + Bar(len, side_effect_cp(), side_effect_ip()); // ok + Bar(get_len(arr), cp, arr); // ok + + int cnt; + int *__counted_by(cnt) ptr; + // Out parameters are okay because it only takes already type safe arguments. + Baz(&ptr, side_effect_cp(), &cnt); // ok + Baz(&ptr, cp, &cnt); // ok + Qux(&cnt, side_effect_cp(), &ptr); // ok + return 0; +} + +// expected-no-diagnostics \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-as-out-params-addrof-deref.c b/clang/test/BoundsSafety/Sema/dynamic-count-as-out-params-addrof-deref.c new file mode 100644 index 0000000000000..bc400a7760b72 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-as-out-params-addrof-deref.c @@ -0,0 +1,17 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +int foo(int *__counted_by(*len)* out_buf, int *len, int *dummy_len); + +int bar() { + int len; + int dummy_len; + int *__counted_by(len) ptr; + foo(&*&*&ptr, &*&len, &dummy_len); + foo(&*(&*&ptr), (&(*(int*)&len)), &dummy_len); + // switched len and dummy_len + foo(&*&ptr, &dummy_len, &*&len); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-invalid-rhs.c b/clang/test/BoundsSafety/Sema/dynamic-count-invalid-rhs.c new file mode 100644 index 0000000000000..4717ff5d0d985 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-invalid-rhs.c @@ -0,0 +1,20 @@ + + +// RUN: %clang_cc1 -fsyntax-only -verify -fbounds-safety %s +#include +#include + +// regression test for crash + +struct S { + int * __counted_by(len) ptr; + int len; + int fam[__counted_by(len)]; +}; + +void bar(struct S * __bidi_indexable p, int * __counted_by(len) p2, int len) { + struct S * __single p3 = p; + // expected-error@+1{{use of undeclared identifier 'asdf'}} + p3->len = asdf; + p3->ptr = p2; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-designated.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-designated.c new file mode 100644 index 0000000000000..8a68e1ea92d34 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-designated.c @@ -0,0 +1,98 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +#define INT_MIN (-2147483648) + +struct S { int len; int *__counted_by(len) ptr; }; + +// expected-note@+1{{consider adding '__counted_by(2)' to 'a'}} +void TestCountedBy(int *a) { + // expected-note@+1{{'arr' declared here}} + int arr[] = {1,2,3}; + // expected-error@+1{{implicitly initializing 's1.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with null always fails}} + struct S s1 = { .len = 4 }; + struct S s2 = { .len = 0 }; // ok + // expected-error@+1{{negative count value of -1 for 's3.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s3 = { .len = -1 }; + // expected-error@+1{{initializing 's4.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with null always fails}} + struct S s4 = { .ptr = 0, .len = 4 }; + struct S s5 = { .len = 0, .ptr = 0 }; // ok + // expected-error@+1{{negative count value of -1 for 's6.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s6 = { .len = -1, .ptr = 0 }; + // expected-error@+1{{initializing 's7.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with array 'arr' (which has 3 elements) always fails}} + struct S s7 = { 4, .ptr = arr }; + struct S s8 = { .ptr = arr, .len = 3 }; // ok + // expected-error@+1{{negative count value of -2147483648 for 's9.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s9 = { .len = INT_MIN, .ptr = arr }; + // expected-error@+1{{initializing 's10.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 2 with 'int *__single' always fails}} + struct S s10 = { .len = 2, .ptr = a }; + struct S s11 = { .len = 1, .ptr = a }; + // expected-error@+1{{negative count value of -2147483648 for 's12.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s12 = { .len = INT_MIN, .ptr = a }; + struct S s13 = { .ptr = 0 }; // ok + // expected-warning@+1{{possibly initializing 's14.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + struct S s14 = { .ptr = arr }; + // expected-warning@+1{{possibly initializing 's15.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + struct S s15 = { .ptr = a }; +} + +struct U { int len; void *__sized_by(len - 1) ptr; }; + +void TestSizedByP1(char *a) { + char arr[] = {1,2,3}; + // expected-error@+1{{implicitly initializing 's1.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single') and size value of 3 with null always fails}} + struct U s1 = { .len = 4 }; + // expected-error@+1{{negative size value of -1 for 's2.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s2 = { .len = 0 }; + // expected-error@+1{{negative size value of -1 for 's4.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s4 = { .ptr = 0, .len = 0 }; + struct U s5 = { .len = 4, .ptr = arr }; // ok + // expected-error@+1{{negative size value of -1 for 's6.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s6 = { .len = 0, .ptr = arr }; + struct U s7 = { .len = 2, .ptr = a }; + struct U s8 = { .len = 1, .ptr = a }; + // expected-error@+1{{negative size value of -1 for 's9.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s9 = { .ptr = a, .len = 0 }; + // expected-error@+1{{negative size value of -1 for 's10.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s10; + // expected-error@+1{{negative size value of -1 for 's11[0].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s11[2]; + // expected-error@+1{{negative size value of -1 for 's12[0].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s12[1] = { {.len = 0} }; + struct U s13[1] = { {.len = 1} }; // ok + // expected-error@+1{{negative size value of -1 for 's14[1].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s14[2] = { {.len = 1} }; + + // expected-error@+1{{negative size value of -1 for 's15.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s15 = { .ptr = 0 }; + // expected-error@+1{{negative size value of -1 for 's16.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s16 = { .ptr = arr }; + // expected-error@+1{{negative size value of -1 for 's17.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s17 = { .ptr = a }; + // expected-error@+1{{negative size value of -1 for 's19[0].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s19[1] = { {.ptr = 0} }; + // expected-error@+2{{negative size value of -1 for 's20[0].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + // expected-error@+1{{negative size value of -1 for 's20[1].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s20[2] = { {.ptr = arr} }; +} + +struct T { void *__sized_by(len + 1) ptr; int len; }; + +void TestSizedByPP1PtrFirst(void) { + // expected-note@+1{{'arr' declared here}} + char arr[] = {1, 2, 3}; + // expected-error@+1{{initializing 's1.ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + struct T s1 = { .ptr = 0 }; + struct T s2 = { .ptr = arr }; // ok + // expected-error@+1{{implicitly initializing 's3[1].ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + struct T s3[4] = { {.len = -1} }; + struct T s4[1] = { {.len = -1} }; // ok + // expected-error@+1{{implicitly initializing 's5[1].ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + struct T s5[4] = { {.len = -1, .ptr = 0} }; + struct T s6[1] = { {.len = -1, .ptr = 0} }; // ok + // expected-error@+1{{initializing 's7.ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 4 with array 'arr' (which has 3 bytes) always fails}} + struct T s7 = { .ptr = arr, 3 }; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-union.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-union.c new file mode 100644 index 0000000000000..4592abbfcd6b8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-union.c @@ -0,0 +1,141 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +union foo { + struct { + int *__counted_by(len) p; + int len; + } v1; + struct { + int dummy; + int *__counted_by(len) p; + int len; + } v2; +}; + +void test_foo(void) { + int arr[] = { 1, 2, 3 }; // expected-note 2{{'arr' declared here}} + + union foo f0; + + union foo f1 = {}; + + union foo f2 = { .v1 = {} }; + + union foo f3 = { .v2 = {} }; + + union foo f4 = { .v1 = { .p = arr, .len = 3 } }; + union foo f5 = { .v1 = { .p = arr, .len = 4 } }; // expected-error{{initializing 'f5.v1.p' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with array 'arr' (which has 3 elements) always fails}} + + union foo f6 = { .v2 = { .dummy = 42, .p = arr, .len = 3 } }; + union foo f7 = { .v2 = { .dummy = 42, .p = arr, .len = 4 } }; // expected-error{{initializing 'f7.v2.p' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with array 'arr' (which has 3 elements) always fails}} +} + +union bar { + struct { + int *__counted_by(len) p; + int len; + } v1; + struct { + int dummy; + int *__counted_by(len + 1) p; + int len; + } v2; +}; + +void test_bar(void) { + // expected-error@+1{{implicitly initializing 'b0.v2.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + union bar b0; + + // ok (clang picks the first field -- v1) + // TODO: Should we emit an error anyway? + union bar b1 = {}; + + union bar b2 = { .v1 = {} }; + + // expected-error@+1{{implicitly initializing 'b3.v2.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + union bar b3 = { .v2 = {} }; +} + +union baz { + struct { + int *__counted_by(len + 1) p; + int len; + } v1; + struct { + int dummy; + int *__counted_by(len) p; + int len; + } v2; +}; + +void test_baz(void) { + // expected-error@+1{{implicitly initializing 'b0.v1.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + union baz b0; + + // expected-error@+1{{implicitly initializing 'b1.v1.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + union baz b1 = {}; + + // expected-error@+1{{implicitly initializing 'b2.v1.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + union baz b2 = { .v1 = {} }; + + union baz b3 = { .v2 = {} }; +} + +struct qux { + union { + struct { + int *__counted_by(len) p; + int len; + } v1; + struct { + int dummy; + int *__counted_by(len + 1) p; + int len; + } v2; + }; +}; + +void test_qux(void) { + // expected-error@+1{{implicitly initializing 'q0..v2.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct qux q0; + + // expected-error@+1{{implicitly initializing 'q1..v2.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct qux q1 = {}; + + struct qux q2 = { .v1 = {} }; + + // expected-error@+1{{implicitly initializing 'q3..v2.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct qux q3 = { .v2 = {} }; +} + +struct quux { + union { + struct { + int *__counted_by(len + 1) p; + int len; + } v1; + struct { + int dummy; + int *__counted_by(len) p; + int len; + } v2; + }; +}; + +void test_quux(void) { + // expected-error@+1{{implicitly initializing 'q0..v1.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct quux q0; + + // expected-error@+1{{implicitly initializing 'q1..v1.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct quux q1 = {}; + + // expected-error@+1{{implicitly initializing 'q2..v1.p' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct quux q2 = { .v1 = {} }; + + struct quux q3 = { .v2 = {} }; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-zero-length.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-zero-length.c new file mode 100644 index 0000000000000..0e3a0a66e3aa2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list-zero-length.c @@ -0,0 +1,45 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(count) ptr; + int count; +}; + +void foo(void) { + int p = 0; + struct S h = { &p }; // expected-warning{{possibly initializing 'h.ptr' of type 'int *__single __counted_by(count)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + struct S i = { .ptr = &p }; // expected-warning{{possibly initializing 'i.ptr' of type 'int *__single __counted_by(count)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + + struct S a = { 0, 0 }; // ok + struct S b = { 0 }; // ok + struct S c = { }; // ok + struct S d = { .ptr = 0, .count = 0 }; // ok + struct S e = { .ptr = 0 }; // ok + + struct S f = { &p, 0 }; // ok + struct S g = { .ptr = &p, .count = 0 }; // ok +} + +struct T { + int *__counted_by(count * 1) ptr; + int count; +}; + +void bar(void) { + int p = 0; + struct T h = { &p }; // expected-warning{{possibly initializing 'h.ptr' of type 'int *__single __counted_by(count * 1)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + struct T i = { .ptr = &p }; // expected-warning{{possibly initializing 'i.ptr' of type 'int *__single __counted_by(count * 1)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + + struct T a = { 0, 0 }; // ok + struct T b = { 0 }; // ok + struct T c = { }; // ok + struct T d = { .ptr = 0, .count = 0 }; // ok + struct T e = { .ptr = 0 }; // ok + + struct T f = { &p, 0 }; // ok + struct T g = { .ptr = &p, .count = 0 }; // ok +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list.c new file mode 100644 index 0000000000000..82152aca856b9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-init-list.c @@ -0,0 +1,267 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +#define INT_MIN (-2147483648) + +struct S { int len; int *__counted_by(len) ptr; }; + +// expected-note@+1{{consider adding '__counted_by(2)' to 'a'}} +void TestCountedBy(int *a) { + // expected-note@+1{{'arr' declared here}} + int arr[] = {1,2,3}; + // expected-error@+1{{implicitly initializing 's1.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with null always fails}} + struct S s1 = { 4 }; + struct S s2 = { 0 }; // ok + // expected-error@+1{{negative count value of -1 for 's3.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s3 = { -1 }; + // expected-error@+1{{initializing 's4.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with null always fails}} + struct S s4 = { 4, 0 }; + struct S s5 = { 0, 0 }; // ok + // expected-error@+1{{negative count value of -1 for 's6.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s6 = { -1, 0 }; + // expected-error@+1{{initializing 's7.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 4 with array 'arr' (which has 3 elements) always fails}} + struct S s7 = { 4, arr }; + struct S s8 = { 3, arr }; // ok + // expected-error@+1{{negative count value of -2147483648 for 's9.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s9 = { INT_MIN, arr }; + // expected-error@+1{{initializing 's10.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 2 with 'int *__single' always fails}} + struct S s10 = { 2, a }; + struct S s11 = { 1, a }; + // expected-error@+1{{negative count value of -2147483648 for 's12.ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + struct S s12 = { INT_MIN, a }; +} + +struct V { unsigned len; void *__sized_by(len) ptr; }; + +// expected-note@+1{{consider adding '__sized_by(2)' to 'a'}} +void TestSizedBy(char *a) { + // expected-note@+1{{'arr' declared here}} + char arr[] = {1,2,3}; + // expected-error@+1{{implicitly initializing 's1.ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') and size value of 4 with null always fails}} + struct V s1 = { 4 }; + struct V s2 = { 0 }; + // expected-error@+1{{initializing 's3.ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') and size value of 4 with null always fails}} + struct V s3 = { 4, 0 }; + struct V s4 = { 0, 0 }; + // expected-error@+1{{initializing 's5.ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') and size value of 4 with array 'arr' (which has 3 bytes) always fails}} + struct V s5 = { 4, arr }; + struct V s6 = { 3, arr }; + // expected-error@+1{{initializing 's7.ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') and size value of 2 with 'char *__single' and pointee of size 1 always fails}} + struct V s7 = { 2, a }; + struct V s8 = { 1, a }; +} + +struct S2 { int len; int *__counted_by_or_null(len) ptr; }; + +void TestCountedByOrNull(int *a) { + // expected-note@+1{{'arr' declared here}} + int arr[] = {1,2,3}; + struct S2 s1 = { 4 }; // ok + struct S2 s2 = { 0 }; // ok + struct S2 s3 = { -1 }; // ok + struct S2 s4 = { 4, 0 }; // ok + struct S2 s5 = { 0, 0 }; // ok + struct S2 s6 = { -1, 0 }; // ok + // expected-error@+1{{initializing 's7.ptr' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') and count value of 4 with array 'arr' (which has 3 elements) always fails}} + struct S2 s7 = { 4, arr }; + struct S2 s8 = { 3, arr }; // ok + // expected-error@+1{{negative count value of -2147483648 for 's9.ptr' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single')}} + struct S2 s9 = { INT_MIN, arr }; + struct S2 s10 = { 2, a }; + struct S2 s11 = { 1, a }; + // expected-error@+1{{possibly initializing 's12.ptr' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') and count value of -2147483648 with non-null; explicitly initialize null to remove this warning}} + struct S2 s12 = { INT_MIN, a }; +} + +struct V2 { unsigned len; void *__sized_by_or_null(len) ptr; }; + +void TestSizedByOrNull(char *a) { + // expected-note@+1{{'arr' declared here}} + char arr[] = {1,2,3}; + struct V2 s1 = { 4 }; // ok + struct V2 s2 = { 0 }; // ok + struct V2 s3 = { 4, 0 }; // ok + struct V2 s4 = { 0, 0 }; // ok + // expected-error@+1{{initializing 's5.ptr' of type 'void *__single __sized_by_or_null(len)' (aka 'void *__single') and size value of 4 with array 'arr' (which has 3 bytes) always fails}} + struct V2 s5 = { 4, arr }; + struct V2 s6 = { 3, arr }; + struct V2 s7 = { 2, a }; + struct V2 s8 = { 1, a }; +} + +struct U { int len; void *__sized_by(len - 1) ptr; }; + +void TestSizedByP1(char *a) { + char arr[] = {1,2,3}; + // expected-error@+1{{implicitly initializing 's1.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single') and size value of 3 with null always fails}} + struct U s1 = { 4 }; + // expected-error@+1{{negative size value of -1 for 's2.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s2 = { 0 }; + // expected-error@+1{{negative size value of -1 for 's4.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s4 = { 0, 0 }; + struct U s5 = { 4, arr }; // ok + // expected-error@+1{{negative size value of -1 for 's6.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s6 = { 0, arr }; + struct U s7 = { 2, a }; + struct U s8 = { 1, a }; + // expected-error@+1{{negative size value of -1 for 's9.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s9 = { 0, a }; + // expected-error@+1{{negative size value of -1 for 's10.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s10; + // expected-error@+1{{negative size value of -1 for 's11[0].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s11[2]; + // expected-error@+1{{negative size value of -1 for 's12[0].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s12[1] = { 0 }; + struct U s13[1] = { 1 }; // ok + // expected-error@+1{{negative size value of -1 for 's14[1].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct U s14[2] = { 1 }; +} + +struct U2 { int len; void *__sized_by_or_null(len - 1) ptr; }; + +void TestSizedByOrNullP1(char *a) { + char arr[] = {1,2,3}; + struct U2 s1 = { 4 }; // ok + struct U2 s2 = { 0 }; // ok + struct U2 s4 = { 0, 0 }; // ok + struct U2 s5 = { 4, arr }; // ok + // expected-error@+1{{negative size value of -1 for 's6.ptr' of type 'void *__single __sized_by_or_null(len - 1)' (aka 'void *__single')}} + struct U2 s6 = { 0, arr }; + struct U2 s7 = { 2, a }; + struct U2 s8 = { 1, a }; + // expected-error@+1{{possibly initializing 's9.ptr' of type 'void *__single __sized_by_or_null(len - 1)' (aka 'void *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + struct U2 s9 = { 0, a }; + struct U2 s10; // ok + struct U2 s11[2]; // ok + struct U2 s12[1] = { 0 }; // ok + struct U2 s13[1] = { 1 }; // ok + struct U2 s14[2] = { 1 }; // ok +} + +struct T { void *__sized_by(len - 1) ptr; int len; }; + +void TestSizedByP1PtrFirst() { + char arr[] = {1, 2, 3}; + // expected-error@+1{{negative size value of -1 for 's1.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct T s1 = { 0 }; + // expected-error@+1{{negative size value of -1 for 's2.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct T s2 = { arr }; + // expected-error@+1{{negative size value of -1 for 's3[1].ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct T s3[4] = { arr, 4}; + struct T s4[1] = { arr, 4}; // ok +} + +struct T2 { void *__sized_by_or_null(len - 1) ptr; int len; }; + +void T2estSizedByOrNullP1PtrFirst() { + char arr[] = {1, 2, 3}; + struct T2 s1 = { 0 }; // ok + // expected-error@+1{{negative size value of -1 for 's2.ptr' of type 'void *__single __sized_by_or_null(len - 1)' (aka 'void *__single')}} + struct T2 s2 = { arr }; + struct T2 s3[4] = { arr, 4}; // ok + struct T2 s4[1] = { arr, 4}; // ok +} + +struct W { void *__sized_by(len + 1) ptr; int len; }; + +void TestSizedByPP1PtrFirst() { + // expected-note@+1{{'arr' declared here}} + char arr[] = {1, 2, 3}; + // expected-error@+1{{initializing 's1.ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + struct W s1 = { 0 }; + struct W s2 = { arr }; + // expected-error@+1{{implicitly initializing 's3[1].ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 1 with null always fails}} + struct W s3[4] = { 0, -1 }; + struct W s4[1] = { 0, -1 }; // ok + // expected-error@+1{{initializing 's5.ptr' of type 'void *__single __sized_by(len + 1)' (aka 'void *__single') and size value of 4 with array 'arr' (which has 3 bytes) always fails}} + struct W s5 = { arr, 3 }; +} + +struct W2 { void *__sized_by_or_null(len + 1) ptr; int len; }; + +void TestSizedByPP1PtrFirstOrNull() { + // expected-note@+1{{'arr' declared here}} + char arr[] = {1, 2, 3}; + struct W2 s1 = { 0 }; // ok + struct W2 s2 = { arr }; // ok + struct W2 s3[4] = { 0, -1 }; // ok + struct W2 s4[1] = { 0, -1 }; // ok + // expected-error@+1{{initializing 's5.ptr' of type 'void *__single __sized_by_or_null(len + 1)' (aka 'void *__single') and size value of 4 with array 'arr' (which has 3 bytes) always fails}} + struct W2 s5 = { arr, 3 }; +} + +// TODO: rdar://76377847 +void TestAssignAfterDeclNoError(void) { + char arr[] = {1, 2, 3}; + // expected-error@+1{{negative size value of -1 for 's.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct T s; + s.ptr = arr; + s.len = 4; +} + +void TestAssignAfterDeclError(void) { + char arr[] = {1, 2, 3}; + // expected-error@+1{{negative size value of -1 for 's.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct T s; + (void)s.ptr; + s.ptr = arr; + s.len = 4; +} + +void T2estAssignAfterDeclNoErrorOrNull(void) { + char arr[] = {1, 2, 3}; + struct T2 s; + s.ptr = arr; + s.len = 4; +} + +void T2estAssignAfterDeclErrorOrNull(void) { + char arr[] = {1, 2, 3}; + struct T2 s; + (void)s.ptr; + s.ptr = arr; + s.len = 4; +} + +struct NestedS { + struct S s; +}; + +struct ReallyNestedS { + struct NestedS nested_s[2]; +}; + +// struct S has __counted_by(len), so zero-init is OK. +void TestImplicitNestedS(void) { + struct NestedS nested_s; + struct NestedS nested_s_arr[3]; + + struct ReallyNestedS really_nested_s; + struct ReallyNestedS really_nested_s_arr[3]; +} + +struct NestedU { + struct U u; +}; + +struct ReallyNestedU { + struct NestedU nested_u[2]; +}; + +// struct U has __sized_by(len - 1), so zero-init is BAD. +void TestImplicitNestedU(void) { + // expected-error@+1{{negative size value of -1 for 'nested_u.u.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct NestedU nested_u; + + // expected-error@+1{{negative size value of -1 for 'nested_u_arr[0].u.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct NestedU nested_u_arr[3]; + + // expected-error@+1{{negative size value of -1 for 'really_nested_u.nested_u[0].u.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct ReallyNestedU really_nested_u; + + // expected-error@+1{{negative size value of -1 for 'really_nested_u_arr[0].nested_u[0].u.ptr' of type 'void *__single __sized_by(len - 1)' (aka 'void *__single')}} + struct ReallyNestedU really_nested_u_arr[3]; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-null.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-null.c new file mode 100644 index 0000000000000..195c75f86b90d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-null.c @@ -0,0 +1,155 @@ + +// TODO: We should get the same diagnostics with/without return_size (rdar://138982703) + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,compound-literal -fbounds-safety-bringup-missing-checks=compound_literal_init %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s + +#include +#include + +struct cb { + int* __counted_by(10) ptr; +}; + +void const_count_callee(int *__counted_by(10) ccp); + +int *__counted_by(10) const_count(void) { + // expected-error@+1{{initializing 'cc' of type 'int *__single __counted_by(10)' (aka 'int *__single') and count value of 10 with null always fails}} + int *__counted_by(10) cc = NULL; + + // expected-error@+1{{assigning null to 'cc' of type 'int *__single __counted_by(10)' (aka 'int *__single') with count value of 10 always fails}} + cc = NULL; + + // expected-error@+1{{passing null to parameter 'ccp' of type 'int *__single __counted_by(10)' (aka 'int *__single') with count value of 10 always fails}} + const_count_callee(NULL); + + // expected-error@+1{{initializing 'local_cb.ptr' of type 'int *__single __counted_by(10)' (aka 'int *__single') and count value of 10 with null always fails}} + struct cb local_cb = { NULL }; + // compound-literal-error@+1{{initializing 'int *__single __counted_by(10)' (aka 'int *__single') and count value of 10 with null always fails}} + local_cb = (struct cb){ NULL }; + + // rs-error@+1{{returning null from a function with result type 'int *__single __counted_by(10)' (aka 'int *__single') and count value of 10 always fails}} + return NULL; +} + +int *__counted_by(10) const_count_null_cast(void) { + // expected-error@+1{{initializing 'cc' of type 'int *__single __counted_by(10)' (aka 'int *__single') and count value of 10 with null always fails}} + int *__counted_by(10) cc = (int*) NULL; + + // expected-error@+1{{assigning null to 'cc' of type 'int *__single __counted_by(10)' (aka 'int *__single') with count value of 10 always fails}} + cc = (int*) NULL; + + // expected-error@+1{{passing null to parameter 'ccp' of type 'int *__single __counted_by(10)' (aka 'int *__single') with count value of 10 always fails}} + const_count_callee((int*) NULL); + + // expected-error@+1{{initializing 'local_cb.ptr' of type 'int *__single __counted_by(10)' (aka 'int *__single') and count value of 10 with null always fails}} + struct cb local_cb = { (int*) NULL }; + // compound-literal-error@+1{{initializing 'int *__single __counted_by(10)' (aka 'int *__single') and count value of 10 with null always fails}} + local_cb = (struct cb){ (int*) NULL }; + + // rs-error@+1{{returning null from a function with result type 'int *__single __counted_by(10)' (aka 'int *__single') and count value of 10 always fails}} + return (int*) NULL; +} + +struct sb { + char* __sized_by(10) ptr; +}; + +void const_size_callee(char *__sized_by(10) csp); + +char *__sized_by(10) const_size(void) { + // expected-error@+1{{initializing 'cs' of type 'char *__single __sized_by(10)' (aka 'char *__single') and size value of 10 with null always fails}} + char *__sized_by(10) cs = NULL; + + // expected-error@+1{{assigning null to 'cs' of type 'char *__single __sized_by(10)' (aka 'char *__single') with size value of 10 always fails}} + cs = NULL; + + // expected-error@+1{{passing null to parameter 'csp' of type 'char *__single __sized_by(10)' (aka 'char *__single') with size value of 10 always fails}} + const_size_callee(NULL); + + // expected-error@+1{{initializing 'local_sb.ptr' of type 'char *__single __sized_by(10)' (aka 'char *__single') and size value of 10 with null always fails}} + struct sb local_sb = { NULL }; + // compound-literal-error@+1{{initializing 'char *__single __sized_by(10)' (aka 'char *__single') and size value of 10 with null always fails}} + local_sb = (struct sb){ NULL }; + + // rs-error@+1{{returning null from a function with result type 'char *__single __sized_by(10)' (aka 'char *__single') and size value of 10 always fails}} + return NULL; +} + +char *__sized_by(10) const_size_null_cast(void) { + // expected-error@+1{{initializing 'cs' of type 'char *__single __sized_by(10)' (aka 'char *__single') and size value of 10 with null always fails}} + char *__sized_by(10) cs = (char*) NULL; + + // expected-error@+1{{assigning null to 'cs' of type 'char *__single __sized_by(10)' (aka 'char *__single') with size value of 10 always fails}} + cs = (char*) NULL; + + // expected-error@+1{{passing null to parameter 'csp' of type 'char *__single __sized_by(10)' (aka 'char *__single') with size value of 10 always fails}} + const_size_callee((char*) NULL); + + // expected-error@+1{{initializing 'local_sb.ptr' of type 'char *__single __sized_by(10)' (aka 'char *__single') and size value of 10 with null always fails}} + struct sb local_sb = { (char*) NULL }; + // compound-literal-error@+1{{initializing 'char *__single __sized_by(10)' (aka 'char *__single') and size value of 10 with null always fails}} + local_sb = (struct sb){ (char*) NULL }; + + // rs-error@+1{{returning null from a function with result type 'char *__single __sized_by(10)' (aka 'char *__single') and size value of 10 always fails}} + return (char*) NULL; +} + +void dynamic_count_callee(int *__counted_by(len) dcp, int len); + +void dynamic_count(void) { + // expected-error@+2{{initializing 'dc' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 10 with null always fails}} + int len = 10; + int *__counted_by(len) dc = NULL; + + // expected-error@+2{{assigning null to 'dc' of type 'int *__single __counted_by(len)' (aka 'int *__single') with count value of 10 always fails}} + len = 10; + dc = NULL; + + // expected-error@+1{{passing null to parameter 'dcp' of type 'int *__single __counted_by(len)' (aka 'int *__single') with count value of 10 always fails}} + dynamic_count_callee(NULL, 10); +} + +void dynamic_count_null_cast(void) { + // expected-error@+2{{initializing 'dc' of type 'int *__single __counted_by(len)' (aka 'int *__single') and count value of 10 with null always fails}} + int len = 10; + int *__counted_by(len) dc = (int*) NULL; + + // expected-error@+2{{assigning null to 'dc' of type 'int *__single __counted_by(len)' (aka 'int *__single') with count value of 10 always fails}} + len = 10; + dc = (int*) NULL; + + // expected-error@+1{{passing null to parameter 'dcp' of type 'int *__single __counted_by(len)' (aka 'int *__single') with count value of 10 always fails}} + dynamic_count_callee((int*) NULL, 10); +} + +void dynamic_size_callee(char *__sized_by(size) dsp, int size); + +void dynamic_size(void) { + // expected-error@+2{{initializing 'ds' of type 'char *__single __sized_by(size)' (aka 'char *__single') and size value of 10 with null always fails}} + int size = 10; + char *__sized_by(size) ds = NULL; + + // expected-error@+2{{assigning null to 'ds' of type 'char *__single __sized_by(size)' (aka 'char *__single') with size value of 10 always fails}} + size = 10; + ds = NULL; + + // expected-error@+1{{passing null to parameter 'dsp' of type 'char *__single __sized_by(size)' (aka 'char *__single') with size value of 10 always fails}} + dynamic_size_callee(NULL, 10); +} + +void dynamic_size_null_cast(void) { + // expected-error@+2{{initializing 'ds' of type 'char *__single __sized_by(size)' (aka 'char *__single') and size value of 10 with null always fails}} + int size = 10; + char *__sized_by(size) ds = (char*) NULL; + + // expected-error@+2{{assigning null to 'ds' of type 'char *__single __sized_by(size)' (aka 'char *__single') with size value of 10 always fails}} + size = 10; + ds = (char*) NULL; + + // expected-error@+1{{passing null to parameter 'dsp' of type 'char *__single __sized_by(size)' (aka 'char *__single') with size value of 10 always fails}} + dynamic_size_callee((char*) NULL, 10); +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-return.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-return.c new file mode 100644 index 0000000000000..e8e421e2dcb8b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-return.c @@ -0,0 +1,148 @@ + +// TODO: We should get the same diagnostics with/without return_size (rdar://138982703) + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=guarded %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected -fbounds-safety-bringup-missing-checks=return_size %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=guarded %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected -fbounds-safety-bringup-missing-checks=return_size %s + +// guarded-no-diagnostics + +#include +#include + +// expected-note@+1 2{{'g_array' declared here}} +static int32_t g_array[42]; + +// __counted_by()/__sized_by() with a negative count and any pointer. + +int *__counted_by(-1) negative_cb(void) { + // expected-error@+1{{negative count value of -1 for 'int *__single __counted_by(-1)'}} + return 0; +} + +void *__sized_by(-1) negative_sb(void) { + // expected-error@+1{{negative size value of -1 for 'void *__single __sized_by(-1)'}} + return 0; +} + +// __counted_by_or_null()/__sized_by_or_null() with a negative count and nonnull ptr. + +int32_t *__counted_by_or_null(-1) negative_cbn(void) { + // expected-error@+1{{negative count value of -1 for 'int32_t *__single __counted_by_or_null(-1)'}} + return g_array; +} + +void *__sized_by_or_null(-1) negative_sbn(void) { + // expected-error@+1{{negative size value of -1 for 'void *__single __sized_by_or_null(-1)'}} + return g_array; +} + +// __counted_by()/__sized_by() with a positive count and an array. + +int32_t *__counted_by(42) array_cb_ok(void) { + return g_array; +} + +void *__sized_by(42*4) array_sb_ok(void) { + return g_array; +} + +int32_t *__counted_by(43) array_cb_bad(void) { + // expected-error-re@+1{{returning array 'g_array' (which has 42 elements) from a function with result type 'int32_t *__single __counted_by(43)'{{.*}} and count value of 43 always fails}} + return g_array; +} + +void *__sized_by(42*4+1) array_sb_bad(void) { + // expected-error-re@+1{{returning array 'g_array' (which has 168 bytes) from a function with result type 'void *__single __sized_by(169)'{{.*}} and size value of 169 always fails}} + return g_array; +} + +// __single to __counted_by()/__sized_by() with a positive count/size. + +int32_t *__counted_by(1) single_cb_ok(int32_t *__single p) { + return p; +} + +void *__sized_by(4) single_sb_ok(int32_t *__single p) { + return p; +} + +int32_t *__counted_by(2) single_cb_bad(int32_t *__single p) { + // expected-error-re@+1{{returning 'int32_t *__single'{{.*}} from a function with result type 'int32_t *__single __counted_by(2)'{{.*}} and count value of 2 always fails}} + return p; +} + +void *__sized_by(5) single_sb_bad(int32_t *__single p) { + // expected-error-re@+1{{returning 'int32_t *__single'{{.*}} with pointee of size 4 from a function with result type 'void *__single __sized_by(5)'{{.*}} and size value of 5 always fails}} + return p; +} + +// NULL to __counted_by()/__sized_by() with a positive count. + +int *__counted_by(42) null_cb(int arg) { + switch (arg) { + case 0: + // expected-error@+1{{returning null from a function with result type 'int *__single __counted_by(42)' (aka 'int *__single') and count value of 42 always fails}} + return (void*) 0; + case 1: + // expected-error@+1{{returning null from a function with result type 'int *__single __counted_by(42)' (aka 'int *__single') and count value of 42 always fails}} + return (int*) 0; + } + + // expected-error@+1{{returning null from a function with result type 'int *__single __counted_by(42)' (aka 'int *__single') and count value of 42 always fails}} + return 0; +} + +int *__counted_by(size) null_cb_sized(int arg, int size) { + // No diagnostics + switch (arg) { + case 0: + return 0; + case 1: + return (void*) 0; + case 2: + return (int*) 0; + } + return 0; +} + +char *__sized_by(42) null_sb(int arg) { + switch (arg) { + case 0: + // expected-error@+1{{returning null from a function with result type 'char *__single __sized_by(42)' (aka 'char *__single') and size value of 42 always fails}} + return (void*) 0; + case 1: + // expected-error@+1{{returning null from a function with result type 'char *__single __sized_by(42)' (aka 'char *__single') and size value of 42 always fails}} + return (char*) 0; + } + + // expected-error@+1{{returning null from a function with result type 'char *__single __sized_by(42)' (aka 'char *__single') and size value of 42 always fails}} + return 0; +} + +int *__sized_by(size) null_sb_sized(int arg, int size) { + // No diagnostics + switch (arg) { + case 0: + return 0; + case 1: + return (void*) 0; + case 2: + return (int*) 0; + } + return 0; +} + + +// The __counted_by_or_null()/__sized_by_or_null() pointer is set to some unknown value with a negative count/size. + +int *__counted_by_or_null(-1) negative_cbn_unknown(int *p) { + // expected-error@+1{{possibly returning non-null from a function with result type 'int *__single __counted_by_or_null(-1)' (aka 'int *__single') with count value of -1; explicitly return null to remove this warning}} + return p; +} + +void *__sized_by_or_null(-1) negative_sbn_unknown(int *p) { + // expected-error@+1{{possibly returning non-null from a function with result type 'void *__single __sized_by_or_null(-1)' (aka 'void *__single') with size value of -1; explicitly return null to remove this warning}} + return p; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-count-ptr-value-reference-no-side-effect.c b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-value-reference-no-side-effect.c new file mode 100644 index 0000000000000..10dd11e43e6b0 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-count-ptr-value-reference-no-side-effect.c @@ -0,0 +1,11 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void foo(int *__counted_by(count) p, int count) { + int *end = p + 9; + p = end; // expected-note{{'p' has been assigned here}} + count = (int)(end - p); // expected-error{{cannot reference 'p' after it is changed during consecutive assignments}} +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-inout-count-assign-only-len.c b/clang/test/BoundsSafety/Sema/dynamic-inout-count-assign-only-len.c new file mode 100644 index 0000000000000..a459d984cee9c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-inout-count-assign-only-len.c @@ -0,0 +1,45 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void in_buf_inout_len(int *__counted_by(*len) buf, int *len) { + *len = *len - 1; +bb: + (*len)--; +} + +// Make sure that for other cases we cannot assign only len. + +void inout_buf_inout_len(int *__counted_by(*len) * out_buf, int *len) { + // expected-error@+1{{assignment to '*len' requires corresponding assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*out_buf'; add self assignment '*out_buf = *out_buf' if the value has not changed}} + *len = *len - 1; +bb: + // expected-error@+1{{assignment to '*len' requires corresponding assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*out_buf'; add self assignment '*out_buf = *out_buf' if the value has not changed}} + (*len)--; +} + +void in_buf_inout_buf_inout_len(int *__counted_by(*len) buf, int *__counted_by(*len) * out_buf, int *len) { + // expected-error@+1{{assignment to '*len' requires corresponding assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*out_buf'; add self assignment '*out_buf = *out_buf' if the value has not changed}} + *len = *len - 1; +bb: + // expected-error@+1{{assignment to '*len' requires corresponding assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*out_buf'; add self assignment '*out_buf = *out_buf' if the value has not changed}} + (*len)--; +} + +void inout_buf_in_buf_inout_len(int *__counted_by(*len) * out_buf, int *__counted_by(*len) buf, int *len) { + // expected-error@+1{{assignment to '*len' requires corresponding assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*out_buf'; add self assignment '*out_buf = *out_buf' if the value has not changed}} + *len = *len - 1; +bb: + // expected-error@+1{{assignment to '*len' requires corresponding assignment to 'int *__single __counted_by(*len)' (aka 'int *__single') '*out_buf'; add self assignment '*out_buf = *out_buf' if the value has not changed}} + (*len)--; +} + +void in_buf_in_len(int *__counted_by(len) buf, int len) { + // expected-error@+1{{assignment to 'len' requires corresponding assignment to 'int *__single __counted_by(len)' (aka 'int *__single') 'buf'; add self assignment 'buf = buf' if the value has not changed}} + len = len - 1; +bb: + // expected-error@+1{{assignment to 'len' requires corresponding assignment to 'int *__single __counted_by(len)' (aka 'int *__single') 'buf'; add self assignment 'buf = buf' if the value has not changed}} + len--; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-inout-count-call-site-counted-by.c b/clang/test/BoundsSafety/Sema/dynamic-inout-count-call-site-counted-by.c new file mode 100644 index 0000000000000..818bf464a28dc --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-inout-count-call-site-counted-by.c @@ -0,0 +1,144 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +/* ------------------- + counted_by + ------------------- */ +struct foo { + int *__counted_by(len) p; + int *__counted_by(len) q; + int len; +}; + +void in_buf_inout_len(int *__counted_by(*len) buf, int *len); + +void inout_buf_inout_len(int *__counted_by(*len) * buf, int *len) { + inout_buf_inout_len(buf, len); + + in_buf_inout_len(*buf, len); +} + +void in_buf_inout_len(int *__counted_by(*len) buf, int *len) { + in_buf_inout_len(buf, len); + + // expected-error@+1{{parameter 'buf' with '__counted_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_buf_inout_len(&buf, len); +} + +void in_buf_inout_buf_inout_len(int *__counted_by(*len) p, int *__counted_by(*len) * q, int *len) { + // expected-error@+1{{passing 'len' as an indirect parameter; must also pass 'q' because the type of 'q', 'int *__single __counted_by(*len)*__single' (aka 'int *__single*__single'), refers to 'len'}} + in_buf_inout_len(p, len); + + // expected-error@+1{{passing 'len' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by(*len)' (aka 'int *__single'), refers to 'len'}} + inout_buf_inout_len(q, len); + + // expected-error@+1{{parameter 'p' with '__counted_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + in_buf_inout_buf_inout_len(*q, &p, len); +} + +void bar(void) { + int array[3] = {1, 2, 3}; + + int len = 3; + int *__counted_by(len) p = array; + + int non_related_len = 3; + + struct foo f; + f.len = 3; + f.p = array; + f.q = array; + + in_buf_inout_len(p, &len); + + // expected-error@+1{{passing address of 'len' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + in_buf_inout_len(0, &len); + + // expected-error@+1{{passing address of 'len' as an indirect parameter; must also pass 'q' or its address because the type of 'q', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + in_buf_inout_len(f.p, &f.len); + + in_buf_inout_buf_inout_len(f.p, &f.q, &f.len); + + // expected-error@+1{{passing address of 'len' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + in_buf_inout_len(array, &f.len); + + // expected-error@+1{{passing address of 'len' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + in_buf_inout_len(array, &len); + + in_buf_inout_len(array, &non_related_len); + + in_buf_inout_len(f.p, &non_related_len); +} + +/* ------------------- + counted_by_or_null + ------------------- */ +struct foo_nullable { + int *__counted_by_or_null(size) p; + int *__counted_by_or_null(size) q; + int size; +}; + +void in_buf_inout_size_nullable(int *__counted_by_or_null(*size) buf, int *size); + +void inout_buf_inout_size_nullable(int *__counted_by_or_null(*size) * buf, int *size) { + inout_buf_inout_size_nullable(buf, size); + + in_buf_inout_size_nullable(*buf, size); +} + +void in_buf_inout_size_nullable(int *__counted_by_or_null(*size) buf, int *size) { + in_buf_inout_size_nullable(buf, size); + + // expected-error@+1{{parameter 'buf' with '__counted_by_or_null' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_buf_inout_size_nullable(&buf, size); +} + +void in_buf_inout_buf_inout_size_nullable(int *__counted_by_or_null(*size) p, int *__counted_by_or_null(*size) * q, int *size) { + // expected-error@+1{{passing 'size' as an indirect parameter; must also pass 'q' because the type of 'q', 'int *__single __counted_by_or_null(*size)*__single' (aka 'int *__single*__single'), refers to 'size'}} + in_buf_inout_size_nullable(p, size); + + // expected-error@+1{{passing 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by_or_null(*size)' (aka 'int *__single'), refers to 'size'}} + inout_buf_inout_size_nullable(q, size); + + // expected-error@+1{{parameter 'p' with '__counted_by_or_null' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + in_buf_inout_buf_inout_size_nullable(*q, &p, size); +} + +void bar_nullable(void) { + int array[3] = {1, 2, 3}; + + int size = 3; + int *__counted_by_or_null(size) p = array; + + int non_related_size = 12; + + struct foo_nullable f; + f.size = 3; + f.p = array; + f.q = array; + + in_buf_inout_size_nullable(p, &size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(0, &size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'q' or its address because the type of 'q', 'int *__single __counted_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(f.p, &f.size); + + in_buf_inout_buf_inout_size_nullable(f.p, &f.q, &f.size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(array, &f.size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __counted_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(array, &size); + + in_buf_inout_size_nullable(array, &non_related_size); + + in_buf_inout_size_nullable(f.p, &non_related_size); + +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-inout-count-call-site-sized-by.c b/clang/test/BoundsSafety/Sema/dynamic-inout-count-call-site-sized-by.c new file mode 100644 index 0000000000000..6e314588f7a19 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-inout-count-call-site-sized-by.c @@ -0,0 +1,145 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +/* ------------------- + sized_by + ------------------- */ +struct foo { + int *__sized_by(size) p; + int *__sized_by(size) q; + int size; +}; + +void in_buf_inout_size(int *__sized_by(*size) buf, int *size); + +void inout_buf_inout_size(int *__sized_by(*size) * buf, int *size) { + inout_buf_inout_size(buf, size); + + in_buf_inout_size(*buf, size); +} + +void in_buf_inout_size(int *__sized_by(*size) buf, int *size) { + in_buf_inout_size(buf, size); + + // expected-error@+1{{parameter 'buf' with '__sized_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_buf_inout_size(&buf, size); +} + +void in_buf_inout_buf_inout_size(int *__sized_by(*size) p, int *__sized_by(*size) * q, int *size) { + // expected-error@+1{{passing 'size' as an indirect parameter; must also pass 'q' because the type of 'q', 'int *__single __sized_by(*size)*__single' (aka 'int *__single*__single'), refers to 'size'}} + in_buf_inout_size(p, size); + + // expected-error@+1{{passing 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by(*size)' (aka 'int *__single'), refers to 'size'}} + inout_buf_inout_size(q, size); + + // expected-error@+1{{parameter 'p' with '__sized_by' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + in_buf_inout_buf_inout_size(*q, &p, size); +} + +void bar(void) { + int array[3] = {1, 2, 3}; + + int size = 12; + int *__sized_by(size) p = array; + + int non_related_size = 12; + + struct foo f; + f.size = 12; + f.p = array; + f.q = array; + + in_buf_inout_size(p, &size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size(0, &size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'q' or its address because the type of 'q', 'int *__single __sized_by(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size(f.p, &f.size); + + in_buf_inout_buf_inout_size(f.p, &f.q, &f.size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size(array, &f.size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size(array, &size); + + in_buf_inout_size(array, &non_related_size); + + in_buf_inout_size(f.p, &non_related_size); + +} + +/* ------------------- + sized_by_or_null + ------------------- */ +struct foo_nullable { + int *__sized_by_or_null(size) p; + int *__sized_by_or_null(size) q; + int size; +}; + +void in_buf_inout_size_nullable(int *__sized_by_or_null(*size) buf, int *size); + +void inout_buf_inout_size_nullable(int *__sized_by_or_null(*size) * buf, int *size) { + inout_buf_inout_size_nullable(buf, size); + + in_buf_inout_size_nullable(*buf, size); +} + +void in_buf_inout_size_nullable(int *__sized_by_or_null(*size) buf, int *size) { + in_buf_inout_size_nullable(buf, size); + + // expected-error@+1{{parameter 'buf' with '__sized_by_or_null' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + inout_buf_inout_size_nullable(&buf, size); +} + +void in_buf_inout_buf_inout_size_nullable(int *__sized_by_or_null(*size) p, int *__sized_by_or_null(*size) * q, int *size) { + // expected-error@+1{{passing 'size' as an indirect parameter; must also pass 'q' because the type of 'q', 'int *__single __sized_by_or_null(*size)*__single' (aka 'int *__single*__single'), refers to 'size'}} + in_buf_inout_size_nullable(p, size); + + // expected-error@+1{{passing 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by_or_null(*size)' (aka 'int *__single'), refers to 'size'}} + inout_buf_inout_size_nullable(q, size); + + // expected-error@+1{{parameter 'p' with '__sized_by_or_null' attribute depending on an indirect count is implicitly read-only and cannot be passed as an indirect argument}} + in_buf_inout_buf_inout_size_nullable(*q, &p, size); +} + +void bar_nullable(void) { + int array[3] = {1, 2, 3}; + + int size = 12; + int *__sized_by_or_null(size) p = array; + + int non_related_size = 12; + + struct foo_nullable f; + f.size = 12; + f.p = array; + f.q = array; + + in_buf_inout_size_nullable(p, &size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(0, &size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'q' or its address because the type of 'q', 'int *__single __sized_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(f.p, &f.size); + + in_buf_inout_buf_inout_size_nullable(f.p, &f.q, &f.size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(array, &f.size); + + // expected-error@+1{{passing address of 'size' as an indirect parameter; must also pass 'p' or its address because the type of 'p', 'int *__single __sized_by_or_null(size)' (aka 'int *__single'), refers to 'size'}} + in_buf_inout_size_nullable(array, &size); + + in_buf_inout_size_nullable(array, &non_related_size); + + in_buf_inout_size_nullable(f.p, &non_related_size); + +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-inout-count-immutable-buf.c b/clang/test/BoundsSafety/Sema/dynamic-inout-count-immutable-buf.c new file mode 100644 index 0000000000000..d37dcf7dab24c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-inout-count-immutable-buf.c @@ -0,0 +1,73 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void foo_c(int *__counted_by(*len) buf, int *len) { + buf = buf; + *len = *len - 1; +} + +void bar_c(int *__counted_by(*len) buf, int *len) { + // expected-error@+1{{parameter 'buf' with '__counted_by' attribute depending on an indirect count is implicitly read-only}} + buf = buf + 1; + *len = *len - 1; +} + +void baz_c(int *__counted_by(*len) buf, int *len) { + // expected-error@+1{{parameter 'buf' with '__counted_by' attribute depending on an indirect count is implicitly read-only}} + buf++; + *len = *len - 1; +} + +void foo_s(int *__sized_by(*size) buf, int *size) { + buf = buf; + *size = *size - 4; +} + +void bar_s(int *__sized_by(*size) buf, int *size) { + // expected-error@+1{{parameter 'buf' with '__sized_by' attribute depending on an indirect count is implicitly read-only}} + buf = buf + 1; + *size = *size - 4; +} + +void baz_s(int *__sized_by(*size) buf, int *size) { + // expected-error@+1{{parameter 'buf' with '__sized_by' attribute depending on an indirect count is implicitly read-only}} + buf++; + *size = *size - 4; +} + +void foo_cn(int *__counted_by_or_null(*len) buf, int *len) { + buf = buf; + *len = *len - 1; +} + +void bar_cn(int *__counted_by_or_null(*len) buf, int *len) { + // expected-error@+1{{parameter 'buf' with '__counted_by_or_null' attribute depending on an indirect count is implicitly read-only}} + buf = buf + 1; + *len = *len - 1; +} + +void baz_cn(int *__counted_by_or_null(*len) buf, int *len) { + // expected-error@+1{{parameter 'buf' with '__counted_by_or_null' attribute depending on an indirect count is implicitly read-only}} + buf++; + *len = *len - 1; +} + +void foo_sn(int *__sized_by_or_null(*size) buf, int *size) { + buf = buf; + *size = *size - 4; +} + +void bar_sn(int *__sized_by_or_null(*size) buf, int *size) { + // expected-error@+1{{parameter 'buf' with '__sized_by_or_null' attribute depending on an indirect count is implicitly read-only}} + buf = buf + 1; + *size = *size - 4; +} + +void baz_sn(int *__sized_by_or_null(*size) buf, int *size) { + // expected-error@+1{{parameter 'buf' with '__sized_by_or_null' attribute depending on an indirect count is implicitly read-only}} + buf++; + *size = *size - 4; +} diff --git a/clang/test/BoundsSafety/Sema/dynamic-range-ptr-init-list.c b/clang/test/BoundsSafety/Sema/dynamic-range-ptr-init-list.c new file mode 100644 index 0000000000000..b09e21b5d95a7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/dynamic-range-ptr-init-list.c @@ -0,0 +1,54 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + char *__ended_by(iter) start; + char *__ended_by(end) iter; + char *end; +}; + +void Test(void) { + char arr[10]; + struct S s_order_all = {arr, arr, arr + 10}; + struct S s_implicit_all; + // expected-warning@+1{{implicitly initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + struct S s_implicit_partial1 = { arr }; + // expected-warning@+1{{implicitly initializing field 'end' of type 'char *__single /* __started_by(iter) */ ' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_implicit_partial2 = { arr, arr+1 }; + // expected-warning@+1{{implicitly initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + struct S s_implicit_partial3 = { .start = arr }; + // expected-warning@+2{{implicitly initializing field 'start' of type 'char *__single __ended_by(iter)' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + // expected-warning@+1{{implicitly initializing field 'end' of type 'char *__single /* __started_by(iter) */ ' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_implicit_partial4 = { .iter = arr }; + // expected-warning@+1{{implicitly initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + struct S s_implicit_partial5 = { .end = arr }; + // expected-warning@+2{{implicitly initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + // expected-warning@+1{{implicitly initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + struct S s_implicit_partial6 = { .end = arr, .start = arr }; + // expected-warning@+1{{implicitly initializing field 'end' of type 'char *__single /* __started_by(iter) */ ' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_implicit_partial7 = { .iter = arr, .start = arr }; + // expected-warning@+1{{implicitly initializing field 'start' of type 'char *__single __ended_by(iter)' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_implicit_partial8 = { .iter = arr, .end = arr }; + // expected-warning@+1{{initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + struct S s_partial1 = { arr, 0, 0 }; + // expected-warning@+1{{initializing field 'end' of type 'char *__single /* __started_by(iter) */ ' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_partial2 = { arr, arr+1, 0 }; + // expected-warning@+1{{initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + struct S s_partial3 = { .start = arr, .iter = 0, .end = 0 }; + // expected-warning@+2{{initializing field 'start' of type 'char *__single __ended_by(iter)' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + // expected-warning@+1{{initializing field 'end' of type 'char *__single /* __started_by(iter) */ ' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_partial4 = { .iter = arr, .end = 0, .start = 0 }; + // expected-warning@+1{{initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + struct S s_partial5 = { .end = arr, .start = 0, .iter = 0 }; + // expected-warning@+2{{initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'end' is initialized with a value rarely succeeds}} + // expected-warning@+1{{initializing field 'iter' of type 'char *__single __ended_by(end) /* __started_by(start) */ ' (aka 'char *__single') to NULL while 'start' is initialized with a value rarely succeeds}} + struct S s_partial6 = { .end = arr, .start = arr, .iter = 0 }; + // expected-warning@+1{{initializing field 'end' of type 'char *__single /* __started_by(iter) */ ' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_partial7 = { .iter = arr, .start = arr, .end = 0 }; + // expected-warning@+1{{initializing field 'start' of type 'char *__single __ended_by(iter)' (aka 'char *__single') to NULL while 'iter' is initialized with a value rarely succeeds}} + struct S s_partial8 = { .iter = arr, .end = arr, .start = 0 }; + struct S s_designate_all = { .iter = &arr[1], .end = arr + 2, .start = arr }; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/ended-by-nested-assignments.c b/clang/test/BoundsSafety/Sema/ended-by-nested-assignments.c new file mode 100644 index 0000000000000..f5fabbaa06095 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ended-by-nested-assignments.c @@ -0,0 +1,17 @@ + + +// RUN: %clang_cc1 -fsyntax-only -verify=with-checks -fbounds-safety -fbounds-safety-bringup-missing-checks=indirect_count_update %s +// RUN: %clang_cc1 -fsyntax-only -verify=without-checks -fbounds-safety -fno-bounds-safety-bringup-missing-checks=indirect_count_update %s +#include + +// without-checks-no-diagnostics + +void foo(int *__ended_by(end) start, int * end) { + // with-checks-error@+1{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'start' requires corresponding assignment to 'end'; add self assignment 'end = end' if the value has not changed}} + *start++ = 0; +} + +void bar(int *__ended_by(end) start, int * end) { + // with-checks-error@+1{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'start' requires corresponding assignment to 'end'; add self assignment 'end = end' if the value has not changed}} + *(start = start+1) = 0; +} diff --git a/clang/test/BoundsSafety/Sema/ended_by_assignments.c b/clang/test/BoundsSafety/Sema/ended_by_assignments.c new file mode 100644 index 0000000000000..ffecb96441ae2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ended_by_assignments.c @@ -0,0 +1,58 @@ + +// TODO: We should get the same diagnostics with/without return_size (rdar://138982703) + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,rs -fbounds-safety-bringup-missing-checks=return_size %s +#include + +int *__ended_by(end) func_ret_end(int *end) { + // rs-error@+1{{parameter 'end' is implicitly read-only due to being used by the '__ended_by' attribute in the return type of 'func_ret_end' ('int *__single __ended_by(end)' (aka 'int *__single'))}} + end = 0; + return end; +} + +int *__ended_by(end) func_ret_end2(int *__ended_by(end) start, int *end) { + return start + 1; +} + +void func_out_start_out_end(int *__ended_by(*out_end) *out_start, + int **out_end) { + out_start = 0; + out_end = 0; +} + +void func_out_start_in_end(int *__ended_by(end) *out_start, + int *end) { + *out_start = *out_start + 1; + // expected-error@+1{{parameter 'end' referred to by an indirect '__ended_by' pointer is implicitly read-only}} + end--; +} + +void func_out_start_in_end2(int *__ended_by(end) *out_start, + int *end) { + // expected-error@+1{{parameter 'end' referred to by an indirect '__ended_by' pointer is implicitly read-only}} + end--; +} + +void func_out_start_in_end3(int *__ended_by(end) *out_start, + int *end) { + *out_start = *out_start + 1; +} + +void func_in_start_out_end(int *__ended_by(*out_end) start, + int **out_end) { + // expected-error@+1{{parameter 'start' with '__ended_by' attribute depending on an indirect end pointer is implicitly read-only}} + start++; +} + +void func_in_start_out_end2(int *__ended_by(*out_end) start, + int **out_end) { + *out_end = *out_end - 1; + // expected-error@+1{{parameter 'start' with '__ended_by' attribute depending on an indirect end pointer is implicitly read-only}} + start++; +} + +void func_in_start_out_end3(int *__ended_by(*out_end) start, + int **out_end) { + *out_end = *out_end - 1; +} diff --git a/clang/test/BoundsSafety/Sema/ended_by_decls.c b/clang/test/BoundsSafety/Sema/ended_by_decls.c new file mode 100644 index 0000000000000..e0e3eb0d103f9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ended_by_decls.c @@ -0,0 +1,108 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +extern int *end; +extern int *__ended_by(end) start; + +int *end2; +extern int *__ended_by(end2) start2; +// expected-error@-1{{pointer with '__ended_by' and the argument of the attribute must be defined in the same translation unit}} + +extern int *end3; +int *__ended_by(end3) start3; +// expected-error@-1{{pointer with '__ended_by' and the argument of the attribute must be defined in the same translation unit}} + +extern int *end4; +extern int *__ended_by(end4) start4; + +void *end_share; +void *__ended_by(end_share) start5; +// expected-note@-1{{previous use is here}} +void *__ended_by(end_share) start6; +// expected-error@-1{{variable 'end_share' referred to by __ended_by variable cannot be used in other dynamic bounds attributes}} + +int *const const_end = 0; +int *data_const_end __unsafe_late_const; + +int *__ended_by(const_end) start_with_const_end; +void test_global_start_with_const_end() { + start_with_const_end = 0; +} + +int *__ended_by(data_const_end) start_with_data_const_end; +void test_global_start_with_data_const_end() { + start_with_data_const_end = 0; +} + +void test_extern_start_end() { + start = 0; + end = 0; +} + +void test_const_end(int *__ended_by(const_end) start); +void test_data_const_end(int *__ended_by(data_const_end) start); + +void test_redecl(int *__ended_by(end) start, int* end); +void test_redecl(int *__ended_by(end) start, int* end) {} + +void *__ended_by(end) test_redecl_return(int *__ended_by(end) start, int* end); +void *__ended_by(end) test_redecl_return(int *__ended_by(end) start, int* end); + + +// FIXME: weird error messages +// expected-error@+3{{pointer arithmetic on single pointer 'end' is out of bounds; consider adding '__counted_by' to 'end'}} +// expected-note@+2{{pointer 'end' declared here}} +// expected-error@+1{{'__ended_by' attribute requires a pointer type argument}} +void foo_bin_end(int *__ended_by(end+1) start, int* end); +void foo_cptr_end(int *__ended_by(end) start, char* end); +// expected-error@+1{{'__ended_by' attribute requires a pointer type argument}} +void foo_int_end(int *__ended_by(i) start, int i); +void foo_out_start_out_end(int *__ended_by(*out_end) *out_start, int **out_end); +void foo_out_end(int *__ended_by(*out_end) start, int **out_end); +int *__ended_by(end) foo_ret_end(int *end); + +// expected-note@+3{{add a count attribute}} +// expected-error@+2{{parameter of array type 'int[]' decays to a __single pointer}} +// expected-error@+1{{'__ended_by' attribute only applies to pointer arguments}} +void foo_end_in_bracket(int buf[__ended_by(end)], int *end); + +// expected-error@+1{{invalid argument expression to bounds attribute}} +int *__ended_by((int *)42) invalid_arg_in_ret_proto(void); + +// Check function with no prototype. DO NOT put 'void' in the parentheses. +// expected-error@+1{{invalid argument expression to bounds attribute}} +int *__ended_by((int *)42) invalid_arg_in_ret_noproto(); + +struct S { + int *end; + int *__ended_by(end) start; + int *__ended_by(end-1) start_1; // expected-error{{invalid argument expression to bounds attribute}} + int *__bidi_indexable bidi_end; + int *__ended_by(bidi_end) start_with_bidi_end; // expected-error{{end-pointer must be '__single'}} + int *__ended_by(end) __bidi_indexable start2; // expected-error{{pointer cannot be '__ended_by' and '__bidi_indexable' at the same time}} + int *__indexable __ended_by(end) start3; // expected-error{{pointer cannot be '__ended_by' and '__indexable' at the same time}} +}; + +struct T { + void *p1; + void *__ended_by(p1) p2; + void *__ended_by(p2 - 1) p3; // expected-error{{invalid argument expression to bounds attribute}} +}; + +struct U { + void *__sized_by(8) p1; + void *__ended_by(p1 - 1) p2; // expected-error{{invalid argument expression to bounds attribute}} +}; + +int baz(void) { + int *end; + int *__ended_by(end) start; + return 0; +} + +void type_of(int * __ended_by(end) p, int * end) { + __typeof__(p) p2; // expected-error{{__typeof__ on an expression of type 'int *__single __ended_by(end)' (aka 'int *__single') is not yet supported}} +} diff --git a/clang/test/BoundsSafety/Sema/ended_by_incdec.c b/clang/test/BoundsSafety/Sema/ended_by_incdec.c new file mode 100644 index 0000000000000..75ec97c2e985f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ended_by_incdec.c @@ -0,0 +1,36 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +void Foo(int *__ended_by(end) start, int *end) { + start--; // expected-error{{negative pointer arithmetic on pointer that starts the '__ended_by' chain always traps}} + ++end; // expected-error{{positive pointer arithmetic on end pointer always traps}} +} + +void Bar(int *__ended_by(end) start, int *end) { + start+=4; // expected-error{{assignment to 'int *__single __ended_by(end)' (aka 'int *__single') 'start' requires corresponding assignment to 'end'; add self assignment 'end = end' if the value has not changed}} +} + +typedef struct { + char *__ended_by(iter) start; + char *__ended_by(end) iter; + char *end; +} T; + +void Baz(T *tp) { + --tp->start; // expected-error{{negative pointer arithmetic on pointer that starts the '__ended_by' chain always traps}} + tp->end++; // expected-error{{positive pointer arithmetic on end pointer always traps}} +} + +void Qux(T *tp) { + tp->start = tp->start; + tp->end = tp->end; + tp->iter--; +} + +void Quux(T *tp) { + tp->start++; + ++tp->iter; + --tp->end; +} diff --git a/clang/test/BoundsSafety/Sema/ended_by_locals.c b/clang/test/BoundsSafety/Sema/ended_by_locals.c new file mode 100644 index 0000000000000..d42fd40fd3fe7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ended_by_locals.c @@ -0,0 +1,28 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +// expected-no-diagnostics + +void init_both(int * __bidi_indexable asdf, int asdf_len) { + const int *myEndPtr = asdf + asdf_len; + const int * __ended_by(myEndPtr) myEndedByPtr = asdf; +} + +void init_end(int * __bidi_indexable asdf, int asdf_len) { + const int *myEndPtr = asdf + asdf_len; + const int * __ended_by(myEndPtr) myEndedByPtr; // rdar://103382748 __ended_by needs to be initialized pairwise with end pointer +} + +void init_start(int * __bidi_indexable asdf, int asdf_len) { + const int *myEndPtr; // rdar://103382748 while not breaking bounds safety, this will always trap unless `asdf` is null + const int * __ended_by(myEndPtr) myEndedByPtr = asdf; +} + +void init_neither(int * __bidi_indexable asdf, int asdf_len) { + const int *myEndPtr; + const int * __ended_by(myEndPtr) myEndedByPtr; + myEndPtr = asdf + asdf_len; + myEndedByPtr = asdf; +} diff --git a/clang/test/BoundsSafety/Sema/ended_by_to_counted_by.c b/clang/test/BoundsSafety/Sema/ended_by_to_counted_by.c new file mode 100644 index 0000000000000..30581971a8796 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ended_by_to_counted_by.c @@ -0,0 +1,28 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// expected-no-diagnostics + +#include + +struct CountedData { + int *__counted_by(len) buf; + int len; +}; + +struct EndedData { + int *__ended_by(end) start; + int *end; +}; + +void Foo(struct CountedData *cp, struct EndedData *ep) { + cp->buf = ep->start; + cp->len = 10; +} + +int glen; +void Bar(struct CountedData *cp, struct EndedData *ep) { + cp->buf = ep->end; + cp->len = glen; +} diff --git a/clang/test/BoundsSafety/Sema/error-on-flexible-array-member-nocrash.c b/clang/test/BoundsSafety/Sema/error-on-flexible-array-member-nocrash.c new file mode 100644 index 0000000000000..535bc42981820 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/error-on-flexible-array-member-nocrash.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -fbounds-safety -fsyntax-only -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -verify %s + + + +#include + +struct saddr { + unsigned len; + char fam[__counted_by(len)]; +}; + +void test(struct saddr *sa, unsigned length) { + // expected-error@+1{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} + sa = sa + 1; + sa->len = length; +} diff --git a/clang/test/BoundsSafety/Sema/extern-incomplete-array-redecl.c b/clang/test/BoundsSafety/Sema/extern-incomplete-array-redecl.c new file mode 100644 index 0000000000000..f1ea2008a5c0d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/extern-incomplete-array-redecl.c @@ -0,0 +1,52 @@ + +// RUN: %clang_cc1 -isystem %S/mock-sdk -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -isystem %S/mock-sdk -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include + +// expected-note@extern-array-mock.h:3 {{'externArray' declared here}} +// expected-error@+1{{conflicting '__counted_by' attribute with the previous variable declaration}} +extern unsigned externArray[__counted_by(10)]; + +extern const unsigned externCountedArray[__counted_by(2)]; +const unsigned externCountedArray[] = {1, 2}; + +extern const unsigned externCountVar; +extern const unsigned externCountedArrayVar[__counted_by(externCountVar)]; +const unsigned externCountVar = 2; +const unsigned externCountedArrayVar[] = {1, 2}; + +extern const unsigned externCountVar2; +extern const unsigned externCountedArrayVar2[__counted_by(externCountVar2)]; +const unsigned externCountedArrayVar2[] = {1, 2}; +const unsigned externCountVar2 = 2; + +extern const unsigned externCountVar3; +extern const unsigned externCountedArrayVar3[__counted_by(externCountVar3)]; +const unsigned externCountedArrayVar3[] = {1, 2}; +const unsigned externCountVar3 = 3; // rdar://129246717 this should be an error + +extern const unsigned externCountVar4; +extern const unsigned externCountedArrayVar4[__counted_by(externCountVar4)]; +const unsigned externCountVar4 = 3; // rdar://129246717 this should be an error +const unsigned externCountedArrayVar4[] = {1, 2}; + +extern const unsigned externCountVar5; +extern const unsigned externCountedArrayVar5[__counted_by(externCountVar5)]; +const unsigned externCountedArrayVar5[] = {1, 2}; // rdar://129246717 this should be an error + +extern const unsigned externCountVar6; +extern const unsigned externCountedArrayVar6[__counted_by(externCountVar6)]; +const unsigned externCountVar6 = 3; // rdar://129246717 this should be an error + +extern const unsigned externCountedArrayUnsafe[__counted_by(10)]; +const unsigned externCountedArrayUnsafe[] = {1, 2}; // rdar://129246717 this should be an error + +void bar(const unsigned *pointer); + +void foo(void){ + f(); + bar(externArray); + unsigned *ptr = externArray; +} diff --git a/clang/test/BoundsSafety/Sema/extern-incomplete-array.c b/clang/test/BoundsSafety/Sema/extern-incomplete-array.c new file mode 100644 index 0000000000000..464b81fee0638 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/extern-incomplete-array.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -isystem %S/mock-sdk -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -isystem %S/mock-sdk -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include + +void bar(const unsigned *pointer); + +void foo(void){ + f(); + bar(externArray); // expected-warning{{accessing elements of an unannotated incomplete array always fails at runtime}} + unsigned *ptr = externArray; // expected-warning{{accessing elements of an unannotated incomplete array always fails at runtime}} +} + +extern const char baz[__null_terminated]; + +void qux(void) { + const char *__null_terminated x = baz; // ok +} diff --git a/clang/test/BoundsSafety/Sema/false-flexbase-array.c b/clang/test/BoundsSafety/Sema/false-flexbase-array.c new file mode 100644 index 0000000000000..1f868de0c84b1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/false-flexbase-array.c @@ -0,0 +1,18 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// Regression test for rdar://133766202 +// expected-no-diagnostics + +struct S { + int field1; + int field2; +}; + +void foo() { + struct S arr[2]; + arr->field1 = 3; +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-assign-null.c b/clang/test/BoundsSafety/Sema/flexible-array-member-assign-null.c new file mode 100644 index 0000000000000..4b7e1bc6c5516 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-assign-null.c @@ -0,0 +1,69 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +void init_null(void) { + struct flexible *__single s = 0; +} + +void init_null_bidi(void) { + struct flexible *s = 0; +} + +void init_casted_null(void) { + struct flexible *__single s = (struct flexible *)0; +} + +void init_casted_null_bidi(void) { + struct flexible *s = (struct flexible *)0; +} + +void assign_null(void) { + struct flexible *__single s; + s = 0; +} + +void assign_casted_null(void) { + struct flexible *__single s; + s = (struct flexible *)0; +} + +void impl_init_null_member_ref(void) { + struct flexible *__single s; + s->count = 10; // expected-error{{assignment to 's->count' requires an immediately preceding assignment to 's' with a wide pointer}} +} + +void init_null_member_ref(void) { + struct flexible *__single s = 0; + s->count = 10; // expected-error{{base of member reference is a null pointer}} +} + +void init_casted_null_member_ref(void) { + struct flexible *__single s = (struct flexible *)0; + s->count = 0; // expected-error{{base of member reference is a null pointer}} +} + +void assign_null_member_ref(void) { + struct flexible *__single s; + s = 0; + s->count = 2; // expected-error{{base of member reference is a null pointer}} +} + +void assign_casted_null_member_ref(void) { + struct flexible *__single s; + s = (struct flexible *)0; + s->count = 7; // expected-error{{base of member reference is a null pointer}} +} + +void assign_casted_null_member_ref_bidi(void) { + struct flexible *s; + s = (struct flexible *)0; + s->count = 7; // XXX: currently, flexible array member operations are analyzed only when their base is a single pointer type +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-assign-unsafe.c b/clang/test/BoundsSafety/Sema/flexible-array-member-assign-unsafe.c new file mode 100644 index 0000000000000..08e0fe63e69fa --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-assign-unsafe.c @@ -0,0 +1,57 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +void pass_to_bidi_indexable(struct flexible *__unsafe_indexable flex) { + // expected-error@+1{{initializing 'struct flexible *__bidi_indexable' with an expression of incompatible type 'struct flexible *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct flexible *b = flex; +} + +void pass_to_single(struct flexible *__unsafe_indexable flex) { + // expected-error@+1{{initializing 'struct flexible *__single' with an expression of incompatible type 'struct flexible *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct flexible *__single s = flex; +} + +void pass_to_single_forge(struct flexible *__unsafe_indexable flex) { + struct flexible *__single s = __unsafe_forge_single(struct flexible *, flex); +} + +void pass_to_single2(struct flexible *__unsafe_indexable flex) { + struct flexible *__single s; + // expected-error@+1{{assigning to 'struct flexible *__single' from incompatible type 'struct flexible *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + s = flex; +} + +void pass_to_single_assign(struct flexible *__unsafe_indexable flex) { + // expected-error@+1{{initializing 'struct flexible *__single' with an expression of incompatible type 'struct flexible *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct flexible *__single s = flex; + s->count = flex->count; +} + +struct flex_unsafe { + int count; + int elems[]; +}; + +void promote_unsafe_to_bidi_indexable(struct flex_unsafe *__unsafe_indexable flex) { + // expected-error@+1{{initializing 'struct flex_unsafe *__bidi_indexable' with an expression of incompatible type 'struct flex_unsafe *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct flex_unsafe *b = flex; +} + +void promote_unsafe_to_single(struct flex_unsafe *__unsafe_indexable flex) { + // expected-error@+1{{initializing 'struct flex_unsafe *__bidi_indexable' with an expression of incompatible type 'struct flex_unsafe *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + struct flex_unsafe *s = flex; +} + +void promote_unsafe_to_unsafe(struct flex_unsafe *__unsafe_indexable flex) { + struct flex_unsafe *__unsafe_indexable s = flex; +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-assign-with-single.c b/clang/test/BoundsSafety/Sema/flexible-array-member-assign-with-single.c new file mode 100644 index 0000000000000..c0f95253a70f1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-assign-with-single.c @@ -0,0 +1,56 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct flexible { + int count; + int elems[__counted_by(count)]; +}; + +void init_single(void *p) { + struct flexible *__single s = p; +} + +void init_casted_single(void *p) { + struct flexible *__single s = (struct flexible *)p; +} + +void assign_single(void *p) { + struct flexible *__single s; + s = p; +} + +void assign_casted_single(void *p) { + struct flexible *__single s; + s = (struct flexible *)p; +} + +void init_single_member_ref(void *p) { + struct flexible *__single s = p; // expected-note{{'s' is initialized with a '__single' pointer}} + s->count = 10; // expected-error{{assignment to 's->count' requires an immediately preceding assignment to 's' with a wide pointer}} +} + +void init_casted_single_member_ref(void *p) { + struct flexible *__single s = (struct flexible *)p; // expected-note{{'s' is initialized with a '__single' pointer}} + s->count = 10; // expected-error{{assignment to 's->count' requires an immediately preceding assignment to 's' with a wide pointer}} +} + +void assign_single_member_ref(void *p) { + struct flexible *__single s; + s = p; // expected-note{{'s' is initialized with a '__single' pointer}} + s->count = 2; // expected-error{{assignment to 's->count' requires an immediately preceding assignment to 's' with a wide pointer}} +} + +void assign_casted_single_member_ref(void *p) { + struct flexible *__single s; + s = (struct flexible *)p; // expected-note{{'s' is initialized with a '__single' pointer}} + s->count = 7; // expected-error{{assignment to 's->count' requires an immediately preceding assignment to 's' with a wide pointer}} +} + +void assign_casted_single_member_ref_bidi(void *p) { + struct flexible *s; + s = (struct flexible *)p; + s->count = 7; // XXX: currently, flexible array member operations are analyzed only when their base is a single pointer type +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-convert-to-sized-by.c b/clang/test/BoundsSafety/Sema/flexible-array-member-convert-to-sized-by.c new file mode 100644 index 0000000000000..a7031f657f21f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-convert-to-sized-by.c @@ -0,0 +1,19 @@ + + +// RUN: %clang_cc1 -ast-dump -fbounds-safety -verify %s + +// expected-no-diagnostics + +#include +#include + +struct flex { + uint8_t count; + uint8_t body[__counted_by(count - 1)]; +}; + +void pass_ptr(const void *__sized_by(size), unsigned size); + +void foo(struct flex *f) { + pass_ptr(f, 8); +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-forge-with-count.c b/clang/test/BoundsSafety/Sema/flexible-array-member-forge-with-count.c new file mode 100644 index 0000000000000..29ba2e1132fa9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-forge-with-count.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify %s -o /dev/null +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -o /dev/null + +#include + +typedef struct { + int len; + unsigned fam[__counted_by(len)]; +} S; + +void bar(const unsigned *pointer); + +void foo(void){ + S s; + bar(s.fam); + unsigned *ptr = s.fam; + ptr = __unsafe_forge_bidi_indexable(unsigned *, s.fam, s.len * sizeof(unsigned)); +} + +// expected-no-diagnostics diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-in-middle.c b/clang/test/BoundsSafety/Sema/flexible-array-member-in-middle.c new file mode 100644 index 0000000000000..c17c17e35aed9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-in-middle.c @@ -0,0 +1,30 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct flex { + int len; + char fam[]; +}; + +struct flex_with_count { + int len; + char fam[__counted_by(len)]; +}; + +struct flex_in_union { + struct flex fnc; // expected-warning{{field 'fnc' with variable sized type 'struct flex' not at the end of a struct or class is a GNU extension}} + union _u{ + struct flex_with_count fc; + int i; + } u; // expected-warning{{field 'u' with variable sized type 'union _u' not at the end of a struct or class is a GNU extension}} + + int dummy; +}; + +char foo(struct flex_in_union * bar) { + // expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} + return bar->dummy ? bar->fnc.fam[bar->fnc.len - 1] : bar->u.fc.fam[bar->u.fc.len - 1]; +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-multiple-decls.c b/clang/test/BoundsSafety/Sema/flexible-array-member-multiple-decls.c new file mode 100644 index 0000000000000..2c5914dc420e3 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-multiple-decls.c @@ -0,0 +1,146 @@ + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +typedef struct { + int len; + int offs; + int fam[__counted_by(len - offs)]; +} S; + +void good(S *s) { + int arr[10] = {0}; + s = (S *)&arr[5]; + s->offs = 5; + s->len = 10; +} + +void missing_offs(S *s) { + int arr[10] = {0}; + s = (S *)&arr[5]; + // expected-error@+1{{assignment to 'int' 's->len' requires corresponding assignment to 's->offs'; add self assignment 's->offs = s->offs' if the value has not changed}} + s->len = 10; +} + +void missing_s(S *s) { + // expected-error@+1{{assignment to 's->offs' requires an immediately preceding assignment to 's' with a wide pointer}} + s->offs = 5; + s->len = 10; +} + +void missing_len_offs(S *s) { + int arr[10] = {0}; + s = (S *)&arr[5]; +} + +void missing_len(S *s) { + int arr[10] = {0}; + s = (S *)&arr[5]; + // expected-error@+1{{assignment to 'int' 's->offs' requires corresponding assignment to 's->len'; add self assignment 's->len = s->len' if the value has not changed}} + s->offs = 5; +} + +// rdar://132802568 +void flexbase_middle(S *s) { + int arr[10] = {0}; + // expected-error@+1{{assignment to 's->offs' requires an immediately preceding assignment to 's' with a wide pointer}} + s->offs = 5; + s = (S *)&arr[5]; + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + s->len = 10; +} + +void flexbase_last(S *s) { + int arr[10] = {0}; + // expected-error@+1{{assignment to 's->offs' requires an immediately preceding assignment to 's' with a wide pointer}} + s->offs = 5; + s->len = 10; + s = (S *)&arr[5]; +} + +typedef struct { + int len; + int off1; + int off2; + int fam[__counted_by(len - (off1 + off2))]; +} S2; + +void good2(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; + s->len = 10; + s->off1 = 2; + s->off2 = 3; +} + +void missing_off1_off2(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; + // expected-error@+2{{assignment to 'int' 's->len' requires corresponding assignment to 's->off1'; add self assignment 's->off1 = s->off1' if the value has not changed}} + // expected-error@+1{{assignment to 'int' 's->len' requires corresponding assignment to 's->off2'; add self assignment 's->off2 = s->off2' if the value has not changed}} + s->len = 10; +} + +void missing_s2(S2 *s) { + // expected-error@+1{{assignment to 's->off1' requires an immediately preceding assignment to 's' with a wide pointer}} + s->off1 = 3; + s->off2 = 2; + s->len = 10; +} + +void missing_len_off1_off2(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; +} + + +void missing_len_off1(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; + // expected-error@+2{{assignment to 'int' 's->off2' requires corresponding assignment to 's->off1'; add self assignment 's->off1 = s->off1' if the value has not changed}} + // expected-error@+1{{assignment to 'int' 's->off2' requires corresponding assignment to 's->len'; add self assignment 's->len = s->len' if the value has not changed}} + s->off2 = 1; +} + +void missing_len_off2(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; + // expected-error@+2{{assignment to 'int' 's->off1' requires corresponding assignment to 's->off2'; add self assignment 's->off2 = s->off2' if the value has not changed}} + // expected-error@+1{{assignment to 'int' 's->off1' requires corresponding assignment to 's->len'; add self assignment 's->len = s->len' if the value has not changed}} + s->off1 = 3; +} + +void missing_len2(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; + // expected-error@+1{{assignment to 'int' 's->off1' requires corresponding assignment to 's->len'; add self assignment 's->len = s->len' if the value has not changed}} + s->off1 = 2; + s->off2 = 3; +} + +void missing_len_off1_2(S2 *s) { + int arr[10] = {0}; + s = (S2 *)&arr[5]; +} + +// rdar://132802568 +void flexbase_middle2(S2 *s) { + int arr[10] = {0}; + // expected-error@+1{{assignment to 's->len' requires an immediately preceding assignment to 's' with a wide pointer}} + s->len = 10; + s->off1 = 2; + s = (S2 *)&arr[5]; + // expected-error@+1{{assignments to dependent variables should not have side effects between them}} + s->off2 = 3; +} + +void flexbase_last2(S2 *s) { + int arr[10] = {0}; + // expected-error@+1{{assignment to 's->len' requires an immediately preceding assignment to 's' with a wide pointer}} + s->len = 10; + s->off1 = 2; + s->off2 = 3; + s = (S2 *)&arr[5]; +} diff --git a/clang/test/BoundsSafety/Sema/flexible-array-member-restrictions.c b/clang/test/BoundsSafety/Sema/flexible-array-member-restrictions.c new file mode 100644 index 0000000000000..a95f03980e243 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/flexible-array-member-restrictions.c @@ -0,0 +1,127 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +typedef struct flexible { + int count; + int elems[__counted_by(count)]; // \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} +} flex_t; + +// just to make sure this is OK +flex_t zeroes = {}; +flex_t zeroes2 = { 0 }; +flex_t zeroes3 = { .count = 0 }; +struct flexible flex = { 3, {1, 2, 3} }; +struct flexible flex2 = { .count = 3, .elems = { 1, 2, 3} }; +struct flexible flex3 = {3, { [2] = 3 } }; +struct flexible flex4 = { .count = 3, .elems = { [2] = 3} }; +struct flexible *returning_flexible_ptr(void); +flex_t *returning_flex_ptr(void); +void accepting_flexible_ptr(struct flexible *p); +void accepting_flex_ptr(flex_t *p); + +// these shouldn't work +struct flexible returning_flexible(void); // expected-error{{-fbounds-safety forbids passing 'struct flexible' by copy}} +flex_t returning_flex(void); // expected-error{{-fbounds-safety forbids passing 'flex_t' (aka 'struct flexible') by copy}} +void accepting_flexible(struct flexible p); // expected-error{{-fbounds-safety forbids passing 'struct flexible' by copy}} +void accepting_flex(flex_t p); // expected-error{{-fbounds-safety forbids passing 'flex_t' (aka 'struct flexible') by copy}} + +flex_t negative_count_die = { .count = -1, {1} }; // expected-error{{flexible array member is initialized with 1 element, but count value is initialized to -1}} +flex_t negative_count_die2 = { .count = -1 }; // expected-error{{flexible array member is initialized with 0 elements, but count value is initialized to -1}} +flex_t negative_count = { -1, {1} }; // expected-error{{flexible array member is initialized with 1 element, but count value is initialized to -1}} +flex_t zero_count = { 0, {1, 2, 3 } }; // expected-error{{flexible array member is initialized with 3 elements, but count value is initialized to 0}} +flex_t count_too_small = { 2, {1, 2, 3} }; // expected-error{{flexible array member is initialized with 3 elements, but count value is initialized to 2}} +flex_t count_too_large = {4, {1, 2, 3} }; // expected-error{{flexible array member is initialized with 3 elements, but count value is initialized to 4}} +flex_t count_too_large_2 = {3}; // expected-error{{flexible array member is initialized with 0 elements, but count value is initialized to 3}} +flex_t count_too_large_die = { .count = 3 }; // expected-error{{flexible array member is initialized with 0 elements, but count value is initialized to 3}} +flex_t count_too_large_die2 = { .count = 3, .elems = { 0 } }; // expected-error{{flexible array member is initialized with 1 element, but count value is initialized to 3}} +flex_t count_too_large_die3 = { .count = 3, .elems = { [1] = 0 } }; // expected-error{{flexible array member is initialized with 2 elements, but count value is initialized to 3}} + +void foo(void) { + flex_t flex_local = flex; // expected-error{{-fbounds-safety forbids passing 'struct flexible' by copy}} + flex_local = flex; // expected-error{{-fbounds-safety forbids passing 'struct flexible' by copy}} +} + +flex_t *bar(flex_t *__bidi_indexable flex) { + ++flex; // expected-error{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} + flex++; // expected-error{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} + --flex; // expected-error{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} + flex--; // expected-error{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} + (void) (flex + 1); // expected-error{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} + (void) (flex - 1); // expected-error{{-fbounds-safety forbids arithmetic on pointers to types with a flexible array member}} +} + +void baz(flex_t *__counted_by(1) flex); // expected-error{{cannot apply '__counted_by' attribute to 'flex_t *' (aka 'struct flexible *') because 'flex_t' (aka 'struct flexible') has unknown size; did you mean to use '__sized_by' instead?}} +void qux(flex_t *__sized_by(siz) flex, unsigned siz); + +void quux(flex_t *flex, char *__bidi_indexable buf) { + flex = (flex_t *)buf; // OK. run-time check inserted with flex->count. +} + +void quuz(flex_t *flex, char *__bidi_indexable buf) { + flex = (flex_t *)buf; // OK. + flex->count = 10; +} + +void corge(flex_t *flex, char *__bidi_indexable buf) { + flex->count = 10; // expected-error{{assignment to 'flex->count' requires an immediately preceding assignment to 'flex' with a wide pointer}} + flex = (flex_t *)buf; +} + +void grault(flex_t *flex) { + flex->count = 10; // expected-error{{assignment to 'flex->count' requires an immediately preceding assignment to 'flex' with a wide pointer}} +} + +void garply(char *__bidi_indexable buf) { + flex_t *__single flex = (flex_t *)buf; + flex->count = 10; +} + +void waldo(char *__bidi_indexable buf) { + flex_t *flex; + flex->count = 10; // OK. +} + +void fred(flex_t *flex, char *__bidi_indexable buf) { + flex = (flex_t *)buf; + int a = 5; + flex->count = a; // expected-error{{assignment to 'flex->count' requires an immediately preceding assignment to 'flex' with a wide pointer}} +} + +flex_t g_flex = {4, {1, 2, 3, 4}}; +void global_flex_count_assign(unsigned new_count) { + g_flex.count = new_count; // run-time check +} +void global_flex_count_increment() { + g_flex.count++; // expected-error{{incrementing 'g_flex.count' always traps}} +} + +void global_flex_count_decrement() { + g_flex.count--; // ok. run-time check +} + +void global_flex_count_compound_assign(unsigned diff) { + g_flex.count += diff; // run-time check +} + +typedef struct flexible_unannotated { + int count; + int elems[]; // \ + // expected-note{{initialized flexible array member 'elems' is here}} \ + // expected-note{{initialized flexible array member 'elems' is here}} +} flex_bad_t; + +flex_bad_t bad1 = { 0 }; // expected-error{{flexible array member is initialized without a count}} +flex_bad_t bad2 = {1, {1} }; // expected-error{{flexible array member is initialized without a count}} diff --git a/clang/test/BoundsSafety/Sema/forge-ptr-expr-incompl-array.c b/clang/test/BoundsSafety/Sema/forge-ptr-expr-incompl-array.c new file mode 100644 index 0000000000000..d7fdd7d296c10 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/forge-ptr-expr-incompl-array.c @@ -0,0 +1,20 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Foo { + int f; + int vla[]; +}; + +void Test() { + struct Foo f; + (void) __unsafe_forge_bidi_indexable(int *, f.vla, 0); + (void) __unsafe_forge_single(int *, f.vla); + (void) __unsafe_forge_terminated_by(int *, f.vla, 255); +} + +// expected-no-diagnostics diff --git a/clang/test/BoundsSafety/Sema/forge-ptr-expr.c b/clang/test/BoundsSafety/Sema/forge-ptr-expr.c new file mode 100644 index 0000000000000..87a4e0e3047b6 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/forge-ptr-expr.c @@ -0,0 +1,153 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,bs %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected,bs %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify=expected,bsa %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify=expected,bsa %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify=expected,bsa %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify=expected,bsa %s + +#include + +/* Define to nothing in attribute-only mode */ +#ifndef __indexable +#define __indexable +#endif + +#define forge_bidi_indexable(A, B) __unsafe_forge_bidi_indexable(int *, A, B) +#define forge_single(A) __unsafe_forge_single(int *, A) +#define forge_terminated_by(A, B) __unsafe_forge_terminated_by(int *, A, B) + +void Test1() { + int *__indexable ptrArray = forge_bidi_indexable(0, sizeof(int)); + int *__single ptr = forge_single(0); + int *__terminated_by(0) ptr2 = forge_terminated_by(0, 0); +} + +#define MACRO_ZERO 0 +#define MACRO_ONE 1 +#define MACRO_MINUS_ONE -1 + +/* In C++ enum is not an int, use macros to make the test pass */ +#ifdef __cplusplus +#define ZERO 0 +#define ONE 1 +#else +enum Enum { ZERO, ONE}; +#endif + +int global_int_var; +int* global_int_ptr_var; +struct StructFoo { + int member_int_var; + int* member_int_ptr_var; +}; +int get_negative_int(void) { return -1; } +#define NULL 0 + +#define MACRO_UINT64_MAX (~0ull) +#define MACRO_UINT32_MAX (~0u) + +void Test2() { + // bs-error@+2{{'__unsafe_forge_bidi_indexable' requires a pointer, array or integer address argument}} + // bsa-error-re@+1{{{{.*}}'double'{{.*}}pointer type}} + (void) forge_bidi_indexable(0., 0); + (void) forge_single(0.); // expected-error{{'__unsafe_forge_single' requires a pointer, array or integer address argument}} + (void) forge_terminated_by(0., 0); // expected-error{{'__unsafe_forge_terminated_by' requires a pointer, array or integer address argument}} + + (void) forge_bidi_indexable(ZERO, 0); + (void) forge_bidi_indexable(ONE, 0); + (void) forge_bidi_indexable(MACRO_ZERO, 0); + (void) forge_bidi_indexable(MACRO_ONE, 0); + (void) forge_bidi_indexable(NULL, 0); + + (void) forge_single(ZERO); + (void) forge_single(ONE); + (void) forge_single(MACRO_ZERO); + (void) forge_single(MACRO_ONE); + (void) forge_single(NULL); + + (void) forge_terminated_by(ZERO, 0); + (void) forge_terminated_by(ONE, 0); + (void) forge_terminated_by(MACRO_ZERO, 0); + (void) forge_terminated_by(MACRO_ONE, 0); + (void) forge_terminated_by(NULL, 0); + + struct StructFoo F; + // bs-error@+2{{'__unsafe_forge_bidi_indexable' requires a pointer, array or integer address argument}} + // bsa-error-re@+1{{{{.*}}'struct StructFoo'{{.*}}pointer type}} + (void) forge_bidi_indexable(F, 0); + (void) forge_bidi_indexable(&F, 0); + (void) forge_bidi_indexable(&F.member_int_var, 0); + (void) forge_bidi_indexable(F.member_int_ptr_var, 0); + + (void) forge_single(F); // expected-error{{'__unsafe_forge_single' requires a pointer, array or integer address argument}} + (void) forge_single(&F); + (void) forge_single(&F.member_int_var); + (void) forge_single(F.member_int_ptr_var); + + (void) forge_terminated_by(F, 0); // expected-error{{'__unsafe_forge_terminated_by' requires a pointer, array or integer address argument}} + (void) forge_terminated_by(&F, 0); + (void) forge_terminated_by(&F.member_int_var, 0); + (void) forge_terminated_by(F.member_int_ptr_var, 0); + + int local_int_var = 42; + int* local_int_ptr_var; + (void) forge_bidi_indexable(&local_int_var, 0); + (void) forge_bidi_indexable(local_int_ptr_var, 0); + (void) forge_bidi_indexable(&global_int_var, 0); + (void) forge_bidi_indexable(global_int_ptr_var, 0); + (void) forge_bidi_indexable(~0ull, 0); + (void) forge_bidi_indexable(-1, 0); // bs-error{{negative address argument to '__unsafe_forge_bidi_indexable'}} + (void) forge_bidi_indexable(MACRO_MINUS_ONE, 0); // bs-error{{negative address argument to '__unsafe_forge_bidi_indexable'}} + (void) forge_bidi_indexable(&local_int_var, MACRO_UINT64_MAX); + // bs-error@-1{{negative size argument to '__unsafe_forge_bidi_indexable'}} + (void) forge_bidi_indexable(0, MACRO_UINT32_MAX); + (void) forge_bidi_indexable(0, (unsigned char) 240); + + (void) forge_single(&local_int_var); + (void) forge_single(local_int_ptr_var); + (void) forge_single(&global_int_var); + (void) forge_single(global_int_ptr_var); + (void) forge_single(~0ull); + (void) forge_single(-1); // expected-error{{negative address argument to '__unsafe_forge_single'}} + (void) forge_single(MACRO_MINUS_ONE); // expected-error{{negative address argument to '__unsafe_forge_single'}} + + (void) forge_terminated_by(&local_int_var, 0); + (void) forge_terminated_by(local_int_ptr_var, 0); + (void) forge_terminated_by(&global_int_var, 0); + (void) forge_terminated_by(global_int_ptr_var, 0); + (void) forge_terminated_by(~0ull, 0); + (void) forge_terminated_by(-1, 0); // expected-error{{negative address argument to '__unsafe_forge_terminated_by'}} + (void) forge_terminated_by(MACRO_MINUS_ONE, 0); // expected-error{{negative address argument to '__unsafe_forge_terminated_by'}} +} + +void Test3() { + float f; + (void) forge_bidi_indexable(0, f); // bs-error{{'__unsafe_forge_bidi_indexable' requires an integer size argument}} + struct StructFoo s; + (void) forge_bidi_indexable(0, s); // bs-error{{'__unsafe_forge_bidi_indexable' requires an integer size argument}} + (void) forge_bidi_indexable(0, -4); // bs-error{{negative size argument to '__unsafe_forge_bidi_indexable'}} + + (void) forge_terminated_by(0, f); // expected-error{{'__terminated_by__' attribute requires an integer constant}} + (void) forge_terminated_by(0, s); // expected-error{{'__terminated_by__' attribute requires an integer constant}} +} + +int global_array[6]; +void Test4() { + int local_array[6]; + (void) forge_bidi_indexable(local_array, 0); + (void) forge_single(local_array); + (void) forge_terminated_by(local_array, 0); + + (void) forge_bidi_indexable(global_array, 0); + (void) forge_single(global_array); + (void) forge_terminated_by(global_array, 0); +} + +void Test5() { + // Function pointers are supported. + (void) forge_bidi_indexable(Test5, 0); + (void) forge_single(Test5); + (void) forge_terminated_by(Test5, 0); +} diff --git a/clang/test/BoundsSafety/Sema/fptr-count-return.c b/clang/test/BoundsSafety/Sema/fptr-count-return.c new file mode 100644 index 0000000000000..87e6a7b056a51 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/fptr-count-return.c @@ -0,0 +1,66 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -ast-dump -verify %s | FileCheck %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump -verify %s | FileCheck %s +#include + +// CHECK: VarDecl {{.*}} fptr 'int *__single*__single __counted_by(len)(*__single)(int)' +int **__counted_by(len) (*fptr)(int len); + +// expected-error@+1{{cannot apply '__counted_by' attribute to 'void *' because 'void' has unknown size; did you mean to use '__sized_by' instead?}} +void *__counted_by(len) (*fptr1)(int len); + +// expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} +int *__counted_by(len)* (*fptr2)(int len); + +// CHECK: VarDecl {{.*}} fptr3 'int *__single __sized_by(len1)(*__single)(int)' +// CHECK: VarDecl {{.*}} fptr4 'int *__single __sized_by(len2)(*__single)(void *__single __sized_by(len2), int)' +int *__sized_by(len1)(*fptr3)(int len1), *__sized_by(len2)(*fptr4)(void *__sized_by(len2), int len2); + +// CHECK: VarDecl {{.*}} fptr5 'void *__single __sized_by(len1)(*__single)(int)' +void *__sized_by(len1) (*fptr5)(int len1), + +// expected-error@+2{{use of undeclared identifier 'len1'; did you mean 'len2'?}} +// expected-note@+1{{'len2' declared here}} + *__sized_by(len1) (*fptr6)(void *__sized_by(len2), unsigned len2); + +int glen; +// CHECK: VarDecl {{.*}} fptr7 'void *__single __sized_by(glen)(*__single)(int)' +void *__sized_by(glen) (*fptr7)(int glen); // ok. glen shadowed. + +// expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} +void *__sized_by(glen) (*fptr8)(); + + +// CHECK: RecordDecl {{.*}} struct T1 definition +// CHECK: |-FieldDecl {{.*}} len 'int' +// CHECK: `-FieldDecl {{.*}} fptr 'void *__single __sized_by(len)(*__single)(int)' +struct T1 { + int len; + void *__sized_by(len) (*fptr)(int len); +}; + +struct T2 { + int field_len; + // expected-error@+1{{use of undeclared identifier 'field_len'}} + int *__sized_by(field_len) (*fptr)(int len); +}; + +struct T3 { + int field_len; + // expected-error@+1{{use of undeclared identifier 'field_len'}} + void *__sized_by(field_len) (*fptr)(); +}; + +// CHECK: RecordDecl {{.*}} struct T4 definition +// CHECK: |-FieldDecl {{.*}} fptr1 'void *__single __sized_by(len1)(*__single)(unsigned int)' +// CHECK: |-FieldDecl {{.*}} fptr2 'void *__single __sized_by(len2)(*__single)(unsigned int)' +// CHECK: |-FieldDecl {{.*}} fptr3 'void *__single __sized_by(len3)(*__single)(unsigned int)' +struct T4 { + void *__sized_by(len1) (*fptr1)(unsigned len1), + *__sized_by(len2) (*fptr2)(unsigned len2); + + void *__sized_by(len3) (*fptr3)(unsigned len3), + // expected-error@+2{{use of undeclared identifier 'len3'; did you mean 'len4'?}} + // expected-note@+1{{'len4' declared here}} + *__sized_by(len3) (*fptr4)(unsigned len4); +}; diff --git a/clang/test/BoundsSafety/Sema/fptr-not-indexable.c b/clang/test/BoundsSafety/Sema/fptr-not-indexable.c new file mode 100644 index 0000000000000..de2656def09d7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/fptr-not-indexable.c @@ -0,0 +1,31 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void Test(void) { + void (*fptrImplicitSingleOk)(void) = &Test; + void (*__single fptrExplicitSingleOk)(void) = &Test; + void (*__unsafe_indexable fptrExplicitUnsafeIndexableOk)(void) = &Test; + void (*__indexable fptrExplicitIndexableError)(void) = &Test; // expected-error{{function pointers cannot be indexable}} + void (*__bidi_indexable fptrExplicitBidiIndexableError)(void) = &Test; // expected-error{{function pointers cannot be indexable}} + typedef void (*fun_ptr)(void); + fun_ptr fptrImplicitSingleTypeDefOk = &Test; + typedef void (*__single fun_ptr_single)(void); + + fun_ptr_single fptrExplicitSingleTypeDefOk1 = &Test; + fun_ptr __single fptrExplicitSingleTypeDefOk2 = &Test; + + typedef void (*__unsafe_indexable fun_ptr_unsafe)(void); + fun_ptr_unsafe fptrExplicitUnsafeIndexableTypeDefOk1 = &Test; + fun_ptr __unsafe_indexable fptrExplicitUnsafeIndexableTypeDefOk2 = &Test; + + typedef void (*__indexable fun_ptr_idxble)(void); // expected-error{{function pointers cannot be indexable}} + fun_ptr_idxble fptrExplicitIndexableTypeDefError1 = &Test; + fun_ptr __indexable ptrExplicitIndexableTypeDefError2 = &Test; // expected-error{{function pointers cannot be indexable}} + + typedef void (*__bidi_indexable fun_ptr_bidi_idxble)(void); // expected-error{{function pointers cannot be indexable}} + fun_ptr_bidi_idxble fptrExplicitBidiIndexableTypeDefError1 = &Test; + fun_ptr __bidi_indexable fptrExplicitBidiIndexableTypeDefError2 = &Test; // expected-error{{function pointers cannot be indexable}} +} diff --git a/clang/test/BoundsSafety/Sema/global-count-pointer.c b/clang/test/BoundsSafety/Sema/global-count-pointer.c new file mode 100644 index 0000000000000..76d4701cbdf0b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/global-count-pointer.c @@ -0,0 +1,85 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +// TODO: rdar://85557264 +int len = 0; +int *__counted_by(len) ptr; +void *__sized_by(len) ptr_share_len; + +const unsigned const_len = 0; +int *__counted_by(const_len) ptr_with_const_len; + +int late_const_len __unsafe_late_const = 0; +void *__sized_by(late_const_len) ptr_with_late_const_len; + +int arrlen; +// expected-warning@+1{{array with '__counted_by' and the argument of the attribute should be defined in the same translation unit}} +extern int arr[__counted_by(arrlen)]; + +extern int arrlen2; +extern int arr2[__counted_by(arrlen2)]; + +extern unsigned extlen; +extern void *__sized_by(extlen) extptr; + +extern unsigned extlen2; +// expected-error@+1{{pointer with '__counted_by' and the argument of the attribute must be defined in the same translation unit}} +int *__counted_by(extlen2) ptr2; +// expected-error@+1{{pointer with '__sized_by' and the argument of the attribute must be defined in the same translation unit}} +void *__sized_by(extlen2) ptr3; +// expected-error@+1{{pointer with '__counted_by_or_null' and the argument of the attribute must be defined in the same translation unit}} +int *__counted_by_or_null(extlen2) ptr4; +// expected-error@+1{{pointer with '__sized_by_or_null' and the argument of the attribute must be defined in the same translation unit}} +void *__sized_by_or_null(extlen2) ptr5; + +unsigned long len2; +// expected-error@+1{{pointer with '__counted_by' and the argument of the attribute must be defined in the same translation unit}} +extern int *__counted_by(len2) extptr2; +// expected-error@+1{{pointer with '__sized_by' and the argument of the attribute must be defined in the same translation unit}} +extern void *__sized_by(len2) extptr3; +// expected-error@+1{{pointer with '__counted_by_or_null' and the argument of the attribute must be defined in the same translation unit}} +extern int *__counted_by_or_null(len2) extptr4; +// expected-error@+1{{pointer with '__sized_by_or_null' and the argument of the attribute must be defined in the same translation unit}} +extern void *__sized_by_or_null(len2) extptr5; + +extern int redecl_len1; +extern void *__sized_by(redecl_len1) redecl_ptr1; +int redecl_len1; +void *__sized_by(redecl_len1) redecl_ptr1; + +extern int redecl_len2; +extern int *__sized_by(redecl_len2) redecl_ptr2; // expected-note{{'redecl_ptr2' declared here}} +int redecl_len2; +int *__counted_by(redecl_len2) redecl_ptr2; +// expected-error@-1{{conflicting '__counted_by' attribute with the previous variable declaration}} + +extern int redecl_len3; +extern int *redecl_ptr3; // expected-note 4{{'redecl_ptr3' declared here}} +int redecl_len3; +int *__counted_by(redecl_len3) redecl_ptr3; +// expected-error@-1{{conflicting '__counted_by' attribute with the previous variable declaration}} +int *__sized_by(redecl_len3) redecl_ptr3; +// expected-error@-1{{conflicting '__sized_by' attribute with the previous variable declaration}} +int *__counted_by_or_null(redecl_len3) redecl_ptr3; +// expected-error@-1{{conflicting '__counted_by_or_null' attribute with the previous variable declaration}} +int *__sized_by_or_null(redecl_len3) redecl_ptr3; +// expected-error@-1{{conflicting '__sized_by_or_null' attribute with the previous variable declaration}} + +extern int redecl_len4; +extern int *__counted_by(redecl_len4) redecl_ptr4; +extern int redecl_len5; +extern int *__counted_by(redecl_len5) redecl_ptr5; // expected-note 4{{'redecl_ptr5' declared here}} +int redecl_len4; +int *__counted_by(redecl_len4) redecl_ptr5; +// expected-error@-1{{conflicting '__counted_by' attribute with the previous variable declaration}} +int *__sized_by(redecl_len4) redecl_ptr5; +// expected-error@-1{{conflicting '__sized_by' attribute with the previous variable declaration}} +int *__counted_by_or_null(redecl_len4) redecl_ptr5; +// expected-error@-1{{conflicting '__counted_by_or_null' attribute with the previous variable declaration}} +int *__sized_by_or_null(redecl_len4) redecl_ptr5; +// expected-error@-1{{conflicting '__sized_by_or_null' attribute with the previous variable declaration}} + +int len_2 = 2; +int *__counted_by(len_2 - 2) ptr_with_expr_count; diff --git a/clang/test/BoundsSafety/Sema/global-flexible-array-member-assign.c b/clang/test/BoundsSafety/Sema/global-flexible-array-member-assign.c new file mode 100644 index 0000000000000..90baab472b338 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/global-flexible-array-member-assign.c @@ -0,0 +1,37 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +// expected-no-diagnostics + +// This file exercises buildAndChainOldCountCheck for nested MemberExprs + +struct nested_fam { + int dummy; + int len; +}; +struct outer { + struct nested_fam hdr2; + int dummy; +}; +struct outerouter { + struct outer hdr; + char fam[__counted_by(hdr.hdr2.len)]; +}; + +struct outerouter s = { + .hdr.hdr2.len = 2, + .fam = {99,98} +}; + +void foo() { + s.hdr.hdr2.len = 1; +} + +void bar() { + // rdar://127523062 error here + struct outerouter t; + t.hdr.hdr2.len = 8; +} diff --git a/clang/test/BoundsSafety/Sema/global-flexible-array-member.c b/clang/test/BoundsSafety/Sema/global-flexible-array-member.c new file mode 100644 index 0000000000000..c4c8d9878c0b5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/global-flexible-array-member.c @@ -0,0 +1,144 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + + +struct Inner { + int dummy; + int len; +}; +struct Outer { + struct Inner hdr; + char fam[__counted_by(hdr.len)]; // expected-note 2{{initialized flexible array member 'fam' is here}} +}; + +struct Outer a = {{0, 2}, {3,5}}; +void assign_global_fam() { + a = a; // expected-error{{-fbounds-safety forbids passing 'struct Outer' by copy because it has a flexible array member}} + a.hdr.len = 1; +} +void assign_init_list_expr() { + a = (struct Outer){{0,33}, {7,11}}; // expected-error{{initialization of flexible array member is not allowed}} + a.hdr.len = 1; +} + +struct Outer b = {.hdr = {.len = 22}}; // expected-error{{flexible array member is initialized with 0 elements, but count value is initialized to 22}} +struct Outer c = {.hdr = {.dummy = 44}}; + +struct { + int len; + char fam[__counted_by(len)]; +} d = { .len = 2, .fam = {1,2} }; + +struct Outer e; +void assign_global_to_global() { + e = a; // expected-error{{-fbounds-safety forbids passing 'struct Outer' by copy because it has a flexible array member}} +} + +struct Middle { + int dummy; + struct Inner next; +}; + +struct Deep { + struct Middle hdr; + char fam[__counted_by(hdr.next.len)]; // expected-note 8{{initialized flexible array member 'fam' is here}} +}; + +// if these stop emitting errors about compile-time constants it's time to add a non-const version of each case +const struct Deep f = {{-5, {-1, 1}}, {99}}; +struct Deep g = {f.hdr, {98}}; // expected-error{{initializer element is not a compile-time constant}} +struct Deep h = {{-6, a.hdr}, {97}}; // expected-error{{initializer element is not a compile-time constant}} + +extern struct Inner i; +struct Deep j = {{-7, i}, {96}}; // expected-error{{initializer element is not a compile-time constant}} +struct Deep k = {{-8, {-3, f.hdr.next.len}}, f.fam[0]}; // expected-error{{initializer element is not a compile-time constant}} +struct Deep l = {{-9, {-4, (float)f.hdr.next.len}}, f.fam[0]}; // expected-error{{initializer element is not a compile-time constant}} +struct Deep m = {{-10, {-5, 1}}, f.fam}; // expected-error{{initializer element is not a compile-time constant}} + // expected-error@-1{{incompatible pointer to integer conversion initializing 'char' with an expression of type 'char const[__counted_by(hdr.next.len)]' (aka 'const char[]')}} +struct Deep n = {{-11, {-6, 2}}, .fam = f.fam}; // expected-error{{flexible array requires brace-enclosed initializer}} +struct Deep o = f; // expected-error{{initializer element is not a compile-time constant}} + +struct Deep p = {{-12, {-7, 1.0}}, {95}}; // expected-error{{count 'hdr.next.len' has non-integer value '1.' of type 'double'}} + +// check that we handle misformed initializer lists gracefully +struct Deep q = {{-13, {-8, i}}, {94}}; // expected-error{{initializing 'int' with an expression of incompatible type 'struct Inner'}} +struct Deep r = {i, {93}}; // expected-error{{initializing 'int' with an expression of incompatible type 'struct Inner'}} +struct Deep s = {{-14, -9, 1, 42}, {92}}; // expected-warning{{excess elements in struct initializer}} +struct Deep t = {{-15, -10, 1}, {91, 90}}; // expected-error{{flexible array member is initialized with 2 elements, but count value is initialized to 1}} +struct Deep u = {.len = 1, -16, -11, 89}; // expected-error{{field designator 'len' does not refer to any field in type 'struct Deep'}} + +// check that we find the right field in more obscure initializers +struct Deep v = {-17, -12, 2, 42}; // expected-error{{flexible array member is initialized with 1 element, but count value is initialized to 2}} +struct Deep w = {{-18, .next.len = 2}, {88}}; // expected-error{{flexible array member is initialized with 1 element, but count value is initialized to 2}} + +struct CastInCount { + float len; + char fam[__counted_by((int)len)]; // expected-note{{initialized flexible array member 'fam' is here}} +}; +struct CastInCount x = {2.5, {87}}; // expected-error{{flexible array member is initialized with 1 element, but count value is initialized to 2}} + +typedef union { + int i; + float f; +} U; +struct UnionCount { + U len; + char fam[__counted_by(len.i)]; // expected-error{{count parameter refers to union 'len' of type 'U'}} + // expected-note@-1{{initialized flexible array member 'fam' is here}} +}; +struct UnionCount y = {.len.f = 3.5, {86}}; // expected-error{{count 'len.i' has non-integer value '3.5' of type 'double'}} + +struct Deep ø = { + .hdr.next.len = 2, + .fam = {85,84,83} // expected-error{{flexible array member is initialized with 3 elements, but count value is initialized to 2}} +}; + +struct Deep ã = { + .hdr.next.len = 4, + .fam = {82,81,80} // expected-error{{flexible array member is initialized with 3 elements, but count value is initialized to 4}} +}; + +struct AnonBitfield { + int : 10; + struct { + int : 11; + struct { + int : 12; + struct { + int : 13; + int asdf; + }; + struct { + int : 14; + int : 15; + }; + struct { + int : 16; + int nonAnon: 17; + int len; + }; + }; + int dummy; + } a; + char fam[__counted_by(a.len)]; // expected-note{{initialized flexible array member 'fam' is here}} +}; + +struct AnonBitfield z = { 1, {}, 2, 3, 4, {5, 6, 7}}; +struct AnonBitfield å = { 1, {}, 2, 3, 4, {5, 6}}; // expected-error{{flexible array member is initialized with 2 elements, but count value is initialized to 3}} +struct AnonBitfield ä = { 1, 2, 3, 4, {5, 6}}; // expected-error{{initializer for aggregate with no elements requires explicit braces}} + // expected-warning@-1{{excess elements in scalar initializer}} +struct AnonBitfield ö = { { { {1}, {}, {2, 3} }, 4}, {5, 6, 7}}; +struct AnonBitfield æ = { 1, {}, 2, 3, 4, 5, 6, 7 }; + +struct ASDF { + int asdf; + struct Deep footer; +}; +struct ASDF ß = { + // This error is not a -fbounds-safety limitation, but a general clang limitation that only allows initializing top level FAMs + .footer = { .hdr.next.len = 2, .fam = {82, 81} } // expected-error{{initialization of flexible array member is not allowed}} +}; diff --git a/clang/test/BoundsSafety/Sema/implicit-integer-conversion.c b/clang/test/BoundsSafety/Sema/implicit-integer-conversion.c new file mode 100644 index 0000000000000..d720ff892e37d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/implicit-integer-conversion.c @@ -0,0 +1,12 @@ + + +// RUN: %clang_cc1 -fbounds-safety -verify -Wshorten-64-to-32 -triple x86_64 -fsyntax-only %s + +#include +#include + +void foo(int *__counted_by(size) buf, uint32_t size); + +void bar(int *__sized_by(len) arr, uint64_t len) { + foo(arr, len); // expected-warning{{implicit conversion loses integer precision: 'uint64_t' (aka 'unsigned long') to 'uint32_t' (aka 'unsigned int')}} +} diff --git a/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion-abi-assume.c b/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion-abi-assume.c new file mode 100644 index 0000000000000..25b228fde9ccc --- /dev/null +++ b/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion-abi-assume.c @@ -0,0 +1,73 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +// This is a test for +// "-Wbounds-attributes-implicit-conversion-single-to-explicit-indexable"'s +// behavior when the `__ptrcheck_abi_assume_*()` macros are used. +// +// rdar://91980829 +// Currently this warning is not emitted for the code in this test. Ideally we +// would emit warnings here but the current implementation looks for +// `attr::PtrAutoAttr` to make sure we don't warn on assignment to local +// variables that are implicitly `__bidi_indexable`. Unfortunately we see +// exactly the same attribute for ABI visible pointers that are implicitly +// annotated using __ptrcheck_abi_assume_bidi_indexable() or +// __ptrcheck_abi_assume_indexable(). Thus we cannot distinguish them and so no +// warnings are emitted. + +// expected-no-diagnostics + +#include +extern int *__single explicitly_single0; + +//-------------------------------------------------------------------------- +// Assume __bidi_indexable on ABI pointers +//-------------------------------------------------------------------------- +__ptrcheck_abi_assume_bidi_indexable(); + +void take_bidi0(int *b_arg0); +int *implicitly_bidi0; + +typedef struct { + int *field; +} BidiStruct_t; + +void use_bidi(void) { + // Initialization + BidiStruct_t bidi = {.field = explicitly_single0}; + // Assignment + implicitly_bidi0 = explicitly_single0; + bidi.field = explicitly_single0; + // Argument pass + take_bidi0(explicitly_single0); +} + +int *no_warn_ret_bidi(void) { + return explicitly_single0; +} + +//-------------------------------------------------------------------------- +// Assume __indexable on ABI pointers +//-------------------------------------------------------------------------- +__ptrcheck_abi_assume_indexable(); + +void take_idx0(int *i_arg0); +int *implicitly_idx0; + +typedef struct { + int *field; +} IdxStruct_t; + +void use_idx(void) { + // Initialization + IdxStruct_t idx = {.field = explicitly_single0}; + // Assignment + implicitly_idx0 = explicitly_single0; + idx.field = explicitly_single0; + // Argument pass + take_idx0(explicitly_single0); +} + +int *no_warn_ret_idx(void) { + return explicitly_single0; +} diff --git a/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion-nested.c b/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion-nested.c new file mode 100644 index 0000000000000..5817a955de4c3 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion-nested.c @@ -0,0 +1,506 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +// Nested pointer variant of +// `implicit-single-to-explicit-indexable-conversion.c`. The diagnostics for +// this variant explicitly call out the fact the source type is `__single` and +// that the destination type is `__bidi_indexable` or `__indexable`. This is +// done so that it is more obvious that the important part is the attribute on +// the outer most pointer. + +//-------------------------------------------------------------------------- +// Functions taking a `__bidi_indexable` pointer argument +// +// Each function should only have one use and the argument name should be +// unique so that we can uniquely identify the warning that these notes +// came from. +//-------------------------------------------------------------------------- +// expected-note@+1{{passing argument to parameter 'b_arg0' here}} +void take_bidi0(int **__bidi_indexable b_arg0); +// expected-note@+1{{passing argument to parameter 'b_arg1' here}} +void take_bidi1(int **__bidi_indexable b_arg1); +// expected-note@+1{{passing argument to parameter 'b_arg2' here}} +void take_bidi2(int **__bidi_indexable b_arg2); +// expected-note@+1{{passing argument to parameter 'b_arg3' here}} +void take_bidi3(int **__bidi_indexable b_arg3); +// expected-note@+1{{passing argument to parameter 'b_arg4' here}} +void take_bidi4(int **__bidi_indexable b_arg4); +void take_bidi5(int **__bidi_indexable b_arg5); + +//-------------------------------------------------------------------------- +// Functions taking an `__indexable` pointer argument +// +// Each function should only have one use and the argument name should be +// unique so that we can uniquely identify the warning that these notes +// came from. +//-------------------------------------------------------------------------- +// expected-note@+1{{passing argument to parameter 'i_arg0' here}} +void take_idx0(int **__indexable i_arg0); +// expected-note@+1{{passing argument to parameter 'i_arg1' here}} +void take_idx1(int **__indexable i_arg1); +// expected-note@+1{{passing argument to parameter 'i_arg2' here}} +void take_idx2(int **__indexable i_arg2); +// expected-note@+1{{passing argument to parameter 'i_arg3' here}} +void take_idx3(int **__indexable i_arg3); +// expected-note@+1{{passing argument to parameter 'i_arg4' here}} +void take_idx4(int **__indexable i_arg4); +void take_idx5(int **__indexable i_arg5); + +//-------------------------------------------------------------------------- +// Pointers marked explicitly as __single +// +// These pointers should only have one usage so that we know that the +// generated notes come from the correct warning. +//-------------------------------------------------------------------------- +extern int **__single explicitly_single0; +// expected-note@+1{{pointer 'explicitly_single1' declared here}} +extern int **__single explicitly_single1; +extern int **__single explicitly_single2; +// expected-note@+1{{pointer 'explicitly_single3' declared here}} +extern int **__single explicitly_single3; +extern int **__single explicitly_single4; +// expected-note@+1{{pointer 'explicitly_single5' declared here}} +extern int **__single explicitly_single5; +extern int **__single explicitly_single6; +// expected-note@+1{{pointer 'explicitly_single7' declared here}} +extern int **__single explicitly_single7; +extern int **__single explicitly_single8; +extern int **__single explicitly_single9; +// expected-note@+1{{pointer 'explicitly_single10' declared here}} +extern int **__single explicitly_single10; +extern int **__single explicitly_single11; +// expected-note@+1{{pointer 'explicitly_single12' declared here}} +extern int **__single explicitly_single12; +extern int **__single explicitly_single13; +// expected-note@+1{{pointer 'explicitly_single14' declared here}} +extern int **__single explicitly_single14; +extern int **__single explicitly_single15; +// expected-note@+1{{pointer 'explicitly_single16' declared here}} +extern int **__single explicitly_single16; +extern int **__single explicitly_single17; +// expected-note@+1{{pointer 'explicitly_single18' declared here}} +extern int **__single explicitly_single18; +extern int **__single explicitly_single19; +// expected-note@+1{{pointer 'explicitly_single20' declared here}} +extern int **__single explicitly_single20; +extern int **__single explicitly_single21; +// expected-note@+1{{pointer 'explicitly_single22' declared here}} +extern int **__single explicitly_single22; +extern int **__single explicitly_single23; +// expected-note@+1{{pointer 'explicitly_single24' declared here}} +extern int **__single explicitly_single24; +extern int **__single explicitly_single25; + +//-------------------------------------------------------------------------- +// Pointers marked implicitly as __single +// +// These pointers should only have one usage so that we know that the +// generated notes come from the correct warning. +//-------------------------------------------------------------------------- +extern int **implicitly_single0; +// expected-note@+1{{pointer 'implicitly_single1' declared here}} +extern int **implicitly_single1; +extern int **implicitly_single2; +// expected-note@+1{{pointer 'implicitly_single3' declared here}} +extern int **implicitly_single3; +extern int **implicitly_single4; +// expected-note@+1{{pointer 'implicitly_single5' declared here}} +extern int **implicitly_single5; +extern int **implicitly_single6; +// expected-note@+1{{pointer 'implicitly_single7' declared here}} +extern int **implicitly_single7; +extern int **implicitly_single8; +extern int **implicitly_single9; +// expected-note@+1{{pointer 'implicitly_single10' declared here}} +extern int **implicitly_single10; +extern int **implicitly_single11; +// expected-note@+1{{pointer 'implicitly_single12' declared here}} +extern int **implicitly_single12; +extern int **implicitly_single13; +// expected-note@+1{{pointer 'implicitly_single14' declared here}} +extern int **implicitly_single14; +extern int **implicitly_single15; +// expected-note@+1{{pointer 'implicitly_single16' declared here}} +extern int **implicitly_single16; +extern int **implicitly_single17; +// expected-note@+1{{pointer 'implicitly_single18' declared here}} +extern int **implicitly_single18; +extern int **implicitly_single19; +// expected-note@+1{{pointer 'implicitly_single20' declared here}} +extern int **implicitly_single20; +extern int **implicitly_single21; +// expected-note@+1{{pointer 'implicitly_single22' declared here}} +extern int **implicitly_single22; +extern int **implicitly_single23; +// expected-note@+1{{pointer 'implicitly_single24' declared here}} +extern int **implicitly_single24; +extern int **implicitly_single25; + +//-------------------------------------------------------------------------- +// Pointers that will cause a bitcast as well as a -fbounds-safety attribute cast. +// +// These pointers should only have one usage so that we know that the +// generated notes come from the correct warning. +//-------------------------------------------------------------------------- +typedef unsigned long uint64_t; +_Static_assert(sizeof(uint64_t) > sizeof(int), "type must be larger than int"); +extern uint64_t **__single bigger_explicit_single0; +// expected-note@+1{{pointer 'bigger_explicit_single1' declared here}} +extern uint64_t **__single bigger_explicit_single1; +extern uint64_t **__single bigger_explicit_single2; +// expected-note@+1{{pointer 'bigger_explicit_single3' declared here}} +extern uint64_t **__single bigger_explicit_single3; +extern uint64_t **__single bigger_explicit_single4; +// expected-note@+1{{pointer 'bigger_explicit_single5' declared here}} +extern uint64_t **__single bigger_explicit_single5; +extern uint64_t **__single bigger_explicit_single6; +// expected-note@+1{{pointer 'bigger_explicit_single7' declared here}} +extern uint64_t **__single bigger_explicit_single7; +extern uint64_t **__single bigger_explicit_single8; +extern uint64_t **__single bigger_explicit_single9; +// expected-note@+1{{'bigger_explicit_single10' declared here}} +extern uint64_t **__single bigger_explicit_single10; +extern uint64_t **__single bigger_explicit_single11; +// expected-note@+1{{pointer 'bigger_explicit_single12' declared here}} +extern uint64_t **__single bigger_explicit_single12; +extern uint64_t **__single bigger_explicit_single13; +// expected-note@+1{{pointer 'bigger_explicit_single14' declared here}} +extern uint64_t **__single bigger_explicit_single14; +extern uint64_t **__single bigger_explicit_single15; +// expected-note@+1{{pointer 'bigger_explicit_single16' declared here}} +extern uint64_t **__single bigger_explicit_single16; +extern uint64_t **__single bigger_explicit_single17; +// expected-note@+1{{pointer 'bigger_explicit_single18' declared here}} +extern uint64_t **__single bigger_explicit_single18; +extern uint64_t **__single bigger_explicit_single19; +// expected-note@+1{{pointer 'bigger_explicit_single20' declared here}} +extern uint64_t **__single bigger_explicit_single20; +extern uint64_t **__single bigger_explicit_single21; +// expected-note@+1{{pointer 'bigger_explicit_single22' declared here}} +extern uint64_t **__single bigger_explicit_single22; +extern uint64_t **__single bigger_explicit_single23; +// expected-note@+1{{pointer 'bigger_explicit_single24' declared here}} +extern uint64_t **__single bigger_explicit_single24; +extern uint64_t **__single bigger_explicit_single25; + +//-------------------------------------------------------------------------- +// SINGLE_EXPR +// +// This macro expands to an expression that isn't a DeclRefExpr but still +// but is still __single. Uses of this macro when implicitly cast to an +// indexable pointer should not suggest adding +// the `__counted_by` attribute. +//-------------------------------------------------------------------------- +int dummy_elements[] = {0, 1, 2, 3}; +int *dummy_array[] = { + &dummy_elements[0], + &dummy_elements[1], + &dummy_elements[2], + &dummy_elements[3]}; +// SINGLE_EXPR isn't a DeclRefExpr so when this is implicitly cast we shouldn't +// suggest adding `__counted_by` to non existing DeclRefExpr. +#define SINGLE_EXPR ((int **__single)(&dummy_array[1])) + +//-------------------------------------------------------------------------- +// Function that return a __single pointer +// +// TODO(dliew): We should teach clang to produce a diagnostic for this case +// that suggests adding an attribute to the function return type. +//-------------------------------------------------------------------------- +int **__single returns_explicit_single(void); + +typedef struct { + int **__bidi_indexable field; +} BidiStruct_t; + +typedef struct { + int **__indexable field; +} IdxStruct_t; + +void use(void) { + //-------------------------------------------------------------------------- + // Initialization + //-------------------------------------------------------------------------- + int **implicit_bidi = explicitly_single0; // no warning + int **implicit_bidi2 = implicitly_single0; // no warning + int **implicit_bidi3 = SINGLE_EXPR; // no warning + // Note: This is a different warning than this test is really intended to test + // but we test it here for completeness. + // expected-warning@+1{{incompatible pointer types initializing 'int *__single*__bidi_indexable' with an expression of type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single')}} + int **implicit_bidi4 = bigger_explicit_single0; + int **implicit_bidi5 = returns_explicit_single(); // no warning + + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single1'}} + int **__bidi_indexable explicit_bidi = explicitly_single1; + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single1'}} + int **__bidi_indexable explicit_bidi2 = implicitly_single1; + // expected-warning-re@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + int **__bidi_indexable explicit_bidi3 = SINGLE_EXPR; + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single1'}} + int **__bidi_indexable explicit_bidi4 = bigger_explicit_single1; + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + int **__bidi_indexable explicit_bidi5 = returns_explicit_single(); + int **__bidi_indexable explicit_bidi6 = (int **__bidi_indexable)explicitly_single2; // no warning + int **__bidi_indexable explicit_bidi7 = (int **__bidi_indexable)implicitly_single2; // no warning + int **__bidi_indexable explicit_bidi8 = (int **__bidi_indexable)SINGLE_EXPR; // no warning + int **__bidi_indexable explicit_bidi9 = (int **__bidi_indexable)bigger_explicit_single2; // no warning + int **__bidi_indexable explicit_bidi10 = (int **__bidi_indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single3'}} + int **__indexable explicit_idx = explicitly_single3; + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single3'}} + int **__indexable explicit_idx2 = implicitly_single3; + // expected-warning-re@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + int **__indexable explicit_idx3 = SINGLE_EXPR; + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single3'}} + int **__indexable explicit_idx4 = bigger_explicit_single3; + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + int **__indexable explicit_idx5 = returns_explicit_single(); + int **__indexable explicit_idx6 = (int **__indexable)explicitly_single4; // no warning + int **__indexable explicit_idx7 = (int **__indexable)implicitly_single4; // no warning + int **__indexable explicit_idx8 = (int **__indexable)SINGLE_EXPR; // no warning + int **__indexable explicit_idx9 = (int **__indexable)bigger_explicit_single4; // no warning + int **__indexable explicit_idx10 = (int **__indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single5'}} + BidiStruct_t bidiStruct = {.field = explicitly_single5}; + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single5'}} + BidiStruct_t bidiStruct2 = {.field = implicitly_single5}; + // expected-warning-re@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + BidiStruct_t bidiStruct3 = {.field = SINGLE_EXPR}; + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single5'}} + BidiStruct_t bidiStruct4 = {.field = bigger_explicit_single5}; + // expected-warning@+1{{initializing __bidi_indexable type 'int *__single*__bidi_indexable' with an expression of __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + BidiStruct_t bidiStruct5 = {.field = returns_explicit_single()}; + BidiStruct_t bidiStruct6 = {.field = (int **__bidi_indexable)explicitly_single6}; // no warning + BidiStruct_t bidiStruct7 = {.field = (int **__bidi_indexable)implicitly_single6}; // no warning + BidiStruct_t bidiStruct8 = {.field = (int **__bidi_indexable)SINGLE_EXPR}; // no warning + BidiStruct_t bidiStruct9 = {.field = (int **__bidi_indexable)bigger_explicit_single6}; // no warning + BidiStruct_t bidiStruct10 = {.field = (int **__bidi_indexable)returns_explicit_single()}; // no warning + + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single7'}} + IdxStruct_t idxStruct = {.field = explicitly_single7}; + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single7'}} + IdxStruct_t idxStruct2 = {.field = implicitly_single7}; + // expected-warning-re@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + IdxStruct_t idxStruct3 = {.field = SINGLE_EXPR}; + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single7'}} + IdxStruct_t idxStruct4 = {.field = bigger_explicit_single7}; + // expected-warning@+1{{initializing __indexable type 'int *__single*__indexable' with an expression of __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + IdxStruct_t idxStruct5 = {.field = returns_explicit_single()}; + IdxStruct_t idxStruct6 = {.field = (int **__indexable)explicitly_single8}; // no warning + IdxStruct_t idxStruct7 = {.field = (int **__indexable)implicitly_single8}; // no warning + IdxStruct_t idxStruct8 = {.field = (int **__indexable)SINGLE_EXPR}; // no warning + IdxStruct_t idxStruct9 = {.field = (int **__indexable)bigger_explicit_single8}; // no warning + IdxStruct_t idxStruct10 = {.field = (int **__indexable)returns_explicit_single()}; // no warning + + //-------------------------------------------------------------------------- + // Assignment + //-------------------------------------------------------------------------- + implicit_bidi = implicitly_single9; // no warning + implicit_bidi = explicitly_single9; // no warning + implicit_bidi = SINGLE_EXPR; // no warning + // Note: This is a different warning than this test is really intended to test + // but we test it here for completeness. + // expected-warning@+1{{incompatible pointer types assigning to 'int *__single*__bidi_indexable' from 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single')}} + implicit_bidi = bigger_explicit_single9; + implicit_bidi = returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single10'}} + explicit_bidi = implicitly_single10; + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single10'}} + explicit_bidi = explicitly_single10; + // expected-warning-re@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + explicit_bidi = SINGLE_EXPR; + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single10'}} + explicit_bidi = bigger_explicit_single10; + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + explicit_bidi = returns_explicit_single(); + + explicit_bidi = (int **__bidi_indexable)implicitly_single11; // no warning + explicit_bidi = (int **__bidi_indexable)explicitly_single11; // no warning + explicit_bidi = (int **__bidi_indexable)SINGLE_EXPR; // no warning + explicit_bidi = (int **__bidi_indexable)bigger_explicit_single11; // no warning + explicit_bidi = (int **__bidi_indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single24'}} + explicit_idx = implicitly_single24; + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single24'}} + explicit_idx = explicitly_single24; + // expected-warning-re@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + explicit_idx = SINGLE_EXPR; + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single24'}} + explicit_idx = bigger_explicit_single24; + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + explicit_idx = returns_explicit_single(); + + explicit_idx = (int **__indexable)implicitly_single25; // no warning + explicit_idx = (int **__indexable)explicitly_single25; // no warning + explicit_idx = (int **__indexable)SINGLE_EXPR; // no warning + explicit_idx = (int **__indexable)bigger_explicit_single25; // no warning + explicit_idx = (int **__indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single12'}} + bidiStruct.field = implicitly_single12; + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single12'}} + bidiStruct.field = explicitly_single12; + // expected-warning-re@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + bidiStruct.field = SINGLE_EXPR; + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single12'}} + bidiStruct.field = bigger_explicit_single12; + // expected-warning@+1{{assigning to __bidi_indexable type 'int *__single*__bidi_indexable' from __single type 'int *__single*__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + bidiStruct.field = returns_explicit_single(); + bidiStruct.field = (int **__bidi_indexable)implicitly_single13; // no warning + bidiStruct.field = (int **__bidi_indexable)explicitly_single13; // no warning + bidiStruct.field = (int **__bidi_indexable)SINGLE_EXPR; // no warning + bidiStruct.field = (int **__bidi_indexable)bigger_explicit_single13; // no warning + bidiStruct.field = (int **__bidi_indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single14'}} + idxStruct.field = implicitly_single14; + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single14'}} + idxStruct.field = explicitly_single14; + // expected-warning-re@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + idxStruct.field = SINGLE_EXPR; + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single14'}} + idxStruct.field = bigger_explicit_single14; + // expected-warning@+1{{assigning to __indexable type 'int *__single*__indexable' from __single type 'int *__single*__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + idxStruct.field = returns_explicit_single(); + idxStruct.field = (int **__indexable)implicitly_single15; // no warning + idxStruct.field = (int **__indexable)explicitly_single15; // no warning + idxStruct.field = (int **__indexable)SINGLE_EXPR; // no warning + idxStruct.field = (int **__indexable)bigger_explicit_single15; // no warning + idxStruct.field = (int **__indexable)returns_explicit_single(); // no warning + + //-------------------------------------------------------------------------- + // Argument pass + //-------------------------------------------------------------------------- + // expected-warning@+1{{passing __single type 'int *__single*__single' to parameter of __bidi_indexable type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single16'}} + take_bidi0(implicitly_single16); + // expected-warning@+1{{passing __single type 'int *__single*__single' to parameter of __bidi_indexable type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single16'}} + take_bidi1(explicitly_single16); + // expected-warning-re@+1{{passing __single type 'int *__single*__single' to parameter of __bidi_indexable type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + take_bidi2(SINGLE_EXPR); + // expected-warning@+1{{passing __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') to parameter of __bidi_indexable type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single16'}} + take_bidi3(bigger_explicit_single16); + // expected-warning@+1{{passing __single type 'int *__single*__single' to parameter of __bidi_indexable type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + take_bidi4(returns_explicit_single()); + + take_bidi5((int **__bidi_indexable)implicitly_single17); // no warning + take_bidi5((int **__bidi_indexable)explicitly_single17); // no warning + take_bidi5((int **__bidi_indexable)SINGLE_EXPR); // no warning + take_bidi5((int **__bidi_indexable)bigger_explicit_single17); // no warning + take_bidi5((int **__bidi_indexable)returns_explicit_single()); // no warning + + // expected-warning@+1{{passing __single type 'int *__single*__single' to parameter of __indexable type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single18'}} + take_idx0(implicitly_single18); + // expected-warning@+1{{passing __single type 'int *__single*__single' to parameter of __indexable type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single18'}} + take_idx1(explicitly_single18); + // expected-warning-re@+1{{passing __single type 'int *__single*__single' to parameter of __indexable type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + take_idx2(SINGLE_EXPR); + // expected-warning@+1{{passing __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') to parameter of __indexable type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single18'}} + take_idx3(bigger_explicit_single18); + // expected-warning@+1{{passing __single type 'int *__single*__single' to parameter of __indexable type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + take_idx4(returns_explicit_single()); + + take_idx5((int **__indexable)implicitly_single19); // no warning + take_idx5((int **__indexable)explicitly_single19); // no warning + take_idx5((int **__indexable)SINGLE_EXPR); // no warning + take_idx5((int **__indexable)bigger_explicit_single19); // no warning + take_idx5((int **__indexable)returns_explicit_single()); // no warning +} + +//-------------------------------------------------------------------------- +// Implicit conversion on return +//-------------------------------------------------------------------------- +int **__bidi_indexable warn_ret_bidi_expl(void) { + // expected-warning@+1{{returning __single type 'int *__single*__single' from a function with __bidi_indexable result type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single20'}} + return explicitly_single20; +} + +int **__bidi_indexable warn_ret_bidi_impl(void) { + // expected-warning@+1{{returning __single type 'int *__single*__single' from a function with __bidi_indexable result type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single20'}} + return implicitly_single20; +} + +int **__bidi_indexable warn_ret_bidi_single_expr(void) { + // expected-warning-re@+1{{returning __single type 'int *__single*__single' from a function with __bidi_indexable result type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + return SINGLE_EXPR; +} + +int **__bidi_indexable warn_ret_bidi_bigger_explicit(void) { + // expected-warning@+1{{returning __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') from a function with __bidi_indexable result type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single20'}} + return bigger_explicit_single20; +} + +int **__bidi_indexable warn_ret_bidi_tail_call_ret_single(void) { + // expected-warning@+1{{returning __single type 'int *__single*__single' from a function with __bidi_indexable result type 'int *__single*__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + return returns_explicit_single(); +} + +int **__bidi_indexable no_warn_ret_bidi_expl(void) { + return (int **__bidi_indexable)explicitly_single21; // no warning +} + +int **__bidi_indexable no_warn_ret_bidi_impl(void) { + return (int **__bidi_indexable)implicitly_single21; // no warning +} + +int **__bidi_indexable no_warn_ret_bidi_single_expr(void) { + return (int **__bidi_indexable)SINGLE_EXPR; // no warning +} + +int **__bidi_indexable no_warn_ret_bidi_bigger_explicit(void) { + return (int **__bidi_indexable)bigger_explicit_single21; // no warning +} + +int **__bidi_indexable no_warn_ret_bidi_tail_call_ret_single(void) { + return (int **__bidi_indexable)returns_explicit_single(); // no warning +} + +int **__indexable warn_ret_idx_expl(void) { + // expected-warning@+1{{returning __single type 'int *__single*__single' from a function with __indexable result type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single22'}} + return explicitly_single22; +} + +int **__indexable warn_ret_idx_impl(void) { + // expected-warning@+1{{returning __single type 'int *__single*__single' from a function with __indexable result type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single22'}} + return implicitly_single22; +} + +int **__indexable warn_ret_idx_single_expr(void) { + // expected-warning-re@+1{{returning __single type 'int *__single*__single' from a function with __indexable result type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + return SINGLE_EXPR; +} + +int **__indexable warn_ret_idx_bigger_explicit(void) { + // expected-warning@+1{{returning __single type 'uint64_t *__single*__single' (aka 'unsigned long *__single*__single') from a function with __indexable result type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single22'}} + return bigger_explicit_single22; +} + +int **__indexable warn_ret_idx_tail_call_ret_single(void) { + // expected-warning@+1{{eturning __single type 'int *__single*__single' from a function with __indexable result type 'int *__single*__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + return returns_explicit_single(); +} + +int **__indexable no_warn_ret_idx_expl(void) { + return (int **__indexable)explicitly_single23; // no warning +} + +int **__indexable no_warn_ret_idx_impl(void) { + return (int **__indexable)implicitly_single23; // no warning +} + +int **__indexable no_warn_ret_idx_single_expr(void) { + return (int **__indexable)SINGLE_EXPR; // no warning +} + +int **__indexable no_warn_ret_idx_bigger_explicit(void) { + return (int **__indexable)bigger_explicit_single23; // no warning +} + +int **__indexable no_warn_ret_idx_tail_call_ret_single(void) { + return (int **__indexable)returns_explicit_single(); // no warning +} diff --git a/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion.c b/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion.c new file mode 100644 index 0000000000000..80632cf34ef3e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/implicit-single-to-explicit-indexable-conversion.c @@ -0,0 +1,496 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -verify %s + +#include + +//-------------------------------------------------------------------------- +// Functions taking a `__bidi_indexable` pointer argument +// +// Each function should only have one use and the argument name should be +// unique so that we can uniquely identify the warning that these notes +// came from. +//-------------------------------------------------------------------------- +// expected-note@+1{{passing argument to parameter 'b_arg0' here}} +void take_bidi0(int *__bidi_indexable b_arg0); +// expected-note@+1{{passing argument to parameter 'b_arg1' here}} +void take_bidi1(int *__bidi_indexable b_arg1); +// expected-note@+1{{passing argument to parameter 'b_arg2' here}} +void take_bidi2(int *__bidi_indexable b_arg2); +// expected-note@+1{{passing argument to parameter 'b_arg3' here}} +void take_bidi3(int *__bidi_indexable b_arg3); +// expected-note@+1{{passing argument to parameter 'b_arg4' here}} +void take_bidi4(int *__bidi_indexable b_arg4); +void take_bidi5(int *__bidi_indexable b_arg5); + +//-------------------------------------------------------------------------- +// Functions taking an `__indexable` pointer argument +// +// Each function should only have one use and the argument name should be +// unique so that we can uniquely identify the warning that these notes +// came from. +//-------------------------------------------------------------------------- +// expected-note@+1{{passing argument to parameter 'i_arg0' here}} +void take_idx0(int *__indexable i_arg0); +// expected-note@+1{{passing argument to parameter 'i_arg1' here}} +void take_idx1(int *__indexable i_arg1); +// expected-note@+1{{passing argument to parameter 'i_arg2' here}} +void take_idx2(int *__indexable i_arg2); +// expected-note@+1{{passing argument to parameter 'i_arg3' here}} +void take_idx3(int *__indexable i_arg3); +// expected-note@+1{{passing argument to parameter 'i_arg4' here}} +void take_idx4(int *__indexable i_arg4); +void take_idx5(int *__indexable i_arg5); + +//-------------------------------------------------------------------------- +// Pointers marked explicitly as __single +// +// These pointers should only have one usage so that we know that the +// generated notes come from the correct warning. +//-------------------------------------------------------------------------- +extern int *__single explicitly_single0; +// expected-note@+1{{pointer 'explicitly_single1' declared here}} +extern int *__single explicitly_single1; +extern int *__single explicitly_single2; +// expected-note@+1{{pointer 'explicitly_single3' declared here}} +extern int *__single explicitly_single3; +extern int *__single explicitly_single4; +// expected-note@+1{{pointer 'explicitly_single5' declared here}} +extern int *__single explicitly_single5; +extern int *__single explicitly_single6; +// expected-note@+1{{pointer 'explicitly_single7' declared here}} +extern int *__single explicitly_single7; +extern int *__single explicitly_single8; +extern int *__single explicitly_single9; +// expected-note@+1{{pointer 'explicitly_single10' declared here}} +extern int *__single explicitly_single10; +extern int *__single explicitly_single11; +// expected-note@+1{{pointer 'explicitly_single12' declared here}} +extern int *__single explicitly_single12; +extern int *__single explicitly_single13; +// expected-note@+1{{pointer 'explicitly_single14' declared here}} +extern int *__single explicitly_single14; +extern int *__single explicitly_single15; +// expected-note@+1{{pointer 'explicitly_single16' declared here}} +extern int *__single explicitly_single16; +extern int *__single explicitly_single17; +// expected-note@+1{{pointer 'explicitly_single18' declared here}} +extern int *__single explicitly_single18; +extern int *__single explicitly_single19; +// expected-note@+1{{pointer 'explicitly_single20' declared here}} +extern int *__single explicitly_single20; +extern int *__single explicitly_single21; +// expected-note@+1{{pointer 'explicitly_single22' declared here}} +extern int *__single explicitly_single22; +extern int *__single explicitly_single23; +// expected-note@+1{{pointer 'explicitly_single24' declared here}} +extern int *__single explicitly_single24; +extern int *__single explicitly_single25; + +//-------------------------------------------------------------------------- +// Pointers marked implicitly as __single +// +// These pointers should only have one usage so that we know that the +// generated notes come from the correct warning. +//-------------------------------------------------------------------------- +extern int *implicitly_single0; +// expected-note@+1{{pointer 'implicitly_single1' declared here}} +extern int *implicitly_single1; +extern int *implicitly_single2; +// expected-note@+1{{pointer 'implicitly_single3' declared here}} +extern int *implicitly_single3; +extern int *implicitly_single4; +// expected-note@+1{{pointer 'implicitly_single5' declared here}} +extern int *implicitly_single5; +extern int *implicitly_single6; +// expected-note@+1{{pointer 'implicitly_single7' declared here}} +extern int *implicitly_single7; +extern int *implicitly_single8; +extern int *implicitly_single9; +// expected-note@+1{{pointer 'implicitly_single10' declared here}} +extern int *implicitly_single10; +extern int *implicitly_single11; +// expected-note@+1{{pointer 'implicitly_single12' declared here}} +extern int *implicitly_single12; +extern int *implicitly_single13; +// expected-note@+1{{pointer 'implicitly_single14' declared here}} +extern int *implicitly_single14; +extern int *implicitly_single15; +// expected-note@+1{{pointer 'implicitly_single16' declared here}} +extern int *implicitly_single16; +extern int *implicitly_single17; +// expected-note@+1{{pointer 'implicitly_single18' declared here}} +extern int *implicitly_single18; +extern int *implicitly_single19; +// expected-note@+1{{pointer 'implicitly_single20' declared here}} +extern int *implicitly_single20; +extern int *implicitly_single21; +// expected-note@+1{{pointer 'implicitly_single22' declared here}} +extern int *implicitly_single22; +extern int *implicitly_single23; +// expected-note@+1{{pointer 'implicitly_single24' declared here}} +extern int *implicitly_single24; +extern int *implicitly_single25; + +//-------------------------------------------------------------------------- +// Pointers that will cause a bitcast as well as a -fbounds-safety attribute cast. +// +// These pointers should only have one usage so that we know that the +// generated notes come from the correct warning. +//-------------------------------------------------------------------------- +typedef unsigned long uint64_t; +_Static_assert(sizeof(uint64_t) > sizeof(int), "type must be larger than int"); +extern uint64_t *__single bigger_explicit_single0; +// expected-note@+1{{pointer 'bigger_explicit_single1' declared here}} +extern uint64_t *__single bigger_explicit_single1; +extern uint64_t *__single bigger_explicit_single2; +// expected-note@+1{{pointer 'bigger_explicit_single3' declared here}} +extern uint64_t *__single bigger_explicit_single3; +extern uint64_t *__single bigger_explicit_single4; +// expected-note@+1{{pointer 'bigger_explicit_single5' declared here}} +extern uint64_t *__single bigger_explicit_single5; +extern uint64_t *__single bigger_explicit_single6; +// expected-note@+1{{pointer 'bigger_explicit_single7' declared here}} +extern uint64_t *__single bigger_explicit_single7; +extern uint64_t *__single bigger_explicit_single8; +extern uint64_t *__single bigger_explicit_single9; +// expected-note@+1{{'bigger_explicit_single10' declared here}} +extern uint64_t *__single bigger_explicit_single10; +extern uint64_t *__single bigger_explicit_single11; +// expected-note@+1{{pointer 'bigger_explicit_single12' declared here}} +extern uint64_t *__single bigger_explicit_single12; +extern uint64_t *__single bigger_explicit_single13; +// expected-note@+1{{pointer 'bigger_explicit_single14' declared here}} +extern uint64_t *__single bigger_explicit_single14; +extern uint64_t *__single bigger_explicit_single15; +// expected-note@+1{{pointer 'bigger_explicit_single16' declared here}} +extern uint64_t *__single bigger_explicit_single16; +extern uint64_t *__single bigger_explicit_single17; +// expected-note@+1{{pointer 'bigger_explicit_single18' declared here}} +extern uint64_t *__single bigger_explicit_single18; +extern uint64_t *__single bigger_explicit_single19; +// expected-note@+1{{pointer 'bigger_explicit_single20' declared here}} +extern uint64_t *__single bigger_explicit_single20; +extern uint64_t *__single bigger_explicit_single21; +// expected-note@+1{{pointer 'bigger_explicit_single22' declared here}} +extern uint64_t *__single bigger_explicit_single22; +extern uint64_t *__single bigger_explicit_single23; +// expected-note@+1{{pointer 'bigger_explicit_single24' declared here}} +extern uint64_t *__single bigger_explicit_single24; +extern uint64_t *__single bigger_explicit_single25; + +//-------------------------------------------------------------------------- +// SINGLE_EXPR +// +// This macro expands to an expression that isn't a DeclRefExpr but still +// but is still __single. Uses of this macro when implicitly cast to an +// indexable pointer should not suggest adding +// the `__counted_by` attribute. +//-------------------------------------------------------------------------- +int dummy_array[] = {0, 1, 2, 3}; +// SINGLE_EXPR isn't a DeclRefExpr so when this is implicitly cast we shouldn't +// suggest adding `__counted_by` to non existing DeclRefExpr. +#define SINGLE_EXPR ((int *__single)(&dummy_array[2])) + +//-------------------------------------------------------------------------- +// Function that return a __single pointer +// +// TODO(dliew): We should teach clang to produce a diagnostic for this case +// that suggests adding an attribute to the function return type +// (rdar://91928583). +//-------------------------------------------------------------------------- +int *__single returns_explicit_single(void); + +typedef struct { + int *__bidi_indexable field; +} BidiStruct_t; + +typedef struct { + int *__indexable field; +} IdxStruct_t; + +void use(void) { + //-------------------------------------------------------------------------- + // Initialization + //-------------------------------------------------------------------------- + int *implicit_bidi = explicitly_single0; // no warning + int *implicit_bidi2 = implicitly_single0; // no warning + int *implicit_bidi3 = SINGLE_EXPR; // no warning + // Note: This is a different warning than this test is really intended to test + // but we test it here for completeness. + // expected-warning@+1{{incompatible pointer types initializing 'int *__bidi_indexable' with an expression of type 'uint64_t *__single' (aka 'unsigned long *__single')}} + int *implicit_bidi4 = bigger_explicit_single0; + int *implicit_bidi5 = returns_explicit_single(); // no warning + int * _Nonnull implicit_bidi6 = returns_explicit_single(); // no warning + + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single1}} + int *__bidi_indexable explicit_bidi = explicitly_single1; + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single1}} + int *__bidi_indexable explicit_bidi2 = implicitly_single1; + // expected-warning-re@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + int *__bidi_indexable explicit_bidi3 = SINGLE_EXPR; + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'uint64_t *__single' (aka 'unsigned long *__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single1'}} + int *__bidi_indexable explicit_bidi4 = bigger_explicit_single1; + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + int *__bidi_indexable explicit_bidi5 = returns_explicit_single(); + int *__bidi_indexable explicit_bidi6 = (int *__bidi_indexable)explicitly_single2; // no warning + int *__bidi_indexable explicit_bidi7 = (int *__bidi_indexable)implicitly_single2; // no warning + int *__bidi_indexable explicit_bidi8 = (int *__bidi_indexable)SINGLE_EXPR; // no warning + int *__bidi_indexable explicit_bidi9 = (int *__bidi_indexable)bigger_explicit_single2; // no warning + int *__bidi_indexable explicit_bidi10 = (int *__bidi_indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single3'}} + int *__indexable explicit_idx = explicitly_single3; + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single3'}} + int *__indexable explicit_idx2 = implicitly_single3; + // expected-warning-re@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + int *__indexable explicit_idx3 = SINGLE_EXPR; + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'uint64_t *__single' (aka 'unsigned long *__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single3'}} + int *__indexable explicit_idx4 = bigger_explicit_single3; + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + int *__indexable explicit_idx5 = returns_explicit_single(); + int *__indexable explicit_idx6 = (int *__indexable)explicitly_single4; // no warning + int *__indexable explicit_idx7 = (int *__indexable)implicitly_single4; // no warning + int *__indexable explicit_idx8 = (int *__indexable)SINGLE_EXPR; // no warning + int *__indexable explicit_idx9 = (int *__indexable)bigger_explicit_single4; // no warning + int *__indexable explicit_idx10 = (int *__indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single5'}} + BidiStruct_t bidiStruct = {.field = explicitly_single5}; + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single5'}} + BidiStruct_t bidiStruct2 = {.field = implicitly_single5}; + // expected-warning-re@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + BidiStruct_t bidiStruct3 = {.field = SINGLE_EXPR}; + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'uint64_t *__single' (aka 'unsigned long *__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single5'}} + BidiStruct_t bidiStruct4 = {.field = bigger_explicit_single5}; + // expected-warning@+1{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + BidiStruct_t bidiStruct5 = {.field = returns_explicit_single()}; + BidiStruct_t bidiStruct6 = {.field = (int *__bidi_indexable)explicitly_single6}; // no warning + BidiStruct_t bidiStruct7 = {.field = (int *__bidi_indexable)implicitly_single6}; // no warning + BidiStruct_t bidiStruct8 = {.field = (int *__bidi_indexable)SINGLE_EXPR}; // no warning + BidiStruct_t bidiStruct9 = {.field = (int *__bidi_indexable)bigger_explicit_single6}; // no warning + BidiStruct_t bidiStruct10 = {.field = (int *__bidi_indexable)returns_explicit_single()}; // no warning + + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single7'}} + IdxStruct_t idxStruct = {.field = explicitly_single7}; + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single7'}} + IdxStruct_t idxStruct2 = {.field = implicitly_single7}; + // expected-warning-re@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + IdxStruct_t idxStruct3 = {.field = SINGLE_EXPR}; + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'uint64_t *__single' (aka 'unsigned long *__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single7'}} + IdxStruct_t idxStruct4 = {.field = bigger_explicit_single7}; + // expected-warning@+1{{initializing type 'int *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + IdxStruct_t idxStruct5 = {.field = returns_explicit_single()}; + IdxStruct_t idxStruct6 = {.field = (int *__indexable)explicitly_single8}; // no warning + IdxStruct_t idxStruct7 = {.field = (int *__indexable)implicitly_single8}; // no warning + IdxStruct_t idxStruct8 = {.field = (int *__indexable)SINGLE_EXPR}; // no warning + IdxStruct_t idxStruct9 = {.field = (int *__indexable)bigger_explicit_single8}; // no warning + IdxStruct_t idxStruct10 = {.field = (int *__indexable)returns_explicit_single()}; // no warning + + //-------------------------------------------------------------------------- + // Assignment + //-------------------------------------------------------------------------- + implicit_bidi = implicitly_single9; // no warning + implicit_bidi = explicitly_single9; // no warning + implicit_bidi = SINGLE_EXPR; // no warning + // Note: This is a different warning than this test is really intended to test + // but we test it here for completeness. + // expected-warning@+1{{incompatible pointer types assigning to 'int *__bidi_indexable' from 'uint64_t *__single' (aka 'unsigned long *__single')}} + implicit_bidi = bigger_explicit_single9; + implicit_bidi = returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single10'}} + explicit_bidi = implicitly_single10; + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single10'}} + explicit_bidi = explicitly_single10; + // expected-warning-re@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + explicit_bidi = SINGLE_EXPR; + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'uint64_t *__single' (aka 'unsigned long *__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single10'}} + explicit_bidi = bigger_explicit_single10; + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + explicit_bidi = returns_explicit_single(); + + explicit_bidi = (int *__bidi_indexable)implicitly_single11; // no warning + explicit_bidi = (int *__bidi_indexable)explicitly_single11; // no warning + explicit_bidi = (int *__bidi_indexable)SINGLE_EXPR; // no warning + explicit_bidi = (int *__bidi_indexable)bigger_explicit_single11; // no warning + explicit_bidi = (int *__bidi_indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single24'}} + explicit_idx = implicitly_single24; + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single24'}} + explicit_idx = explicitly_single24; + // expected-warning-re@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + explicit_idx = SINGLE_EXPR; + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'uint64_t *__single' (aka 'unsigned long *__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single24'}} + explicit_idx = bigger_explicit_single24; + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + explicit_idx = returns_explicit_single(); + + explicit_idx = (int *__indexable)implicitly_single25; // no warning + explicit_idx = (int *__indexable)explicitly_single25; // no warning + explicit_idx = (int *__indexable)SINGLE_EXPR; // no warning + explicit_idx = (int *__indexable)bigger_explicit_single25; // no warning + explicit_idx = (int *__indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single12'}} + bidiStruct.field = implicitly_single12; + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single12'}} + bidiStruct.field = explicitly_single12; + // expected-warning-re@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + bidiStruct.field = SINGLE_EXPR; + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'uint64_t *__single' (aka 'unsigned long *__single') results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single12'}} + bidiStruct.field = bigger_explicit_single12; + // expected-warning@+1{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + bidiStruct.field = returns_explicit_single(); + bidiStruct.field = (int *__bidi_indexable)implicitly_single13; // no warning + bidiStruct.field = (int *__bidi_indexable)explicitly_single13; // no warning + bidiStruct.field = (int *__bidi_indexable)SINGLE_EXPR; // no warning + bidiStruct.field = (int *__bidi_indexable)bigger_explicit_single13; // no warning + bidiStruct.field = (int *__bidi_indexable)returns_explicit_single(); // no warning + + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single14'}} + idxStruct.field = implicitly_single14; + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single14'}} + idxStruct.field = explicitly_single14; + // expected-warning-re@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + idxStruct.field = SINGLE_EXPR; + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'uint64_t *__single' (aka 'unsigned long *__single') results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single14'}} + idxStruct.field = bigger_explicit_single14; + // expected-warning@+1{{assigning to type 'int *__indexable' from type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + idxStruct.field = returns_explicit_single(); + idxStruct.field = (int *__indexable)implicitly_single15; // no warning + idxStruct.field = (int *__indexable)explicitly_single15; // no warning + idxStruct.field = (int *__indexable)SINGLE_EXPR; // no warning + idxStruct.field = (int *__indexable)bigger_explicit_single15; // no warning + idxStruct.field = (int *__indexable)returns_explicit_single(); // no warning + + //-------------------------------------------------------------------------- + // Argument pass + //-------------------------------------------------------------------------- + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single16'}} + take_bidi0(implicitly_single16); + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single16'}} + take_bidi1(explicitly_single16); + // expected-warning-re@+1{{passing type 'int *__single' to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + take_bidi2(SINGLE_EXPR); + // expected-warning@+1{{passing type 'uint64_t *__single' (aka 'unsigned long *__single') to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single16'}} + take_bidi3(bigger_explicit_single16); + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + take_bidi4(returns_explicit_single()); + + take_bidi5((int *__bidi_indexable)implicitly_single17); // no warning + take_bidi5((int *__bidi_indexable)explicitly_single17); // no warning + take_bidi5((int *__bidi_indexable)SINGLE_EXPR); // no warning + take_bidi5((int *__bidi_indexable)bigger_explicit_single17); // no warning + take_bidi5((int *__bidi_indexable)returns_explicit_single()); // no warning + + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single18'}} + take_idx0(implicitly_single18); + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single18'}} + take_idx1(explicitly_single18); + // expected-warning-re@+1{{passing type 'int *__single' to parameter of type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + take_idx2(SINGLE_EXPR); + // expected-warning@+1{{passing type 'uint64_t *__single' (aka 'unsigned long *__single') to parameter of type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single18'}} + take_idx3(bigger_explicit_single18); + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + take_idx4(returns_explicit_single()); + + take_idx5((int *__indexable)implicitly_single19); // no warning + take_idx5((int *__indexable)explicitly_single19); // no warning + take_idx5((int *__indexable)SINGLE_EXPR); // no warning + take_idx5((int *__indexable)bigger_explicit_single19); // no warning + take_idx5((int *__indexable)returns_explicit_single()); // no warning +} + +//-------------------------------------------------------------------------- +// Implicit conversion on return +//-------------------------------------------------------------------------- +int *__bidi_indexable warn_ret_bidi_expl(void) { + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single20'}} + return explicitly_single20; +} + +int *__bidi_indexable warn_ret_bidi_impl(void) { + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single20'}} + return implicitly_single20; +} + +int *__bidi_indexable warn_ret_bidi_single_expr(void) { + // expected-warning-re@+1{{returning type 'int *__single' from a function with result type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + return SINGLE_EXPR; +} + +int *__bidi_indexable warn_ret_bidi_bigger_explicit(void) { + // expected-warning@+1{{returning type 'uint64_t *__single' (aka 'unsigned long *__single') from a function with result type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single20'}} + return bigger_explicit_single20; +} + +int *__bidi_indexable warn_ret_bidi_tail_call_ret_single(void) { + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + return returns_explicit_single(); +} + +int *__bidi_indexable no_warn_ret_bidi_expl(void) { + return (int *__bidi_indexable)explicitly_single21; // no warning +} + +int *__bidi_indexable no_warn_ret_bidi_impl(void) { + return (int *__bidi_indexable)implicitly_single21; // no warning +} + +int *__bidi_indexable no_warn_ret_bidi_single_expr(void) { + return (int *__bidi_indexable)SINGLE_EXPR; // no warning +} + +int *__bidi_indexable no_warn_ret_bidi_bigger_explicit(void) { + return (int *__bidi_indexable)bigger_explicit_single21; // no warning +} + +int *__bidi_indexable no_warn_ret_bidi_tail_call_ret_single(void) { + return (int *__bidi_indexable)returns_explicit_single(); // no warning +} + +int *__indexable warn_ret_idx_expl(void) { + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'explicitly_single22'}} + return explicitly_single22; +} + +int *__indexable warn_ret_idx_impl(void) { + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'implicitly_single22'}} + return implicitly_single22; +} + +int *__indexable warn_ret_idx_single_expr(void) { + // expected-warning-re@+1{{returning type 'int *__single' from a function with result type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced{{$}}}} + return SINGLE_EXPR; +} + +int *__indexable warn_ret_idx_bigger_explicit(void) { + // expected-warning@+1{{returning type 'uint64_t *__single' (aka 'unsigned long *__single') from a function with result type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'bigger_explicit_single22'}} + return bigger_explicit_single22; +} + +int *__indexable warn_ret_idx_tail_call_ret_single(void) { + // expected-warning@+1{{returning type 'int *__single' from a function with result type 'int *__indexable' results in an __indexable pointer that will trap if a non-zero offset is dereferenced}} + return returns_explicit_single(); +} + +int *__indexable no_warn_ret_idx_expl(void) { + return (int *__indexable)explicitly_single23; // no warning +} + +int *__indexable no_warn_ret_idx_impl(void) { + return (int *__indexable)implicitly_single23; // no warning +} + +int *__indexable no_warn_ret_idx_single_expr(void) { + return (int *__indexable)SINGLE_EXPR; // no warning +} + +int *__indexable no_warn_ret_idx_bigger_explicit(void) { + return (int *__indexable)bigger_explicit_single23; // no warning +} + +int *__indexable no_warn_ret_idx_tail_call_ret_single(void) { + return (int *__indexable)returns_explicit_single(); // no warning +} diff --git a/clang/test/BoundsSafety/Sema/implicit-sized-by-to-explicit-sized-by.c b/clang/test/BoundsSafety/Sema/implicit-sized-by-to-explicit-sized-by.c new file mode 100644 index 0000000000000..c4083bd52b59f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/implicit-sized-by-to-explicit-sized-by.c @@ -0,0 +1,19 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-no-diagnostics + +// This should get __sized_by_or_null(count * size). +void *my_calloc(unsigned count, unsigned size) + __attribute__((alloc_size(1, 2))); + +#define ALLOC(size) my_calloc(size, 1) + +void test(void *__sized_by(*out_len) * out, unsigned *out_len) { + *out = ALLOC(sizeof(int)); + *out_len = sizeof(int); +} diff --git a/clang/test/BoundsSafety/Sema/include-project-header.c b/clang/test/BoundsSafety/Sema/include-project-header.c new file mode 100644 index 0000000000000..3b05f4ce33d09 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/include-project-header.c @@ -0,0 +1,14 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// expected-no-diagnostics + +#include +#include "mock-header.h" + +int main() { + // In this configuration, `returns_pointer` returns a __single pointer, so + // it converts to __bidi_indexable and dereferences properly. + int *p = returns_pointer(); + return *p; +} diff --git a/clang/test/BoundsSafety/Sema/include-system-header.c b/clang/test/BoundsSafety/Sema/include-system-header.c new file mode 100644 index 0000000000000..a5e2231883686 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/include-system-header.c @@ -0,0 +1,13 @@ + +// RUN: %clang_cc1 -fsyntax-only -DSYSTEM_HEADER -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -DSYSTEM_HEADER -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include "mock-header.h" + +int main() { + // In this configuration, `returns_pointer` returns an __unsafe_indexable + // pointer, so it _does not_ convert to a __bidi_indexable pointer. + int *p = returns_pointer(); // expected-error{{initializing 'int *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + return *p; +} diff --git a/clang/test/BoundsSafety/Sema/incompatible-nested-pointer-array.c b/clang/test/BoundsSafety/Sema/incompatible-nested-pointer-array.c new file mode 100644 index 0000000000000..b0c41da1b5098 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/incompatible-nested-pointer-array.c @@ -0,0 +1,31 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +typedef struct lol { + int *x[3]; +} lol_t; + +lol_t *g; + +// expected-note@+1{{passing argument to parameter 'arg' here}} +void test_arg(int *__unsafe_indexable *__counted_by(3) arg); + +void test(void) { + // expected-error@+1{{initializing 'int *__unsafe_indexable*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__single[3]'}} + int *__unsafe_indexable *__counted_by(3) m1 = g->x; + int *__unsafe_indexable *__counted_by(3) m2 = (int *__unsafe_indexable *)g->x; + + // expected-error@+1{{passing 'int *__single[3]' to parameter of incompatible nested pointer type 'int *__unsafe_indexable*__single __counted_by(3)' (aka 'int *__unsafe_indexable*__single')}} + test_arg(g->x); + + int *__unsafe_indexable *pp1; + // expected-error@+1{{assigning to 'int *__unsafe_indexable*__bidi_indexable' from 'int *__single[3]'}} + pp1 = g->x; +} + +int *__unsafe_indexable * test_ret() { + // expected-error@+1{{returning 'int *__single[3]' from a function with result of incompatible nested pointer type 'int *__unsafe_indexable*__single'}} + return g->x; +} diff --git a/clang/test/BoundsSafety/Sema/incompatible-ptr-casts-no-implicit-bound.c b/clang/test/BoundsSafety/Sema/incompatible-ptr-casts-no-implicit-bound.c new file mode 100644 index 0000000000000..d0e240cde8e3c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/incompatible-ptr-casts-no-implicit-bound.c @@ -0,0 +1,52 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +// expected-note@+1{{passing argument to parameter 'fp' here}} +int* foo(int **__unsafe_indexable fp); +// expected-note@+1{{passing argument to parameter 'fp' here}} +int* fooBound(int *__bidi_indexable fp); + +typedef int* (*bar_t)(int *__unsafe_indexable*__bidi_indexable); +typedef int* (*baz_t)(int **__single); + +void Test () { + // expected-note@+1{{pointer 'ptrThin' declared here}} + int *__single ptrThin; + // expected-error@+1{{initializing 'int *__bidi_indexable*__single' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'; use explicit cast to perform this conversion}} + int *__bidi_indexable *__single ptrBoundPtrThin = &ptrThin; + int *__single *__single ptrThinPtrThin = &ptrThin; // ok + int *__indexable *__single ptrArrayPtrThin; + + // expected-error@+1{{passing 'int *__indexable*__single' to parameter of incompatible nested pointer type 'int *__single*__unsafe_indexable'; use explicit cast to perform this conversion}} + foo(ptrArrayPtrThin); + foo(ptrThinPtrThin); // ok + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'ptrThin'}} + fooBound(ptrThin); + fooBound(*ptrBoundPtrThin); // ok + + long intVal = 0xdeadbeef; + int *__bidi_indexable ptr = (int *)intVal; // expected-error{{initializing 'int *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + ptr = intVal; + + // expected-error@+1{{incompatible integer to pointer conversion initializing 'int *__unsafe_indexable' with an expression of type 'long'}} + int *__unsafe_indexable ptrUnsafe = intVal; + + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + int *__single ptrThin2 = intVal; + + int **ptrPtr = (int**)ptr; // ok + // expected-warning@+1{{incompatible pointer types assigning}} + ptrPtr = ptr; + + int ***ptrPtrPtr = (int***)ptr; // ok + // expected-warning@+1{{incompatible pointer types assigning}} + ptrPtrPtr = ptr; + + // expected-error@+1{{conversion between pointers to functions with incompatible bound attributes}} + bar_t fp1 = foo; + // expected-error@+1{{conversion between pointers to functions with incompatible bound attributes}} + baz_t fp2 = foo; +} diff --git a/clang/test/BoundsSafety/Sema/incompatible-ptr-casts.c b/clang/test/BoundsSafety/Sema/incompatible-ptr-casts.c new file mode 100644 index 0000000000000..2e1198e5531d4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/incompatible-ptr-casts.c @@ -0,0 +1,167 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note +#include +#include "system-header-func-decl.h" + +int* fooBound(int *__bidi_indexable fp); + +void arrayFoo(int *(*__unsafe_indexable fp)[1]); + +typedef int* (*bar_t)(int *__unsafe_indexable*__bidi_indexable); +typedef int* (*baz_t)(int **__single); + +void Test () { + int *__single ptrThin; + int *__single (*__bidi_indexable ptrThinAPtrBound)[1]; + + // initializing + + // expected-error@+1{{initializing 'int *__bidi_indexable*__single' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + int *__bidi_indexable *__single ptrBoundPtrThin = &ptrThin; + int *__single *__bidi_indexable ptrThinPtrBound = &ptrThin; // ok + int *__single *__single ptrThinPtrThin = &ptrThin; // ok + // expected-error@+1{{initializing 'int *__indexable*__single' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + int *__indexable *__single ptrArrayPtrThin = &ptrThin; + + // expected-error@+1{{initializing 'int *__bidi_indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + int *__bidi_indexable(*__single ptrBoundAPtrThin)[1] = ptrThinAPtrBound; + int *__single(*__single ptrThinAPtrThin)[1] = ptrThinAPtrBound; + // expected-error@+1{{initializing 'int *__indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + int *__indexable(*__single ptrArrayAPtrThin)[1] = ptrThinAPtrBound; + + // expected-error@+1{{initializing 'int *__bidi_indexable*__single' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + int *__bidi_indexable *__single ptrBoundPtrThin2 = ptrThinAPtrBound; + // expected-warning@+1{{incompatible pointer types initializing 'int *__single*__bidi_indexable' with an expression of type 'int *__single(*__bidi_indexable)[1]'}} + int *__single *__bidi_indexable ptrThinPtrBound2 = ptrThinAPtrBound; + // expected-warning@+1{{incompatible pointer types initializing 'int *__single*__single' with an expression of type 'int *__single(*__bidi_indexable)[1]'}} + int *__single *__single ptrThinPtrThin2 = ptrThinAPtrBound; + // expected-error@+1{{initializing 'int *__indexable*__single' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + int *__indexable *__single ptrArrayPtrThin2 = ptrThinAPtrBound; + + // expected-error@+1{{initializing 'int *__bidi_indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + int *__bidi_indexable(*__single ptrBoundAPtrThin2)[1] = &ptrThin; + // expected-warning@+1{{incompatible pointer types initializing 'int *__single(*__bidi_indexable)[1]' with an expression of type 'int *__single*__bidi_indexable'}} + int *__single(*__bidi_indexable ptrThinAPtrBound2)[1] = &ptrThin; + // expected-warning@+1{{incompatible pointer types initializing 'int *__single(*__single)[1]' with an expression of type 'int *__single*__bidi_indexable'}} + int *__single(*__single ptrThinAPtrThin2)[1] = &ptrThin; + // expected-error@+1{{initializing 'int *__indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + int *__indexable(*__single ptrArrayAPtrThin2)[1] = &ptrThin; + + int *__bidi_indexable *__single *__bidi_indexable ptrBoundPtrThinPtrBound = &ptrBoundPtrThin; + int *__bidi_indexable *__single *__single ptrBoundPtrThinPtrThin = &ptrBoundPtrThin; + // expected-error@+1{{initializing 'int *__bidi_indexable*__indexable*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__bidi_indexable*__single*__bidi_indexable'}} + int *__bidi_indexable *__indexable *__bidi_indexable ptrBoundPtrArrayPtrBound = &ptrBoundPtrThin; + // expected-error@+1{{initializing 'int *__indexable*__single*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__bidi_indexable*__single*__bidi_indexable'}} + int *__indexable *__single *__bidi_indexable ptrArrayPtrThinPtrBound = &ptrBoundPtrThin; + // expected-error@+1{{initializing 'int *__indexable*__indexable*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__bidi_indexable*__single*__bidi_indexable'}} + int *__indexable *__indexable *__bidi_indexable ptrArrayPtrArrayPtrBound = &ptrBoundPtrThin; + + int *__bidi_indexable(*__single * __bidi_indexable ptrBoundAPtrThinPtrBound)[1] = &ptrBoundAPtrThin; + int *__bidi_indexable(*__single * __single ptrBoundAPtrThinPtrThin)[1] = &ptrBoundAPtrThin; + // expected-error@+1{{initializing 'int *__bidi_indexable(*__indexable*__bidi_indexable)[1]' with an expression of incompatible nested pointer type 'int *__bidi_indexable(*__single*__bidi_indexable)[1]'}} + int *__bidi_indexable(*__indexable * __bidi_indexable ptrBoundAPtrArrayPtrBound)[1] = &ptrBoundAPtrThin; + // expected-error@+1{{initializing 'int *__indexable(*__single*__bidi_indexable)[1]' with an expression of incompatible nested pointer type 'int *__bidi_indexable(*__single*__bidi_indexable)[1]'}} + int *__indexable(*__single * __bidi_indexable ptrArrayAPtrThinPtrBound)[1] = &ptrBoundAPtrThin; + // expected-error@+1{{initializing 'int *__indexable(*__indexable*__bidi_indexable)[1]' with an expression of incompatible nested pointer type 'int *__bidi_indexable(*__single*__bidi_indexable)[1]'}} + int *__indexable(*__indexable * __bidi_indexable ptrArrayAPtrArrayPtrBound)[1] = &ptrBoundAPtrThin; + + // casting + + int *__bidi_indexable *__single ptrBoundPtrThin3 = (int *__bidi_indexable *__single) & ptrThin; + int *__bidi_indexable *__single implicitPtrBoundPtrThin3 = & ptrThin; // expected-error{{initializing 'int *__bidi_indexable*__single' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + int *__single *__bidi_indexable ptrThinPtrBound3 = (int *__single *__bidi_indexable) & ptrThin; + int *__single *__bidi_indexable implicitPtrThinPtrBound3 = & ptrThin; + int *__single *__single ptrThinPtrThin3 = (int *__single *__single) & ptrThin; + int *__single *__single implicitPtrThinPtrThin3 = & ptrThin; + int *__indexable *__single ptrArrayPtrThin3 = (int *__indexable *__single) & ptrThin; + int *__indexable *__single implicitPtrArrayPtrThin3 = & ptrThin; // expected-error{{initializing 'int *__indexable*__single' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + + int *__bidi_indexable(*__single ptrBoundAPtrThin3)[1] = (int *__bidi_indexable(*__single)[1])ptrThinAPtrBound; + int *__bidi_indexable(*__single implicitPtrBoundAPtrThin3)[1] = ptrThinAPtrBound; // expected-error{{initializing 'int *__bidi_indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + int *__single(*__bidi_indexable ptrThinAPtrBound3)[1] = (int *__single(*__bidi_indexable)[1])ptrThinAPtrBound; + int *__single(*__bidi_indexable implicitPtrThinAPtrBound3)[1] = ptrThinAPtrBound; + int *__single(*__single ptrThinAPtrThin3)[1] = (int *__single(*__single)[1])ptrThinAPtrBound; + int *__single(*__single implicitPtrThinAPtrThin3)[1] = ptrThinAPtrBound; + int *__indexable(*__single ptrArrayAPtrThin3)[1] = (int *__indexable(*__single)[1])ptrThinAPtrBound; + int *__indexable(*__single implicitPtrArrayAPtrThin3)[1] = ptrThinAPtrBound; // expected-error{{initializing 'int *__indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + + int *__bidi_indexable *__single ptrBoundPtrThin4 = (int *__bidi_indexable *__single)ptrThinAPtrBound; + int *__bidi_indexable *__single implicitPtrBoundPtrThin4 = ptrThinAPtrBound; // expected-error{{initializing 'int *__bidi_indexable*__single' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + int *__single *__bidi_indexable ptrThinPtrBound4 = (int *__single *__bidi_indexable)ptrThinAPtrBound; + int *__single *__bidi_indexable implicitPtrThinPtrBound4 = ptrThinAPtrBound; // expected-warning{{incompatible pointer types initializing}} + int *__single *__single ptrThinPtrThin4 = (int *__single *__single)ptrThinAPtrBound; + int *__single *__single implicitPtrThinPtrThin4 = ptrThinAPtrBound; // expected-warning{{incompatible pointer types initializing}} + int *__indexable *__single ptrArrayPtrThin4 = (int *__indexable *__single)ptrThinAPtrBound; + int *__indexable *__single implicitPtrArrayPtrThin4 = ptrThinAPtrBound; // expected-error{{initializing 'int *__indexable*__single' with an expression of incompatible nested pointer type 'int *__single(*__bidi_indexable)[1]'}} + + int *__bidi_indexable(*__single ptrBoundAPtrThin4)[1] = (int *__bidi_indexable(*__single)[1]) & ptrThin; + int *__bidi_indexable(*__single implicitPtrBoundAPtrThin4)[1] = & ptrThin; // expected-error{{initializing 'int *__bidi_indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + int *__single(*__bidi_indexable ptrThinAPtrBound4)[1] = (int *__single(*__bidi_indexable)[1]) & ptrThin; + int *__single(*__bidi_indexable implicitPtrThinAPtrBound4)[1] = & ptrThin; // expected-warning{{incompatible pointer types initializing}} + int *__single(*__single ptrThinAPtrThin4)[1] = (int *__single(*__single)[1]) & ptrThin; + int *__single(*__single implicitPtrThinAPtrThin4)[1] = & ptrThin; // expected-warning{{incompatible pointer types initializing}} + int *__indexable(*__single ptrArrayAPtrThin4)[1] = (int *__indexable(*__single)[1]) & ptrThin; + int *__indexable(*__single implicitPtrArrayAPtrThin4)[1] = & ptrThin; // expected-error{{initializing 'int *__indexable(*__single)[1]' with an expression of incompatible nested pointer type 'int *__single*__bidi_indexable'}} + + int *__bidi_indexable *__single *__bidi_indexable ptrBoundPtrThinPtrBound2 = (int *__bidi_indexable *__single *__bidi_indexable) & ptrBoundPtrThin; + int *__bidi_indexable *__single *__bidi_indexable implicitPtrBoundPtrThinPtrBound2 = & ptrBoundPtrThin; + int *__bidi_indexable *__single *__single ptrBoundPtrThinPtrThin2 = (int *__bidi_indexable *__single *__single) & ptrBoundPtrThin; + int *__bidi_indexable *__single *__single implicitPtrBoundPtrThinPtrThin2 = & ptrBoundPtrThin; + int *__bidi_indexable *__indexable *__bidi_indexable ptrBoundPtrArrayPtrBound2 = (int *__bidi_indexable *__indexable *__bidi_indexable) & ptrBoundPtrThin; + int *__bidi_indexable *__indexable *__bidi_indexable implicitPtrBoundPtrArrayPtrBound2 = & ptrBoundPtrThin; // expected-error{{initializing 'int *__bidi_indexable*__indexable*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__bidi_indexable*__single*__bidi_indexable'}} + int *__indexable *__single *__bidi_indexable ptrArrayPtrThinPtrBound2 = (int *__indexable *__single *__bidi_indexable) & ptrBoundPtrThin; + int *__indexable *__single *__bidi_indexable implicitPtrArrayPtrThinPtrBound2 = & ptrBoundPtrThin; // expected-error{{initializing 'int *__indexable*__single*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__bidi_indexable*__single*__bidi_indexable'}} + int *__indexable *__indexable *__bidi_indexable ptrArrayPtrArrayPtrBound2 = (int *__indexable *__indexable *__bidi_indexable) & ptrBoundPtrThin; + int *__indexable *__indexable *__bidi_indexable implicitPtrArrayPtrArrayPtrBound2 = & ptrBoundPtrThin; // expected-error{{initializing 'int *__indexable*__indexable*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__bidi_indexable*__single*__bidi_indexable'}} + + int *__bidi_indexable(*__single * __bidi_indexable ptrBoundAPtrThinPtrBound2)[1] = (int *__bidi_indexable(*__single * __bidi_indexable)[1]) & ptrBoundAPtrThin; + int *__bidi_indexable(*__single * __bidi_indexable implicitPtrBoundAPtrThinPtrBound2)[1] = & ptrBoundAPtrThin; + int *__bidi_indexable(*__single * __single ptrBoundAPtrThinPtrThin2)[1] = (int *__bidi_indexable(*__single * __single)[1]) & ptrBoundAPtrThin; + int *__bidi_indexable(*__single * __single implicitPtrBoundAPtrThinPtrThin2)[1] = & ptrBoundAPtrThin; + int *__bidi_indexable(*__indexable * __bidi_indexable ptrBoundAPtrArrayPtrBound2)[1] = (int *__bidi_indexable(*__indexable * __bidi_indexable)[1]) & ptrBoundAPtrThin; + int *__bidi_indexable(*__indexable * __bidi_indexable implicitPtrBoundAPtrArrayPtrBound2)[1] = & ptrBoundAPtrThin; // expected-error{{initializing 'int *__bidi_indexable(*__indexable*__bidi_indexable)[1]' with an expression of incompatible nested pointer type 'int *__bidi_indexable(*__single*__bidi_indexable)[1]'}} + int *__indexable(*__single * __bidi_indexable ptrArrayAPtrThinPtrBound2)[1] = (int *__indexable(*__single * __bidi_indexable)[1]) & ptrBoundAPtrThin; + int *__indexable(*__single * __bidi_indexable implicitPtrArrayAPtrThinPtrBound2)[1] = & ptrBoundAPtrThin; // expected-error{{initializing 'int *__indexable(*__single*__bidi_indexable)[1]' with an expression of incompatible nested pointer type 'int *__bidi_indexable(*__single*__bidi_indexable)[1]'}} + int *__indexable(*__indexable * __bidi_indexable ptrArrayAPtrArrayPtrBound2)[1] = (int *__indexable(*__indexable * __bidi_indexable)[1]) & ptrBoundAPtrThin; + int *__indexable(*__indexable * __bidi_indexable implicitPtrArrayAPtrArrayPtrBound2)[1] = & ptrBoundAPtrThin; // expected-error{{initializing 'int *__indexable(*__indexable*__bidi_indexable)[1]' with an expression of incompatible nested pointer type 'int *__bidi_indexable(*__single*__bidi_indexable)[1]'}} + + // passing + + // expected-error@+1{{int *__indexable*__single' to parameter of incompatible nested pointer type 'int **'}} + foo(ptrArrayPtrThin); + foo(ptrThinPtrBound); // ok + foo(ptrThinPtrThin); // ok + // expected-warning@+1{{passing type 'int *__single' to parameter of type 'int *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + fooBound(ptrThin); + fooBound(*ptrBoundPtrThin); // ok + + // expected-error@+1{{passing 'int *__indexable(*__single)[1]' to parameter of incompatible nested pointer type 'int *__single(*__unsafe_indexable)[1]'}} + arrayFoo(ptrArrayAPtrThin); + arrayFoo(ptrThinAPtrBound); // ok + arrayFoo(ptrThinAPtrThin); // ok + + long intVal; + int *ptr = (int*)intVal; // expected-error{{initializing 'int *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable' to perform this conversion}} + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + ptr = intVal; + + // FIXME: rdar://70651808 + // expected-error@+1{{incompatible integer to pointer conversion initializing 'int *__unsafe_indexable' with an expression of type 'long'}} + int *__unsafe_indexable ptrUnsafe = intVal; + + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety; use '__unsafe_forge_single' or '__unsafe_forge_bidi_indexable'}} + int *__single ptrThin2 = intVal; + + int **ptrPtr = (int**)ptr; // ok + // expected-warning@+1{{incompatible pointer types assigning}} + ptrPtr = ptr; + + int ***ptrPtrPtr = (int***)ptr; // ok + // expected-warning@+1{{incompatible pointer types assigning}} + ptrPtrPtr = ptr; + + // expected-error@+1{{conversion between pointers to functions with incompatible bound attributes}} + bar_t fp1 = foo; + baz_t fp2 = foo; +} diff --git a/clang/test/BoundsSafety/Sema/incomplete-single-to-indexable.c b/clang/test/BoundsSafety/Sema/incomplete-single-to-indexable.c new file mode 100644 index 0000000000000..fed31bc8a2d6c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/incomplete-single-to-indexable.c @@ -0,0 +1,92 @@ + +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve -mvscale-min=4 -mvscale-max=4 -flax-vector-conversions=none -ffreestanding -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve -mvscale-min=4 -mvscale-max=4 -flax-vector-conversions=none -ffreestanding -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include +struct oq_struct; + +typedef struct oq_struct* oq_struct_t; +#define NULL ((void *__single)0) + +oq_struct_t __unsafe_indexable foo(); + +oq_struct_t test() { + oq_struct_t local = 0; // expected-note{{pointer 'local' declared here}} + // expected-error@+1{{cannot assign to indexable pointer with type 'struct oq_struct *__bidi_indexable' from __single pointer to incomplete type 'oq_struct_t __single' (aka 'struct oq_struct *__single'); consider declaring pointer 'local' as '__single'}} + local = __unsafe_forge_single(oq_struct_t, foo()); + return local; +} + +void test_sve(svint8_t *arg) { + // expected-error@+1{{cannot initialize indexable pointer with type 'svint8_t *__bidi_indexable' (aka '__SVInt8_t *__bidi_indexable') from __single pointer to incomplete type 'svint8_t *__single' (aka '__SVInt8_t *__single'); consider declaring pointer 'local' as '__single'}} + svint8_t *local = arg; // expected-note{{pointer 'local' declared here}} +} + +typedef void(func_t)(void); +// function type is okay because it will always be '__single' +void test_func(func_t *arg) { + func_t *local = arg; +} + +void test_null() { + int *impl_bidi_ptr = NULL; + int *__bidi_indexable bidi_ptr = NULL; + oq_struct_t __indexable ind_ptr = NULL; +} + +void test_null_to_bidi() { + int *impl_bidi_ptr = (int *__bidi_indexable)NULL; + int *impl_bidi_ptr2 = (int *__indexable)NULL; + int *impl_bidi_ptr3 = (void *)(char *)NULL; +} + +// The extra note is omitted because another note about the param is already emitted +// and there's no FixIt. +// expected-note@+1{{passing argument to parameter 'param' here}} +void bidi_sink(void* __bidi_indexable param); + +// The extra note is omitted because another note about the param is already emitted +// and there's no FixIt. +// expected-note@+1{{passing argument to parameter here}} +void bidi_sink_no_param_name(void* __bidi_indexable); + +int* __bidi_indexable single_opaque_assigned_to_indexable_ret( + void* __single p) { + // expected-error@+1{{cannot return __single pointer to incomplete type 'void *__single' when return type is an indexable pointer 'int *__bidi_indexable'; consider making the return type '__single'}} + return p; +} + +void single_opaque_arg_passed(void* __single p) { + // expected-error@+1{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'param' parameter '__single'}} + bidi_sink(p); +} + +void single_opaque_arg_passed2(void* __single p) { + // expected-error@+1{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the parameter '__single'}} + bidi_sink_no_param_name(p); +} + +__ptrcheck_abi_assume_bidi_indexable(); + +// expected-note@+1{{passing argument to parameter 'implicit_bidi' here}} +void implicit_bidi_sink(void* implicit_bidi); + +// expected-note@+3{{pointer 'implicit_bidi2' declared here}} // This is emitted due to a FixIt needs to be attached to a diagnostic and we can't attach it to the error. +// expected-note@+2{{passing argument to parameter 'implicit_bidi2' here}} +// expected-note@+1{{passing argument to parameter 'implicit_bidi2' here}} +void implicit_bidi_sink2(void* implicit_bidi2); + +__ptrcheck_abi_assume_single(); + +void single_opaque_arg_passed_to_implicit_bidi_param(void* __single p) { + // expected-error@+1{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'implicit_bidi' parameter '__single'}} + implicit_bidi_sink(p); +} + +void single_opaque_arg_passed_to_implicit_bidi_param2(void* __single p) { + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'implicit_bidi2' parameter '__single'}} + // expected-error@+2{{cannot pass __single pointer to incomplete type argument 'void *__single' when parameter is an indexable pointer 'void *__bidi_indexable'; consider making the 'implicit_bidi2' parameter '__single'}} + implicit_bidi_sink2(p); + implicit_bidi_sink2(p); +} diff --git a/clang/test/BoundsSafety/Sema/indexable-ptr-minus-assign.c b/clang/test/BoundsSafety/Sema/indexable-ptr-minus-assign.c new file mode 100644 index 0000000000000..e0993b26d95b8 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/indexable-ptr-minus-assign.c @@ -0,0 +1,19 @@ + + +// RUN: %clang_cc1 -fbounds-safety -fsyntax-only -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -verify %s + +#include + +int foo(int * __indexable ptr, unsigned idx) { + ptr -= idx; + // expected-error@-1 {{decremented indexable pointer 'ptr' is out of bounds}} + return *ptr; +} + +int main() { + int a; + int * __indexable p = &a; + + return foo(p, -1); +} diff --git a/clang/test/BoundsSafety/Sema/init-global-indexable-array-oob.c b/clang/test/BoundsSafety/Sema/init-global-indexable-array-oob.c new file mode 100644 index 0000000000000..9bde9d43e4887 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/init-global-indexable-array-oob.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int array[10] = {0}; +int *__indexable idxa = array - 1; // expected-error{{initializer element is not a compile-time constant}} +int *__indexable idxb = array; +int *__indexable idxc = array + 9; +int *__indexable idxd = array + 10; // expected-error{{initializer element is not a compile-time constant}} + +struct { + int p; + int q[10]; +} struct_with_array; +int *__indexable idxe = struct_with_array.q - 1; // expected-error{{initializer element is not a compile-time constant}} +int *__indexable idxf = struct_with_array.q; +int *__indexable idxg = struct_with_array.q + 9; +int *__indexable idxh = struct_with_array.q + 10; // expected-error{{initializer element is not a compile-time constant}} diff --git a/clang/test/BoundsSafety/Sema/init-global-indexable-bidi-oob.c b/clang/test/BoundsSafety/Sema/init-global-indexable-bidi-oob.c new file mode 100644 index 0000000000000..35d6420383ed4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/init-global-indexable-bidi-oob.c @@ -0,0 +1,12 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int array[10] = {0}; + +const int * __bidi_indexable const bidx = &array[9] - 10; + +const int * __indexable const idxa = bidx; +// expected-error@-1{{initializer element is not a compile-time constant}} diff --git a/clang/test/BoundsSafety/Sema/init-struct-const-count.c b/clang/test/BoundsSafety/Sema/init-struct-const-count.c new file mode 100644 index 0000000000000..e4c02ed169f0a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/init-struct-const-count.c @@ -0,0 +1,131 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fno-bounds-safety-bringup-missing-checks=all -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fbounds-safety-bringup-missing-checks=all -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fno-bounds-safety-bringup-missing-checks=all -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fbounds-safety-bringup-missing-checks=all -verify %s +#include + +// expected-no-diagnostics + +// ============================================================================= +// __counted_by +// ============================================================================= + +struct cb { + const int count; + int* __counted_by(count) ptr; +}; + +void consume_cb(struct cb); + +void init_list_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +void init_list_cb_bidi(int count_param, int* __bidi_indexable ptr) { + struct cb c = {.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +void compound_literal_init_cb(int count_param, int*__counted_by(count_param) ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +void compound_literal_init_cb_bidi(int count_param, int*__bidi_indexable ptr) { + struct cb c = (struct cb){.count = count_param, .ptr = ptr }; + consume_cb(c); +} + +// ============================================================================= +// __counted_by_or_null +// ============================================================================= + +struct cbon { + const int count; + int* __counted_by_or_null(count) ptr; +}; + +void consume_cbon(struct cbon); + +void init_list_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +void init_list_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = {.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +void compound_literal_init_cbon(int count_param, int*__counted_by_or_null(count_param) ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +void compound_literal_init_cbon_bidi(int count_param, int*__bidi_indexable ptr) { + struct cbon c = (struct cbon){.count = count_param, .ptr = ptr }; + consume_cbon(c); +} + +// ============================================================================= +// __sized_by +// ============================================================================= + +struct sb { + const int count; + char* __sized_by(count) ptr; +}; + +void consume_sb(struct sb); + +void init_list_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +void init_list_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = {.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +void compound_literal_init_sb(int count_param, char*__sized_by(count_param) ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +void compound_literal_init_sb_bidi(int count_param, char*__bidi_indexable ptr) { + struct sb c = (struct sb){.count = count_param, .ptr = ptr }; + consume_sb(c); +} + +// ============================================================================= +// __sized_by_or_null +// ============================================================================= + +struct sbon { + const int count; + char* __sized_by_or_null(count) ptr; +}; + +void consume_sbon(struct sbon); + +void init_list_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +void init_list_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = {.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +void compound_literal_init_sbon(int count_param, char*__sized_by_or_null(count_param) ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} + +void compound_literal_init_sbon_bidi(int count_param, char*__bidi_indexable ptr) { + struct sbon c = (struct sbon){.count = count_param, .ptr = ptr }; + consume_sbon(c); +} diff --git a/clang/test/BoundsSafety/Sema/late-parsing-do-not-regress-others.c b/clang/test/BoundsSafety/Sema/late-parsing-do-not-regress-others.c new file mode 100644 index 0000000000000..89de646ea4d81 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/late-parsing-do-not-regress-others.c @@ -0,0 +1,31 @@ + + +// RUN: %clang_cc1 -fsyntax-only -verify -Wthread-safety %s +// RUN: %clang_cc1 -fbounds-safety -fsyntax-only -verify -Wthread-safety %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c -fsyntax-only -verify -Wthread-safety %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c++ -fsyntax-only -verify -Wthread-safety %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -fsyntax-only -verify -Wthread-safety %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c++ -fsyntax-only -verify -Wthread-safety %s + +#include + +#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__ ((exclusive_lock_function(__VA_ARGS__))) +#define SHARED_LOCK_FUNCTION(...) __attribute__ ((shared_lock_function(__VA_ARGS__))) +#define UNLOCK_FUNCTION(...) __attribute__ ((unlock_function(__VA_ARGS__))) + +void elf_fun_params(int lvar EXCLUSIVE_LOCK_FUNCTION()); // \ + // expected-warning {{'exclusive_lock_function' attribute applies to function parameters only if their type is a reference to a 'scoped_lockable'-annotated type}} +void slf_fun_params(int lvar SHARED_LOCK_FUNCTION()); // \ + // expected-warning {{'shared_lock_function' attribute applies to function parameters only if their type is a reference to a 'scoped_lockable'-annotated type}} +void uf_fun_params(int lvar UNLOCK_FUNCTION()); // \ + // expected-warning {{'unlock_function' attribute applies to function parameters only if their type is a reference to a 'scoped_lockable'-annotated type}} + +// regression tests added for rdar://92699615 +typedef bool __attribute__((capability("role"))) role_t; +extern role_t guard1; +int __attribute__((guarded_by(guard1))) alloc1; +extern int guard2; +int __attribute__((guarded_by(guard2))) alloc2; // \ + // expected-warning{{'guarded_by' attribute requires arguments whose type is annotated with 'capability' attribute; type here is 'int'}} +int __attribute__((guarded_by(guard3))) alloc3; // \ +// expected-error{{use of undeclared identifier 'guard3'}} diff --git a/clang/test/BoundsSafety/Sema/local-var-dynamic-count-different-lifetime.c b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-different-lifetime.c new file mode 100644 index 0000000000000..1d3a5c6e700be --- /dev/null +++ b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-different-lifetime.c @@ -0,0 +1,213 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void none_none(void) { + int len1; + int *__counted_by(len1) ptr1; + + int len2; + int *__sized_by(len2) ptr2; +} + +void none_static(void) { + int len1; + static int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + int len2; + static int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void none_auto(void) { + int len1; + auto int *__counted_by(len1) ptr1; + + int len2; + auto int *__sized_by(len2) ptr2; +} + +void none_register(void) { + int len1; + register int *__counted_by(len1) ptr1; + + int len2; + register int *__sized_by(len2) ptr2; +} + +void static_none(void) { + static int len1; + int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + static int len2; + int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void static_static(void) { + static int len1; + static int *__counted_by(len1) ptr1; + + static int len2; + static int *__sized_by(len2) ptr2; +} + +void static_auto(void) { + static int len1; + auto int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + static int len2; + auto int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void static_register(void) { + static int len1; + register int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + static int len2; + register int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void auto_none(void) { + auto int len1; + int *__counted_by(len1) ptr1; + + auto int len2; + int *__sized_by(len2) ptr2; +} + +void auto_static(void) { + auto int len1; + static int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + auto int len2; + static int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void auto_auto(void) { + auto int len1; + auto int *__counted_by(len1) ptr1; + + auto int len2; + auto int *__sized_by(len2) ptr2; +} + +void auto_register(void) { + auto int len1; + register int *__counted_by(len1) ptr1; + + auto int len2; + register int *__sized_by(len2) ptr2; +} + +void register_none(void) { + register int len1; + int *__counted_by(len1) ptr1; + + register int len2; + int *__sized_by(len2) ptr2; +} + +void register_static(void) { + register int len1; + static int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + register int len2; + static int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void register_auto(void) { + register int len1; + auto int *__counted_by(len1) ptr1; + + register int len2; + auto int *__sized_by(len2) ptr2; +} + +void register_register(void) { + register int len1; + register int *__counted_by(len1) ptr1; + + register int len2; + register int *__sized_by(len2) ptr2; +} + +void extern_none(void) { + extern int len1; + int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + extern int len2; + int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void extern_static(void) { + extern int len1; + static int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + extern int len2; + static int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void extern_auto(void) { + extern int len1; + auto int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + extern int len2; + auto int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void extern_register(void) { + extern int len1; + register int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + extern int len2; + register int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void private_extern_none(void) { + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len1; + int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len2; + int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void private_extern_static(void) { + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len1; + static int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len2; + static int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void private_extern_auto(void) { + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len1; + auto int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len2; + auto int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} + +void private_extern_register(void) { + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len1; + register int *__counted_by(len1) ptr1; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int len2; + register int *__sized_by(len2) ptr2; // expected-error{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} +} diff --git a/clang/test/BoundsSafety/Sema/local-var-dynamic-count-different-scope-and-lifetime.c b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-different-scope-and-lifetime.c new file mode 100644 index 0000000000000..3105e90621ed9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-different-scope-and-lifetime.c @@ -0,0 +1,114 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +int g_len1; + +void f1(void) { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by(g_len1) ptr; + // expected-error@+2{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + int *__sized_by(g_len1) ptr2; + // expected-error@+2{{argument of '__counted_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by_or_null' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by_or_null(g_len1) ptr3; + // expected-error@+2{{argument of '__sized_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by_or_null' attribute cannot refer to declaration of a different lifetime}} + int *__sized_by_or_null(g_len1) ptr4; +} + +static int g_len2; + +void f2(void) { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by(g_len2) ptr; + // expected-error@+2{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + int *__sized_by(g_len2) ptr2; + // expected-error@+2{{argument of '__counted_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by_or_null' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by_or_null(g_len2) ptr3; + // expected-error@+2{{argument of '__sized_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by_or_null' attribute cannot refer to declaration of a different lifetime}} + int *__sized_by_or_null(g_len2) ptr4; +} + +int g_len3; + +void f3(void) { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + static int *__counted_by(g_len3) ptr; + // expected-error@+2{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + static int *__sized_by(g_len3) ptr2; + // expected-error@+2{{argument of '__counted_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by_or_null' attribute cannot refer to declaration of a different lifetime}} + static int *__counted_by_or_null(g_len3) ptr3; + // expected-error@+2{{argument of '__sized_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by_or_null' attribute cannot refer to declaration of a different lifetime}} + static int *__sized_by_or_null(g_len3) ptr4; +} + +static int g_len4; + +void f4(void) { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + static int *__counted_by(g_len4) ptr; + // expected-error@+2{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + static int *__sized_by(g_len4) ptr2; + // expected-error@+2{{argument of '__counted_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by_or_null' attribute cannot refer to declaration of a different lifetime}} + static int *__counted_by_or_null(g_len4) ptr3; + // expected-error@+2{{argument of '__sized_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by_or_null' attribute cannot refer to declaration of a different lifetime}} + static int *__sized_by_or_null(g_len4) ptr4; +} + +void f5(void) { + static int len; + { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by(len) ptr; + // expected-error@+2{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + int *__sized_by(len) ptr2; + // expected-error@+2{{argument of '__counted_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by_or_null' attribute cannot refer to declaration of a different lifetime}} + int *__counted_by_or_null(len) ptr3; + // expected-error@+2{{argument of '__sized_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by_or_null' attribute cannot refer to declaration of a different lifetime}} + int *__sized_by_or_null(len) ptr4; + } +} + +void f6(void) { + int len; + { + // expected-error@+2{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + static int *__counted_by(len) ptr; + // expected-error@+2{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + static int *__sized_by(len) ptr2; + // expected-error@+2{{argument of '__counted_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__counted_by_or_null' attribute cannot refer to declaration of a different lifetime}} + static int *__counted_by_or_null(len) ptr3; + // expected-error@+2{{argument of '__sized_by_or_null' attribute cannot refer to declaration from a different scope}} + // expected-error@+1{{argument of '__sized_by_or_null' attribute cannot refer to declaration of a different lifetime}} + static int *__sized_by_or_null(len) ptr4; + } +} diff --git a/clang/test/BoundsSafety/Sema/local-var-dynamic-count-external-storage.c b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-external-storage.c new file mode 100644 index 0000000000000..d228f7c8a4eed --- /dev/null +++ b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-external-storage.c @@ -0,0 +1,64 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int glen; +extern int extglen; + +void ptr(void) { + extern int len1; + extern int *__counted_by(len1) ptr1; + + extern int len2; + extern int *__sized_by(len2) ptr2; + + int len3; + // expected-error@+3{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int *__counted_by(len3) ptr3; + + extern int len4; + // expected-error@+3{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int *__sized_by(len4) ptr4; + + // expected-error@+4{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + // expected-error@+3{{argument of '__sized_by' attribute cannot refer to declaration from a different scope}} + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int *__sized_by(extglen) ptr5; + +} + +void fptr(void) { + extern int *__counted_by(42) (*fptr1)(void); + + extern int *__sized_by(42) (*fptr2)(void); + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int *__counted_by(42) (*fptr3)(void); + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int *__sized_by(42) (*fptr4)(void); +} + +void incomplete_array(void) { + extern int array1[__counted_by(42)]; + + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int array2[__counted_by(42)]; + + extern int len3; + // expected-error@+3{{argument of '__counted_by' attribute cannot refer to declaration of a different lifetime}} + // expected-warning@+2{{use of __private_extern__ on a declaration may not produce external symbol private to the linkage unit and is deprecated}} + // expected-note@+1{{use __attribute__((visibility("hidden"))) attribute instead}} + __private_extern__ int array3[__counted_by(len3)]; + +} diff --git a/clang/test/BoundsSafety/Sema/local-var-dynamic-count-in-basic-block.c b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-in-basic-block.c new file mode 100644 index 0000000000000..98d9a02be6596 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/local-var-dynamic-count-in-basic-block.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +void Test() { + int len; + + if (len) { + int *__counted_by(len) ptr; // expected-error{{argument of '__counted_by' attribute cannot refer to declaration from a different scope}} + } + // expected-error@+2 3{{local variable len2 must be declared right next to its dependent decl}} + // expected-error@+3 3{{local variable ptr2 must be declared right next to its dependent decl}} rdar://115657607 + int len2; + // expected-note@+1 4{{previous use is here}} + int *__counted_by(len2) ptr2; + int *__counted_by(len2) ptr3; // expected-error{{variable 'len2' referred to by __counted_by variable cannot be used in other dynamic bounds attributes}} + int *__counted_by_or_null(len2) ptr4; // expected-error{{variable 'len2' referred to by __counted_by_or_null variable cannot be used in other dynamic bounds attributes}} + int *__sized_by(len2) ptr5; // expected-error{{variable 'len2' referred to by __sized_by variable cannot be used in other dynamic bounds attributes}} + int *__sized_by_or_null(len2) ptr6; // expected-error{{variable 'len2' referred to by __sized_by_or_null variable cannot be used in other dynamic bounds attributes}} +} diff --git a/clang/test/BoundsSafety/Sema/mismatching-out-parameter.c b/clang/test/BoundsSafety/Sema/mismatching-out-parameter.c new file mode 100644 index 0000000000000..503cb19d02c71 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/mismatching-out-parameter.c @@ -0,0 +1,261 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + int *__counted_by(len) buf; + int len; +}; + +struct S_Nullable { + int *__counted_by_or_null(len) buf; + int len; +}; + +struct T { + int *__counted_by(len + 1) buf; + int len; +}; + +struct T_Nullable { + int *__counted_by_or_null(len + 1) buf; + int len; +}; + +struct U { + int *__counted_by(len) buf; + int *__counted_by(len) buf2; + int len; +}; + +struct U_Nullable { + int *__counted_by_or_null(len) buf; + int *__counted_by_or_null(len) buf2; + int len; +}; + +struct V { + int *__sized_by(len) buf; + int *__counted_by(len) buf2; + int *__indexable buf3; + int *__bidi_indexable buf4; + int *__single buf5; + int *__sized_by_or_null(len) buf6; + int *__counted_by_or_null(len) buf7; + + int len; +}; + +int arr[10]; + +void foo(int *out_len, int *__counted_by(*out_len) * out_buf) { + // expected-note@-1 4{{passing argument to parameter 'out_buf' here}} + *out_len = 9; + *out_buf = arr; + return; +} + +void foo_nullable(int *out_len, int *__counted_by_or_null(*out_len) * out_buf) { + // expected-note@-1 4{{passing argument to parameter 'out_buf' here}} + *out_len = 9; + *out_buf = arr; + return; +} + +void bar(int *fake_out_len, int **fake_out_buf) { + *fake_out_buf = arr; + *fake_out_len = 12; + return; +} + +void bar_nullable(int *fake_out_len, int **fake_out_buf) { + *fake_out_buf = arr; + *fake_out_len = 12; + return; +} + +void baz123(int *__counted_by(*out_len) buf, int *out_len, int *fake_out_len); +void baz132(int *__counted_by(*out_len) buf, int *fake_out_len, int *out_len); +void baz213(int *out_len, int *__counted_by(*out_len) buf, int *fake_out_len); +void baz312(int *fake_out_len, int *__counted_by(*out_len) buf, int *out_len); +void bazo123(int *__counted_by(*out_len) *out_buf, int *out_len, int *fake_out_len); +void bazo132(int *__counted_by(*out_len) *out_buf, int *fake_out_len, int *out_len); +void bazo213(int *out_len, int *__counted_by(*out_len) *out_buf, int *fake_out_len); +void bazo312(int *fake_out_len, int *__counted_by(*out_len) *out_buf, int *out_len); + +void baz123_nullable(int *__counted_by_or_null(*out_len) buf, int *out_len, int *fake_out_len); +void baz132_nullable(int *__counted_by_or_null(*out_len) buf, int *fake_out_len, int *out_len); +void baz213_nullable(int *out_len, int *__counted_by_or_null(*out_len) buf, int *fake_out_len); +void baz312_nullable(int *fake_out_len, int *__counted_by_or_null(*out_len) buf, int *out_len); +void bazo123_nullable(int *__counted_by_or_null(*out_len) *out_buf, int *out_len, int *fake_out_len); +void bazo132_nullable(int *__counted_by_or_null(*out_len) *out_buf, int *fake_out_len, int *out_len); +void bazo213_nullable(int *out_len, int *__counted_by_or_null(*out_len) *out_buf, int *fake_out_len); +void bazo312_nullable(int *fake_out_len, int *__counted_by_or_null(*out_len) *out_buf, int *out_len); + +int main() { + struct S s = {0}; + struct S_Nullable s_n = {0}; + // expected-error@+1{{initializing 't.buf' of type 'int *__single __counted_by(len + 1)' (aka 'int *__single') and count value of 1 with null always fails}} + struct T t = {0}; + struct T_Nullable t_n = {0}; + struct U u = {0}; + struct U_Nullable u_n = {0}; + struct V v = {0}; + + int local_len = 10; + int *__single ptr_to_len = &s.len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + int **ptr_to_buf = &s.buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + int **ptr_ptr_to_len = &ptr_to_len; + ptr_to_buf = &s.buf; // expected-error{{pointer with '__counted_by' cannot be pointed to by any other variable; exception is when the variable is passed as a compatible argument to a function}} + *ptr_to_len = &s.len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + *ptr_to_len = &s_n.len; // expected-error{{field referred to by '__counted_by_or_null' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + struct S *struct_ptr = &s; + *ptr_to_len = &struct_ptr->len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + *ptr_to_len = &(*struct_ptr).len; // expected-error{{field referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + + *ptr_to_len = 100; + + foo(&s.len, &s.buf); + foo(&local_len, &s.buf); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + foo(&t.len, &t.buf); // expected-error{{incompatible count expression '*out_len' vs. 'len + 1' in argument to function}} + foo(&u.len, &u.buf); + // expected-error@-1{{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + bar(&s.len, &s.buf); // expected-error{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + foo(&v.len, &v.buf); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + foo(&v.len, &v.buf2); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf' or its address because the type of 'buf', 'int *__single __sized_by(len)' (aka 'int *__single'), refers to 'len'}} + foo(&v.len, &v.buf3); + // expected-error@-1 {{passing 'int *__indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by(*out_len)*__single' (aka 'int *__single*__single')}} + foo(&v.len, &v.buf4); + // expected-error@-1 {{passing 'int *__bidi_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by(*out_len)*__single' (aka 'int *__single*__single')}} + foo(&v.len, &v.buf5); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf' or its address because the type of 'buf', 'int *__single __sized_by(len)' (aka 'int *__single'), refers to 'len'}} + bar(&v.len, &v.buf); + // expected-error@-1 {{passing address of 'len' referred to by '__sized_by' to a parameter that is not referred to by the same attribute}} + bar(&v.len, &v.buf2); + // expected-error@-1 {{passing address of 'len' referred to by '__sized_by' to a parameter that is not referred to by the same attribute}} + foo(&v.len, &v.buf3); + // expected-error@-1 {{passing 'int *__indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by(*out_len)*__single' (aka 'int *__single*__single')}} + foo(&v.len, &v.buf4); + // expected-error@-1 {{passing 'int *__bidi_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by(*out_len)*__single' (aka 'int *__single*__single')}} + foo(&v.len, &v.buf5); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf' or its address because the type of 'buf', 'int *__single __sized_by(len)' (aka 'int *__single'), refers to 'len'}} + + foo(&s_n.len, &s_n.buf); + foo(&local_len, &s_n.buf); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + foo(&t_n.len, &t_n.buf); // expected-error{{incompatible count expression '*out_len' vs. 'len + 1' in argument to function}} + foo(&u_n.len, &u_n.buf); + // expected-error@-1{{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by_or_null(len)' (aka 'int *__single'), refers to 'len'}} + bar(&s_n.len, &s_n.buf); // expected-error{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + + foo_nullable(&s.len, &s.buf); + foo_nullable(&local_len, &s.buf); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + foo_nullable(&t.len, &t.buf); // expected-error{{incompatible count expression '*out_len' vs. 'len + 1' in argument to function}} + foo_nullable(&u.len, &u.buf); + // expected-error@-1{{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + bar_nullable(&s.len, &s.buf); // expected-error{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + foo_nullable(&v.len, &v.buf); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + foo_nullable(&v.len, &v.buf2); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf' or its address because the type of 'buf', 'int *__single __sized_by(len)' (aka 'int *__single'), refers to 'len'}} + foo_nullable(&v.len, &v.buf3); + // expected-error@-1 {{passing 'int *__indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by_or_null(*out_len)*__single' (aka 'int *__single*__single')}} + foo_nullable(&v.len, &v.buf4); + // expected-error@-1 {{passing 'int *__bidi_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by_or_null(*out_len)*__single' (aka 'int *__single*__single')}} + foo_nullable(&v.len, &v.buf5); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf' or its address because the type of 'buf', 'int *__single __sized_by(len)' (aka 'int *__single'), refers to 'len'}} + bar_nullable(&v.len, &v.buf); + // expected-error@-1 {{passing address of 'len' referred to by '__sized_by' to a parameter that is not referred to by the same attribute}} + bar_nullable(&v.len, &v.buf2); + // expected-error@-1 {{passing address of 'len' referred to by '__sized_by' to a parameter that is not referred to by the same attribute}} + foo_nullable(&v.len, &v.buf3); + // expected-error@-1 {{passing 'int *__indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by_or_null(*out_len)*__single' (aka 'int *__single*__single')}} + foo_nullable(&v.len, &v.buf4); + // expected-error@-1 {{passing 'int *__bidi_indexable*__bidi_indexable' to parameter of incompatible nested pointer type 'int *__single __counted_by_or_null(*out_len)*__single' (aka 'int *__single*__single')}} + foo_nullable(&v.len, &v.buf5); + // expected-error@-1 {{passing address of 'len' as an indirect parameter; must also pass 'buf' or its address because the type of 'buf', 'int *__single __sized_by(len)' (aka 'int *__single'), refers to 'len'}} + + foo_nullable(&s_n.len, &s_n.buf); + foo_nullable(&local_len, &s_n.buf); // expected-error{{incompatible dynamic count pointer argument to parameter of type}} + foo_nullable(&t_n.len, &t_n.buf); // expected-error{{incompatible count expression '*out_len' vs. 'len + 1' in argument to function}} + foo_nullable(&u_n.len, &u_n.buf); + // expected-error@-1{{passing address of 'len' as an indirect parameter; must also pass 'buf2' or its address because the type of 'buf2', 'int *__single __counted_by_or_null(len)' (aka 'int *__single'), refers to 'len'}} + bar_nullable(&s_n.len, &s_n.buf); // expected-error{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + + baz123(s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + baz132(s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + baz213(&s.len, s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + baz312(&s.len, s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + + baz123(s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + baz132(s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + baz213(&s_n.len, s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + baz312(&s_n.len, s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + + bazo123(&s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + bazo132(&s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + bazo213(&s.len, &s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + bazo312(&s.len, &s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + + bazo123(&s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + bazo132(&s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + bazo213(&s_n.len, &s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + bazo312(&s_n.len, &s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + + baz123_nullable(s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + baz132_nullable(s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + baz213_nullable(&s.len, s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + baz312_nullable(&s.len, s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + + bazo123_nullable(&s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + bazo132_nullable(&s.buf, &s.len, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + bazo213_nullable(&s.len, &s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + bazo312_nullable(&s.len, &s.buf, &s.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by' to a parameter that is not referred to by the same attribute}} + + baz123_nullable(s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + baz132_nullable(s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + baz213_nullable(&s_n.len, s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + baz312_nullable(&s_n.len, s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + + bazo123_nullable(&s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + bazo132_nullable(&s_n.buf, &s_n.len, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + bazo213_nullable(&s_n.len, &s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + bazo312_nullable(&s_n.len, &s_n.buf, &s_n.len); + // expected-error@-1{{passing address of 'len' referred to by '__counted_by_or_null' to a parameter that is not referred to by the same attribute}} + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/missing-fam-name.c b/clang/test/BoundsSafety/Sema/missing-fam-name.c new file mode 100644 index 0000000000000..b97a565c1cdc6 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/missing-fam-name.c @@ -0,0 +1,10 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct type { + int count; + char [__counted_by(count)]; // expected-error{{expected member name or ';' after declaration specifiers}} +}; diff --git a/clang/test/BoundsSafety/Sema/mock-header.h b/clang/test/BoundsSafety/Sema/mock-header.h new file mode 100644 index 0000000000000..b242addddf3c6 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/mock-header.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef SYSTEM_HEADER +#pragma clang system_header +#endif + +int *returns_pointer(void); diff --git a/clang/test/BoundsSafety/Sema/mock-sdk/array-parameter.h b/clang/test/BoundsSafety/Sema/mock-sdk/array-parameter.h new file mode 100644 index 0000000000000..1680868eda97c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/mock-sdk/array-parameter.h @@ -0,0 +1 @@ +void foo(int array[]); diff --git a/clang/test/BoundsSafety/Sema/mock-sdk/extern-array-mock.h b/clang/test/BoundsSafety/Sema/mock-sdk/extern-array-mock.h new file mode 100644 index 0000000000000..c93491f808138 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/mock-sdk/extern-array-mock.h @@ -0,0 +1,10 @@ +#ifndef __EXTERN_ARRAY_MOCK_STD_H__ +#define __EXTERN_ARRAY_MOCK_STD_H__ +extern unsigned externArray[]; + +inline int f() { + int *ptr = externArray; + return *ptr; +} + +#endif diff --git a/clang/test/BoundsSafety/Sema/multi-level-attr-decls.c b/clang/test/BoundsSafety/Sema/multi-level-attr-decls.c new file mode 100644 index 0000000000000..00e21660b4036 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/multi-level-attr-decls.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -fbounds-safety -ast-dump %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -ast-dump %s 2>&1 | FileCheck %s +#include + +struct Foo { + int *__bidi_indexable *__single ptrBoundPtrThin; + // CHECK: FieldDecl {{.+}} ptrBoundPtrThin 'int *__bidi_indexable*__single' +}; + + +typedef struct Foo Foo; + +Foo *__bidi_indexable *__single Test (Foo *__single *__bidi_indexable argFooPtrThinPtrBound) { + Foo *__single *__bidi_indexable localFooPtrThinPtrBound = argFooPtrThinPtrBound; + Foo *__bidi_indexable *__single Res; + return Res; +} +// CHECK: FunctionDecl {{.+}} Test 'Foo *__bidi_indexable*__single(Foo *__single*__bidi_indexable)' +// CHECK: VarDecl {{.+}} localFooPtrThinPtrBound 'Foo *__single*__bidi_indexable' cinit +// CHECK: VarDecl {{.+}} Res 'Foo *__bidi_indexable*__single' diff --git a/clang/test/BoundsSafety/Sema/multiple-dependees-assign.c b/clang/test/BoundsSafety/Sema/multiple-dependees-assign.c new file mode 100644 index 0000000000000..b0adb23fffc11 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/multiple-dependees-assign.c @@ -0,0 +1,92 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct T { + int cnt1; + int cnt2; + int *__counted_by(2 * cnt1 + 3 * cnt2) ptr; + int *__counted_by(cnt2) ptr2; +}; + +void full_group_assigned(void) { + int arr[10]; + struct T t; + + t.cnt1 = 1; + t.ptr = arr; + t.ptr2 = arr; + t.cnt2 = 2; +} + +void missing_cnt1(void) { + int arr[10]; + struct T t; + + t.ptr = arr; // expected-error{{assignment to 'int *__single __counted_by(2 * cnt1 + 3 * cnt2)' (aka 'int *__single') 't.ptr' requires corresponding assignment to 't.cnt1'; add self assignment 't.cnt1 = t.cnt1' if the value has not changed}} + t.ptr2 = arr; + t.cnt2 = 2; +} + +void missing_cnt2(void) { + int arr[10]; + struct T t; + + t.ptr = arr; // expected-error{{assignment to 'int *__single __counted_by(2 * cnt1 + 3 * cnt2)' (aka 'int *__single') 't.ptr' requires corresponding assignment to 't.cnt2'; add self assignment 't.cnt2 = t.cnt2' if the value has not changed}} + t.cnt1 = 5; + t.ptr2 = arr; +} + +void missing_ptr(void) { + int arr[10]; + struct T t; + + t.cnt1 = 5; // expected-error{{assignment to 't.cnt1' requires corresponding assignment to 'int *__single __counted_by(2 * cnt1 + 3 * cnt2)' (aka 'int *__single') 't.ptr'; add self assignment 't.ptr = t.ptr' if the value has not changed}} + t.cnt2 = 0; + t.ptr2 = arr; +} + + +void missing_ptr2(void) { + int arr[10]; + struct T t; + + t.ptr = &arr[0]; + t.cnt1 = 5; + t.cnt2 = 0; // expected-error{{assignment to 't.cnt2' requires corresponding assignment to 'int *__single __counted_by(cnt2)' (aka 'int *__single') 't.ptr2'; add self assignment 't.ptr2 = t.ptr2' if the value has not changed}} +} + +void only_cnt1(void) { + int arr[10]; + struct T t; + + // expected-error@+1{{assignment to 't.cnt1' requires corresponding assignment to 'int *__single __counted_by(2 * cnt1 + 3 * cnt2)' (aka 'int *__single') 't.ptr'; add self assignment 't.ptr = t.ptr' if the value has not changed}} + t.cnt1 = 5; +} + +void only_ptr(void) { + int arr[10]; + struct T t; + // expected-error@+2{{assignment to 'int *__single __counted_by(2 * cnt1 + 3 * cnt2)' (aka 'int *__single') 't.ptr' requires corresponding assignment to 't.cnt1'; add self assignment 't.cnt1 = t.cnt1' if the value has not changed}} + // expected-error@+1{{assignment to 'int *__single __counted_by(2 * cnt1 + 3 * cnt2)' (aka 'int *__single') 't.ptr' requires corresponding assignment to 't.cnt2'; add self assignment 't.cnt2 = t.cnt2' if the value has not changed}} + t.ptr = arr; +} + +void only_ptr2(void) { + int arr[10]; + struct T t; + // expected-error@+1{{assignment to 'int *__single __counted_by(cnt2)' (aka 'int *__single') 't.ptr2' requires corresponding assignment to 't.cnt2'; add self assignment 't.cnt2 = t.cnt2' if the value has not changed}} + t.ptr2 = arr; +} + +void self_assign_ptr_cnt2(void) { + int arr[10]; + struct T t; + + t.ptr = t.ptr; + t.ptr2 = arr; + t.cnt2 = t.cnt2; + t.cnt1 = 3; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/negative-count.c b/clang/test/BoundsSafety/Sema/negative-count.c new file mode 100644 index 0000000000000..ff25a407d43f7 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/negative-count.c @@ -0,0 +1,126 @@ + + +// RUN: %clang_cc1 -fbounds-safety -triple arm64 -verify %s + +#include +#include + +int *__bidi_indexable to_bidi(int * arg) { + int len = -1; + // expected-error@+1{{possibly initializing 'p' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') and count value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __counted_by_or_null(len) p = arg; + return p; +} + +int *__bidi_indexable to_bidi_literal_count(int * arg) { + // expected-error@+1{{possibly initializing 'p' of type 'int *__single __counted_by_or_null(-1)' (aka 'int *__single') and count value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __counted_by_or_null(-1) p = arg; + return p; +} + +int *__bidi_indexable to_bidi_const_count(int * arg) { + const int len = -1; + // expected-error@+1{{possibly initializing 'p' of type 'int *__single __counted_by_or_null(-1)' (aka 'int *__single') and count value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __counted_by_or_null(len) p = arg; + return p; +} + +void back_and_forth_to_bidi(int * __bidi_indexable arg) { + int len = -1; + int * __counted_by_or_null(len) p = NULL; + arg = p; + p = arg; + len = len; + arg = p; +} + +void foo(int *__counted_by(len) p, int len); + +void to_counted_by_arg(int * arg) { + int len = -1; + int * __counted_by_or_null(len) p = NULL; + foo(p, len); + int len2 = -1; + // expected-error@+1{{possibly initializing 'p2' of type 'int *__single __counted_by_or_null(len2)' (aka 'int *__single') and count value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __counted_by_or_null(len2) p2 = arg; + foo(p2, len2); + // expected-error@+1{{negative count value of -1 for 'p' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + foo(NULL, -1); + foo(NULL, 0); + // expected-error@+1{{negative count value of -1 for 'p' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + foo(arg, -1); + foo(arg, 0); +} + +void bar(int *__counted_by_or_null(len) p, int len); + +void to_counted_by_or_null_arg(int * arg) { + // expected-error@+1{{possibly passing non-null to parameter 'p' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') with count value of -1; explicitly pass null to remove this warning}} + bar(arg, -1); + bar(arg, 0); + bar(NULL, -1); + bar(NULL, 0); +} + +int * __counted_by_or_null(len) nullable_ret(int len); +int * __counted_by(len) nonnullable_ret(int len); + +void ret_values() { + int len = -1; + nullable_ret(-1); + nullable_ret(len); + nonnullable_ret(-1); + nonnullable_ret(len); + len = 0; + nullable_ret(len); + nonnullable_ret(len); +} + +struct offset_fam { + int len; + // expected-note@+1 5{{initialized flexible array member 'buf' is here}} + int buf[__counted_by(len-1)]; +}; + +struct offset_nonnullable { + int len; + int * __counted_by(len-1) buf; +}; + +struct offset_nullable { + int len; + int * __counted_by_or_null(len-1) buf; +}; + +void struct_fields(int * arg) { + struct offset_fam fam1; // rdar://127523062 We should not allow stack allocated flexible array members + // expected-error@+1{{flexible array member is initialized with 0 elements, but count value is initialized to -1}} + struct offset_fam fam2 = {}; + // expected-error@+1{{flexible array member is initialized with 0 elements, but count value is initialized to -1}} + struct offset_fam fam3 = { .buf = {} }; + // expected-error@+1{{flexible array member is initialized with 1 element, but count value is initialized to -1}} + static struct offset_fam fam4 = { .buf = {1} }; + static struct offset_fam fam5 = { .buf = {}, .len = 1}; + static struct offset_fam fam6 = { .len = 1, .buf = {} }; + // expected-error@+1{{flexible array member is initialized with 1 element, but count value is initialized to 0}} + static struct offset_fam fam7 = { .len = 1, .buf = {1} }; + // expected-error@+1{{flexible array member is initialized with 0 elements, but count value is initialized to 1}} + static struct offset_fam fam8 = { .len = 2, .buf = {} }; + static struct offset_fam fam9 = { .len = 2, .buf = {1} }; + + // expected-error@+1{{negative count value of -1 for 'nn1.buf' of type 'int *__single __counted_by(len - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn1; + // expected-error@+1{{negative count value of -1 for 'nn2.buf' of type 'int *__single __counted_by(len - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn2 = {}; + // expected-error@+1{{negative count value of -1 for 'nn3.buf' of type 'int *__single __counted_by(len - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn3 = { .buf = arg }; + struct offset_nonnullable nn4 = { .len = 1, .buf = arg }; + struct offset_nonnullable nn5 = { .len = 2, .buf = arg }; + + struct offset_nullable n1; + struct offset_nullable n2 = {}; + // expected-error@+1{{possibly initializing 'n3.buf' of type 'int *__single __counted_by_or_null(len - 1)' (aka 'int *__single') and count value of -1 with non-null; explicitly initialize null to remove this warning}} + struct offset_nullable n3 = { .buf = arg }; + struct offset_nullable n4 = { .len = 1, .buf = arg }; + struct offset_nullable n5 = { .len = 2, .buf = arg }; +} diff --git a/clang/test/BoundsSafety/Sema/negative-size.c b/clang/test/BoundsSafety/Sema/negative-size.c new file mode 100644 index 0000000000000..6f5eb3fcffaf1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/negative-size.c @@ -0,0 +1,110 @@ + + +// RUN: %clang_cc1 -fbounds-safety -triple arm64 -verify %s + +#include +#include + +int *__bidi_indexable to_bidi(int * arg) { + int size = -1; + // expected-error@+1{{possibly initializing 'p' of type 'int *__single __sized_by_or_null(size)' (aka 'int *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __sized_by_or_null(size) p = arg; + return p; +} + +int *__bidi_indexable to_bidi_literal_size(int * arg) { + // expected-error@+1{{possibly initializing 'p' of type 'int *__single __sized_by_or_null(-1)' (aka 'int *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __sized_by_or_null(-1) p = arg; + return p; +} + +int *__bidi_indexable to_bidi_const_size(int * arg) { + const int size = -1; + // expected-error@+1{{possibly initializing 'p' of type 'int *__single __sized_by_or_null(-1)' (aka 'int *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __sized_by_or_null(size) p = arg; + return p; +} + +void back_and_forth_to_bidi(int * __bidi_indexable arg) { + int size = -1; + int * __sized_by_or_null(size) p = NULL; + arg = p; + p = arg; + size = size; + arg = p; +} + +void foo(int *__sized_by(size) p, int size); + +void to_sized_by_arg(int * arg) { + int size = -1; + int * __sized_by_or_null(size) p = NULL; + foo(p, size); + int size2 = -1; + // expected-error@+1{{possibly initializing 'p2' of type 'int *__single __sized_by_or_null(size2)' (aka 'int *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + int * __sized_by_or_null(size2) p2 = arg; + foo(p2, size2); + // expected-error@+1{{negative size value of -1 for 'p' of type 'int *__single __sized_by(size)' (aka 'int *__single')}} + foo(NULL, -1); + foo(NULL, 0); + // expected-error@+1{{negative size value of -1 for 'p' of type 'int *__single __sized_by(size)' (aka 'int *__single')}} + foo(arg, -1); + foo(arg, 0); +} + +void bar(int *__sized_by_or_null(size) p, int size); + +void to_sized_by_or_null_arg(int * arg) { + // expected-error@+1{{possibly passing non-null to parameter 'p' of type 'int *__single __sized_by_or_null(size)' (aka 'int *__single') with size value of -1; explicitly pass null to remove this warning}} + bar(arg, -1); + bar(arg, 0); + bar(NULL, -1); + bar(NULL, 0); +} + +int * __sized_by_or_null(size) nullable_ret(int size); +int * __sized_by(size) nonnullable_ret(int size); + +void ret_values() { + int size = -1; + nullable_ret(-1); + nullable_ret(size); + nonnullable_ret(-1); + nonnullable_ret(size); + size = 0; + nullable_ret(size); + nonnullable_ret(size); +} + +struct offset_nonnullable { + int size; + int * __sized_by(size-1) buf; +}; + +struct offset_nullable { + int size; + int * __sized_by_or_null(size-1) buf; +}; + +void struct_fields(int * arg) { + // expected-error@+1{{negative size value of -1 for 'nn1.buf' of type 'int *__single __sized_by(size - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn1; + // expected-error@+1{{negative size value of -1 for 'nn2.buf' of type 'int *__single __sized_by(size - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn2 = {}; + // expected-error@+1{{negative size value of -1 for 'nn3.buf' of type 'int *__single __sized_by(size - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn3 = { .buf = arg }; + struct offset_nonnullable nn4 = { .size = 1, .buf = arg }; + struct offset_nonnullable nn5 = { .size = 5, .buf = arg }; + // expected-error@+1{{negative size value of -1 for 'nn6.buf' of type 'int *__single __sized_by(size - 1)' (aka 'int *__single')}} + struct offset_nonnullable nn6 = { .buf = arg, .size = 0 }; + + struct offset_nullable n1; + struct offset_nullable n2 = {}; + // expected-error@+1{{possibly initializing 'n3.buf' of type 'int *__single __sized_by_or_null(size - 1)' (aka 'int *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + struct offset_nullable n3 = { .buf = arg }; + struct offset_nullable n4 = { .size = 1, .buf = arg }; + struct offset_nullable n5 = { .size = 5, .buf = arg }; + // expected-error@+1{{possibly initializing 'n6.buf' of type 'int *__single __sized_by_or_null(size - 1)' (aka 'int *__single') and size value of -1 with non-null; explicitly initialize null to remove this warning}} + struct offset_nullable n6 = { .buf = arg, .size = 0 }; +} + diff --git a/clang/test/BoundsSafety/Sema/nested-anon-union-count.c b/clang/test/BoundsSafety/Sema/nested-anon-union-count.c new file mode 100644 index 0000000000000..a4f9e685755da --- /dev/null +++ b/clang/test/BoundsSafety/Sema/nested-anon-union-count.c @@ -0,0 +1,17 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// Extracted from nested-struct-member-count.c because the error message refers to a line number, making it fragile +struct InnerAnonUnion { + struct B { + union { + int len; + float f; + }; + int dummy; + } hdr; + char fam[__counted_by(hdr.len)]; // expected-error-re{{count parameter refers to union 'hdr.' of type 'union B::(anonymous at {{.*}}/BoundsSafety/Sema/nested-anon-union-count.c:10:9)'}} +}; diff --git a/clang/test/BoundsSafety/Sema/nested-dependent-count-different-instances-under-same-base.c b/clang/test/BoundsSafety/Sema/nested-dependent-count-different-instances-under-same-base.c new file mode 100644 index 0000000000000..e82dc2ede5e80 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/nested-dependent-count-different-instances-under-same-base.c @@ -0,0 +1,32 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +struct nested { + int *__counted_by(l) bp; + int *bp2; + int l; +}; + +struct S { + int l; + struct nested n1; + int *__counted_by(l) ptr; + struct nested n2; +}; + +int main() { + struct S s; + struct S *sp = &s; + + int arr[10]; + sp->n1.bp = arr; + sp->n1.l = 9; + + sp->n2.l = 10; // expected-error{{assignment to 'sp->n2.l' requires corresponding assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 'sp->n2.bp'; add self assignment 'sp->n2.bp = sp->n2.bp' if the value has not changed}} + sp->n1.l = 0; // expected-error{{assignment to 'sp->n1.l' requires corresponding assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 'sp->n1.bp'; add self assignment 'sp->n1.bp = sp->n1.bp' if the value has not changed}} + sp->n2.bp = &arr[1]; // expected-error{{assignment to 'int *__single __counted_by(l)' (aka 'int *__single') 'sp->n2.bp' requires corresponding assignment to 'sp->n2.l'; add self assignment 'sp->n2.l = sp->n2.l' if the value has not changed}} + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/nested-dependent-count.c b/clang/test/BoundsSafety/Sema/nested-dependent-count.c new file mode 100644 index 0000000000000..7e1cf34a9212c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/nested-dependent-count.c @@ -0,0 +1,41 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct S { + // expected-error@+1{{'__counted_by' attribute requires an integer type argument}} + int *__counted_by(len) *__counted_by(len) buf; // expected-error{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *len; +}; + +int fooOut(int *__counted_by(len) *out_buf, int len); // ok +int fooOutOut(int *__counted_by(*len) *out_buf, int *len); // ok +// expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} +int bar(int *__counted_by(len) *__counted_by(len) out_buf, int len); + +int baz() { + int len; + int *__counted_by(len) buf; + + // expected-error@+1{{passing address of 'buf' as an indirect parameter; must also pass 'len' or its address because the type of 'buf', 'int *__single __counted_by(len)' (aka 'int *__single'), refers to 'len'}} + fooOut(&buf, len); + fooOutOut(&buf, &len); + + int *p = &len; // expected-error{{variable referred to by '__counted_by' cannot be pointed to by any other variable; exception is when the pointer is passed as a compatible argument to a function}} + + return 0; +} + +int main() { + int len; + + { + // expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__counted_by(len) *nested_buf; + } + + len = 100; + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/nested-endedby.c b/clang/test/BoundsSafety/Sema/nested-endedby.c new file mode 100644 index 0000000000000..46bdba79c3b12 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/nested-endedby.c @@ -0,0 +1,47 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +struct S { + unsigned len; + // expected-error@+1{{'__ended_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__ended_by(end) *__counted_by(len) buf; + int **end; + // expected-error@+1{{'__ended_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__ended_by(end2) *__ended_by(end3) buf2; + int **end2; + int *end3; +}; + +int fooOut(int *__ended_by(end) *out_start, int *end); // ok +int fooOutOut(int *__ended_by(*out_end) *out_buf, int **out_end); // ok +// FIXME: doesn't sound exactly right +// expected-error@+1{{'__ended_by' attribute on nested pointer type is only allowed on indirect parameters}} +int bar(int *__ended_by(end1) *__ended_by(end2) out_buf, int **end1, int *end2); + +void baz() { + int *end; + int *__ended_by(end) start; + + fooOut(&start, end); + fooOutOut(&start, &end); + + int **p = &end; // expected-error{{variable referred to by '__ended_by' cannot be pointed to by any other variable}} + // expected-error@+1{{pointer with '__ended_by' cannot be pointed to by any other variable}} + p = &start; +} + +void qux() { + int *end; + + { + // expected-error@+1{{'__ended_by' attribute on nested pointer type is only allowed on indirect parameters}} + int *__ended_by(end) *nested_buf; + // expected-error@+2{{local variable buf must be declared right next to its dependent decl}} + // expected-error@+1{{argument of '__ended_by' attribute cannot refer to declaration from a different scope}} + int *__ended_by(end) buf; + } + + end = 0; +} diff --git a/clang/test/BoundsSafety/Sema/nested-struct-member-count.c b/clang/test/BoundsSafety/Sema/nested-struct-member-count.c new file mode 100644 index 0000000000000..dc05830363d10 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/nested-struct-member-count.c @@ -0,0 +1,450 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void side_effect(); + +void ext(int *p); + +// this pointer can never be created in safe -fbounds-safety code since it requires taking the pointer of an array with __counted_by +// expected-error@+1{{pointer to incomplete __counted_by array type 'int[]' not allowed; did you mean to use a nested pointer type?}} +void ext2(int (*ptr)[__counted_by(*len)], int *len) { + *len = 2; // expected-error{{assignment to '*len' requires corresponding assignment to 'int[__counted_by(*len)]' (aka 'int[]') '*ptr'; add self assignment '*ptr = *ptr' if the value has not changed}} +} + +void ext3(char *__counted_by(*len) ptr, int *len); +void ext4(void *p); + +struct SimpleInner { + int dummy; + int len; +}; + +void ext_set_len(struct SimpleInner * p) { + p->len = 2; // len is not referred to by any counted_by attributes yet +} + +struct SimpleOuter { + struct SimpleInner hdr; + char fam[__counted_by(hdr.len)]; // expected-note 17{{referred to by count parameter here}} +}; + +void set_len(struct SimpleInner * p) { + p->len = 2; // struct SimpleInner doesn't contain any FAMs, so not checked + // we cannot take the address of SimpleOuter::hdr though, so this is never referred to by a FAM + + side_effect(); + + p->len++; +} + +void set_len2(struct SimpleOuter * s) { + s->hdr.len = 2; // expected-error{{assignment to 's->hdr.len' requires an immediately preceding assignment to 's' with a wide pointer}} + + side_effect(); + + s->hdr.len++; // expected-error{{assignment to 's->hdr.len' requires an immediately preceding assignment to 's' with a wide pointer}} +} + +struct SimpleInner2 { // avoid sharing with previous test cases + int dummy; + int len; +}; +struct SimpleOuter2 { + struct SimpleInner2 hdr; + char fam[__counted_by(hdr.len)]; +}; +struct UnrelatedFAMOuter { + struct SimpleInner hdr; + int dummy[]; +}; +struct UnrelatedCountedByFAMOuter { + struct SimpleInner hdr; + int outer_len; + char fam[__counted_by(outer_len)]; +}; + +// regression tests for assignment to count in struct with FAM that doesn't refer to the count (but some other struct does) +void set_len3(struct UnrelatedFAMOuter * s) { + s->hdr.len = 1; +} +void set_len4() { + struct UnrelatedFAMOuter s; + s.hdr.len = 1; +} +void set_len5(struct UnrelatedCountedByFAMOuter * s) { + s->hdr.len = 1; +} +void set_len6() { + struct UnrelatedCountedByFAMOuter s; + s.hdr.len = 1; +} + +void address_of_nested_len(struct SimpleInner * p) { + (void)&p->len; + int * p2 = &p->len; // ok, base struct contains no FAM +} + +void address_of_len(struct SimpleOuter * s) { + int * p = &s->hdr.len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + int * p2 = &(s->hdr.len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + int * p3 = &(s->hdr).len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + ext(&s->hdr.len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + ext2(&s->fam, &s->hdr.len); // expected-error{{cannot take address of incomplete __counted_by array}} + // expected-error@-1{{cannot take address of field referred to by __counted_by on a flexible array member}} + // expected-note@-2{{remove '&' to get address as 'char *' instead of 'char (*)[__counted_by(hdr.len)]'}} + + ext2((int (*__single)[__counted_by(*len)]) &s->fam, &s->hdr.len); // expected-error{{cannot take address of incomplete __counted_by array}} + // expected-note@-1{{remove '&' to get address as 'char *' instead of 'char (*)[__counted_by(hdr.len)]'}} + // expected-error@-2{{cannot take address of field referred to by __counted_by on a flexible array member}} + + // type confusion + ext2(s->fam, &s->hdr.len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + ext3(s->fam, &s->hdr.len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + ext4(&s->hdr.len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + (void) &s->hdr.len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + (void) &(s->hdr.len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} +} + +void address_of_inner_struct(struct SimpleOuter * s) { + (void) &s->hdr; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + (void) &(s->hdr); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + ext4((void*)&s->hdr); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + // these are valid casts in plain C, but result in unsafe assignment to s->hdr.len due to type confusion in the -fbounds-safety type system + set_len((struct SimpleInner *)s); + set_len((struct SimpleInner *)&s->hdr.dummy); +} + +void assign_inner_struct(struct SimpleOuter * s, struct SimpleInner * i) { + struct SimpleInner * p = &s->hdr; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + s->hdr = (struct SimpleInner) {0, 10}; // expected-error{{cannot assign 's->hdr' because it contains field 'len' referred to by flexible array member 'fam'}} + + *&s->hdr = *i; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} +} + +struct InnerFAM { + int len; + int inner_fam[__counted_by(len)]; // expected-note{{previous use is here}} +}; + +void ext_set_inner_len(struct InnerFAM * p) { + p = p; + p->len = 2; +} + +struct MultipleFAMs { + // rdar://132712477 hdr.len is not bounds checked with respect to hdr.inner_fam, this should be an error + struct InnerFAM hdr; // expected-warning{{field 'hdr' with variable sized type 'struct InnerFAM' not at the end of a struct or class is a GNU extension}} + int offset; + char fam[__counted_by(hdr.len - offset)]; // expected-error{{field 'len' referred to by flexible array member cannot be used in other dynamic bounds attributes}} +}; + +void set_inner_len(struct InnerFAM * p) { + p = p; + p->len = 2; +} + +void set_len_via_fam(struct MultipleFAMs * s) { + s->hdr.inner_fam[0] = 2; // unsafe write to s->offset + s->hdr.inner_fam[1] = 2; +} + +struct InnerInner { + int dummy; + int innermost_len; +}; +struct MediumInner { + struct InnerInner next; + int dummy; +}; +struct DeepNestingOuter { + struct MediumInner hdr; + int outer_len; + char fam[__counted_by(outer_len + hdr.next.innermost_len)]; // expected-note 10{{referred to by count parameter here}} +}; + +void addresses_with_deep_nesting(struct DeepNestingOuter * s) { + int * p = &s->outer_len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + struct MediumInner * p2 = &s->hdr; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + struct InnerInner * p3 = &s->hdr.next; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + int * p4 = &s->hdr.next.innermost_len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + + ext4(&s->outer_len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + ext4(&s->hdr); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + ext4(&s->hdr.next); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + ext4(&s->hdr.next.innermost_len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} +} + +void assign_with_deep_nesting(struct DeepNestingOuter * s, int a, struct MediumInner b, struct InnerInner c) { + s->outer_len = a; // expected-error{{assignment to 's->outer_len' requires an immediately preceding assignment to 's' with a wide pointer}} + s->hdr = b; // expected-error{{cannot assign 's->hdr' because it contains field 'next' referred to by flexible array member 'fam'}} + s->hdr.next = c; // expected-error{{cannot assign 's->hdr.next' because it contains field 'innermost_len' referred to by flexible array member 'fam'}} + s->hdr.next.innermost_len = a; + + side_effect(); + + s = s; + s->outer_len = a; // expected-error{{assignment to 'int' 's->outer_len' requires corresponding assignment to 's->hdr.next.innermost_len'; add self assignment 's->hdr.next.innermost_len = s->hdr.next.innermost_len' if the value has not changed}} + + side_effect(); + + s = s; + s->outer_len = a; + s->hdr.next.innermost_len = a; +} + +struct InnerDup { + int dummy; + int len; +}; +struct OuterDup { + struct InnerDup a; + struct InnerDup b; + char fam[__counted_by(a.len)]; // expected-error{{cannot depend on nested field 'len' because it exists in multiple instances in struct 'OuterDup'}} +}; + +void non_aliasing_assignment_to_same_decl(struct OuterDup * s) { + s->b.len = 2; // should not affect fam + + side_effect(); + + s = s; + s->a.len = 2; // should not require assignment to s->b.len +} + +struct OuterDup2 { + struct InnerDup a; + struct InnerDup b; + char fam[__counted_by(a.len + b.len)]; // expected-error{{cannot depend on nested field 'len' because it exists in multiple instances in struct 'OuterDup2'}} +}; + +void non_aliasing_ref_to_same_decl(struct OuterDup2 * s) { + s = s; + s->a.len = 2; // should require assignment to s->b.len if OuterDup2::fam is error free + + side_effect(); + + s = s; + s->b.len = 2; // should require assignment to s->a.len if OuterDup2::fam is error free + + side_effect(); + + s = s; + s->a.len = 2; + s->b.len = 2; +} + +struct CommonHeader { + int dummy; + int len; +}; +struct SubType1 { + struct CommonHeader hdr; + char fam1[__counted_by(hdr.len)]; // expected-note{{referred to by count parameter here}} +}; +struct SubType2 { + struct CommonHeader hdr; + int offset; + char fam2[__counted_by(hdr.len - offset)]; // expected-note{{referred to by count parameter here}} +}; + +void shared_header_type(struct SubType1 * s1, struct SubType2 * s2) { + s1 = s1; + s1->hdr.len = 2; + + side_effect(); + + s1->hdr.len = 2; // expected-error{{assignment to 's1->hdr.len' requires an immediately preceding assignment to 's1' with a wide pointer}} + + side_effect(); + + s2 = s2; + s2->hdr.len = 2; + s2->offset = 1; + + side_effect(); + + s2 = s2; + s2->hdr.len = 2; // expected-error{{assignment to 'int' 's2->hdr.len' requires corresponding assignment to 's2->offset'; add self assignment 's2->offset = s2->offset' if the value has not changed}} + + side_effect(); + + s2 = s2; + s2->offset = 1; // expected-error{{assignment to 'int' 's2->offset' requires corresponding assignment to 's2->hdr.len'; add self assignment 's2->hdr.len = s2->hdr.len' if the value has not changed}} + + side_effect(); + + s1->hdr = s2->hdr; // expected-error{{cannot assign 's1->hdr' because it contains field 'len' referred to by flexible array member 'fam1'}} + + side_effect(); + + s2->hdr = s1->hdr; // expected-error{{cannot assign 's2->hdr' because it contains field 'len' referred to by flexible array member 'fam2'}} +} + +struct CommonOuterHeader { + struct CommonHeader next; + int dummy; +}; +struct DeepSubType1 { + struct CommonOuterHeader hdr; + char fam1[__counted_by(hdr.next.len)]; // expected-note 2{{referred to by count parameter here}} +}; +struct DeepSubType2 { + struct CommonOuterHeader hdr; + int offset; + char fam2[__counted_by(hdr.next.len - offset)]; // expected-note 2{{referred to by count parameter here}} +}; + +void shared_nested_header_type(struct DeepSubType1 * s1, struct DeepSubType2 * s2) { + s1 = s1; + s1->hdr.next.len = 2; + + side_effect(); + + s1->hdr.next.len = 2; // expected-error{{assignment to 's1->hdr.next.len' requires an immediately preceding assignment to 's1' with a wide pointer}} + + side_effect(); + + s2 = s2; + s2->hdr.next.len = 2; + s2->offset = 1; + + side_effect(); + + s2 = s2; + s2->hdr.next.len = 2; // expected-error{{assignment to 'int' 's2->hdr.next.len' requires corresponding assignment to 's2->offset'; add self assignment 's2->offset = s2->offset' if the value has not changed}} + + side_effect(); + + s2 = s2; + s2->offset = 1; // expected-error{{assignment to 'int' 's2->offset' requires corresponding assignment to 's2->hdr.next.len'; add self assignment 's2->hdr.next.len = s2->hdr.next.len' if the value has not changed}} + + side_effect(); + + s1->hdr = s2->hdr; // expected-error{{cannot assign 's1->hdr' because it contains field 'next' referred to by flexible array member 'fam1'}} + + side_effect(); + + s2->hdr = s1->hdr; // expected-error{{cannot assign 's2->hdr' because it contains field 'next' referred to by flexible array member 'fam2'}} + + side_effect(); + + s1->hdr.next = s2->hdr.next; // expected-error{{cannot assign 's1->hdr.next' because it contains field 'len' referred to by flexible array member 'fam1'}} + + side_effect(); + + s2->hdr.next = s1->hdr.next; // expected-error{{cannot assign 's2->hdr.next' because it contains field 'len' referred to by flexible array member 'fam2'}} +}; + +struct PointerHeader { + int dummy; + int len; +}; +struct CountPointer { + struct PointerHeader hdr; + char * __counted_by(hdr.len) p; // expected-error{{invalid argument expression to bounds attribute}} + // expected-note@-1{{nested struct member in count parameter only supported for flexible array members}} +}; +struct CountPointer2 { + struct PointerHeader hdr; + char * __counted_by(hdr.len) p2; // expected-error{{invalid argument expression to bounds attribute}} + // expected-note@-1{{nested struct member in count parameter only supported for flexible array members}} +}; + +void nested_count_pointer_count(struct CountPointer * s) { + s->hdr.len = 2; +} + +struct InnerPointer { + int len; + char * __sized_by(len) p; +}; +struct OuterWithInnerPointer { + struct InnerPointer hdr; + char fam[__counted_by(hdr.len)]; +}; + +void fam_with_inner_pointer(struct OuterWithInnerPointer * s) { + s = s; + s->hdr.len = 2; // expected-error{{assignment to 's->hdr.len' requires corresponding assignment to 'char *__single __sized_by(len)' (aka 'char *__single') 's->hdr.p'; add self assignment 's->hdr.p = s->hdr.p' if the value has not changed}} +} + +struct PointeeHeader { + int dummy; + int len; +}; +struct FAMArrow { + struct PointeeHeader *hdr; + char fam[__counted_by(hdr->len)]; // expected-error{{arrow notation not allowed for struct member in count parameter}} +}; +struct FAMDerefStruct { + struct PointeeHeader *hdr; + char fam[__counted_by((*hdr).len)]; // expected-error{{dereference operator in '__counted_by' is only allowed for function parameters}} +}; + +typedef union { + int i; + float f; +} U; +struct UnionCount { + U len; + char fam[__counted_by(len.i)]; // expected-error{{count parameter refers to union 'len' of type 'U'}} + // expected-note@-1 2{{referred to by count parameter here}} +}; +void union_assign(struct UnionCount * s) { + s->len.i = 1; +} +void type_pun_assign(struct UnionCount * s) { + s->len.f = 1.0; +} +void ext_u(U * u) { + u->i = 1; +} +void union_address_param(struct UnionCount * s) { + ext_u(&s->len); // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} +} +void union_address_assign(struct UnionCount * s) { + U * u = &s->len; // expected-error{{cannot take address of field referred to by __counted_by on a flexible array member}} + u->i = 1; +} +void ext_f(float * f) { + *f = 1.0; +} +void union_member_address_param(struct UnionCount * s) { + ext_f(&s->len.f); +} + +struct AnonStruct { + struct { + int len; + int dummy; + }; + char fam[__counted_by(len)]; // expected-error{{count expression on struct field may only reference other fields of the same struct}} rdar://125394428 +}; +struct AnonUnion { + union { + int len; + float f; + }; + char fam[__counted_by(len)]; // expected-error{{count expression on struct field may only reference other fields of the same struct}} +}; +struct InnerAnonStruct { + struct A { + struct { + int len; + int dummy2; + }; + int dummy; + } hdr; + char fam[__counted_by(hdr.len)]; +}; diff --git a/clang/test/BoundsSafety/Sema/non-adjacent-dependent-var-decl-with-dynamic-count.c b/clang/test/BoundsSafety/Sema/non-adjacent-dependent-var-decl-with-dynamic-count.c new file mode 100644 index 0000000000000..8fdd348b0c373 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-adjacent-dependent-var-decl-with-dynamic-count.c @@ -0,0 +1,11 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +void side_effect(); + +void Test() { + int len; // // expected-error{{local variable len must be declared right next to its dependent decl}} + side_effect(); + int *__counted_by(len) ptr; // expected-error{{local variable ptr must be declared right next to its dependent decl}} +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/attrs_and_qualifiers.c b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/attrs_and_qualifiers.c new file mode 100644 index 0000000000000..eee836dd90883 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/attrs_and_qualifiers.c @@ -0,0 +1,79 @@ + + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -fsyntax-only -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -ast-dump %s | FileCheck %s +#include + +const int * auto_global_var1; +// CHECK: auto_global_var1 'const int *__single' + +const int * _Nullable auto_global_var2; +// CHECK: auto_global_var2 'const int *__single _Nullable':'const int *__single' + +const int * __ptrauth(2, 0, 0) auto_global_var3; +// CHECK: auto_global_var3 'const int *__single__ptrauth(2,0,0)' + +const int * __single global_var1; +// CHECK: global_var1 'const int *__single' + +const int * _Nullable __single global_var2; +// CHECK: global_var2 'const int *__single _Nullable':'const int *__single' + +const int * __ptrauth(2, 0, 0) __single global_var3; +// CHECK: global_var3 'const int *__single__ptrauth(2,0,0)' + + +const int * _Nullable __ptrauth(2, 0, 0) __single c1_global_var1; +// CHECK: c1_global_var1 'const int *__single _Nullable __ptrauth(2,0,0)':'const int *__single__ptrauth(2,0,0)' + +const int * _Nullable __single __ptrauth(2, 0, 0) c1_global_var2; +// CHECK: c1_global_var2 'const int *__single _Nullable __ptrauth(2,0,0)':'const int *__single__ptrauth(2,0,0)' + +const int * __ptrauth(2, 0, 0) _Nullable __single c1_global_var3; +// CHECK: c1_global_var3 'const int *__single__ptrauth(2,0,0) _Nullable':'const int *__single__ptrauth(2,0,0)' + +const int * __ptrauth(2, 0, 0) __single _Nullable c1_global_var4; +// CHECK: c1_global_var4 'const int *__single__ptrauth(2,0,0) _Nullable':'const int *__single__ptrauth(2,0,0)' + +const int * __single __ptrauth(2, 0, 0) _Nullable c1_global_var5; +// CHECK: c1_global_var5 'const int *__single__ptrauth(2,0,0) _Nullable':'const int *__single__ptrauth(2,0,0)' + +const int * __single _Nullable __ptrauth(2, 0, 0) c1_global_var6; +// CHECK: c1_global_var6 'const int *__single _Nullable __ptrauth(2,0,0)':'const int *__single__ptrauth(2,0,0)' + + +int * const _Nullable __ptrauth(2, 0, 0) __single c2_global_var1; +// CHECK: c2_global_var1 'int *__singleconst _Nullable __ptrauth(2,0,0)':'int *__singleconst __ptrauth(2,0,0)' + +int * const _Nullable __single __ptrauth(2, 0, 0) c2_global_var2; +// CHECK: c2_global_var2 'int *__singleconst _Nullable __ptrauth(2,0,0)':'int *__singleconst __ptrauth(2,0,0)' + +int * const __ptrauth(2, 0, 0) _Nullable __single c2_global_var3; +// CHECK: c2_global_var3 'int *__singleconst __ptrauth(2,0,0) _Nullable':'int *__singleconst __ptrauth(2,0,0)' + +int * const __ptrauth(2, 0, 0) __single _Nullable c2_global_var4; +// CHECK: c2_global_var4 'int *__singleconst __ptrauth(2,0,0) _Nullable':'int *__singleconst __ptrauth(2,0,0)' + +int * const __single __ptrauth(2, 0, 0) _Nullable c2_global_var5; +// CHECK: c2_global_var5 'int *__singleconst __ptrauth(2,0,0) _Nullable':'int *__singleconst __ptrauth(2,0,0)' + +int * const __single _Nullable __ptrauth(2, 0, 0) c2_global_var6; +// CHECK: c2_global_var6 'int *__singleconst _Nullable __ptrauth(2,0,0)':'int *__singleconst __ptrauth(2,0,0)' + + +const int * const _Nullable __ptrauth(2, 0, 0) __single c12_global_var1; +// CHECK: c12_global_var1 'const int *__singleconst _Nullable __ptrauth(2,0,0)':'const int *__singleconst __ptrauth(2,0,0)' +const int * const _Nullable __single __ptrauth(2, 0, 0) c12_global_var2; +// CHECK: c12_global_var2 'const int *__singleconst _Nullable __ptrauth(2,0,0)':'const int *__singleconst __ptrauth(2,0,0)' + +const int * const __ptrauth(2, 0, 0) _Nullable __single c12_global_var3; +// CHECK: c12_global_var3 'const int *__singleconst __ptrauth(2,0,0) _Nullable':'const int *__singleconst __ptrauth(2,0,0)' + +const int * const __ptrauth(2, 0, 0) __single _Nullable c12_global_var4; +// CHECK: c12_global_var4 'const int *__singleconst __ptrauth(2,0,0) _Nullable':'const int *__singleconst __ptrauth(2,0,0)' + +const int * const __single __ptrauth(2, 0, 0) _Nullable c12_global_var5; +// CHECK: c12_global_var5 'const int *__singleconst __ptrauth(2,0,0) _Nullable':'const int *__singleconst __ptrauth(2,0,0)' + +const int * const __single _Nullable __ptrauth(2, 0, 0) c12_global_var6; +// CHECK: c12_global_var6 'const int *__singleconst _Nullable __ptrauth(2,0,0)':'const int *__singleconst __ptrauth(2,0,0)' diff --git a/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/nullability_and_bounds-safety.c b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/nullability_and_bounds-safety.c new file mode 100644 index 0000000000000..f9481130e2fb1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/nullability_and_bounds-safety.c @@ -0,0 +1,49 @@ + + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -fsyntax-only -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -ast-dump %s | FileCheck %s +#include + +int * _Nullable __single global_var1; +// CHECK: global_var1 'int *__single _Nullable':'int *__single' + +int * __single _Nullable global_var2; +// CHECK: global_var2 'int *__single _Nullable':'int *__single' + +void f(void) { + int * _Nullable __bidi_indexable local_var1; + // CHECK: local_var1 'int *__bidi_indexable _Nullable':'int *__bidi_indexable' + + int * __bidi_indexable _Nullable local_var2; + // CHECK: local_var2 'int *__bidi_indexable _Nullable':'int *__bidi_indexable' + + int (* _Nullable __bidi_indexable local_var3)[4]; + + int (* __bidi_indexable _Nullable local_var4)[4]; + + int (* _Nullable __indexable local_var5)[4]; + + int (* __indexable _Nullable local_var6)[4]; +} + +struct S { + int * __single _Nullable member_var1; + // CHECK: member_var1 'int *__single _Nullable':'int *__single' + + int * _Nullable __single member_var2; + // CHECK: member_var2 'int *__single _Nullable':'int *__single' +}; + +union U { + int * __single _Nullable union_var1; + // CHECK: union_var1 'int *__single _Nullable':'int *__single' + + int * _Nullable __single union_var2; + // CHECK: union_var2 'int *__single _Nullable':'int *__single' +}; + +void foo1(int * _Nonnull __single __counted_by(n) ptr1, unsigned n); +// CHECK: ptr1 'int *__single __counted_by(n) _Nonnull':'int *__single' + +void foo2(int * __single __counted_by(n) _Nonnull ptr2, unsigned n); +// CHECK: ptr2 'int *__single __counted_by(n) _Nonnull':'int *__single' diff --git a/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth.c b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth.c new file mode 100644 index 0000000000000..7f9e6ade8fe45 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth.c @@ -0,0 +1,42 @@ + + +#include + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -fsyntax-only -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -ast-dump %s | FileCheck %s + +int * __ptrauth(2, 0, 0) global_var; +// CHECK: global_var 'int *__single__ptrauth(2,0,0)' + +int * __ptrauth(2, 0, 0) global_array[5]; +// CHECK: global_array 'int *__single__ptrauth(2,0,0)[5]' + +void foo(void) { + int * __ptrauth(2, 0, 0) local_var; +} +// CHECK: local_var 'int *__bidi_indexable__ptrauth(2,0,0)' + +void foo2(void) { + int * __ptrauth(2, 0, 0) local_array[5]; +} +// CHECK: local_array 'int *__single__ptrauth(2,0,0)[5]' + +struct Foo { + int * __ptrauth(2, 0, 0) member_var; +}; +// CHECK: member_var 'int *__single__ptrauth(2,0,0)' + +struct Foo2 { + int * __ptrauth(2, 0, 0) member_array[5]; +}; +// CHECK: member_array 'int *__single__ptrauth(2,0,0)[5]' + +union U { + int * __ptrauth(2, 0, 0) union_var; +}; +// CHECK: union_var 'int *__single__ptrauth(2,0,0)' + +union U2 { + int * __ptrauth(2, 0, 0) union_array[5]; +}; +// CHECK: union_array 'int *__single__ptrauth(2,0,0)[5]' diff --git a/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_and_bounds-safety.c b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_and_bounds-safety.c new file mode 100644 index 0000000000000..7d5bdf88472c0 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_and_bounds-safety.c @@ -0,0 +1,101 @@ + + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -fsyntax-only -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -ast-dump %s | FileCheck %s + + +#include + +int * __ptrauth(2, 0, 0) __single global_var1; +// CHECK: global_var1 'int *__single__ptrauth(2,0,0)' + +int * __single __ptrauth(2, 0, 0) global_var2; +// CHECK: global_var2 'int *__single__ptrauth(2,0,0)' + +void foo(void) { + int n; + int *__ptrauth(2, 0, 0) __counted_by(n) local_buf1; + // CHECK: local_buf1 'int *__single __counted_by(n)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n2; + int *__counted_by(n2) __ptrauth(2, 0, 0) local_buf2; + // CHECK: local_buf2 'int *__single __counted_by(n2)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n3; + int *__ptrauth(2, 0, 0) local_buf3 __counted_by(n3); + // CHECK: local_buf3 'int *__single __counted_by(n3)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n4; + unsigned char *__ptrauth(2, 0, 0) __counted_by(n4) local_byte_buf1; + // CHECK: local_byte_buf1 'unsigned char *__single __counted_by(n4)__ptrauth(2,0,0)':'unsigned char *__single__ptrauth(2,0,0)' + + int n5; + unsigned char *__counted_by(n5) __ptrauth(2, 0, 0) local_byte_buf2; + // CHECK: local_byte_buf2 'unsigned char *__single __counted_by(n5)__ptrauth(2,0,0)':'unsigned char *__single__ptrauth(2,0,0)' + + int n6; + unsigned char *__ptrauth(2, 0, 0) local_byte_buf3 __counted_by(n6); + // CHECK: local_byte_buf3 'unsigned char *__single __counted_by(n6)__ptrauth(2,0,0)':'unsigned char *__single__ptrauth(2,0,0)' +} + +struct Foo { + int * __ptrauth(2, 0, 0) __single member_var1; + // CHECK: member_var1 'int *__single__ptrauth(2,0,0)' + + int * __single __ptrauth(2, 0, 0) member_var2; + // CHECK: member_var2 'int *__single__ptrauth(2,0,0)' + + int n; + int *__ptrauth(2, 0, 0) __counted_by(n) member_buf1; + // CHECK: member_buf1 'int *__single __counted_by(n)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int *__counted_by(n) __ptrauth(2, 0, 0) member_buf2; + // CHECK: member_buf2 'int *__single __counted_by(n)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int *__ptrauth(2, 0, 0) member_buf3 __counted_by(n); + // CHECK: member_buf3 'int *__single __counted_by(n)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + unsigned char *__ptrauth(2, 0, 0) __counted_by(n) member_byte_buf1; + // CHECK: member_byte_buf1 'unsigned char *__single __counted_by(n)__ptrauth(2,0,0)':'unsigned char *__single__ptrauth(2,0,0)' + + unsigned char *__counted_by(n) __ptrauth(2, 0, 0) member_byte_buf2; + // CHECK: member_byte_buf2 'unsigned char *__single __counted_by(n)__ptrauth(2,0,0)':'unsigned char *__single__ptrauth(2,0,0)' + + unsigned char *__ptrauth(2, 0, 0) member_byte_buf3 __counted_by(n); + // CHECK: member_byte_buf3 'unsigned char *__single __counted_by(n)__ptrauth(2,0,0)':'unsigned char *__single__ptrauth(2,0,0)' +}; + +union U { + int * __ptrauth(2, 0, 0) __single union_var1; + // CHECK: union_var1 'int *__single__ptrauth(2,0,0)' + + int * __single __ptrauth(2, 0, 0) union_var2; + // CHECK: union_var2 'int *__single__ptrauth(2,0,0)' +}; + +void bar(void) { + int n; + int *__ptrauth(2, 0, 0) __counted_by(n) local_buf1; + // CHECK: local_buf1 'int *__single __counted_by(n)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + int n2; + int *__counted_by(n2) __ptrauth(2, 0, 0) local_buf2; + // CHECK: local_buf2 'int *__single __counted_by(n2)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n3; + int *__ptrauth(2, 0, 0) local_buf3 __counted_by(n3); + // CHECK: local_buf3 'int *__single __counted_by(n3)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' +} + +struct Bar { + int n; + int *__ptrauth(2, 0, 0) __counted_by(n) member_buf1; + // CHECK: member_buf1 'int *__single __counted_by(n)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n2; + int *__counted_by(n2) __ptrauth(2, 0, 0) member_buf2; + // CHECK: member_buf2 'int *__single __counted_by(n2)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n3; + int *__ptrauth(2, 0, 0) member_buf3 __counted_by(n3); + // CHECK: member_buf3 'int *__single __counted_by(n3)__ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' +}; diff --git a/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_nullability.c b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_nullability.c new file mode 100644 index 0000000000000..102fc0953bea3 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_nullability.c @@ -0,0 +1,12 @@ + + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -fsyntax-only -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -ast-dump %s | FileCheck %s + +#include + +int * _Nullable __ptrauth(2, 0, 0) global_var1; +// CHECK: global_var1 'int *__single _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + +int * __ptrauth(2, 0, 0) _Nullable global_var2; +// CHECK: global_var2 'int *__single__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' diff --git a/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_nullability_bounds-safety.c b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_nullability_bounds-safety.c new file mode 100644 index 0000000000000..0bd4fd180fb3d --- /dev/null +++ b/clang/test/BoundsSafety/Sema/non-bounds-safety-attrs/ptrauth_nullability_bounds-safety.c @@ -0,0 +1,57 @@ + + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -fsyntax-only -ast-dump %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -fsyntax-only -ast-dump %s | FileCheck %s +#include + +int * _Nullable __ptrauth(2, 0, 0) __single global_var1; +// CHECK: global_var1 'int *__single _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + +int * _Nullable __single __ptrauth(2, 0, 0) global_var2; +// CHECK: global_var2 'int *__single _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + +int * __ptrauth(2, 0, 0) _Nullable __single global_var3; +// CHECK: global_var3 'int *__single__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' + +int * __ptrauth(2, 0, 0) __single _Nullable global_var4; +// CHECK: global_var4 'int *__single__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' + +int * __single __ptrauth(2, 0, 0) _Nullable global_var5; +// CHECK: global_var5 'int *__single__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' + +int * __single _Nullable __ptrauth(2, 0, 0) global_var6; +// CHECK: global_var6 'int *__single _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + +void foo() { + int n1 = 0; + int *_Nullable __ptrauth(2, 0, 0) __counted_by(n1) local_buf11; + // CHECK: local_buf11 'int *__single __counted_by(n1) _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n2 = 0; + int *__ptrauth(2, 0, 0) _Nullable __counted_by(n2) local_buf12; + // CHECK: local_buf12 'int *__single __counted_by(n2)__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' + + int n3 = 0; + int *__ptrauth(2, 0, 0) __counted_by(n3) _Nullable local_buf13; + // CHECK: local_buf13 'int *__single __counted_by(n3)__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' + + int n4 = 0; + int *_Nullable __counted_by(n4) __ptrauth(2, 0, 0) local_buf21; + // CHECK: local_buf21 'int *__single __counted_by(n4) _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n5; + int *__counted_by(n5) _Nullable __ptrauth(2, 0, 0) local_buf22; + // CHECK: local_buf22 'int *__single __counted_by(n5) _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n6; + int *__counted_by(n6) __ptrauth(2, 0, 0) _Nullable local_buf23; + // CHECK: local_buf23 'int *__single __counted_by(n6)__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' + + int n7; + int *_Nullable __ptrauth(2, 0, 0) local_buf31 __counted_by(n7); + // CHECK: local_buf31 'int *__single __counted_by(n7) _Nullable __ptrauth(2,0,0)':'int *__single__ptrauth(2,0,0)' + + int n8; + int *__ptrauth(2, 0, 0) _Nullable local_buf32 __counted_by(n8); + // CHECK: local_buf32 'int *__single __counted_by(n8)__ptrauth(2,0,0) _Nullable':'int *__single__ptrauth(2,0,0)' +} diff --git a/clang/test/BoundsSafety/Sema/null-terminated-predefined-string.c b/clang/test/BoundsSafety/Sema/null-terminated-predefined-string.c new file mode 100644 index 0000000000000..d1c38e933e744 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/null-terminated-predefined-string.c @@ -0,0 +1,68 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void fnull_terminated(const char *); +void fnull_terminated_explicit(const char *__null_terminated); +// expected-note@+3{{passing argument to parameter here}} +// expected-note@+2{{passing argument to parameter here}} +// expected-note@+1{{passing argument to parameter here}} +void fnull_terminated_iptr(const int *__null_terminated); +// expected-note@+3{{passing argument to parameter here}} +// expected-note@+2{{passing argument to parameter here}} +// expected-note@+1{{passing argument to parameter here}} +void fterminated_by_minus_one(const int *__terminated_by(-1)); + +void test(void) { + fnull_terminated(__func__); + fnull_terminated(__FUNCTION__); + fnull_terminated(__PRETTY_FUNCTION__); + + fnull_terminated_explicit(__func__); + fnull_terminated_explicit(__FUNCTION__); + fnull_terminated_explicit(__PRETTY_FUNCTION__); + + // expected-error@+3{{passing 'const char[5]' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + fnull_terminated_iptr(__func__); + // expected-error@+3{{passing 'const char[5]' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + fnull_terminated_iptr(__FUNCTION__); + // expected-error@+3{{passing 'const char[16]' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + fnull_terminated_iptr(__PRETTY_FUNCTION__); + + // expected-error@+1{{passing 'const char[5]' to parameter of incompatible type 'const int *__single __terminated_by(-1)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + fterminated_by_minus_one(__func__); + // expected-error@+1{{passing 'const char[5]' to parameter of incompatible type 'const int *__single __terminated_by(-1)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + fterminated_by_minus_one(__FUNCTION__); + // expected-error@+1{{passing 'const char[16]' to parameter of incompatible type 'const int *__single __terminated_by(-1)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + fterminated_by_minus_one(__PRETTY_FUNCTION__); +} + +void test_assign(const char *fname) { + fname = __func__; + fname = __FUNCTION__; + fname = __PRETTY_FUNCTION__; + + const char *__null_terminated lfname1 = __func__; + const char *__null_terminated lfname2 = __FUNCTION__; + const char *__null_terminated lfname3 = __PRETTY_FUNCTION__; + + const char *lfname_non_null_terminated1 = __func__; + const char *lfname_non_null_terminated2 = __FUNCTION__; + const char *lfname_non_null_terminated3 = __PRETTY_FUNCTION__; + + // expected-error@+1{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + const char *__terminated_by(-1) lfname_term_minus_one1 = __func__; + // expected-error@+1{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + const char *__terminated_by(-1) lfname_term_minus_one2 = __FUNCTION__; + // expected-error@+1{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + const char *__terminated_by(-1) lfname_term_minus_one3 = __PRETTY_FUNCTION__; + +} diff --git a/clang/test/BoundsSafety/Sema/passing-nulls-for-out-parameters.c b/clang/test/BoundsSafety/Sema/passing-nulls-for-out-parameters.c new file mode 100644 index 0000000000000..1d60303fa75bb --- /dev/null +++ b/clang/test/BoundsSafety/Sema/passing-nulls-for-out-parameters.c @@ -0,0 +1,160 @@ + +// XFAIL: * +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// TODO: ended_by +#include + +void out_ptr_out_count(int *__counted_by(*out_count) *out_ptr, + unsigned *out_count); + +void calls_out_ptr_out_count(void) { + // with null out buffer and null out count. + out_ptr_out_count(0, 0); + + // with non-null out buffer and null out count. + // no error?? + int *__counted_by(4) p1; + out_ptr_out_count(&p1, 0); + + // with null out buffer and non-null out count. + // reading `*out_count` is fine in this case, but updating `*out_count` + // will require updating `*out_ptr` so allowing this is not useful. + unsigned count1; + int *__counted_by(count1) p2; + out_ptr_out_count(0, &count1); + + // with null out buffer and unrelated out count. + unsigned count2; + out_ptr_out_count(0, &count2); + + // with incompatible out buffer and null out count. + // `*out_ptr` will generate promotion to bidi_indexable using `*out_count`, + // which will lead to null pointer dereference. + int *__single p3; + out_ptr_out_count(&p3, 0); + + unsigned count4; + int *__counted_by(count4) p4; + out_ptr_out_count(&p4, 0); +} + +void out_ptr_in_ptr_out_count(int *__counted_by(*out_count) *out_ptr, + int *__counted_by(*out_count) ptr, + unsigned *out_count); + +void calls_out_ptr_in_ptr_out_count(void) { + // not allowed due to `ptr`. + out_ptr_in_ptr_out_count(0, 0, 0); + int *p1; + out_ptr_in_ptr_out_count(0, p1, 0); +} + +void out_ptr2_out_count(int *__counted_by(*out_count) *out_ptr1, + int *__counted_by(*out_count) *out_ptr2, + unsigned *out_count); + +void calls_out_ptr2_out_count(void) { + // should be fine + out_ptr2_out_count(0, 0, 0); + + int *__counted_by(4) p1; + out_ptr2_out_count(&p1, 0, 0); + + int *__counted_by(0) p2; + out_ptr2_out_count(0, &p2, 0); + + // doesn't fit in the current model because updating `*out_ptr1` or + // `*out_count` would require self assignment `*out_ptr2 = *out_ptr2`, + // which involves null pointer dereference. + unsigned count3; + int *__counted_by(count3) p3; + out_ptr2_out_count(&p3, 0, &count3); + + unsigned count4; + int *__counted_by(count4) p4; + out_ptr2_out_count(0, &p4, 0); +} + +// ptr promotion to bidi_indexable will access `*out_count`, whether +// `ptr` is null or not. Thus, `out_count` shouldn't be null. +void in_ptr_out_count(int *__counted_by(*out_count) ptr, unsigned *out_count); + +void calls_in_ptr_out_count(void) { + // no error?? + // with null buffer and null out count. + in_ptr_out_count(0, 0); + + // with non-null buffer and null out count. + // no error?? + int *p1; + in_ptr_out_count(p1, 0); + + // with null buffer and non-null out count. + unsigned count1; + int *__counted_by(count1) p2; + in_ptr_out_count(0, &count1); + + // with null buffer and unrelated out count. + // no error?? + unsigned count2; + in_ptr_out_count(0, &count2); +} + +// -fbounds-safety doesn't support inout buffer. +void out_ptr_in_count(int *__counted_by(count) *out_ptr, unsigned count); + +void calls_out_ptr_in_count(void) { + // with null buffer and null count. + // no error? + in_ptr_out_count(0, 0); + + // with null buffer and non-null count. + // weird error: non-pointer to safe pointer conversion is not allowed...? + in_ptr_out_count(0, 2); + + // with non-null buffer and null count. + int *__counted_by(4) p1; + in_ptr_out_count(&p1, 0); + + // weird error: non-pointer to safe pointer conversion is not allowed...? + int *__counted_by(4) p2; + in_ptr_out_count(&p2, 4); + + // with incompatible, non-null buffer and null count. + // no error? + int *__single p3; + in_ptr_out_count(&p3, 0); + + unsigned count1; + int *__counted_by(count1) p4; + in_ptr_out_count(&p4, 0); + + // with null buffer and non-null count. + // weird error: non-pointer to safe pointer conversion is not allowed...? + unsigned count2; + int *__counted_by(count2) p5; + in_ptr_out_count(0, count2); + + // with null buffer and unrelated out count. + // weird error: non-pointer to safe pointer conversion is not allowed...? + unsigned count3; + in_ptr_out_count(0, count3); +} + +void out_ptr_in_ptr_in_count(int *__counted_by(count) *out_ptr, + int *__counted_by(count) ptr, + unsigned count); + +void calls_out_ptr_in_ptr_in_count(void) { + // should be fine + out_ptr_in_ptr_in_count(0, 0, 0); + + // should be a warning/error for in ptr + out_ptr_in_ptr_in_count(0, 0, 10); + + // should be fine + int arr[10]; + out_ptr_in_ptr_in_count(0, arr, 10); +} diff --git a/clang/test/BoundsSafety/Sema/pointer-attrs-on-typedefs.c b/clang/test/BoundsSafety/Sema/pointer-attrs-on-typedefs.c new file mode 100644 index 0000000000000..f742f3ec5d23a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/pointer-attrs-on-typedefs.c @@ -0,0 +1,18 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +typedef int * unspec_ptr_t; +typedef int *__single single_ptr_t; +typedef int *__bidi_indexable bidi_ptr_t; + +unspec_ptr_t __bidi_indexable g_bidi; + +void foo(unspec_ptr_t __bidi_indexable a_bidi_ok) { + unspec_ptr_t __single l_single_ok; + single_ptr_t __single l_single_single_ok; + // expected-error@+1{{pointer cannot have more than one bound attribute}} + single_ptr_t __bidi_indexable l_single_bidi_fail; +} diff --git a/clang/test/BoundsSafety/Sema/pointer-bounds-diags.c b/clang/test/BoundsSafety/Sema/pointer-bounds-diags.c new file mode 100644 index 0000000000000..4d3a468f42c8f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/pointer-bounds-diags.c @@ -0,0 +1,15 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +void foo(int *unqual, int *__bidi_indexable bidi, int *__indexable fwd, + int *__single single, int *__unsafe_indexable unsafe) { + (void)__builtin_get_pointer_lower_bound(unqual); + (void)__builtin_get_pointer_lower_bound(bidi); + (void)__builtin_get_pointer_lower_bound(fwd); + (void)__builtin_get_pointer_lower_bound(single); + // expected-error@+1{{cannot extract the lower bound of 'int *__unsafe_indexable' because it has no bounds specification}} + (void)__builtin_get_pointer_lower_bound(unsafe); +} diff --git a/clang/test/BoundsSafety/Sema/pointer-to-integer-cast.c b/clang/test/BoundsSafety/Sema/pointer-to-integer-cast.c new file mode 100644 index 0000000000000..140ad4dbde3b6 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/pointer-to-integer-cast.c @@ -0,0 +1,27 @@ + + +// Use arm64-apple-ios to ensure that sizeof(uint8_t) < sizeof(uintptr_t). +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include + +char buf[42]; + +// ok +uintptr_t buf_ptr = (uintptr_t)buf; + +// expected-error@+2{{initializer element is not a compile-time constant}} +// expected-warning@+1{{cast to smaller integer type 'uint8_t' (aka 'unsigned char') from 'char *__bidi_indexable'}} +uint8_t buf_8 = (uint8_t)buf; + +void foo(int *__single s, int *__indexable i, int *__bidi_indexable bi) { + uintptr_t s_ptr = (uintptr_t)s; // ok + uintptr_t i_ptr = (uintptr_t)i; // ok + uintptr_t bi_ptr = (uintptr_t)bi; // ok + + uint8_t s_8 = (uint8_t)s; // expected-warning{{cast to smaller integer type 'uint8_t' (aka 'unsigned char') from 'int *__single'}} + uint8_t i_8 = (uint8_t)i; // expected-warning{{cast to smaller integer type 'uint8_t' (aka 'unsigned char') from 'int *__indexable'}} + uint8_t bi_8 = (uint8_t)bi; // expected-warning{{cast to smaller integer type 'uint8_t' (aka 'unsigned char') from 'int *__bidi_indexable'}} +} diff --git a/clang/test/BoundsSafety/Sema/ptr-arith-illegal-incdec-no-auto-bound.c b/clang/test/BoundsSafety/Sema/ptr-arith-illegal-incdec-no-auto-bound.c new file mode 100644 index 0000000000000..75a4327ac34ed --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptr-arith-illegal-incdec-no-auto-bound.c @@ -0,0 +1,20 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int* foo(int *__single a) { + ++a; // expected-error{{pointer arithmetic on single pointer 'a' is out of bounds; consider adding '__counted_by' to 'a'}} + // expected-note@-2{{pointer 'a' declared here}} + return a++; // expected-error{{pointer arithmetic on single pointer 'a' is out of bounds; consider adding '__counted_by' to 'a'}} + // expected-note@-4{{pointer 'a' declared here}} +} + +int main() { + int *__indexable ap; + ap--; // expected-error{{decremented indexable pointer 'ap' is out of bounds}} + --ap; // expected-error{{decremented indexable pointer 'ap' is out of bounds}} + int *inc = foo(ap); + return *inc; +} diff --git a/clang/test/BoundsSafety/Sema/ptr-arith-illegal-incdec.c b/clang/test/BoundsSafety/Sema/ptr-arith-illegal-incdec.c new file mode 100644 index 0000000000000..a0566d9e3b85e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptr-arith-illegal-incdec.c @@ -0,0 +1,21 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +int* foo(int *a) { + ++a; // expected-error{{pointer arithmetic on single pointer 'a' is out of bounds; consider adding '__counted_by' to 'a'}} + // expected-note@-2{{pointer 'a' declared here}} + return a++; // expected-error{{pointer arithmetic on single pointer 'a' is out of bounds; consider adding '__counted_by' to 'a'}} + // expected-note@-4{{pointer 'a' declared here}} +} + + +int main() { + int arr[1]; + int *__indexable ap = arr; + ap--; // expected-error{{decremented indexable pointer 'ap' is out of bounds}} + --ap; // expected-error{{decremented indexable pointer 'ap' is out of bounds}} + int *inc = foo(arr); + return *inc; +} diff --git a/clang/test/BoundsSafety/Sema/ptr-arith-struct-member.c b/clang/test/BoundsSafety/Sema/ptr-arith-struct-member.c new file mode 100644 index 0000000000000..c223fb32ccacf --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptr-arith-struct-member.c @@ -0,0 +1,11 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +struct Foo { int *buf; int cnt; }; + +void Test(struct Foo *f) { + // expected-error@+2{{pointer arithmetic on single pointer 'f->buf' is out of bounds; consider adding '__counted_by' to 'Foo::buf'}} + // expected-note@-4{{pointer 'Foo::buf' declared here}} + int *ptr = f->buf + 2; +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/ptr-arith.c b/clang/test/BoundsSafety/Sema/ptr-arith.c new file mode 100644 index 0000000000000..77b6fed438157 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptr-arith.c @@ -0,0 +1,37 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +int a = -3; + +int foo() { + int buf[10]; + int *__bidi_indexable ptrBound = buf; + int *__single ptrThin = &ptrBound[1]; + int *__indexable ptrArray = &ptrBound[0]; + + ptrBound[9] = 3; // ok + + ptrArray[a] = 5; // run-time error + ptrArray[2] = 2; // ok + ptrArray[-1] = 3; // expected-error{{array subscript with a negative index on indexable pointer 'ptrArray' is out of bounds}} + + a[ptrArray] = 5; // run-time error + 2[ptrArray] = -1; // ok + a[ptrArray] = 5[ptrThin]; // expected-error{{array subscript on single pointer 'ptrThin' must use a constant index of 0 to be in bounds}} + -3[ptrArray] = 2; // expected-error{{expression is not assignable}} + + ptrThin = ptrArray + (-2); // expected-error{{decremented indexable pointer 'ptrArray' is out of bounds}} + ptrThin = ptrArray + 4; // ok + + ptrBound = ptrThin + 3; // expected-error{{pointer arithmetic on single pointer 'ptrThin' is out of bounds; consider adding '__counted_by' to 'ptrThin'}} + // expected-note@-18{{pointer 'ptrThin' declared here}} + + // With casts + // TODO(dliew): Support suggesting `__counted_by` on the `ptrThin` decl in the presence of this cast. + char *tmp = ((char *)ptrThin) + 1; // expected-error-re{{pointer arithmetic on single pointer '((char *)ptrThin)' is out of {{bounds$}}}} + + return ptrThin[2]; // expected-error{{array subscript on single pointer 'ptrThin' must use a constant index of 0 to be in bounds}} +} + diff --git a/clang/test/BoundsSafety/Sema/ptrauth-unsupported.c b/clang/test/BoundsSafety/Sema/ptrauth-unsupported.c new file mode 100644 index 0000000000000..eeb878b6214d2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptrauth-unsupported.c @@ -0,0 +1,18 @@ + +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Foo { + char *__bidi_indexable __ptrauth(1, 1, 1) inner1; + // expected-error@-1 {{pointer authentication is currently unsupported on indexable pointers}} + char *__indexable __ptrauth(1, 1, 1) inner2; + // expected-error@-1 {{pointer authentication is currently unsupported on indexable pointers}} + char *__ptrauth(1, 1, 1) __bidi_indexable inner3; + // expected-error@-1 {{pointer authentication is currently unsupported on indexable pointers}} + char *__ptrauth(1, 1, 1) __indexable inner4; + // expected-error@-1 {{pointer authentication is currently unsupported on indexable pointers}} + char *__ptrauth(1, 1, 1) __single inner5; + char *__ptrauth(1, 1, 1) __unsafe_indexable inner6; +}; diff --git a/clang/test/BoundsSafety/Sema/ptrcheck-unavailable.c b/clang/test/BoundsSafety/Sema/ptrcheck-unavailable.c new file mode 100644 index 0000000000000..75abd3543dcc4 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptrcheck-unavailable.c @@ -0,0 +1,41 @@ + +#include + +// ptrcheck macros should not affect code when compiled without -fbounds-safety +// RUN: %clang_cc1 -fsyntax-only -pedantic -D TEST_UNSAFE %s -verify=expected + +// RUN: %clang_cc1 -fsyntax-only -pedantic -fbounds-safety %s -verify=expected +// RUN: %clang_cc1 -fsyntax-only -pedantic -fbounds-safety -D TEST_UNSAFE %s -verify=unsafe + +// ptcheck macros should not affect code when compiled with -fexperimental-bounds-safety-attributes + +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x c %s -verify=expected +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x c -D TEST_UNSAFE %s -verify=expected + +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x c++ %s -verify=expected +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x c++ -D TEST_UNSAFE %s -verify=expected + +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x objective-c %s -verify=expected +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x objective-c -D TEST_UNSAFE %s -verify=expected + +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x objective-c++ %s -verify=expected +// RUN: %clang_cc1 -fsyntax-only -pedantic -fexperimental-bounds-safety-attributes -x objective-c++ -D TEST_UNSAFE %s -verify=expected + +// expected-no-diagnostics + +int * __ptrcheck_unavailable unsafe_func(void); // unsafe-note{{'unsafe_func' has been explicitly marked unavailable here}} +int * __ptrcheck_unavailable_r(safe_func) unsafe_func2(void); // unsafe-note{{'unsafe_func2' has been explicitly marked unavailable here}} +void safe_func(int *__counted_by(*out_size) * out_arr, int *out_size); + +#ifdef TEST_UNSAFE +void test1(void) { + int *buf = unsafe_func(); // unsafe-error{{'unsafe_func' is unavailable: unavailable with -fbounds-safety}} + buf = unsafe_func2(); // unsafe-error{{'unsafe_func2' is unavailable: unavailable with -fbounds-safety. Use safe_func instead.}} +} +#endif + +void test2(void) { + int len; + int * __counted_by(len) buf; + safe_func(&buf, &len); +} diff --git a/clang/test/BoundsSafety/Sema/ptrs-with-multiple-count-attrs.c b/clang/test/BoundsSafety/Sema/ptrs-with-multiple-count-attrs.c new file mode 100644 index 0000000000000..64ea25452c499 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptrs-with-multiple-count-attrs.c @@ -0,0 +1,12 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#define __counted_by(N) __attribute__((__counted_by__(N))) + +struct T { + unsigned n; + int * end; + // expected-error@+2{{expected ';' at end of declaration list}} + // expected-error@+1{{a parameter list without types is only allowed in a function definition}} + int *__counted_by(3) ended_by(end) ptr; +}; \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/ptrs-with-multiple-range-attrs.c b/clang/test/BoundsSafety/Sema/ptrs-with-multiple-range-attrs.c new file mode 100644 index 0000000000000..a484296bf12b2 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ptrs-with-multiple-range-attrs.c @@ -0,0 +1,110 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +void foo(int *__counted_by(len) __counted_by(len+2)* buf, int len); // expected-error{{pointer cannot have more than one count attribute}} +void bar(int **__counted_by(len) buf __counted_by(len + 1), int len); // expected-error{{pointer cannot have more than one count attribute}} +int *__counted_by(len) __counted_by(len + 2) baz(int len); // expected-error{{pointer cannot have more than one count attribute}} + +int *__counted_by(len) bazx(int len); +int *__counted_by(len) bazx(int len); // ok - same count expr +int *__counted_by(len) bazx(int len) {} // ok - same count expr + +int *__counted_by(4) bazz(); +int *__counted_by(4) bazz(); // ok - same count expr +int *__counted_by(4) bazz() {} // ok - same count expr + +// expected-note@+1{{previous declaration is here}} +int *bazy(); +// expected-error@+1{{conflicting '__counted_by' attribute with the previous function declaration}} +int *__counted_by(4) bazy() {} // ok + +// expected-note@+2{{previous declaration is here}} +// expected-note@+1{{previous declaration is here}} +int *__counted_by(len) bayz(unsigned len); +// expected-error@+1{{conflicting '__counted_by' attribute with the previous function declaration}} +int *bayz(unsigned len) {} +// expected-error@+1{{conflicting '__sized_by' attribute with the previous function declaration}} +int *__sized_by(len) bayz(unsigned len) {} + +// expected-note@+1{{previous declaration is here}} +int *baxz(unsigned len, int **pptr); +// expected-error@+1{{conflicting '__counted_by' attribute with the previous function declaration}} +int *baxz(unsigned len, int *__counted_by(len)* pptr) {} // ok + +// expected-note@+1{{previous declaration is here}} +int *baxy(unsigned len, int *__counted_by(len)* pptr); +// expected-error@+1{{conflicting '__counted_by' attribute with the previous function declaration}} +int *baxy(unsigned len, int ** pptr) {} + +char *__counted_by(len) qux(int len); +// expected-note@+1{{previous declaration is here}} +char *__sized_by(len) qux(int len); +// expected-error@+1{{conflicting '__counted_by' attribute with the previous function declaration}} +char *__counted_by(4) qux(int len) {} + +int *__counted_by(len) __counted_by(len) quux(int len) {} // ok - same count expr + +int *__counted_by(len) __counted_by(cnt) quuz(int len, int cnt) {} // expected-error{{pointer cannot have more than one count attribute}} + +void corge(int *__counted_by(len) ptr, int len); +void corge(int *__counted_by(len) ptr, int len); // ok +void corge(int *__counted_by(len) ptr, int len) {} // ok + +// expected-note@+1{{previous declaration is here}} +int ** grault(int len); +// expected-error@+1{{conflicting '__counted_by' attribute with the previous function declaration}} +int *__counted_by(len) grault(int len) {} + +int ** grault2(int len); +// expected-error@+1{{'__counted_by' attribute on nested pointer type is only allowed on indirect parameters}} +int *__counted_by(len)* grault2(int len) {} + +struct S { + int *__counted_by(l) bp2 __counted_by(l+1); // expected-error{{pointer cannot have more than one count attribute}} + int l; +}; + +struct T { + unsigned n; + int * end; + int *__counted_by(n) __ended_by(end) ptr; // expected-error{{pointer cannot have count and range at the same time}} + int *__ended_by(end) __counted_by(n) ptr2; // expected-error{{pointer cannot have count and range at the same time}} +}; + +void end1(int *__ended_by(b) a, int *b); // OK +void end1(int *__ended_by(b) a, int *b); // OK redeclaration + +// expected-note@+3{{previous declaration is here}} \ + expected-note@+3{{previous declaration is here}} \ + expected-note@+3{{previous declaration is here}} +void end1(int *__ended_by(b) a, int *b) {} // OK definition over declaration + +void end1(int *a, int *b); // expected-error{{conflicting '__ended_by' attribute with the previous function declaration}} +void end1(int *a, int *__ended_by(a) b); // expected-error{{conflicting '__ended_by' attribute with the previous function declaration}} +void end1(int *__ended_by(b) a, int *__ended_by(a) b); // expected-error{{conflicting '__ended_by' attribute with the previous function declaration}} + +void end2(int *__ended_by(b) a, int *__ended_by(c) b, int *c); // OK +void end2(int *__ended_by(b) a, int *__ended_by(c) b, int *c); // OK + +// expected-note@+1{{previous declaration is here}} +void end2(int *__ended_by(b) a, int *__ended_by(c) b, int *c) {} // OK definition over declaration + +void end2(int *__ended_by(b) a, int *__ended_by(c) b, int *__ended_by(a) c); // expected-error{{conflicting '__ended_by' attribute with the previous function declaration}} + +#ifdef __started_by +// this is not currently compiled; it's meant to break if/when __started_by +// explicitly becomes a thing +void start(int *a, int *__started_by(a) b); // OK +void start(int *a, int *__started_by(a) b); // OK redeclaration +// expected-note@+1{{previous declaration is here}} expected-note@+1{{previous declaration is here}} +void start(int *a, int *__started_by(a) b); // OK definition over declaration + +void start(int *a, int *__started_by(a) b); // expected-error{{conflicting '__started_by' attribute with the previous function declaration}} +void start(int *a, int *__started_by(a) b); // expected-error{{conflicting '__started_by' attribute with the previous function declaration}} +#endif diff --git a/clang/test/BoundsSafety/Sema/redundant-attrs.c b/clang/test/BoundsSafety/Sema/redundant-attrs.c new file mode 100644 index 0000000000000..ebbe4e020c8ce --- /dev/null +++ b/clang/test/BoundsSafety/Sema/redundant-attrs.c @@ -0,0 +1,126 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +#include "redundant-attrs.h" + +int *__bidi_indexable __bidi_indexable ptrBoundBound; // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} +int *__indexable __bidi_indexable ptrArrayBound; // expected-error{{pointer cannot have more than one bound attribute}} +int *__indexable __single ptrArrayThin; // expected-error{{pointer cannot have more than one bound attribute}} +int *__bidi_indexable *__indexable ptrBoundPtrArray; +int *__bidi_indexable *__bidi_indexable ptrBoundPtrBound; +int *__indexable __single *ptrArrayThinCast; // expected-error{{pointer cannot have more than one bound attribute}} +int *__bidi_indexable __single *ptrBoudCastThin; // expected-error{{pointer cannot have more than one bound attribute}} + +// Using `#pragma clang abi_ptr_attr set(X Y)` must fail if X and Y are different +// attributes, but you must be able to use `#pragma clang abi_ptr_attr set(X)` and +// then `#pragma clang abi_ptr_attr set(Y)` (which the macros below expand to). +__ptrcheck_abi_assume_unsafe_indexable() +__ptrcheck_abi_assume_single() // Only default, explicit bounds override instead of conflict. + +int *__unsafe_indexable foo(int * __unsafe_indexable x) { + int * __unsafe_indexable * __unsafe_indexable __unsafe_indexable y = &x; // expected-warning{{pointer annotated with __unsafe_indexable multiple times. Annotate only once to remove this warning}} + return *y; +} + +// expected-error@+1{{pointer cannot have more than one bound attribute}} +_Pragma("clang abi_ptr_attr set(single unsafe_indexable)") +void bar(int * x); + +typedef int *__bidi_indexable bidiPtr; +typedef int *__bidi_indexable __bidi_indexable bidiBidiPtr; // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} +bidiPtr __bidi_indexable ptrBoundBound2; + +#define bidiPtr2 int *__bidi_indexable +bidiPtr2 __bidi_indexable ptrBoundBound3; +bidiPtr2_header __bidi_indexable ptrBoundBound3_header; + +typedef int *intPtr; +intPtr __single __single ptrBoundBound4; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} +char * __null_terminated __null_terminated ptrBoundBound5; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} + +int len = 0; +int * __counted_by(len) __counted_by(len) ptrBoundBound6; // expected-error{{pointer cannot have more than one count attribute}} + +struct S1 { + int size; + int arrBoundBound[__counted_by(size) __counted_by(size)]; // expected-error{{array cannot have more than one count attribute}} +}; +struct S2 { + int size; + int len; + int arrBoundBound[__counted_by(size) __counted_by(len)]; // expected-error{{array cannot have more than one count attribute}} +}; + +#define intPtr2 int * +intPtr2 __single __single ptrBoundBound7; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} +intPtr2_header __single __single ptrBoundBound7_header; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} +int * __counted_by(len) __counted_by(len) ptrBoundBound8; // expected-error{{pointer cannot have more than one count attribute}} + +bidiPtr __indexable ptrBoundBoundMismatch; // expected-error{{pointer cannot have more than one bound attribute}} +bidiPtr2 __indexable ptrBoundBoundMismatch2; // expected-error{{pointer cannot have more than one bound attribute}} +bidiPtr2_header __indexable ptrBoundBoundMismatch3; // expected-error{{pointer cannot have more than one bound attribute}} + +#define BIDI_INDEXABLE(T, X) do { T __bidi_indexable X; } while (0) +void macro_decl(void) { + BIDI_INDEXABLE(int * __bidi_indexable, varName); +} + +#define BIDI_INDEXABLE2(X) do { int * __bidi_indexable X; } while (0) +void macro_decl2(void) { + BIDI_INDEXABLE2(__bidi_indexable varName); +} + +void macro_decl3(void) { + BIDI_INDEXABLE(int * __bidi_indexable __bidi_indexable, varName); // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} + BIDI_INDEXABLE_header(int * __bidi_indexable __bidi_indexable, varName_header); // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} +} + +void macro_decl4(void) { + BIDI_INDEXABLE2(__bidi_indexable __bidi_indexable varName); // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} + BIDI_INDEXABLE2_header(__bidi_indexable __bidi_indexable varName_header); // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} +} + +int * __single * __single __single ptrBoundBoundNested; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} +int * __single __single * __single ptrBoundBoundNested2; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} +_Atomic(int * __single) __single * __single ptrBoundBoundNestedAtomic; +_Atomic(int * __single) __single __single * __single ptrBoundBoundNestedAtomic2; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} +_Atomic(int * __single __single) __single * __single ptrBoundBoundNestedAtomic3; // expected-warning{{pointer annotated with __single multiple times. Annotate only once to remove this warning}} + +int * __null_terminated * __null_terminated __null_terminated ptrBoundBoundNestedNullTerm; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} +int * __null_terminated __null_terminated * __null_terminated ptrBoundBoundNestedNullTerm2; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} + +typedef int *__null_terminated nullTermPtr; +nullTermPtr __null_terminated * __null_terminated ptrBoundBoundNestedNullTermTypedef; +nullTermPtr __null_terminated __null_terminated * __null_terminated ptrBoundBoundNestedNullTermTypedef2; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} +nullTermPtr __terminated_by(1) ptrTerm0Term1; // expected-error{{pointer cannot have more than one terminator attribute}} + // expected-note@-1{{conflicting arguments for terminator were '0' and '1'}} + +typedef int *__null_terminated __null_terminated nullTermNullTermPtr; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} +nullTermNullTermPtr * __null_terminated ptrBoundBoundNestedNullTermTypedef3; + +#define nullTermPtr2 int *__null_terminated +nullTermPtr2 __null_terminated ptrNullTermNullTerm; +nullTermPtr2_header __null_terminated ptrNullTermNullTerm_header; + +int * __attribute__((aligned (16))) __single ptrUnrelatedAttributeBound; +int * __null_terminated __attribute__((aligned (16))) __null_terminated ptrUnrelatedAttributeBoundBound; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} +nullTermPtr __attribute__((aligned (16))) __null_terminated ptrUnrelatedAttributeBoundBound2; + +int * __attribute__((__bidi_indexable__)) __attribute__((__bidi_indexable__)) ptrBoundBoundNoMacro; // expected-warning{{pointer annotated with __bidi_indexable multiple times. Annotate only once to remove this warning}} +#define nullTermPtr3 int * __attribute__((__bidi_indexable__)) +nullTermPtr3 __attribute__((__bidi_indexable__)) ptrBoundBoundNoMacro2; +nullTermPtr3_header __attribute__((__bidi_indexable__)) ptrBoundBoundNoMacro3; + +int *__terminated_by(0) __terminated_by(0u) ptrTerminatedByDifferentSignedness; //expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} +int *__terminated_by(0) __terminated_by(0l) ptrTerminatedByDifferentWidth; //expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} +int *const __terminated_by(0) _Nullable __terminated_by(0) * _Nullable __terminated_by(0) ptrTerminatedNestedAndNullable; //expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} + +typedef int *_Nullable __bidi_indexable nullableBidiPtr; +nullableBidiPtr __single ptrNullableBidiSingleConflict; //expected-error{{pointer cannot have more than one bound attribute}} + +typedef int *_Nullable nullablePtr; +nullablePtr __single ptrAttributedTypeSeparateDecl; + +typedef int *_Nullable __null_terminated __bidi_indexable nullableNTBidiPtr; //expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} +nullableNTBidiPtr ptrNullableNTBidi; diff --git a/clang/test/BoundsSafety/Sema/redundant-attrs.h b/clang/test/BoundsSafety/Sema/redundant-attrs.h new file mode 100644 index 0000000000000..35115bd714be1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/redundant-attrs.h @@ -0,0 +1,6 @@ +#define bidiPtr2_header int *__bidi_indexable +#define intPtr2_header int * +#define BIDI_INDEXABLE_header(T, X) do { T __bidi_indexable X; } while (0) +#define BIDI_INDEXABLE2_header(X) do { int * __bidi_indexable X; } while (0) +#define nullTermPtr2_header int *__null_terminated +#define nullTermPtr3_header int * __attribute__((__bidi_indexable__)) diff --git a/clang/test/BoundsSafety/Sema/self-assign.c b/clang/test/BoundsSafety/Sema/self-assign.c new file mode 100644 index 0000000000000..15ccdf5c69a45 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/self-assign.c @@ -0,0 +1,34 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wself-assign -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wself-assign -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// -Wself-assign should not trigger for self-assignments of variables that have +// external bounds, or on the external bounds of those variables. + +void count(int *__counted_by(count) p, int count) { + p = p; + count = count; + if (1) { + int count2 = count; + int *__counted_by(count2) p2 = p; + count2 = count2; + p2 = p2; + } +} + +void end(int *__ended_by(end) begin, int *end) { + begin = begin; + end = end; + if (1) { + int *end2 = end; + int *__ended_by(end2) begin2 = begin; + begin2 = begin2; + end2 = end2; + } +} + +void unrelated(int unrelated) { + unrelated = unrelated; // expected-warning{{explicitly assigning value of variable of type 'int' to itself}} +} diff --git a/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-convert-dynamic_count.c b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-convert-dynamic_count.c new file mode 100644 index 0000000000000..6a7fc395ed40b --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-convert-dynamic_count.c @@ -0,0 +1,881 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include +#include + +//============================================================================== +// Constant 0 count +//============================================================================== + +// not a trap +void cb_const_size_zero(int *__counted_by(0) buf) {} +int *__counted_by(0) use_cb_const_size_zero(int *buf) { + int *local = buf; + cb_const_size_zero(local); + + int *__counted_by(0) local2 = local; + return local; +} + +// not a trap +void cbon_const_size_zero(int *__counted_by_or_null(0) buf) {} +int *__counted_by_or_null(0) use_cbon_const_size_zero(int *buf) { + int *local = buf; + cbon_const_size_zero(local); + + int *__counted_by_or_null(0) local2 = local; + return local; +} + +// not a trap +void sb_const_size_zero(void *__sized_by(0) buf) {} +void *__sized_by(0) use_sb_const_size_zero(int *buf) { + int *local = buf; + sb_const_size_zero(local); + + void *__sized_by(0) local2 = local; + return local; +} + +// not a trap +void sbon_const_size_zero(void *__sized_by_or_null(0) buf) {} +void *__sized_by_or_null(0) use_sbon_const_size_zero(int *buf) { + int *local = buf; + sbon_const_size_zero(local); + + void *__sized_by_or_null(0) local2 = local; + return local; +} + +//============================================================================== +// __sized_by constant count sizeof(int) +//============================================================================== + +// not a trap (provided buf != null) +void sb_const_sizeof_int(void *__sized_by(sizeof(int)) buf) {} +void *__sized_by(sizeof(int)) use_sb_const_sizeof_int(int *buf) { + int *local = buf; + sb_const_sizeof_int(local); + + void *__sized_by(sizeof(int)) local2 = local; + return local; +} + +// trap - false negative warning +void *__sized_by(sizeof(int)) use_sb_const_sizeof_int_null_init(int *buf) { + int *local = 0; + sb_const_sizeof_int(local); + + void *__sized_by(sizeof(int)) local2 = local; + return local; +} + + +// not a trap +void sbon_const_sizeof_int(void *__sized_by_or_null(sizeof(int)) buf) {} +void *__sized_by_or_null(sizeof(int)) use_sbon_const_sizeof_int(int *buf) { + int *local = buf; + sbon_const_sizeof_int(local); + + void *__sized_by_or_null(sizeof(int)) local2 = local; + return local; +} + +//============================================================================== +// __sized_by constant count sizeof(int)+1 +//============================================================================== + +// trap +void sb_const_sizeof_int_plus_one(void *__sized_by(sizeof(int)+1) buf) {} +void *__sized_by(sizeof(int)+1) use_sb_const_sizeof_int_plus_one(int *buf) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'void *__single __sized_by(5UL)' (aka 'void *__single') requires 5 bytes or more}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap when converting to 'void *__single __sized_by(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + sb_const_sizeof_int_plus_one(local); + + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap when converting to 'void *__single __sized_by(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + void *__sized_by(sizeof(int)+1) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will trap in a future compiler version when converting to 'void *__single __sized_by(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + return local; +} + + + +// trap if local != null +void sbon_const_sizeof_int_plus_one(void *__sized_by_or_null(sizeof(int)+1) buf) {} +void *__sized_by_or_null(sizeof(int)+1) use_sbon_const_sizeof_int_plus_one(int *buf) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'void *__single __sized_by_or_null(5UL)' (aka 'void *__single') requires 5 bytes or more}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'void *__single __sized_by_or_null(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + sbon_const_sizeof_int_plus_one(local); + + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'void *__single __sized_by_or_null(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + void *__sized_by_or_null(sizeof(int)+1) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will trap (unless 'local' is null) in a future compiler version when converting to 'void *__single __sized_by_or_null(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + return local; +} + +// not a trap +void use_sbon_const_sizeof_int_plus_one_null_ptr_reach_call(int *buf) { + int *local = (int* __single) 0; + sbon_const_sizeof_int_plus_one(local); // At callsite we know the pointer is null. +} + + + +//============================================================================== +// __counted_by constant count 1 +//============================================================================== + +// not a trap (provided local != null) +void cb_const_size_one(int *__counted_by(1) buf) {} +int *__counted_by(1) use_cb_const_size_one(int* buf) { + int *local = buf; + cb_const_size_one(local); + + int *__counted_by(1) local2 = local; + return local; +} + + + +// not a trap +void cbon_const_size_one(int *__counted_by_or_null(1) buf) {} +int *__counted_by_or_null(1) use_cbon_const_size_one(int* buf) { + int *local = buf; + cbon_const_size_one(local); + + int *__counted_by_or_null(1) local2 = local; + return local; +} + + +//============================================================================== +// __counted_by constant count > 1 +//============================================================================== + +// trap +void cb_const_size_two(int *__counted_by(2) buf) {} +int *__counted_by(2) use_cb_const_size_two(int* buf) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'int *__single __counted_by(2)' (aka 'int *__single') requires 8 bytes or more}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap when converting to 'int *__single __counted_by(2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + cb_const_size_two(local); + + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap when converting to 'int *__single __counted_by(2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + int *__counted_by(2) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will trap in a future compiler version when converting to 'int *__single __counted_by(2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + return local; +} + + +// trap if ptr != null +void cbon_const_size_two(int *__counted_by_or_null(2) buf) {} +int *__counted_by_or_null(2) use_cbon_const_size_two(int* buf) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'int *__single __counted_by_or_null(2)' (aka 'int *__single') requires 8 bytes or more}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'int *__single __counted_by_or_null(2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + cbon_const_size_two(local); + + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'int *__single __counted_by_or_null(2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + int *__counted_by_or_null(2) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will trap (unless 'local' is null) in a future compiler version when converting to 'int *__single __counted_by_or_null(2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + return local; +} + + + +// trap +_Static_assert(sizeof(long long int) > sizeof(int), "unexpected diff"); +void cb_const_size_two_ll(long long int *__counted_by(2) buf) {} +long long int *__counted_by(2) use_cb_const_size_two_ll(int* buf) { // expected-note 6{{pointer 'buf' declared here}} + // This tests the pointee size types (int vs long long int) not matching. + + // expected-note@+3 6{{pointer 'local' declared here}} + // expected-note@+2 3{{__single parameter 'buf' used to initialize 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but cast of 'local' to 'long long *__bidi_indexable' has pointee type 'long long' (8 bytes)}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'long long *__single __counted_by(2)' (aka 'long long *__single') requires 16 bytes or more}} + int *local = buf; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap when converting to 'long long *__single __counted_by(2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + cb_const_size_two_ll((long long int*)local); + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap when converting to 'long long *__single __counted_by(2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + long long int *__counted_by(2) local2 = (long long int*) local; + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will trap in a future compiler version when converting to 'long long *__single __counted_by(2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + return (long long int*)local; +} + +void cb_const_size_one_ll(long long int *__counted_by(1) buf) {} +long long int *__counted_by(1) use_cb_const_size_one_ll(int* buf) { // expected-note 6{{pointer 'buf' declared here}} + // This tests the pointee size types (int vs long long int) not matching. + // expected-note@+3 6{{pointer 'local' declared here}} + // expected-note@+2 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'long long *__single __counted_by(1)' (aka 'long long *__single') requires 8 bytes or more}} + // expected-note@+1 3{{__single parameter 'buf' used to initialize 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but cast of 'local' to 'long long *__bidi_indexable' has pointee type 'long long' (8 bytes)}} + int *local = buf; + + // expected-warning@+2{{passing __bidi_indexable local variable 'local' will trap when converting to 'long long *__single __counted_by(1)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + cb_const_size_one_ll((long long int*) local); + + // expected-warning@+2{{assigning from __bidi_indexable local variable 'local' will trap when converting to 'long long *__single __counted_by(1)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + long long int *__counted_by(1) local2 = (long long int*) local; + + // expected-warning@+2{{returning __bidi_indexable local variable 'local' will trap in a future compiler version when converting to 'long long *__single __counted_by(1)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + return (long long int*) local; +} + + +// trap if ptr != null +void cbon_const_size_two_ll(long long int *__counted_by_or_null(2) buf) {} +long long int *__counted_by_or_null(2) use_cbon_const_size_two_ll(int* buf) { // expected-note 6{{pointer 'buf' declared here}} + // This tests the pointee size types (int vs long long int) not matching. + + // expected-note@+3 6{{pointer 'local' declared here}} + // expected-note@+2 3{{__single parameter 'buf' used to initialize 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but cast of 'local' to 'long long *__bidi_indexable' has pointee type 'long long' (8 bytes)}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'long long *__single __counted_by_or_null(2)' (aka 'long long *__single') requires 16 bytes or more}} + int *local = buf; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'long long *__single __counted_by_or_null(2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + cbon_const_size_two_ll((long long int*)local); + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'long long *__single __counted_by_or_null(2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + long long int *__counted_by_or_null(2) local2 = (long long int*) local; + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will trap (unless 'local' is null) in a future compiler version when converting to 'long long *__single __counted_by_or_null(2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer}} + return (long long int*)local; +} + +//============================================================================== +// Non const size in type +// __counted_by/__counted_by_or_null +//============================================================================== + +void cb_non_const_size(int *__counted_by(size) buf, size_t size) {} +void cbon_non_const_size(int *__counted_by_or_null(size) buf, size_t size) {} + + +// - if ptr != null then traps if size != 1 and size != 0 +// - if ptr null then traps if size != 0 +int *__counted_by(size) use_cb_non_const_size_non_const_arg(int *buf, size_t size) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + cb_non_const_size(local, size); + + size_t size2 = size; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + int *__counted_by(size2) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + return local; +} + + +// if ptr null, never traps +// if ptr != null then traps if size != 1 and size != 0 +int *__counted_by_or_null(size) use_cbon_non_const_size_non_const_arg(int *buf, size_t size) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by_or_null(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + cbon_non_const_size(local, size); + + size_t size2 = size; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by_or_null(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + int *__counted_by_or_null(size2) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by_or_null(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + return local; +} + +// not a trap for call +// maybe a trap for return +int *__counted_by(*size) use_cb_non_const_size_zero_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap because the analysis knows that `size` is 0. + cb_non_const_size(local, 0); + + // False positive + // The analysis doesn't know that `size2` is 0 + size_t size2 = 0; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + int *__counted_by(size2) local2 = local; + + // not a trap because the analysis knows that `size` is 0. + const size_t size3 = 0; + int *__counted_by(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is 0 + *size = 0; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by(*size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + return local; +} + +// not a trap +int *__counted_by_or_null(*size) use_cbon_non_const_size_zero_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap because the analysis knows that `size` is 0. + cbon_non_const_size(local, 0); + + // False positive + // The analysis doesn't know that `size2` is 0 + size_t size2 = 0; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by_or_null(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + int *__counted_by_or_null(size2) local2 = local; + + // not a trap because the analysis knows that `size` is 0. + const size_t size3 = 0; + int *__counted_by_or_null(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is 0 + *size = 0; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by_or_null(*size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + return local; +} + +// traps iff ptr == null +int *__counted_by(*size) use_cb_non_const_size_one_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap in most cases because the analysis knows that `size` is 1. + // Currently doesn't warn about the trap when local == null. + cb_non_const_size(local, 1); + + // False positive + // The analysis doesn't know that `size2` is 1 + size_t size2 = 1; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + int *__counted_by(size2) local2 = local; + + // not a trap because the analysis knows that `size` is 1. + const size_t size3 = 1; + int *__counted_by(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is 0 + *size = 0; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by(*size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + return local; +} + +// not a trap +int* __counted_by_or_null(*size) use_cbon_non_const_size_one_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap because the analysis knows that `size` is 1. + cbon_non_const_size(local, 1); + + // False positive + // The analysis doesn't know that `size2` is 1 + size_t size2 = 1; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by_or_null(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + int *__counted_by_or_null(size2) local2 = local; + + // not a trap because the analysis knows that `size` is 1. + const size_t size3 = 1; + int *__counted_by_or_null(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is 1 + *size = 1; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by_or_null(*size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + return local; +} + +// trap +int* __counted_by(*size) use_cb_non_const_size_two_arg(int *buf, size_t *size) { // expected-note 4{{pointer 'buf' declared here}} + // expected-note@+4 4{{pointer 'local' declared here}} + // expected-note@+3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'int *__single __counted_by(size)' (aka 'int *__single') requires 8 bytes or more}} + // expected-note@+2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'int *__single __counted_by(2UL)' (aka 'int *__single') requires 8 bytes or more}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap when converting to 'int *__single __counted_by(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + cb_non_const_size(local, 2); + + // The analysis isn't aware that `size2` is 2 so the warning is not as + // specific as it should be. + size_t size2 = 2; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + int *__counted_by(size2) local2 = local; + + // The analysis is aware that `size` is 2 so the warning is more specific + const size_t size3 = 2; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap when converting to 'int *__single __counted_by(2UL)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + int *__counted_by(size3) local3 = local; + + // The analysis isn't aware that `*size` is 2 so the warning is not as + // specific as it should be. + *size = 2; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by(*size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + return local; +} + +// trap if ptr != null +int* __counted_by_or_null(*size) use_cbon_non_const_size_two_arg(int *buf, size_t *size) { // expected-note 4{{pointer 'buf' declared here}} + // expected-note@+4 4{{pointer 'local' declared here}} + // expected-note@+3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'int *__single __counted_by_or_null(size)' (aka 'int *__single') requires 8 bytes or more}} + // expected-note@+2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'int *__single __counted_by_or_null(2UL)' (aka 'int *__single') requires 8 bytes or more}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'int *__single __counted_by_or_null(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + cbon_non_const_size(local, 2); + + // The analysis isn't aware that `size` is 2 so the warning is not as + // specific as it should be. + size_t size2 = 2; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by_or_null(size2)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + int *__counted_by_or_null(size2) local2 = local; + + // The analysis is aware that `size` is 2 so the warning is more specific + const size_t size3 = 2; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'int *__single __counted_by_or_null(2UL)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer}} + int *__counted_by_or_null(size3) local3 = local; + + // The analysis isn't aware that `*size` is 2 so the warning not as specific + // as it should be. + *size = 2; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'int *__single __counted_by_or_null(*size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap}} + return local; +} + +// - if ptr != null then traps if size > 0 +// - if ptr null then traps if size > 0 +void cb_non_const_size_ll(long long int* __counted_by(size) local, size_t size); +long long int *__counted_by(size) use_cb_non_const_size_non_const_arg_ll(int *buf, size_t size) { // expected-note 7{{pointer 'buf' declared here}} + // This tests the pointee size types (sizeof(int) < sizeof(long long int)) not matching. + + // expected-note@+3 7{{pointer 'local' declared here}} + // expected-note@+2 4{{__single parameter 'buf' used to initialize 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but cast of 'local' to 'long long *__bidi_indexable' has pointee type 'long long' (8 bytes)}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'long long *__single __counted_by(size)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap. If 'local' is null then any count != 0 will trap}} + cb_non_const_size_ll((long long int*) local, size); + + // FIXME: Consider suppressing the cast warning if it flows into something + // we know won't trap. + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + cb_non_const_size_ll((long long int*) local, 0); // not a trap + + size_t size2 = size; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'long long *__single __counted_by(size2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap. If 'local' is null then any count != 0 will trap}} + long long int *__counted_by(size2) local2 = (long long int*) local; + + // expected-warning@+2{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'long long *__single __counted_by(size)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap. If 'local' is null then any count != 0 will trap}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + return (long long int*) local; +} + +void cb_non_const_size_c(char* __counted_by(size) local, size_t size); +char *__counted_by(size) use_cb_non_const_size_non_const_arg_c(int *buf, size_t size) { // expected-note 3{{pointer 'buf' declared here}} + // This tests the pointee size types (sizeof(int) > sizeof(char)) not matching. + + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'char *__single __counted_by(size)' (aka 'char *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 4 will trap. If 'local' is null then any count != 0 will trap}} + cb_non_const_size_c((char*) local, size); + + cb_non_const_size_c((char*) local, 4); // not a trap + cb_non_const_size_c((char*) local, 3); // not a trap + cb_non_const_size_c((char*) local, 2); // not a trap + cb_non_const_size_c((char*) local, 1); // not a trap + + size_t size2 = size; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'char *__single __counted_by(size2)' (aka 'char *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 4 will trap. If 'local' is null then any count != 0 will trap}} + char *__counted_by(size2) local2 = (char*) local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'char *__single __counted_by(size)' (aka 'char *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 4 will trap. If 'local' is null then any count != 0 will trap}} + return (char*) local; +} + +// - if ptr != null then traps if size > 0 +void cbon_non_const_size_ll(long long int* __counted_by_or_null(size) local, size_t size); +long long int *__counted_by_or_null(size) use_cbon_non_const_size_non_const_arg_ll(int *buf, size_t size) { // expected-note 6{{pointer 'buf' declared here}} + // This tests the pointee size types (sizeof(int) < sizeof(long long int)) not matching. + + // expected-note@+3 6{{pointer 'local' declared here}} + // expected-note@+2 3{{__single parameter 'buf' used to initialize 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but cast of 'local' to 'long long *__bidi_indexable' has pointee type 'long long' (8 bytes)}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'long long *__single __counted_by_or_null(size)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap}} + cbon_non_const_size_ll((long long int*) local, size); + + size_t size2 = size; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'long long *__single __counted_by_or_null(size2)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap}} + long long int *__counted_by_or_null(size2) local2 = (long long int*) local; + + // expected-warning@+2{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'long long *__single __counted_by_or_null(size)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + return (long long int*) local; +} + +void cbon_non_const_size_c(char* __counted_by_or_null(size) local, size_t size); +char *__counted_by_or_null(size) use_cbon_non_const_size_non_const_arg_c(int *buf, size_t size) { // expected-note 3{{pointer 'buf' declared here}} + // This tests the pointee size types (sizeof(int) > sizeof(char)) not matching. + + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'char *__single __counted_by_or_null(size)' (aka 'char *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 4 will trap}} + cbon_non_const_size_c((char*) local, size); + + size_t size2 = size; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'char *__single __counted_by_or_null(size2)' (aka 'char *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 4 will trap}} + char *__counted_by_or_null(size2) local2 = (char*) local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'char *__single __counted_by_or_null(size)' (aka 'char *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 4 will trap}} + return (char*) local; +} + +//============================================================================== +// Non const size in type +// __sized_by/__sized_by_or_null +//============================================================================== + +void sb_non_const_size(void *__sized_by(size) buf, size_t size) {} +void sbon_non_const_size(void *__sized_by_or_null(size) buf, size_t size) {} + +// Check the size because the warning text mentions the size explicitly. +_Static_assert(sizeof(int) == 4, "int has unexpected size"); + +// - if ptr != null then traps if size > sizeof(int) and size != 0 +// - if ptr null then traps if size != 0 +void *__sized_by(size) use_sb_non_const_size_non_const_arg(int *buf, size_t size) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by(size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + sb_non_const_size(local, size); + + size_t size2 = size; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + void *__sized_by(size2) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by(size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + return local; +} + + + +// - if ptr != null then traps if size > sizeof(int) and size != 0 +// - if ptr null then won't trap +void *__sized_by_or_null(size) use_sbon_non_const_size_non_const_arg(int *buf, size_t size) { // expected-note 3{{pointer 'buf' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by_or_null(size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + sbon_non_const_size(local, size); + + size_t size2 = size; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by_or_null(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + void *__sized_by_or_null(size2) local2 = local; + + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by_or_null(size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + return local; +} + + +// not a trap +void *__sized_by(*size) use_sb_non_const_size_zero_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap + sb_non_const_size(local, 0); + + // False positive + // The analysis doesn't know that `size` is 0 + size_t size2 = 0; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + void *__sized_by(size2) local2 = local; + + // The analysis knows that `size3` is 0 so it doesn't warn in this case. + const size_t size3 = 0; + void *__sized_by(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is 0 + *size = 0; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by(*size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + return local; +} + + +// not a trap +void* __sized_by_or_null(*size) use_sbon_non_const_size_zero_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap + sbon_non_const_size(local, 0); + + // False positive + // The analysis doesn't know that `size` is 0 + size_t size2 = 0; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by_or_null(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + void* __sized_by_or_null(size2) local2 = local; + + // The analysis knows that `size3` is 0 so it doesn't warn in this case. + const size_t size3 = 0; + void* __sized_by_or_null(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is 0 + *size = 0; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by_or_null(*size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + return local; +} + + + +// traps only if `local` is null +void* __sized_by(*size) use_sb_non_const_sizeof_int_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // traps only if `local` is null. We currently don't warn about this. + sb_non_const_size(local, sizeof(int)); + + // False positive + // The analysis doesn't know that `size` is `sizeof(int)` + size_t size2 = sizeof(int); + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + void* __sized_by(size2) local2 = local; + + // traps only if `local` is null. We currently don't warn about this. + // The analysis knows that `size3` is `sizeof(int)` so it doesn't warn in + // this case. + const size_t size3 = sizeof(int); + void* __sized_by(size3) local3 = local; + + + // False positive + // The analysis doesn't know that `*size` is `sizeof(int)` + *size = sizeof(int); + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by(*size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + return local; +} + + +// not a trap +void* __sized_by_or_null(*size) use_sbon_non_const_sizeof_int_arg(int *buf, size_t *size) { // expected-note 2{{pointer 'buf' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + + // not a trap + sbon_non_const_size(local, sizeof(int)); + + // False positive + // The analysis doesn't know that `size` is `sizeof(int)` + size_t size2 = sizeof(int); + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by_or_null(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + void* __sized_by_or_null(size2) local2 = local; + + const size_t size3 = sizeof(int); + // The analysis knows that `size3` is `sizeof(int)` so it doesn't warn in + // this case. + void* __sized_by_or_null(size3) local3 = local; + + // False positive + // The analysis doesn't know that `*size` is `sizeof(int)` + *size = sizeof(int); + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by_or_null(*size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + return local; +} + + + +// trap +void* __sized_by(*size) use_sb_non_const_sizeof_int_plus_one_arg(int *buf, size_t *size) { // expected-note 4{{pointer 'buf' declared here}} + // expected-note@+4 4{{pointer 'local' declared here}} + // expected-note@+3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'void *__single __sized_by(size)' (aka 'void *__single') requires 5 bytes or more}} + // expected-note@+2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'void *__single __sized_by(5UL)' (aka 'void *__single') requires 5 bytes or more}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap when converting to 'void *__single __sized_by(size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + sb_non_const_size(local, sizeof(int)+1); + + // The analysis isn't aware that `size` is `sizeof(int)+1` so the warning + // not as specific as it should be. + size_t size2 = sizeof(int)+1; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + void* __sized_by(size2) local2 = local; + + // The analysis is aware that `size` is `sizeof(int)+1` so the warning is + // more specific. + const size_t size3 = sizeof(int)+1; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap when converting to 'void *__single __sized_by(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + void* __sized_by(size3) local3 = local; + + // The analysis isn't aware that `*size` is `sizeof(int)+1` so the warning + // not as specific as it should be. + *size = sizeof(int)+1; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by(*size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap. If 'local' is null then any size != 0 will trap}} + return local; +} + + +// traps iff ptr is not null +void* __sized_by_or_null(*size) use_sbon_non_const_sizeof_int_plus_one_arg(int *buf, size_t *size) { // expected-note 4{{pointer 'buf' declared here}} + // expected-note@+4 4{{pointer 'local' declared here}} + // expected-note@+3{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'void *__single __sized_by_or_null(size)' (aka 'void *__single') requires 5 bytes or more}} + // expected-note@+2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but conversion of 'local' to 'void *__single __sized_by_or_null(5UL)' (aka 'void *__single') requires 5 bytes or more}} + // expected-note@+1 2{{__single parameter 'buf' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + int *local = buf; + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'void *__single __sized_by_or_null(size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + sbon_non_const_size(local, sizeof(int)+1); + + // The analysis isn't aware that `size` is `sizeof(int)+1` so the warning + // not as specific as it should be. + size_t size2 = sizeof(int)+1; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will likely trap when converting to 'void *__single __sized_by_or_null(size2)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + void* __sized_by_or_null(size2) local2 = local; + + // The analysis is aware that `size` is `sizeof(int)+1` so the warning is + // more specific. + const size_t size3 = sizeof(int)+1; + // expected-warning@+1{{assigning from __bidi_indexable local variable 'local' will trap (unless 'local' is null) when converting to 'void *__single __sized_by_or_null(5UL)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer}} + void* __sized_by_or_null(size3) local3 = local; + + // The analysis isn't aware that `*size` is `sizeof(int)+1` so the warning + // not as specific as it should be. + *size = sizeof(int)+1; + // expected-warning@+1{{returning __bidi_indexable local variable 'local' will likely trap in a future compiler version when converting to 'void *__single __sized_by_or_null(*size)' (aka 'void *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any size > 4 will trap}} + return local; +} + +//============================================================================== +// Multiple __single assignments +// +// This is non-exhaustive due to the huge number of test cases we'd need to +// write. +//============================================================================== + +int* single_global; // expected-note{{pointer 'single_global' declared here}} +struct StructWithSinglePtr { + int* ptr; // expected-note{{StructWithSinglePtr::ptr declared here}} + int* arr[4]; // expected-note{{StructWithSinglePtr::arr declared here}} +}; +int* ret_single(); // expected-note{{'ret_single' declared here}} + +void cb_test_multiple_single_assignees_same_size( + int* p, // expected-note{{pointer 'p' declared here}} + struct StructWithSinglePtr s, + size_t size) { + int* single_ptrs[4] = {0}; // expected-note{{single_ptrs' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + + // expected-note@+1{{__single parameter 'p' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + local = p; + // expected-note@+1{{__single struct member 'ptr' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + local = s.ptr; + // expected-note@+1{{__single global 'single_global' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + local = single_global; + // expected-note@+1{{__single element from array 'single_ptrs' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + local = single_ptrs[0]; + // expected-note@+1{{__single return value from call to 'ret_single' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + local = ret_single(); + // FIXME: This text is slightly wrong. `arr` isn't being assigned directly. + // expected-note@+1{{__single struct member 'arr' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes}} + local = s.arr[0]; + + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'int *__single __counted_by(size)' (aka 'int *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 1 will trap. If 'local' is null then any count != 0 will trap}} + cb_non_const_size(local, size); +} + +_Static_assert(sizeof(long long int) > sizeof(int), "unexpected size diff"); +void consume_one_ll_int(long long int* __counted_by(1)); +void cb_test_multiple_single_assignees_mixed_sizes_suppresses_warning(int* p, long long int* q, int cond) { + long long int* local; + if (cond) + local = q; + else + local = (long long int* __bidi_indexable)(int* __bidi_indexable) p; // out-of-bounds pointer + + // False negative: The analysis conservatively assumes that `local` takes the + // largest bounds. In this case the assignment of `q` has the largest bounds + // which would not lead to a trap. The assignment of `p` would be a trap + consume_one_ll_int(local); +} + +void consume_dynamic_ll_int(long long int* __counted_by(size), size_t size); +void cb_test_multiple_single_assignees_mixed_sizes_warns( + char* p, // expected-note 2{{pointer 'p' declared here}} + int* q, // expected-note 2{{pointer 'q' declared here}} + int cond, size_t size) { + long long int* local; // expected-note 2{{pointer 'local' declared here}} + if (cond) { + // expected-note@+2{{__single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes) but 'local' has pointee type 'long long' (8 bytes)}} + // expected-note@+1{{_single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'int' (4 bytes)}} + local = (long long int* __bidi_indexable)(int* __bidi_indexable) q; // out-of-bounds pointer + } + else { + // expected-note@+2{{__single parameter 'p' assigned to 'local' here results in 'local' having the bounds of a single 'char' (1 bytes) but 'local' has pointee type 'long long' (8 bytes)}} + // expected-note@+1{{_single parameter 'p' assigned to 'local' here results in 'local' having the bounds of a single 'char' (1 bytes)}} + local = (long long int* __bidi_indexable)(char* __bidi_indexable) p; // out-of-bounds pointer + } + + // expected-warning@+2{{passing __bidi_indexable 'local' will pass an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'long long'}} + // expected-warning@+1{{passing __bidi_indexable local variable 'local' will likely trap when converting to 'long long *__single __counted_by(size)' (aka 'long long *__single') due to 'local' having the bounds of a __single pointer. If 'local' is non-null then any count > 0 will trap. If 'local' is null then any count != 0 will trap}} + consume_dynamic_ll_int(local, size); +} + +//============================================================================== +// __unsafe_forge_bidi_indexable +//============================================================================== + +void consume_sized_by_void(void* __sized_by(size), size_t size); +void void_bidi(void * p) { + // False negative: This will trap. The analysis currently doesn't + // understand pointers that come from `__unsafe_forge_bidi_indexable`. + // + // This isn't really `__single` so it's not surprising the analysis does + // not understand this. + void* local = __unsafe_forge_bidi_indexable(void*, p, sizeof(char)); + consume_sized_by_void(local, sizeof(int)); +} + + diff --git a/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-unsafe-cast-to-larger-type.c b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-unsafe-cast-to-larger-type.c new file mode 100644 index 0000000000000..4d97a90608da9 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-unsafe-cast-to-larger-type.c @@ -0,0 +1,335 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +typedef int SmallTy; +typedef long long int BigTy; +_Static_assert(sizeof(BigTy) > sizeof(SmallTy), "expected size diff failed"); + + +//============================================================================== +// Explicit Unsafe casts that stay as __bidi_indexable pointers +//============================================================================== +void unsafe_explicit_cast_assign_to_local(SmallTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + BigTy* local2 = (BigTy*) local; + + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + local2 = (BigTy*) local; +} + +void unsafe_explicit_cast_assign_to_local_multiple_singles( + SmallTy* p, // expected-note 2{{pointer 'p' declared here}} + SmallTy* q, // expected-note 2{{pointer 'q' declared here}} + int condition) { + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + if (condition) + // expected-note@+1 2{{__single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = q; + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + BigTy* local2 = (BigTy*) local; + + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + local2 = (BigTy*) local; +} + +struct StructWithBigTyBidi { + BigTy* __bidi_indexable field; +}; + + +struct NestedStructWithBigTy { + struct StructWithBigTyBidi nested; + BigTy* __bidi_indexable field; +}; + +void unsafe_explicit_cast_struct_init(SmallTy* p) { // expected-note 3{{pointer 'p' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + struct StructWithBigTyBidi local2 = {.field = (BigTy*) local}; + + struct NestedStructWithBigTy local3 = { + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + .nested = {.field = (BigTy*) local}, + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + .field = (BigTy*) local}; +} + +BigTy* __bidi_indexable unsafe_explicit_return_bidi(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + return (BigTy*) local; +} + +void receive_bidi(BigTy* __bidi_indexable); +void unsafe_explicit_cast_call_arg(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + receive_bidi((BigTy*) local); +} + +void unsafe_explicit_cast_in_expr(SmallTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+3 2{{pointer 'local' declared here}} + // expected-note@+2{{_single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but '((BigTy *)local)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+2{{indexing __bidi_indexable '((BigTy *)local)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local' is assigned a __single pointer that results in '((BigTy *)local)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + ((BigTy*) local)[0] = 0; +} + +typedef char TinyTy; +_Static_assert(sizeof(SmallTy) > sizeof(TinyTy), "expected size diff failed"); + +void unsafe_explicit_cast_assign_to_local_zero_elt_oob(TinyTy* p) { // expected-note 4{{pointer 'p' declared here}} + // expected-note@+3 4{{pointer 'local' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but '(BigTy *)local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = (SmallTy* __bidi_indexable)(TinyTy* __bidi_indexable) p; + // expected-warning@+2{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + BigTy* local2 = (BigTy*) local; + + // expected-warning@+2{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + local2 = (BigTy*) local; +} + +void unsafe_explicit_cast_struct_init_zero_elt_oob(TinyTy* p) { // expected-note 6{{pointer 'p' declared here}} + // expected-note@+3 6{{pointer 'local' declared here}} + // expected-note@+2 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but '(BigTy *)local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = (SmallTy* __bidi_indexable)(TinyTy* __bidi_indexable) p; + + struct StructWithBigTyBidi local2 = { + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + .field = (BigTy*) local + }; + + struct NestedStructWithBigTy local3 = { + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + .nested = {.field = (BigTy*) local}, + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + .field = (BigTy*) local}; +} + +//============================================================================== +// Implicit Unsafe casts that stay as __bidi_indexable pointers +//============================================================================== +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincompatible-pointer-types" +void unsafe_implicit_cast_assign_to_local(SmallTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + BigTy* local2 = local; + + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + local2 = local; +} + +void unsafe_implicit_cast_struct_init(SmallTy* p) { // expected-note 3{{pointer 'p' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + struct StructWithBigTyBidi local2 = {.field = local}; + + struct NestedStructWithBigTy local3 = { + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + .nested = {.field = local}, + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + .field = local}; +} + +BigTy* __bidi_indexable unsafe_implicit_return_bidi(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + return local; +} + +void unsafe_implicit_cast_call_arg_bidi(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + receive_bidi(local); +} + +void unsafe_implicit_cast_assign_to_local_zero_elt_oob(TinyTy* p) { // expected-note 4{{pointer 'p' declared here}} + // expected-note@+3 4{{pointer 'local' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but 'local' has pointee type 'SmallTy' (aka 'int') (4 bytes)}} + SmallTy* local = (SmallTy* __bidi_indexable)(TinyTy* __bidi_indexable) p; + // expected-warning@+2{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + BigTy* local2 = local; + + // expected-warning@+2{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{implicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + local2 = local; +} + +#pragma clang diagnostic pop + +//============================================================================== +// Explicit Unsafe casts that then are later converted to __single pointers +//============================================================================== +void unsafe_explicit_cast_assign_to_local_single(SmallTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + BigTy* __single local2 = (BigTy*) local; + + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + local2 = (BigTy*) local; +} + +struct StructWithBigTySingle { + BigTy* field; +}; + + +struct NestedStructWithBigTySingle { + struct StructWithBigTySingle nested; + BigTy* field; +}; + +void unsafe_explicit_cast_struct_init_single(SmallTy* p) { // expected-note 3{{pointer 'p' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + struct StructWithBigTySingle local2 = {.field = (BigTy*) local}; + + struct NestedStructWithBigTySingle local3 = { + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + .nested = {.field = (BigTy*) local}, + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + .field = (BigTy*) local}; +} + +BigTy* unsafe_explicit_return_single(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + return (BigTy*) local; +} + +void receive_single(BigTy*); +void unsafe_explicit_cast_call_arg_single(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + receive_single((BigTy*) local); +} + +void unsafe_explicit_cast_assign_to_local_zero_elt_oob_single(TinyTy* p) { // expected-note 4{{pointer 'p' declared here}} + // expected-note@+3 4{{pointer 'local' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but '(BigTy *)local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = (SmallTy* __bidi_indexable)(TinyTy* __bidi_indexable) p; + // expected-warning@+2{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + BigTy* __single local2 = (BigTy*) local; + + // expected-warning@+2{{assigning from __bidi_indexable '(BigTy *)local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in '(BigTy *)local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + local2 = (BigTy*) local; +} + +void unsafe_explicit_cast_in_expr_single(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{explicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + ((BigTy* __single) local)[0] = 5; +} + +//============================================================================== +// Implicit unsafe casts that then are later converted to __single pointers +//============================================================================== +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincompatible-pointer-types" +void unsafe_implicit_cast_assign_to_local_single(SmallTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + BigTy* __single local2 = local; + + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + local2 = local; +} + +void unsafe_implicit_cast_struct_init_single(SmallTy* p) { // expected-note 3{{pointer 'p' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + struct StructWithBigTySingle local2 = {.field = local}; + + struct NestedStructWithBigTySingle local3 = { + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + .nested = {.field = local}, + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + .field = local}; +} + +BigTy* unsafe_implicit_return_single(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + return local; +} + +void unsafe_implicit_cast_call_arg_single(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + receive_single(local); +} + +void unsafe_implicit_cast_assign_to_local_zero_elt_oob_single(TinyTy* p) { // expected-note 4{{pointer 'p' declared here}} + // expected-note@+3 4{{pointer 'local' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'TinyTy' (aka 'char') (1 bytes) but 'local' has pointee type 'SmallTy' (aka 'int') (4 bytes)}} + SmallTy* local = (SmallTy* __bidi_indexable)(TinyTy* __bidi_indexable) p; + // expected-warning@+2{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + BigTy* __single local2 = local; + + // expected-warning@+2{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + local2 = local; +} + +#pragma clang diagnostic pop diff --git a/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-unsafe-zeroth-element.c b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-unsafe-zeroth-element.c new file mode 100644 index 0000000000000..a8b90825f0152 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op-unsafe-zeroth-element.c @@ -0,0 +1,428 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +typedef int SmallTy; +typedef long long int BigTy; +_Static_assert(sizeof(BigTy) > sizeof(SmallTy), "expected size diff failed"); + +//============================================================================== +// Local __bidi_indexable with bounds smaller than its element type (i.e. the +// 0th element can't be accessed safely). +// +// Currently we don't generate traps for this (rdar://119744147) but we still +// warn because it will become a trap in the future. +//============================================================================== + +void param_single_explicit_cast_deref(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + BigTy* local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + // expected-warning@+1{{dereferencing __bidi_indexable 'local' will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + *local = 0; +} + +void param_single_explicit_cast_deref_with_cast_to_larger(SmallTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+3 2{{pointer 'local' declared here}} + // expected-note@+2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but '((BigTy *)local)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallTy* local = p; + // expected-warning@+2{{dereferencing __bidi_indexable '((BigTy *)local)' will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local' is assigned a __single pointer that results in '((BigTy *)local)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local) = 0; +} + + +void param_single_explicit_cast_index(SmallTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + BigTy* local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + // expected-warning@+1{{indexing __bidi_indexable 'local' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + local[0] = 0; +} + +struct SmallStructTy { + int a; + int b; +}; +_Static_assert(sizeof(struct SmallStructTy) == 8, "wrong size"); + +struct BigStructTy { + struct SmallStructTy inner; + int c; +}; +_Static_assert(sizeof(struct BigStructTy) == 12, "wrong size"); +_Static_assert(sizeof(struct SmallStructTy) < sizeof(struct BigStructTy), "expected size diff failed"); + +void param_single_explicit_cast_struct_deref(struct SmallStructTy* p, struct BigStructTy* q) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but 'local' has pointee type 'struct BigStructTy' (12 bytes)}} + struct BigStructTy* local = (struct BigStructTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + // expected-warning@+1{{dereferencing __bidi_indexable 'local' will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructTy'}} + *local = *q; +} + +struct StructWithBigTyBidi { + BigTy* __bidi_indexable member; +}; + +void receiveBigTyBidi(BigTy* __bidi_indexable); +BigTy* __bidi_indexable param_single_explicit_cast_oob_escapes_bidi(SmallTy* p) { // expected-note 4{{pointer 'p' declared here}} + // expected-note@+2 4{{pointer 'local' declared here}} + // expected-note@+1 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + BigTy* local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + + // expected-warning@+1{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + BigTy* local2 = local; + + // expected-warning@+1{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + struct StructWithBigTyBidi local3 = { .member = local }; + + // expected-warning@+1{{passing __bidi_indexable 'local' will pass an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + receiveBigTyBidi(local); + + // expected-warning@+1{{returning __bidi_indexable 'local' will return an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + return local; +} + +// Test that multiple single 'entities' are supported. This is in no way +// exhaustive. + +SmallTy* retSmallTy(void); // expected-note 4{{'retSmallTy' declared here}} +struct StructWithSmallTySingle { + SmallTy* member; // expected-note 4{{StructWithSmallTySingle::member declared here}} +}; +SmallTy* globalSmallTy; // expected-note 4{{pointer 'globalSmallTy' declared here}} +BigTy* __bidi_indexable param_single_explicit_cast_oob_escapes_bidi_multiple_singles( + SmallTy* p, // expected-note 4{{pointer 'p' declared here}} + int condition, + struct StructWithSmallTySingle s) { + SmallTy* arr[2]; // expected-note 4{{'arr' declared here}} + // expected-note@+1 4{{pointer 'local' declared here}} + BigTy* local; + if (condition == 0) + // expected-note@+1 4{{__single parameter 'p' assigned to 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + else if (condition == 1) + // expected-note@+1 4{{__single global 'globalSmallTy' assigned to 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) globalSmallTy; + else if (condition == 2) + // expected-note@+1 4{{__single return value from call to 'retSmallTy' assigned to 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) retSmallTy(); + else if (condition == 3) + // expected-note@+1 4{{__single struct member 'member' assigned to 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) s.member; + else if (condition == 4) + // expected-note@+1 4{{_single element from array 'arr' assigned to 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) arr[0]; + + // expected-warning@+1{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + BigTy* local2 = local; + + // expected-warning@+1{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + struct StructWithBigTyBidi local3 = { .member = local }; + + // expected-warning@+1{{passing __bidi_indexable 'local' will pass an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + receiveBigTyBidi(local); + + // expected-warning@+1{{returning __bidi_indexable 'local' will return an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + return local; +} + +struct StructWithBigTySingle { + BigTy* member; +}; + +void receiveBigTySingle(BigTy*); +BigTy* param_single_explicit_cast_oob_escapes_single(SmallTy* p) { // expected-note 8{{pointer 'p' declared here}} + // expected-note@+3 8{{pointer 'local' declared here}} + // expected-note@+2 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + BigTy* local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + + // expected-warning@+2{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + // expected-warning@+1{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + BigTy* __single local2 = local; + + // expected-warning@+2{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + // expected-warning@+1{{assigning from __bidi_indexable 'local' will propagate an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + struct StructWithBigTySingle local3 = { .member = local }; + + // expected-warning@+2{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + // expected-warning@+1{{passing __bidi_indexable 'local' will pass an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + receiveBigTySingle(local); + + // expected-warning@+2{{implicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + // expected-warning@+1{{returning __bidi_indexable 'local' will return an out-of-bounds pointer. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long')}} + return local; +} + +// TODO: add __single variant + +//============================================================================== +// Member access +// +// Despite the bounds of the local (that suggest some part of it can +// be accessed) access through `->` will trap if **any** field is accessed +// due to `CodeGenFunction::EmitMemberExpr` taking the size of the base expr +// to `->` into account. +//============================================================================== + +void param_single_explicit_cast_nested_member_access(struct SmallStructTy* p) { // expected-note 3{{pointer 'p' declared here}} + // expected-note@+2 3{{pointer 'local' declared here}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but 'local' has pointee type 'struct BigStructTy' (12 bytes)}} + struct BigStructTy* local = (struct BigStructTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + + // The bounds of `local` suggest that `inner` is accessible but + // `CodeGenFunction::EmitMemberExpr` takes the struct size into account so + // no part of the struct is accessible through `->`. + // + // expected-warning@+1{{accessing field BigStructTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructTy'}} + local->inner.a = 0; + // expected-warning@+1{{accessing field BigStructTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructTy'}} + local->inner.b = 0; + + // expected-warning@+1{{accessing field BigStructTy::c through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructTy'}} + local->c = 0; +} + +void param_single_nested_member_access_explicit_cast_to_larger_at_use(struct SmallStructTy* p) { // expected-note 6{{pointer 'p' declared here}} + // expected-note@+3 6{{pointer 'local' declared here}} + // expected-note@+2 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but cast of 'local' to 'struct BigStructTy *__bidi_indexable' has pointee type 'struct BigStructTy' (12 bytes)}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but '((struct BigStructTy *)local)' has pointee type 'struct BigStructTy' (12 bytes}} + struct SmallStructTy* local = p; + + // The bounds of `local` suggest that `inner` is accessible but + // `CodeGenFunction::EmitMemberExpr` takes the struct size into account so + // no part of the struct is accessible through `((BigStructTy*)local)->` + // + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may tra}} + // expected-warning@+1{{accessing field BigStructTy::inner through __bidi_indexable '((struct BigStructTy *)local)' will always trap. At runtime 'local' is assigned a __single pointer that results in '((struct BigStructTy *)local)' having bounds smaller than a single 'struct BigStructTy' (12 bytes)}} + ((struct BigStructTy*) local)->inner.a = 0; + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may tra}} + // expected-warning@+1{{accessing field BigStructTy::inner through __bidi_indexable '((struct BigStructTy *)local)' will always trap. At runtime 'local' is assigned a __single pointer that results in '((struct BigStructTy *)local)' having bounds smaller than a single 'struct BigStructTy' (12 bytes)}} + ((struct BigStructTy*) local)->inner.b = 0; + + // expected-warning@+2{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may tra}} + // expected-warning@+1{{accessing field BigStructTy::c through __bidi_indexable '((struct BigStructTy *)local)' will always trap. At runtime 'local' is assigned a __single pointer that results in '((struct BigStructTy *)local)' having bounds smaller than a single 'struct BigStructTy' (12 bytes)}} + ((struct BigStructTy*) local)->c = 0; +} + +void param_single_nested_member_access_explicit_cast_to_smaller_at_use(struct BigStructTy* p) { + struct BigStructTy* local = p; + + // No warnings when casting to a smaller type. + ((struct SmallStructTy*) local)->a = 0; + ((struct SmallStructTy*) local)->b = 0; +} + +struct BigStructWithFAMTy { + struct SmallStructTy inner; + int c; + char buf[__counted_by(c)]; +}; + +void param_single_explicit_cast_fam_member_access(struct SmallStructTy* p) { // expected-note 5{{pointer 'p' declared here}} + // expected-note@+2 5{{pointer 'local' declared here}} + // expected-note@+1 5{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but 'local' has pointee type 'struct BigStructWithFAMTy' (12 bytes)}} + struct BigStructWithFAMTy* local = (struct BigStructWithFAMTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + + // The bounds of `local` suggest that `inner` is accessible but + // `CodeGenFunction::EmitMemberExpr` takes the struct size into account so + // no part of the struct is accessible through `->`. + // + // expected-warning@+1{{accessing field BigStructWithFAMTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithFAMTy'}} + local->inner.a = 0; + // expected-warning@+1{{accessing field BigStructWithFAMTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithFAMTy'}} + local->inner.b = 0; + + // expected-warning@+1{{accessing field BigStructWithFAMTy::buf through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithFAMTy'}} + local->buf[0] = 0; + + // expected-warning@+1{{accessing field BigStructWithFAMTy::buf through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithFAMTy'}} + char* local2 = local->buf + 1; + + // expected-warning@+1{{accessing field BigStructWithFAMTy::buf through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithFAMTy'}} + char* local3 = &(local->buf[0]); +} + +struct BigStructWithBitFieldsTy { + struct SmallStructTy inner; + int c:1; + int d:1; +}; + +void param_single_explicit_cast_bit_field_access(struct SmallStructTy* p) { // expected-note 4{{pointer 'p' declared here}} + // expected-note@+2 4{{pointer 'local' declared here}} + // expected-note@+1 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but 'local' has pointee type 'struct BigStructWithBitFieldsTy' (12 bytes)}} + struct BigStructWithBitFieldsTy* local = (struct BigStructWithBitFieldsTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + + // The bounds of `local` suggest that `inner` is accessible but + // `CodeGenFunction::EmitMemberExpr` takes the struct size into account so + // no part of the struct is accessible through `->`. + // + // expected-warning@+1{{accessing field BigStructWithBitFieldsTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithBitFieldsTy'}} + local->inner.a = 0; + // expected-warning@+1{{accessing field BigStructWithBitFieldsTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithBitFieldsTy'}} + local->inner.b = 0; + + // expected-warning@+1{{accessing field BigStructWithBitFieldsTy::c through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithBitFieldsTy'}} + local->c = 0; + + // expected-warning@+1{{accessing field BigStructWithBitFieldsTy::d through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithBitFieldsTy'}} + local->d = 0; +} + +struct BigStructWithUnannotatedFAMTy { + struct SmallStructTy inner; + int c; + char buffer[]; +}; + +void param_single_explicit_cast_unannotated_fam_member_access(struct SmallStructTy* p) { // expected-note 2{{pointer 'p' declared here}} + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but 'local' has pointee type 'struct BigStructWithUnannotatedFAMTy' (12 bytes)}} + struct BigStructWithUnannotatedFAMTy* local = (struct BigStructWithUnannotatedFAMTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + + // The bounds of `local` suggest that `inner` is accessible but + // `CodeGenFunction::EmitMemberExpr` takes the struct size into account so + // no part of the struct is accessible through `->`. + // + // expected-warning@+1{{accessing field BigStructWithUnannotatedFAMTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithUnannotatedFAMTy'}} + local->inner.a = 0; + // expected-warning@+1{{accessing field BigStructWithUnannotatedFAMTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'struct BigStructWithUnannotatedFAMTy'}} + local->inner.b = 0; + + // The warning is suppressed in this case because `warn_bounds_safety_promoting_incomplete_array_without_count` already fires. + // expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} + local->buffer[0] = 0; +} + +union BigUnionTy { + struct SmallStructTy inner; + struct BigStructWithFAMTy big_inner; +}; + +void param_single_explicit_cast_union_access(struct SmallStructTy* p) { // expected-note 6{{pointer 'p' declared here}} + // expected-note@+2 6{{pointer 'local' declared here}} + // expected-note@+1 6{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'struct SmallStructTy' (8 bytes) but 'local' has pointee type 'union BigUnionTy' (12 bytes)}} + union BigUnionTy* local = (union BigUnionTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + + // expected-warning@+1{{accessing field BigUnionTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'union BigUnionTy'}} + local->inner.a = 0; + // expected-warning@+1{{accessing field BigUnionTy::inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'union BigUnionTy'}} + local->inner.b = 0; + // expected-warning@+1{{accessing field BigUnionTy::big_inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'union BigUnionTy'}} + local->big_inner.inner.a = 0; + // expected-warning@+1{{accessing field BigUnionTy::big_inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'union BigUnionTy'}} + local->big_inner.inner.b = 0; + + // expected-warning@+1{{accessing field BigUnionTy::big_inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'union BigUnionTy'}} + local->big_inner.c = 0; + // expected-warning@+1{{accessing field BigUnionTy::big_inner through __bidi_indexable 'local' will always trap. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'union BigUnionTy'}} + local->big_inner.buf[0] = 0; +} + +//============================================================================== +// False negatives +//============================================================================== + +void param_single_explicit_cast_not_all_assignments_too_small(SmallTy* p) { + BigTy* local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + *local = 0; // No warn + + // Currently happens-before isn't taken into account so this assignment is + // considered which means not all assignments to `local` are a __single pointer + // that has element type which is too small. + local = 0; +} + +void param_single_explicit_cast_not_all_assignments_too_small2(SmallTy* p, BigTy* q, int condition) { + BigTy* local; + if (condition) + local = (BigTy* __bidi_indexable)(SmallTy* __bidi_indexable) p; + else + local = q; + + *local = 0; // No warn +} + +struct OtherStructWithAnnotatedFAMTy { + int a; + int b; + char buffer[__counted_by(a)]; +}; +_Static_assert(sizeof(struct OtherStructWithAnnotatedFAMTy) == sizeof(struct SmallStructTy), "size mismatch"); + +void param_single_explicit_cast_annotated_fam_member_access_false_neg(struct SmallStructTy* p) { + struct OtherStructWithAnnotatedFAMTy* local = (struct OtherStructWithAnnotatedFAMTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + local->a = 0; + local->b = 0; + + // TODO: We should warn about this because its a guaranteed runtime trap. + // rdar://120566596 + local->buffer[0] = 0; +} + +struct OtherStructWithUnannotatedFAMTy { + int a; + int b; + char buffer[]; +}; +_Static_assert(sizeof(struct OtherStructWithUnannotatedFAMTy) == sizeof(struct SmallStructTy), "size mismatch"); + +void param_single_explicit_cast_unannotated_fam_member_access_false_neg(struct SmallStructTy* p) { + struct OtherStructWithUnannotatedFAMTy* local = (struct OtherStructWithUnannotatedFAMTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + local->a = 0; + local->b = 0; + + // This existing warning already fires so this case doesn't need additional + // support but this test case is here to make sure this doesn't regress. + // expected-warning@+1{{accessing elements of an unannotated incomplete array always fails at runtime}} + local->buffer[0] = 0; +} + +//============================================================================== +// No warnings expected +//============================================================================== + +// Same layout as `SmallStructTy` but not the same type +struct OtherSmallStructTy { + int a; + int b; +}; +_Static_assert(sizeof(struct OtherSmallStructTy) == sizeof(struct SmallStructTy), "size mismatch"); + +void param_single_explicit_cast_same_struct_size(struct SmallStructTy* p, struct OtherSmallStructTy* q) { + struct OtherSmallStructTy* local = (struct OtherSmallStructTy* __bidi_indexable)(struct SmallStructTy* __bidi_indexable) p; + *local = *q; +} + +// Flexible Array Members + +struct SmallFAM { + int count; + char buf[__counted_by(count)]; +}; +_Static_assert(sizeof(struct SmallFAM) == 4, "wrong size"); + +struct BiggerFAM { + int count; + int extra_field; + char buf[__counted_by(count)]; +}; +_Static_assert(sizeof(struct BiggerFAM) == 8, "wrong size"); + +_Static_assert(sizeof(struct SmallFAM) < sizeof(struct BiggerFAM), "expected size diff failed"); + +void param_single_explicit_cast_fam_struct_deref(struct SmallFAM* p) { + struct BiggerFAM* local = (struct BiggerFAM* __bidi_indexable)(struct SmallFAM* __bidi_indexable) p; + // No warn + // Even though `p` is a `__single` when it is assigned to `local` the bounds + // are dynamically computed from `p`'s count field. Thus the compiler can't + // know what the bounds of `local` will be at runtime. + local[0].count = 0; +} diff --git a/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op.c b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op.c new file mode 100644 index 0000000000000..4e1e10ffb28c5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-entity-to-indexable-with-unsafe-op.c @@ -0,0 +1,2021 @@ + + +// -triple arm64-apple-darwin23.2.0 is used because some diagnostic text mentions platform specific type sizes +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fblocks -Wno-gnu-alignof-expression -triple arm64-apple-darwin23.2.0 -verify %s + +#include + +//============================================================================== +// Parameter source of __single +//============================================================================== + +void implicit_unconditional_trap(int *p, int count) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + for (int i = 0; i < count; ++i) + local[i] = 0; // expected-warning{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 1 or < 0}} +} + +// Currently we warn here but we may want to use an explicit cast as a suppression mechanism in the future. +void explicit_single_param(int * __single p, int count) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + for (int i = 0; i < count; ++i) + local[i] = 0; // expected-warning{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 1 or < 0}} +} + +void explicit_single_param_cast(int * __single p, int count) { // expected-note{{pointer 'p' declared here}} + int *local = (int* __bidi_indexable) p; // expected-note{{pointer 'local' initialized here}} + for (int i = 0; i < count; ++i) + local[i] = 0; // expected-warning{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 1 or < 0}} +} + +// Don't duplicate the existing warning for explicitly __bidi_indexable pointers. +void explicit_unconditional_trap(int *p, int count) { // expected-note{{pointer 'p' declared here}} + int *__bidi_indexable local = p; // expected-warning{{initializing type 'int *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'p'}} + for (int i = 0; i < count; ++i) + local[i] = 0; +} + +// Don't duplicate the existing warning for explicitly __bidi_indexable pointers. +void explicit_unconditional_trap_assign(int *p, int count) { // expected-note{{pointer 'p' declared here}} + int *__bidi_indexable local; + local = p; // expected-warning{{assigning to type 'int *__bidi_indexable' from type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'p'}} + for (int i = 0; i < count; ++i) + local[i] = 0; +} + +// The parameter is properly annotated, no need to warn. +void counted_by_param(int *__counted_by(count) p, int count) { + int *local = p; + for (int i = 0; i < count; ++i) + local[i] = 0; // no-warning +} + +// The parameter is properly annotated, no need to warn. +void indexable_param(int *__indexable p, int count) { + int *local = p; + for (int i = 0; i < count; ++i) + local[i] = 0; // no-warning +} + +// The parameter is properly annotated, no need to warn. +void bidi_indexable_param(int *__bidi_indexable p, int count) { + int *local = p; + for (int i = 0; i < count; ++i) + local[i] = 0; // no-warning +} + +// The parameter is properly annotated, no need to warn. +void null_terminated_param(int *__null_terminated p) { + int *local = __unsafe_null_terminated_to_indexable(p); + while (local) { + *local = 1; + ++local; // no-warning + } +} + +void implicit_unconditional_trap_Nonnull_attr(int *p, int count) { // expected-note{{pointer 'p' declared here}} + int * _Nonnull local = p; // expected-note{{pointer 'local' initialized here}} + for (int i = 0; i < count; ++i) + local[i] = 0; // expected-warning{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 1 or < 0}} +} + +void simple_assignment(int *p, int count) { // expected-note{{pointer 'p' declared here}} + int *local; // expected-note{{pointer 'local' declared here}} + local = p; // expected-note{{pointer 'local' assigned here}} + for (int i = 0; i < count; ++i) + local[i] = 0; // expected-warning{{indexing into a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in a trap if the index expression is >= 1 or < 0}} +} + +void reassignment_after_indexing(int *p, int count) { // expected-note{{pointer 'p' declared here}} + // expected-note@+1{{__single parameter 'p' assigned to 'local' here}} + int *local = p; // expected-note{{pointer 'local' declared here}} + int *__single something_else; // expected-note{{pointer 'something_else' declared here}} + for (int i = 0; i < count; ++i) { + // expected-warning@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap if the index expression is >= 1 or < 0}} + local[i] = 0; + local = something_else; // expected-note{{__single local variable 'something_else' assigned to 'local' here}} + } +} + +void reassignment_after_indexing_no_warn(int *p, int count) { + int *local = p; // no-warning + int *__bidi_indexable something_else; + for (int i = 0; i < count; ++i) { + local[i] = 0; + local = something_else; // This value isn't a __single + } +} + +void unary_op(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + ++local; // expected-warning-re{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} +} + +void unary_op2(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + --local; // expected-warning-re{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} +} + +void unary_op3(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + local++; // expected-warning-re{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} +} + +void unary_op4(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + local--; // expected-warning-re{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} +} + + +void binary_op(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + *(local + 2) = 3; // expected-warning{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} +} + +void binary_op2(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + *(2 + local) = 3; // expected-warning{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} +} + +void binary_op3(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + int* new_p = local + 2; +} + +void binary_op4(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + *(local - 1) = 3; // expected-warning{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} +} + + +void binary_op_non_constant_offset(int *p, int offset) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is >= 1 or < 0}} + int* new_p = local + offset; +} + +void binary_op_constant_offset(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; + int* new_p; + new_p = local + 0; // No warning + new_p = 0 + local; // No warning + + int *local2 = p; // expected-note{{pointer 'local2' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + new_p = local2 + 1; +} + + +void binary_op_compound_constant_offset(int *p) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + local += 2; // expected-warning{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} +} + +void binary_op_compound_variable_index(int *p, int offset) { // expected-note{{pointer 'p' declared here}} + int *local = p; // expected-note{{pointer 'local' initialized here}} + local += offset; // expected-warning{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is >= 1 or < 0}} +} + +void constant_index(int* p) { // expected-note 2 {{pointer 'p' declared here}} + int* local = p; // expected-note 2 {{pointer 'local' initialized here}} + local[0] = 5; // No warning + local[(int) 0] = 6; // No warning + local[((int) 0)] = 6; // No warning + local[1+2+3 - 6] = 6; // No warning + *local = 10; // No warning + + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 6; + + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1+2+3 -5] = 6; +} + +void param_single_paren_used_initialized_arith(int* p) { // expected-note{{pointer 'p' declared here}} + int* (local) = (p); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + (++local); +} + +void param_single_paren_used_initialized_arith2(int* p) { // expected-note{{pointer 'p' declared here}} + int* (local) = (p); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + (local) += 1; +} + +void param_single_paren_used_assign_arith(int* p) { // expected-note{{pointer 'p' declared here}} + int* (local); // expected-note{{pointer 'local' declared here}} + (local) = (p); // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in an out-of-bounds pointer}} + (++local); +} + +void param_single_paren_used_assign_idx(int* p) { // expected-note{{pointer 'p' declared here}} + int* (local); // expected-note{{pointer 'local' declared here}} + (local) = (p); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +void param_single_paren_used_assign_idx_cast(void* p) { // expected-note{{pointer 'p' declared here}} + int* (local); // expected-note{{pointer 'local' declared here}} + (local) = ((int*) p); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +void param_single_paren_used_assign_idx_cast2(void* p) { // expected-note{{pointer 'p' declared here}} + int* (local); // expected-note{{pointer 'local' declared here}} + (local) = (int*) (p); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +void param_single_paren_used_assign_idx_cast3(void* p) { // expected-note{{pointer 'p' declared here}} + int* (local); // expected-note{{pointer 'local' declared here}} + (local) = ((int*) ((int*) p)); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +struct UnsizedType; +struct SizedType { + int foo; +}; + +void warn_param_incomplete_type_to_sized(struct UnsizedType* p) { // expected-note{{pointer 'p' declared here}} + struct SizedType* local = (struct SizedType*) p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1].foo = 0; +} + +void param_single_explicit_bidi_cast(int* p) { // expected-note{{pointer 'p' declared here}} + int* local = (int* __bidi_indexable) p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +typedef long long int BigTy; +typedef int SmallerTy; + +_Static_assert(sizeof(BigTy) > sizeof(SmallerTy), "expected size diff failed"); +void param_single_explicit_cast_type_change_second_cast_to_bigger(BigTy* p) { + // We should not warn here because `local` gets the bounds of a single + // `BigTy`, not the bounds of a single `SmallerTy`. This is because + // the BoundsSafetyPointerCast will create the bounds from BigTy and + // then the BitCast (which does not affect the stored bounds) to SmallerTy + // happens afterwards. This can be seen from the AST: + // + // + // `-CStyleCastExpr 'SmallerTy *__bidi_indexable' + // `-CStyleCastExpr 'BigTy *__bidi_indexable' + // `-ImplicitCastExpr 'BigTy *__single' part_of_explicit_cast + // `-DeclRefExpr 'BigTy *__single' lvalue ParmVar 'p' 'BigTy *__single' + // + // + // if sizeof(BigTy) == 8 and sizeof(SmallerTy) == 4, then its safe to index + // local[0] and local[1] + // + SmallerTy* local = (SmallerTy* __bidi_indexable)(BigTy* __bidi_indexable) p; + local[1] = 0; // No warn +} + +void param_single_explicit_cast_type_change_second_cast_to_smaller(SmallerTy* p) { // expected-note{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but 'local' has pointee type 'BigTy' (aka 'long long') (8 bytes}} + BigTy* local = (BigTy* __bidi_indexable)(SmallerTy* __bidi_indexable) p; + // expected-warning@+1{{indexing __bidi_indexable 'local' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local' is assigned a __single pointer that results in 'local' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + local[1] = 0; +} + +void param_single_explicit_cast_to_larger_and_attr_change(SmallerTy* p) { // expected-note{{pointer 'p' declared her}} + // This example traps at runtime because the `local` gets the bounds of a + // single `BigTy`. + // + // `-CStyleCastExpr 0x12e02a528 'BigTy *__bidi_indexable' + // `-ImplicitCastExpr 0x12e02a510 'BigTy *__single' part_of_explicit_cast + // `-ImplicitCastExpr 0x12e02a498 'SmallerTy *__single' part_of_explicit_cast + // `-DeclRefExpr 0x12e02a460 'SmallerTy *__single' lvalue ParmVar 0x12d90b008 'p' 'SmallerTy *__single' + // + BigTy* local = (BigTy* __bidi_indexable) p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wbounds-safety-single-to-indexable-bounds-truncated" +void param_single_explicit_cast_to_smaller_and_attr_change(BigTy* p) { // expected-note{{pointer 'p' declared her}} + // This example traps at runtime because the `(SmallerTy* __bidi_indexable)` explicit + // cast does the BitCast first, then the BoundsSafetyPointerCast. So `local` gets + // the bounds of a single `SmallerTy`. + // + // `-CStyleCastExpr 'int *__bidi_indexable' + // `-ImplicitCastExpr 'int *__single' part_of_explicit_cast + // `-ImplicitCastExpr 'long long *__single' part_of_explicit_cast + // `-DeclRefExpr 'long long *__single' lvalue ParmVar 0x135115908 'p' 'long long *__single' + // + SmallerTy* local = (SmallerTy* __bidi_indexable) p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincompatible-pointer-types" +void param_single_implicit_cast_to_smaller_and_attr_change(BigTy* p) { // expected-note{{pointer 'p' declared here}} + // This example traps the BitCast happens before the BoundsSafetyPointerCast so + // the wide pointer gets the bounds of a single `SmallerTy`. + // + // `-ImplicitCastExpr 'SmallerTy *__bidi_indexable' + // `-ImplicitCastExpr 'int *__single' + // `-ImplicitCastExpr 'BigTy *__single' + // `-DeclRefExpr 'BigTy *__single' lvalue ParmVar 'p' 'BigTy *__single' + // + SmallerTy* local = p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +void param_single_implicit_cast_to_larger_and_attr_change(SmallerTy* p) { // expected-note{{pointer 'p' declared here}} + // This example traps the BitCast happens before the BoundsSafetyPointerCast so + // the wide pointer gets the bounds of a single `BigTy`. + // + // `-VarDecl col:10 used local 'BigTy *__bidi_indexable' cinit + // `-ImplicitCastExpr 'BigTy *__bidi_indexable' + // `-ImplicitCastExpr 'long long *__single' + // `-ImplicitCastExpr 'SmallerTy *__single' + // `-DeclRefExpr 'SmallerTy *__single' lvalue ParmVar 'p' 'SmallerTy *__single' + // + BigTy* local = p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +struct SimpleType { + int member; +}; + +struct SameLayoutAsSimpleType { + int member; +}; +_Static_assert(sizeof(struct SimpleType) == sizeof(struct SameLayoutAsSimpleType), "Expected types to have same size"); + +void param_implicit_cast_to_type_with_same_layout(struct SimpleType* p) { // expected-note{{pointer 'p' declared here}} + // Check that even though SimpleType and SameLayoutAsSimpleType are not the same + // that a warning is still emitted. + // + struct SameLayoutAsSimpleType* local = p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1].member = 0; +}; + +void param_explicit_cast_to_type_with_same_layout(struct SimpleType* p) { // expected-note{{pointer 'p' declared here}} + // Check that even though SimpleType and SameLayoutAsSimpleType are not the same + // that a warning is still emitted. + // + struct SameLayoutAsSimpleType* local = (struct SameLayoutAsSimpleType*) p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1].member = 0; +}; + + +#pragma clang diagnostic pop +#pragma clang diagnostic pop + +void param_multiple_fb_cast(BigTy* p) { // expected-note{{pointer 'p' declared here}} + // Check we still warn when there are multiple BoundsSafetyPointerCast. + // + // This example traps because only the top BoundsSafetyPointerCast matters. + // `local` gets the bounds of a single `SmallerTy`. + // + // `-CStyleCastExpr 'SmallerTy *__bidi_indexable' + // `-CStyleCastExpr 'SmallerTy *__single' + // `-ImplicitCastExpr 'SmallerTy *__bidi_indexable' part_of_explicit_cast + // `-CStyleCastExpr 'BigTy *__bidi_indexable' + // `-ImplicitCastExpr 'BigTy *__single' part_of_explicit_cast + // `-DeclRefExpr 'BigTy *__single' lvalue ParmVar 'p' 'BigTy *__single' + // + SmallerTy* local = (SmallerTy* __bidi_indexable)(SmallerTy* __single) (BigTy* __bidi_indexable) p; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + + +// expected-note@+2{{pointer 'p' declared here}} +// expected-note@+1{{pointer 'q' declared here}} +void param_multiple_singles_idx(int* p, int* q) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = p; // expected-note{{__single parameter 'p' assigned to 'local' here}} + local = q; // expected-note{{__single parameter 'q' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +// expected-note@+2{{pointer 'p' declared here}} +// expected-note@+1{{pointer 'q' declared here}} +void param_multiple_singles_arith(int* p, int* q) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = p; // expected-note{{__single parameter 'p' assigned to 'local' here}} + local = q; // expected-note{{__single parameter 'q' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + +void param_multiple_assignments_with_branches(int b, + // expected-note@+1{{pointer 'p' declared here}} + int* p, + // expected-note@+1{{pointer 'q' declared here}} + int* q) { + + int* local; // expected-note{{pointer 'local' declared here}} + if (b) + local = p; // expected-note{{__single parameter 'p' assigned to 'local' here}} + else + local = q; // expected-note{{__single parameter 'q' assigned to 'local' here}} + + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +//============================================================================== +// Pointer arithmetic with mismatched pointees +//============================================================================== +typedef char EvenSmallerTy; +_Static_assert(sizeof(EvenSmallerTy) < sizeof(SmallerTy), "unexpected type size diff"); +void param_binary_mismatched_pointee_signed_variable_offset(SmallerTy* p, int off) { // expected-note 6{{pointer 'p' declared here}} + // expected-note@+3 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + // expected-note@+2 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{pointer 'local' declared here}} + SmallerTy* local = p; + + // Warn even though these could be a false positives because we don't know the + // value of `offset` at compile time. The warnings try to workaround that by + // explaining what values of `offset` create an out-of-bounds pointer. + // + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is >= 4 or < 0}} + *((EvenSmallerTy*)local + off) = 0; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is >= 4 or < 0}} + *((EvenSmallerTy*)local - off) = 0; + + // Warn about the arithmetic because any offset creates an out-of-bounds pointer + // due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local + off) = 0; + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local - off) = 0; +} + +void param_binary_mismatched_pointee_signed_variable_offset_multiple_single_entities( + SmallerTy* p, // expected-note 6{{pointer 'p' declared here}} + SmallerTy* q, // expected-note 6{{pointer 'q' declared here}} + int cond, int off) { + + // expected-note@+3 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + // expected-note@+2 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 6{{pointer 'local' declared here}} + SmallerTy* local = p; + if (cond) + // expected-note@+2 2{{__single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 4{{__single parameter 'q' assigned to 'local' here}} + local = q; + + // Warn even though these could be a false positives because we don't know the + // value of `offset` at compile time. The warnings try to workaround that by + // explaining what values of `offset` create an out-of-bounds pointer. + // + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer if the offset is >= 4 or < 0}} + *((EvenSmallerTy*)local + off) = 0; + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer if the offset is >= 4 or < 0}} + *((EvenSmallerTy*)local - off) = 0; + + // Warn about the arithmetic because any offset creates an out-of-bounds pointer + // due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning-re@+2{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local + off) = 0; + // expected-warning-re@+2{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local - off) = 0; +} + +void param_binary_mismatched_pointee_unsigned_variable_offset(SmallerTy* p, unsigned int off) { // expected-note 6{{pointer 'p' declared here}} + // expected-note@+3 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes}} + // expected-note@+2 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{pointer 'local' declared here}} + SmallerTy* local = p; + + // Warn even though this could be a false positive because we don't know the + // value of `offset` at compile time. The warning tries to workaround that by + // explaining which values of `offset` create an out-of-bounds pointer. + // + // expected-warning-re@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is >= 4{{$}}}} + *((EvenSmallerTy*)local + off) = 0; + + // Warn even though this could be a false positive when `offset == 0`. + // expected-warning-re@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is < 0{{$}}}}} + *((EvenSmallerTy*)local - off) = 0; + + // Warn about the arithmetic because any offset creates an out-of-bounds pointer + // due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local + off) = 0; + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local - off) = 0; +} + +void param_binary_mismatched_pointee_unsigned_variable_offset_multiple_single_entities( + SmallerTy* p, // expected-note 6{{pointer 'p' declared here}} + SmallerTy* q, // expected-note 6{{pointer 'q' declared here}} + int cond, unsigned int off) { + // expected-note@+3 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + // expected-note@+2 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 6{{pointer 'local' declared here}} + SmallerTy* local = p; + if (cond) + // expected-note@+2 2{{__single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + // expected-note@+1 4{{_single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local = q; + + // Warn even though these could be a false positives because we don't know the + // value of `offset` at compile time. The warnings try to workaround that by + // explaining which values of `offset` create an out-of-bounds pointer. + // + // expected-warning-re@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer if the offset is >= 4{{$}}}} + *((EvenSmallerTy*)local + off) = 0; + // expected-warning-re@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer if the offset is < 0{{$}}}} + *((EvenSmallerTy*)local - off) = 0; + + // Warn about the arithmetic because any offset creates an out-of-bounds pointer + // due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning-re@+2{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local + off) = 0; + // expected-warning-re@+2{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local - off) = 0; +} + +void param_binary_mismatched_pointee_const_offset(SmallerTy* p) { // expected-note 12{{pointer 'p' declared here}} + // expected-note@+3 8{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + // expected-note@+2 4{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{pointer 'local' declared here}} + SmallerTy* local = p; + const int signedOne = 1; + const int signedFour = 4; + + // Positive or zero effective offsets that won't generate an out-of-bounds pointer + _Static_assert(sizeof(SmallerTy) == 4, "unexpected size"); + *((EvenSmallerTy*)local + 0) = 0; + *((EvenSmallerTy*)local + 1) = 0; + *((EvenSmallerTy*)local + 2) = 0; + *((EvenSmallerTy*)local + 3) = 0; + *((EvenSmallerTy*)local + signedOne) = 0; + + *((EvenSmallerTy*)local - -0) = 0; + *((EvenSmallerTy*)local - -1) = 0; + *((EvenSmallerTy*)local - -2) = 0; + *((EvenSmallerTy*)local - -3) = 0; + + // Positive effective offsets that will generate an out-of-bounds pointer + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local + 4) = 0; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local - -4) = 0; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local + signedFour) = 0; + + // Negative effective offsets that will generate an out-of-bounds pointer + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local -1) = 0; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local + -1) = 0; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local - signedOne) = 0; + + // Large negative constant + const long long int MostNegativeValue = 0x8000000000000000UL; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local + MostNegativeValue) = 0; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *((EvenSmallerTy*)local - MostNegativeValue) = 0; + + // Warn about the arithmetic because any offset creates an out-of-bounds pointer + // due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local + 0) = 0; + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *((BigTy*)local - 0) = 0; +} + +void param_unary_mismatched_pointee(SmallerTy* p) { // expected-note 4{{pointer 'p' declared here}} + SmallerTy* local = p; + + // These constructs are forbidden. They are commented out because their + // presence seems to prevent emission of subsequent diagnostics. + // error: assignment to cast is illegal, lvalue casts are not supported + // ((char*) local)++; + // ++((char*) local); + // ((char*) local)--; + // --((char*) local); + // ((char*) local) += 1; + // ((char*) local) -= 1; + + // The pointee type of the `p` and `local2` do not match + // sizeof(*p) > sizeof(*local2) + EvenSmallerTy* local2 = (EvenSmallerTy* __bidi_indexable)(SmallerTy* __bidi_indexable) p; + + // Don't warn. A single increment won't go out-of-bounds. + ++local2; + --local2; + local2++; + local2--; + + // There's a false negative here. The analysis isn't tracking the value + // of pointers as execution progresses so there's no way to detect this right + // now. + ++local2; + ++local2; + ++local2; // `local2` is now out-of-bounds here. + + // The pointee type of the `p` and `local3` do not match + // sizeof(*p) < sizeof(*local3). So this pointer points to partially + // out-of-bounds memory. + // + // expected-note@+1 4{{pointer 'local3' initialized here}} + BigTy* local3 = (BigTy* __bidi_indexable)(SmallerTy* __bidi_indexable) p; + + // expected-warning-re@+1{{pointer arithmetic over a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} + ++local3; + // expected-warning-re@+1{{pointer arithmetic over a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} + --local3; + // expected-warning-re@+1{{pointer arithmetic over a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} + local3++; + // expected-warning-re@+1{{pointer arithmetic over a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in an out-of-bounds pointer{{$}}}} + local3--; +} + +void param_unary_mismatched_pointee_multiple_single_entities(SmallerTy* p, // expected-note 4{{pointer 'p' declared here}} + SmallerTy* q, // expected-note 4{{pointer 'q' declared here}} + int condition) { + SmallerTy* local = p; + + if (condition) + local = q; + + // These constructs are forbidden. They are commented out because their + // presence seems to prevent emission of subsequent diagnostics. + // error: assignment to cast is illegal, lvalue casts are not supported + // ((char*) local)++; + // ++((char*) local); + // ((char*) local)--; + // --((char*) local); + // ((char*) local) += 1; + // ((char*) local) -= 1; + + // The pointee type of the `p` and `local2` do not match + // sizeof(*p) > sizeof(*local2) + EvenSmallerTy* local2 = (EvenSmallerTy* __bidi_indexable)(SmallerTy* __bidi_indexable) p; + + if (condition) + local2 = (EvenSmallerTy* __bidi_indexable)(SmallerTy* __bidi_indexable) q; + + // Don't warn. A single increment won't go out-of-bounds. + ++local2; + --local2; + local2++; + local2--; + + // There's a false negative here. The analysis isn't tracking the value + // of pointers as execution progresses so there's no way to detect this right + // now. + ++local2; + ++local2; + ++local2; // `local2` is now out-of-bounds here. + + // The pointee type of the `p` and `local3` do not match + // sizeof(*p) < sizeof(*local3). So this pointer points to partially + // out-of-bounds memory. + // + // expected-note@+2 4{{pointer 'local3' declared here}} + // expected-note@+1 4{{__single parameter 'p' assigned to 'local3' here}} + BigTy* local3 = (BigTy* __bidi_indexable)(SmallerTy* __bidi_indexable) p; + + if (condition) + // expected-note@+1 4{{__single parameter 'q' assigned to 'local3' here}} + local3 = (BigTy* __bidi_indexable)(SmallerTy* __bidi_indexable) q; + + // expected-warning-re@+1{{pointer arithmetic over __bidi_indexable local variable 'local3' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + ++local3; + // expected-warning-re@+1{{pointer arithmetic over __bidi_indexable local variable 'local3' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + --local3; + // expected-warning-re@+1{{pointer arithmetic over __bidi_indexable local variable 'local3' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + local3++; + // expected-warning-re@+1{{pointer arithmetic over __bidi_indexable local variable 'local3' that is assigned from a __single pointer results in an out-of-bounds pointer{{$}}}} + local3--; +} + + +// Original reproducer for rdar://122055103 +typedef struct Header { + int count; +} Header_t; +typedef struct Nested { + Header_t header; + int more_data; +} Nested_t; + +void my_memset(void*__sized_by(size), int value, unsigned long long size); + +void test(Nested_t* p) { + Nested_t* local = p; + // There should be no warning here. This pointer arithmetic is in-bounds. + my_memset((char*) local + sizeof(Header_t), 0, sizeof(Nested_t) - sizeof(Header_t)); +} + +//============================================================================== +// Indexing with mismatched pointees +//============================================================================== +typedef char EvenSmallerTy; +_Static_assert(sizeof(EvenSmallerTy) < sizeof(SmallerTy), "unexpected type size diff"); +void param_idx_mismatched_pointee_signed_variable_offset(SmallerTy* p, int off) { // expected-note 6{{pointer 'p' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local = p; + + // Warn even though these could be a false positives because we don't know the + // value of `off` at compile time. The warnings try to workaround that by + // explaining what values of `off` create an out-of-bounds pointer. + // + // expected-warning@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 4 or < 0}} + ((EvenSmallerTy*)local)[off] = 0; + // expected-warning@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 4 or < 0}} + ((EvenSmallerTy*)local)[-off] = 0; + + // expected-note@+3 4{{pointer 'local2' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local2 = p; + + // Warn about the arithmetic because any offset creates an out-of-bounds + // pointer due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[off] = 0; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[-off] = 0; +} + + +void param_idx_mismatched_pointee_signed_variable_offset_multiple_assignees( + SmallerTy* p, // expected-note 6{{pointer 'p' declared here}} + SmallerTy* q, // expected-note 6{{pointer 'q' declared here}} + int cond, + int off) { + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local = p; + if (cond) + // expected-note@+1 2{{__single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + local = q; + + // Warn even though these could be a false positives because we don't know the + // value of `off` at compile time. The warnings try to workaround that by + // explaining what values of `off` create an out-of-bounds pointer. + // + // expected-warning@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap if the index expression is >= 4 or < 0}} + ((EvenSmallerTy*)local)[off] = 0; + // expected-warning@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap if the index expression is >= 4 or < 0}} + ((EvenSmallerTy*)local)[-off] = 0; + + // expected-note@+3 4{{pointer 'local2' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local2 = p; + if (cond) + // expected-note@+2 2{{__single parameter 'q' assigned to 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'q' assigned to 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local2 = q; + + // Warn about the arithmetic because any offset creates an out-of-bounds + // pointer due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[off] = 0; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[-off] = 0; +} + +void param_idx_mismatched_pointee_unsigned_variable_offset(SmallerTy* p, unsigned off, unsigned long long off2) { // expected-note 7{{pointer 'p' declared here}} + // expected-note@+1 3{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local = p; + + // Warn even though these could be a false positives because we don't know the + // value of `off` at compile time. The warnings try to workaround that by + // explaining what values of `off` create an out-of-bounds pointer. + // + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 4{{$}}}} + ((EvenSmallerTy*)local)[off] = 0; + + // The warning text is a little surprising at first glance. One might expect + // it to say `if the index expression is < 0`. However, the result of + // negating an unsigned type is still an unsigned type so the warning only + // says `>= 4`. So while the programmer probably intended a negative offset + // they will likely get a larger offset than intended. + // + // For the first case `-off` will get zero extended after being negated which + // can result in a large offset. E.g. if `off` is `1` `-off` becomes 2**32 -1 + // (`-1` ins 32-bit two's complement). + // + // For the second case `-off` will not get extended after being negated. This + // can result in an even larger offset. E.g. if `off` is `1` `-off` becomes + // 2**64 -1 (`-1` in 64-bit two's complement) + + // Ideally clang should warn about code like this but it currently doesn't + // (rdar://123416393). + // + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 4{{$}}}} + ((EvenSmallerTy*)local)[-off] = 0; + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 4{{$}}}} + ((EvenSmallerTy*)local)[-off2] = 0; + + + // expected-note@+3 4{{pointer 'local2' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local2 = p; + + // Warn about the arithmetic because any offset creates an out-of-bounds + // pointer due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[off] = 0; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[-off] = 0; +} + +void param_idx_mismatched_pointee_unsigned_variable_offset_multiple_assignees( + SmallerTy* p, // expected-note 6{{pointer 'p' declared here}} + SmallerTy* q, // expected-note 6{{pointer 'q' declared here}} + int cond, + unsigned off) { + // expected-note@+2 2{{pointer 'local' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local = p; + if (cond) + // expected-note@+1 2{{__single parameter 'q' assigned to 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + local = q; + + // Warn even though these could be a false positives because we don't know the + // value of `off` at compile time. The warnings try to workaround that by + // explaining what values of `off` create an out-of-bounds pointer. + // + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap if the index expression is >= 4{{$}}}} + ((EvenSmallerTy*)local)[off] = 0; + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap if the index expression is >= 4{{$}}}} + ((EvenSmallerTy*)local)[-off] = 0; + + // expected-note@+3 4{{pointer 'local2' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes}} + SmallerTy* local2 = p; + if (cond) + // expected-note@+2 2{{__single parameter 'q' assigned to 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'q' assigned to 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + local2 = q; + + // Warn about the arithmetic because any offset creates an out-of-bounds + // pointer due to `sizeof(BigTy) > sizeof(SmallerTy)` + // + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[off] = 0; + // expected-warning@+2{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + // expected-warning@+1{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + ((BigTy*)local2)[-off] = 0; +} + +void param_idx_mismatched_pointee_const_offset(SmallerTy* p) { // expected-note 10{{pointer 'p' declared here}} + // expected-note@+1 6{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local = p; + const int signedOne = 1; + const int signedFour = 4; + + // Positive or zero effective offsets that won't generate an out-of-bounds pointer + _Static_assert(sizeof(SmallerTy) == 4, "unexpected size"); + ((EvenSmallerTy*)local)[0] = 0; + ((EvenSmallerTy*)local)[1] = 0; + ((EvenSmallerTy*)local)[2] = 0; + ((EvenSmallerTy*)local)[3] = 0; + ((EvenSmallerTy*)local)[signedOne] = 0; + ((EvenSmallerTy*)local)[-0] = 0; + + // Positive effective offsets that will generate an out-of-bounds pointer + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + ((EvenSmallerTy*)local)[4] = 0; + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + ((EvenSmallerTy*)local)[signedFour] = 0; + + // Negative effective offsets that will generate an out-of-bounds pointer + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + ((EvenSmallerTy*)local)[-1] = 0; + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + ((EvenSmallerTy*)local)[-signedOne] = 0; + + // Large negative constant + const long long int MostNegativeValue = 0x8000000000000000UL; + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap{{$}}}} + ((EvenSmallerTy*)local)[MostNegativeValue] = 0; + // expected-warning-re@+3{{overflow in expression; result is {{.+}} with type 'long long'}} + // expected-warning@+2{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in a trap if the index expression is >= 4 or < 0}} + // Note: When overflow happens Clang fails to evaluate the index expression as a constant + ((EvenSmallerTy*)local)[-MostNegativeValue] = 0; + + // expected-note@+3 4{{pointer 'local2' declared here}} + // expected-note@+2 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes}} + SmallerTy* local2 = p; + + // Warn about the arithmetic because any offset creates an out-of-bounds pointer + // due to `sizeof(BigTy) > sizeof(SmallerTy)` + // expected-warning@+2{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + ((BigTy*)local2)[0] = 0; + // expected-warning@+2{{indexing __bidi_indexable '((BigTy *)local2)' at any index will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + ((BigTy*)local2)[-0] = 0; +} + +//============================================================================== +// Dereferencing with mismatched pointees +//============================================================================== + +void param_deref_mismatched_pointee_smaller(SmallerTy* p, int off) { // expected-note 2{{pointer 'p' declared here}} + SmallerTy* local = p; + *((EvenSmallerTy* __single) local) = 0; + *((EvenSmallerTy*) local) = 0; + + // Check that we don't emit any warnings about the dereferencing + *(((EvenSmallerTy*) local) + 0) = 0; + *(((EvenSmallerTy*) local) + 1) = 0; + *(((EvenSmallerTy*) local) + 2) = 0; + *(((EvenSmallerTy*) local) + 3) = 0; + + // expected-note@+1{{_single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local2 = p; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + *(((EvenSmallerTy*) local2) + 4) = 0; + + // expected-note@+1{{__single parameter 'p' used to initialize 'local3' here results in 'local3' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local3' to 'EvenSmallerTy *__bidi_indexable' (aka 'char *__bidi_indexable') has pointee type 'EvenSmallerTy' (aka 'char') (1 bytes)}} + SmallerTy* local3 = p; + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in an out-of-bounds pointer if the offset is >= 4 or < 0}} + *(((EvenSmallerTy*) local3) + off) = 0; +} + +void param_deref_mismatched_pointee_larger(SmallerTy* p, int off) { // expected-note 7{{pointer 'p' declared here}} + // expected-note@+2{{pointer 'local' declared here}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local' here results in 'local' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local' to 'BigTy *__single' (aka 'long long *__single') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local = p; + // No warning about dereference as its dereferencing a __single. + // expected-warning@+1{{explicit cast of out-of-bounds __bidi_indexable to __single will trap in a future compiler version due to the bounds of 'local' being too small to access a single element of type 'BigTy' (aka 'long long')}} + *((BigTy* __single) local) = 0; + + // expected-note@+3 2{{pointer 'local2' declared here}} + // expected-note@+2{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but '((BigTy *)local2)' has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + // expected-note@+1{{__single parameter 'p' used to initialize 'local2' here results in 'local2' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local2' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local2 = p; + // expected-warning@+2{{dereferencing __bidi_indexable '((BigTy *)local2)' will access out-of-bounds memory and will trap in a future compiler version. At runtime 'local2' is assigned a __single pointer that results in '((BigTy *)local2)' having bounds smaller than a single 'BigTy' (aka 'long long') (8 bytes)}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local2' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may tra}} + *((BigTy*) local2) = 0; + + // expected-note@+2{{pointer 'local3' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local3' here results in 'local3' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local3' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local3 = p; + // No warning about dereference: There's no warning because the current + // implementation only warns if the operand of the dereference is a DeclRefExpr + // after walking through all the casts. This behavior is probably fine because + // there are already multiple warnings about the bad use of `local3`. + // + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local3' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *(((BigTy*) local3) + 0) = 0; + + // expected-note@+2{{pointer 'local4' declared here}} + // expected-note@+1 2{{__single parameter 'p' used to initialize 'local4' here results in 'local4' having the bounds of a single 'SmallerTy' (aka 'int') (4 bytes) but cast of 'local4' to 'BigTy *__bidi_indexable' (aka 'long long *__bidi_indexable') has pointee type 'BigTy' (aka 'long long') (8 bytes)}} + SmallerTy* local4 = p; + // No warning about dereference: There's no warning because the current + // implementation only warns if the operand of the dereference is a DeclRefExpr + // after walking through all the casts. This behavior is probably fine because + // there are already multiple warnings about the bad use of `local4` + // + // expected-warning@+2{{pointer arithmetic over a __bidi_indexable local variable 'local4' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + // expected-warning@+1{{explicit cast of __bidi_indexable 'local4' to a larger pointee type creates an out-of-bounds pointer. Later uses of the result may trap}} + *(((BigTy*) local4) + off) = 0; +} + +//============================================================================== +// Unevaluated contexts +//============================================================================== + +int* __bidi_indexable bidi_source(void); + +void unevaluated_context_sizeof(int *p) { // expected-note{{pointer 'p' declared here}} + int* local = p; + int y = sizeof(local[1]); + + // This is very subtle but this checks that variable assignments are ignored in an unevaluated context. + // if the the assignment to `local2` in `sizeof()` is ignored then the analysis only sees one assignment + // to `local2` which allows the warning at `local2[1]` to fire. If the assignment to `local2` is not ignored + // then the warning won't fire. + int* local2 = p; // expected-note{{pointer 'local2' initialized here}} + int tmp = sizeof(local2 = bidi_source()); // expected-warning{{expression with side effects has no effect in an unevaluated context}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in a trap{{$}}}} + int z = local2[1]; +} + +void unevaluated_context_alignof(int *p) { // expected-note{{pointer 'p' declared here}} + int* local = p; + int y = _Alignof(local[1]); + + // This is very subtle but this checks that variable assignments are ignored in an unevaluated context. + // if the the assignment to `local2` in `_Alignof()` is ignored then the analysis only sees one assignment + // to `local2` which allows the warning at `local2[1]` to fire. If the assignment to `local2` is not ignored + // then the warning won't fire. + int* local2 = p; // expected-note{{pointer 'local2' initialized here}} + int tmp = _Alignof(local2 = bidi_source()); // expected-warning{{expression with side effects has no effect in an unevaluated context}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in a trap{{$}}}} + int z = local2[1]; +} + +void unevaluated_context___alignof(int *p) { // expected-note{{pointer 'p' declared here}} + int* local = p; + int y = __alignof(local[1]); + + // This is very subtle but this checks that variable assignments are ignored in an unevaluated context. + // if the the assignment to `local2` in `__alignof()` is ignored then the analysis only sees one assignment + // to `local2` which allows the warning at `local2[1]` to fire. If the assignment to `local2` is not ignored + // then the warning won't fire. + int* local2 = p; // expected-note{{pointer 'local2' initialized here}} + int tmp = __alignof(local2 = bidi_source()); // expected-warning{{expression with side effects has no effect in an unevaluated context}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in a trap{{$}}}} + int z = local2[1]; +} + +void unevaluated_context_typeof(int *p) { // expected-note{{pointer 'p' declared here}} + int* local = p; + typeof(local[1]) y = 0; + + // This is very subtle but this checks that variable assignments are ignored in an unevaluated context. + // if the the assignment to `local2` in `__typeof()` is ignored then the analysis only sees one assignment + // to `local2` which allows the warning at `local2[1]` to fire. If the assignment to `local2` is not ignored + // then the warning won't fire. + int* local2 = p; // expected-note{{pointer 'local2' initialized here}} + typeof(local2 = bidi_source()) tmp = 0; + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in a trap{{$}}}} + int z = local2[1]; +} + +void unevaluated_context__Generic(int *p) { // expected-note 3{{pointer 'p' declared here}} + // No warning should fire here + int* local = p; + int tmp = _Generic(local[1], // Not evaluated + int:5, // Only this expr should be evaluated + char: local[1], // Not evaluated + default: local[1] // Not evaluated + ); + + // A warning should fire here + int* local2 = p; // expected-note{{pointer 'local2' initialized here}} + int tmp2 = _Generic(local2[1], // Not evaluated + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local2' initialized from __single parameter 'p' results in a trap{{$}}}} + int: local2[1], // Only this expr should be evaluated + char: 5, // Not evaluated + default: 5); // Not evaluated + + // A warning should fire here + int* local3 = p; // expected-note{{pointer 'local3' initialized here}} + int tmp3 = _Generic(local2[1], // Not evaluated + char: 5, // Not evaluated + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local3' initialized from __single parameter 'p' results in a trap{{$}}}} + default: local3[1]); // Only this expr should be evaluated + + + // This is very subtle but this checks that variable assignments are ignored in an unevaluated context. + // if the the assignment to `local4` in `__Generic` is ignored then the analysis only sees one assignment + // to `local4` which allows the warning at `loca42[1]` to fire. If the assignment to `local4` is not ignored + // then the warning won't fire. + int* local4 = p; // expected-note{{pointer 'local4' initialized here}} + int tmp4; + _Generic(5, + int: tmp4 = 0, + default: local4 = bidi_source() + ); + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local4' initialized from __single parameter 'p' results in a trap{{$}}}} + int z = local4[1]; +} + +//============================================================================== +// Blocks +//============================================================================== + +// In some senses this is a false positive because the block is never called. +void decl_block(void) { + int (^blk)(int*) = ^(int* x) { // expected-note{{pointer 'x' declared here}} + int* block_local = x; // expected-note{{pointer 'block_local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'block_local' initialized from __single parameter 'x' results in a trap{{$}}}} + return block_local[1]; + }; +} + +void decl_block_with_call(void) { + int* __single p = 0; + int (^blk)(int*) = ^(int* x) { // expected-note{{pointer 'x' declared here}} + int* block_local = x; // expected-note{{pointer 'block_local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'block_local' initialized from __single parameter 'x' results in a trap{{$}}}} + return block_local[1]; + }; + blk(p); +} + +void block_capture(int* p) { // expected-note{{pointer 'p' declared here}} + int (^blk)(void) = ^(void) { + int* block_local = p; // expected-note{{pointer 'block_local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'block_local' initialized from __single parameter 'p' results in a trap{{$}}}} + return block_local[1]; + }; +} + +void block_capture2(int* p) { // expected-note{{pointer 'p' declared here}} + int* __block f_local; // expected-note{{pointer 'f_local' declared here}} + int (^blk)(void) = ^(void) { + f_local = p; // expected-note{{'f_local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'f_local' assigned from __single parameter 'p' results in a trap{{$}}}} + return f_local[1]; + }; +} + +void block_capture3(void) { + int* __single p; // expected-note{{pointer 'p' declared here}} + int (^blk)(void) = ^(void) { + int* block_local = p; // expected-note{{pointer 'block_local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'block_local' initialized from __single local variable 'p' results in a trap{{$}}}} + return block_local[1]; + }; +} + +void block_local(int* p) { // expected-note{{pointer 'p' declared here}} + int* __block local; // expected-note{{pointer 'local' declared here}} + local = p; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in a trap{{$}}}} + local[1] = 0; +} + +//============================================================================== +// Global source of __single +//============================================================================== + +int* global_count0; // expected-note{{pointer 'global_count0' declared here}} +int* global_count1; // expected-note{{pointer 'global_count1' declared here}} +int* global_count2; // expected-note{{pointer 'global_count2' declared here}} +int* global_count3; // expected-note{{pointer 'global_count3' declared here}} +int* global_count4; // expected-note{{pointer 'global_count4' declared here}} +void* global_count5; // expected-note{{pointer 'global_count5' declared here}} +int* global_count6; // expected-note{{pointer 'global_count6' declared here}} +int* global_count7; // expected-note{{pointer 'global_count7' declared here}} +int* global_count8; // expected-note{{pointer 'global_count8' declared here}} +int* global_count9; // expected-note{{pointer 'global_count9' declared here}} + +void initialized_from_single_global_var_idx(void) { + int* local = global_count0; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single global 'global_count0' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_single_global_var_idx(void) { + int* local; // expected-note{{pointer 'local' declared here}} + local = global_count1; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single global 'global_count1' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_single_global_var_arith(void) { + int* local = global_count2; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single global 'global_count2' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_single_global_var_arith(void) { + int* local; // expected-note{{pointer 'local' declared here}} + local = global_count3; // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single global 'global_count3' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_single_global_var_idx_parens(void) { + int* local; // expected-note{{pointer 'local' declared here}} + (local) = (global_count4); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single global 'global_count4' results in a trap{{$}}}} + (local[1]) = 0; +} + +void assigned_from_single_global_var_idx_parens_cast(void) { + int* local; // expected-note{{pointer 'local' declared here}} + (local) = ((int*) global_count5); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single global 'global_count5' results in a trap{{$}}}} + (local[1]) = 0; +} + +void assigned_from_multiple_globals_idx(void) { + // expected-note@+1{{pointer 'local' declared here}} + int *local = global_count6; // expected-note{{__single global 'global_count6' assigned to 'local' here}} + local = global_count7; // expected-note{{__single global 'global_count7' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_multiple_globals_arith(void) { + // expected-note@+1{{pointer 'local' declared here}} + int *local = global_count8; // expected-note{{__single global 'global_count8' assigned to 'local' here}} + local = global_count9; // expected-note{{__single global 'global_count9' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + +//============================================================================== +// Local var source of __single +//============================================================================== + +void initialized_from_local_single_var_idx(int* p) { + int* __single s = p; // expected-note{{pointer 's' declared here}} + int* local = s; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single local variable 's' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_local_single_var_idx(int* p) { + int* __single s = p; // expected-note{{pointer 's' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + local = s; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single local variable 's' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_local_single_var_arith(int* p) { + int* __single s = p; // expected-note{{pointer 's' declared here}} + int* local = s; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single local variable 's' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_local_single_var_arith(int* p) { + int* __single s = p; // expected-note{{pointer 's' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + local = s; // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single local variable 's' results in an out-of-bounds pointer}} + ++local; +} + +void initialized_from_local_static_single_var_idx(int* p) { + static int* __single s = 0; // expected-note{{pointer 's' declared here}} + int* local = s; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single local variable 's' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_local_static_single_var_idx(int* p) { + static int* __single s = 0; // expected-note{{pointer 's' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + local = s; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single local variable 's' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_local_static_single_var_arith(int* p) { + static int* __single s = 0; // expected-note{{pointer 's' declared here}} + int* local = s; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single local variable 's' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_local_static_single_var_arith(int* p) { + static int* __single s = 0; // expected-note{{pointer 's' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + local = s; // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single local variable 's' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_local_single_var_idx_parens(int* p) { + int* __single s = (p); // expected-note{{pointer 's' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + (local) = (s); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single local variable 's' results in a trap{{$}}}} + (local[1]) = 0; +} + +void assigned_from_local_single_var_idx_parens_cast(int* p) { + void* __single s = (p); // expected-note{{pointer 's' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + (local) = ((int*) s); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single local variable 's' results in a trap{{$}}}} + (local[1]) = 0; +} + +void initialized_from_multiple_local_single_vars_idx(int* p) { + int* __single s = p; // expected-note{{pointer 's' declared here}} + int* __single s2 = p; // expected-note{{pointer 's2' declared here}} + // expected-note@+1{{pointer 'local' declared here}} + int* local = s; // expected-note{{__single local variable 's' assigned to 'local' here}} + local = s2; // expected-note{{__single local variable 's2' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_multiple_local_single_vars_arith(int* p) { + int* __single s = p; // expected-note{{pointer 's' declared here}} + int* __single s2 = p; // expected-note{{pointer 's2' declared here}} + // expected-note@+1{{pointer 'local' declared here}} + int* local = s; // expected-note{{__single local variable 's' assigned to 'local' here}} + local = s2; // expected-note{{__single local variable 's2' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + + +//============================================================================== +// function return source of __single +//============================================================================== + +int* single_source0(void); // expected-note{{'single_source0' declared here}} +int* single_source1(void); // expected-note{{'single_source1' declared here}} +int* single_source2(void); // expected-note{{'single_source2' declared here}} +int* single_source3(void); // expected-note{{'single_source3' declared here}} +int* single_source4(void); // expected-note{{'single_source4' declared here}} +void* single_source5(void); // expected-note{{'single_source5' declared here}} +int* single_source6(void); // expected-note{{'single_source6' declared here}} +int* single_source7(void); // expected-note{{'single_source7' declared here}} +int* single_source8(void); // expected-note{{'single_source8' declared here}} +int* single_source9(void); // expected-note{{'single_source9' declared here}} + +void initialized_from_func_call_return_single_idx(void) { + int* local = single_source0(); // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single return value from call to 'single_source0' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_func_call_return_single_arith(void) { + int* local = single_source1(); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single return value from call to 'single_source1' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_func_call_return_single_idx(void) { + int* local; // expected-note{{pointer 'local' declared here}} + local = single_source2(); // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single return value from call to 'single_source2' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_func_call_return_single_arith(void) { + int* local; // expected-note{{pointer 'local' declared here}} + local = single_source3(); // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single return value from call to 'single_source3' results in an out-of-bounds pointer}} + ++local; +} + +void initialized_from_func_call_return_single_arith_parens(void) { + int* local = (single_source4()); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single return value from call to 'single_source4' results in an out-of-bounds pointer}} + (++local); +} + +void initialized_from_func_call_return_single_arith_parens_cast(void) { + int* local = ((int*) single_source5()); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single return value from call to 'single_source5' results in an out-of-bounds pointer}} + (++local); +} + +// Currently the FunctionDecl for this function actually returns __single (but +// the return value is promoted to __bidi_indexable with the appropriate bounds +// at call sites) so special logic is used to avoid warning for functions like +// these because in practice calls to these functions are not treated as +// returning __single. +void* custom_malloc(unsigned long long size) __attribute__((alloc_size(1))); + +void initialized_from_func_call_to_alloc_func_idx(void) { + int* local = custom_malloc(5); + local[1] = 0; // No warning +} + +void initialized_from_func_call_to_alloc_func_arith(void) { + int* local = custom_malloc(5); + ++local; // No warning +} + +void assigned_from_func_call_to_alloc_func_idx(void) { + int* local; + local = custom_malloc(5); + local[1] = 0; // No warning +} + +void assigned_from_func_call_to_alloc_func_arith(void) { + int* local; + local = custom_malloc(5); + ++local; // No warning +} + +void initialized_from_multiple_func_calls_return_single_idx(void) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = single_source6(); // expected-note{{__single return value from call to 'single_source6' assigned to 'local' here}} + local = single_source7(); // expected-note{{__single return value from call to 'single_source7' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_multiple_func_calls_return_single_artih(void) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = single_source8(); // expected-note{{__single return value from call to 'single_source8' assigned to 'local' here}} + local = single_source9(); // expected-note{{__single return value from call to 'single_source9' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + + +//============================================================================== +// Array element source of __single +//============================================================================== + +void initialized_from_array_elt_idx(int* ptrs[__counted_by(size)], int size) { // expected-note{{'ptrs' declared here}} + if (size < 2) + return; + int* local = ptrs[0]; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single element from array 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_array_elt_arith(int* ptrs[__counted_by(size)], int size) { // expected-note{{'ptrs' declared here}} + if (size < 2) + return; + int* local = ptrs[0]; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single element from array 'ptrs' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_array_elt_idx(int* ptrs[__counted_by(size)], int size) { // expected-note{{'ptrs' declared here}} + if (size < 2) + return; + int* local; // expected-note{{pointer 'local' declared here}} + local = ptrs[0]; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single element from array 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_array_elt_arith(int* ptrs[__counted_by(size)], int size) { // expected-note{{'ptrs' declared here}} + if (size < 2) + return; + int* local; // expected-note{{pointer 'local' declared here}} + local = ptrs[0]; // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single element from array 'ptrs' results in an out-of-bounds pointer}} + ++local; +} + +int* global_array0[4]; // expected-note{{'global_array0' declared here}} + +void initialized_from_global_array_elt_idx(void) { + int* local = global_array0[0]; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single element from array 'global_array0' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_array_elt_arith_parens(int* ptrs[__counted_by(size)], int size) { // expected-note{{'ptrs' declared here}} + if (size < 2) + return; + int* local = (ptrs[0]); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single element from array 'ptrs' results in an out-of-bounds pointer}} + (++local); +} + +void initialized_from_array_elt_arith_parens_cast(int* ptrs[__counted_by(size)], int size) { // expected-note{{'ptrs' declared here}} + if (size < 2) + return; + int* local = ((int*) ptrs[0]); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single element from array 'ptrs' results in an out-of-bounds pointer}} + (++local); +} + +// expected-note@+2{{'ptrs' declared here}} +// expected-note@+1{{'ptrs2' declared here}} +void initialized_from_multiple_array_elt_idx(int* ptrs[__counted_by(size)], int* ptrs2[__counted_by(size)], int size) { + if (size < 2) + return; + // expected-note@+1{{pointer 'local' declared here}} + int* local = ptrs[0]; // expected-note{{__single element from array 'ptrs' assigned to 'local' here}} + local = ptrs2[1]; // expected-note{{__single element from array 'ptrs2' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +// expected-note@+2{{'ptrs' declared here}} +// expected-note@+1{{'ptrs2' declared here}} +void initialized_from_multiple_array_elt_arith(int* ptrs[__counted_by(size)], int* ptrs2[__counted_by(size)], int size) { + if (size < 2) + return; + // expected-note@+1{{pointer 'local' declared here}} + int* local = ptrs[0]; // expected-note{{__single element from array 'ptrs' assigned to 'local' here}} + local = ptrs2[1]; // expected-note{{__single element from array 'ptrs2' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + +//============================================================================== +// Struct member source of __single +//============================================================================== + +struct StructWithSinglePtr0 { + int* ptr; // expected-note{{StructWithSinglePtr0::ptr declared here}} +}; + +struct StructWithSinglePtr1 { + int* ptr; // expected-note{{StructWithSinglePtr1::ptr declared here}} +}; + +struct StructWithSinglePtr2 { + int* ptr; // expected-note{{StructWithSinglePtr2::ptr declared here}} +}; + + +struct StructWithSinglePtr3 { + int* ptr; // expected-note{{StructWithSinglePtr3::ptr declared here}} +}; + +struct StructWithSinglePtr4 { + int* ptr; // expected-note{{StructWithSinglePtr4::ptr declared here}} +}; + +struct StructWithSinglePtr5 { + void* ptr; // expected-note{{StructWithSinglePtr5::ptr declared here}} +}; + +struct StructWithSinglePtr6 { + int* ptr; // expected-note{{StructWithSinglePtr6::ptr declared here}} +}; + +struct StructWithSinglePtr7 { + int* ptr; // expected-note{{StructWithSinglePtr7::ptr declared here}} +}; + +struct StructWithSinglePtr8 { + int* ptr; // expected-note{{StructWithSinglePtr8::ptr declared here}} +}; + +struct StructWithSinglePtr9 { + int* ptr; // expected-note{{StructWithSinglePtr9::ptr declared here}} +}; + +union UnionWithSinglePtr0 { + int* ptr; // expected-note{{UnionWithSinglePtr0::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr1 { + int* ptr; // expected-note{{UnionWithSinglePtr1::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr2 { + int* ptr; // expected-note{{UnionWithSinglePtr2::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr3 { + int* ptr; // expected-note{{UnionWithSinglePtr3::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr4 { + int* ptr; // expected-note{{UnionWithSinglePtr4::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr5 { + void* ptr; // expected-note{{UnionWithSinglePtr5::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr6 { + int* ptr; // expected-note{{UnionWithSinglePtr6::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr7 { + int* ptr; // expected-note{{UnionWithSinglePtr7::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr8 { + int* ptr; // expected-note{{UnionWithSinglePtr8::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSinglePtr9 { + int* ptr; // expected-note{{UnionWithSinglePtr9::ptr declared here}} + unsigned long long integer; +}; + +void initialized_from_struct_member_idx(struct StructWithSinglePtr0* s) { + int* local = s->ptr; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single struct member 'ptr' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_union_member_idx(union UnionWithSinglePtr0* s) { + int* local = s->ptr; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single union member 'ptr' results in a trap{{$}}}} + local[1] = 0; +} + + +void initialized_from_struct_member_arith(struct StructWithSinglePtr1* s) { + int* local = s->ptr; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single struct member 'ptr' results in an out-of-bounds pointer}} + ++local; +} + +void initialized_from_union_member_arith(union UnionWithSinglePtr1* s) { + int* local = s->ptr; // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single union member 'ptr' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_struct_member_idx(struct StructWithSinglePtr2* s) { + int* local; // expected-note{{pointer 'local' declared here}} + local = s->ptr; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single struct member 'ptr' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_union_member_idx(union UnionWithSinglePtr2* s) { + int* local; // expected-note{{pointer 'local' declared here}} + local = s->ptr; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single union member 'ptr' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_struct_member_arith(struct StructWithSinglePtr3* s) { + int* local; // expected-note{{pointer 'local' declared here}} + local = s->ptr; // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single struct member 'ptr' results in an out-of-bounds pointer}} + ++local; +} + +void assigned_from_union_member_arith(union UnionWithSinglePtr3* s) { + int* local; // expected-note{{pointer 'local' declared here}} + local = s->ptr; // expected-note{{pointer 'local' assigned here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single union member 'ptr' results in an out-of-bounds pointer}} + ++local; +} + +void initialized_from_struct_member_arith_parens(struct StructWithSinglePtr4* s) { + int* local = (s->ptr); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single struct member 'ptr' results in an out-of-bounds pointer}} + (++local); +} + +void initialized_from_union_member_arith_parens(union UnionWithSinglePtr4* s) { + int* local = (s->ptr); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single union member 'ptr' results in an out-of-bounds pointer}} + (++local); +} + +void initialized_from_struct_member_arith_parens_cast(struct StructWithSinglePtr5* s) { + int* local = ((int*) s->ptr); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single struct member 'ptr' results in an out-of-bounds pointer}} + (++local); +} + +void initialized_from_union_member_arith_parens_cast(union UnionWithSinglePtr5* s) { + int* local = ((int*) s->ptr); // expected-note{{pointer 'local' initialized here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single union member 'ptr' results in an out-of-bounds pointer}} + (++local); +} + +void initialized_from_multiple_struct_member_idx(struct StructWithSinglePtr6* s1, struct StructWithSinglePtr7* s2) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = s1->ptr; // expected-note{{__single struct member 'ptr' assigned to 'local' here}} + local = s2->ptr; // expected-note{{__single struct member 'ptr' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_multiple_union_member_idx(union UnionWithSinglePtr6* u1, union UnionWithSinglePtr7* u2) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = u1->ptr; // expected-note{{__single union member 'ptr' assigned to 'local' here}} + local = u2->ptr; // expected-note{{__single union member 'ptr' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_multiple_struct_member_arith(struct StructWithSinglePtr8* s1, struct StructWithSinglePtr9* s2) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = s1->ptr; // expected-note{{__single struct member 'ptr' assigned to 'local' here}} + local = s2->ptr; // expected-note{{__single struct member 'ptr' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + +void initialized_from_multiple_union_member_arith(union UnionWithSinglePtr8* u1, union UnionWithSinglePtr9* u2) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = u1->ptr; // expected-note{{__single union member 'ptr' assigned to 'local' here}} + local = u2->ptr; // expected-note{{__single union member 'ptr' assigned to 'local' here}} + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; +} + +struct StructWithArrayOfSingles0 { + int* ptrs[4]; // expected-note{{StructWithArrayOfSingles0::ptrs declared here}} +}; + +struct StructWithArrayOfSingles1 { + int* ptrs[4]; // expected-note{{StructWithArrayOfSingles1::ptrs declared here}} +}; + +struct StructWithArrayOfSingles2 { + int* ptrs[4]; // expected-note{{StructWithArrayOfSingles2::ptrs declared here}} +}; + +struct StructWithArrayOfSingles3 { + int* ptrs[4]; // expected-note{{StructWithArrayOfSingles3::ptrs declared here}} +}; + +struct StructWithArrayOfSingles4 { + int* ptrs[4]; // expected-note{{StructWithArrayOfSingles4::ptrs declared here}} +}; + +union UnionWithArrayOfSingles0 { + int* ptrs[4]; // expected-note{{UnionWithArrayOfSingles0::ptrs declared here}} + int unused; +}; + +union UnionWithArrayOfSingles1 { + int* ptrs[4]; // expected-note{{UnionWithArrayOfSingles1::ptrs declared here}} + int unused; +}; + +union UnionWithArrayOfSingles2 { + int* ptrs[4]; // expected-note{{UnionWithArrayOfSingles2::ptrs declared here}} + int unused; +}; + +union UnionWithArrayOfSingles3 { + int* ptrs[4]; // expected-note{{UnionWithArrayOfSingles3::ptrs declared here}} + int unused; +}; + +union UnionWithArrayOfSingles4 { + int* ptrs[4]; // expected-note{{UnionWithArrayOfSingles4::ptrs declared here}} + int unused; +}; + +void initialized_from_struct_member_via_array_idx(struct StructWithArrayOfSingles0* s) { + int* local = s->ptrs[1]; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single struct member 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_struct_member_via_array_idx(struct StructWithArrayOfSingles1* s) { + int* local; // expected-note{{pointer 'local' declared here}} + local = s->ptrs[1]; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single struct member 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +void initialized_from_union_member_via_array_idx(union UnionWithArrayOfSingles0* s) { + int* local = s->ptrs[1]; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single union member 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_struct_union_via_array_idx(union UnionWithArrayOfSingles1* s) { + int* local; // expected-note{{pointer 'local' declared here}} + local = s->ptrs[1]; // expected-note{{pointer 'local' assigned here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' assigned from __single union member 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +struct StructWithArrayOfSingleStructPtrs0 { + struct StructWithArrayOfSingles2* struct_ptrs[4]; +}; + +void initialized_from_struct_member_via_array_nested_idx(struct StructWithArrayOfSingleStructPtrs0* s) { + int* local = s->struct_ptrs[1]->ptrs[2]; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single struct member 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +union UnionWithArrayOfSingleUnionPtrs0 { + union UnionWithArrayOfSingles2* union_ptrs[4]; +}; + +void initialized_from_union_member_via_array_nested_idx(union UnionWithArrayOfSingleUnionPtrs0* s) { + int* local = s->union_ptrs[1]->ptrs[2]; // expected-note{{pointer 'local' initialized here}} + // expected-warning-re@+1{{indexing into a __bidi_indexable local variable 'local' initialized from __single union member 'ptrs' results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_multiple_struct_members_via_array_idx(struct StructWithArrayOfSingles3* s1, struct StructWithArrayOfSingles4* s2) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = s1->ptrs[1]; // expected-note{{__single struct member 'ptrs' assigned to 'local' here}} + local = s2->ptrs[2]; // expected-note{{__single struct member 'ptrs' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +void assigned_from_multiple_union_members_via_array_idx(union UnionWithArrayOfSingles3* u1, union UnionWithArrayOfSingles4* u2) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = u1->ptrs[1]; // expected-note{{__single union member 'ptrs' assigned to 'local' here}} + local = u2->ptrs[2]; // expected-note{{__single union member 'ptrs' assigned to 'local' here}} + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +//============================================================================== +// Multiple __single entity types +//============================================================================== +int* global_single_int; // expected-note{{pointer 'global_single_int' declared here}} +int* single_int_source(void); // expected-note{{'single_int_source' declared here}} + +struct StructWithSingleIntPtr { + int* ptr; // expected-note{{StructWithSingleIntPtr::ptr declared here}} +}; + +struct StructWithSingleIntArray { + int* ptrs[4]; // expected-note{{StructWithSingleIntArray::ptrs }} +}; + + +union UnionWithSingleIntPtr { + int* ptr; // expected-note{{UnionWithSingleIntPtr::ptr declared here}} + unsigned long long integer; +}; + +union UnionWithSingleIntArray { + int* ptrs[4]; // expected-note{{UnionWithSingleIntArray::ptrs declared here}} + unsigned long long integer; +}; + +void multiple_entity_types( + // expected-note@+1{{pointer 'p' declared here}} + int* p, + // expected-note@+1{{'ptrs' declared here}} + int* ptrs[__counted_by(size)], int size, + struct StructWithSingleIntPtr* s, + union UnionWithSingleIntPtr* u, + struct StructWithSingleIntArray* sa, + union UnionWithSingleIntArray* ua) { + int* local; // expected-note{{pointer 'local' declared here}} + int* __single local_single; // expected-note{{pointer 'local_single' declared here}} + + // Assign multiple entities that are all __single + local = p; // expected-note{{__single parameter 'p' assigned to 'local' here}} + local = global_single_int; // expected-note{{__single global 'global_single_int' assigned to 'local' here}} + local = local_single; // expected-note{{__single local variable 'local_single' assigned to 'local' here}} + local = single_int_source(); // expected-note{{__single return value from call to 'single_int_source' assigned to 'local' here}} + local = ptrs[0]; // expected-note{{__single element from array 'ptrs' assigned to 'local' here}} + local = s->ptr; // expected-note{{__single struct member 'ptr' assigned to 'local' here}} + local = u->ptr; // expected-note{{__single union member 'ptr' assigned to 'local' here}} + local = sa->ptrs[1]; // expected-note{{__single struct member 'ptrs' assigned to 'local' here}} + local = ua->ptrs[1]; // expected-note{{__single union member 'ptrs' assigned to 'local' here}} + + // expected-warning-re@+1{{indexing into __bidi_indexable local variable 'local' that is assigned from a __single pointer results in a trap{{$}}}} + local[1] = 0; +} + +//============================================================================== +// typedefs with VLAs +//============================================================================== + +// FIXME(dliew): Clang currently warns twice because the size expression of the +// VLA type gets visited twice. rdar://117235333 +void typedefs_with_vla(int *x, int size) { // expected-note 2{{pointer 'x' declared here}} + int* local = x; // expected-note 2{{pointer 'local' initialized here}} + for (int i = 0; i < size; ++i) { + // expected-warning@+1 2{{indexing into a __bidi_indexable local variable 'local' initialized from __single parameter 'x' results in a trap if the index expression is >= 1 or < 0}} + typedef int arr[local[i]]; + arr a; // has x[i] elements on each loop iteration + if (i > 0) + a[0] = 0; + } +} + +//============================================================================== +// False positives +//============================================================================== + +void false_positive_reachability_ignored(int *p) { // expected-note{{pointer 'p' declared here}} + int* local = p; // expected-note{{pointer 'local' initialized here}} + + if (0) { + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' initialized from __single parameter 'p' results in an out-of-bounds pointer}} + ++local; // Not reachable + } +} + +void false_positive_reachability_ignored2(int *p) { // expected-note{{pointer 'p' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + + if (0) { + // expected-note@+1{{pointer 'local' assigned here}} + local = p; // Not reachable + } + + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in an out-of-bounds pointer}} + ++local; +} + +void false_positive_happens_before_ignored(int* p) { // expected-note{{pointer 'p' declared here}} + int* local; // expected-note{{pointer 'local' declared here}} + // expected-warning@+1{{pointer arithmetic over a __bidi_indexable local variable 'local' assigned from __single parameter 'p' results in an out-of-bounds pointer}} + ++local; + + // The first __single assignment is made **after** the bad operation. + // To fix this we need the analysis to understand happens-before (rdar://117166345). + local = p; // expected-note{{pointer 'local' assigned here}} +} + +void false_positive_assignment_notes( + // expected-note@+1{{pointer 'p' declared here}} + int* p, + // expected-note@+1{{pointer 'q' declared here}} + int* q, + // expected-note@+1{{pointer 'r' declared here}} + int* r) { + // expected-note@+1{{pointer 'local' declared here}} + int* local = p; // expected-note{{__single parameter 'p' assigned to 'local' here}} + + // This warning is not a false positive because `local` definitely has the + // bounds of a `__single` by the time the unsafe operation happens. + // expected-warning@+1{{pointer arithmetic over __bidi_indexable local variable 'local' that is assigned from a __single pointer results in an out-of-bounds pointer}} + ++local; + + // These notes are false positives because these assignments happen **after** the unsafe. + // operations. To fix this we need the analysis to understand happens-before (rdar://117166345). + local = q; // expected-note{{__single parameter 'q' assigned to 'local' here}} + local = r; // expected-note{{__single parameter 'r' assigned to 'local' here}} +} + +//============================================================================== +// False negatives +//============================================================================== +void false_neg_multiple_assignment_different_expr(int* p, int* __bidi_indexable q) { + // Multiple assignments from a different expr prevent a diagnostic from being + // emitted. + int* local = p; + local = q; + ++local; // Missing warning +} + +void false_neg_happens_before_ignored(int* p, int* __bidi_indexable q) { + // We fail to warn here because the analysis sees multiple definitions of + // `local` but fails to take into account at `++local` only one definition + // of local is possible. (rdar://117166345) + int* local = p; + ++local; // Missing warning + local = q; +} + +void false_neg_reachability_ignored(int* p, int* __bidi_indexable q) { + // We fail to warn here because the analysis sees multiple definitions of + // `local` but fails to take into account at `++local` only one definition + // of local is possible. + int* local = p; + + if (0) { + local = q; // Not reachable + } + + ++local; // Missing warning +} + +void false_neg_transitive_assignments_ignored(int *p) { + // We fail to warn here because the analysis doesn't track a __single + // indirectly flowing into a local __bidi_indexable via another + // local variable. + int *local = p; + int *local2 = local; + ++local2; // Missing warning +} diff --git a/clang/test/BoundsSafety/Sema/single-to-bidi-truncate.c b/clang/test/BoundsSafety/Sema/single-to-bidi-truncate.c new file mode 100644 index 0000000000000..acd7822e6a59e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-to-bidi-truncate.c @@ -0,0 +1,274 @@ +#include + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: not %clang_cc1 -fsyntax-only -fbounds-safety -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s + +void concrete_to_incomplete(int *p) { + // expected-note@+3{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+2{{silence by making the destination '__single'}} + // expected-warning@+1{{casting 'int *__single' to 'void *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'void' having unknown size)}} + void *l = p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:13-[[@LINE-1]]:13}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_apply_fixit(int *p) { + void *l = (int *__bidi_indexable)p; +} + +// expected-note@+1{{pointer 'p' declared here}} +void concrete_to_incomplete_indi(int *p) { + // expected-note@+4{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+3{{silence by making the destination '__single'}} + // expected-warning@+2{{casting 'int *__single' to 'void *__indexable' creates a '__indexable' pointer with zero length due to 'void' having unknown size)}} + // expected-warning@+1{{initializing type 'void *__indexable' with an expression of type 'int *__single' results in an __indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'p'}} + void *__indexable l = p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:25-[[@LINE-1]]:25}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_indi_apply_fixit(int *p) { + void *__indexable l = (int *__bidi_indexable)p; +} + +// expected-note@+1{{pointer 'p' declared here}} +void concrete_to_incomplete_bidi(int *p) { + // expected-note@+4{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+3{{silence by making the destination '__single'}} + // expected-warning@+2{{casting 'int *__single' to 'void *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'void' having unknown size)}} + // expected-warning@+1{{initializing type 'void *__bidi_indexable' with an expression of type 'int *__single' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'p'}} + void *__bidi_indexable l = p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:30-[[@LINE-1]]:30}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_bidi_apply_fixit(int *p) { + void *__bidi_indexable l = (int *__bidi_indexable)p; +} + +void concrete_to_incomplete_explicit_bidi(int *p) { + // expected-note@+3{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+2{{silence by making the destination '__single'}} + // expected-warning@+1{{casting 'int *__single' to 'void *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'void' having unknown size)}} + void *l = (void *__bidi_indexable)p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:37-[[@LINE-1]]:37}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_explicit_bidi_apply_fixit(int *p) { + void *l = (void *__bidi_indexable)(int *__bidi_indexable)p; +} + +void concrete_to_incomplete_explicit_indi(int *p) { + // expected-note@+3{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+2{{silence by making the destination '__single'}} + // expected-warning@+1{{casting 'int *__single' to 'void *__indexable' creates a '__indexable' pointer with zero length due to 'void' having unknown size)}} + void *l = (void *__indexable)p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:32-[[@LINE-1]]:32}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_explicit_indi_apply_fixit(int *p) { + void *l = (void *__indexable)(int *__bidi_indexable)p; +} + +void concrete_to_incomplete_indi_explicit(int *p) { + // expected-note@+3{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+2{{silence by making the destination '__single'}} + // expected-warning@+1{{casting 'int *__single' to 'void *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'void' having unknown size)}} + void *__indexable l = (void *__bidi_indexable)p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:49-[[@LINE-1]]:49}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_indi_explicit_apply_fixit(int *p) { + void *__indexable l = (void *__bidi_indexable)(int *__bidi_indexable)p; +} + +void concrete_to_incomplete_bidi_explicit(int *p) { + // expected-note@+3{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+2{{silence by making the destination '__single'}} + // expected-warning@+1{{casting 'int *__single' to 'void *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'void' having unknown size)}} + void *__bidi_indexable l = (void *__bidi_indexable)p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:54-[[@LINE-1]]:54}:"(int *__bidi_indexable)" +} + +void concrete_to_incomplete_bidi_explicit_apply_fixit(int *p) { + void *__bidi_indexable l = (void *__bidi_indexable)(int *__bidi_indexable)p; +} + +void wider_to_narrower(int *p) { + // expected-note@+3{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+2{{silence by making the destination '__single'}} + // expected-warning@+1{{casting 'int *__single' to 'char *__bidi_indexable' creates a '__bidi_indexable' pointer with bounds containing only one 'char'}} + char *l = (char *__bidi_indexable)p + 1; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:37-[[@LINE-1]]:37}:"(int *__bidi_indexable)" + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:37-[[@LINE-2]]:37}:"(char *__single)" +} + +void wider_to_narrower_apply_fixit1(int *p) { + char *l = (char *__bidi_indexable)(int *__bidi_indexable)p + 1; +} + +void wider_to_narrower_apply_fixit2(int *p) { + char *l = (char *__bidi_indexable)(char *__single)p + 1; +} + +// expected-note@+1{{passing argument to parameter here}} +void bidi_arg(char *__bidi_indexable); + +void wider_to_narrower_arg(int *p) { + // expected-warning@+3{{casting 'int *__single' to 'char *__bidi_indexable' creates a '__bidi_indexable' pointer with bounds containing only one 'char'}} + // expected-note@+2{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+1{{silence by making the destination '__single'}} + bidi_arg((char *__bidi_indexable)p); + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:36-[[@LINE-1]]:36}:"(int *__bidi_indexable)" + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:36-[[@LINE-2]]:36}:"(char *__single)" +} + +void wider_to_narrower_arg_apply_fixit1(int *p) { + bidi_arg((char *__bidi_indexable)(int *__bidi_indexable)p); +} + +void wider_to_narrower_arg_apply_fixit2(int *p) { + bidi_arg((char *__bidi_indexable)(char *__single)p); +} + +void wider_to_narrower_arg2(int *p) { + // expected-warning@+1{{passing type 'char *__single' to parameter of type 'char *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced}} + bidi_arg((char *)p); +} + +char *__bidi_indexable wider_to_narrower_ret(int *p) { + // expected-warning@+3{{casting 'int *__single' to 'char *__bidi_indexable' creates a '__bidi_indexable' pointer with bounds containing only one 'char'}} + // expected-note@+2{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+1{{silence by making the destination '__single'}} + return (char *__bidi_indexable)p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:34-[[@LINE-1]]:34}:"(int *__bidi_indexable)" + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:34-[[@LINE-2]]:34}:"(char *__single)" +} + +char *__bidi_indexable wider_to_narrower_ret_apply_fixit1(int *p) { + return (char *__bidi_indexable)(int *__bidi_indexable)p; +} + +char *__bidi_indexable wider_to_narrower_ret_apply_fixit2(int *p) { + return (char *__bidi_indexable)(char *__single)p; +} + +// expected-note@+1{{pointer 'p' declared here}} +void *__bidi_indexable concrete_to_incomplete_ret(int *__single p) { + // expected-warning@+4{{casting 'int *__single' to 'void *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'void' having unknown size)}} + // expected-warning@+3{{returning type 'int *__single' from a function with result type 'void *__bidi_indexable' results in a __bidi_indexable pointer that will trap if a non-zero offset is dereferenced. consider adding '__counted_by' to 'p'}} + // expected-note@+2{{cast to 'int *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+1{{silence by making the destination '__single'}} + return p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:10-[[@LINE-1]]:10}:"(int *__bidi_indexable)" +} + +void *__bidi_indexable concrete_to_incomplete_ret_apply_fixit(int *__single p) { + return (int *__bidi_indexable)p; +} + +void narrower_to_wider(char *p) { + int *l = (int *__bidi_indexable)p + 1; +} + +void incomplete_to_concrete(void *p) { + char *l = (char *)p; +} + +void incomplete_to_concrete_bidi(void *p) { + // expected-error@+1{{cannot cast from __single pointer to incomplete type 'void *__single' to indexable pointer type 'void *__bidi_indexable'}} + char *l = (void *__bidi_indexable)p; +} + +struct s; + +void opaque_to_concrete(struct s *p) { + // expected-error@+1{{cannot initialize indexable pointer with type 'char *__bidi_indexable' from __single pointer to incomplete type 'struct s *__single'; consider declaring pointer 'l' as '__single'}} + char *l = p; // expected-note{{pointer 'l' declared here}} +} + +// This is also undesirable because it's not obvious whether 'struct s' incomplete for the programmer. +void opaque_to_concrete_explicit(struct s *p) { + char *l = (char *)p; +} + +void opaque_to_concrete_explicit2(struct s *p) { + // expected-error@+1{{cannot initialize indexable pointer with type 'char *__bidi_indexable' from __single pointer to incomplete type 'struct s *__single'; consider declaring pointer 'l' as '__single'}} + char *l = (struct s *)p; // expected-note{{pointer 'l' declared here}} +} + +void concrete_to_opaque(char *p) { + // expected-note@+4{{cast to 'char *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+3{{silence by making the destination '__single'}} + // expected-warning@+2{{casting 'char *__single' to 'struct s *__bidi_indexable' creates a '__bidi_indexable' pointer with zero length due to 'struct s' having unknown size)}} + // expected-warning@+1{{incompatible pointer types initializing 'struct s *__bidi_indexable' with an expression of type 'char *__single'}} + struct s *l = p; +} + +struct t; + +void opaque_to_opaque(struct t *p) { + // expected-error@+1{{cannot initialize indexable pointer with type 'struct s *__bidi_indexable' from __single pointer to incomplete type 'struct t *__single'; consider declaring pointer 'l' as '__single'}} + struct s *l = p; // expected-note{{pointer 'l' declared here}} +} + + +struct flex { + unsigned count; + int arr[__counted_by(count)]; +}; + +void flexible_to_concrete_narrower(struct flex *p) { + // expected-warning@+1{{incompatible pointer types initializing 'char *__bidi_indexable' with an expression of type 'struct flex *__single'}} + char *l = p; +} + +void flexible_to_concrete_narrower_apply_fixit1(struct flex *p) { + // expected-warning@+1{{incompatible pointer types initializing 'char *__bidi_indexable' with an expression of type 'struct flex *__bidi_indexable'}} + char *l = (struct flex *__bidi_indexable)p; + } + +void flexible_to_concrete_narrower_apply_fixit2(struct flex *p) { + char *l = (char *__single)p; +} + +void flexible_to_concrete_wider(struct flex *p) { + // expected-warning@+1{{incompatible pointer types initializing 'long long *__bidi_indexable' with an expression of type 'struct flex *__single'}} + long long *l = p; +} + +void flexible_to_concrete_match(struct flex *p) { + // expected-warning@+1{{incompatible pointer types initializing 'unsigned int *__bidi_indexable' with an expression of type 'struct flex *__single'}} + unsigned *l = p; +} + +void flexible_to_incomplete(struct flex *p) { + void *c = p; +} + +void incomplete_to_flexible(void *p) { + // expected-error@+1{{cannot initialize indexable pointer with type 'struct flex *__bidi_indexable' from __single pointer to incomplete type 'void *__single'; consider declaring pointer 'l' as '__single'}} + struct flex *l = p; // expected-note{{pointer 'l' declared here}} +} + +void concrete_match_to_flexible(unsigned *p) { + // expected-warning@+1{{incompatible pointer types initializing 'struct flex *__bidi_indexable' with an expression of type 'unsigned int *__single'}} + struct flex *l = p; +} + +void concrete_narrower_to_flexible(char *p) { + // expected-warning@+1{{incompatible pointer types initializing 'struct flex *__bidi_indexable' with an expression of type 'char *__single'}} + struct flex *l = p; +} + +void concrete_wider_to_flexible(long long *p) { + // expected-note@+4{{cast to 'long long *__bidi_indexable' first to keep bounds of 'p'}} + // expected-note@+3{{silence by making the destination '__single'}} + // expected-warning@+2{{casting 'long long *__single' to 'struct flex *__bidi_indexable' creates a '__bidi_indexable' pointer with bounds containing only one 'struct flex'}} + // expected-warning@+1{{incompatible pointer types initializing 'struct flex *__bidi_indexable' with an expression of type 'long long *__single'}} + struct flex *l = p; + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:20-[[@LINE-1]]:20}:"(long long *__bidi_indexable)" +} + +void concrete_wider_to_flexible_apply_fixit(long long *p) { + // expected-warning@+1{{incompatible pointer types initializing 'struct flex *__bidi_indexable' with an expression of type 'long long *__bidi_indexable'}} + struct flex *l = (long long *__bidi_indexable)p; +} diff --git a/clang/test/BoundsSafety/Sema/single-to-count-ptr-promotion.c b/clang/test/BoundsSafety/Sema/single-to-count-ptr-promotion.c new file mode 100644 index 0000000000000..11d1938f2a940 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-to-count-ptr-promotion.c @@ -0,0 +1,110 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=bounds-safety %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=bounds-safety %s + +#include + +void foo(int *__counted_by(len) ptr, int len) {} + +// bounds-safety-note@+3{{consider adding '__counted_by(len)' to 'ptr'}} +// bounds-safety-note@+2{{consider adding '__counted_by(2)' to 'ptr'}} +// bounds-safety-note@+1{{consider adding '__counted_by(3)' to 'ptr'}} +void bar(int *ptr, int len) { + int *__indexable ptr_bound; + // bounds-safety-note@+2{{count passed here}} + // bounds-safety-warning@+1{{count value is not statically known: passing 'int *__single' to parameter 'ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') is invalid for any count other than 0 or 1}} + foo(ptr, len); + foo(ptr, 1); + const int one = 1; + foo(ptr, one); + const int zero = 0; + foo(ptr, zero); + const int two = 2; + // bounds-safety-error@+1{{passing 'int *__single' to parameter 'ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single') with count value of 2 always fails}} + foo(ptr, two); + const int neg = -1; + // bounds-safety-error@+1{{negative count value of -1 for 'ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + foo(ptr, neg); + + foo(ptr_bound, len); + foo(ptr_bound, 1); + foo(ptr_bound, zero); + foo(ptr_bound, two); + // bounds-safety-error@+1{{negative count value of -1 for 'ptr' of type 'int *__single __counted_by(len)' (aka 'int *__single')}} + foo(ptr_bound, neg); + + int local_len; + // bounds-safety-warning@+1{{possibly initializing 'local_ptr' of type 'int *__single __counted_by(local_len)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + int *__counted_by(local_len) local_ptr = ptr; + int *__counted_by(1) local_ptr2 = ptr; + // bounds-safety-error@+1{{initializing 'local_ptr3' of type 'int *__single __counted_by(2)' (aka 'int *__single') and count value of 2 with 'int *__single' always fails}} + int *__counted_by(2) local_ptr3 = ptr; + const int three = 3; + // bounds-safety-error@+1{{initializing 'local_ptr4' of type 'int *__single __counted_by(3)' (aka 'int *__single') and count value of 3 with 'int *__single' always fails}} + int *__counted_by(three) local_ptr4 = ptr; + const int neg2 = -1; + // bounds-safety-error@+1{{negative count value of -1 for 'local_ptr5' of type 'int *__single __counted_by(-1)' (aka 'int *__single')}} + int *__counted_by(neg2) local_ptr5 = ptr; +} + +void baz(int *__counted_by(len) ptr1, int *__counted_by(1) ptr2, int len) { + int *__single sp; + int *__unsafe_indexable up; + // bounds-safety-warning@+1{{count value is not statically known: assigning to 'ptr1' of type 'int *__single __counted_by(len)' (aka 'int *__single') from 'int *__single' is invalid for any count other than 0 or 1}} + ptr1 = sp; + // bounds-safety-note@+1{{count assigned here}} + len = len; + + ptr2 = sp; + ptr2 = up; // bounds-safety-error{{assigning to 'int *__single __counted_by(1)' (aka 'int *__single') from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} + +void foo_nullable(int *__counted_by_or_null(len) ptr, int len) {} + +// bounds-safety-note@+1{{consider adding '__counted_by_or_null(len)' to 'ptr'}} +void bar_nullable(int *ptr, int len) { + int *__indexable ptr_bound; + // bounds-safety-note@+2{{count passed here}} + // bounds-safety-warning@+1{{count value is not statically known: passing 'int *__single' to parameter 'ptr' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') is invalid for any count other than 0 or 1 unless the pointer is null}} + foo_nullable(ptr, len); + foo_nullable(ptr, 1); + const int one = 1; + foo_nullable(ptr, one); + const int zero = 0; + foo_nullable(ptr, zero); + const int two = 2; + foo_nullable(ptr, two); + const int neg = -1; + // bounds-safety-error@+1{{possibly passing non-null to parameter 'ptr' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') with count value of -1; explicitly pass null to remove this warning}} + foo_nullable(ptr, neg); + + foo_nullable(ptr_bound, len); + foo_nullable(ptr_bound, 1); + foo_nullable(ptr_bound, zero); + foo_nullable(ptr_bound, two); + // bounds-safety-error@+1{{possibly passing non-null to parameter 'ptr' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') with count value of -1; explicitly pass null to remove this warning}} + foo_nullable(ptr_bound, neg); + + int local_len; + // bounds-safety-warning@+1{{possibly initializing 'local_ptr' of type 'int *__single __counted_by_or_null(local_len)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + int *__counted_by_or_null(local_len) local_ptr = ptr; + int *__counted_by_or_null(1) local_ptr2 = ptr; + int *__counted_by_or_null(2) local_ptr3 = ptr; + const int three = 3; + int *__counted_by_or_null(three) local_ptr4 = ptr; + const int neg2 = -1; + // bounds-safety-error@+1{{possibly initializing 'local_ptr5' of type 'int *__single __counted_by_or_null(-1)' (aka 'int *__single') and count value of -1 with non-null; explicitly initialize null to remove this warning}} + int *__counted_by_or_null(neg2) local_ptr5 = ptr; +} + +void baz_nullable(int *__counted_by_or_null(len) ptr1, int *__counted_by_or_null(1) ptr2, int len) { + int *__single sp; + int *__unsafe_indexable up; + // bounds-safety-warning@+1{{count value is not statically known: assigning to 'ptr1' of type 'int *__single __counted_by_or_null(len)' (aka 'int *__single') from 'int *__single' is invalid for any count other than 0 or 1 unless the pointer is null}} + ptr1 = sp; + // bounds-safety-note@+1{{count assigned here}} + len = len; + + ptr2 = sp; + ptr2 = up; // bounds-safety-error{{assigning to 'int *__single __counted_by_or_null(1)' (aka 'int *__single') from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} diff --git a/clang/test/BoundsSafety/Sema/single-to-sized-ptr-promotion.c b/clang/test/BoundsSafety/Sema/single-to-sized-ptr-promotion.c new file mode 100644 index 0000000000000..51e3f12000ce1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-to-sized-ptr-promotion.c @@ -0,0 +1,102 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include +void foo(int *__sized_by(len) ptr, int len) {} +void foo_void(void *__sized_by(len) ptr, int len) {} +void foo_fixed(void *__sized_by(8) ptr) {} + +// expected-note@+4{{consider adding '__sized_by(len)' to 'ptr'}} +// expected-note@+3{{consider adding '__sized_by(8)' to 'ptr'}} +// expected-note@+2{{consider adding '__sized_by(5)' to 'ptr'}} +// expected-note@+1{{consider adding '__sized_by(12)' to 'ptr'}} +void bar(int *ptr, int len) { + int *ptr_auto_bound; + // expected-note@+2{{size passed here}} + // expected-warning@+1{{size value is not statically known: passing 'int *__single' to parameter 'ptr' of type 'int *__single __sized_by(len)' (aka 'int *__single') is invalid for any size greater than 4}} + foo(ptr, len); + foo(ptr, 1); + foo(ptr, sizeof(int)); + const int four = 1 * sizeof(int); + foo(ptr, four); + const int zero = 0; + foo(ptr, zero); + const int eight = 2 * sizeof(int); + foo(ptr, eight); // expected-error{{passing 'int *__single' with pointee of size 4 to parameter 'ptr' of type 'int *__single __sized_by(len)' (aka 'int *__single') with size value of 8 always fails}} + const int neg = -1; + foo(ptr, neg); // expected-error{{negative size value of -1 for 'ptr' of type 'int *__single __sized_by(len)' (aka 'int *__single')}} + foo_fixed(ptr); // expected-error{{passing 'int *__single' with pointee of size 4 to parameter 'ptr' of type 'void *__single __sized_by(8)' (aka 'void *__single') with size value of 8 always fails}} + + foo_void(ptr, zero); + foo_void(ptr, four); + foo_void(ptr, eight); // expected-error{{passing 'int *__single' with pointee of size 4 to parameter 'ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single') with size value of 8 always fails}} + foo_void(ptr, neg); // expected-error{{negative size value of -1 for 'ptr' of type 'void *__single __sized_by(len)' (aka 'void *__single')}} + + foo(ptr_auto_bound, len); + foo(ptr_auto_bound, 1); + foo(ptr_auto_bound, zero); + foo(ptr_auto_bound, eight); + foo(ptr_auto_bound, neg); // expected-error{{negative size value of -1 for 'ptr' of type 'int *__single __sized_by(len)' (aka 'int *__single')}} + + int local_len; + // expected-warning@+1{{possibly initializing 'local_ptr' of type 'int *__single __sized_by(local_len)' (aka 'int *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + int *__sized_by(local_len) local_ptr = ptr; + int *__sized_by(4) local_ptr2 = ptr; + // expected-error@+1{{initializing 'local_ptr3' of type 'int *__single __sized_by(5)' (aka 'int *__single') and size value of 5 with 'int *__single' and pointee of size 4 always fails}} + int *__sized_by(5) local_ptr3 = ptr; + const int three = 3 * sizeof(int); + // expected-error@+1{{initializing 'local_ptr4' of type 'int *__single __sized_by(12)' (aka 'int *__single') and size value of 12 with 'int *__single' and pointee of size 4 always fails}} + int *__sized_by(three) local_ptr4 = ptr; + const int neg2 = -1; + // expected-error@+1{{negative size value of -1 for 'local_ptr5' of type 'int *__single __sized_by(-1)' (aka 'int *__single')}} + int *__sized_by(neg2) local_ptr5 = ptr; +} + +void baz(int *__sized_by(len) ptr1, int *__sized_by(4) ptr2, int len) { + int *__single sp; + int *__unsafe_indexable up; + // expected-warning@+1{{size value is not statically known: assigning to 'ptr1' of type 'int *__single __sized_by(len)' (aka 'int *__single') from 'int *__single' is invalid for any size greater than 4}} + ptr1 = sp; + // expected-note@+1{{size assigned here}} + len = len; + ptr1 = up; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + ptr2 = sp; + ptr2 = up; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} + +struct qux; + +void quux(void *__sized_by(42) ptr1, + void *__sized_by(0) ptr2, + void *__sized_by(len) ptr3, int len, + struct qux *__single qux) { + // expected-error@+1{{assigning to 'ptr1' of type 'void *__single __sized_by(42)' (aka 'void *__single') with size value of 42 from 'struct qux *__single' with pointee of size 0 always fails}} + ptr1 = qux; + + ptr2 = qux; // ok + + // expected-error@+1{{assigning to 'ptr3' of type 'void *__single __sized_by(len)' (aka 'void *__single') with size value of 42 from 'struct qux *__single' with pointee of size 0 always fails}} + ptr3 = qux; + len = 42; +} + +struct unsized; + +void external(struct unsized *__sized_by(size) p, int size); + +void convert_unsized(struct unsized *__single p, int size) { + // expected-error@+1{{argument of '__sized_by' attribute cannot refer to declaration of a different lifetime}} + struct unsized *__single __sized_by(size) a = p; // requires explicit __single because of rdar://112196572 + // expected-warning@+1{{size value is not statically known: passing 'struct unsized *__single' to parameter 'p' of type 'struct unsized *__single __sized_by(size)' (aka 'struct unsized *__single') is invalid for any size greater than 0}} + external(p, size); // expected-note{{size passed here}} +} + +void convert_unsized2(struct unsized *__single p, int size) { + // expected-note@+1{{size initialized here}} + int s = size; + // expected-warning@+1{{size value is not statically known: initializing 'a' of type 'struct unsized *__single __sized_by(s)' (aka 'struct unsized *__single') with 'struct unsized *__single' is invalid for any size greater than 0}} + struct unsized *__single __sized_by(s) a = p; // requires explicit __single because of rdar://112196572 + // expected-warning@+1{{size value is not statically known: passing 'struct unsized *__single' to parameter 'p' of type 'struct unsized *__single __sized_by(size)' (aka 'struct unsized *__single') is invalid for any size greater than 0}} + external(p, size); // expected-note{{size passed here}} +} diff --git a/clang/test/BoundsSafety/Sema/single-unknown-size-casts.c b/clang/test/BoundsSafety/Sema/single-unknown-size-casts.c new file mode 100644 index 0000000000000..d9d39e9cf8057 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/single-unknown-size-casts.c @@ -0,0 +1,26 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +extern struct a __a; + +struct b { + void *a; +}; + +struct b b = { + .a = &__a, +}; + +void foo(struct b b, struct a * a) { + a = b.a; +} + +struct c; + +void bar(struct c * c, struct a * a) { + a = c; // expected-warning{{incompatible pointer types assigning to 'struct a *__single' from 'struct c *__single'}} +} diff --git a/clang/test/BoundsSafety/Sema/sized-by-ptr-arith-constant-count.c b/clang/test/BoundsSafety/Sema/sized-by-ptr-arith-constant-count.c new file mode 100644 index 0000000000000..545c3fa0f5d55 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/sized-by-ptr-arith-constant-count.c @@ -0,0 +1,766 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected,legacy %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fbounds-safety-bringup-missing-checks=indirect_count_update -verify=expected,legacy,extra %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -fbounds-safety-bringup-missing-checks=compound_literal_init -verify=expected,legacy,cli %s +#include + +void consume_sb(int* __sized_by(3) p); +void consume_sbon(int* __sized_by_or_null(3) p); + +struct sb { + int count; + int* __sized_by(count) buf; +}; + +struct sbon { + int count; + int* __sized_by_or_null(count) buf; +}; + +void side_effect(void); + +int global_arr [2] = {0}; +// expected-note@+2 4{{__sized_by attribute is here}} +// extra-note@+1 2{{__sized_by attribute is here}} +int*__sized_by(2) global_sb = global_arr; +// expected-note@+2 4{{__sized_by_or_null attribute is here}} +// extra-note@+1 2{{__sized_by_or_null attribute is here}} +int*__sized_by_or_null(2) global_sbon = global_arr; + +const int const_size = 2; +// expected-note@+2 4{{__sized_by attribute is here}} +// extra-note@+1 2{{__sized_by attribute is here}} +int*__sized_by(const_size) global_sb_const_qual_count = global_arr; +// expected-note@+2 4{{__sized_by_or_null attribute is here}} +// extra-note@+1 2{{__sized_by_or_null attribute is here}} +int*__sized_by_or_null(const_size) global_sbon_const_qual_count = global_arr; + +// expected-note@+2 4{{__sized_by attribute is here}} +// extra-note@+1 2{{__sized_by attribute is here}} +int*__sized_by(1+1) global_sb_opo = global_arr; +// expected-note@+2 4{{__sized_by_or_null attribute is here}} +// extra-note@+1 2{{__sized_by_or_null attribute is here}} +int*__sized_by_or_null(1+1) global_sbon_opo = global_arr; + +// legacy-note@+3 1{{__sized_by attribute is here}} +// expected-note@+2 8{{__sized_by attribute is here}} +// extra-note@+1 3{{__sized_by attribute is here}} +int* __sized_by(2) test_sb(int* __sized_by(3) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__sized_by attribute is here}} + // extra-note@+1 2{{__sized_by attribute is here}} + int* __sized_by(2) local_sb = p; + local_sb = p; // OK + side_effect(); + ++local_sb; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sb++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + --local_sb; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local_sb--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local_sb += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sb -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_sb++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++local_sb = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *--local_sb = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *local_sb-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local = local_sb + 1; // OK because `local_sb` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_sb++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + ++global_sb; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sb--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + --global_sb; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + global_sb += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sb -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_sb++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++global_sb = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *global_sb-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *--global_sb = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + global_sb = global_sb + 1; // OK because `global_sb` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + consume_sb(++p); // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + struct sb S = { + .count = 2, + // legacy-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + S = (struct sb){.buf = p++}; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} +} + +// legacy-note@+3 1{{__sized_by attribute is here}} +// expected-note@+2 8{{__sized_by attribute is here}} +// extra-note@+1 3{{__sized_by attribute is here}} +int* __sized_by(2) test_sb_constant_fold_count(int* __sized_by(2+1) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__sized_by attribute is here}} + // extra-note@+1 2{{__sized_by attribute is here}} + int* __sized_by(1+1) local_sb = p; + local_sb = p; // OK + side_effect(); + ++local_sb; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sb++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + --local_sb; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local_sb--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local_sb += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sb -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_sb++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++local_sb = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *--local_sb = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *local_sb-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local = local_sb + 1; // OK because `local_sb` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_sb_opo++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + ++global_sb_opo; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sb_opo--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + --global_sb_opo; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + global_sb_opo += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sb_opo -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_sb_opo++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++global_sb_opo = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *global_sb_opo-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *--global_sb_opo = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + global_sb_opo = global_sb_opo + 1; // OK because `global_sb` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + consume_sb(++p); // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + struct sb S = { + .count = 2, + // legacy-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + S = (struct sb){.buf = p++}; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size of 3 always traps}} +} + +// legacy-note@+3 1{{__sized_by attribute is here}} +// expected-note@+2 8{{__sized_by attribute is here}} +// extra-note@+1 3{{__sized_by attribute is here}} +int* __sized_by(size) test_sb_const_qualified_size(const int size, int* __sized_by(size) p) { + int* local; + // Modify local var + const int local_size = 2; + // expected-note@+2 4{{__sized_by attribute is here}} + // extra-note@+1 2{{__sized_by attribute is here}} + int* __sized_by(local_size) local_sb = p; + side_effect(); + local_sb = p; // OK + + side_effect(); + ++local_sb; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sb++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + --local_sb; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local_sb--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + local_sb += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sb -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_sb++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++local_sb = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *--local_sb = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *local_sb-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + local = local_sb + 1; // OK because `local_sb` gets promoted to a __bidi_indexable first. + side_effect(); + + // Modify global + global_sb_const_qual_count++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + ++global_sb_const_qual_count; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + --global_sb_const_qual_count; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + global_sb_const_qual_count += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sb_const_qual_count-= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_sb_const_qual_count++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++global_sb_const_qual_count = 1; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *global_sb_const_qual_count-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *--global_sb_const_qual_count = 1; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + global_sb_const_qual_count = global_sb_const_qual_count + 1; // OK because `global_sb` gets promoted to a __bidi_indexable first. + + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__sized_by' attributed pointer with constant size 'size' always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__sized_by' attributed pointer with constant size 'size' always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} + + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + + side_effect(); + + consume_sb(++p); // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + struct sb S = { + .count = 2, + // legacy-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + }; + // cli-error@+1{{initalizer for '__sized_by' pointer with side effects is not yet supported}} + S = (struct sb){.buf = p++}; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__sized_by' attributed pointer with constant size 'size' always traps}} +} + +// legacy-note@+3 1{{__sized_by_or_null attribute is here}} +// expected-note@+2 8{{__sized_by_or_null attribute is here}} +// extra-note@+1 3{{__sized_by_or_null attribute is here}} +int* __sized_by_or_null(2) test_sbon(int* __sized_by_or_null(3) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__sized_by_or_null attribute is here}} + // extra-note@+1 2{{__sized_by_or_null attribute is here}} + int* __sized_by_or_null(2) local_sbon = p; + local_sbon = p; // OK + side_effect(); + ++local_sbon; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sbon++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + --local_sbon; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local_sbon--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local_sbon += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sbon -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_sbon++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++local_sbon = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *--local_sbon = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *local_sbon-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local = local_sbon + 1; // OK because `local_sbon` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_sbon++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + ++global_sbon; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sbon--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + --global_sbon; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + global_sbon += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sbon -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_sbon++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++global_sbon = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *global_sbon-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *--global_sbon = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + global_sbon = global_sbon + 1; // OK because `global_sbon` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + consume_sbon(++p); // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + struct sbon S = { + .count = 2, + // legacy-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + S = (struct sbon){.buf = p++}; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} +} + +// legacy-note@+3 1{{__sized_by_or_null attribute is here}} +// expected-note@+2 8{{__sized_by_or_null attribute is here}} +// extra-note@+1 3{{__sized_by_or_null attribute is here}} +int* __sized_by_or_null(2) test_sbon_constant_fold_count(int* __sized_by_or_null(2+1) p) { + int* local; + + // Modify local var + // expected-note@+2 4{{__sized_by_or_null attribute is here}} + // extra-note@+1 2{{__sized_by_or_null attribute is here}} + int* __sized_by_or_null(1+1) local_sbon = p; + local_sbon = p; // OK + side_effect(); + ++local_sbon; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sbon++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + --local_sbon; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local_sbon--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local_sbon += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sbon -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_sbon++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++local_sbon = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *--local_sbon = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *local_sbon-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local = local_sbon + 1; // OK because `local_sbon` gets promoted to a __bidi_indexable first. + side_effect(); + + + // Modify global + global_sbon_opo++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + ++global_sbon_opo; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sbon_opo--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + --global_sbon_opo; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + global_sbon_opo += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sbon_opo -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_sbon_opo++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++global_sbon_opo = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *global_sbon_opo-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *--global_sbon_opo = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + global_sbon_opo = global_sbon_opo + 1; // OK because `global_sbon` gets promoted to a __bidi_indexable first. + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + consume_sbon(++p); // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + struct sbon S = { + .count = 2, + // legacy-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + }; + // cli-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + S = (struct sbon){.buf = p++}; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 3 always traps}} +} + +// legacy-note@+3 1{{__sized_by_or_null attribute is here}} +// expected-note@+2 8{{__sized_by_or_null attribute is here}} +// extra-note@+1 3{{__sized_by_or_null attribute is here}} +int* __sized_by_or_null(size) test_sbon_const_qualified_size(const int size, int* __sized_by_or_null(size) p) { + int* local; + // Modify local var + const int local_size = 2; + // expected-note@+2 4{{__sized_by_or_null attribute is here}} + // extra-note@+1 2{{__sized_by_or_null attribute is here}} + int* __sized_by_or_null(local_size) local_sbon = p; + side_effect(); + local_sbon = p; // OK + + side_effect(); + ++local_sbon; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sbon++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + --local_sbon; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local_sbon--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + local_sbon += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + local_sbon -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *local_sbon++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++local_sbon = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *--local_sbon = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *local_sbon-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + local = local_sbon + 1; // OK because `local_sbon` gets promoted to a __bidi_indexable first. + side_effect(); + + // Modify global + global_sbon_const_qual_count++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + ++global_sbon_const_qual_count; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + --global_sbon_const_qual_count; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + global_sbon_const_qual_count += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + global_sbon_const_qual_count-= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *global_sbon_const_qual_count++ = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + *++global_sbon_const_qual_count = 1; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size of 2 always traps}} + side_effect(); + + *global_sbon_const_qual_count-- = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *--global_sbon_const_qual_count = 1; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + global_sbon_const_qual_count = global_sbon_const_qual_count + 1; // OK because `global_sbon` gets promoted to a __bidi_indexable first. + + + // Modify param + ++p; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + side_effect(); + p++; // expected-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + side_effect(); + --p; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + p--; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + p += 1; // expected-error{{compound addition-assignment on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + side_effect(); + p -= 1; // expected-error{{compound subtraction-assignment on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + side_effect(); + + // Only when -fbounds-safety-bringup-missing-checks=indirect_count_update + *++p = 0; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + side_effect(); + *p++ = 0; // extra-error{{positive pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + + *--p = 0; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + side_effect(); + *p-- = 0; // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} + + + p *= 1; // expected-error{{invalid operands to binary expression}} + p *= 2; // expected-error{{invalid operands to binary expression}} + p |= 2; // expected-error{{invalid operands to binary expression}} + p &= 2; // expected-error{{invalid operands to binary expression}} + p ^= 2; // expected-error{{invalid operands to binary expression}} + p %= 2; // expected-error{{invalid operands to binary expression}} + p <<= 0; // expected-error{{invalid operands to binary expression}} + p >>= 0; // expected-error{{invalid operands to binary expression}} + + // Increment in other contexts + // legacy-error@+2{{multiple consecutive assignments to a dynamic count pointer 'p' must be simplified; keep only one of the assignments}} + // legacy-note@+1{{previously assigned here}} + p++, p = local; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + side_effect(); + *(++p) = 0; // extra-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + + side_effect(); + + consume_sbon(++p); // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + struct sbon S = { + .count = 2, + // legacy-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + .buf = ++p // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + }; + // cli-error@+1{{initalizer for '__sized_by_or_null' pointer with side effects is not yet supported}} + S = (struct sbon){.buf = p++}; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} + + side_effect(); + + return ++p; // expected-error{{pointer arithmetic on '__sized_by_or_null' attributed pointer with constant size 'size' always traps}} +} + +// Warning diagnostic tests + +void downgrade_to_warning(int* __sized_by(4) ptr) { // expected-note{{__sized_by attribute is here}} +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wbounds-safety-externally-counted-ptr-arith-constant-count" + ++ptr; // expected-warning{{positive pointer arithmetic on '__sized_by' attributed pointer with constant size of 4 always traps}} +#pragma clang diagnostic pop +} + +void downgrade_to_ignored(int* __sized_by(4) ptr) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wbounds-safety-externally-counted-ptr-arith-constant-count" + ++ptr; // ok +} diff --git a/clang/test/BoundsSafety/Sema/sized_by_incdec.c b/clang/test/BoundsSafety/Sema/sized_by_incdec.c new file mode 100644 index 0000000000000..4becdcee32b20 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/sized_by_incdec.c @@ -0,0 +1,160 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +void Foo(int *__sized_by(*len) *ptr, int *len) { + (*ptr)++; // expected-note{{previously assigned here}} + // expected-error@+2{{assignment to 'int *__single __sized_by(*len)' (aka 'int *__single') '*ptr' requires corresponding assignment to '*len'; add self assignment '*len = *len' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'ptr' must be simplified; keep only one of the assignments}} + ++(*ptr); +} + +void FooOrNull(int *__sized_by_or_null(*len) *ptr, int *len) { + (*ptr)++; // expected-note{{previously assigned here}} + // expected-error@+2{{assignment to 'int *__single __sized_by_or_null(*len)' (aka 'int *__single') '*ptr' requires corresponding assignment to '*len'; add self assignment '*len = *len' if the value has not changed}} + // expected-error@+1{{multiple consecutive assignments to a dynamic count pointer 'ptr' must be simplified; keep only one of the assignments}} + ++(*ptr); +} + +void Bar(int *__sized_by(*len) *ptr, int *len) { + *ptr = 0; + (*len)++; +} + +void BarOrNull(int *__sized_by_or_null(*len) *ptr, int *len) { + *ptr = 0; + (*len)++; +} + +void TestPtrPostIncrement(int *__sized_by(*len) *ptr, int *len) { + (*ptr)++; + (*len)--; +} + +void TestPtrPostIncrementOrNull(int *__sized_by_or_null(*len) *ptr, int *len) { + (*ptr)++; + (*len)--; +} + +void TestLenPostIncrementOrNull(int *__sized_by_or_null(*len) ptr, int *len) { + ptr = ptr; + (*len)++; // expected-error{{incrementing '*len' without updating 'ptr' always traps}} +} + +void TestLenPostIncrement(int *__sized_by(*len) ptr, int *len) { + ptr = ptr; + (*len)++; // expected-error{{incrementing '*len' without updating 'ptr' always traps}} +} + +void TestPtrPreDecrement(int *__sized_by(*len) *ptr, int *len) { + --(*ptr); // expected-error{{negative pointer arithmetic on '__sized_by' pointer always traps}} +} + +void TestPtrPreDecrementOrNull(int *__sized_by_or_null(*len) *ptr, int *len) { + --(*ptr); // expected-error{{negative pointer arithmetic on '__sized_by_or_null' pointer always traps}} +} + +typedef struct { + char *__sized_by(len1 + len2) buf; + unsigned len1; + unsigned len2; +} S; + +void TestMultipleCounts1(S *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2 = sp->len2; + sp->len1++; +} + +void TestMultipleCounts2(S *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2++; + sp->len1 = sp->len1; +} + +void TestMultipleCounts3(S *sp) { + sp->buf = sp->buf; + sp->len2++; // expected-error{{incrementing 'sp->len2' without updating 'sp->buf' always traps}} + sp->len1 = sp->len1; +} + +typedef struct { + char *__sized_by_or_null(len1 + len2) buf; + unsigned len1; + unsigned len2; +} SOrNull; + +void TestMultipleCounts1OrNull(SOrNull *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2 = sp->len2; + sp->len1++; +} + +void TestMultipleCounts2OrNull(S *sp, char *__bidi_indexable new_ptr) { + sp->buf = new_ptr; + sp->len2++; + sp->len1 = sp->len1; +} + +void TestMultipleCounts3OrNull(S *sp) { + sp->buf = sp->buf; + sp->len2++; // expected-error{{incrementing 'sp->len2' without updating 'sp->buf' always traps}} + sp->len1 = sp->len1; +} + +typedef struct { + char *__sized_by(len) buf; + unsigned len; +} T; + +void Baz(T *tp) { + tp->buf = tp->buf; + tp->len++; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} +} + +void Qux(T *tp) { + ++tp->len; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} + tp->buf = tp->buf; +} + +void Quux(T *tp) { + tp->buf = tp->buf; + tp->len--; +} + +void Quuz(T *tp) { + tp->len--; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __sized_by(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} + +void Corge(T *tp) { + tp->len+=2; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __sized_by(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} + +typedef struct { + char *__sized_by_or_null(len) buf; + unsigned len; +} TOrNull; + +void BazOrNull(TOrNull *tp) { + tp->buf = tp->buf; + tp->len++; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} +} + +void QuxOrNull(TOrNull *tp) { + ++tp->len; // expected-error{{incrementing 'tp->len' without updating 'tp->buf' always traps}} + tp->buf = tp->buf; +} + +void QuuxOrNull(TOrNull *tp) { + tp->buf = tp->buf; + tp->len--; +} + +void QuuzOrNull(TOrNull *tp) { + tp->len--; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __sized_by_or_null(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} + +void CorgeOrNull(TOrNull *tp) { + tp->len+=2; // expected-error{{assignment to 'tp->len' requires corresponding assignment to 'char *__single __sized_by_or_null(len)' (aka 'char *__single') 'tp->buf'; add self assignment 'tp->buf = tp->buf' if the value has not changed}} +} diff --git a/clang/test/BoundsSafety/Sema/source-location-for-counted-attribute-type.c b/clang/test/BoundsSafety/Sema/source-location-for-counted-attribute-type.c new file mode 100644 index 0000000000000..debc307b39a70 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/source-location-for-counted-attribute-type.c @@ -0,0 +1,97 @@ + +// RUN: not %clang_cc1 -fbounds-safety -fsyntax-only %s 2>&1 | FileCheck %s +#include + +// This test is essentially a test of +// `SourceLocationFor(const CountAttributedType *CATy, Sema &S)`. The test is +// checking the column number attached the diagnostic. +// FIXME: This should probably be a unit test. We can't test `__sized_by` or +// `__sized_by_or_null` here because the diagnostic we are relying isn't emitted +// for those attributes. + +struct IncompleteTy; // Incomplete + +//============================================================================== +// counted_by +//============================================================================== + +#define custom_counted_by(X) __attribute__((counted_by(X))) + +#define custom_counted_by__(X) __attribute__((__counted_by__(X))) + +#define COUNT_ARG 0 +struct CB { + int count; + // CHECK: [[@LINE+1]]:26: note: consider using '__sized_by' instead of '__counted_by' + struct IncompleteTy* __counted_by(count) b_macro; + // CHECK: [[@LINE+1]]:41: note: consider using '__sized_by' instead of '__counted_by' + struct IncompleteTy* __attribute__((counted_by(count))) b_direct_non_affixed; + // CHECK: [[@LINE+1]]:41: note: consider using '__sized_by' instead of '__counted_by' + struct IncompleteTy* __attribute__((__counted_by__(count))) b_direct_affixed; + // NOTE: In these cases the locations just point to the count expressions as + // a fallback. + // CHECK: [[@LINE+1]]:44: note: consider using '__sized_by' instead of '__counted_by' + struct IncompleteTy* custom_counted_by(count) b_custom_macro; + // CHECK: [[@LINE+1]]:46: note: consider using '__sized_by' instead of '__counted_by' + struct IncompleteTy* custom_counted_by__(count) b_custom_macro_underscored; + + // CHECK: [[@LINE+1]]:39: note: consider using '__sized_by' instead of '__counted_by' + struct IncompleteTy* __counted_by(COUNT_ARG) b_macro_macro_arg; + // CHECK: [[@LINE+1]]:52: note: consider using '__sized_by' instead of '__counted_by' + struct IncompleteTy* __attribute__((counted_by(COUNT_ARG))) b_direct_non_affixed_macro_arg; + // CHECK: [[@LINE+1]]:56: note: consider using '__sized_by' instead of '__counted_by' + struct IncompleteTy* __attribute__((__counted_by__(COUNT_ARG))) b_direct_affixed_macro_arg; +}; + +void useCB(struct CB* cb) { + struct IncompleteTy* local0 = cb->b_macro; + struct IncompleteTy* local1 = cb->b_direct_non_affixed; + struct IncompleteTy* local2 = cb->b_direct_affixed; + struct IncompleteTy* local3 = cb->b_custom_macro; + struct IncompleteTy* local4 = cb->b_custom_macro_underscored; + struct IncompleteTy* local5 = cb->b_macro_macro_arg; + struct IncompleteTy* local6 = cb->b_direct_non_affixed_macro_arg; + struct IncompleteTy* local7 = cb->b_direct_affixed_macro_arg; +} + +//============================================================================== +// counted_by_or_null +//============================================================================== + +#define custom_counted_by_or_null(X) __attribute__((counted_by_or_null(X))) +#define custom_counted_by_or_null__(X) __attribute__((__counted_by_or_null__(X))) + +struct CBON { + int count; + // CHECK: [[@LINE+1]]:26: note: consider using '__sized_by_or_null' instead of '__counted_by_or_null' + struct IncompleteTy* __counted_by_or_null(count) b_macro; + // CHECK: [[@LINE+1]]:41: note: consider using '__sized_by_or_null' instead of '__counted_by_or_null' + struct IncompleteTy* __attribute__((counted_by_or_null(count))) b_direct_non_affixed; + // CHECK: [[@LINE+1]]:41: note: consider using '__sized_by_or_null' instead of '__counted_by_or_null' + struct IncompleteTy* __attribute__((__counted_by_or_null__(count))) b_direct_affixed; + // NOTE: In these cases the locations just points to the count expressions as + // a fallback. + // CHECK: [[@LINE+1]]:52: note: consider using '__sized_by_or_null' instead of '__counted_by_or_null' + struct IncompleteTy* custom_counted_by_or_null(count) b_custom_macro; + // CHECK: [[@LINE+1]]:54: note: consider using '__sized_by_or_null' instead of '__counted_by_or_null' + struct IncompleteTy* custom_counted_by_or_null__(count) b_custom_macro_underscored; + + // CHECK: [[@LINE+1]]:47: note: consider using '__sized_by_or_null' instead of '__counted_by_or_null' + struct IncompleteTy* __counted_by_or_null(COUNT_ARG) b_macro_macro_arg; + // CHECK: [[@LINE+1]]:60: note: consider using '__sized_by_or_null' instead of '__counted_by_or_null' + struct IncompleteTy* __attribute__((counted_by_or_null(COUNT_ARG))) b_direct_non_affixed_macro_arg; + // CHECK: [[@LINE+1]]:64: note: consider using '__sized_by_or_null' instead of '__counted_by_or_null' + struct IncompleteTy* __attribute__((__counted_by_or_null__(COUNT_ARG))) b_direct_affixed_macro_arg; +}; + + +void useCBON(struct CBON* cbon) { + struct IncompleteTy* local0 = cbon->b_macro; + struct IncompleteTy* local1 = cbon->b_direct_non_affixed; + struct IncompleteTy* local2 = cbon->b_direct_affixed; + struct IncompleteTy* local3 = cbon->b_custom_macro; + struct IncompleteTy* local4 = cbon->b_custom_macro_underscored; + struct IncompleteTy* local5 = cbon->b_macro_macro_arg; + struct IncompleteTy* local6 = cbon->b_direct_non_affixed_macro_arg; + struct IncompleteTy* local7 = cbon->b_direct_affixed_macro_arg; +} diff --git a/clang/test/BoundsSafety/Sema/static-bound-ptr-init.c b/clang/test/BoundsSafety/Sema/static-bound-ptr-init.c new file mode 100644 index 0000000000000..bd5c7344cad72 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/static-bound-ptr-init.c @@ -0,0 +1,108 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -I%S/SystemHeaders/include -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -I%S/SystemHeaders/include -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include +struct T { + void (*fp)(const struct T *t); + int i; +}; + +static void foo(const struct T *t) {} + +const struct T t = { + .fp = foo, + .i = 0, +}; + +// BoundsSafetyPointerCast Evaluation Results +// Types - B: __bidi_indexable, T: __single, U: __unsafe_indexable, A: __indexable +// S: CString, C: Count +// Result - P: Compile-time constant, F: Compile-time error, +// R: No compile-time constant, Z: P and Zero-bound, +// D: Depending on the metadata +// * (at least one elem) +// ^ (upper bounds check) +// < (if the destination type size is smaller) + +// To | B | T | U | A | S | C | +// From| | | | | | | +// ---------------------------- +// B | P | R | P | R | R | R | +// T | P | P<| P | P | Z | RD| +// U | F | P | P | F | F | F | +// A | P | R | P | P | R | R | +// S | P | P | P | P*| P | R | +// C | P | D | P | P | R | R | + +int g_var = 0; + +// B -> B: P +int *__bidi_indexable bi_p = &g_var; +// B -> A: R^ +int *__indexable fi_p = &g_var; +// B -> T: R +int *__single s_p = &g_var; +// B -> U: P +int *__unsafe_indexable ui_p = &g_var; + +// T -> B: P +// expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type}} +void *__bidi_indexable bi_p3 = foo; // expected-note{{pointer 'bi_p3' declared here}} +// T -> A: P +// expected-error-re@+1{{cannot initialize indexable pointer {{.+}} from __single pointer to incomplete type}} +void *__indexable fi_p3 = foo; // expected-note{{pointer 'fi_p3' declared here}} +// T -> T: P +void *__single s_p3 = foo; +// T -> U: P +void *__unsafe_indexable ui_p2 = foo; + +void Test () { + // Special case: "unspecified" should convert to __unsafe_indexable + const char *__unsafe_indexable ui_p3 = mock_system_func(); + + static int len1; + // B -> C: R + // expected-warning@+1{{possibly initializing 'c_p1' of type 'int *__single __counted_by(len1)' (aka 'int *__single') and implicit count value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set count value to 0 to remove this warning}} + static int *__counted_by(len1) c_p1 = &g_var; // expected-error{{initializer element is not a compile-time constant}} + static int len2; + // T -> C: RD + +#pragma mark - UNEXPECTEDLY PASSES + // expected-warning@+1{{possibly initializing 'c_p2' of type 'void *__single __sized_by(len2)' (aka 'void *__single') and implicit size value of 0 with non-null, which creates a non-dereferenceable pointer; explicitly set size value to 0 to remove this warning}} + static void *__sized_by(len2) c_p2 = foo; // expected-error{{initializer element is not a compile-time constant}} + static int len3 = 1; + // T -> C: RD + // expected-error@+1{{initializing 'c_p3' of type 'void *__single __sized_by(len3)' (aka 'void *__single') and size value of 1 with 'void (*__single)(const struct T *__single)' and pointee of size 0 always fails}} + static void *__sized_by(len3) c_p3 = foo; // expected-error{{initializer element is not a compile-time constant}} +#pragma mark - + + static int s_var = 0; + static int *__counted_by(10) c_p4 = &s_var; // expected-error{{initializer element is not a compile-time constant}} + + static int s_arr[10]; + static int *__counted_by(10) c_p5 = s_arr; // ok + + static const int len4 = 1; + static int *__counted_by(len4) c_p6 = &g_var; // ok +} + +struct RangedStruct { + int *__ended_by(end) start; + int *end; +}; + +// TODO +void TestEndedBy() { + // expected-error@+2{{initializer element is not a compile-time constant}} + static struct RangedStruct rs = { + 0, 0 + }; + + static char s_arr[10]; + // expected-error@+1{{initializer element is not a compile-time constant}} + static char *end = &s_arr[10]; + // expected-error@+1{{initializer element is not a compile-time constant}} + static char *__ended_by(end) start = &s_arr[0]; +} diff --git a/clang/test/BoundsSafety/Sema/system-header-func-decl.h b/clang/test/BoundsSafety/Sema/system-header-func-decl.h new file mode 100644 index 0000000000000..0dd43c66b1c47 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-decl.h @@ -0,0 +1,3 @@ +#pragma clang system_header + +int* foo(int ** fp); diff --git a/clang/test/BoundsSafety/Sema/system-header-func-redecl-after-single.c b/clang/test/BoundsSafety/Sema/system-header-func-redecl-after-single.c new file mode 100644 index 0000000000000..9767110f342fa --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-redecl-after-single.c @@ -0,0 +1,15 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note + +// expected-no-diagnostics + +#include +#include "system-header-func-decl.h" + +int *__single foo(int *__single *__single); + +void bar(void) { + int *__single s; + foo(&s); +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/system-header-func-redecl-after-unsafe-indexable.c b/clang/test/BoundsSafety/Sema/system-header-func-redecl-after-unsafe-indexable.c new file mode 100644 index 0000000000000..4362c4d76e424 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-redecl-after-unsafe-indexable.c @@ -0,0 +1,15 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note + +// expected-no-diagnostics + +#include +#include "system-header-func-decl.h" + +int *__unsafe_indexable foo(int *__unsafe_indexable *__unsafe_indexable); + +void bar(void) { + int *__unsafe_indexable s; + foo(&s); +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/system-header-func-redecl-before-single.c b/clang/test/BoundsSafety/Sema/system-header-func-redecl-before-single.c new file mode 100644 index 0000000000000..3bb80ceda86bf --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-redecl-before-single.c @@ -0,0 +1,16 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note + +// expected-no-diagnostics + +#include + +int *__single foo(int *__single *__single); + +#include "system-header-func-decl.h" + +void bar(void) { + int *__single s; + foo(&s); +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/system-header-func-redecl-before-unsafe-indexable.c b/clang/test/BoundsSafety/Sema/system-header-func-redecl-before-unsafe-indexable.c new file mode 100644 index 0000000000000..309cb5e1bec4e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-redecl-before-unsafe-indexable.c @@ -0,0 +1,16 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note + +// expected-no-diagnostics + +#include + +int *__unsafe_indexable foo(int *__unsafe_indexable *__unsafe_indexable); + +#include "system-header-func-decl.h" + +void bar(void) { + int *__unsafe_indexable s; + foo(&s); +} \ No newline at end of file diff --git a/clang/test/BoundsSafety/Sema/system-header-func-redecl-indexable-0.c b/clang/test/BoundsSafety/Sema/system-header-func-redecl-indexable-0.c new file mode 100644 index 0000000000000..c8a291b05cb34 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-redecl-indexable-0.c @@ -0,0 +1,9 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note + +#include +#include "system-header-func-decl.h" + +// expected-error@+1{{conflicting types for 'foo'}} +int *__single foo(int *__indexable *__single); diff --git a/clang/test/BoundsSafety/Sema/system-header-func-redecl-indexable-1.c b/clang/test/BoundsSafety/Sema/system-header-func-redecl-indexable-1.c new file mode 100644 index 0000000000000..605f1c5f968d5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/system-header-func-redecl-indexable-1.c @@ -0,0 +1,9 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s -verify-ignore-unexpected=note +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s -verify-ignore-unexpected=note + +#include +#include "system-header-func-decl.h" + +// expected-error@+1{{conflicting types for 'foo'}} +int *__single foo(int *__single *__indexable); diff --git a/clang/test/BoundsSafety/Sema/template-specialization-sugars.cpp b/clang/test/BoundsSafety/Sema/template-specialization-sugars.cpp new file mode 100644 index 0000000000000..a48bc875bd089 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/template-specialization-sugars.cpp @@ -0,0 +1,38 @@ + + +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -std=c++17 -verify %s + +#include + +// expected-no-diagnostics + +struct true_type { + static constexpr bool value = true; +}; + +struct false_type { + static constexpr bool value = false; +}; + +template +struct is_ptr : false_type {}; + +template +struct is_ptr : true_type {}; + +template +inline constexpr bool is_ptr_v = is_ptr::value; + +int main() { + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + static_assert(is_ptr_v); + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-array-empty.c b/clang/test/BoundsSafety/Sema/terminated-by-array-empty.c new file mode 100644 index 0000000000000..c8d6c585f5015 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-array-empty.c @@ -0,0 +1,14 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Empty { + int a[__null_terminated 0]; // expected-error{{'__terminated_by' attribute cannot be applied to empty arrays}} +}; + +void empty(void) { + int a[__null_terminated 0]; // expected-error{{'__terminated_by' attribute cannot be applied to empty arrays}} + int b[__null_terminated] = {}; // expected-error{{incomplete array 'b' with '__terminated_by' attribute must be initialized with at least one element}} +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-array-init.c b/clang/test/BoundsSafety/Sema/terminated-by-array-init.c new file mode 100644 index 0000000000000..993f08306750f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-array-init.c @@ -0,0 +1,427 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include +#include + +typedef __WCHAR_TYPE__ wchar_t; +typedef __CHAR16_TYPE__ char16_t; +typedef __CHAR32_TYPE__ char32_t; + +struct Foo { + int a[__null_terminated 2]; + int b[__terminated_by(42) 2]; +}; + +struct Bar { + int a[__null_terminated 2]; +}; + +struct Baz { + char a[__terminated_by('X') 3]; + char b[__null_terminated 3]; +}; + +struct Qux { + struct Foo foo; + struct Bar bar; + struct Baz baz; +}; + +struct Quux { + const char *__null_terminated p; +}; + +void explicit_const_init(void) { + // ok + int a1[__null_terminated 3] = {1, 2, 0}; + + // expected-error@+1{{array 'a2' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '3')}} + int a2[__null_terminated 3] = {1, 2, 3}; + + // ok + int a3[__terminated_by(42) 3] = {1, 2, 42}; + + // expected-error@+1{{array 'a4' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + int a4[__terminated_by(42) 3] = {1, 2, 0}; + + // ok + char s1[__null_terminated 3] = "HI"; + + // expected-error@+1{{array 's2' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got '0')}} + char s2[__terminated_by('X') 3] = "HI"; + + // ok + struct Foo foo1 = {{1, 0}, {2, 42}}; + + // expected-error@+1{{array 'foo2.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '1')}} + struct Foo foo2 = {{1, 1}, {2, 42}}; + + // expected-error@+2{{array 'foo3.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '1')}} + // expected-error@+1{{array 'foo3.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '2')}} + struct Foo foo3 = {{1, 1}, {2, 2}}; + + // ok + struct Qux qux1 = {{{1, 0}, {2, 42}}, {{1, 0}}, {{'Z', 'Y', 'X'}, "HI"}}; + + // expected-error@+5{{array 'qux2.foo.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '1')}} + // expected-error@+4{{array 'qux2.foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '2')}} + // expected-error@+3{{array 'qux2.bar.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '3')}} + // expected-error@+2{{array 'qux2.baz.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got '0')}} + // expected-error@+1{{array 'qux2.baz.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got ''X'')}} + struct Qux qux2 = {{{1, 1}, {2, 2}}, {{1, 3}}, {"HI", {'Z', 'Y', 'X'}}}; +} + +void explicit_const_init_excess(void) { + // expected-warning@+1{{excess elements in array initializer}} + int a1[__null_terminated 3] = {1, 2, 0, 4}; + + // expected-warning@+2{{excess elements in array initializer}} + // expected-error@+1{{array 'a2' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '3')}} + int a2[__null_terminated 3] = {1, 2, 3, 4}; + + // expected-warning@+1{{excess elements in array initializer}} + int a3[__terminated_by(42) 3] = {1, 2, 42, 4}; + + // expected-warning@+2{{excess elements in array initializer}} + // expected-error@+1{{array 'a4' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + int a4[__terminated_by(42) 3] = {1, 2, 0, 42}; + + // ok + char s1[__null_terminated 3] = "HI\0"; + + // expected-error@+1{{array 's2' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got ''J'')}} + char s2[__terminated_by('X') 3] = "HEJ"; + + // expected-warning@+1{{initializer-string for char array is too long}} + char s3[__null_terminated 3] = "HI\0HI"; + + // expected-warning@+2{{initializer-string for char array is too long}} + // expected-error@+1{{array 's4' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got ''C'')}} + char s4[__terminated_by('X') 3] = "ABCD"; + + // expected-warning@+2{{excess elements in array initializer}} + // expected-warning@+1{{excess elements in array initializer}} + struct Foo foo1 = {{1, 0, 1}, {2, 42, 0}}; + + // expected-warning@+2{{excess elements in array initializer}} + // expected-error@+1{{array 'foo2.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '1')}} + struct Foo foo2 = {{1, 1, 0}, {2, 42}}; + + // expected-warning@+4{{excess elements in array initializer}} + // expected-warning@+3{{excess elements in array initializer}} + // expected-error@+2{{array 'foo3.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '1')}} + // expected-error@+1{{array 'foo3.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '2')}} + struct Foo foo3 = {{1, 1, 0}, {2, 2, 42}}; + + // ok + struct Baz baz1 = {"ZYX", "HI\0"}; + + // expected-warning@+2{{initializer-string for char array is too long}} + // expected-warning@+1{{initializer-string for char array is too long}} + struct Baz baz2 = {"ZYXW", "HI\0HI"}; + + // expected-warning@+4{{initializer-string for char array is too long}} + // expected-warning@+3{{initializer-string for char array is too long}} + // expected-error@+2{{array 'baz3.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got ''C'')}} + // expected-error@+1{{array 'baz3.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got ''X'')}} + struct Baz baz3 = {"ABCD", "ZYXW"}; + + // expected-warning@+4{{excess elements in array initializer}} + // expected-warning@+3{{excess elements in array initializer}} + // expected-warning@+2{{excess elements in array initializer}} + // expected-warning@+1{{excess elements in array initializer}} + struct Qux qux1 = {{{1, 0, 1}, {2, 42, 0}}, {{1, 0, 1}}, {{'Z', 'Y', 'X', 'W'}, "HI\0"}}; + + // expected-warning@+8{{excess elements in array initializer}} + // expected-warning@+7{{excess elements in array initializer}} + // expected-warning@+6{{excess elements in array initializer}} + // expected-error@+5{{array 'qux2.foo.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '1')}} + // expected-error@+4{{array 'qux2.foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '2')}} + // expected-error@+3{{array 'qux2.bar.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '3')}} + // expected-error@+2{{array 'qux2.baz.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got ''A'')}} + // expected-error@+1{{array 'qux2.baz.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got ''J'')}} + struct Qux qux2 = {{{1, 1, 0}, {2, 2}}, {{1, 3, 0}}, {{'Z', 'Y', 'A', 'W'}, "HEJ"}}; +} + +void implicit_init(void) { + // ok + int a1[__null_terminated 3] = {}; + + // ok + int a2[__null_terminated 3] = {1, 2}; + + // expected-error@+1{{array 'a3' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + int a3[__terminated_by(42) 3] = {}; + + // expected-error@+1{{array 'a4' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + int a4[__terminated_by(42) 3] = {1, 2}; + + // ok + char s1[__null_terminated 3] = ""; + + // ok + char s2[__null_terminated 3] = "X"; + + // expected-error@+1{{array 'foo1.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + struct Foo foo1 = {}; + + // expected-error@+1{{array 'foo2.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + struct Foo foo2 = {{}}; + + // expected-error@+1{{array 'foo3.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + struct Foo foo3 = {{}, {}}; + + // ok + struct Foo foo4 = {{}, {1, 42}}; + + // ok + struct Foo foo5 = {{1}, {1, 42}}; + + // ok + struct Bar bar1 = {}; + + // ok + struct Bar bar2 = {{}}; + + // expected-error@+1{{array 'baz1.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got '0')}} + struct Baz baz1 = {}; + + // expected-error@+1{{array 'baz2.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got '0')}} + struct Baz baz2 = {{}}; + + // expected-error@+1{{array 'baz3.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got '0')}} + struct Baz baz3 = {{}, {}}; + + // ok + struct Baz baz4 = {"ZYX"}; + + // expected-error@+2{{array 'qux1.foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + // expected-error@+1{{array 'qux1.baz.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got '0')}} + struct Qux qux1 = {}; + + // expected-error@+2{{array 'qux2.foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + // expected-error@+1{{array 'qux2.baz.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got '0')}} + struct Qux qux2 = {{}, {}, {}}; + + // expected-error@+2{{array 'qux3.foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + // expected-error@+1{{array 'qux3.baz.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got '0')}} + struct Qux qux3 = {{{}, {}}, {{}}, {{}, {}}}; + + // ok + struct Qux qux4 = {{{}, {1, 42}}, {}, {{'Z', 'Y', 'X'}}}; + + // ok + struct Qux qux5 = {{{}, {1, 42}}, {}, {{'Z', 'Y', 'X'}, {}}}; +} + +void explicit_non_const_init(int i, char c) { + // expected-error@+1{{terminator in array 'a1' must be a compile-time constant}} + int a1[__null_terminated 3] = {1, 2, i}; + + // ok + int a2[__null_terminated 3] = {i, i, 0}; + + // expected-error@+5{{terminator in array 'qux1.foo.a' must be a compile-time constant}} + // expected-error@+4{{terminator in array 'qux1.foo.b' must be a compile-time constant}} + // expected-error@+3{{terminator in array 'qux1.bar.a' must be a compile-time constant}} + // expected-error@+2{{terminator in array 'qux1.baz.a' must be a compile-time constant}} + // expected-error@+1{{terminator in array 'qux1.baz.b' must be a compile-time constant}} + struct Qux qux1 = {{{0, i}, {0, i}}, {{0, i}}, {{'A', 'A', c}, {'A', 'A', c}}}; + + // ok + struct Qux qux2 = {{{i, 0}, {i, 42}}, {{i, 0}}, {{c, c, 'X'}, {c, c, '\0'}}}; +} + +void incomplete_array_init(void) { + // ok + int a1[__null_terminated] = {1, 2, 0}; + + // expected-error@+1{{array 'a2' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '3')}} + int a2[__null_terminated] = {1, 2, 3}; + + // ok + int a3[__null_terminated] = (int[3]){1, 2, 0}; + + // expected-error@+1{{array 'a4' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '3')}} + int a4[__null_terminated] = (int[3]){1, 2, 3}; + + // ok + char s1[__null_terminated] = "HI"; + + // expected-error@+1{{array 's2' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got '0')}} + char s2[__terminated_by('X')] = "HI"; +} + +void string_literal_init(void) { + // ok + char s1[__null_terminated 3] = "HI"; + + // ok + wchar_t s2[__null_terminated 3] = L"HI"; + + // ok + char16_t s3[__null_terminated 3] = u"HI"; + + // ok + char32_t s4[__null_terminated 3] = U"HI"; + + // ok + char s5[__terminated_by('X') 3] = "ZYX"; + + // ok + wchar_t s6[__terminated_by(L'X') 3] = L"ZYX"; + + // ok + char16_t s7[__terminated_by(u'X') 3] = u"ZYX"; + + // ok + char32_t s8[__terminated_by(U'X') 3] = U"ZYX"; + + // expected-error@+1{{array 's9' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got ''C'')}} + char s9[__terminated_by('X') 3] = "ABC"; + + // expected-error@+1{{array 's10' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'L'X''; got 'L'C'')}} + wchar_t s10[__terminated_by(L'X') 3] = L"ABC"; + + // expected-error@+1{{array 's11' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'u'X''; got 'u'C'')}} + char16_t s11[__terminated_by(u'X') 3] = u"ABC"; + + // expected-error@+1{{array 's12' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: 'U'X''; got 'U'C'')}} + char32_t s12[__terminated_by(U'X') 3] = U"ABC"; + + // ok + wchar_t s13[__terminated_by(U'\U0010F00C') 3] = L"\U0010F00A\U0010F00B\U0010F00C"; + + // ok + char16_t s14[__terminated_by(u'\u1122') 3] = u"\u1120\u1121\u1122"; + + // ok + char32_t s15[__terminated_by(U'\U0010F00C') 3] = U"\U0010F00A\U0010F00B\U0010F00C"; +} + +void sign_test(void) { + // ok + signed int a1[__terminated_by(-1) 2] = {0, -1}; + + // ok + unsigned int a2[__terminated_by(-1) 2] = {0, -1}; + + // ok + unsigned long long a3[__terminated_by(-1) 2] = {0, -1}; +} + +void array_of_pointers(void) { + // ok + char *__null_terminated a1[__null_terminated 3] = {"foo", "bar", 0}; + + // expected-error@+1{{terminator in array 'a2' must be a compile-time constant}} + char *__null_terminated a2[__null_terminated 3] = {"foo", "bar", "baz"}; + + // ok + char *__null_terminated a3[__null_terminated 3] = {"foo", 0, 0}; + + // ok + char *__null_terminated a4[__null_terminated 3] = {}; + + // ok + char *__null_terminated a5[__null_terminated 3] = {0}; + + // ok + char *__null_terminated a6[__null_terminated 3] = {"foo"}; + + // ok + char *__null_terminated a7[3] = {}; + + // ok + char *__null_terminated a8[3] = {0}; + + // ok + char *__null_terminated a9[3] = {"foo"}; +} + +void foo_as_param(struct Foo foo); +void bar_as_param(struct Bar bar); +void quux_as_param(struct Quux quux); + +void as_param(void) { + // expected-error@+1{{array 'foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + foo_as_param((struct Foo){}); + + // expected-error@+2{{array 'foo.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '2')}} + // expected-error@+1{{array 'foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '4')}} + foo_as_param((struct Foo){{1, 2}, {3, 4}}); + + // ok + foo_as_param((struct Foo){{1, 0}, {3, 42}}); + + // ok + bar_as_param((struct Bar){}); + + // expected-error@+1{{array 'bar.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '2')}} + bar_as_param((struct Bar){{1, 2}}); + + // ok + bar_as_param((struct Bar){{1, 0}}); +} + +struct Foo foo_as_ret(void) { + // expected-error@+1{{array '.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + return (struct Foo){}; + + // expected-error@+2{{array '.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '2')}} + // expected-error@+1{{array '.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '4')}} + return (struct Foo){{1, 2}, {3, 4}}; + + // ok + return (struct Foo){{1, 0}, {3, 42}}; +} + +struct Bar bar_as_ret(void) { + // ok + return (struct Bar){}; + + // expected-error@+1{{array '.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '0'; got '2')}} + return (struct Bar){{1, 2}}; + + // ok + return (struct Bar){{1, 0}}; +} + +struct Foo copy_init(void) { + struct Foo foo1 = {{1, 0}, {2, 42}}; + struct Bar bar1 = {}; + struct Baz baz1 = {"ZYX", "HI\0"}; + + // ok + struct Foo foo2 = foo1; + + // ok + struct Qux qux1 = { .foo = foo1, .bar = bar1, .baz = baz1 }; + + // ok + struct Qux qux2 = { .foo = foo1, /* .bar = ... */ .baz = baz1 }; + + // expected-error@+1{{array 'qux3.baz.a' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: ''X''; got '0'}} + struct Qux qux3 = { .foo = foo1, .bar = bar1 /* .baz = ... */ }; + + // ok + foo_as_param(foo1); + + // ok + return foo1; +} + +struct Quux copy_init2(void) { + struct Quux quux1 = { "Hello world" }; + + // ok + struct Quux quux2 = quux1; + + // ok + quux_as_param(quux1); + + // ok + return quux1; +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-array-uninit.c b/clang/test/BoundsSafety/Sema/terminated-by-array-uninit.c new file mode 100644 index 0000000000000..c4bf0afec4a96 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-array-uninit.c @@ -0,0 +1,71 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Foo { + int a[__null_terminated 2]; + int b[__terminated_by(42) 2]; +}; + +struct Bar { + int a[__null_terminated 2]; +}; + +struct Baz { + struct Foo f; + struct Bar b; +}; + +// ok +int g_a[__null_terminated 8]; + +// expected-error@+1{{array 'g_b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} +int g_b[__terminated_by(42) 8]; + +// expected-error@+1{{array 'g_foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} +struct Foo g_foo; + +// ok +struct Bar g_bar; + +// expected-error@+1{{array 'g_baz.f.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} +struct Baz g_baz; + +void no_init_static_local(void) { + // ok + static int a[__null_terminated 8]; + + // expected-error@+1{{array 'b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + static int b[__terminated_by(42) 8]; + + // expected-error@+1{{array 'foo.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + static struct Foo foo; + + // ok + static struct Bar bar; + + // expected-error@+1{{array 'baz.f.b' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + static struct Baz baz; +} + +void no_init_local(void) { + // expected-error@+1{{array 'a' with '__terminated_by' attribute must be initialized}} + int a[__null_terminated 8]; + + // expected-error@+1{{array 'b' with '__terminated_by' attribute must be initialized}} + int b[__terminated_by(42) 8]; + + // expected-error@+2{{array 'foo.a' with '__terminated_by' attribute must be initialized}} + // expected-error@+1{{array 'foo.b' with '__terminated_by' attribute must be initialized}} + struct Foo foo; + + // expected-error@+1{{array 'bar.a' with '__terminated_by' attribute must be initialized}} + struct Bar bar; + + // expected-error@+3{{array 'baz.f.a' with '__terminated_by' attribute must be initialized}} + // expected-error@+2{{array 'baz.f.b' with '__terminated_by' attribute must be initialized}} + // expected-error@+1{{array 'baz.b.a' with '__terminated_by' attribute must be initialized}} + struct Baz baz; +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-attr.c b/clang/test/BoundsSafety/Sema/terminated-by-attr.c new file mode 100644 index 0000000000000..98c4cc34114d0 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-attr.c @@ -0,0 +1,76 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// Check if the terminator is an ICE. +void term(int val) { + char a1[__terminated_by(0)] = ""; + char a2[__terminated_by(0x40 + 1)] = {'A'}; + char a3[__terminated_by(val)] = ""; // expected-error{{'__terminated_by' attribute requires an integer constant}} +} + +void multiple_attrs(void) { + char a1[__null_terminated __null_terminated] = ""; // expected-warning{{array annotated with __terminated_by multiple times. Annotate only once to remove this warning}} + char a2[__null_terminated __terminated_by(42)] = ""; // expected-error{{array cannot have more than one terminator attribute}} + // expected-note@-1{{conflicting arguments for terminator were '0' and '42'}} + char a3[__terminated_by(42)] = ""; // expected-error{{array 'a3' with '__terminated_by' attribute is initialized with an incorrect terminator (expected: '42'; got '0')}} + + char *__null_terminated __null_terminated p1 = ""; // expected-warning{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} + char *__null_terminated __terminated_by(42) p2 = ""; // expected-error{{pointer cannot have more than one terminator attribute}} + // expected-note@-1{{conflicting arguments for terminator were '0' and '42'}} + char * __terminated_by(0) __terminated_by(1) __terminated_by(2) __terminated_by(0) p3 = ""; + // expected-error@-1 2{{pointer cannot have more than one terminator attribute}} + // expected-note@-2{{conflicting arguments for terminator were '0' and '1'}} + // expected-note@-3{{conflicting arguments for terminator were '0' and '2'}} + // expected-warning@-4{{pointer annotated with __terminated_by multiple times. Annotate only once to remove this warning}} + + char *__null_terminated a4[__null_terminated 1] = {0}; // ok (the attributes apply to different types) +} + +void type(int v) { + char __null_terminated c; // expected-error{{'__terminated_by' attribute can be applied to pointers, constant-length arrays or incomplete arrays}} + char a1[__null_terminated v]; // expected-error{{'__terminated_by' attribute can be applied to pointers, constant-length arrays or incomplete arrays}} + + float a2[__null_terminated 1]; // expected-error{{element type of array with '__terminated_by' attribute must be an integer or a non-wide pointer}} + float *__null_terminated p1; // expected-error{{pointee type of pointer with '__terminated_by' attribute must be an integer or a non-wide pointer}} + + int(*__null_terminated p2)[1]; // expected-error{{pointee type of pointer with '__terminated_by' attribute must be an integer or a non-wide pointer}} + + struct Foo { + int x; + }; + + struct Foo a3[__null_terminated 1]; // expected-error{{element type of array with '__terminated_by' attribute must be an integer or a non-wide pointer}} + struct Foo *__null_terminated p3; // expected-error{{pointee type of pointer with '__terminated_by' attribute must be an integer or a non-wide pointer}} + + int *__single a4[__null_terminated 1] = {}; // ok + int *__single *__null_terminated p4 = 0; // ok + + int *__bidi_indexable a5[__null_terminated 1]; // expected-error{{element type of array with '__terminated_by' attribute must be an integer or a non-wide pointer}} + int *__bidi_indexable *__null_terminated p5; // expected-error{{pointee type of pointer with '__terminated_by' attribute must be an integer or a non-wide pointer}} +} + +void ptr_attrs(void) { + char *__null_terminated __single p1 = ""; // ok + char *__single __null_terminated p2 = ""; // ok + + char *__null_terminated __indexable p3 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + char *__indexable __null_terminated p4 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + + char *__null_terminated __bidi_indexable p5 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + char *__bidi_indexable __null_terminated p6 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + + char *__null_terminated __unsafe_indexable p7 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + char *__unsafe_indexable __null_terminated p8 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + + char *__null_terminated __counted_by(0) p9 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + char *__counted_by(0) __null_terminated p10 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + + char *__null_terminated __sized_by(0) p11 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + char *__sized_by(0) __null_terminated p12 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} + + char *__sized_by(0) p13 = 0; + __typeof__(p13) __null_terminated p14 = ""; // expected-error{{'__terminated_by' attribute currently can be applied only to '__single' pointers}} +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-explicit-casts.c b/clang/test/BoundsSafety/Sema/terminated-by-explicit-casts.c new file mode 100644 index 0000000000000..3a1ffa1e6f4c1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-explicit-casts.c @@ -0,0 +1,54 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// __null_terminated should be inherited. +void inherit(const char *__null_terminated p) { + const char *__null_terminated q1 = (const char *)p; + const char *__null_terminated q2 = (char *)p; +} + +// __null_terminated shouldn't be inherited. +void dont_inherit(const char *__null_terminated p) { + // expected-error@+1{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const int *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + const char *__null_terminated q1 = (const int *)p; + + // expected-error@+1{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'int *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + const char *__null_terminated q2 = (int *)p; + + // expected-error@+1{{pointers with incompatible terminators casting 'const char *__single __terminated_by(0)' (aka 'const char *__single') to incompatible type 'const char * __terminated_by(42)' (aka 'const char *')}} + const char *__null_terminated q3 = (const char *__terminated_by(42))p; + + // expected-error@+1{{casting 'const char *__single __terminated_by(0)' (aka 'const char *__single') to incompatible type 'const char *__single' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + const char *__null_terminated q4 = (const char *__single)p; + + // expected-error@+3{{casting 'const char *__single __terminated_by(0)' (aka 'const char *__single') to incompatible type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + const char *__null_terminated q5 = (const char *__bidi_indexable)p; +} + +void inherit_nested(const char *__null_terminated *__null_terminated p) { + const char *__null_terminated *__null_terminated q1 = (const char **)p; + const char *__null_terminated *__null_terminated q2 = (const char *__null_terminated *)p; + const char *__null_terminated *__null_terminated q3 = (const char **__null_terminated)p; +} + +void dont_inherit_nested(const char *__null_terminated *__null_terminated p) { + // expected-error@+1{{initializing 'const char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'const char *__single*__single') with an expression of incompatible type 'const int *__single*__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + const char *__null_terminated *__null_terminated q1 = (const int **)p; + + // expected-error@+1{{pointers with incompatible terminators casting 'const char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'const char *__single*__single') to incompatible type 'const char * __terminated_by(42)*' (aka 'const char **')}} + const char *__null_terminated *__null_terminated q2 = (const char *__terminated_by(42) *)p; + + // expected-error@+1{{casting 'const char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'const char *__single*__single') to incompatible type 'const char *__single*' that discards '__terminated_by' attribute is not allowed}} + const char *__null_terminated *__null_terminated q3 = (const char *__single *)p; +} + +void test_terminated_by(const char *__terminated_by(8) p) { + // expected-error@+1{{casting 'const char *__single __terminated_by(8)' (aka 'const char *__single') to incompatible type 'const char *__bidi_indexable' requires a linear search for the terminator; use '__terminated_by_to_indexable()' to perform this conversion explicitly}} + const char *__bidi_indexable q = (const char *__bidi_indexable)p; +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-from-indexable.c b/clang/test/BoundsSafety/Sema/terminated-by-from-indexable.c new file mode 100644 index 0000000000000..01559ef16065f --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-from-indexable.c @@ -0,0 +1,85 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Foo { + int x; + int y; +}; + +struct Bar; + +static int array[42]; + +void term_ice(int *__indexable ptr, int val) { + __unsafe_terminated_by_from_indexable(0, ptr); // ok + __unsafe_terminated_by_from_indexable(42 * 1337, ptr); // ok + __unsafe_terminated_by_from_indexable(val, ptr); // expected-error{{terminator value is not a constant expression}} +} + +void ptr_type(int *__null_terminated tb, int *__single s, int *__indexable i, + int *__bidi_indexable bi, int *__unsafe_indexable ui, + int *__counted_by(len) cb, int len) { + __unsafe_null_terminated_from_indexable(array); // ok + __unsafe_null_terminated_from_indexable(tb); // ok (tb is a __single pointer) + __unsafe_null_terminated_from_indexable(s); // ok + __unsafe_null_terminated_from_indexable(i); // ok + __unsafe_null_terminated_from_indexable(bi); // ok + __unsafe_null_terminated_from_indexable(ui); // expected-error{{pointer argument must be a safe pointer ('int *__unsafe_indexable' invalid)}} + __unsafe_null_terminated_from_indexable(cb); // ok + __unsafe_null_terminated_from_indexable(*s); // expected-error{{pointer argument must be a safe pointer ('int' invalid)}} + __unsafe_null_terminated_from_indexable(0); // expected-error{{pointer argument must be a safe pointer ('int' invalid)}} +} + +void pointee_type(float *__indexable f, int *__indexable i, char *__indexable c, + struct Foo *__indexable foo, struct Bar *__indexable bar, + float **__indexable pf, int **__indexable pi, + char **__indexable pc, void **__indexable pv, + struct Foo **__indexable pfoo, + struct Bar **__indexable pbar, + int *__unsafe_indexable *__indexable pui, + int *__bidi_indexable *__indexable pbi) { + __unsafe_null_terminated_from_indexable(f); // expected-error{{pointee type of the pointer argument must be an integer or a non-wide pointer}} + __unsafe_null_terminated_from_indexable(i); // ok + __unsafe_null_terminated_from_indexable(c); // ok + __unsafe_null_terminated_from_indexable(foo); // expected-error{{pointee type of the pointer argument must be an integer or a non-wide pointer}} + __unsafe_null_terminated_from_indexable(bar); // expected-error{{pointee type of the pointer argument must be an integer or a non-wide pointer}} + __unsafe_null_terminated_from_indexable(pf); // ok + __unsafe_null_terminated_from_indexable(pi); // ok + __unsafe_null_terminated_from_indexable(pc); // ok + __unsafe_null_terminated_from_indexable(pv); // ok + __unsafe_null_terminated_from_indexable(pfoo); // ok + __unsafe_null_terminated_from_indexable(pbar); // ok + __unsafe_null_terminated_from_indexable(pui); // ok + __unsafe_null_terminated_from_indexable(pbi); // expected-error{{pointee type of the pointer argument must be an integer or a non-wide pointer}} +} + +void ptr_to_term_type(int *__null_terminated tb, int *__single s, + int *__indexable i, int *__bidi_indexable bi, + int *__unsafe_indexable ui, int *__counted_by(len) cb, + int len) { + __unsafe_null_terminated_from_indexable(i, array); // ok + __unsafe_null_terminated_from_indexable(i, tb); // ok + __unsafe_null_terminated_from_indexable(i, s); // ok + __unsafe_null_terminated_from_indexable(i, i); // ok + __unsafe_null_terminated_from_indexable(i, bi); // ok + __unsafe_null_terminated_from_indexable(i, ui); // ok + __unsafe_null_terminated_from_indexable(i, cb); // ok + __unsafe_null_terminated_from_indexable(i, *s); // expected-error{{pointer to terminator argument must be a pointer ('int' invalid)}} + __unsafe_null_terminated_from_indexable(i, 0); // expected-error{{pointer to terminator argument must be a pointer ('int' invalid)}} +} + +void pointee_mismatch(float *__indexable f, int *__indexable i, + char *__indexable c, int **__indexable pi, + void **__indexable pv) { + __unsafe_null_terminated_from_indexable(array, array); // ok + __unsafe_null_terminated_from_indexable(i, f); // expected-error{{pointee types of the pointer and pointer to terminator arguments must be the same}} + __unsafe_null_terminated_from_indexable(i, i); // ok + __unsafe_null_terminated_from_indexable(i, c); // expected-error{{pointee types of the pointer and pointer to terminator arguments must be the same}} + __unsafe_null_terminated_from_indexable(i, pi); // expected-error{{pointee types of the pointer and pointer to terminator arguments must be the same}} + __unsafe_null_terminated_from_indexable(pi, pi); // ok + __unsafe_null_terminated_from_indexable(pv, pv); // ok + __unsafe_null_terminated_from_indexable(pi, pv); // expected-error{{pointee types of the pointer and pointer to terminator arguments must be the same}} +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-ptr-arith.c b/clang/test/BoundsSafety/Sema/terminated-by-ptr-arith.c new file mode 100644 index 0000000000000..7b24f174b02de --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-ptr-arith.c @@ -0,0 +1,46 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void test(int *__null_terminated p, int v) { + p++; // ok + ++p; // ok + p--; // expected-error{{cannot decrement '__terminated_by' pointer 'p'}} + --p; // expected-error{{cannot decrement '__terminated_by' pointer 'p'}} + + p += 0; // ok + p -= 0; // ok + p += 1; // ok + p -= 1; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + p += 2; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + p -= 2; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + p += 2 - 1; // ok + p += 1 - 2; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + p -= 1 - 2; // ok + p -= 2 - 1; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + p += v; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + p -= v; // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + + (void)(p + 0); // ok + (void)(p - 0); // ok + (void)(p + 1); // ok + (void)(p - 1); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + (void)(p + 2); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + (void)(p - 2); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + (void)(p + (2 - 1)); // ok + (void)(p + (1 - 2)); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + (void)(p - (1 - 2)); // ok + (void)(p - (2 - 1)); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + (void)(p + v); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + (void)(p - v); // expected-error{{pointer arithmetic on '__terminated_by' pointer 'p' can only increase the value by one}} + + (void)p[0]; // ok + (void)p[1]; // expected-error{{array subscript on '__terminated_by' pointer 'p' is not allowed}} + (void)p[-1]; // expected-error{{array subscript on '__terminated_by' pointer 'p' is not allowed}} + (void)p[1 - 1]; // ok + (void)p[2 - 1]; // expected-error{{array subscript on '__terminated_by' pointer 'p' is not allowed}} + (void)p[1 - 2]; // expected-error{{array subscript on '__terminated_by' pointer 'p' is not allowed}} + (void)p[v]; // expected-error{{array subscript on '__terminated_by' pointer 'p' is not allowed}} +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-ptr-assign-unsafe.c b/clang/test/BoundsSafety/Sema/terminated-by-ptr-assign-unsafe.c new file mode 100644 index 0000000000000..9b24b6267c6ff --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-ptr-assign-unsafe.c @@ -0,0 +1,116 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void unsafe_char(const char *__unsafe_indexable); + +void unsafe_int(const int *__unsafe_indexable); + +// expected-note@+1{{passing argument to parameter here}} +void nul_char(const char *__null_terminated); + +// expected-note@+1{{passing argument to parameter here}} +void x_char(const char *__terminated_by('X')); + +// expected-note@+1{{passing argument to parameter here}} +void nul_int(const int *__null_terminated); + +// __null_terminated char pointer <-> __unsafe_indexable char pointer + +char *__null_terminated unsafe_indexable_to_null_terminated_char(char *__unsafe_indexable p) { + // expected-error@+1{{initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__unsafe_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + char *__null_terminated q = p; + + // expected-error@+1{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from incompatible type 'char *__unsafe_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + q = p; + + // expected-error@+1{{passing 'char *__unsafe_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + nul_char(p); + + // expected-error@+1{{returning 'char *__unsafe_indexable' from a function with incompatible result type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; + + // expected-error@+1{{casting 'char *__unsafe_indexable' to incompatible type 'char * __terminated_by(0)' (aka 'char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + (void)((char *__null_terminated)p); +} + +char *__unsafe_indexable null_terminated_char_to_unsafe_indexable(char *__null_terminated p) { + // ok + char *__unsafe_indexable q = p; + + // ok + q = p; + + // ok + unsafe_char(p); + + // ok + return p; + + // ok + (void)((char *__unsafe_indexable)p); +} + +// __terminated_by('X') char pointer <-> __unsafe_indexable char pointer + +char *__terminated_by('X') unsafe_indexable_to_terminated_by_char(char *__unsafe_indexable p) { + // expected-error@+1{{initializing 'char *__single __terminated_by('X')' (aka 'char *__single') with an expression of incompatible type 'char *__unsafe_indexable' is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + char *__terminated_by('X') q = p; + + // expected-error@+1{{assigning to 'char *__single __terminated_by('X')' (aka 'char *__single') from incompatible type 'char *__unsafe_indexable' is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + q = p; + + // expected-error@+1{{passing 'char *__unsafe_indexable' to parameter of incompatible type 'const char *__single __terminated_by('X')' (aka 'const char *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + x_char(p); + + // expected-error@+1{{returning 'char *__unsafe_indexable' from a function with incompatible result type 'char *__single __terminated_by('X')' (aka 'char *__single') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + return p; + + // expected-error@+1{{casting 'char *__unsafe_indexable' to incompatible type 'char * __terminated_by('X')' (aka 'char *') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + (void)((char *__terminated_by('X'))p); +} + +char *__unsafe_indexable terminated_by_char_to_unsafe_indexable(char *__terminated_by('X') p) { + char *__unsafe_indexable q = p; + + q = p; + + unsafe_char(p); + + return p; + + (void)((char *__unsafe_indexable)p); +} + +// __null_terminated int pointer <-> __unsafe_indexable int pointer + +int *__null_terminated unsafe_indexable_to_null_terminated_int(int *__unsafe_indexable p) { + // expected-error@+1{{initializing 'int *__single __terminated_by(0)' (aka 'int *__single') with an expression of incompatible type 'int *__unsafe_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + int *__null_terminated q = p; + + // expected-error@+1{{assigning to 'int *__single __terminated_by(0)' (aka 'int *__single') from incompatible type 'int *__unsafe_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + q = p; + + // expected-error@+1{{passing 'int *__unsafe_indexable' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + nul_int(p); + + // expected-error@+1{{returning 'int *__unsafe_indexable' from a function with incompatible result type 'int *__single __terminated_by(0)' (aka 'int *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; + + // expected-error@+1{{casting 'int *__unsafe_indexable' to incompatible type 'int * __terminated_by(0)' (aka 'int *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + (void)((int *__null_terminated)p); +} + +int *__unsafe_indexable null_terminated_int_to_unsafe_indexable(int *__null_terminated p) { + int *__unsafe_indexable q = p; + + q = p; + + unsafe_int(p); + + return p; + + (void)((int *__unsafe_indexable)p); +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-ptr-assign.c b/clang/test/BoundsSafety/Sema/terminated-by-ptr-assign.c new file mode 100644 index 0000000000000..2925aaa065ad3 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-ptr-assign.c @@ -0,0 +1,799 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-note@+1 +{{passing argument to parameter here}} +void nul(const char *__null_terminated); + +void nul_c(const char *const __null_terminated); + +// expected-note@+1 +{{passing argument to parameter here}} +void nul_int(const int *__null_terminated); + +// expected-note@+1{{passing argument to parameter here}} +void _42(const char *__terminated_by(42)); + +// expected-note@+1{{passing argument to parameter here}} +void _42_int(const int *__terminated_by(42)); + +// expected-note@+1 +{{passing argument to parameter here}} +void nul_nested(char *__null_terminated *__null_terminated); + +void _42_nested(char *__terminated_by(42) * __null_terminated); + +// Null + +char *__null_terminated null(void) { + char *__null_terminated p = 0; // ok + p = 0; // ok + nul(0); // ok + return 0; // ok + (void)((char *__null_terminated)0); // ok +} + +// Char arrays +const char *__null_terminated const_arr_stringlit(int x) { + const char const_arr_stringlit[] = "hello"; + + const char *__null_terminated p = const_arr_stringlit; // ok + p = const_arr_stringlit; // ok + nul(const_arr_stringlit); // ok + (void)((const char *__null_terminated) const_arr_stringlit); // ok + return const_arr_stringlit; // ok +} + +const char *__null_terminated const_arr_stringlit_arit(int x) { + const char const_arr_stringlit[] = "hello"; + + const char *__null_terminated p = const_arr_stringlit + 3; // ok + p = const_arr_stringlit + 3; // ok + nul(const_arr_stringlit + 3); // ok + (void)((const char *__null_terminated)( const_arr_stringlit + 3)); // ok + return const_arr_stringlit + 3; // ok +} + +const char *__null_terminated const_arr_stringlit_arit_end(int x) { + const char const_arr_stringlit[] = "hello"; + + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = const_arr_stringlit + 6; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = const_arr_stringlit + 6; + // expected-error@+3{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(const_arr_stringlit + 6); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated)( const_arr_stringlit + 6)); + // expected-error@+3{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return const_arr_stringlit + 6; +} + +const char *__null_terminated const_arr_stringlit_arit_pastend(int x) { + const char const_arr_stringlit[] = "hello"; + + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = const_arr_stringlit + 7; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = const_arr_stringlit + 7; + // expected-error@+3{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(const_arr_stringlit + 7); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated)( const_arr_stringlit + 7)); + // expected-error@+3{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return const_arr_stringlit + 7; +} + +const char *__null_terminated const_arr_braced(int x) { + const char const_arr_braced[] = "hello"; + + const char *__null_terminated p = const_arr_braced; // ok + p = const_arr_braced; // ok + nul(const_arr_braced); // ok + (void)((const char *__null_terminated) const_arr_braced); // ok + return const_arr_braced; // ok +} + +const char *__null_terminated const_arr_stringlit_nonnt_trunc(int x) { + const char const_arr_stringlit_nonnt[2] = "he"; + + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char[2]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = const_arr_stringlit_nonnt; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char[2]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = const_arr_stringlit_nonnt; + // expected-error@+3{{passing 'const char[2]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(const_arr_stringlit_nonnt); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) const_arr_stringlit_nonnt); + // expected-error@+3{{returning 'const char[2]' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return const_arr_stringlit_nonnt; +} + +const char *__null_terminated const_arr_braced_nonnt_trunc(int x) { + const char const_arr_braced_nonnt[2] = {'h', 'e'}; + + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char[2]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = const_arr_braced_nonnt; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char[2]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = const_arr_braced_nonnt; + // expected-error@+3{{passing 'const char[2]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(const_arr_braced_nonnt); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) const_arr_braced_nonnt); + // expected-error@+3{{returning 'const char[2]' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return const_arr_braced_nonnt; +} + +const char *__null_terminated const_arr_stringlit_nt(int x) { + const char const_arr_stringlit_nt[3] = "he"; + + const char *__null_terminated p = const_arr_stringlit_nt; + p = const_arr_stringlit_nt; + nul(const_arr_stringlit_nt); + (void)((const char *__null_terminated) const_arr_stringlit_nt); + return const_arr_stringlit_nt; +} + +const char *__null_terminated const_arr_braced_implicit_nt(int x) { + const char const_arr_braced_implicit_nt[3] = {'h', 'e'}; + + const char *__null_terminated p = const_arr_braced_implicit_nt; + p = const_arr_braced_implicit_nt; + nul(const_arr_braced_implicit_nt); + (void)((const char *__null_terminated) const_arr_braced_implicit_nt); + return const_arr_braced_implicit_nt; +} + +const char *__null_terminated const_arr_stringlit_nt_oversized(int x) { + const char const_arr_stringlit_nt_oversized[4] = "he"; + + const char *__null_terminated p = const_arr_stringlit_nt_oversized; + p = const_arr_stringlit_nt_oversized; + nul(const_arr_stringlit_nt_oversized); + (void)((const char *__null_terminated) const_arr_stringlit_nt_oversized); + return const_arr_stringlit_nt_oversized; +} + +const char *__null_terminated const_arr_braced_nonnt_oversized(int x) { + const char const_arr_braced_nonnt[4] = {'h', 'e', '\0', 'w'}; + + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char[4]' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = const_arr_braced_nonnt; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char[4]' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = const_arr_braced_nonnt; + // expected-error@+3{{passing 'const char[4]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(const_arr_braced_nonnt); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) const_arr_braced_nonnt); + // expected-error@+3{{returning 'const char[4]' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return const_arr_braced_nonnt; +} + +const char *__null_terminated arr_stringlit(int x) { + // expected-note@+1 4{{consider adding 'const' to 'arr_stringlit'}} + char arr_stringlit[] = "hello"; + + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'char[6]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = arr_stringlit; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'char[6]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = arr_stringlit; + // expected-error@+3{{passing 'char[6]' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(arr_stringlit); + // expected-error@+3{{casting 'char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) arr_stringlit); + // expected-error@+3{{returning 'char[6]' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return arr_stringlit; +} + +// Compound literal + +const int *__null_terminated compound_lit_explicit_ok(void) { + const int *__null_terminated p = (const int[3]){1, 2, 0}; // ok + p = (const int[3]){1, 2, 0}; // ok + nul_int((const int[3]){1, 2, 0}); // ok + (void)((const int *__null_terminated) (const int[3]){1, 2, 0}); // ok + return (const int[3]){1, 2, 0}; // ok +} + +const int *__null_terminated compound_lit_explicit_bad(void) { + // expected-error@+3{{initializing 'const int *__single __terminated_by(0)' (aka 'const int *__single') with an expression of incompatible type 'const int[3]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const int *__null_terminated p = (const int[3]){1, 2, 3}; + // expected-error@+3{{assigning to 'const int *__single __terminated_by(0)' (aka 'const int *__single') from incompatible type 'const int[3]' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = (const int[3]){1, 2, 3}; + // expected-error@+3{{passing 'const int[3]' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul_int((const int[3]){1, 2, 3}); + // expected-error@+3{{casting 'const int *__bidi_indexable' to incompatible type 'const int * __terminated_by(0)' (aka 'const int *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const int *__null_terminated) (const int[3]){1, 2, 3}); + // expected-error@+3{{returning 'const int[3]' from a function with incompatible result type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return (const int[3]){1, 2, 3}; +} + +const int *__null_terminated compound_lit_implicit_ok(void) { + const int *__null_terminated p = (const int[3]){1, 2}; // ok + p = (const int[3]){1, 2}; // ok + nul_int((const int[3]){1, 2}); // ok + (void)((const int *__null_terminated) (const int[3]){1, 2}); // ok + return (const int[3]){1, 2}; // ok +} + +const int *__terminated_by(42) compound_lit_implicit_bad(void) { + // expected-error@+1{{initializing 'const int *__single __terminated_by(42)' (aka 'const int *__single') with an expression of incompatible type 'const int[3]' is an unsafe operation}} + const int *__terminated_by(42) p = (const int[3]){1, 2}; + // expected-error@+1{{assigning to 'const int *__single __terminated_by(42)' (aka 'const int *__single') from incompatible type 'const int[3]' is an unsafe operation}} + p = (const int[3]){1, 2}; + // expected-error@+1{{passing 'const int[3]' to parameter of incompatible type 'const int *__single __terminated_by(42)' (aka 'const int *__single') is an unsafe operation}} + _42_int((const int[3]){1, 2}); + // expected-error@+1{{casting 'const int *__bidi_indexable' to incompatible type 'const int * __terminated_by(42)' (aka 'const int *') is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + (void)((const int *__terminated_by(42)) (const int[3]){1, 2}); + // expected-error@+1{{returning 'const int[3]' from a function with incompatible result type 'const int *__single __terminated_by(42)' (aka 'const int *__single') is an unsafe operation}} + return (const int[3]){1, 2}; +} + +const int *__null_terminated compound_lit_arith_ok(void) { + const int *__null_terminated p = (const int[3]){1, 2, 0} + 2; // ok + p = (const int[3]){1, 2, 0} + 2; // ok + nul_int((const int[3]){1, 2, 0} + 2); // ok + (void)((const int *__null_terminated) ((const int[3]){1, 2, 0} + 2)); // ok + return (const int[3]){1, 2, 0} + 2; // ok +} + +const int *__null_terminated compound_lit_arith_end(void) { + // expected-error@+3{{initializing 'const int *__single __terminated_by(0)' (aka 'const int *__single') with an expression of incompatible type 'const int *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const int *__null_terminated p = (const int[3]){1, 2, 3} + 3; + // expected-error@+3{{assigning to 'const int *__single __terminated_by(0)' (aka 'const int *__single') from incompatible type 'const int *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = (const int[3]){1, 2, 3} + 3; + // expected-error@+3{{passing 'const int *__bidi_indexable' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul_int((const int[3]){1, 2, 3} + 3); + // expected-error@+3{{casting 'const int *__bidi_indexable' to incompatible type 'const int * __terminated_by(0)' (aka 'const int *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const int *__null_terminated) ((const int[3]){1, 2, 3} + 3)); + // expected-error@+3{{returning 'const int *__bidi_indexable' from a function with incompatible result type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return (const int[3]){1, 2, 3} + 3; +} + +const int *__null_terminated compound_lit_arith_past_end(void) { + // expected-error@+3{{initializing 'const int *__single __terminated_by(0)' (aka 'const int *__single') with an expression of incompatible type 'const int *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const int *__null_terminated p = (const int[3]){1, 2, 3} + 4; + // expected-error@+3{{assigning to 'const int *__single __terminated_by(0)' (aka 'const int *__single') from incompatible type 'const int *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = (const int[3]){1, 2, 3} + 4; + // expected-error@+3{{passing 'const int *__bidi_indexable' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul_int((const int[3]){1, 2, 3} + 4); + // expected-error@+3{{casting 'const int *__bidi_indexable' to incompatible type 'const int * __terminated_by(0)' (aka 'const int *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const int *__null_terminated) ((const int[3]){1, 2, 3} + 4)); + // expected-error@+3{{returning 'const int *__bidi_indexable' from a function with incompatible result type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return (const int[3]){1, 2, 3} + 4; +} + +const int *__null_terminated compound_lit_arith_neg(void) { + // expected-error@+3{{initializing 'const int *__single __terminated_by(0)' (aka 'const int *__single') with an expression of incompatible type 'const int *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const int *__null_terminated p = (const int[3]){1, 2, 3} - 1; + // expected-error@+3{{assigning to 'const int *__single __terminated_by(0)' (aka 'const int *__single') from incompatible type 'const int *__bidi_indexable' is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = (const int[3]){1, 2, 3} - 1; + // expected-error@+3{{passing 'const int *__bidi_indexable' to parameter of incompatible type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul_int((const int[3]){1, 2, 3} - 1); + // expected-error@+3{{casting 'const int *__bidi_indexable' to incompatible type 'const int * __terminated_by(0)' (aka 'const int *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const int *__null_terminated) ((const int[3]){1, 2, 3} - 1)); + // expected-error@+3{{returning 'const int *__bidi_indexable' from a function with incompatible result type 'const int *__single __terminated_by(0)' (aka 'const int *__single') is an unsafe operation}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return (const int[3]){1, 2, 3} - 1; +} + +// String literal + +char *__null_terminated string_literal_nul(void) { + char *__null_terminated p = "init"; // ok + p = "assign"; // ok + nul("passing"); // ok + return "returning"; // ok + (void)((const char *__null_terminated) "casting"); // ok +} + +char *__terminated_by(42) string_literal_42(void) { + char *__terminated_by(42) p = "init"; // expected-error{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + p = "assign"; // expected-error{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + _42("passing"); // expected-error{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + return "returning"; // expected-error{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + // expected-error@+1{{'__terminated_by' pointer converted from a string literal must be NUL-terminated}} + (void)((const char *__terminated_by(42)) "casting"); +} + +// Terminators + +char *__null_terminated terminators(char *__terminated_by(42) p, char *__null_terminated q) { + // expected-error@+1{{pointers with incompatible terminators initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__single __terminated_by(42)' (aka 'char *__single')}} + char *__null_terminated a = p; + + // ok + char *__null_terminated b = q; + + // expected-error@+1{{pointers with incompatible terminators assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from incompatible type 'char *__single __terminated_by(42)' (aka 'char *__single')}} + a = p; + + // ok + a = q; + + // expected-error@+1{{pointers with incompatible terminators passing 'char *__single __terminated_by(42)' (aka 'char *__single') to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single')}} + nul(p); + + // ok + _42(p); + + // expected-error@+1{{pointers with incompatible terminators returning 'char *__single __terminated_by(42)' (aka 'char *__single') from a function with incompatible result type 'char *__single __terminated_by(0)' (aka 'char *__single')}} + return p; + + // ok + return q; + + // expected-error@+1{{pointers with incompatible terminators casting 'char *__single __terminated_by(42)' (aka 'char *__single') to incompatible type 'char * __terminated_by(0)' (aka 'char *')}} + (void)((char *__null_terminated)p); + + // ok + (void)((char *__null_terminated)q); +} + +char *__null_terminated *__null_terminated nested_terminators(char *__terminated_by(42) * __null_terminated p, char *__null_terminated *__null_terminated q) { + // expected-error@+1{{pointers with incompatible terminators initializing 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') with an expression of incompatible type 'char *__single __terminated_by(42)*__single __terminated_by(0)' (aka 'char *__single*__single')}} + char *__null_terminated *__null_terminated a = p; + + // ok + char *__null_terminated *__null_terminated b = q; + + // expected-error@+1{{pointers with incompatible terminators assigning to 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') from incompatible type 'char *__single __terminated_by(42)*__single __terminated_by(0)' (aka 'char *__single*__single')}} + a = p; + + // ok + a = q; + + // expected-error@+1{{pointers with incompatible terminators passing 'char *__single __terminated_by(42)*__single __terminated_by(0)' (aka 'char *__single*__single') to parameter of incompatible type 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single')}} + nul_nested(p); + + // ok + _42_nested(p); + + // expected-error@+1{{pointers with incompatible terminators returning 'char *__single __terminated_by(42)*__single __terminated_by(0)' (aka 'char *__single*__single') from a function with incompatible result type 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single')}} + return p; + + // ok + return q; + + // expected-error@+1{{pointers with incompatible terminators casting 'char *__single __terminated_by(42)*__single __terminated_by(0)' (aka 'char *__single*__single') to incompatible type 'char * __terminated_by(0)* __terminated_by(0)' (aka 'char **')}} + (void)((char *__null_terminated *__null_terminated)p); + + // ok + (void)((char *__null_terminated *__null_terminated)q); +} + +// Quals + +char *__null_terminated quals_c_to_nc(char *const __null_terminated p) { + // ok + char *__null_terminated a = p; + + // ok + a = p; + + // ok + nul(p); + + // ok + (void)((char *__null_terminated)p); + + // ok + return p; +} + +char *const __null_terminated quals_nc_to_c(char *__null_terminated p) { + // ok + char *const __null_terminated a = p; + + // expected-note@-2{{variable 'a' declared const here}} + // expected-error@+1{{cannot assign to variable 'a' with const-qualified type 'char *__single __terminated_by(0)const' (aka 'char *__singleconst')}} + a = p; + + // ok + nul_c(p); + + // ok + (void)((char *const __null_terminated)p); + + // ok + return p; +} + +// Non-TerminatedBy to TerminatedBy + +char *__null_terminated single_to_terminated_by(char *__single p) { + // expected-error@+1{{initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + char *__null_terminated q = p; + + // expected-error@+1{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from incompatible type 'char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + q = p; + + // expected-error@+1{{passing 'char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + nul(p); + + // expected-error@+1{{returning 'char *__single' from a function with incompatible result type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + return p; + + // expected-error@+1{{casting 'char *__single' to incompatible type 'char * __terminated_by(0)' (aka 'char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + (void)((char *__null_terminated)p); +} + +char *__null_terminated indexable_to_terminated_by(char *__indexable p) { + // expected-error@+3{{initializing 'char *__single __terminated_by(0)' (aka 'char *__single') with an expression of incompatible type 'char *__indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + char *__null_terminated q = p; + + // expected-error@+3{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from incompatible type 'char *__indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + q = p; + + // expected-error@+3{{passing 'char *__indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(p); + + // expected-error@+3{{returning 'char *__indexable' from a function with incompatible result type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return p; + + // expected-error@+3{{casting 'char *__indexable' to incompatible type 'char * __terminated_by(0)' (aka 'char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((char *__null_terminated)p); +} + +char *__null_terminated *__null_terminated nested_to_terminated_by(char *__single *__null_terminated p) { + // expected-error@+1{{initializing 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') with an expression of incompatible type 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') that adds '__terminated_by' attribute is not allowed}} + char *__null_terminated *__null_terminated q = p; + + // expected-error@+1{{assigning to 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') from incompatible type 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') that adds '__terminated_by' attribute is not allowed}} + q = p; + + // expected-error@+1{{passing 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') to parameter of incompatible type 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') that adds '__terminated_by' attribute is not allowed}} + nul_nested(p); + + // expected-error@+1{{returning 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') from a function with incompatible result type 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') that adds '__terminated_by' attribute is not allowed}} + return p; + + // expected-error@+1{{casting 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') to incompatible type 'char * __terminated_by(0)* __terminated_by(0)' (aka 'char **') that adds '__terminated_by' attribute is not allowed}} + (void)((char *__null_terminated *__null_terminated)p); +} + +// TerminatedBy to Non-TerminatedBy + +// expected-note@+1{{passing argument to parameter here}} +void foo_single(char *__single); + +// expected-note@+1{{passing argument to parameter here}} +void foo_indexable(char *__indexable); + +char *__single single_from_terminated_by(char *__null_terminated p) { + // expected-error@+1{{initializing 'char *__single' with an expression of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + char *__single q = p; + + // expected-error@+1{{assigning to 'char *__single' from incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + q = p; + + // expected-error@+1{{passing 'char *__single __terminated_by(0)' (aka 'char *__single') to parameter of incompatible type 'char *__single' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + foo_single(p); + + // expected-error@+1{{returning 'char *__single __terminated_by(0)' (aka 'char *__single') from a function with incompatible result type 'char *__single' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + return p; + + // expected-error@+1{{casting 'char *__single __terminated_by(0)' (aka 'char *__single') to incompatible type 'char *__single' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + (void)((char *__single)p); +} + +char *__indexable indexable_from_terminated_by(char *__null_terminated p) { + // expected-error@+3{{initializing 'char *__indexable' with an expression of incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + char *__indexable q = p; + + // expected-error@+3{{assigning to 'char *__indexable' from incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + q = p; + + // expected-error@+3{{passing 'char *__single __terminated_by(0)' (aka 'char *__single') to parameter of incompatible type 'char *__indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + foo_indexable(p); + + // expected-error@+3{{returning 'char *__single __terminated_by(0)' (aka 'char *__single') from a function with incompatible result type 'char *__indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + return p; + + // expected-error@+3{{casting 'char *__single __terminated_by(0)' (aka 'char *__single') to incompatible type 'char *__indexable' requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@+2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + (void)((char *__indexable)p); +} + +// expected-note@+1{{passing argument to parameter here}} +void bar(char *__single *__null_terminated); + +char *__single *__null_terminated nested_from_terminated_by(char *__null_terminated *__null_terminated p) { + // expected-error@+1{{initializing 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') with an expression of incompatible type 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') that discards '__terminated_by' attribute is not allowed}} + char *__single *__null_terminated q = p; + + // expected-error@+1{{assigning to 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') from incompatible type 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') that discards '__terminated_by' attribute is not allowed}} + q = p; + + // expected-error@+1{{passing 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') to parameter of incompatible type 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') that discards '__terminated_by' attribute is not allowed}} + bar(p); + + // expected-error@+1{{returning 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') from a function with incompatible result type 'char *__single*__single __terminated_by(0)' (aka 'char *__single*__single') that discards '__terminated_by' attribute is not allowed}} + return p; + + // expected-error@+1{{casting 'char *__single __terminated_by(0)*__single __terminated_by(0)' (aka 'char *__single*__single') to incompatible type 'char *__single* __terminated_by(0)' (aka 'char *__single*') that discards '__terminated_by' attribute is not allowed}} + (void)((char *__single *__null_terminated)p); +} + +void sign_mismatch(void) { + const unsigned char array[] = "foo"; + // expected-warning@+1{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of type 'const unsigned char[4]' converts between pointers to integer types where one is of the unique plain 'char' type and the other is not}} + const char *__terminated_by(0) p = array; +} + +// Conditional operators + +char *__null_terminated cond_op_string_lit(int cond) { + char *__null_terminated p = cond ? "init-true" : "init-false"; // ok + p = cond ? "assign-true" : "assign-false"; // ok + nul(cond ? "passing-true" : "passing-false"); // ok + (void)((const char *__null_terminated) (cond ? "casting-true" : "casting-false")); // ok + return cond ? "returning-true" : "returning-false"; // ok +} + +char *__null_terminated cond_op_nt_with_string_lit(int cond, char *__null_terminated s) { + char *__null_terminated p = cond ? s : "init-false"; // ok + p = cond ? s : "assign-false"; // ok + nul(cond ? s : "passing-false"); // ok + (void)((const char *__null_terminated) (cond ? s : "casting-false")); // ok + return cond ? s : "returning-false"; // ok +} + +char *__null_terminated cond_op_string_lit_nested(int c, int d, int e) { + char *__null_terminated p = c ? (d ? "tt" : "tf") : (e ? "ft" : "ff"); // ok + p = c ? (d ? "tt" : "tf") : (e ? "ft" : "ff"); // ok + nul(c ? (d ? "tt" : "tf") : (e ? "ft" : "ff")); // ok + (void)((const char *__null_terminated)(c ? (d ? "tt" : "tf") : (e ? "ft" : "ff"))); // ok + return c ? (d ? "tt" : "tf") : (e ? "ft" : "ff"); // ok +} + +const char *__null_terminated cond_op_char_array(int cond) { + const char t[] = "true"; + const char f[] = "false"; + const char *__null_terminated p = cond ? t : f; // ok + p = cond ? t : f; // ok + nul(cond ? t : f); // ok + (void)((const char *__null_terminated) (cond ? t : f)); // ok + return cond ? t : f; // ok +} + +const char *__null_terminated cond_op_char_array_arith(int cond) { + const char t[] = "true"; + const char f[] = "false"; + const char *__null_terminated p = cond ? t+3 : f+4; // ok + p = cond ? t+3 : f+4; // ok + nul(cond ? t+3 : f+4); // ok + (void)((const char *__null_terminated) (cond ? t+3 : f+4)); // ok + return cond ? t+3 : f+4; // ok +} + +const char *__null_terminated cond_op_char_array_arith_true_end(int cond) { + const char t[] = "true"; + const char f[] = "false"; + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = cond ? t+5 : f+4; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = cond ? t+5 : f+4; + // expected-error@+3{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(cond ? t+5 : f+4); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) (cond ? t+5 : f+4)); + // expected-error@+3{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return cond ? t+5 : f+4; +} + +const char *__null_terminated cond_op_char_array_arith_false_end(int cond) { + const char t[] = "true"; + const char f[] = "false"; + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = cond ? t+4 : f+6; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = cond ? t+4 : f+6; + // expected-error@+3{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(cond ? t+4 : f+6); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) (cond ? t+4 : f+6)); + // expected-error@+3{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return cond ? t+4 : f+6; +} + +const char *__null_terminated cond_op_char_array_wrong_term(int cond) { + const char t[] = "true"; + const char f[5] = "false"; // not NUL-terminated + // expected-error@+3{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + const char *__null_terminated p = cond ? t : f; + // expected-error@+3{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__bidi_indexable' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + p = cond ? t : f; + // expected-error@+3{{passing 'const char *__bidi_indexable' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + nul(cond ? t : f); + // expected-error@+3{{casting 'const char *__bidi_indexable' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + (void)((const char *__null_terminated) (cond ? t : f)); + // expected-error@+3{{returning 'const char *__bidi_indexable' from a function with incompatible result type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@+2{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@+1{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + return cond ? t : f; +} + +const int *__null_terminated cond_op_different_type(int cond) { + const int i[] = {1, 2, 3, 0}; + const long l[] = {1, 2, 0}; + // expected-error@+1{{conditional expression evaluates values with incompatible pointee types 'const int *__bidi_indexable' and 'const long *__bidi_indexable'; use explicit casts to perform this conversion}} + const long *__null_terminated p = cond ? i : l; + // expected-error@+1{{conditional expression evaluates values with incompatible pointee types 'const int *__bidi_indexable' and 'const long *__bidi_indexable'; use explicit casts to perform this conversion}} + p = cond ? i : l; + // expected-error@+1{{conditional expression evaluates values with incompatible pointee types 'const int *__bidi_indexable' and 'const long *__bidi_indexable'; use explicit casts to perform this conversion}} + nul_int(cond ? i : l); + // expected-error@+1{{conditional expression evaluates values with incompatible pointee types 'const int *__bidi_indexable' and 'const long *__bidi_indexable'; use explicit casts to perform this conversion}} + (void)((const int *__null_terminated) (cond ? i : l)); + // expected-error@+1{{conditional expression evaluates values with incompatible pointee types 'const int *__bidi_indexable' and 'const long *__bidi_indexable'; use explicit casts to perform this conversion}} + return cond ? i : l; +} + +char *__null_terminated gnu_cond_op_string_lit_true(void) { + char *__null_terminated p = "t" ?: "f"; // ok + p = "t" ?: "f"; // ok + nul("t" ?: "f"); // ok + (void)((const char *__null_terminated) ("t" ?: "f")); // ok + return "t" ?: "f"; // ok +} + +char *__null_terminated gnu_cond_op_string_lit_false(void) { + char *__null_terminated p = 0 ?: "f"; // ok + p = 0 ?: "f"; // ok + nul(0 ?: "f"); // ok + (void)((const char *__null_terminated) (0 ?: "f")); // ok + return 0 ?: "f"; // ok +} + +void const_ptr_indexable_to_terminated_by(int x) { + const char *__indexable ptr_terminated_by;; + + // expected-error@+1{{initializing 'const char *__single __terminated_by(4)' (aka 'const char *__single') with an expression of incompatible type 'const char *__indexable' is an unsafe operation; use '__unsafe_terminated_by_from_indexable()' or '__unsafe_forge_terminated_by()' to perform this conversion}} + const char *__terminated_by(4) p = ptr_terminated_by; +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-ptr-relaxed-casting.c b/clang/test/BoundsSafety/Sema/terminated-by-ptr-relaxed-casting.c new file mode 100644 index 0000000000000..a0169f1541ae5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-ptr-relaxed-casting.c @@ -0,0 +1,60 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=strict,both %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=relaxed,both -Wno-error=bounds-safety-strict-terminated-by-cast -Wno-error %s + +#include + +// The relaxed case should be removed by rdar://118390724 + +void foo(const char * __null_terminated); // both-note{{passing argument to parameter here}} +void bar(const char * __null_terminated * __single); // both-note{{passing argument to parameter here}} + +void test(const char * __single sp) { + // strict-error@+2{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()}} + // relaxed-warning@+1{{initializing 'const char *__single __terminated_by(0)' (aka 'const char *__single') with an expression of incompatible type 'const char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()}} + const char * __null_terminated ntp = sp; + // strict-error@+2{{casting 'const char *__single' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // relaxed-warning@+1{{casting 'const char *__single' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + const char * __null_terminated ntp2 = (const char * __null_terminated) sp; + + // strict-error@+2{{passing 'const char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // relaxed-warning@+1{{passing 'const char *__single' to parameter of incompatible type 'const char *__single __terminated_by(0)' (aka 'const char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + foo(sp); + // strict-error@+2{{casting 'const char *__single' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // relaxed-warning@+1{{casting 'const char *__single' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + foo((const char * __null_terminated) sp); + + const char * __null_terminated ntp3 = ntp; + // strict-error@+2{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // relaxed-warning@+1{{assigning to 'const char *__single __terminated_by(0)' (aka 'const char *__single') from incompatible type 'const char *__single' is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + ntp3 = sp; + // strict-error@+2{{casting 'const char *__single' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // relaxed-warning@+1{{casting 'const char *__single' to incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + ntp3 = (const char * __null_terminated) sp; + + + /* --- Nested --- */ + + const char * __single * __single spp = &sp; + // strict-error@+2{{initializing 'const char *__single __terminated_by(0)*__single' (aka 'const char *__single*__single') with an expression of incompatible type 'const char *__single*__single' that adds '__terminated_by' attribute is not allowed}} + // relaxed-warning@+1{{initializing 'const char *__single __terminated_by(0)*__single' (aka 'const char *__single*__single') with an expression of incompatible type 'const char *__single*__single' that adds '__terminated_by' attribute is not allowed}} + const char * __null_terminated * __single ntpp = spp; + // strict-error@+2{{casting 'const char *__single*__single' to incompatible type 'const char * __terminated_by(0)*__single' (aka 'const char **__single') that adds '__terminated_by' attribute is not allowed}} + // relaxed-warning@+1{{casting 'const char *__single*__single' to incompatible type 'const char * __terminated_by(0)*__single' (aka 'const char **__single') that adds '__terminated_by' attribute is not allowed}} + const char * __null_terminated * __single ntpp2 = (const char * __null_terminated * __single) spp; + + // strict-error@+2{{passing 'const char *__single*__single' to parameter of incompatible type 'const char *__single __terminated_by(0)*__single' (aka 'const char *__single*__single') that adds '__terminated_by' attribute is not allowed}} + // relaxed-warning@+1{{passing 'const char *__single*__single' to parameter of incompatible type 'const char *__single __terminated_by(0)*__single' (aka 'const char *__single*__single') that adds '__terminated_by' attribute is not allowed}} + bar(spp); + // strict-error@+2{{casting 'const char *__single*__single' to incompatible type 'const char * __terminated_by(0)*__single' (aka 'const char **__single') that adds '__terminated_by' attribute is not allowed}} + // relaxed-warning@+1{{casting 'const char *__single*__single' to incompatible type 'const char * __terminated_by(0)*__single' (aka 'const char **__single') that adds '__terminated_by' attribute is not allowed}} + bar((const char * __null_terminated * __single) spp); + + const char * __null_terminated * __single ntpp3 = ntpp; + // strict-error@+2{{assigning to 'const char *__single __terminated_by(0)*__single' (aka 'const char *__single*__single') from incompatible type 'const char *__single*__single' that adds '__terminated_by' attribute is not allowed}} + // relaxed-warning@+1{{assigning to 'const char *__single __terminated_by(0)*__single' (aka 'const char *__single*__single') from incompatible type 'const char *__single*__single' that adds '__terminated_by' attribute is not allowed}} + ntpp3 = spp; + // strict-error@+2{{casting 'const char *__single*__single' to incompatible type 'const char * __terminated_by(0)*__single' (aka 'const char **__single') that adds '__terminated_by' attribute is not allowed}} + // relaxed-warning@+1{{casting 'const char *__single*__single' to incompatible type 'const char * __terminated_by(0)*__single' (aka 'const char **__single') that adds '__terminated_by' attribute is not allowed}} + ntpp3 = (const char * __null_terminated * __single) spp; +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-ptr-uninit.c b/clang/test/BoundsSafety/Sema/terminated-by-ptr-uninit.c new file mode 100644 index 0000000000000..ff646781c5467 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-ptr-uninit.c @@ -0,0 +1,49 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-no-diagnostics + +struct Foo { + int *__null_terminated a; + int *__terminated_by(42) b; +}; + +struct Bar { + int *__null_terminated a; +}; + +struct Baz { + struct Foo f; + struct Bar b; +}; + +int *__null_terminated g_a; // ok +int *__null_terminated g_b; // ok +struct Foo g_foo; // ok +struct Bar g_bar; // ok +struct Baz g_baz; // ok + +void no_init_static_local(void) { + static int *__null_terminated a; // ok + static int *__null_terminated b; // ok + static struct Foo foo; // ok + static struct Bar bar; // ok + static struct Baz baz; // ok +} + +void no_init_local(void) { + int *__null_terminated a; // ok + int *__null_terminated b; // ok + struct Foo foo; // ok + struct Bar bar; // ok + struct Baz baz; // ok +} + +void empty_init_local(void) { + struct Foo foo = {}; // ok + struct Bar bar = {}; // ok + struct Baz baz = {}; // ok +} diff --git a/clang/test/BoundsSafety/Sema/terminated-by-redecl.c b/clang/test/BoundsSafety/Sema/terminated-by-redecl.c new file mode 100644 index 0000000000000..5f38fd9515ae1 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-redecl.c @@ -0,0 +1,55 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-note@+1{{previous declaration is here}} +char *test(); +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +char *__null_terminated test(); + +// expected-note@+1{{previous declaration is here}} +const char *test2(); +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +const char *__terminated_by(-1) test2(); + +const char *test3(); +const char *__null_terminated test3(); + +// expected-note@+1{{previous declaration is here}} +void test4(int *__null_terminated arg); +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +void test4(int *arg); + +// expected-note@+1{{previous declaration is here}} +void test5(int *__null_terminated arg); +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +void test5(int *__terminated_by(-1) arg); + +// expected-note@+1{{previous declaration is here}} +void test6(int *__counted_by(len) buf, int len); +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +void test6(int *__null_terminated buf, int len); + +// expected-note@+1{{previous declaration is here}} +void test7(int *__null_terminated buf, int len); +// expected-error@+1{{conflicting '__sized_by' attribute with the previous function declaration}} +void test7(int *__sized_by(len) buf, int len); + +// expected-note@+1{{previous declaration is here}} +void test8(int *__null_terminated buf, int len); +// expected-error@+1{{conflicting '__ended_by' attribute with the previous function declaration}} +void test8(int *__ended_by(end) buf, int *end); + +// expected-note@+1{{previous declaration is here}} +void test9(int *__ended_by(end) buf, int *end); +// expected-error@+1{{conflicting '__ended_by' attribute with the previous function declaration}} +void test9(int *buf, int *__null_terminated end); + +#include "terminated-by-redecl.h" + +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +void test_system_nt_argument(int *p); +// expected-error@+1{{conflicting '__terminated_by' attribute with the previous function declaration}} +int *test_system_nt_return(); diff --git a/clang/test/BoundsSafety/Sema/terminated-by-redecl.h b/clang/test/BoundsSafety/Sema/terminated-by-redecl.h new file mode 100644 index 0000000000000..f436aa769f9de --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-redecl.h @@ -0,0 +1,12 @@ +#pragma clang system_header + +#include + +// expected-note@+1{{previous declaration is here}} +void test_system_nt_argument(int *__null_terminated p); + +// expected-note@+1{{previous declaration is here}} +int *__null_terminated test_system_nt_return(); + +void test_system_nt_argument_impl(int *__null_terminated p); +void test_system_nt_argument_impl(int *p); diff --git a/clang/test/BoundsSafety/Sema/terminated-by-to-indexable.c b/clang/test/BoundsSafety/Sema/terminated-by-to-indexable.c new file mode 100644 index 0000000000000..1fc21ee6cb5bc --- /dev/null +++ b/clang/test/BoundsSafety/Sema/terminated-by-to-indexable.c @@ -0,0 +1,62 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +static int array[42]; +static int nt_array[__null_terminated 42]; + +void ptr_type(int *__null_terminated tb, int *__single s, int *__bidi_indexable bi) { + __terminated_by_to_indexable(array); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int *__bidi_indexable' invalid)}} + __terminated_by_to_indexable(nt_array); // ok (the array decays to a __single __null_terminated pointer) + + __terminated_by_to_indexable(tb); // ok + __terminated_by_to_indexable(s); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int *__single' invalid)}} + __terminated_by_to_indexable(bi); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int *__bidi_indexable' invalid)}} + __terminated_by_to_indexable(*s); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int' invalid)}} + __terminated_by_to_indexable(0); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int' invalid)}} +} + +void unsafe_ptr_type(int *__null_terminated tb, int *__single s, int *__bidi_indexable bi) { + __unsafe_terminated_by_to_indexable(array); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int *__bidi_indexable' invalid)}} + __unsafe_terminated_by_to_indexable(nt_array); // ok (the array decays to a __single __null_terminated pointer) + + __unsafe_terminated_by_to_indexable(tb); // ok + __unsafe_terminated_by_to_indexable(s); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int *__single' invalid)}} + __unsafe_terminated_by_to_indexable(bi); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int *__bidi_indexable' invalid)}} + __unsafe_terminated_by_to_indexable(*s); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int' invalid)}} + __unsafe_terminated_by_to_indexable(0); // expected-error{{pointer argument must be a '__terminated_by' pointer ('int' invalid)}} +} + +void term_ice(int *__null_terminated p, int val) { + (void)__builtin_terminated_by_to_indexable(p, 0); // ok + (void)__builtin_terminated_by_to_indexable(p, 42 * 0); // ok + (void)__builtin_terminated_by_to_indexable(p, val); // expected-error{{terminator value is not a constant expression}} +} + +void unsafe_term_ice(int *__null_terminated p, int val) { + (void)__builtin_unsafe_terminated_by_to_indexable(p, 0); // ok + (void)__builtin_unsafe_terminated_by_to_indexable(p, 42 * 0); // ok + (void)__builtin_unsafe_terminated_by_to_indexable(p, val); // expected-error{{terminator value is not a constant expression}} +} + +void null(int *__null_terminated p, int *__terminated_by(42) q) { + __null_terminated_to_indexable(p); // ok + __null_terminated_to_indexable(q); // expected-error{{pointer argument must be terminated by '0' (got '42')}} +} + +void unsafe_null(int *__null_terminated p, int *__terminated_by(42) q) { + __unsafe_null_terminated_to_indexable(p); // ok + __unsafe_null_terminated_to_indexable(q); // expected-error{{pointer argument must be terminated by '0' (got '42')}} +} + +void _42(int *__null_terminated p, int *__terminated_by(42) q) { + (void)__builtin_terminated_by_to_indexable(p, 42); // expected-error{{pointer argument must be terminated by '42' (got '0')}} + (void)__builtin_terminated_by_to_indexable(q, 42); // ok +} + +void unsafe_42(int *__null_terminated p, int *__terminated_by(42) q) { + (void)__builtin_unsafe_terminated_by_to_indexable(p, 42); // expected-error{{pointer argument must be terminated by '42' (got '0')}} + (void)__builtin_unsafe_terminated_by_to_indexable(q, 42); // ok +} diff --git a/clang/test/BoundsSafety/Sema/ternary-on-indexables.c b/clang/test/BoundsSafety/Sema/ternary-on-indexables.c new file mode 100644 index 0000000000000..325282efddcf3 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ternary-on-indexables.c @@ -0,0 +1,349 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +#pragma clang diagnostic ignored "-Wbounds-attributes-implicit-conversion-single-to-explicit-indexable" + +void Test(int sel) { + int a; + int *x = &a; + int *y = &a; + int *z = sel ? x : y; + + char c; + char *x_char = &c; + char *y_char = &c; + char *z_char = sel ? x_char : y_char; + + void *__indexable x_ix_void = &a; + void *__indexable y_ix_void = &a; + void *__indexable z_ix_void = y ?: y; + + int *__indexable x_ix = &a; + int *__indexable y_ix = &a; + int *__indexable z_ix = sel ? x_ix : y_ix; + + int *__single x_sg = &a; + int *__single y_sg = &a; + int *__single z_sg = sel ? x_sg : y_sg; + + int *__unsafe_indexable x_uix = &a; + int *__unsafe_indexable y_uix = &a; + int *__unsafe_indexable z_uix = sel ? x_uix : y_uix; + + char *__unsafe_indexable x_uix_char = &c; + char *__unsafe_indexable y_uix_char = &c; + char *__unsafe_indexable z_uix_char = sel ? x_uix_char : y_uix_char; + + z = sel ? x : y; + z = sel ? x : y_ix; + z = sel ? x : y_sg; + z = sel ? x : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x : y_ix_void; + + z = sel ? x_ix : y; + z = sel ? x_ix : y_ix; + z = sel ? x_ix : y_sg; + z = sel ? x_ix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_ix : y_ix_void; + + z = sel ? x_sg : y; + z = sel ? x_sg : y_ix; + z = sel ? x_sg : y_sg; + z = sel ? x_sg : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_sg : y_ix_void; + + z = sel ? x_uix : y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_uix : y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_uix : y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_uix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_uix : y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z = sel ? x_ix_void : y; + z = sel ? x_ix_void : y_ix; + z = sel ? x_ix_void : y_sg; + z = sel ? x_ix_void : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = sel ? x_ix_void : y_ix_void; + + z_ix = sel ? x : y; + z_ix = sel ? x : y_ix; + z_ix = sel ? x : y_sg; + z_ix = sel ? x : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x : y_ix_void; + + z_ix = sel ? x_ix : y; + z_ix = sel ? x_ix : y_ix; + z_ix = sel ? x_ix : y_sg; + z_ix = sel ? x_ix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_ix : y_ix_void; + + z_ix = sel ? x_sg : y; + z_ix = sel ? x_sg : y_ix; + z_ix = sel ? x_sg : y_sg; + z_ix = sel ? x_sg : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_sg : y_ix_void; + + z_ix = sel ? x_uix : y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_uix : y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_uix : y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_uix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_uix : y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_ix = sel ? x_ix_void : y; + z_ix = sel ? x_ix_void : y_ix; + z_ix = sel ? x_ix_void : y_sg; + z_ix = sel ? x_ix_void : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = sel ? x_ix_void : y_ix_void; + + z_sg = sel ? x_ix : y; + z_sg = sel ? x_ix : y_ix; + z_sg = sel ? x_ix : y_sg; + z_sg = sel ? x_ix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_ix : y_ix_void; + + z_sg = sel ? x_sg : y; + z_sg = sel ? x_sg : y_ix; + z_sg = sel ? x_sg : y_sg; + z_sg = sel ? x_sg : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_sg : y_ix_void; + + z_sg = sel ? x_uix : y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_uix : y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_uix : y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_uix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_uix : y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_sg = sel ? x_ix_void : y; + z_sg = sel ? x_ix_void : y_ix; + z_sg = sel ? x_ix_void : y_sg; + z_sg = sel ? x_ix_void : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = sel ? x_ix_void : y_ix_void; + + z_uix = sel ? x_ix : y; + z_uix = sel ? x_ix : y_ix; + z_uix = sel ? x_ix : y_sg; + z_uix = sel ? x_ix : y_uix; + z_uix = sel ? x_ix : y_ix_void; + + z_uix = sel ? x_sg : y; + z_uix = sel ? x_sg : y_ix; + z_uix = sel ? x_sg : y_sg; + z_uix = sel ? x_sg : y_uix; + z_uix = sel ? x_sg : y_ix_void; + + z_uix = sel ? x_uix : y; + z_uix = sel ? x_uix : y_ix; + z_uix = sel ? x_uix : y_sg; + z_uix = sel ? x_uix : y_uix; + z_uix = sel ? x_uix : y_ix_void; + + z_uix = sel ? x_ix_void : y; + z_uix = sel ? x_ix_void : y_ix; + z_uix = sel ? x_ix_void : y_sg; + z_uix = sel ? x_ix_void : y_uix; + z_uix = sel ? x_ix_void : y_ix_void; + + z_ix_void = sel ? x_ix : y; + z_ix_void = sel ? x_ix : y_ix; + z_ix_void = sel ? x_ix : y_sg; + z_ix_void = sel ? x_ix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_ix : y_ix_void; + + z_ix_void = sel ? x_sg : y; + z_ix_void = sel ? x_sg : y_ix; + z_ix_void = sel ? x_sg : y_sg; + z_ix_void = sel ? x_sg : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_sg : y_ix_void; + + z_ix_void = sel ? x_uix : y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_uix : y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_uix : y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_uix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_uix : y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_ix_void = sel ? x_ix_void : y; + z_ix_void = sel ? x_ix_void : y_ix; + z_ix_void = sel ? x_ix_void : y_sg; + z_ix_void = sel ? x_ix_void : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = sel ? x_ix : y; + + z_char = sel ? x_ix : y_ix; // expected-warning{{incompatible pointer types assigning}} + z_char = sel ? x_ix : y_sg; // expected-warning{{incompatible pointer types assigning}} + z_char = sel ? x_ix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_ix : y_ix_void; + + z_char = sel ? x_sg : y; // expected-warning{{incompatible pointer types assigning}} + z_char = sel ? x_sg : y_ix; // expected-warning{{incompatible pointer types assigning}} + z_char = sel ? x_sg : y_sg; // expected-warning{{incompatible pointer types assigning}} + z_char = sel ? x_sg : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_sg : y_ix_void; + + z_char = sel ? x_uix : y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_uix : y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_uix : y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_uix : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_uix : y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_char = sel ? x_ix_void : y; + z_char = sel ? x_ix_void : y_ix; + z_char = sel ? x_ix_void : y_sg; + z_char = sel ? x_ix_void : y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = sel ? x_ix_void : y_ix_void; + + z = x ?: y; + z = x ?: y_ix; + z = x ?: y_sg; + z = x ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x ?: y_ix_void; + + z = x_ix ?: y; + z = x_ix ?: y_ix; + z = x_ix ?: y_sg; + z = x_ix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_ix ?: y_ix_void; + + z = x_sg ?: y; + z = x_sg ?: y_ix; + z = x_sg ?: y_sg; + z = x_sg ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_sg ?: y_ix_void; + + z = x_uix ?: y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_uix ?: y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_uix ?: y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_uix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_uix ?: y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z = x_ix_void ?: y; + z = x_ix_void ?: y_ix; + z = x_ix_void ?: y_sg; + z = x_ix_void ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z = x_ix_void ?: y_ix_void; + + z_ix = x ?: y; + z_ix = x ?: y_ix; + z_ix = x ?: y_sg; + z_ix = x ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x ?: y_ix_void; + + z_ix = x_ix ?: y; + z_ix = x_ix ?: y_ix; + z_ix = x_ix ?: y_sg; + z_ix = x_ix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_ix ?: y_ix_void; + + z_ix = x_sg ?: y; + z_ix = x_sg ?: y_ix; + z_ix = x_sg ?: y_sg; + z_ix = x_sg ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_sg ?: y_ix_void; + + z_ix = x_uix ?: y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_uix ?: y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_uix ?: y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_uix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_uix ?: y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_ix = x_ix_void ?: y; + z_ix = x_ix_void ?: y_ix; + z_ix = x_ix_void ?: y_sg; + z_ix = x_ix_void ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix = x_ix_void ?: y_ix_void; + + z_sg = x_ix ?: y; + z_sg = x_ix ?: y_ix; + z_sg = x_ix ?: y_sg; + z_sg = x_ix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_ix ?: y_ix_void; + + z_sg = x_sg ?: y; + z_sg = x_sg ?: y_ix; + z_sg = x_sg ?: y_sg; + z_sg = x_sg ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_sg ?: y_ix_void; + + z_sg = x_uix ?: y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_uix ?: y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_uix ?: y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_uix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_uix ?: y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_sg = x_ix_void ?: y; + z_sg = x_ix_void ?: y_ix; + z_sg = x_ix_void ?: y_sg; + z_sg = x_ix_void ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_sg = x_ix_void ?: y_ix_void; + + z_uix = x_ix ?: y; + z_uix = x_ix ?: y_ix; + z_uix = x_ix ?: y_sg; + z_uix = x_ix ?: y_uix; + z_uix = x_ix ?: y_ix_void; + + z_uix = x_sg ?: y; + z_uix = x_sg ?: y_ix; + z_uix = x_sg ?: y_sg; + z_uix = x_sg ?: y_uix; + z_uix = x_sg ?: y_ix_void; + + z_uix = x_uix ?: y; + z_uix = x_uix ?: y_ix; + z_uix = x_uix ?: y_sg; + z_uix = x_uix ?: y_uix; + z_uix = x_uix ?: y_ix_void; + + z_uix = x_ix_void ?: y; + z_uix = x_ix_void ?: y_ix; + z_uix = x_ix_void ?: y_sg; + z_uix = x_ix_void ?: y_uix; + z_uix = x_ix_void ?: y_ix_void; + + z_ix_void = x_ix ?: y; + z_ix_void = x_ix ?: y_ix; + z_ix_void = x_ix ?: y_sg; + z_ix_void = x_ix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_ix ?: y_ix_void; + + z_ix_void = x_sg ?: y; + z_ix_void = x_sg ?: y_ix; + z_ix_void = x_sg ?: y_sg; + z_ix_void = x_sg ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_sg ?: y_ix_void; + + z_ix_void = x_uix ?: y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_uix ?: y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_uix ?: y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_uix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_uix ?: y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_ix_void = x_ix_void ?: y; + z_ix_void = x_ix_void ?: y_ix; + z_ix_void = x_ix_void ?: y_sg; + z_ix_void = x_ix_void ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_ix_void = x_ix ?: y; + + z_char = x_ix ?: y_ix; // expected-warning{{incompatible pointer types assigning}} + z_char = x_ix ?: y_sg; // expected-warning{{incompatible pointer types assigning}} + z_char = x_ix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_ix ?: y_ix_void; + + z_char = x_sg ?: y; // expected-warning{{incompatible pointer types assigning}} + z_char = x_sg ?: y_ix; // expected-warning{{incompatible pointer types assigning}} + z_char = x_sg ?: y_sg; // expected-warning{{incompatible pointer types assigning}} + z_char = x_sg ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_sg ?: y_ix_void; + + z_char = x_uix ?: y; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_uix ?: y_ix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_uix ?: y_sg; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_uix ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_uix ?: y_ix_void; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + z_char = x_ix_void ?: y; + z_char = x_ix_void ?: y_ix; + z_char = x_ix_void ?: y_sg; + z_char = x_ix_void ?: y_uix; // expected-error-re{{assigning to '{{.+}}' from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + z_char = x_ix_void ?: y_ix_void; +} + diff --git a/clang/test/BoundsSafety/Sema/ternary-on-terminated-by.c b/clang/test/BoundsSafety/Sema/ternary-on-terminated-by.c new file mode 100644 index 0000000000000..aa4a8b91b0416 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/ternary-on-terminated-by.c @@ -0,0 +1,56 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-bounds-safety-single-to-indexable-bounds-truncated -x objective-c -fbounds-attributes-objc-experimental -verify %s +#include + +void Test(int sel) { + char c; + char *x = &c; + char *y = &c; + char *z = sel ? x : y; + + char * __null_terminated x_nt = __unsafe_forge_null_terminated(char *, x); + char * __null_terminated y_nt = __unsafe_forge_null_terminated(char *, y); + z = sel ? x_nt : y_nt; // expected-error{{assigning to 'char *__bidi_indexable' from incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') requires a linear search for the terminator; use '__null_terminated_to_indexable()' to perform this conversion explicitly}} + // expected-note@-5{{consider adding '__null_terminated' to 'z'}} + // expected-note@-2{{consider using '__null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound excludes the null terminator}} + // expected-note@-3{{consider using '__unsafe_null_terminated_to_indexable()' to perform this conversion. Note this conversion requires a linear scan of memory to find the null terminator and the resulting upper bound includes the null terminator}} + z = sel ? x : y_nt; // expected-error{{converting 'char *__bidi_indexable' to incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@-1{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@-2{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + + char * __null_terminated z_nt = sel ? x_nt : y_nt; + z_nt = sel ? x_nt : y; // expected-error{{converting 'char *__bidi_indexable' to incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@-1{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@-2{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + z_nt = sel ? x : y_nt; // expected-error{{converting 'char *__bidi_indexable' to incompatible type 'char *__single __terminated_by(0)' (aka 'char *__single') is an unsafe operation; use '__unsafe_null_terminated_from_indexable()' or '__unsafe_forge_null_terminated()' to perform this conversion}} + // expected-note@-1{{consider using '__unsafe_null_terminated_from_indexable()' to perform this conversion. Note this performs a linear scan of memory to find the null terminator}} + // expected-note@-2{{consider using '__unsafe_null_terminated_from_indexable()' with a pointer to the null terminator to perform this conversion. Note this performs the conversion in constant time}} + + char * __single y_single = &c; + char * __single x_single = &c; + z_nt = sel ? x_nt : y_single; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'char *__single __terminated_by(0)' (aka 'char *__single') and 'char *__single'}} + z_nt = sel ? x_single : y_nt; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'char *__single' and 'char *__single __terminated_by(0)' (aka 'char *__single')}} + + char * __terminated_by(2) x_2t = __unsafe_forge_terminated_by(char *, x, 2); + z_nt = sel ? x_2t : y_nt; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'char *__single __terminated_by(2)' (aka 'char *__single') and 'char *__single __terminated_by(0)' (aka 'char *__single')}} + z_nt = sel ? x_nt : x_2t; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'char *__single __terminated_by(0)' (aka 'char *__single') and 'char *__single __terminated_by(2)' (aka 'char *__single')}} + + // Type comparison takes a different path when the pointee types are not the same, exercise this + const char * __null_terminated x_c_nt = x_nt; + z_nt = sel ? x_c_nt : y_nt; // expected-warning{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from 'const char *__single __terminated_by(0)' (aka 'const char *__single') discards qualifiers}} + z_nt = sel ? x_nt : x_c_nt; // expected-warning{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from 'const char *__single __terminated_by(0)' (aka 'const char *__single') discards qualifiers}} + + z_nt = sel ? x_c_nt : x_2t; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'const char *__single __terminated_by(0)' (aka 'const char *__single') and 'char *__single __terminated_by(2)' (aka 'char *__single')}} + z_nt = sel ? x_2t : x_c_nt; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'char *__single __terminated_by(2)' (aka 'char *__single') and 'const char *__single __terminated_by(0)' (aka 'const char *__single')}} + + z_nt = sel ? x_c_nt : x_single; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'const char *__single __terminated_by(0)' (aka 'const char *__single') and 'char *__single'}} + z_nt = sel ? x_single : x_c_nt; // expected-error{{conditional expression evaluates values with mismatching __terminated_by attributes 'char *__single' and 'const char *__single __terminated_by(0)' (aka 'const char *__single')}} + + unsigned char * __null_terminated x_u_nt = x_nt; // expected-warning{{initializing 'unsigned char *__single __terminated_by(0)' (aka 'unsigned char *__single') with an expression of type 'char *__single __terminated_by(0)' (aka 'char *__single') converts between pointers to integer types where one is of the unique plain 'char' type and the other is not}} + signed char * __null_terminated y_s_nt = y_nt; // expected-warning{{initializing 'signed char *__single __terminated_by(0)' (aka 'signed char *__single') with an expression of type 'char *__single __terminated_by(0)' (aka 'char *__single') converts between pointers to integer types where one is of the unique plain 'char' type and the other is not}} + z_nt = sel ? x_u_nt : y_s_nt; // expected-error{{conditional expression evaluates values with incompatible pointee types 'unsigned char *__single __terminated_by(0)' (aka 'unsigned char *__single') and 'signed char *__single __terminated_by(0)' (aka 'signed char *__single'); use explicit casts to perform this conversion}} + z_nt = sel ? y_s_nt : x_u_nt; // expected-error{{conditional expression evaluates values with incompatible pointee types 'signed char *__single __terminated_by(0)' (aka 'signed char *__single') and 'unsigned char *__single __terminated_by(0)' (aka 'unsigned char *__single'); use explicit casts to perform this conversion}} + z_nt = sel ? y_s_nt : (signed char * __null_terminated)x_u_nt; // expected-warning{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from 'signed char *__single __terminated_by(0)' (aka 'signed char *__single') converts between pointers to integer types where one is of the unique plain 'char' type and the other is not}} + z_nt = sel ? (signed char * __single _Nullable __null_terminated) y_s_nt : (signed char * _Nullable __null_terminated)x_u_nt; // expected-warning{{assigning to 'char *__single __terminated_by(0)' (aka 'char *__single') from 'signed char *__single __terminated_by(0) _Nullable' (aka 'signed char *__single') converts between pointers to integer types where one is of the unique plain 'char' type and the other is not}} +} diff --git a/clang/test/BoundsSafety/Sema/typedef-counted-pointer.c b/clang/test/BoundsSafety/Sema/typedef-counted-pointer.c new file mode 100644 index 0000000000000..8f5b68b11f112 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/typedef-counted-pointer.c @@ -0,0 +1,46 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +typedef int *__counted_by(10) giptr_counted_t; // expected-error{{'__counted_by' inside typedef is only allowed for function type}} +typedef int *__sized_by(1) giptr_sized_t; // expected-error{{'__sized_by' inside typedef is only allowed for function type}} +typedef int *__counted_by_or_null(10) giptr_counted_t; // expected-error{{'__counted_by_or_null' inside typedef is only allowed for function type}} +typedef int *__sized_by_or_null(1) giptr_sized_t; // expected-error{{'__sized_by_or_null' inside typedef is only allowed for function type}} +typedef int *__counted_by(10) * foo_t; // expected-error{{'__counted_by' inside typedef is only allowed for function type}} + +giptr_counted_t gip_cnt; +giptr_sized_t gip_siz; + +void foo() { + giptr_counted_t gip_cnt_local; + + int n; + typedef int *__counted_by(n) liptr_counted_t; // expected-error{{'__counted_by' inside typedef is only allowed for function type}} +} + +typedef int *__counted_by(16) f1(void); // ok +typedef void *__sized_by(16) f2(void); // ok +typedef int *__counted_by(len) f3(int len); // ok +typedef void *__sized_by(len) f4(int len); // ok + +typedef int *__counted_by_or_null(16) f5(void); // ok +typedef void *__sized_by_or_null(16) f6(void); // ok +typedef int *__counted_by_or_null(len) f7(int len); // ok +typedef void *__sized_by_or_null(len) f8(int len); // ok + +typedef int *__counted_by(16) (*fp1)(void); // ok +typedef void *__sized_by(16) (*fp2)(void); // ok +typedef int *__counted_by(len) (*fp3)(int len); // ok +typedef void *__sized_by(len) (*fp4)(int len); // ok + +typedef int *__counted_by_or_null(16) (*fp5)(void); // ok +typedef void *__sized_by_or_null(16) (*fp6)(void); // ok +typedef int *__counted_by_or_null(len) (*fp7)(int len); // ok +typedef void *__sized_by_or_null(len) (*fp8)(int len); // ok diff --git a/clang/test/BoundsSafety/Sema/typedef-endedby.c b/clang/test/BoundsSafety/Sema/typedef-endedby.c new file mode 100644 index 0000000000000..f5c6cfd23c5ec --- /dev/null +++ b/clang/test/BoundsSafety/Sema/typedef-endedby.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +int *gend; +// expected-error@+1{{'__ended_by' inside typedef is only allowed for function type}} +typedef int *__ended_by(gend) endedz_t; +// expected-error@+1{{'__ended_by' inside typedef is only allowed for function type}} +typedef int *__ended_by(gend) * endedz_nested_t; + + +void foo() { + endedz_t gip_cnt_local; + + int *end; + typedef int *__ended_by(end) liptr_counted_t; // expected-error{{'__ended_by' inside typedef is only allowed for function type}} +} + +typedef int *__ended_by(pend) f(int *pend); // ok +typedef int *__ended_by(pend) (*fp)(void *pend);// ok diff --git a/clang/test/BoundsSafety/Sema/typeof-func-rebuild-types.c b/clang/test/BoundsSafety/Sema/typeof-func-rebuild-types.c new file mode 100644 index 0000000000000..3555cbcb84781 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/typeof-func-rebuild-types.c @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// Check if clang rebuilds param/return types of function declared with +// typeof/typedef. +// If the types are not rebuilt, the count expr in `bar` and `baz` will refer +// to `len` in `foo`, and the analysis will fail to catch the following errors. + +void foo(int *__counted_by(len), int len); + +__typeof__(foo) bar; + +typedef void(baz_t)(int *__counted_by(len), int len); + +baz_t baz; + +void test(void) { + // expected-note@+1 3{{'array' declared here}} + int array[42]; + // expected-error@+1{{passing array 'array' (which has 42 elements) to parameter of type 'int *__single __counted_by(len)' (aka 'int *__single') with count value of 100 always fails}} + foo(array, 100); + // expected-error@+1{{passing array 'array' (which has 42 elements) to parameter of type 'int *__single __counted_by(len)' (aka 'int *__single') with count value of 100 always fails}} + bar(array, 100); + // expected-error@+1{{passing array 'array' (which has 42 elements) to parameter of type 'int *__single __counted_by(len)' (aka 'int *__single') with count value of 100 always fails}} + baz(array, 100); +} diff --git a/clang/test/BoundsSafety/Sema/typeof-func-redecl.c b/clang/test/BoundsSafety/Sema/typeof-func-redecl.c new file mode 100644 index 0000000000000..18ec90a2ea126 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/typeof-func-redecl.c @@ -0,0 +1,15 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +// expected-no-diagnostics + +void foo(int len, int *__counted_by(len) p); + +__typeof__(foo) foo; +extern __attribute__((weak_import)) __typeof__(foo) foo; + +void bar(int len, int *__counted_by(len) p) { foo(len, p); } diff --git a/clang/test/BoundsSafety/Sema/typo-error.c b/clang/test/BoundsSafety/Sema/typo-error.c new file mode 100644 index 0000000000000..24fc281693cef --- /dev/null +++ b/clang/test/BoundsSafety/Sema/typo-error.c @@ -0,0 +1,13 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s + +#include + +void bar(unsigned x, void *__sized_by(z) y, unsigned z); + +// expected-note@+1{{'filter_mode' declared here}} +void foo(unsigned filter_mode) { + void *p = 0; + // expected-error@+1{{use of undeclared identifier 'filterMode'}} + bar(filterMode, p, 0); +} diff --git a/clang/test/BoundsSafety/Sema/unreachable-noret.c b/clang/test/BoundsSafety/Sema/unreachable-noret.c new file mode 100644 index 0000000000000..dbaaae3d53720 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unreachable-noret.c @@ -0,0 +1,21 @@ + + +// RUN: %clang_cc1 -fbounds-safety -Wunreachable-code -verify %s +// RUN: %clang_cc1 -fbounds-safety -Wunreachable-code -x objective-c -fbounds-attributes-objc-experimental -verify %s + +// expected-no-diagnostics + +#include + +#define NO_RETURN __attribute__((noreturn)) +void NO_RETURN halt(const void * const p_fatal_error); +static void NO_RETURN handler_private(const void *__sized_by(0x78) p_stack) +{ + int foo; + halt(&foo); +} + +void NO_RETURN handler_irq(const void *__sized_by(0x78) p_stack) +{ + handler_private(p_stack); +} diff --git a/clang/test/BoundsSafety/Sema/unsafe-buffer-usage-interop-crash.cpp b/clang/test/BoundsSafety/Sema/unsafe-buffer-usage-interop-crash.cpp new file mode 100644 index 0000000000000..914611b845887 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unsafe-buffer-usage-interop-crash.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -std=c++20 -Wno-all -fexperimental-bounds-safety-attributes -verify %s +#include +typedef unsigned size_t; +class MyClass { + size_t m; + int q[__counted_by(m)]; + + // Previously, this example will crash. +}; + +namespace value_dependent_assertion_violation { + // Running attribute-only mode on the C++ code below crashes + // previously. + void g(int * __counted_by(n) * p, size_t n); + + template + struct S { + void f(T p) { + g(nullptr, sizeof(p)); + } + }; + + // expected-error@-4{{incompatible dynamic count pointer argument to parameter of type 'int * __counted_by(n)*' (aka 'int **')}} + template struct S; // expected-note{{in instantiation of member function 'value_dependent_assertion_violation::S::f' requested here}} +} diff --git a/clang/test/BoundsSafety/Sema/unsafe-late-const.c b/clang/test/BoundsSafety/Sema/unsafe-late-const.c new file mode 100644 index 0000000000000..869fcdea0e174 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unsafe-late-const.c @@ -0,0 +1,22 @@ + + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x c++ -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c -verify %s +// RUN: %clang_cc1 -fexperimental-bounds-safety-attributes -x objective-c++ -verify %s + +#include + +unsigned global_data_const __unsafe_late_const; +void *global_data_const2 __unsafe_late_const; + +// expected-error@+2{{'__unsafe_late_const' attribute only applies to global variables}} +// expected-error@+2{{'__unsafe_late_const' attribute only applies to global variables}} +void __unsafe_late_const +test(int param __unsafe_late_const) { + static long static_local __unsafe_late_const; + // expected-error@+1{{'__unsafe_late_const' attribute only applies to global variables}} + int local __unsafe_late_const; +} diff --git a/clang/test/BoundsSafety/Sema/unsafe-to-bound-no-auto-bound.c b/clang/test/BoundsSafety/Sema/unsafe-to-bound-no-auto-bound.c new file mode 100644 index 0000000000000..508aa7520d9b5 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unsafe-to-bound-no-auto-bound.c @@ -0,0 +1,19 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +int main() { + int * __unsafe_indexable up; + int * __bidi_indexable bp = up; // expected-error{{initializing 'int *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + bp = (int *)up; // expected-error{{assigning to 'int *__bidi_indexable' from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + bp = (int *)bp; + int *__indexable ap = (int *)up; // expected-error{{initializing 'int *__indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + ap = up; // expected-error{{assigning to 'int *__indexable' from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + ap = (int *)ap; + + void *__bidi_indexable vp = up; // expected-error{{initializing 'void *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/unsafe-to-bound.c b/clang/test/BoundsSafety/Sema/unsafe-to-bound.c new file mode 100644 index 0000000000000..a48fe0b21c53a --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unsafe-to-bound.c @@ -0,0 +1,28 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify=expected %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify=expected %s + +#include + +int main() { + int * __unsafe_indexable up; + int * __bidi_indexable bp = up; // expected-error{{initializing 'int *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + bp = (int *)up; // expected-error{{assigning to 'int *__bidi_indexable' from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + bp = (int *)bp; + int *__indexable ap = (int *)up; // expected-error{{initializing 'int *__indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + ap = up; // expected-error{{assigning to 'int *__indexable' from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + ap = (int *)ap; + int *auto_p = up; // expected-error{{initializing 'int *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + auto_p = up; // expected-error{{assigning to 'int *__bidi_indexable' from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + auto_p = (int*)up; // expected-error{{assigning to 'int *__bidi_indexable' from incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + auto_p = (void*__bidi_indexable)0; // no error + + void *vp = up; // expected-error{{initializing 'void *__bidi_indexable' with an expression of incompatible type 'int *__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + vp = (void*)0; // no error + + bp = (int*)0; + + typedef int * myintptr_t; + myintptr_t mp = (myintptr_t)0; + return 0; +} diff --git a/clang/test/BoundsSafety/Sema/unsafe-to-indexable-explicit.c b/clang/test/BoundsSafety/Sema/unsafe-to-indexable-explicit.c new file mode 100644 index 0000000000000..ec875df69c718 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unsafe-to-indexable-explicit.c @@ -0,0 +1,59 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-unused-value -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-unused-value -verify %s + +#include + +typedef struct { + void *vptr; + int valid; +} S; + +void foo() { + int *__unsafe_indexable ptrUnsafe; + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + int *ptrAuto = (int *__bidi_indexable)(ptrUnsafe); + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + int *__indexable ptrIndex = (char *__bidi_indexable)(ptrUnsafe); + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + int *__bidi_indexable ptrBidiIndex = (void *__indexable)(ptrUnsafe); + int *ptrAuto2 = (void *__indexable)0; + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety}} + int *ptrAuto3 = (void *__indexable)0xdead; + + (void *__bidi_indexable) ptrIndex; + (void *__indexable) ptrIndex; + (void *__bidi_indexable) ptrBidiIndex; + (void *__indexable) ptrBidiIndex; + (int *__bidi_indexable) ptrIndex; + (int *__indexable) ptrIndex; + (int *__bidi_indexable) ptrBidiIndex; + (int *__indexable) ptrBidiIndex; + (S *__bidi_indexable) ptrIndex; + (S *__indexable) ptrIndex; + (S *__bidi_indexable) ptrBidiIndex; + (S *__indexable) ptrBidiIndex; + + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety}} + ptrAuto = (char *__bidi_indexable) (0xdead); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety}} + (char *__single) (0xdead); + (char *) (0xdead); + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety}} + (char *__bidi_indexable) 0xdead; + // expected-error@+1{{non-pointer to safe pointer conversion is not allowed with -fbounds-safety}} + (S *__indexable) 0xdead; + + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + (void *__bidi_indexable) ptrUnsafe; + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + (void *__indexable) ptrUnsafe; + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + (int *__bidi_indexable) ptrUnsafe; + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + (int *__indexable) ptrUnsafe; + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + (S *__bidi_indexable) ptrUnsafe; + // expected-error-re@+1{{casting 'int *__unsafe_indexable' to incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} + (S *__indexable) ptrUnsafe; +} diff --git a/clang/test/BoundsSafety/Sema/unsafe-to-single.c b/clang/test/BoundsSafety/Sema/unsafe-to-single.c new file mode 100644 index 0000000000000..bcdb07578396c --- /dev/null +++ b/clang/test/BoundsSafety/Sema/unsafe-to-single.c @@ -0,0 +1,76 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wno-unused-value -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wno-unused-value -verify %s + +#include + +struct SizedByData { + void *__sized_by(len) data; + unsigned len; +}; + +void unsafe_to_sizedby(int *__unsafe_indexable ptrUnsafe) { + struct SizedByData st; + st.data = ptrUnsafe; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} + +void unsafe_to_single(int *__unsafe_indexable ptrUnsafe) { + int *__single sp = ptrUnsafe; // expected-error-re{{initializing {{.+}} with an expression of incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} + +struct EndedByData { + char *end; + char *__ended_by(end) begin; +}; + +void unsafe_to_endedby(struct EndedByData *st) { + int *__unsafe_indexable ptrUnsafe; + st->begin = ptrUnsafe; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + st->end = ptrUnsafe; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} + +// expected-note@+1{{passing argument to parameter 'arg' here}} +void func_with_single(int *arg); +void unsafe_to_single_parameter(void) { + int *__unsafe_indexable ptrUnsafe; + func_with_single(ptrUnsafe); // expected-error-re{{passing '{{.+}}*__unsafe_indexable' to parameter of incompatible type {{.+}} casts away '__unsafe_indexable' qualifier}} +} + +typedef int * myintptr_t; +void null_to_single(void) { + myintptr_t __single dst = (myintptr_t)0; +} + +void pointer_casts(int *__unsafe_indexable ptrUnsafe) { + int *__single ptrSingle = (char *)ptrUnsafe; // expected-error-re{{initializing {{.+}} with an expression of incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + ptrSingle = (char *__unsafe_indexable)ptrSingle; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} + ptrSingle = (char *__unsafe_indexable)ptrUnsafe; // expected-error-re{{assigning to {{.+}} from incompatible type '{{.+}}*__unsafe_indexable' casts away '__unsafe_indexable' qualifier}} +} + +void unsafe_to_single_nested(void) { + int *__unsafe_indexable* ptrUnsafeNested; + int **ptr2d = ptrUnsafeNested; // expected-error{{initializing 'int *__single*__bidi_indexable' with an expression of incompatible nested pointer type 'int *__unsafe_indexable*__bidi_indexable'}} +} + +void pointer_cast_2d(void) { + int **ptr2d; + char **cptr2d = (char**)ptr2d; +} + +void pointer_cast_2d_unsafe(void) { + int *__unsafe_indexable*ptr2d; + // expected-error@+1{{initializing 'char *__single*__bidi_indexable' with an expression of incompatible nested pointer type 'char *__unsafe_indexable*__bidi_indexable'}} + char **cptr2d = (char**)ptr2d; +} + +void pointer_cast_2d_unsafe_explicit(void) { + int **ptr2d; + // expected-error@+1{{initializing 'char *__single*__bidi_indexable' with an expression of incompatible nested pointer type 'char *__unsafe_indexable*__bidi_indexable'}} + char **cptr2d = (char *__unsafe_indexable*)ptr2d; +} + +void pointer_cast_2d_unsafe_to_safe_explicit(void) { + int *__unsafe_indexable*ptr2d; + // expected-error@+1{{initializing 'char *__unsafe_indexable*__bidi_indexable' with an expression of incompatible nested pointer type 'char *__single*__bidi_indexable'}} + char *__unsafe_indexable*cptr2d = (char *__single*)ptr2d; +} diff --git a/clang/test/BoundsSafety/Sema/warn-access-vla.c b/clang/test/BoundsSafety/Sema/warn-access-vla.c new file mode 100644 index 0000000000000..b155f69d629ab --- /dev/null +++ b/clang/test/BoundsSafety/Sema/warn-access-vla.c @@ -0,0 +1,17 @@ + + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +struct Foo { + int f; + int vla[]; +}; + +void Test() { + struct Foo f; + (void) f.vla; + // expected-warning@-1{{accessing elements of an unannotated incomplete array always fails at runtime}} +} diff --git a/clang/test/BoundsSafety/Sema/wide-ptr-inline-assembly.c b/clang/test/BoundsSafety/Sema/wide-ptr-inline-assembly.c new file mode 100644 index 0000000000000..03962b0d13320 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/wide-ptr-inline-assembly.c @@ -0,0 +1,103 @@ + + +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -fsyntax-only -fbounds-safety -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx11.0.0 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void outputs(void) { + char * __bidi_indexable c_b_i; + __asm__ volatile ("xyz %0" : "=r" (c_b_i)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + char * __indexable c_i; + __asm__ volatile ("xyz %0" : "=r" (c_i)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + int n; + char * __counted_by(n) c_b; + __asm__ volatile ("xyz %0" : "=r" (c_b)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + + char * __single s; + __asm__ volatile ("xyz %0" : "=r" (s)); + + char * r; + __asm__ volatile ("xyz %0" : "=r" (r)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} +} + +void output_count(void) { + int n; + char * __counted_by(n) c_b; + __asm__ volatile ("xyz %0" : "=r" (n)); + // expected-error@-1{{external count of a pointer cannot be used with inline assembly}} +} + +struct Foo { + int n; + char * __counted_by(n) c_b; +}; + +void output_field(void) { + struct Foo f; + __asm__ volatile ("xyz %0" : "=r" (f.c_b)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + __asm__ volatile ("xyz %0" : "=r" (f.n)); + // expected-error@-1{{external count of a pointer cannot be used with inline assembly}} +} + +void output_ended_by( char * end, char * __ended_by(end) c_e_b) { + __asm__ volatile ("xyz %0" : "=r" (c_e_b)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} +} + +void output_end( char * end, char * __ended_by(end) c_e_b) { + __asm__ volatile ("xyz %0" : "=r" (end)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} +} + +void input_ended_by( char * end, char * __ended_by(end) c_e_b) { + __asm__ volatile ("xyz %0" :: "r" (c_e_b)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + + __asm__ volatile ("xyz %0" :: "r" (end)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} +} + +void inputs(unsigned long long temp) { + unsigned foo; + + unsigned * __bidi_indexable u_b_i; + __asm__ volatile ("xyz %1,%0" + : "=r" (foo) + : "r" (u_b_i)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + + unsigned * __indexable u_i; + __asm__ volatile ("xyz %1,%0" + : "=r" (foo) + : "r" (u_i)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + + int n; + unsigned * __counted_by(n) u_c_b; + __asm__ volatile ("xyz %1,%0" + : "=r" (foo) + : "r" (u_c_b)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} + + __asm__ volatile ("xyz %1,%0" + : "=r" (foo) + : "r" (n)); + // expected-error@-1{{external count of a pointer cannot be used with inline assembly}} + + unsigned * __single u_s; + __asm__ volatile ("xyz %1,%0" + : "=r" (foo) + : "r" (u_s)); + + unsigned * u_r; + __asm__ volatile ("xyz %1,%0" + : "=r" (foo) + : "r" (u_r)); + // expected-error@-1{{pointer with bounds cannot be used with inline assembly}} +} diff --git a/clang/test/BoundsSafety/Sema/wide-to-counted-pointer-arg-side-effect.c b/clang/test/BoundsSafety/Sema/wide-to-counted-pointer-arg-side-effect.c new file mode 100644 index 0000000000000..af0a5ee42078e --- /dev/null +++ b/clang/test/BoundsSafety/Sema/wide-to-counted-pointer-arg-side-effect.c @@ -0,0 +1,22 @@ + + +// RUN: %clang_cc1 -fbounds-safety -verify %s +// RUN: %clang_cc1 -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -verify %s + +#include + +void Foo(int *__counted_by(len) buf, int len) {} + +unsigned long get_len(void *__bidi_indexable ptr); +unsigned long trap_if_bigger_than_max(unsigned long len); + + +int Test() { + int arr[10]; + Foo(arr, trap_if_bigger_than_max(get_len(arr))); + unsigned long len = trap_if_bigger_than_max(get_len(arr)); + Foo(arr, len); + return 0; +} + +// expected-no-diagnostics diff --git a/clang/test/BoundsSafety/Sema/wpointer-arith-byte-size-check.c b/clang/test/BoundsSafety/Sema/wpointer-arith-byte-size-check.c new file mode 100644 index 0000000000000..56e72c3696705 --- /dev/null +++ b/clang/test/BoundsSafety/Sema/wpointer-arith-byte-size-check.c @@ -0,0 +1,22 @@ + +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -Wpointer-arith -verify %s +// RUN: %clang_cc1 -fsyntax-only -fbounds-safety -x objective-c -fbounds-attributes-objc-experimental -Wpointer-arith -verify %s + +// expected-no-diagnostics + +#include + +struct S { + int *__sized_by(l) bp; + int l; +}; + +int main() { + struct S s; + + int arr[10]; + s.bp = arr; + s.l = 10; + + return 0; +} diff --git a/clang/test/CAS/Inputs/MacOSX11.0.sdk/SDKSettings.json b/clang/test/CAS/Inputs/MacOSX11.0.sdk/SDKSettings.json new file mode 100644 index 0000000000000..b40e35e882e60 --- /dev/null +++ b/clang/test/CAS/Inputs/MacOSX11.0.sdk/SDKSettings.json @@ -0,0 +1,23 @@ +{ + "DefaultVariant": "macos", "DisplayName": "macOS 11", + "Version": "11.0", + "MaximumDeploymentTarget": "11.0.99", + "PropertyConditionFallbackNames": [], "VersionMap": { + "iOSMac_macOS": { + "13.2": "10.15.1", + "13.4": "10.15.4", + "13.3.1": "10.15.3", + "13.3": "10.15.2", + "13.1": "10.15", + "14.0": "11.0" + }, + "macOS_iOSMac": { + "10.15.2": "13.3", + "11.0": "14.0", + "10.15": "13.1", + "10.15.3": "13.3.1", + "10.15.1": "13.2", + "10.15.4": "13.4" + } + } +} diff --git a/clang/test/CAS/Inputs/SDK/Library/Frameworks/.keep b/clang/test/CAS/Inputs/SDK/Library/Frameworks/.keep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/CAS/Inputs/SDK/usr/include/stdlib.h b/clang/test/CAS/Inputs/SDK/usr/include/stdlib.h new file mode 100644 index 0000000000000..a1bf1a8b29450 --- /dev/null +++ b/clang/test/CAS/Inputs/SDK/usr/include/stdlib.h @@ -0,0 +1 @@ +typedef __SIZE_TYPE__ size_t; diff --git a/clang/test/CAS/Inputs/cmake-build/.cmake/api/v1/reply/codemodel-v2-1234.json b/clang/test/CAS/Inputs/cmake-build/.cmake/api/v1/reply/codemodel-v2-1234.json new file mode 100644 index 0000000000000..030043507ff45 --- /dev/null +++ b/clang/test/CAS/Inputs/cmake-build/.cmake/api/v1/reply/codemodel-v2-1234.json @@ -0,0 +1,145 @@ +{ + "configurations": + [ + { + "directories": + [ + { + "build": ".", + "childIndexes": + [ + 1, + 7, + 9, + 10 + ], + "hasInstallRule": true, + "projectIndex": 0, + "source": "." + }, + { + "build": "tools", + "childIndexes": + [ + 2, + 3 + ], + "hasInstallRule": true, + "parentIndex": 0, + "projectIndex": 0, + "source": "tools" + }, + { + "build": "tools/llc", + "hasInstallRule": true, + "parentIndex": 1, + "projectIndex": 0, + "source": "tools/llc" + }, + { + "build": "tools/clang", + "childIndexes": + [ + 4, + 5 + ], + "hasInstallRule": true, + "parentIndex": 1, + "projectIndex": 0, + "source": "/llvm/llvm-project/clang" + }, + { + "build": "tools/clang/include", + "parentIndex": 3, + "projectIndex": 0, + "source": "/llvm/llvm-project/clang/include" + }, + { + "build": "tools/clang/tools", + "childIndexes": + [ + 6 + ], + "hasInstallRule": true, + "parentIndex": 3, + "projectIndex": 0, + "source": "/llvm/llvm-project/clang/tools" + }, + { + "build": "tools/clang/tools/extra", + "hasInstallRule": true, + "parentIndex": 5, + "projectIndex": 0, + "source": "/llvm/llvm-project/clang-tools-extra" + }, + { + "build": "third-party/benchmark", + "childIndexes": + [ + 8 + ], + "parentIndex": 0, + "projectIndex": 1, + "source": "/llvm/llvm-project/third-party/benchmark" + }, + { + "build": "third-party/benchmark/src", + "parentIndex": 7, + "projectIndex": 1, + "source": "/llvm/llvm-project/third-party/benchmark/src" + }, + { + "build": "other/benchmark", + "parentIndex": 0, + "projectIndex": 1, + "source": "/llvm/llvm-project/other/benchmark" + }, + { + "build": "another/benchmark", + "parentIndex": 0, + "projectIndex": 1, + "source": "/llvm/llvm-project/another/benchmark" + } + ], + "name": "Debug", + "projects": + [ + { + "directoryIndexes": + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6 + ], + "name": "LLVM" + }, + { + "directoryIndexes": + [ + 7, + 8, + 9, + 10 + ], + "name": "benchmark", + "parentIndex": 0 + } + ] + } + ], + "kind": "codemodel", + "paths": + { + "build": "/llvm/build", + "source": "/llvm/llvm-project/llvm" + }, + "version": + { + "major": 2, + "minor": 2 + } +} diff --git a/clang/test/CAS/Inputs/cmake-build/.cmake/api/v1/reply/index-2022-05-31T01-02-40-0412.json b/clang/test/CAS/Inputs/cmake-build/.cmake/api/v1/reply/index-2022-05-31T01-02-40-0412.json new file mode 100644 index 0000000000000..46774e936eab9 --- /dev/null +++ b/clang/test/CAS/Inputs/cmake-build/.cmake/api/v1/reply/index-2022-05-31T01-02-40-0412.json @@ -0,0 +1,50 @@ +{ + "cmake": + { + "generator": + { + "multiConfig": false, + "name": "Ninja" + }, + "paths": + { + "cmake": "/bin/cmake", + "cpack": "/bin/cpack", + "ctest": "/bin/ctest", + "root": "/share/cmake-3.19" + }, + "version": + { + "isDirty": true, + "major": 3, + "minor": 19, + "patch": 6, + "string": "3.19.6" + } + }, + "objects": + [ + { + "jsonFile": "codemodel-v2-1234.json", + "kind": "codemodel", + "version": + { + "major": 2, + "minor": 2 + } + } + ], + "reply": + { + "codemodel-v2": + { + "jsonFile": "codemodel-v2-1234.json", + "kind": "codemodel", + "version": + { + "major": 2, + "minor": 2 + } + } + } +} diff --git a/clang/test/CAS/Inputs/pgo.profraw b/clang/test/CAS/Inputs/pgo.profraw new file mode 100644 index 0000000000000..401ba073493c5 --- /dev/null +++ b/clang/test/CAS/Inputs/pgo.profraw @@ -0,0 +1 @@ +:fe diff --git a/clang/test/CAS/Inputs/pgo2.profraw b/clang/test/CAS/Inputs/pgo2.profraw new file mode 100644 index 0000000000000..dd73f168d68b0 --- /dev/null +++ b/clang/test/CAS/Inputs/pgo2.profraw @@ -0,0 +1,6 @@ +:ir +ic1 +10 +2 +999000 +359800 diff --git a/clang/test/CAS/Inputs/test.h b/clang/test/CAS/Inputs/test.h new file mode 100644 index 0000000000000..92d167e6b8fa5 --- /dev/null +++ b/clang/test/CAS/Inputs/test.h @@ -0,0 +1,7 @@ +#define TEST __TEST + +#pragma clang attribute push(__attribute__((annotate("test"))), apply_to = function) + +int test(void); + +#pragma clang attribute pop diff --git a/clang/test/CAS/Inputs/toolchain_dir/lib/clang/1000/include/stdarg.h b/clang/test/CAS/Inputs/toolchain_dir/lib/clang/1000/include/stdarg.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/CAS/Inputs/toolchain_dir/usr/lib/clang/1000/include/stdarg.h b/clang/test/CAS/Inputs/toolchain_dir/usr/lib/clang/1000/include/stdarg.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/clang/test/CAS/analyze-action-remote-service.c b/clang/test/CAS/analyze-action-remote-service.c new file mode 100644 index 0000000000000..a095b318dc2a2 --- /dev/null +++ b/clang/test/CAS/analyze-action-remote-service.c @@ -0,0 +1,40 @@ +// REQUIRES: remote-cache-service + +// Need a short path for the unix domain socket (and unique for this test file). +// RUN: rm -f %{remote-cache-dir}/%basename_t +// RUN: rm -rf %t && mkdir -p %t + +// RUN: %clang_cc1 -cc1 -triple x86_64-apple-macosx12 -analyze -analyzer-checker=deadcode -analyzer-output plist %s -o %t/regular.plist 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-DIAG +// RUN: FileCheck %s --input-file=%t/regular.plist --check-prefix=CHECK-PLIST + +// CHECK-DIAG: Value stored to 'v' during its initialization is never read +// CHECK-PLIST: Value stored to 'v' during its initialization is never read + +// RUN: llvm-remote-cache-test -socket-path=%{remote-cache-dir}/%basename_t -cache-path=%t/cache -- env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macosx12 --analyze --analyzer-output plist %s -o %t/cached.plist +// RUN: rm %t/cached.plist +// RUN: llvm-remote-cache-test -socket-path=%{remote-cache-dir}/%basename_t -cache-path=%t/cache -- env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macosx12 --analyze --analyzer-output plist %s -o %t/cached.plist -Rcompile-job-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-HIT + +// CHECK-HIT: remark: compile job cache hit +// CHECK-HIT: Value stored to 'v' during its initialization is never read + +// RUN: diff -u %t/regular.plist %t/cached.plist + +// Check cache is skipped for analyzer html output. +// RUN: llvm-remote-cache-test -socket-path=%{remote-cache-dir}/%basename_t -cache-path=%t/cache -- env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macosx12 --analyze --analyzer-output html %s -o %t/analysis -Rcompile-job-cache -Wclang-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-HTML +// RUN: llvm-remote-cache-test -socket-path=%{remote-cache-dir}/%basename_t -cache-path=%t/cache -- env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macosx12 --analyze --analyzer-output html %s -o %t/analysis -Rcompile-job-cache -Wclang-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-HTML + +// CHECK-HTML: remark: compile job cache miss +// CHECK-HTML: remark: compile job cache skipped +// FIXME: `analyse` action passes `-w` for `-cc1` args and the "caching disabled" warning doesn't show up. + +void foo(int *p) { + int v = p[0]; +} diff --git a/clang/test/CAS/analyze-action.c b/clang/test/CAS/analyze-action.c new file mode 100644 index 0000000000000..40f5800388ccb --- /dev/null +++ b/clang/test/CAS/analyze-action.c @@ -0,0 +1,37 @@ +// RUN: rm -rf %t && mkdir -p %t + +// RUN: %clang_cc1 -cc1 -triple x86_64-apple-macosx12 -analyze -analyzer-checker=deadcode -analyzer-output plist %s -o %t/regular.plist 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-DIAG +// RUN: FileCheck %s --input-file=%t/regular.plist --check-prefix=CHECK-PLIST + +// CHECK-DIAG: Value stored to 'v' during its initialization is never read +// CHECK-PLIST: Value stored to 'v' during its initialization is never read + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macosx12 -fcas-path %t/cas -analyze -analyzer-checker=deadcode -analyzer-output plist %s -o %t/cached.plist +// RUN: %clang @%t/t.rsp + +// RUN: rm %t/cached.plist +// RUN: %clang @%t/t.rsp -Rcompile-job-cache 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-HIT + +// CHECK-HIT: remark: compile job cache hit +// CHECK-HIT: Value stored to 'v' during its initialization is never read + +// RUN: diff -u %t/regular.plist %t/cached.plist + +// Check cache is skipped for analyzer html output. +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t2.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macosx12 -fcas-path %t/cas -analyze -analyzer-checker=deadcode -analyzer-output html %s -o %t/analysis +// RUN: %clang @%t/t2.rsp -Rcompile-job-cache 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-HTML +// RUN: %clang @%t/t2.rsp -Rcompile-job-cache 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-HTML + +// CHECK-HTML: remark: compile job cache miss +// CHECK-HTML: warning: caching disabled because analyzer output is not supported +// CHECK-HTML: remark: compile job cache skipped + +void foo(int *p) { + int v = p[0]; +} diff --git a/clang/test/CAS/availability-check.c b/clang/test/CAS/availability-check.c new file mode 100644 index 0000000000000..9d4ad7d267477 --- /dev/null +++ b/clang/test/CAS/availability-check.c @@ -0,0 +1,22 @@ +// RUN: rm -rf %t && mkdir %t + +// RUN: %clang_cc1 -triple x86_64-apple-ios14-macabi -isysroot %S/Inputs/MacOSX11.0.sdk -fsyntax-only -verify %s + +// RUN: %clang -cc1depscan -o %t/cmd.rsp -fdepscan=inline -fdepscan-include-tree -cc1-args \ +// RUN: -cc1 -fcas-path %t/cas -triple x86_64-apple-ios14-macabi -isysroot %S/Inputs/MacOSX11.0.sdk %s -fsyntax-only + +// RUN: %clang -cc1depscan -o %t/cmd-casfs.rsp -fdepscan=inline -cc1-args \ +// RUN: -cc1 -fcas-path %t/cas -triple x86_64-apple-ios14-macabi -isysroot %S/Inputs/MacOSX11.0.sdk %s -fsyntax-only + +// FIXME: `-verify` should work with a CAS invocation. +// RUN: not %clang @%t/cmd.rsp 2> %t/out.txt +// RUN: not %clang @%t/cmd-casfs.rsp 2> %t/out-casfs.txt +// RUN: FileCheck -input-file %t/out.txt %s +// RUN: FileCheck -input-file %t/out-casfs.txt %s +// CHECK: error: 'fUnavail' is unavailable + +void fUnavail(void) __attribute__((availability(macOS, obsoleted = 10.15))); // expected-note {{marked unavailable here}} + +void test() { + fUnavail(); // expected-error {{unavailable}} +} diff --git a/clang/test/CAS/cache-launcher-scan-diagnostics.c b/clang/test/CAS/cache-launcher-scan-diagnostics.c new file mode 100644 index 0000000000000..a8c652770d971 --- /dev/null +++ b/clang/test/CAS/cache-launcher-scan-diagnostics.c @@ -0,0 +1,10 @@ +// REQUIRES: system-darwin, clang-cc1daemon + +// RUN: rm -rf %t && mkdir -p %t + +// RUN: not env LLVM_CACHE_CAS_PATH=%t/cas cache-build-session %clang-cache \ +// RUN: %clang -fsyntax-only -x c %s \ +// RUN: 2>&1 | FileCheck %s + +#include "missing.h" +// CHECK: fatal error: 'missing.h' file not found diff --git a/clang/test/CAS/cached-diagnostics.c b/clang/test/CAS/cached-diagnostics.c new file mode 100644 index 0000000000000..4acfd43c5a264 --- /dev/null +++ b/clang/test/CAS/cached-diagnostics.c @@ -0,0 +1,103 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t/src +// RUN: mkdir %t/out + +// RUN: %clang_cc1 -triple x86_64-apple-macos12 -fsyntax-only %t/src/main.c -I %t/src/inc -Wunknown-pragmas 2> %t/regular-diags1.txt + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t1.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas -fdepscan-prefix-map=%t/src=/^src \ +// RUN: -emit-obj %t/src/main.c -o %t/out/output.o -I %t/src/inc -Wunknown-pragmas + +// Compare diagnostics after a miss. +// RUN: %clang @%t/t1.rsp 2> %t/diags1.txt +// RUN: diff -u %t/regular-diags1.txt %t/diags1.txt + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t1.noprefix.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas \ +// RUN: -emit-obj %t/src/main.c -o %t/out/output.o -I %t/src/inc -Wunknown-pragmas + +// Compare diagnostics without prefix mappings. +// RUN: %clang @%t/t1.noprefix.rsp 2> %t/diags1.noprefix.txt +// RUN: diff -u %t/regular-diags1.txt %t/diags1.noprefix.txt + +// Check that we have both the remark and source diagnostics. +// RUN: %clang @%t/t1.rsp -Rcompile-job-cache 2> %t/diags-hit1.txt +// RUN: FileCheck %s -input-file %t/diags-hit1.txt +// CHECK: remark: compile job cache hit for +// CHECK: warning: some warning +// CHECK: warning: unknown pragma ignored +// CHECK: warning: using the result of an assignment as a condition without parentheses + +// RUN: cat %t/diags-hit1.txt | grep remark: | sed \ +// RUN: -e "s/^.*hit for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key1 + +// Compare diagnostics after a hit. +// RUN: %clang @%t/t1.rsp 2> %t/cached-diags1.txt +// RUN: diff -u %t/regular-diags1.txt %t/cached-diags1.txt + +// RUN: split-file %s %t/src2 +// RUN: mkdir %t/out2 + +// RUN: %clang_cc1 -triple x86_64-apple-macos12 -fsyntax-only %t/src2/main.c -I %t/src2/inc -Wunknown-pragmas 2> %t/regular-diags2.txt + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t2.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas -fdepscan-prefix-map=%t/src2=/^src \ +// RUN: -emit-obj %t/src2/main.c -o %t/out2/output.o -I %t/src2/inc -Wunknown-pragmas +// RUN: %clang @%t/t2.rsp -Rcompile-job-cache 2> %t/diags-hit2.txt + +// RUN: cat %t/diags-hit2.txt | grep remark: | sed \ +// RUN: -e "s/^.*hit for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key2 +// RUN: diff -u %t/cache-key1 %t/cache-key2 + +// RUN: %clang @%t/t2.rsp 2> %t/cached-diags2.txt +// RUN: diff -u %t/regular-diags2.txt %t/cached-diags2.txt + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/terr.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas -fdepscan-prefix-map=%t/src=/^src \ +// RUN: -emit-obj %t/src/main.c -o %t/out/output.o -I %t/src/inc -Rcompile-job-cache -DERROR + +// RUN: not %clang @%t/terr.rsp -ferror-limit 1 2> %t/diags_error1.txt +// RUN: FileCheck %s -check-prefix=ERROR1 -input-file %t/diags_error1.txt + +// ERROR1: error: E1 +// ERROR1-NOT: error: +// ERROR1: fatal error: too many errors emitted + +// RUN: not %clang @%t/terr.rsp -ferror-limit 2 2> %t/diags_error2.txt +// RUN: FileCheck %s -check-prefix=ERROR2 -input-file %t/diags_error2.txt + +// ERROR2: error: E1 +// ERROR2: error: E2 +// ERROR2-NOT: error: + +//--- main.c + +#include "t1.h" + +#ifdef ERROR +#error E1 +#error E2 +#endif + +//--- inc/t1.h + +#warning some warning + +_Pragma("unknown1") + +#define PRAG(x) _Pragma(x) +PRAG("unknown2") + +void test(int x) { + if (x=0) {} +} + +#define DEPR _Pragma("GCC warning \"name is deprecated\"") +#define PARAM "A" DEPR +#define MAC(x) x + +void test2() { + (void)MAC(PARAM); +} diff --git a/clang/test/CAS/cached-diags-scratch-space.c b/clang/test/CAS/cached-diags-scratch-space.c new file mode 100644 index 0000000000000..6b8f611232f8a --- /dev/null +++ b/clang/test/CAS/cached-diags-scratch-space.c @@ -0,0 +1,235 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: llvm-cas --cas %t/cas --ingest %s > %t/casid + +// Check that this doesn't crash and provides proper round-tripping. + +// RUN: %clang -cc1 -triple x86_64-apple-macos12 -fsyntax-only \ +// RUN: -fconst-strings -Wincompatible-pointer-types-discards-qualifiers \ +// RUN: -serialize-diagnostic-file %t/regular.dia %s 2> %t/regular-diags.txt + +// RUN: %clang -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas \ +// RUN: -fcas-fs @%t/casid -fcache-compile-job -emit-obj -o %t/output.o \ +// RUN: -fconst-strings -Wincompatible-pointer-types-discards-qualifiers \ +// RUN: -serialize-diagnostic-file %t/t1.dia %s 2> %t/diags1.txt + +// RUN: %clang -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas \ +// RUN: -fcas-fs @%t/casid -fcache-compile-job -emit-obj -o %t/output.o \ +// RUN: -fconst-strings -Wincompatible-pointer-types-discards-qualifiers \ +// RUN: -serialize-diagnostic-file %t/t2.dia %s 2> %t/diags2.txt + +// RUN: diff -u %t/regular-diags.txt %t/diags1.txt +// RUN: diff -u %t/regular-diags.txt %t/diags2.txt +// RUN: diff %t/regular.dia %t/t1.dia +// RUN: diff %t/regular.dia %t/t2.dia + +#define STR(x) #x + +void fn(char *x); +void test() { + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); + fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); fn(STR(something1)); +} diff --git a/clang/test/CAS/cas-backend.c b/clang/test/CAS/cas-backend.c new file mode 100644 index 0000000000000..662c45b0f7212 --- /dev/null +++ b/clang/test/CAS/cas-backend.c @@ -0,0 +1,34 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: llvm-cas --cas %t/cas --ingest %s > %t/casid +// +// RUN: %clang -cc1 -fcas-emit-casid-file -triple x86_64-apple-macos11 -fcas-backend \ +// RUN: -fcas-path %t/cas -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache %s -emit-obj -o %t/output.o \ +// RUN: -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb \ +// RUN: -dependency-file %t/deps.d -MT %t/output.o 2>&1 \ +// RUN: | FileCheck %s --allow-empty --check-prefix=CACHE-MISS +// +// RUN: ls %t/output.o && rm %t/output.o +// RUN: ls %t/deps.d && mv %t/deps.d %t/deps.d.orig +// +// RUN: %clang -cc1 -fcas-emit-casid-file\ +// RUN: -triple x86_64-apple-macos11 -fcas-backend \ +// RUN: -fcas-path %t/cas -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache %s -emit-obj -o %t/output.o \ +// RUN: -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb \ +// RUN: -dependency-file %t/deps.d -MT %t/output.o 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CACHE-HIT +// +// RUN: ls %t/output.o +// RUN: diff -u %t/deps.d %t/deps.d.orig +// RUN: llvm-cas-dump --cas %t/cas --casid-file \ +// RUN: --object-stats - %t/output.o.casid +// +// CACHE-HIT: remark: compile job cache hit +// CACHE-MISS-NOT: remark: compile job cache hit + +void test(void) {} + +int test1(void) { + return 0; +} diff --git a/clang/test/CAS/cas-emit-casid.c b/clang/test/CAS/cas-emit-casid.c new file mode 100644 index 0000000000000..e5598a161d923 --- /dev/null +++ b/clang/test/CAS/cas-emit-casid.c @@ -0,0 +1,32 @@ +// REQUIRES: aarch64-registered-target +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang -target arm64-apple-macosx12.0.0 -c -Xclang -fcas-backend -Xclang -fcas-path -Xclang %t/cas -Xclang -fcas-backend-mode=native -Xclang -fcas-emit-casid-file %s -o %t/test.o +// RUN: cat %t/test.o.casid | FileCheck %s --check-prefix=NATIVE_FILENAME +// NATIVE_FILENAME: CASID:Jllvmcas://{{.*}} +// +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang -target arm64-apple-macosx12.0.0 -c -Xclang -fcas-backend -Xclang -fcas-path -Xclang %t/cas -Xclang -fcas-backend-mode=verify -Xclang -fcas-emit-casid-file %s -o %t/test.o +// RUN: cat %t/test.o.casid | FileCheck %s --check-prefix=VERIFY_FILENAME +// VERIFY_FILENAME: CASID:Jllvmcas://{{.*}} +// +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang -target arm64-apple-macosx12.0.0 -c -Xclang -fcas-backend -Xclang -fcas-path -Xclang %t/cas -Xclang -fcas-backend-mode=casid -Xclang -fcas-emit-casid-file %s -o %t/test.o +// RUN: not cat %t/test.o.casid +// +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang -target arm64-apple-macosx12.0.0 -c -Xclang -fcas-backend -Xclang -fcas-path -Xclang %t/cas -Xclang -fcas-backend-mode=native -Xclang -fcas-emit-casid-file %s -o - +// RUN: not cat %t/test.o.casid +// +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang -target arm64-apple-macosx12.0.0 -c -Xclang -fcas-backend -Xclang -fcas-path -Xclang %t/cas -Xclang -fcas-backend-mode=verify -Xclang -fcas-emit-casid-file %s -o - +// RUN: not cat %t/test.o.casid +// +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang -target arm64-apple-macosx12.0.0 -c -Xclang -fcas-backend -Xclang -fcas-path -Xclang %t/cas -Xclang -fcas-backend-mode=casid -Xclang -fcas-emit-casid-file %s -o - +// RUN: not cat %t/test.o.casid + +void test(void) {} + +int test1(void) { + return 0; +} diff --git a/clang/test/CAS/cas-module-file-use-without-cas.c b/clang/test/CAS/cas-module-file-use-without-cas.c new file mode 100644 index 0000000000000..303366a31755c --- /dev/null +++ b/clang/test/CAS/cas-module-file-use-without-cas.c @@ -0,0 +1,63 @@ +// Tests for reusing a PCM output from CAS builds by a non-cas build. +// This is to simulate a configuration by debugger without CAS support. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t %t.cas +// RUN: split-file %s %t + +// RUN: llvm-cas --cas %t.cas --ingest %t > %t/casid + +// == Build B + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=B -fno-implicit-modules \ +// RUN: -emit-module %t/module.modulemap -o %t/B.pcm \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/B.out.txt +// RUN: cat %t/B.out.txt | sed -E "s:^.*cache [a-z]+ for '([^']+)'.*$:\1:" > %t/B.key + +// == Build A, importing B + +// RUN: echo -n '-fmodule-file-cache-key %t/B.pcm ' > %t/B.import.rsp +// RUN: cat %t/B.key >> %t/B.import.rsp + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=A -fno-implicit-modules \ +// RUN: @%t/B.import.rsp -fmodule-file=%t/B.pcm \ +// RUN: -emit-module %t/module.modulemap -o %t/A.pcm \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/A.out.txt +// RUN: cat %t/A.out.txt | sed -E "s:^.*cache [a-z]+ for '([^']+)'.*$:\1:" > %t/A.key + +// == Build tu, importing A and B, without a CAS, this should fail. + +// RUN: not %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: -fmodule-file=%t/A.pcm\ +// RUN: -fmodule-file=%t/B.pcm\ +// RUN: -fsyntax-only %t/tu.c + +// == Using option to ignore CAS info inside module + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: -fmodule-file=%t/A.pcm\ +// RUN: -fmodule-file=%t/B.pcm\ +// RUN: -fsyntax-only %t/tu.c -fmodule-load-ignore-cas + +//--- module.modulemap +module A { header "A.h" export * } +module B { header "B.h" } + +//--- A.h +#include "B.h" + +//--- B.h +void B(void); + +//--- tu.c +#include "A.h" +void tu(void) { + B(); +} diff --git a/clang/test/CAS/casid-output-test.cpp b/clang/test/CAS/casid-output-test.cpp new file mode 100644 index 0000000000000..2e4f98761b64b --- /dev/null +++ b/clang/test/CAS/casid-output-test.cpp @@ -0,0 +1,25 @@ +// RUN: rm -rf %t && mkdir -p %t + +// Check if -fcasid-output works on a cache miss with file based caching +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas CLANG_CACHE_DISABLE_MCCAS=1 %clang-cache %clang -target x86_64-apple-macos11 -Xclang -fcasid-output -g -c %s -o %t/test.o +// RUN: cat %t/test.o | FileCheck %s +// RUN: rm -rf %t/test.o +// Check if -fcasid-output works on a cache hit with file based caching +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas CLANG_CACHE_DISABLE_MCCAS=1 %clang-cache %clang -target x86_64-apple-macos11 -Xclang -fcasid-output -g -c %s -o %t/test.o +// RUN: cat %t/test.o | FileCheck %s +// RUN: rm -rf %t/test.o +// RUN: rm -rf %t/cas + +// Check if -fcasid-output works on a cache miss with MCCAS +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache %clang -target x86_64-apple-macos11 -Xclang -fcasid-output -g -c %s -o %t/test.o +// RUN: cat %t/test.o | FileCheck %s +// RUN: rm -rf %t/test.o + +// Check if -fcasid-output works on a cache hit with MCCAS +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache %clang -target x86_64-apple-macos11 -Xclang -fcasid-output -g -c %s -o %t/test.o +// RUN: cat %t/test.o | FileCheck %s + +// CHECK: llvmcas://{{[a-f0-9]+}} + + +void foo() {} diff --git a/clang/test/CAS/cmd-macro-and-pch.c b/clang/test/CAS/cmd-macro-and-pch.c new file mode 100644 index 0000000000000..2026c561a9cf3 --- /dev/null +++ b/clang/test/CAS/cmd-macro-and-pch.c @@ -0,0 +1,20 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +// Normal compilation for baseline. +// RUN: %clang_cc1 -x c-header %t/prefix.h -DSOME_MACRO=1 -emit-pch -o %t/prefix1.pch -Werror +// RUN: %clang_cc1 %t/t1.c -include-pch %t/prefix1.pch -DSOME_MACRO=1 -fsyntax-only -Werror + +// RUN: %clang -cc1depscan -o %t/pch.rsp -fdepscan=inline -fdepscan-include-tree -cc1-args \ +// RUN: -cc1 -x c-header %t/prefix.h -emit-pch -DSOME_MACRO=1 -fcas-path %t/cas -Werror +// RUN: %clang @%t/pch.rsp -o %t/prefix2.pch + +// RUN: %clang -cc1depscan -o %t/tu.rsp -fdepscan=inline -fdepscan-include-tree -cc1-args \ +// RUN: -cc1 %t/t1.c -fsyntax-only -include-pch %t/prefix2.pch -DSOME_MACRO=1 -fcas-path %t/cas -Werror +// RUN: %clang @%t/tu.rsp + +//--- t1.c + +//--- prefix.h +#undef SOME_MACRO +#define SOME_MACRO 0 diff --git a/clang/test/CAS/custom-diags.m b/clang/test/CAS/custom-diags.m new file mode 100644 index 0000000000000..5bac91f050a1a --- /dev/null +++ b/clang/test/CAS/custom-diags.m @@ -0,0 +1,14 @@ +// RUN: rm -rf %t && mkdir -p %t + +// RUN: not %clang_cc1 -triple arm64-apple-macosx12 -fsyntax-only %s 2> %t/diags-orig + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t.rsp -cc1-args \ +// RUN: -cc1 -triple arm64-apple-macosx12 -fcas-path %t/cas -fsyntax-only %s +// RUN: not %clang @%t/t.rsp 2> %t/diags-cached + +// RUN: diff -u %t/diags-orig %t/diags-cached + +// RUN: FileCheck %s -input-file %t/diags-cached + +const char s8[] = @encode(__SVInt8_t); +// CHECK: cannot yet @encode type __SVInt8_t diff --git a/clang/test/CAS/daemon-cwd.c b/clang/test/CAS/daemon-cwd.c new file mode 100644 index 0000000000000..b41a130784e34 --- /dev/null +++ b/clang/test/CAS/daemon-cwd.c @@ -0,0 +1,27 @@ +// Test running -fdepscan with a daemon launched from different directory. +// +// REQUIRES: system-darwin, clang-cc1daemon + +// RUN: rm -rf %t && mkdir -p %t/include +// RUN: cp %S/Inputs/test.h %t/include +// RUN: echo "#!/bin/sh" >> %t/cmd.sh +// RUN: echo cd %t >> %t/cmd.sh +// RUN: echo %clang -target x86_64-apple-macos11 -fdepscan=daemon \ +// RUN: -fdepscan-prefix-map=%S=/^source \ +// RUN: -fdepscan-prefix-map=%t=/^build \ +// RUN: -fdepscan-prefix-map-toolchain=/^toolchain \ +// RUN: -fdepscan-daemon=%{clang-daemon-dir}/%basename_t \ +// RUN: -Xclang -fcas-path -Xclang %t/cas \ +// RUN: -MD -MF %t/test.d -Iinclude \ +// RUN: -fsyntax-only -x c %s >> %t/cmd.sh +// RUN: chmod +x %t/cmd.sh + +// RUN: %clang -cc1depscand -execute %{clang-daemon-dir}/%basename_t \ +// RUN: -cas-args -fcas-path %t/cas -- %t/cmd.sh +// RUN: (cd %t && %clang -target x86_64-apple-macos11 -MD -MF %t/test2.d \ +// RUN: -Iinclude -fsyntax-only -x c %s) +// RUN: diff %t/test.d %t/test2.d + +#include "test.h" + +int func(void); diff --git a/clang/test/CAS/depscan-cas-log.c b/clang/test/CAS/depscan-cas-log.c new file mode 100644 index 0000000000000..85111a5a9efc1 --- /dev/null +++ b/clang/test/CAS/depscan-cas-log.c @@ -0,0 +1,18 @@ +// Ensure both the first clang process and the daemon have logging enabled. +// It's hard to check this exhaustively, but in practice if the daemon does not +// enable logging there are currently zero records in the log. + +// RUN: rm -rf %t && mkdir %t +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas LLVM_CAS_LOG=1 %clang \ +// RUN: -cc1depscan -fdepscan=daemon -fdepscan-include-tree -o - \ +// RUN: -cc1-args -cc1 -triple x86_64-apple-macosx11.0.0 -emit-obj %s -o %t/t.o -fcas-path %t/cas +// RUN: FileCheck %s --input-file %t/cas/v1.log + +// CHECK: [[PID1:[0-9]*]] {{[0-9]*}}: mmap '{{.*}}v8.index' +// CHECK: [[PID1]] {{[0-9]*}}: create subtrie + +// CHECK: [[PID2:[0-9]*]] {{[0-9]*}}: mmap '{{.*}}v8.index' +// Even a minimal compilation involves at least 9 records for the cache key. +// CHECK-COUNT-9: [[PID2]] {{[0-9]*}}: create record + +// CHECK: [[PID1]] {{[0-9]*}}: close mmap '{{.*}}v8.index' diff --git a/clang/test/CAS/depscan-dependency-file.c b/clang/test/CAS/depscan-dependency-file.c new file mode 100644 index 0000000000000..9fcf72649bf07 --- /dev/null +++ b/clang/test/CAS/depscan-dependency-file.c @@ -0,0 +1,27 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: split-file %s %t + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t.rsp -cc1-args \ +// RUN: -cc1 -fcas-path %t/cas -triple x86_64-apple-macos11 %t/main.c -emit-obj -o %t/output.o -isystem %t/sys \ +// RUN: -MT deps -dependency-file %t/t.d +// RUN: FileCheck %s -input-file=%t/t.d -check-prefix=NOSYS +// RUN: FileCheck %s -input-file=%t/t.d -check-prefix=COMMON + +// Including system headers. +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t.rsp -cc1-args \ +// RUN: -cc1 -fcas-path %t/cas -triple x86_64-apple-macos11 %t/main.c -emit-obj -o %t/output.o -isystem %t/sys \ +// RUN: -MT deps -sys-header-deps -dependency-file %t/t-sys.d +// RUN: FileCheck %s -input-file=%t/t-sys.d -check-prefix=WITHSYS -check-prefix=COMMON + +// NOSYS-NOT: sys.h +// COMMON: main.c +// COMMON: my_header.h +// WITHSYS: sys.h + +//--- main.c +#include "my_header.h" +#include + +//--- my_header.h + +//--- sys/sys.h diff --git a/clang/test/CAS/depscan-include-tree-daemon.c b/clang/test/CAS/depscan-include-tree-daemon.c new file mode 100644 index 0000000000000..b28ba43ebf09c --- /dev/null +++ b/clang/test/CAS/depscan-include-tree-daemon.c @@ -0,0 +1,26 @@ +// REQUIRES: system-darwin, clang-cc1daemon +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: %clang -cc1depscan -o %t/inline.rsp -fdepscan=inline -fdepscan-include-tree -cc1-args -cc1 -triple x86_64-apple-macos11.0 \ +// RUN: -fsyntax-only %t/t.c -I %t/includes -isysroot %S/Inputs/SDK -fcas-path %t/cas -DSOME_MACRO -dependency-file %t/inline.d -MT deps + +// RUN: %clang -cc1depscand -execute %{clang-daemon-dir}/%basename_t -cas-args -fdepscan-include-tree -fcas-path %t/cas -- \ +// RUN: %clang -cc1depscan -o %t/daemon.rsp -fdepscan=daemon -fdepscan-daemon=%{clang-daemon-dir}/%basename_t -fdepscan-include-tree \ +// RUN: -cc1-args -cc1 -triple x86_64-apple-macos11.0 \ +// RUN: -fsyntax-only %t/t.c -I %t/includes -isysroot %S/Inputs/SDK -fcas-path %t/cas -DSOME_MACRO -dependency-file %t/daemon.d -MT deps + +// RUN: diff -u %t/inline.rsp %t/daemon.rsp +// RUN: diff -u %t/inline.d %t/daemon.d + +//--- t.c +#include "t.h" + +int test(struct S *s) { + return s->x; +} + +//--- includes/t.h +struct S { + int x; +}; diff --git a/clang/test/CAS/depscan-include-tree.c b/clang/test/CAS/depscan-include-tree.c new file mode 100644 index 0000000000000..a75fb2e0f8b72 --- /dev/null +++ b/clang/test/CAS/depscan-include-tree.c @@ -0,0 +1,43 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: %clang -cc1depscan -o %t/inline.rsp -fdepscan=inline -fdepscan-include-tree -cc1-args -cc1 -triple x86_64-apple-macos11.0 \ +// RUN: -emit-obj %t/t.c -o %t/t.o -dwarf-ext-refs -fmodule-format=obj \ +// RUN: -coverage-notes-file=%t/t.gcno -coverage-data-file=%t/t.gcda \ +// RUN: -I %t/includes -isysroot %S/Inputs/SDK -fcas-path %t/cas -DSOME_MACRO -dependency-file %t/inline.d -MT deps + +// RUN: FileCheck %s -input-file %t/inline.rsp -DPREFIX=%t +// RUN: FileCheck %s -input-file %t/inline.rsp -DPREFIX=%t -check-prefix=SHOULD + +// RUN: %clang @%t/inline.rsp + +// CHECK: "-fcas-path" "[[PREFIX]]/cas" +// CHECK: "-fcas-include-tree" +// CHECK: "-isysroot" +// CHECK: "-coverage-data-file=[[PREFIX]]/t.gcda" +// CHECK: "-coverage-notes-file=[[PREFIX]]/t.gcno" +// SHOULD-NOT: "-fcas-fs" +// SHOULD-NOT: "-fcas-fs-working-directory" +// SHOULD-NOT: "-I" +// SHOULD-NOT: "[[PREFIX]]/t.c" +// SHOULD-NOT: "-D" +// SHOULD-NOT: "-dwarf-ext-refs" +// SHOULD-NOT: "-fmodule-format=obj" + +// RUN: FileCheck %s -input-file %t/inline.d -check-prefix=DEPS -DPREFIX=%t + +// DEPS: deps: +// DEPS: [[PREFIX]]/t.c +// DEPS: [[PREFIX]]/includes/t.h + +//--- t.c +#include "t.h" + +int test(struct S *s) { + return s->x; +} + +//--- includes/t.h +struct S { + int x; +}; diff --git a/clang/test/CAS/depscan-prefix-map.c b/clang/test/CAS/depscan-prefix-map.c new file mode 100644 index 0000000000000..25d1fa177b85b --- /dev/null +++ b/clang/test/CAS/depscan-prefix-map.c @@ -0,0 +1,72 @@ +// REQUIRES: clang-cc1daemon +// +// Check with prefix mapping: +// +// RUN: rm -rf %t.d +// RUN: mkdir %t.d +// RUN: %clang -cc1depscan -dump-depscan-tree=%t.root -fdepscan=inline \ +// RUN: -cc1-args -triple x86_64-apple-macos11.0 -x c %s -o %t.d/out.o \ +// RUN: -isysroot %S/Inputs/SDK \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/usr/lib/clang/1000 \ +// RUN: -internal-isystem %S/Inputs/toolchain_dir/usr/lib/clang/1000/include \ +// RUN: -working-directory %t.d \ +// RUN: -fcas-path %t.d/cas \ +// RUN: -fdepscan-prefix-map=%S=/^source \ +// RUN: -fdepscan-prefix-map=%t.d=/^testdir \ +// RUN: -fdepscan-prefix-map=%{objroot}=/^objroot \ +// RUN: -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \ +// RUN: -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \ +// RUN: -fdepfile-entry=%t.d/extra \ +// RUN: | FileCheck %s -DPREFIX=%t.d +// RUN: %clang -cc1depscan -dump-depscan-tree=%t.root -fdepscan=inline \ +// RUN: -cc1-args -triple x86_64-apple-macos11.0 -x c %s -o %t.d/out.o \ +// RUN: -isysroot %S/Inputs/SDK \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 \ +// RUN: -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \ +// RUN: -working-directory %t.d \ +// RUN: -fcas-path %t.d/cas \ +// RUN: -fdepscan-prefix-map=%S=/^source \ +// RUN: -fdepscan-prefix-map=%t.d=/^testdir \ +// RUN: -fdepscan-prefix-map=%{objroot}=/^objroot \ +// RUN: -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \ +// RUN: -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \ +// RUN: -fdepfile-entry=%t.d/extra \ +// RUN: | FileCheck %s -DPREFIX=%t.d +// RUN: %clang -cc1depscand -execute %{clang-daemon-dir}/%basename_t \ +// RUN: -cas-args -fcas-path %t.d/cas -- \ +// RUN: %clang -cc1depscan -dump-depscan-tree=%t.root -fdepscan=daemon \ +// RUN: -fdepscan-daemon=%{clang-daemon-dir}/%basename_t \ +// RUN: -cc1-args -triple x86_64-apple-macos11.0 -x c %s -o %t.d/out.o \ +// RUN: -isysroot %S/Inputs/SDK \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/usr/lib/clang/1000 \ +// RUN: -internal-isystem %S/Inputs/toolchain_dir/usr/lib/clang/1000/include \ +// RUN: -working-directory %t.d \ +// RUN: -fcas-path %t.d/cas \ +// RUN: -fdepscan-prefix-map=%S=/^source \ +// RUN: -fdepscan-prefix-map=%t.d=/^testdir \ +// RUN: -fdepscan-prefix-map=%{objroot}=/^objroot \ +// RUN: -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \ +// RUN: -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \ +// RUN: -fdepfile-entry=%t.d/extra \ +// RUN: | FileCheck %s -DPREFIX=%t.d +// +// CHECK: "-fcas-path" "[[PREFIX]]/cas" +// CHECK-SAME: "-working-directory" "/^testdir" +// CHECK-SAME: "-x" "c" "/^source/depscan-prefix-map.c" +// CHECK-SAME: "-isysroot" "/^sdk" +// CHECK-SAME: "-fdepfile-entry=/^testdir/extra" + +// RUN: llvm-cas --cas %t.d/cas --ls-tree-recursive @%t.root \ +// RUN: | FileCheck %s -check-prefix=CHECK-ROOT +// +// RUN: llvm-cas --cas %t.d/cas --ls-tree-recursive @%t.root \ +// RUN: | FileCheck %s -check-prefix=CHECK-ROOT +// +// CHECK-ROOT: tree +// CHECK-ROOT-SAME: /^objroot/test/CAS/{{$}} +// CHECK-ROOT-NEXT: tree {{.*}} /^sdk/Library/Frameworks/{{$}} +// CHECK-ROOT-NEXT: file {{.*}} /^source/depscan-prefix-map.c{{$}} +// CHECK-ROOT-NEXT: file {{.*}} /^toolchain/usr/lib/clang/1000/include/stdarg.h{{$}} + +#include +int test() { return 0; } diff --git a/clang/test/CAS/depscan-update-mccas.c b/clang/test/CAS/depscan-update-mccas.c new file mode 100644 index 0000000000000..c9cd52c67f6eb --- /dev/null +++ b/clang/test/CAS/depscan-update-mccas.c @@ -0,0 +1,24 @@ +// RUN: %clang -cc1depscan -o - -cc1-args -cc1 -triple \ +// RUN: x86_64-apple-darwin10 -debug-info-kind=standalone -dwarf-version=4 \ +// RUN: -debugger-tuning=lldb -emit-obj -fcas-backend -fcas-path %t/cas \ +// RUN: -fcas-emit-casid-file -mllvm -cas-friendly-debug-info %s | FileCheck %s --check-prefix=MCCAS_ON + +// MCCAS_ON: -mllvm +// MCCAS_ON: -cas-friendly-debug-info +// MCCAS_ON: -fcas-backend +// MCCAS_ON: -fcas-emit-casid-file + +// RUN: %clang -cc1depscan -o - -cc1-args -cc1 -triple \ +// RUN: x86_64-apple-darwin10 -debug-info-kind=standalone -dwarf-version=4 \ +// RUN: -debugger-tuning=lldb -emit-llvm -fcas-backend -fcas-path %t/cas \ +// RUN: -fcas-emit-casid-file -mllvm -cas-friendly-debug-info %s | FileCheck %s --check-prefix=MCCAS_OFF + +// MCCAS_OFF-NOT: -mllvm +// MCCAS_OFF-NOT: -cas-friendly-debug-info +// MCCAS_OFF-NOT: -fcas-backend +// MCCAS_OFF-NOT: -fcas-emit-casid-file + + +int foo(int x) { + return x+1; +} diff --git a/clang/test/CAS/depscan-with-error.c b/clang/test/CAS/depscan-with-error.c new file mode 100644 index 0000000000000..5bc50d39803de --- /dev/null +++ b/clang/test/CAS/depscan-with-error.c @@ -0,0 +1,58 @@ +// REQUIRES: clang-cc1daemon, ansi-escape-sequences + +// RUN: rm -rf %t && mkdir -p %t + +// RUN: not %clang -cc1depscan -fdepscan=inline -cc1-args -cc1 -triple x86_64-apple-macos11 -x c %s -o %t/t.o -fcas-path %t/cas \ +// RUN: 2>&1 | FileCheck %s -check-prefix=ERROR + +// Using normal compilation as baseline. +// RUN: not %clang -target x86_64-apple-macos11 -c %s -o %t.o -Wl,-none --serialize-diagnostics %t/t1.diag \ +// RUN: 2>&1 | FileCheck %s -check-prefix=ERROR -check-prefix=DRIVER +// RUN: not env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t.o -Wl,-none --serialize-diagnostics %t/t2.diag \ +// RUN: 2>&1 | FileCheck %s -check-prefix=ERROR -check-prefix=DRIVER +// RUN: not env LLVM_CACHE_CAS_PATH=%t/cas %clang -cc1depscand -execute %{clang-daemon-dir}/%basename_t -cas-args -fcas-path %t/cas -- \ +// RUN: %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t.o -Wl,-none --serialize-diagnostics %t/t3.diag \ +// RUN: 2>&1 | FileCheck %s -check-prefix=ERROR -check-prefix=DRIVER + +// RUN: diff %t/t1.diag %t/t2.diag +// RUN: diff %t/t1.diag %t/t3.diag + +// DRIVER: warning: -Wl,-none: 'linker' input unused +// ERROR: error: 'non-existent.h' file not found +// ERROR: 1 error generated. + +// Make sure successful compilation clears the diagnostic file. +// RUN: echo "int x;" > %t/a.c +// RUN: echo "int y;" > %t/b.c +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %t/a.c -o %t.o --serialize-diagnostics %t/t2.diag +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang -cc1depscand -execute %{clang-daemon-dir}/%basename_t -cas-args -fcas-path %t/cas -- \ +// RUN: %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %t/b.c -o %t.o --serialize-diagnostics %t/t3.diag + +// RUN: c-index-test -read-diagnostics %t/t2.diag 2>&1 | FileCheck %s -check-prefix=SERIAL +// RUN: c-index-test -read-diagnostics %t/t3.diag 2>&1 | FileCheck %s -check-prefix=SERIAL +// SERIAL: Number of diagnostics: 0 + +// Make sure warnings are still emitted for normal compilation. +// RUN: echo "#warning some warning" > %t/warn.c +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %t/warn.c -o %t.o \ +// RUN: 2>&1 | FileCheck %s -check-prefix=WARN +// WARN: warning: some warning + +// Make sure diagnostics emitted during CAS dep-scanning respect the color settings. +// RUN: not %clang -target x86_64-apple-macos11 -c %s -o %t.o -fdiagnostics-color=always -fansi-escape-codes \ +// RUN: 2>&1 | FileCheck %s -check-prefix=COLOR-DIAG +// RUN: not env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t.o -fdiagnostics-color=always -fansi-escape-codes \ +// RUN: 2>&1 | FileCheck %s -check-prefix=COLOR-DIAG +// RUN: not env LLVM_CACHE_CAS_PATH=%t/cas %clang -cc1depscand -execute %{clang-daemon-dir}/%basename_t -cas-args -fcas-path %t/cas -- \ +// RUN: %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t.o -fdiagnostics-color=always -fansi-escape-codes \ +// RUN: 2>&1 | FileCheck %s -check-prefix=COLOR-DIAG +// COLOR-DIAG: [[RED:.\[0;1;31m]]fatal error: [[RESET:.\[0m]] + +#include "non-existent.h" diff --git a/clang/test/CAS/depscan.c b/clang/test/CAS/depscan.c new file mode 100644 index 0000000000000..becb02164f824 --- /dev/null +++ b/clang/test/CAS/depscan.c @@ -0,0 +1,20 @@ +// REQUIRES: clang-cc1daemon +// +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang -cc1depscan -fdepscan=inline -cc1-args -cc1 -triple x86_64-apple-macos11.0 -x c %s -o %s.o -MT %s.o -dependency-file %t.d -fcas-path %t/cas 2>&1 | FileCheck %s -DPREFIX=%t +// RUN: %clang -cc1depscan -fdepscan=inline -cc1-args -triple x86_64-apple-macos11.0 -x c %s -o %s.o -MT %s.o -dependency-file %t.d -fcas-path %t/cas 2>&1 | FileCheck %s -DPREFIX=%t +// +// Check that inline/daemon have identical output. +// RUN: %clang -cc1depscan -o %t/inline.rsp -fdepscan=inline -cc1-args -triple x86_64-apple-macos11.0 -x c %s -o %s.o -MT %s.o -dependency-file %t/inline.d -fcas-path %t/cas +// RUN: %clang -cc1depscand -execute %{clang-daemon-dir}/%basename_t -cas-args -fcas-path %t/cas -- \ +// RUN: %clang -cc1depscan -o %t/daemon.rsp -fdepscan=daemon -fdepscan-daemon=%{clang-daemon-dir}/%basename_t -cc1-args \ +// RUN: -triple x86_64-apple-macos11.0 -x c %s -o %s.o -MT %s.o -dependency-file %t/daemon.d -fcas-path %t/cas +// RUN: diff %t/inline.rsp %t/daemon.rsp +// RUN: diff %t/inline.d %t/daemon.d + +// CHECK: {{^}}"-cc1" +// CHECK: "-fcas-path" "[[PREFIX]]/cas" +// CHECK-NOT: dependency-file +// CHECK-NOT: [[PREFIX]].d + +int test() { return 0; } diff --git a/clang/test/CAS/driver-cache-launcher.c b/clang/test/CAS/driver-cache-launcher.c new file mode 100644 index 0000000000000..121a8245e53f7 --- /dev/null +++ b/clang/test/CAS/driver-cache-launcher.c @@ -0,0 +1,163 @@ +// REQUIRES: shell + +// RUN: rm -rf %t && mkdir %t +// RUN: echo "#!/bin/sh" > %t/clang +// RUN: echo "echo run some compiler with opts \$*" >> %t/clang +// RUN: chmod +x %t/clang +// RUN: ln -s %clang %t/clang-symlink-outside-bindir + +// 'clang-cache' launcher invokes itself, enables caching. +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache %clang -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=CLANG -DPREFIX=%t +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache %clang++ -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=CLANGPP -DPREFIX=%t +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache %t/clang-symlink-outside-bindir -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=CLANG -DPREFIX=%t +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas PATH="%t:$PATH" %clang-cache clang-symlink-outside-bindir -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=CLANG -DPREFIX=%t + +// CLANG: "-cc1depscan" "-fdepscan=auto" +// CLANG: "-fcas-path" "[[PREFIX]]/cas" +// CLANG: "-greproducible" +// CLANG: "-x" "c" + +// CLANGPP: "-cc1depscan" "-fdepscan=auto" +// CLANGPP: "-fcas-path" "[[PREFIX]]/cas" +// CLANGPP: "-greproducible" +// CLANGPP: "-x" "c++" + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache %clang -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=INCLUDE-TREE -DPREFIX=%t +// INCLUDE-TREE: "-cc1depscan" "-fdepscan=auto" +// INCLUDE-TREE: "-fdepscan-include-tree" + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas CLANG_CACHE_USE_CASFS_DEPSCAN=1 %clang-cache %clang -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=CASFS-SCAN -DPREFIX=%t +// CASFS-SCAN-NOT: "-fdepscan-include-tree" + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas cache-build-session %clang-cache %clang -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=SESSION -DPREFIX=%t +// SESSION: "-cc1depscan" "-fdepscan=daemon" "-fdepscan-share-identifier" +// SESSION: "-fcas-path" "[[PREFIX]]/cas" +// SESSION: "-greproducible" + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas CLANG_CACHE_SCAN_DAEMON_SOCKET_PATH=%t/scand cache-build-session %clang-cache %clang -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=SPECIFIC-DAEMON -DPREFIX=%t +// SPECIFIC-DAEMON-NOT: "-fdepscan-share-identifier" +// SPECIFIC-DAEMON: "-cc1depscan" "-fdepscan=daemon" "-fdepscan-daemon=[[PREFIX]]/scand" + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas LLVM_CACHE_PLUGIN_PATH=%t/plugin LLVM_CACHE_PLUGIN_OPTIONS=some-opt=value:opt2=val2 \ +// RUN: %clang-cache %clang -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=PLUGIN -DPREFIX=%t +// PLUGIN: "-fcas-path" "[[PREFIX]]/cas" +// PLUGIN: "-fcas-plugin-path" "[[PREFIX]]/plugin" +// PLUGIN: "-fcas-plugin-option" "some-opt=value" +// PLUGIN: "-fcas-plugin-option" "opt2=val2" + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas \ +// RUN: %clang-cache %clang -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=ENABLE-MCCAS -DPREFIX=%t +// ENABLE-MCCAS: "-fcas-path" "[[PREFIX]]/cas" +// ENABLE-MCCAS: "-fcas-backend" +// ENABLE-MCCAS: "-mllvm" +// ENABLE-MCCAS: "-cas-friendly-debug-info" + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas CLANG_CACHE_VERIFY_MCCAS=1 \ +// RUN: %clang-cache %clang -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=ENABLE-MCCAS-VERIFY -DPREFIX=%t +// ENABLE-MCCAS-VERIFY: "-fcas-path" "[[PREFIX]]/cas" +// ENABLE-MCCAS-VERIFY: "-fcas-backend" +// ENABLE-MCCAS-VERIFY: "-fcas-backend-mode=verify" +// ENABLE-MCCAS-VERIFY: "-mllvm" +// ENABLE-MCCAS-VERIFY: "-cas-friendly-debug-info" + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas CLANG_CACHE_DISABLE_MCCAS=1 \ +// RUN: %clang-cache %clang -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=DISABLE-MCCAS -DPREFIX=%t + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas LLVM_CACHE_REMOTE_SERVICE_SOCKET_PATH=%t/ccremote \ +// RUN: %clang-cache %clang -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=DISABLE-MCCAS -DPREFIX=%t + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas LLVM_CACHE_REMOTE_SERVICE_SOCKET_PATH=%t/ccremote CLANG_CACHE_DISABLE_MCCAS=1 \ +// RUN: %clang-cache %clang -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=DISABLE-MCCAS -DPREFIX=%t + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas LLVM_CACHE_REMOTE_SERVICE_SOCKET_PATH=%t/ccremote CLANG_CACHE_DISABLE_MCCAS=1 CLANG_CACHE_VERIFY_MCCAS=1 \ +// RUN: %clang-cache %clang -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=DISABLE-MCCAS -DPREFIX=%t + +// DISABLE-MCCAS-NOT: "-fcas-backend" +// DISABLE-MCCAS-NOT: "-fcas-backend-mode=verify" +// DISABLE-MCCAS-NOT: "-mllvm" "-cas-friendly-debug-info" + + + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas LLVM_CACHE_REMOTE_SERVICE_SOCKET_PATH=%t/ccremote %clang-cache %clang -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=REMOTE -DPREFIX=%t +// REMOTE: "-fcompilation-caching-service-path" "[[PREFIX]]/ccremote" + +// Using multi-arch invocation. +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache %clang -target x86_64-apple-macos12 -arch x86_64 -arch arm64 -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=MULTIARCH +// MULTIARCH: "-cc1depscan" "-fdepscan=auto" +// MULTIARCH: "-triple" "x86_64-apple-macosx12.0.0" +// MULTIARCH: "-cc1depscan" "-fdepscan=auto" +// MULTIARCH: "-triple" "arm64-apple-macosx12.0.0" + +// RUN: cp -R %S/Inputs/cmake-build %t/cmake-build +// RUN: pushd %t/cmake-build +// RUN: cache-build-session -prefix-map-cmake -v echo 2>&1 | FileCheck %s -check-prefix=SESSION-CMAKE-PREFIX +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas cache-build-session -prefix-map-cmake %clang-cache %clang -c %s -o %t.o -isysroot %S/Inputs/SDK -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -### 2>&1 | FileCheck %s -check-prefix=CLANG-CMAKE-PREFIX -DPREFIX=%t -DINPUTS=%S/Inputs +// RUN: popd + +// SESSION-CMAKE-PREFIX: note: setting LLVM_CACHE_PREFIX_MAPS=/llvm/build=/^build;/llvm/llvm-project/llvm=/^src;/llvm/llvm-project/clang=/^src-clang;/llvm/llvm-project/clang-tools-extra=/^src-clang-tools-extra;/llvm/llvm-project/third-party/benchmark=/^src-benchmark;/llvm/llvm-project/other/benchmark=/^src-benchmark-1;/llvm/llvm-project/another/benchmark=/^src-benchmark-2{{$}} +// SESSION-CMAKE-PREFIX: note: setting LLVM_CACHE_BUILD_SESSION_ID= + +// CLANG-CMAKE-PREFIX: "-cc1depscan" "-fdepscan=daemon" "-fdepscan-share-identifier" +// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=[[INPUTS]]/SDK=/^sdk" +// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=[[INPUTS]]/toolchain_dir=/^toolchain" +// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/build=/^build" +// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/llvm-project/llvm=/^src" +// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/llvm-project/clang=/^src-clang" +// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/llvm-project/clang-tools-extra=/^src-clang-tools-extra" +// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/llvm-project/third-party/benchmark=/^src-benchmark" +// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/llvm-project/other/benchmark=/^src-benchmark-1" +// CLANG-CMAKE-PREFIX: "-fdepscan-prefix-map=/llvm/llvm-project/another/benchmark=/^src-benchmark-2" + +// Make sure `cache-build-session` can invoke an executable script. +// RUN: cache-build-session %t/clang -c %s -o %t.o 2>&1 | FileCheck %s -check-prefix=SESSION-SCRIPT -DSRC=%s -DPREFIX=%t +// SESSION-SCRIPT: run some compiler with opts -c [[SRC]] -o [[PREFIX]].o + +// 'clang-cache' launcher invokes a different clang, does normal non-caching launch. +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache %t/clang -c %s -o %t.o 2>&1 | FileCheck %s -check-prefix=OTHERCLANG -DSRC=%s -DPREFIX=%t +// OTHERCLANG: warning: caching disabled because clang-cache invokes a different clang binary than itself +// OTHERCLANG-NEXT: run some compiler with opts -c [[SRC]] -o [[PREFIX]].o + +// RUN: %clang-cache %clang -x objective-c++ -fmodules -fmodules-cache-path=%t/mcp -fno-cxx-modules -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=NONMOD -DPREFIX=%t +// RUN: %clang-cache %clang -x c++ -fmodules -fmodules-cache-path=%t/mcp -fno-cxx-modules -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=NONMOD -DPREFIX=%t +// RUN: %clang-cache %clang -x objective-c -fmodules -fmodules-cache-path=%t/mcp -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=MOD -DPREFIX=%t +// RUN: %clang-cache %clang -x objective-c++ -fmodules -fmodules-cache-path=%t/mcp -fcxx-modules -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=MOD -DPREFIX=%t +// RUN: %clang-cache %clang -x c++ -fmodules -fmodules-cache-path=%t/mcp -fcxx-modules -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=MOD -DPREFIX=%t +// NONMOD: "-cc1depscan" +// MOD: warning: caching disabled because -fmodules is enabled +// MOD-NOT: "-cc1depscan" + +// RUN: touch %t/t.s +// RUN: %clang-cache %clang -target arm64-apple-macosx12 -c %t/t.s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=ASM +// ASM: warning: caching disabled because assembler language mode is enabled +// ASM-NOT: "-cc1depscan" + +// RUN: env AS_SECURE_LOG_FILE=%t/log %clang-cache %clang -target arm64-apple-macosx12 -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=AS_SECURE_LOG_FILE +// AS_SECURE_LOG_FILE: warning: caching disabled because AS_SECURE_LOG_FILE is set +// AS_SECURE_LOG_FILE-NOT: "-cc1depscan" + +// RUN: env LLVM_CACHE_WARNINGS=-Wno-clang-cache %clang-cache %clang -x c++ -fmodules -fmodules-cache-path=%t/mcp -fcxx-modules -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=MOD_HIDE -DPREFIX=%t +// MOD_HIDE-NOT: warning: caching disabled +// MOD_HIDE-NOT: "-cc1depscan" + +// RUN: env LLVM_CACHE_WARNINGS=-Werror=clang-cache not %clang-cache %clang -x c++ -fmodules -fmodules-cache-path=%t/mcp -fcxx-modules -c %s -o %t.o -### 2>&1 | FileCheck %s -check-prefix=MOD_ERR -DPREFIX=%t +// MOD_ERR: error: caching disabled because -fmodules is enabled + +// RUN: not %clang-cache %t/nonexistent -c %s -o %t.o 2>&1 | FileCheck %s -check-prefix=NONEXISTENT +// NONEXISTENT: error: clang-cache failed to execute compiler + +// RUN: not %clang-cache 2>&1 | FileCheck %s -check-prefix=NOCOMMAND +// NOCOMMAND: error: missing compiler command for clang-cache + +// Link-only job should not attempt to cache. +// RUN: touch %t.o +// RUN: %clang-cache %clang -target arm64-apple-macosx12 %t.o -o %t -Wl,-ObjC -### 2>&1 | FileCheck %s -check-prefix=STATIC_LINK +// STATIC_LINK-NOT: warning: +// STATIC_LINK-NOT: "-cc1depscan" +// STATIC_LINK: "{{[^"]*}}ld" + +// Unused option warning should only be emitted once. +// RUN: touch %t.o +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache %clang -target arm64-apple-macosx12 -fsyntax-only %s -Wl,-ObjC 2>&1 | FileCheck %s -check-prefix=UNUSED_OPT +// UNUSED_OPT-NOT: warning: +// UNUSED_OPT: warning: -Wl,-ObjC: 'linker' input unused +// UNUSED_OPT-NOT: warning: diff --git a/clang/test/CAS/fcache-compile-job-dependency-file.c b/clang/test/CAS/fcache-compile-job-dependency-file.c new file mode 100644 index 0000000000000..e8796fa273dba --- /dev/null +++ b/clang/test/CAS/fcache-compile-job-dependency-file.c @@ -0,0 +1,90 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: split-file %s %t/src +// RUN: llvm-cas --cas %t/cas --ingest %t/src > %t/casid +// +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache %t/src/main.c -emit-obj -o %t/output.o -isystem %t/src/sys \ +// RUN: -dependency-file %t/deps1.d -MT depends 2>&1 \ +// RUN: | FileCheck %s --allow-empty --check-prefix=CACHE-MISS +// +// RUN: FileCheck %s --input-file=%t/deps1.d --check-prefix=DEPS +// DEPS: depends: +// DEPS: main.c +// DEPS: my_header.h +// DEPS-NOT: sys.h + +// RUN: ls %t/output.o && rm %t/output.o +// +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache %t/src/main.c -emit-obj -o %t/output.o -isystem %t/src/sys \ +// RUN: -dependency-file %t/deps2.d -MT depends 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CACHE-HIT +// +// RUN: ls %t/output.o +// RUN: diff -u %t/deps1.d %t/deps2.d +// +// CACHE-HIT: remark: compile job cache hit +// CACHE-MISS-NOT: remark: compile job cache hit + +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache %t/src/main.c -emit-obj -o %t/output.o -isystem %t/src/sys \ +// RUN: -dependency-file %t/deps3.d -MT other1 -MT other2 -MP -fdepfile-entry=extra-depfile.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CACHE-HIT + +// RUN: FileCheck %s --input-file=%t/deps3.d --check-prefix=DEPS_OTHER +// DEPS_OTHER: other1 other2: +// DEPS_OTHER: extra-depfile.json +// DEPS_OTHER: main.c +// DEPS_OTHER: my_header.h +// DEPS_OTHER-NOT: sys.h +// DEPS_OTHER: my_header.h: + +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache %t/src/main.c -emit-obj -o %t/output.o -isystem %t/src/sys \ +// RUN: -sys-header-deps -dependency-file %t/deps4.d -MT depends 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CACHE-MISS + +// Note: currently options that affect the list of deps (like sys-header-deps) +// are part of the cache key, to avoid saving unnecessary paths. + +// RUN: FileCheck %s --input-file=%t/deps4.d --check-prefix=DEPS_SYS +// DEPS_SYS: depends: +// DEPS_SYS: main.c +// DEPS_SYS: my_header.h +// DEPS_SYS: sys.h + +// Using another cas path to avoid reusing artifacts. +// RUN: llvm-cas --cas %t/cas2 --ingest %t/src + +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas2 -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache %t/src/main.c -emit-obj -o %t/output.o -isystem %t/src/sys \ +// RUN: -dependency-file %t/deps-depfile1.d -MT deps -fdepfile-entry=extra-depfile.json -fdepfile-entry=%t/main.c 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CACHE-MISS + +// RUN: FileCheck %s --input-file=%t/deps-depfile1.d --check-prefix=DEPS_DEPFILE1 +// DEPS_DEPFILE1: deps: +// DEPS_DEPFILE1: extra-depfile.json +// DEPS_DEPFILE1: main.c + +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas2 -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache %t/src/main.c -emit-obj -o %t/output.o -isystem %t/src/sys \ +// RUN: -dependency-file %t/deps-depfile2.d -MT deps 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CACHE-HIT + +// RUN: FileCheck %s --input-file=%t/deps-depfile2.d --check-prefix=DEPS_DEPFILE2 +// DEPS_DEPFILE2-NOT: extra-depfile.json +// DEPS_DEPFILE2: main.c + +//--- main.c +#include "my_header.h" +#include + +//--- my_header.h + +//--- sys/sys.h diff --git a/clang/test/CAS/fcache-compile-job-serialized-diagnostics.c b/clang/test/CAS/fcache-compile-job-serialized-diagnostics.c new file mode 100644 index 0000000000000..2d3ece2b6f04b --- /dev/null +++ b/clang/test/CAS/fcache-compile-job-serialized-diagnostics.c @@ -0,0 +1,63 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: llvm-cas --cas %t/cas --ingest %s > %t/casid + +// RUN: %clang -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas \ +// RUN: -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Wimplicit-function-declaration \ +// RUN: -Wno-error=implicit-function-declaration \ +// RUN: -Rcompile-job-cache -emit-obj -o %t/output.o \ +// RUN: -serialize-diagnostic-file %t/diags %s 2>&1 \ +// RUN: | FileCheck %s --allow-empty --check-prefix=CACHE-MISS + +// RUN: c-index-test -read-diagnostics %t/diags 2>&1 | FileCheck %s --check-prefix=SERIALIZED-MISS --check-prefix=SERIALIZED-COMMON + +// RUN: ls %t/output.o && rm %t/output.o + +// RUN: %clang -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas \ +// RUN: -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Wimplicit-function-declaration \ +// RUN: -Wno-error=implicit-function-declaration \ +// RUN: -Rcompile-job-cache -emit-obj -o %t/output.o \ +// RUN: -serialize-diagnostic-file %t/diags %s 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CACHE-HIT + +// RUN: c-index-test -read-diagnostics %t/diags 2>&1 | FileCheck %s --check-prefix=SERIALIZED-HIT --check-prefix=SERIALIZED-COMMON + +// CACHE-HIT: remark: compile job cache hit +// CACHE-HIT: warning: some warning + +// CACHE-MISS: remark: compile job cache miss +// CACHE-MISS: warning: some warning + +// SERIALIZED-HIT: warning: compile job cache hit +// SERIALIZED-MISS: warning: compile job cache miss +// SERIALIZED-COMMON: warning: some warning +// SERIALIZED-COMMON: Number of diagnostics: 2 + +// Make sure warnings are merged with driver ones. +// Using normal compilation as baseline. +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Wl,-none --serialize-diagnostics %t/t1.diag \ +// RUN: 2>&1 | FileCheck %s -check-prefix=WARN +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas CLANG_CACHE_USE_CASFS_DEPSCAN=1 %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Wl,-none --serialize-diagnostics %t/t2.diag \ +// RUN: 2>&1 | FileCheck %s -check-prefix=WARN +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Wl,-none --serialize-diagnostics %t/t2.inc.diag \ +// RUN: 2>&1 | FileCheck %s -check-prefix=WARN +// RUN: diff %t/t1.diag %t/t2.diag +// RUN: diff %t/t1.diag %t/t2.inc.diag + +// Try again with cache hit. +// RUN: rm %t/t2.diag +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas CLANG_CACHE_USE_CASFS_DEPSCAN=1 %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Wl,-none --serialize-diagnostics %t/t2.diag +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Wl,-none --serialize-diagnostics %t/t2.inc.diag \ +// RUN: 2>&1 | FileCheck %s -check-prefix=WARN +// RUN: diff %t/t1.diag %t/t2.diag +// RUN: diff %t/t1.diag %t/t2.inc.diag + +// WARN: warning: -Wl,-none: 'linker' input unused +// WARN: warning: some warning + +#warning some warning diff --git a/clang/test/CAS/fcache-compile-job.c b/clang/test/CAS/fcache-compile-job.c new file mode 100644 index 0000000000000..58c8e41a3b4d8 --- /dev/null +++ b/clang/test/CAS/fcache-compile-job.c @@ -0,0 +1,50 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: llvm-cas --cas %t/cas --ingest %s > %t/casid +// +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache -emit-obj %s -o %t/output.o 2>&1 \ +// RUN: | FileCheck %s --allow-empty --check-prefix=CACHE-MISS +// RUN: ls %t/output.o && rm %t/output.o +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache -emit-obj %s -o %t/output.o 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CACHE-HIT +// RUN: ls %t/output.o && rm %t/output.o +// RUN: cd %t +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache -emit-obj %s -o output.o 2>&1 \ +// RUN: | FileCheck %s --allow-empty --check-prefix=CACHE-HIT +// RUN: ls %t/output.o +// +// Check for a cache hit if the CAS moves: +// RUN: mv %t/cas %t/cas.moved +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas.moved -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache -emit-obj %s -o output.o 2> %t/cache-hit.out +// RUN: FileCheck %s -input-file=%t/cache-hit.out --check-prefix=CACHE-HIT +// RUN: ls %t/output.o + +// RUN: cat %t/cache-hit.out | sed \ +// RUN: -e "s/^.*hit for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key +// RUN: cat %t/cache-hit.out | sed \ +// RUN: -e "s/^.*=> '//" \ +// RUN: -e "s/' .*$//" > %t/cache-result + +// Check for a handling error if the CAS is removed but not action cache. +// First need to ingest the input file so the compile cache can be constructed. +// RUN: llvm-cas --ingest --cas %t/cas.new %s +// Add the 'key => result' association we got earlier. +// RUN: llvm-cas --cas %t/cas.new --put-cache-key @%t/cache-key @%t/cache-result +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas.new -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache -emit-obj %s -o output.o 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CACHE-RESULT +// RUN: ls %t/output.o +// +// CACHE-HIT: remark: compile job cache hit +// CACHE-MISS-NOT: remark: compile job cache hit +// CACHE-RESULT: remark: compile job cache miss +// CACHE-RESULT-SAME: result not found diff --git a/clang/test/CAS/fcas-fs-framework-autolink.c b/clang/test/CAS/fcas-fs-framework-autolink.c new file mode 100644 index 0000000000000..d52134e78b36a --- /dev/null +++ b/clang/test/CAS/fcas-fs-framework-autolink.c @@ -0,0 +1,51 @@ +// Check that when scanning framework modules, changing the framework binary +// does not change the cache key. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_no_fw.json + +// RUN: echo 'build 1' > %t/Foo.framework/Foo + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_fw1.json + +// The existince of the framework is significant, since it affects autolinking. +// RUN: not diff -u %t/deps_fw1.json %t/deps_fw2.json + +// RUN: echo 'build 2' > %t/Foo.framework/Foo + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_fw2.json + +// But the contents of the binary are not. +// RUN: diff -u %t/deps_fw1.json %t/deps_fw2.json + +//--- cdb.json.template +[{ + "directory" : "DIR", + "command" : "clang_tool -fsyntax-only DIR/tu.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache -F DIR", + "file" : "DIR/tu.c" +}] + +//--- Foo.framework/Modules/module.modulemap +framework module Foo { + umbrella header "Foo.h" + export * +} + +//--- Foo.framework/Headers/Foo.h + +//--- tu.c +#include "Foo/Foo.h" diff --git a/clang/test/CAS/fcas-include-tree-prefix-mapping.c b/clang/test/CAS/fcas-include-tree-prefix-mapping.c new file mode 100644 index 0000000000000..088b8af61480d --- /dev/null +++ b/clang/test/CAS/fcas-include-tree-prefix-mapping.c @@ -0,0 +1,190 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t/src +// RUN: mkdir %t/out + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree \ +// RUN: -o %t/t1.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas -Rcompile-job-cache \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \ +// RUN: -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \ +// RUN: -fdepscan-prefix-map=%t/src=/^src -fdepscan-prefix-map=%t/out=/^out \ +// RUN: -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \ +// RUN: -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \ +// RUN: -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb -fdebug-compilation-dir=%t/out \ +// RUN: -emit-llvm %t/src/main.c -o %t/out/output.ll -include %t/src/prefix.h -I %t/src/inc \ +// RUN: -MT deps -dependency-file %t/t1.d +// RUN: %clang @%t/t1.rsp 2> %t/output.txt + +// RUN: cat %t/output.txt | sed \ +// RUN: -e "s/^.*miss for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key + +// RUN: clang-cas-test -print-compile-job-cache-key -cas %t/cas @%t/cache-key > %t/printed-key.txt +// RUN: FileCheck %s -input-file %t/printed-key.txt -DSRC_PREFIX=%t/src -DOUT_PREFIX=%t/out -DSDK_PREFIX=%S/Inputs/SDK -DTOOLCHAIN_PREFIX=%S/Inputs/toolchain_dir + +// CHECK-NOT: [[SRC_PREFIX]] +// CHECK-NOT: [[OUT_PREFIX]] +// CHECK-NOT: [[SDK_PREFIX]] +// CHECK-NOT: [[TOOLCHAIN_PREFIX]] +// CHECK: /^src{{[/\\]}}main.c +// CHECK: /^src{{[/\\]}}inc{{[/\\]}}t.h +// CHECK: /^toolchain{{[/\\]}}lib{{[/\\]}}clang{{[/\\]}}1000{{[/\\]}}include{{[/\\]}}stdarg.h +// CHECK: /^sdk{{[/\\]}}usr{{[/\\]}}include{{[/\\]}}stdlib.h + +// RUN: FileCheck %s -input-file %t/out/output.ll -check-prefix=IR -DSRC_PREFIX=%t/src -DOUT_PREFIX=%t/out -DSDK_PREFIX=%S/Inputs/SDK -DTOOLCHAIN_PREFIX=%S/Inputs/toolchain_dir +// IR-NOT: [[SRC_PREFIX]] +// IR-NOT: [[OUT_PREFIX]] +// IR-NOT: [[SDK_PREFIX]] +// IR-NOT: [[TOOLCHAIN_PREFIX]] + +// Check with prefix header. + +// RUN: split-file %s %t/src2 +// RUN: mkdir %t/out2 + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree \ +// RUN: -o %t/t2.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas -Rcompile-job-cache \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \ +// RUN: -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \ +// RUN: -fdepscan-prefix-map=%t/src2=/^src -fdepscan-prefix-map=%t/out2=/^out \ +// RUN: -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \ +// RUN: -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \ +// RUN: -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb -fdebug-compilation-dir=%t/out2 \ +// RUN: -emit-llvm %t/src2/main.c -o %t/out2/output.ll -include %t/src2/prefix.h -I %t/src2/inc \ +// RUN: -MT deps -dependency-file %t/t2.d +// RUN: %clang @%t/t2.rsp 2> %t/output2.txt + +// RUN: cat %t/output2.txt | sed \ +// RUN: -e "s/^.*hit for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key2 + +// RUN: diff -u %t/cache-key %t/cache-key2 + +// Check dependencies. + +// Baseline for comparison. +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \ +// RUN: -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \ +// RUN: -emit-obj %t/src/main.c -o %t/out/main.o -include %t/src/prefix.h -I %t/src/inc \ +// RUN: -MT deps -dependency-file %t/regular1.d +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \ +// RUN: -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \ +// RUN: -emit-obj %t/src2/main.c -o %t/out2/main.o -include %t/src2/prefix.h -I %t/src2/inc \ +// RUN: -MT deps -dependency-file %t/regular2.d + +// RUN: diff -u %t/regular1.d %t/t1.d +// RUN: diff -u %t/regular2.d %t/t2.d + +// Check with PCH. + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree \ +// RUN: -o %t/pch1.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas -Rcompile-job-cache \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \ +// RUN: -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \ +// RUN: -fdepscan-prefix-map=%t/src=/^src -fdepscan-prefix-map=%t/out=/^out \ +// RUN: -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \ +// RUN: -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \ +// RUN: -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb -fdebug-compilation-dir=%t/out \ +// RUN: -emit-pch -x c-header %t/src/prefix.h -o %t/out/prefix.h.pch -include %t/src/prefix.h -I %t/src/inc +// RUN: %clang @%t/pch1.rsp + +// With different cas path to avoid cache hit. +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree \ +// RUN: -o %t/pch2.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas2 -Rcompile-job-cache \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \ +// RUN: -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \ +// RUN: -fdepscan-prefix-map=%t/src2=/^src -fdepscan-prefix-map=%t/out2=/^out \ +// RUN: -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \ +// RUN: -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \ +// RUN: -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb -fdebug-compilation-dir=%t/out2 \ +// RUN: -emit-pch -x c-header %t/src2/prefix.h -o %t/out2/prefix.h.pch -include %t/src2/prefix.h -I %t/src2/inc +// RUN: %clang @%t/pch2.rsp + +// RUN: diff %t/out/prefix.h.pch %t/out2/prefix.h.pch + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree \ +// RUN: -o %t/t3.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas -Rcompile-job-cache \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \ +// RUN: -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \ +// RUN: -fdepscan-prefix-map=%t/src=/^src -fdepscan-prefix-map=%t/out=/^out \ +// RUN: -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \ +// RUN: -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \ +// RUN: -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb -fdebug-compilation-dir=%t/out \ +// RUN: -emit-obj %t/src/main.c -o %t/out/main.o -include-pch %t/out/prefix.h.pch -I %t/src/inc \ +// RUN: -MT deps -dependency-file %t/t1.pch.d +// RUN: %clang @%t/t3.rsp 2> %t/output3.txt + +// RUN: cat %t/output3.txt | sed \ +// RUN: -e "s/^.*miss for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key3 + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree \ +// RUN: -o %t/t4.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas -Rcompile-job-cache \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \ +// RUN: -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \ +// RUN: -fdepscan-prefix-map=%t/src2=/^src -fdepscan-prefix-map=%t/out2=/^out \ +// RUN: -fdepscan-prefix-map=%S/Inputs/toolchain_dir=/^toolchain \ +// RUN: -fdepscan-prefix-map=%S/Inputs/SDK=/^sdk \ +// RUN: -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb -fdebug-compilation-dir=%t/out2 \ +// RUN: -emit-obj %t/src2/main.c -o %t/out2/main.o -include-pch %t/out2/prefix.h.pch -I %t/src2/inc \ +// RUN: -MT deps -dependency-file %t/t2.pch.d +// RUN: %clang @%t/t4.rsp 2> %t/output4.txt + +// RUN: cat %t/output4.txt | sed \ +// RUN: -e "s/^.*hit for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key4 + +// RUN: diff -u %t/cache-key3 %t/cache-key4 +// RUN: diff %t/out/main.o %t/out2/main.o + +// Check dependencies. + +// Baseline for comparison. +// RUN: %clang_cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas -Rcompile-job-cache \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \ +// RUN: -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \ +// RUN: -emit-pch -x c-header %t/src/prefix.h -o %t/out/reg-prefix.h.pch -include %t/src/prefix.h -I %t/src/inc +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \ +// RUN: -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \ +// RUN: -emit-obj %t/src/main.c -o %t/out/main.o -include-pch %t/out/reg-prefix.h.pch -I %t/src/inc \ +// RUN: -MT deps -dependency-file %t/regular1.pch.d +// RUN: %clang_cc1 -triple x86_64-apple-macos11 -fcas-path %t/cas -Rcompile-job-cache \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \ +// RUN: -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \ +// RUN: -emit-pch -x c-header %t/src2/prefix.h -o %t/out2/reg-prefix.h.pch -include %t/src2/prefix.h -I %t/src2/inc +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -resource-dir %S/Inputs/toolchain_dir/lib/clang/1000 -internal-isystem %S/Inputs/toolchain_dir/lib/clang/1000/include \ +// RUN: -isysroot %S/Inputs/SDK -internal-externc-isystem %S/Inputs/SDK/usr/include \ +// RUN: -emit-obj %t/src2/main.c -o %t/out2/main.o -include-pch %t/out2/reg-prefix.h.pch -I %t/src2/inc \ +// RUN: -MT deps -dependency-file %t/regular2.pch.d + +// RUN: diff -u %t/regular1.pch.d %t/t1.pch.d +// RUN: diff -u %t/regular2.pch.d %t/t2.pch.d + +//--- main.c +#include "t.h" +#include +#include + +int test(void) { + return SOME_VALUE; +} + +//--- inc/t.h + +//--- prefix.h +#define SOME_VALUE 3 +#include "pt.h" + +//--- inc/pt.h +#include "../inc2/pt2.h" + +//--- inc2/pt2.h diff --git a/clang/test/CAS/fcas-include-tree-with-pch.c b/clang/test/CAS/fcas-include-tree-with-pch.c new file mode 100644 index 0000000000000..234a18baf3ab0 --- /dev/null +++ b/clang/test/CAS/fcas-include-tree-with-pch.c @@ -0,0 +1,84 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: ln -s s2.h %t/s2-link.h +// RUN: ln -s s3.h %t/s3-link1.h +// RUN: ln -s s3.h %t/s3-link2.h + +// Normal compilation for baseline. +// RUN: %clang_cc1 -x c-header %t/prefix.h -I %t/inc -DCMD_MACRO=1 -emit-pch -o %t/prefix1.pch +// RUN: %clang_cc1 %t/t1.c -include-pch %t/prefix1.pch -emit-llvm -o %t/source.ll -I %t/inc -DCMD_MACRO=1 + +// RUN: %clang -cc1depscan -o %t/pch.rsp -fdepscan=inline -fdepscan-include-tree -cc1-args \ +// RUN: -cc1 -emit-pch -x c-header %t/prefix.h -I %t/inc -DCMD_MACRO=1 -fcas-path %t/cas +// RUN: %clang @%t/pch.rsp -o %t/prefix2.pch + +// RUN: %clang -cc1depscan -o %t/tu.rsp -fdepscan=inline -fdepscan-include-tree -cc1-args \ +// RUN: -cc1 -emit-llvm %t/t1.c -include-pch %t/prefix2.pch -I %t/inc -DCMD_MACRO=1 -fcas-path %t/cas +// RUN: rm %t/prefix2.pch + +// RUN: %clang @%t/tu.rsp -o %t/tree.ll +// RUN: diff -u %t/source.ll %t/tree.ll + +// Check again with relative paths. +// RUN: cd %t + +// Normal compilation for baseline. +// RUN: %clang_cc1 -x c-header prefix.h -I %t/inc -DCMD_MACRO=1 -emit-pch -o prefix3.pch +// RUN: %clang_cc1 t1.c -include-pch prefix3.pch -emit-llvm -o source-rel.ll -I inc -DCMD_MACRO=1 + +// RUN: %clang -cc1depscan -o pch2.rsp -fdepscan=inline -fdepscan-include-tree -cc1-args \ +// RUN: -cc1 -emit-pch -x c-header prefix.h -I %t/inc -DCMD_MACRO=1 -fcas-path %t/cas +// RUN: %clang @pch2.rsp -o prefix4.pch + +// RUN: %clang -cc1depscan -o tu2.rsp -fdepscan=inline -fdepscan-include-tree -cc1-args \ +// RUN: -cc1 -emit-llvm t1.c -include-pch prefix4.pch -I inc -DCMD_MACRO=1 -fcas-path %t/cas +// RUN: rm %t/prefix4.pch + +// RUN: %clang @tu2.rsp -o tree-rel.ll +// RUN: diff -u source-rel.ll tree-rel.ll + +// Check that -coverage-notes-file and -coverage-data-file are stripped +// RUN: %clang -cc1depscan -o pch3.rsp -fdepscan=inline -fdepscan-include-tree -cc1-args \ +// RUN: -cc1 -emit-pch -x c-header prefix.h -I %t/inc -DCMD_MACRO=1 -fcas-path %t/cas \ +// RUN: -coverage-notes-file=%t/pch.gcno -coverage-data-file=%t/pch.gcda +// RUN: FileCheck %s -check-prefix=COVERAGE -input-file %t/pch3.rsp +// COVERAGE-NOT: -coverage-data-file +// COVERAGE-NOT: -coverage-notes-file + +//--- t1.c +#if S2_MACRO +#include "s2-link.h" +#endif +#include "s3-link2.h" +#include "other.h" + +int test(struct S *s, struct S2 *s2) { + return s->x + s2->y + CMD_MACRO + PREFIX_MACRO + S2_MACRO + S3_MACRO; +} + +//--- prefix.h +#include "s2.h" +#include "s3.h" +#include "s3-link1.h" +#include "other.h" + +#define PREFIX_MACRO S3_MACRO + +struct S { + int x; +}; + +//--- s2.h +#pragma once +#define S2_MACRO 3 +struct S2 { + int y; +}; + +//--- s3.h +#define S3_MACRO 4 + +//--- inc/other.h +#include "../inc2/other2.h" + +//--- inc2/other2.h diff --git a/clang/test/CAS/fcas-include-tree.c b/clang/test/CAS/fcas-include-tree.c new file mode 100644 index 0000000000000..ad95e4ae13b24 --- /dev/null +++ b/clang/test/CAS/fcas-include-tree.c @@ -0,0 +1,69 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 %t/t1.c -E -P -o %t/source.i -isystem %t -DCMD_MACRO=1 -Werror +// RUN: %clang_cc1 %t/t1.c -emit-llvm -o %t/source.ll -isystem %t -DCMD_MACRO=1 -Werror -dependency-file %t/t1-source.d -MT deps + +// RUN: %clang -cc1depscan -o %t/inline.rsp -fdepscan=inline -fdepscan-include-tree -cc1-args \ +// RUN: -cc1 -E -P %t/t1.c -isystem %t -DCMD_MACRO=1 -fcas-path %t/cas -Werror +// RUN: %clang @%t/inline.rsp -o %t/tree.i +// RUN: diff -u %t/source.i %t/tree.i +// RUN: %clang -cc1depscan -o %t/inline2.rsp -fdepscan=inline -fdepscan-include-tree -cc1-args \ +// RUN: -cc1 -emit-llvm %t/t1.c -isystem %t -DCMD_MACRO=1 -fcas-path %t/cas -Werror -dependency-file %t/t1-tree.d -MT deps +// RUN: %clang @%t/inline2.rsp -o %t/tree.ll +// RUN: diff -u %t/source.ll %t/tree.ll +// RUN: diff -u %t/t1-source.d %t/t1-tree.d + +//--- t1.c +#include "top.h" +#include "n1.h" +#include + +#define N2H +#include N2H + +int test(struct S *s) { + return s->x + gv + gv2 + SOMEVAL + SOMEVAL2 + CMD_MACRO; +} + +//--- top.h +#ifndef _TOP_H_ +#define _TOP_H_ + +#if __has_include("n1.h") +#define SOMEVAL 1 +#endif + +#if __has_include("nonexistent.h") +#define SOMEVAL 7 +#else +#define SOMEVAL2 2 +#endif + +#include "n1.h" + +struct S { + int x; +}; + +#endif + +//--- sys.h +#define SOMECHECK defined(SOMEDEF) +// This triggers warning: macro expansion producing 'defined' has undefined behavior [-Wexpansion-to-defined] +#if SOMECHECK +#endif + +//--- n1.h +#ifndef _N1_H_ +#define _N1_H_ + +#pragma once +#pragma clang system_header + +int gv; + +#endif + +//--- n2.h +int gv2; diff --git a/clang/test/CAS/fdepscan-daemon.c b/clang/test/CAS/fdepscan-daemon.c new file mode 100644 index 0000000000000..54ed8b621c19f --- /dev/null +++ b/clang/test/CAS/fdepscan-daemon.c @@ -0,0 +1,13 @@ +// Test running -fdepscan. +// +// REQUIRES: system-darwin, clang-cc1daemon + +// RUN: rm -rf %t +// RUN: %clang -cc1depscand -execute %{clang-daemon-dir}/%basename_t -cas-args -fcas-path %t/cas -- \ +// RUN: %clang -target x86_64-apple-macos11 -I %S/Inputs \ +// RUN: -Xclang -fcas-path -Xclang %t/cas \ +// RUN: -fdepscan=daemon -fdepscan-daemon=%{clang-daemon-dir}/%basename_t -fsyntax-only -x c %s + +#include "test.h" + +int func(void); diff --git a/clang/test/CAS/fdepscan-driver.c b/clang/test/CAS/fdepscan-driver.c new file mode 100644 index 0000000000000..12e13b6743a3b --- /dev/null +++ b/clang/test/CAS/fdepscan-driver.c @@ -0,0 +1,30 @@ +// RUN: %clang -target x86_64-apple-macos11 -fdepscan=daemon -c \ +// RUN: -x c %s -ccc-print-phases 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-PHASES + +// CHECK-PHASES: 0: input +// CHECK-PHASES: 1: depscan, {0}, response-file +// CHECK-PHASES: 2: preprocessor, {1}, cpp-output +// CHECK-PHASES: 3: compiler, {2}, ir +// CHECK-PHASES: 4: backend, {3}, assembler +// CHECK-PHASES: 5: assembler, {4}, object +// CHECK-PHASES: 6: bind-arch, "x86_64", {5}, object + +// RUN: %clang -target x86_64-apple-macos11 -fdepscan=daemon -c \ +// RUN: -x c %s -ccc-print-bindings 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-BINDINGS + +// CHECK-BINDINGS: # "x86_64-apple-macos11" - "clang" +// CHECK-BINDINGS: # "x86_64-apple-macos11" - "clang" + + +// RUN: %clang -target x86_64-apple-macos11 -fdepscan=daemon -c \ +// RUN: -x c %s -### 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK-CMD + +// CHECK-CMD: "-cc1depscan" "-fdepscan=daemon" "-o" +// CHECK-CMD: "-cc1" "@ + +// RUN: %clang -target x86_64-apple-macos11 -fdepscan=daemon -c \ +// RUN: -x c %s -save-temps -### + diff --git a/clang/test/CAS/fdepscan.c b/clang/test/CAS/fdepscan.c new file mode 100644 index 0000000000000..674baf3d9d81f --- /dev/null +++ b/clang/test/CAS/fdepscan.c @@ -0,0 +1,45 @@ +// Test running -fdepscan. +// +// REQUIRES: system-darwin, clang-cc1daemon + +// RUN: rm -rf %t-*.d %t.cas +// RUN: %clang -cc1depscand -execute %{clang-daemon-dir}/%basename_t -cas-args -fcas-path %t.cas -- \ +// RUN: %clang -target x86_64-apple-macos11 -I %S/Inputs -fdepscan=daemon -fdepscan-daemon=%{clang-daemon-dir}/%basename_t \ +// RUN: -E -MD -MF %t-daemon.d -x c %s -Xclang -fcas-path -Xclang %t.cas >/dev/null +// RUN: %clang -target x86_64-apple-macos11 -I %S/Inputs -fdepscan=inline \ +// RUN: -E -MD -MF %t-inline.d -x c %s -Xclang -fcas-path -Xclang %t.cas >/dev/null +// RUN: %clang -target x86_64-apple-macos11 -I %S/Inputs -fdepscan=auto \ +// RUN: -E -MD -MF %t-auto.d -x c %s -Xclang -fcas-path -Xclang %t.cas >/dev/null +// RUN: %clang -target x86_64-apple-macos11 -I %S/Inputs -fdepscan=off \ +// RUN: -E -MD -MF %t-off.d -x c %s -Xclang -fcas-path -Xclang %t.cas >/dev/null +// +// Check -fdepscan-share-related arguments are claimed. +// TODO: Check behaviour. +// +// RUN: %clang -target x86_64-apple-macos11 -I %S/Inputs -fdepscan=off \ +// RUN: -fdepscan-share-parent \ +// RUN: -fdepscan-share-parent= \ +// RUN: -fdepscan-share-parent=python \ +// RUN: -fdepscan-share=python \ +// RUN: -fdepscan-share= \ +// RUN: -fdepscan-share-stop=python \ +// RUN: -fdepscan-share-identifier \ +// RUN: -fno-depscan-share \ +// RUN: -fsyntax-only -x c %s \ +// RUN: -Xclang -fcas-path -Xclang %t.cas \ +// RUN: | FileCheck %s -allow-empty +// CHECK-NOT: warning: +// +// RUN: not %clang -target x86_64-apple-macos11 -I %S/Inputs \ +// RUN: -fdepscan-share-parents 2>&1 \ +// RUN: | FileCheck %s -check-prefix=BAD-SPELLING +// BAD-SPELLING: error: unknown argument '-fdepscan-share-parents' +// +// Check that the dependency files match. +// RUN: diff %t-off.d %t-daemon.d +// RUN: diff %t-off.d %t-inline.d +// RUN: diff %t-off.d %t-auto.d + +#include "test.h" + +int func(void); diff --git a/clang/test/CAS/fmodule-file-cache-key-errors.c b/clang/test/CAS/fmodule-file-cache-key-errors.c new file mode 100644 index 0000000000000..bda2fbe4c657b --- /dev/null +++ b/clang/test/CAS/fmodule-file-cache-key-errors.c @@ -0,0 +1,106 @@ +// Checks error conditions related to -fmodule-file-cache-key, e.g. missing +// cache entry, invalid id, etc. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t %t.cas %t.cas_2 +// RUN: split-file %s %t + +// RUN: llvm-cas --cas %t.cas --ingest %t > %t/casid + +// RUN: not %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: -fmodule-file-cache-key=INVALID \ +// RUN: -fsyntax-only %t/tu.c \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/invalid.txt +// RUN: cat %t/invalid.txt | FileCheck %s -check-prefix=INVALID + +// INVALID: error: unknown argument: '-fmodule-file-cache-key=INVALID' + +// RUN: not %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: -fmodule-file-cache-key INVALID \ +// RUN: -fsyntax-only %t/tu.c \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/invalid2.txt +// RUN: FileCheck %s -check-prefix=INVALID2 -input-file=%t/invalid2.txt + +// INVALID2: error: CAS cannot load module with key '-fsyntax-only' from -fmodule-file-cache-key + +// RUN: not %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: -fmodule-file-cache-key INVALID ALSO_INVALID MORE_INVALID \ +// RUN: -fsyntax-only %t/tu.c \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/invalid3.txt +// RUN: FileCheck %s -check-prefix=INVALID3 -input-file=%t/invalid3.txt + +// INVALID3: error: error reading 'MORE_INVALID' + +// RUN: not %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: -fmodule-file-cache-key PATH KEY \ +// RUN: -fsyntax-only %t/tu.c \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/bad_key.txt +// RUN: cat %t/bad_key.txt | FileCheck %s -check-prefix=BAD_KEY + +// BAD_KEY: error: CAS cannot load module with key 'KEY' from -fmodule-file-cache-key: invalid cas-id 'KEY' + +// RUN: echo -n '-fmodule-file-cache-key PATH ' > %t/bad_key2.rsp +// RUN: cat %t/casid >> %t/bad_key2.rsp + +// RUN: not %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: @%t/bad_key2.rsp \ +// RUN: -fsyntax-only %t/tu.c \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/bad_key2.txt +// RUN: cat %t/bad_key2.txt | FileCheck %s -check-prefix=BAD_KEY2 + +// BAD_KEY2: error: CAS cannot load module with key '{{.*}}' from -fmodule-file-cache-key: cas object is not a valid cache key + +// == Build A + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=A -fno-implicit-modules \ +// RUN: -emit-module %t/module.modulemap -o %t/A.pcm \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/A.out.txt +// RUN: cat %t/A.out.txt | FileCheck %s --check-prefix=CACHE-MISS +// CACHE-MISS: remark: compile job cache miss +// RUN: cat %t/A.out.txt | sed -E "s:^.*cache [a-z]+ for '([^']+)'.*$:\1:" > %t/A.key + +// == Try to import A with an empty action cache, simulating a missing module + +// RUN: llvm-cas --cas %t.cas_2 --import --upstream-cas %t.cas @%t/A.key + +// RUN: echo -n '-fmodule-file-cache-key PATH ' > %t/not_in_cache.rsp +// RUN: cat %t/A.key >> %t/not_in_cache.rsp + +// RUN: not %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: @%t/not_in_cache.rsp \ +// RUN: -fsyntax-only %t/tu.c \ +// RUN: -fcas-path %t.cas_2 -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/not_in_cache.txt +// RUN: cat %t/not_in_cache.txt | FileCheck %s -check-prefix=NOT_IN_CACHE -DPREFIX=%/t + +// NOT_IN_CACHE: error: CAS cannot load module with key '{{.*}}' from -fmodule-file-cache-key: no such entry in action cache; expected compile: +// NOT_IN_CACHE: command-line: +// NOT_IN_CACHE: -cc1 +// NOT_IN_CACHE: filesystem: +// NOT_IN_CACHE: file llvmcas://{{.*}} [[PREFIX]]/A.h + +//--- module.modulemap +module A { header "A.h" } + +//--- A.h +void A(void); + +//--- tu.c +#include "A.h" +void tu(void) { + A(); +} diff --git a/clang/test/CAS/fmodule-file-cache-key-lazy.c b/clang/test/CAS/fmodule-file-cache-key-lazy.c new file mode 100644 index 0000000000000..12132d84d6bf0 --- /dev/null +++ b/clang/test/CAS/fmodule-file-cache-key-lazy.c @@ -0,0 +1,77 @@ +// Tests for combining -fmodule-file-cache-key with lazy-loading modules via +// -fmodule-file==. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t %t.cas +// RUN: split-file %s %t + +// RUN: llvm-cas --cas %t.cas --ingest %t > %t/casid + +// == Build B + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=B -fno-implicit-modules \ +// RUN: -emit-module %t/module.modulemap -o %t/B.pcm \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/B.out.txt +// RUN: cat %t/B.out.txt | FileCheck %s --check-prefix=CACHE-MISS +// RUN: cat %t/B.out.txt | sed -E "s:^.*cache [a-z]+ for '([^']+)'.*$:\1:" > %t/B.key + +// == Build A, importing B + +// RUN: echo -n '-fmodule-file-cache-key %t/B.pcm ' > %t/B.import.rsp +// RUN: cat %t/B.key >> %t/B.import.rsp + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=A -fno-implicit-modules \ +// RUN: @%t/B.import.rsp -fmodule-file=B=%t/B.pcm \ +// RUN: -emit-module %t/module.modulemap -o %t/A.pcm \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/A.out.txt +// RUN: cat %t/A.out.txt | FileCheck %s --check-prefix=CACHE-MISS +// RUN: cat %t/A.out.txt | sed -E "s:^.*cache [a-z]+ for '([^']+)'.*$:\1:" > %t/A.key + +// == Build tu, importing A (implicitly importing B) + +// RUN: echo -n '-fmodule-file-cache-key %t/A.pcm ' > %t/A.import.rsp +// RUN: cat %t/A.key >> %t/A.import.rsp + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: @%t/A.import.rsp -fmodule-file=A=%t/A.pcm \ +// RUN: -fsyntax-only %t/tu.c \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/tu.out.txt +// RUN: cat %t/tu.out.txt | FileCheck %s --check-prefix=CACHE-MISS + +// == Ensure we're reading pcm from cache + +// RUN: rm %t/*.pcm + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: @%t/A.import.rsp -fmodule-file=A=%t/A.pcm \ +// RUN: -fsyntax-only %t/tu.c \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/tu.out.2.txt +// RUN: cat %t/tu.out.2.txt | FileCheck %s --check-prefix=CACHE-HIT + +// CACHE-HIT: remark: compile job cache hit +// CACHE-MISS: remark: compile job cache miss + +//--- module.modulemap +module A { header "A.h" export * } +module B { header "B.h" } + +//--- A.h +#include "B.h" + +//--- B.h +void B(void); + +//--- tu.c +#include "A.h" +void tu(void) { + B(); +} diff --git a/clang/test/CAS/fmodule-file-cache-key-with-pch.c b/clang/test/CAS/fmodule-file-cache-key-with-pch.c new file mode 100644 index 0000000000000..f1ef288a46cbf --- /dev/null +++ b/clang/test/CAS/fmodule-file-cache-key-with-pch.c @@ -0,0 +1,117 @@ +// Check that -fmodule-file-cache-key works with mixed PCH+modules builds. +// This test mimics the way the dep scanner handles PCH (ie. treat it as a file +// input, ingested into the cas fs). + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t %t.cas +// RUN: split-file %s %t + +// RUN: llvm-cas --cas %t.cas --ingest %t > %t/casid + +// == Build B + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=B -fno-implicit-modules \ +// RUN: -fmodule-related-to-pch \ +// RUN: -emit-module %t/module.modulemap -o %t/B.pcm \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/B.out.txt +// RUN: cat %t/B.out.txt | FileCheck %s --check-prefix=CACHE-MISS +// RUN: cat %t/B.out.txt | sed -E "s:^.*cache [a-z]+ for '([^']+)'.*$:\1:" > %t/B.key + +// == Build A, importing B + +// RUN: echo -n '-fmodule-file-cache-key %t/B.pcm ' > %t/B.import.rsp +// RUN: cat %t/B.key >> %t/B.import.rsp + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=A -fno-implicit-modules \ +// RUN: -fmodule-related-to-pch \ +// RUN: @%t/B.import.rsp -fmodule-file=%t/B.pcm \ +// RUN: -emit-module %t/module.modulemap -o %t/A.pcm \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/A.out.txt +// RUN: cat %t/A.out.txt | FileCheck %s --check-prefix=CACHE-MISS +// RUN: cat %t/A.out.txt | sed -E "s:^.*cache [a-z]+ for '([^']+)'.*$:\1:" > %t/A.key + +// == Build C, importing B + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=C -fno-implicit-modules \ +// RUN: -fmodule-related-to-pch \ +// RUN: @%t/B.import.rsp -fmodule-file=%t/B.pcm \ +// RUN: -emit-module %t/module.modulemap -o %t/C.pcm \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/C.out.txt +// RUN: cat %t/C.out.txt | FileCheck %s --check-prefix=CACHE-MISS +// RUN: cat %t/C.out.txt | sed -E "s:^.*cache [a-z]+ for '([^']+)'.*$:\1:" > %t/C.key + +// == Build PCH, importing A (implicitly importing B) + +// RUN: echo -n '-fmodule-file-cache-key %t/A.pcm ' > %t/A.import.rsp +// RUN: cat %t/A.key >> %t/A.import.rsp + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: @%t/A.import.rsp -fmodule-file=%t/A.pcm \ +// RUN: -emit-pch -x c-header %t/prefix.h -o %t/prefix.pch \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/prefix.out.txt +// RUN: cat %t/prefix.out.txt | FileCheck %s --check-prefix=CACHE-MISS + +// == Clear pcms to ensure they load from cache, and re-ingest with pch + +// RUN: rm %t/*.pcm +// RUN: llvm-cas --cas %t.cas --ingest %t > %t/casid +// RUN: rm %t/*.pch + +// == Build tu + +// RUN: echo -n '-fmodule-file-cache-key %t/C.pcm ' > %t/C.import.rsp +// RUN: cat %t/C.key >> %t/C.import.rsp + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: @%t/C.import.rsp -fmodule-file=%t/C.pcm -include-pch %t/prefix.pch \ +// RUN: -fsyntax-only %t/tu.c \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/tu.out.txt +// RUN: cat %t/tu.out.txt | FileCheck %s --check-prefix=CACHE-MISS + +// == Ensure we're reading pcm from cache + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: @%t/C.import.rsp -fmodule-file=%t/C.pcm -include-pch %t/prefix.pch \ +// RUN: -fsyntax-only %t/tu.c \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/tu.out.2.txt +// RUN: cat %t/tu.out.2.txt | FileCheck %s --check-prefix=CACHE-HIT + +// CACHE-HIT: remark: compile job cache hit +// CACHE-MISS: remark: compile job cache miss + +//--- module.modulemap +module A { header "A.h" export * } +module B { header "B.h" } +module C { header "C.h" export * } + +//--- A.h +#include "B.h" + +//--- B.h +void B(void); + +//--- C.h +#include "B.h" +void B(void); + +//--- prefix.h +#include "A.h" + +//--- tu.c +#include "C.h" +void tu(void) { + B(); +} diff --git a/clang/test/CAS/fmodule-file-cache-key.c b/clang/test/CAS/fmodule-file-cache-key.c new file mode 100644 index 0000000000000..b44b40184ee00 --- /dev/null +++ b/clang/test/CAS/fmodule-file-cache-key.c @@ -0,0 +1,77 @@ +// Tests for providing the contents of pcm files via -fmodule-file-cache-key and +// previously cached module compilations. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t %t.cas +// RUN: split-file %s %t + +// RUN: llvm-cas --cas %t.cas --ingest %t > %t/casid + +// == Build B + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=B -fno-implicit-modules \ +// RUN: -emit-module %t/module.modulemap -o %t/B.pcm \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/B.out.txt +// RUN: cat %t/B.out.txt | FileCheck %s --check-prefix=CACHE-MISS +// RUN: cat %t/B.out.txt | sed -E "s:^.*cache [a-z]+ for '([^']+)'.*$:\1:" > %t/B.key + +// == Build A, importing B + +// RUN: echo -n '-fmodule-file-cache-key %t/B.pcm ' > %t/B.import.rsp +// RUN: cat %t/B.key >> %t/B.import.rsp + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=A -fno-implicit-modules \ +// RUN: @%t/B.import.rsp -fmodule-file=%t/B.pcm \ +// RUN: -emit-module %t/module.modulemap -o %t/A.pcm \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/A.out.txt +// RUN: cat %t/A.out.txt | FileCheck %s --check-prefix=CACHE-MISS +// RUN: cat %t/A.out.txt | sed -E "s:^.*cache [a-z]+ for '([^']+)'.*$:\1:" > %t/A.key + +// == Build tu, importing A (implicitly importing B) + +// RUN: echo -n '-fmodule-file-cache-key %t/A.pcm ' > %t/A.import.rsp +// RUN: cat %t/A.key >> %t/A.import.rsp + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: @%t/A.import.rsp -fmodule-file=%t/A.pcm\ +// RUN: -fsyntax-only %t/tu.c \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/tu.out.txt +// RUN: cat %t/tu.out.txt | FileCheck %s --check-prefix=CACHE-MISS + +// == Ensure we're reading pcm from cache + +// RUN: rm %t/*.pcm + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fno-implicit-modules \ +// RUN: @%t/A.import.rsp -fmodule-file=%t/A.pcm\ +// RUN: -fsyntax-only %t/tu.c \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/tu.out.2.txt +// RUN: cat %t/tu.out.2.txt | FileCheck %s --check-prefix=CACHE-HIT + +// CACHE-HIT: remark: compile job cache hit +// CACHE-MISS: remark: compile job cache miss + +//--- module.modulemap +module A { header "A.h" export * } +module B { header "B.h" } + +//--- A.h +#include "B.h" + +//--- B.h +void B(void); + +//--- tu.c +#include "A.h" +void tu(void) { + B(); +} diff --git a/clang/test/CAS/include-tree-with-include-next.c b/clang/test/CAS/include-tree-with-include-next.c new file mode 100644 index 0000000000000..333571302e20f --- /dev/null +++ b/clang/test/CAS/include-tree-with-include-next.c @@ -0,0 +1,17 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +// Normal compilation for baseline. +// RUN: %clang_cc1 %t/t.c -I %t/inc -I %t/inc2 -fsyntax-only -Werror + +// RUN: %clang -cc1depscan -o %t/tu.rsp -fdepscan=inline -fdepscan-include-tree -cc1-args \ +// RUN: -cc1 -fsyntax-only -Werror -fcas-path %t/cas %t/t.c -I %t/inc -I %t/inc2 -Werror +// RUN: %clang @%t/tu.rsp + +//--- t.c +#include "t.h" + +//--- inc/t.h +#include_next "t.h" + +//--- inc2/t.h diff --git a/clang/test/CAS/include-tree-with-sanitizer.c b/clang/test/CAS/include-tree-with-sanitizer.c new file mode 100644 index 0000000000000..179d09d989995 --- /dev/null +++ b/clang/test/CAS/include-tree-with-sanitizer.c @@ -0,0 +1,35 @@ +// RUN: rm -rf %t && mkdir %t +// RUN: touch %t/asan_ignorelist.txt +// RUN: touch %t/sys_asan_ignorelist.txt + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -fsanitize=address -Xclang -fsanitize-ignorelist=%t/asan_ignorelist.txt -Xclang -fsanitize-system-ignorelist=%t/sys_asan_ignorelist.txt \ +// RUN: -target x86_64-apple-macos11 -c %s -o %t/output.o -Rcompile-job-cache 2> %t/output-tree.txt + +// RUN: cat %t/output-tree.txt | sed \ +// RUN: -e "s/^.*miss for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key-tree + +// RUN: clang-cas-test -print-compile-job-cache-key -cas %t/cas @%t/cache-key-tree > %t/key.txt +// RUN: FileCheck %s -DSRC_FILE=%s -DOUT_DIR=%t -input-file %t/key.txt +// +// CHECK: -fsanitize=address \ +// CHECK: -fsanitize-ignorelist=[[OUT_DIR]]{{/|\\}}asan_ignorelist.txt \ +// CHECK: -fsanitize-ignorelist=[[OUT_DIR]]{{/|\\}}sys_asan_ignorelist.txt \ + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas LLVM_CACHE_PREFIX_MAPS="%S=/^src;%t=/^out" %clang-cache \ +// RUN: %clang -fsanitize=address -Xclang -fsanitize-ignorelist=%t/asan_ignorelist.txt -Xclang -fsanitize-system-ignorelist=%t/sys_asan_ignorelist.txt \ +// RUN: -target x86_64-apple-macos11 -c %s -o %t/output.o -Rcompile-job-cache 2> %t/output-tree2.txt + +// RUN: cat %t/output-tree2.txt | sed \ +// RUN: -e "s/^.*miss for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key-tree2 + +// RUN: clang-cas-test -print-compile-job-cache-key -cas %t/cas @%t/cache-key-tree2 > %t/key2.txt +// RUN: FileCheck %s -input-file %t/key2.txt -check-prefix=PREFIXED +// +// PREFIXED: -fsanitize=address \ +// PREFIXED: -fsanitize-ignorelist=/^out{{/|\\}}asan_ignorelist.txt \ +// PREFIXED: -fsanitize-ignorelist=/^out{{/|\\}}sys_asan_ignorelist.txt \ + +void test() {} diff --git a/clang/test/CAS/indexing.c b/clang/test/CAS/indexing.c new file mode 100644 index 0000000000000..2e74a521b1cb2 --- /dev/null +++ b/clang/test/CAS/indexing.c @@ -0,0 +1,19 @@ +// Test that emitted indexing data are allowed to "escape" the CAS sandbox without indexing options affecting the CAS key +// (essentially indexing data are produced when the compilation is executed but they are not replayed if the compilation is cached) + +// RUN: rm -rf %t && mkdir %t + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas -emit-obj %s -o %t/t.o + +// RUN: %clang @%t/t.rsp -Rcompile-job-cache -index-store-path %t/idx -index-unit-output-path t.o 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CACHE-MISS +// RUN: find %t/idx/*/records | count 1 + +// RUN: rm -rf %t/idx && mkdir %t/idx +// RUN: %clang @%t/t.rsp -Rcompile-job-cache 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CACHE-HIT +// RUN: ls %t/idx | count 0 + +// CACHE-MISS: remark: compile job cache miss +// CACHE-HIT: remark: compile job cache hit diff --git a/clang/test/CAS/libclang-prune-data.c b/clang/test/CAS/libclang-prune-data.c new file mode 100644 index 0000000000000..1a2fad431393a --- /dev/null +++ b/clang/test/CAS/libclang-prune-data.c @@ -0,0 +1,63 @@ +// REQUIRES: ondisk_cas + +// Tests that the CAS directory storage can be limited via libclang APIs. +// The test depends on internal details of the CAS directory structure. + +// RUN: rm -rf %t && mkdir -p %t + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas -emit-obj %s -o %t/output.o +// RUN: %clang @%t/t.rsp +// RUN: ls %t/cas | wc -l | grep 2 +// RUN: ls %t/cas | grep v1.1 + +// Limit too high, no change. +// RUN: c-index-test core -prune-cas -cas-path %t/cas 100000000 +// RUN: ls %t/cas | wc -l | grep 2 + +// Under the limit, starts a chain. +// RUN: c-index-test core -prune-cas -cas-path %t/cas 10 +// RUN: ls %t/cas | wc -l | grep 3 +// RUN: ls %t/cas | grep v1.2 + +// Under the limit, starts a chain and abandons oldest dir. +// RUN: c-index-test core -prune-cas -cas-path %t/cas 10 +// RUN: ls %t/cas | wc -l | grep 4 +// RUN: ls %t/cas | grep v1.3 + +// Under the limit, removes abandonded dir, starts a chain and abandons oldest dir. +// RUN: c-index-test core -prune-cas -cas-path %t/cas 10 +// RUN: ls %t/cas | wc -l | grep 4 +// RUN: ls %t/cas | grep v1.4 +// RUN: ls %t/cas | grep -v v1.1 + +// Same test but using the plugin CAS. + +// RUN: rm -rf %t/cas + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/t.rsp -cc1-args \ +// RUN: -cc1 -triple x86_64-apple-macos12 -fcas-path %t/cas -emit-obj %s -o %t/output.o \ +// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext +// RUN: %clang @%t/t.rsp +// RUN: ls %t/cas | wc -l | grep 2 +// RUN: ls %t/cas | grep v1.1 + +// Limit too high, no change. +// RUN: c-index-test core -prune-cas -cas-path %t/cas 100000000 -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext +// RUN: ls %t/cas | wc -l | grep 2 + +// Under the limit, starts a chain. +// RUN: c-index-test core -prune-cas -cas-path %t/cas 10 -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext +// RUN: ls %t/cas | wc -l | grep 3 +// RUN: ls %t/cas | grep v1.2 + +// Under the limit, starts a chain and abandons oldest dir. +// RUN: c-index-test core -prune-cas -cas-path %t/cas 10 -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext +// RUN: ls %t/cas | wc -l | grep 4 +// RUN: ls %t/cas | grep v1.3 + +// Under the limit, removes abandonded dir, starts a chain and abandons oldest dir. +// RUN: c-index-test core -prune-cas -cas-path %t/cas 10 -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext +// RUN: ls %t/cas | wc -l | grep 4 +// RUN: ls %t/cas | grep v1.4 +// RUN: ls %t/cas | grep -v v1.1 diff --git a/clang/test/CAS/libclang-replay-job.c b/clang/test/CAS/libclang-replay-job.c new file mode 100644 index 0000000000000..fc79b88bed2da --- /dev/null +++ b/clang/test/CAS/libclang-replay-job.c @@ -0,0 +1,89 @@ +// REQUIRES: shell + +// RUN: rm -rf %t && mkdir -p %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -format experimental-include-tree-full \ +// RUN: -cas-path %t/cas -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \ +// RUN: -fcas-plugin-option no-logging \ +// RUN: > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/cc1.rsp + +// RUN: (cd %t; %clang @%t/cc1.rsp) +// RUN: (cd %t; %clang @%t/cc1.rsp -Rcompile-job-cache-hit \ +// RUN: -serialize-diagnostic-file %t/t1.dia 2> %t/output1.txt) + +// Verify the warning was recorded and we compare populated .dia files. +// RUN: c-index-test -read-diagnostics %t/t1.dia 2>&1 | FileCheck %s --check-prefix=DIAGS +// DIAGS: warning: some warning + +// RUN: cat %t/output1.txt | grep llvmcas | sed \ +// RUN: -e "s/^.*hit for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key + +// RUN: c-index-test core -upload-cached-job -cas-path %t/cas @%t/cache-key -test-cas-cancellation \ +// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \ +// RUN: -fcas-plugin-option upstream-path=%t/cas-upstream \ +// RUN: 2>&1 | FileCheck %s --check-prefix=UPLOAD-CANCEL +// UPLOAD-CANCEL: actioncache_put_for_digest_async cancelled + +// Delete the "local" cache and use the "upstream" one to re-materialize the outputs locally. +// RUN: rm -rf %t/cas + +// Re-run the scan to populate the include-tree in the cas +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -format experimental-include-tree-full \ +// RUN: -cas-path %t/cas -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \ +// RUN: -fcas-plugin-option no-logging \ +// RUN: > %t/deps2.json +// RUN: diff -u %t/deps.json %t/deps2.json + + +// RUN: c-index-test core -materialize-cached-job -cas-path %t/cas @%t/cache-key -test-cas-cancellation \ +// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \ +// RUN: -fcas-plugin-option upstream-path=%t/cas-upstream \ +// RUN: 2>&1 | FileCheck %s --check-prefix=MATERIALIZE-CANCEL +// MATERIALIZE-CANCEL: actioncache_get_for_digest_async cancelled +// MATERIALIZE-CANCEL: load_object_async cancelled + +// RUN: c-index-test core -replay-cached-job -cas-path %t/cas @%t/cache-key \ +// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \ +// RUN: -fcas-plugin-option no-logging \ +// RUN: -working-dir %t \ +// RUN: -- @%t/cc1.rsp \ +// RUN: -serialize-diagnostic-file %t/t2.dia -Rcompile-job-cache-hit \ +// RUN: -dependency-file %t/t2.d -o %t/output2.o 2> %t/output2.txt + +// RUN: diff %t/output1.o %t/output2.o +// RUN: diff -u %t/output1.txt %t/output2.txt +// RUN: diff %t/t1.dia %t/t2.dia +// RUN: diff -u %t/t1.d %t/t2.d + +// Check with different `-working-dir` flag. +// RUN: mkdir -p %t/a/b +// RUN: cd %t/a +// RUN: c-index-test core -replay-cached-job -cas-path %t/cas @%t/cache-key \ +// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \ +// RUN: -fcas-plugin-option no-logging \ +// RUN: -working-dir %t/a/b \ +// RUN: -- @%t/cc1.rsp \ +// RUN: -serialize-diagnostic-file rel.dia -Rcompile-job-cache-hit \ +// RUN: -dependency-file rel.d -o reloutput.o + +// RUN: diff %t/output1.o %t/a/b/reloutput.o +// RUN: diff -u %t/t1.d %t/a/b/rel.d +// FIXME: Get clang's `-working-directory` to affect relative path for serialized diagnostics. + +// Use relative path to inputs and outputs. +//--- cdb.json.template +[{ + "directory": "DIR", + "command": "clang -c main.c -target x86_64-apple-macos11 -MD -MF t1.d -MT deps -o output1.o", + "file": "DIR/main.c" +}] + +//--- main.c +#warning some warning diff --git a/clang/test/CAS/lit.local.cfg b/clang/test/CAS/lit.local.cfg new file mode 100644 index 0000000000000..5f7b675090e3b --- /dev/null +++ b/clang/test/CAS/lit.local.cfg @@ -0,0 +1,26 @@ +config.substitutions.append(('%{objroot}', config.clang_obj_root)) +if not config.have_ondisk_cas: + config.unsupported = True + +import platform +if platform.system() == 'Windows': + config.unsupported = True + +import tempfile +config.daemon_temp_dir = tempfile.mkdtemp() + +# Feature for the temp directory path is not too long for certain tests. +# The limit on Darwin is 104 charactors and the default spawning daemon from +# clang will take 30 char. This will leave enough room for auto spawned daemon +# and also leave some room for each tests to use afterwards. +if len(config.daemon_temp_dir) < 74: + config.available_features.add('clang-cc1daemon') + config.substitutions.append(('%{clang-daemon-dir}', config.daemon_temp_dir)) + config.environment['TMPDIR'] = config.daemon_temp_dir + + # This also needs small path for unix domain socket path. + if config.enable_remote_cache: + config.available_features.add('remote-cache-service') + remote_cache_dir = os.path.join(config.daemon_temp_dir, 'rmt') + os.mkdir(remote_cache_dir) + config.substitutions.append(('%{remote-cache-dir}', remote_cache_dir)) diff --git a/clang/test/CAS/mccas-emit-casid-file.c b/clang/test/CAS/mccas-emit-casid-file.c new file mode 100644 index 0000000000000..2f93bef46668c --- /dev/null +++ b/clang/test/CAS/mccas-emit-casid-file.c @@ -0,0 +1,16 @@ +// REQUIRES: x86-registered-target +// RUN: rm -rf %t && mkdir %t + +// RUN: %clang -cc1depscan -o %t/args.rsp -cc1-args -cc1 -triple x86_64-apple-darwin10 \ +// RUN: -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb \ +// RUN: -emit-obj -fcas-backend -fcas-path %t/cas -fcas-emit-casid-file %s + +// RUN: %clang @%t/args.rsp -o %t/output1.o + +// cat %t/output1.o.casid | FileCheck %s + +// CHECK: llvmcas://{{[a-z0-9]+}} + +int foo(int x) { + return x+1; +} diff --git a/clang/test/CAS/mccas-replay-test.cpp b/clang/test/CAS/mccas-replay-test.cpp new file mode 100644 index 0000000000000..53f0339fd69d8 --- /dev/null +++ b/clang/test/CAS/mccas-replay-test.cpp @@ -0,0 +1,31 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: export LLVM_CACHE_CAS_PATH=%t/cas && %clang-cache \ +// RUN: %clang -target arm64-apple-macosx12.0.0 -c -Xclang -fcas-backend -Rcompile-job-cache %s -o %t/tmp.o -g 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// CACHE-MISS: remark: compile job cache miss + +// RUN: llvm-objdump -h %t/tmp.o | FileCheck %s -check-prefix=CHECK-OBJDUMP + +// RUN: export LLVM_CACHE_CAS_PATH=%t/cas && %clang-cache \ +// RUN: %clang -target arm64-apple-macosx12.0.0 -c -Xclang -fcas-backend -Rcompile-job-cache %s -o %t/tmp.o -g 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// CACHE-HIT: remark: compile job cache hit + +// RUN: llvm-objdump -h %t/tmp.o 2>&1 | FileCheck %s -check-prefix=CHECK-OBJDUMP + +// CHECK-OBJDUMP: Sections: +// CHECK-OBJDUMP-NEXT: Idx Name Size VMA Type +// CHECK-OBJDUMP-NEXT: 0 __text {{[0-9a-f]+}} {{[0-9a-f]+}} TEXT +// CHECK-OBJDUMP-NEXT: 1 __debug_abbrev {{[0-9a-f]+}} {{[0-9a-f]+}} DATA, DEBUG +// CHECK-OBJDUMP-NEXT: 2 __debug_info {{[0-9a-f]+}} {{[0-9a-f]+}} DATA, DEBUG +// CHECK-OBJDUMP-NEXT: 3 __debug_str {{[0-9a-f]+}} {{[0-9a-f]+}} DATA, DEBUG +// CHECK-OBJDUMP-NEXT: 4 __apple_names {{[0-9a-f]+}} {{[0-9a-f]+}} DATA, DEBUG +// CHECK-OBJDUMP-NEXT: 5 __apple_objc {{[0-9a-f]+}} {{[0-9a-f]+}} DATA, DEBUG +// CHECK-OBJDUMP-NEXT: 6 __apple_namespac {{[0-9a-f]+}} {{[0-9a-f]+}} DATA, DEBUG +// CHECK-OBJDUMP-NEXT: 7 __apple_types {{[0-9a-f]+}} {{[0-9a-f]+}} DATA, DEBUG +// CHECK-OBJDUMP-NEXT: 8 __compact_unwind {{[0-9a-f]+}} {{[0-9a-f]+}} DATA +// CHECK-OBJDUMP-NEXT: 9 __debug_line {{[0-9a-f]+}} {{[0-9a-f]+}} DATA, DEBUG + +// REQUIRES: aarch64-registered-target + +int foo() { + return 1; +} diff --git a/clang/test/CAS/mccas-replay.c b/clang/test/CAS/mccas-replay.c new file mode 100644 index 0000000000000..f42c3a04d0668 --- /dev/null +++ b/clang/test/CAS/mccas-replay.c @@ -0,0 +1,21 @@ +// REQUIRES: x86-registered-target +// RUN: rm -rf %t && mkdir %t + +// RUN: %clang -cc1depscan -o %t/args.rsp -cc1-args -cc1 -triple x86_64-apple-darwin10 \ +// RUN: -debug-info-kind=standalone -dwarf-version=4 -debugger-tuning=lldb \ +// RUN: -emit-obj -fcas-backend -fcas-path %t/cas %s -o - > /dev/null + +// RUN: %clang @%t/args.rsp -o %t/output1.o -Rcompile-job-cache 2> %t/output1.txt + +// RUN: cat %t/output1.txt | grep llvmcas | sed \ +// RUN: -e "s/^.*miss for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key + +// RUN: c-index-test core -replay-cached-job -cas-path %t/cas @%t/cache-key \ +// RUN: -working-dir %t -- @%t/args.rsp -o %t/output2.o + +// RUN: diff %t/output1.o %t/output2.o + +int foo(int x) { + return x+1; +} diff --git a/clang/test/CAS/modules-include-tree-pgo-option.c b/clang/test/CAS/modules-include-tree-pgo-option.c new file mode 100644 index 0000000000000..9ec7a96634b75 --- /dev/null +++ b/clang/test/CAS/modules-include-tree-pgo-option.c @@ -0,0 +1,42 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: llvm-profdata merge -o %t/instrumentation.profdata %S/Inputs/pgo.profraw +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module-name Top > %t/Top.rsp +// RUN: FileCheck %s --input-file=%t/Top.rsp + +// RUN: cat %t/Top.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Top.casid +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Top.casid | FileCheck %s +// CHECK-NOT: instrumentation.profdata + +//--- cdb.json.template +[{ + "file": "DIR/tu.m", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.m -I DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache -fprofile-instr-use=DIR/instrumentation.profdata" +}] + +//--- module.modulemap +module Top { header "Top.h" export *} + +//--- Top.h +#pragma once +struct Top { + int x; +}; +void top(void); + +//--- tu.m +#import "Top.h" + +void tu(void) { + top(); +} diff --git a/clang/test/CAS/modules-include-tree-unimported-impl.c b/clang/test/CAS/modules-include-tree-unimported-impl.c new file mode 100644 index 0000000000000..c8d45aa0bd8cd --- /dev/null +++ b/clang/test/CAS/modules-include-tree-unimported-impl.c @@ -0,0 +1,50 @@ +// Check that a definition for a symbol in an unimported submodule is not +// visible for codegen. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -j 1 \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module Mod > %t/Mod.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/Mod.rsp +// RUN: %clang @%t/tu.rsp -o - | FileCheck %s + +// CHECK-NOT: @record = global +// CHECK: @record = external global + +//--- cdb.json.template +[{ + "file": "DIR/tu.c", + "directory": "DIR", + "command": "clang -S -emit-llvm DIR/tu.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}] + +//--- module.modulemap +module Mod { + module A { + header "A.h" + } + explicit module B { + header "B.h" + } +} + +//--- A.h +extern int record; + +//--- B.h +int record = 7; + +//--- tu.c +#include "A.h" +int tu(void) { + return record; +} diff --git a/clang/test/CAS/output-path-create-directories.c b/clang/test/CAS/output-path-create-directories.c new file mode 100644 index 0000000000000..d4b5ae1125c9c --- /dev/null +++ b/clang/test/CAS/output-path-create-directories.c @@ -0,0 +1,38 @@ +// RUN: rm -rf %t %t.cas +// RUN: split-file %s %t +// RUN: llvm-cas --cas %t.cas --ingest %t > %t/casid + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=Mod -fno-implicit-modules \ +// RUN: -emit-module %t/module.modulemap -o %t/out/B.pcm \ +// RUN: -serialize-diagnostic-file %t/out/B.dia +// RUN: ls %t/out/B.pcm +// RUN: ls %t/out/B.dia + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=Mod -fno-implicit-modules \ +// RUN: -emit-module %t/module.modulemap -o %t/out_miss/B.pcm \ +// RUN: -serialize-diagnostic-file %t/out_miss/B.dia \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/B.out.txt +// RUN: cat %t/B.out.txt | FileCheck %s -check-prefix=CACHE-MISS +// RUN: ls %t/out_miss/B.pcm +// RUN: ls %t/out_miss/B.dia + +// RUN: %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=Mod -fno-implicit-modules \ +// RUN: -emit-module %t/module.modulemap -o %t/out_hit/B.pcm \ +// RUN: -serialize-diagnostic-file %t/out_hit/B.dia \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/B.out.hit.txt +// RUN: cat %t/B.out.hit.txt | FileCheck %s -check-prefix=CACHE-HIT +// RUN: ls %t/out_hit/B.pcm +// RUN: ls %t/out_hit/B.dia + +// CACHE-HIT: remark: compile job cache hit +// CACHE-MISS: remark: compile job cache miss + +//--- module.modulemap +module Mod { header "Header.h" } + +//--- Header.h diff --git a/clang/test/CAS/output-path-error.c b/clang/test/CAS/output-path-error.c new file mode 100644 index 0000000000000..b217ba48402cf --- /dev/null +++ b/clang/test/CAS/output-path-error.c @@ -0,0 +1,22 @@ +// Check that fatal errors from cache-related output paths show up. + +// REQUIRES: shell + +// RUN: rm -rf %t && mkdir -p %t +// RUN: llvm-cas --cas %t/cas --ingest %s > %t/casid + +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache -emit-obj %s -o %t/output.o 2>&1 \ +// RUN: | FileCheck %s --allow-empty --check-prefix=CACHE-MISS + +// Remove only the CAS, but leave the ActionCache. +// RUN: rm -rf %t/cas + +// RUN: not %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache -emit-obj %s -o %t/output.o &> %t/output.txt +// RUN: cat %t/output.txt | FileCheck %s --check-prefix=ERROR + +// CACHE-MISS: remark: compile job cache miss +// ERROR: fatal error: CAS missing expected root-id diff --git a/clang/test/CAS/path-independent-cas-outputs.c b/clang/test/CAS/path-independent-cas-outputs.c new file mode 100644 index 0000000000000..58691a3299818 --- /dev/null +++ b/clang/test/CAS/path-independent-cas-outputs.c @@ -0,0 +1,98 @@ +// RUN: rm -rf %t && mkdir -p %t/a %t/b + +// Check that we got a cache hit even though the output paths are different. + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/a/t1.o -MMD -MT dependencies -MF %t/a/t1.d --serialize-diagnostics %t/a/t1.dia -Rcompile-job-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/b/t2.o -MMD -MT dependencies -MF %t/b/t2.d --serialize-diagnostics %t/b/t2.dia -Rcompile-job-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CACHE-HIT + +// Check that output path is correctly detected with -working-directory. + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -working-directory %t -o a/t1_working_dir.o -DNEW_FLAG -Rcompile-job-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -working-directory %t -o b/t2_working_dir.o -DNEW_FLAG -Rcompile-job-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CACHE-HIT +// RUN: diff %t/a/t1_working_dir.o %t/b/t2_working_dir.o + +/// Check using a different working directory should cache hit as long as the compilation-dirs are setup correctly and inputs are the same. +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -working-directory %t/b -o t3_working_dir.o -DNEW_FLAG -Rcompile-job-cache -fdebug-compilation-dir=%t -fcoverage-compilation-dir=%t \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CACHE-HIT + +// Check that output path is correctly detected with - (stdout) + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o - -DNEW_FLAG2 -Rcompile-job-cache > %t/a/t1_stdout.o +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/b/t2_stdout.o -DNEW_FLAG2 -Rcompile-job-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CACHE-HIT +// RUN: diff %t/a/t1_stdout.o %t/b/t2_stdout.o + +// Check PCH output + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -x c-header %s -o %t/a/t1.pch -Rcompile-job-cache 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -x c-header %s -o %t/b/t2.pch -Rcompile-job-cache 2>&1 | FileCheck %s --check-prefix=CACHE-HIT + +// CACHE-MISS: remark: compile job cache miss +// CACHE-HIT: remark: compile job cache hit + +// Repeat to diff outputs produced from each invocation. CAS path is different to avoid cache hits. + +// RUN: rm -rf %t && mkdir -p %t + +// Baseline to check we got expected outputs. +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -MMD -MT dependencies -MF %t/t.d --serialize-diagnostics %t/t.dia +// RUN: env LLVM_CACHE_CAS_PATH=%t/a/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/a/t1.o -MMD -MT dependencies -MF %t/a/t1.d --serialize-diagnostics %t/a/t1.dia +// RUN: env LLVM_CACHE_CAS_PATH=%t/b/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/b/t2.o -MMD -MT dependencies -MF %t/b/t2.d --serialize-diagnostics %t/b/t2.dia + +// RUN: diff %t/a/t1.o %t/b/t2.o +// RUN: diff %t/t.o %t/a/t1.o + +// RUN: diff %t/a/t1.dia %t/b/t2.dia +// RUN: diff %t/t.dia %t/a/t1.dia + +// RUN: diff %t/a/t1.d %t/b/t2.d +// RUN: diff %t/t.d %t/a/t1.d + +// RUN: env LLVM_CACHE_CAS_PATH=%t/a/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -x c-header %s -o %t/a/t1.pch +// RUN: env LLVM_CACHE_CAS_PATH=%t/b/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -x c-header %s -o %t/b/t2.pch + +// RUN: diff %t/a/t1.pch %t/b/t2.pch + +// Check that caching is independent of whether '--serialize-diagnostics' exists or not. + +// Check with the option missing then present. +// RUN: env LLVM_CACHE_CAS_PATH=%t/d1/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t1.o -Rcompile-job-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: env LLVM_CACHE_CAS_PATH=%t/d1/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t2.o --serialize-diagnostics %t/t1.dia -Rcompile-job-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CACHE-HIT + +// Check with the option present then missing. +// RUN: env LLVM_CACHE_CAS_PATH=%t/d2/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t1.o --serialize-diagnostics %t/t2.dia -Rcompile-job-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: env LLVM_CACHE_CAS_PATH=%t/d2/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t2.o -Rcompile-job-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CACHE-HIT + +// RUN: c-index-test -read-diagnostics %t/t1.dia 2>&1 | FileCheck %s --check-prefix=SERIAL_DIAG-HIT --check-prefix=SERIAL_DIAG-COMMON +// RUN: c-index-test -read-diagnostics %t/t2.dia 2>&1 | FileCheck %s --check-prefix=SERIAL_DIAG-MISS --check-prefix=SERIAL_DIAG-COMMON +// SERIAL_DIAG-MISS: warning: compile job cache miss +// SERIAL_DIAG-HIT: warning: compile job cache hit +// SERIAL_DIAG-COMMON: warning: some warning + +#warning some warning +void test() {} diff --git a/clang/test/CAS/pgo-profile-with-pch.c b/clang/test/CAS/pgo-profile-with-pch.c new file mode 100644 index 0000000000000..03ed10cc46047 --- /dev/null +++ b/clang/test/CAS/pgo-profile-with-pch.c @@ -0,0 +1,28 @@ +// RUN: rm -rf %t.dir && mkdir -p %t.dir + +// Check that use of profile data for PCH is ignored + +// RUN: llvm-profdata merge -o %t.profdata %S/Inputs/pgo.profraw +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t-pch.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-pch -O3 -Rcompile-job-cache \ +// RUN: -x c-header %s -o %t.h.pch -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.profdata +// RUN: %clang @%t-pch.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: FileCheck %s -check-prefix=PCHPROF -input-file %t-pch.rsp +// PCHPROF-NOT: -fprofile-instrument-use-path + +// Update profdata file contents +// RUN: llvm-profdata merge -o %t.profdata %S/Inputs/pgo2.profraw + +// Use the modified profdata file for the main file along with the PCH. +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache \ +// RUN: -x c %s -o %t.o -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.profdata -include-pch %t.h.pch +// RUN: %clang @%t.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: FileCheck %s -check-prefix=TUPROF -input-file %t.rsp +// TUPROF: -fprofile-instrument-use-path + +// Check that the modified profdata is ignored when re-scanning for the PCH. +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t-pch2.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-pch -O3 -Rcompile-job-cache \ +// RUN: -x c-header %s -o %t.h.pch -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.profdata +// RUN: diff -u %t-pch.rsp %t-pch2.rsp + +// CACHE-MISS: remark: compile job cache miss +// CACHE-HIT: remark: compile job cache hit diff --git a/clang/test/CAS/pgo-profile.c b/clang/test/CAS/pgo-profile.c new file mode 100644 index 0000000000000..387b3e8fa7f70 --- /dev/null +++ b/clang/test/CAS/pgo-profile.c @@ -0,0 +1,74 @@ +// RUN: rm -rf %t.dir && mkdir -p %t.dir + +/// Check use pgo profile. +// RUN: llvm-profdata merge -o %t.profdata %S/Inputs/pgo.profraw +// RUN: %clang -cc1depscan -fdepscan=inline -o %t.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache \ +// RUN: -x c %s -o %t.o -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.profdata + +/// Remove profile data to make sure the cc1 command is not reading from file system. +// RUN: rm %t.profdata +// RUN: %clang @%t.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: %clang @%t.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-HIT + +/// Check include tree. +// RUN: llvm-profdata merge -o %t.profdata %S/Inputs/pgo.profraw +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t1.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache \ +// RUN: -x c %s -o %t.o -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.profdata +// RUN: rm %t.profdata +// RUN: %clang @%t1.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: %clang @%t1.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-HIT + +/// Check change profile data will cause cache miss. +// RUN: llvm-profdata merge -o %t.profdata %S/Inputs/pgo2.profraw +// RUN: %clang -cc1depscan -fdepscan=inline -o %t2.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache \ +// RUN: -x c %s -o %t.o -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.profdata +// RUN: not diff %t.rsp %t2.rsp +// RUN: %clang @%t2.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-MISS + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t3.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache \ +// RUN: -x c %s -o %t.o -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.profdata +// RUN: not diff %t1.rsp %t3.rsp +// RUN: %clang @%t3.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-MISS + +// CACHE-MISS: remark: compile job cache miss +// CACHE-HIT: remark: compile job cache hit + +/// Check remapping for profile. +// RUN: mkdir -p %t.dir/a && mkdir -p %t.dir/b +// RUN: cp %t.profdata %t.dir/a/a.profdata +// RUN: cp %t.profdata %t.dir/b/a.profdata +// RUN: %clang -cc1depscan -fdepscan=inline -o %t4.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache -fdepscan-prefix-map=%t.dir/a=/^testdir \ +// RUN: -x c %s -o %t.o -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.dir/a/a.profdata +// RUN: %clang -cc1depscan -fdepscan=inline -o %t5.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache -fdepscan-prefix-map=%t.dir/b=/^testdir \ +// RUN: -x c %s -o %t.o -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.dir/b/a.profdata +// RUN: cat %t4.rsp | FileCheck %s --check-prefix=REMAP +// RUN: %clang @%t4.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: %clang @%t5.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-HIT + +// RUN: cat %t4.rsp | sed \ +// RUN: -e "s/^.*\"-fcas-fs\" \"//" \ +// RUN: -e "s/\" .*$//" > %t.dir/cache-key1 +// RUN: cat %t5.rsp | sed \ +// RUN: -e "s/^.*\"-fcas-fs\" \"//" \ +// RUN: -e "s/\" .*$//" > %t.dir/cache-key2 +// RUN: grep llvmcas %t.dir/cache-key1 +// RUN: diff -u %t.dir/cache-key1 %t.dir/cache-key2 + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t4.inc.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache -fdepscan-prefix-map=%t.dir/a=/^testdir \ +// RUN: -x c %s -o %t.o -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.dir/a/a.profdata +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t5.inc.rsp -cc1-args -cc1 -triple x86_64-apple-macosx12.0.0 -emit-obj -O3 -Rcompile-job-cache -fdepscan-prefix-map=%t.dir/b=/^testdir \ +// RUN: -x c %s -o %t.o -fcas-path %t.dir/cas -fprofile-instrument-use-path=%t.dir/b/a.profdata +// RUN: cat %t4.inc.rsp | FileCheck %s --check-prefix=REMAP +// RUN: %clang @%t4.inc.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: %clang @%t5.inc.rsp 2>&1 | FileCheck %s --check-prefix=CACHE-HIT + +// RUN: cat %t4.inc.rsp | sed \ +// RUN: -e "s/^.*\"-fcas-include-tree\" \"//" \ +// RUN: -e "s/\" .*$//" > %t.dir/inc-cache-key1 +// RUN: cat %t5.inc.rsp | sed \ +// RUN: -e "s/^.*\"-fcas-include-tree\" \"//" \ +// RUN: -e "s/\" .*$//" > %t.dir/inc-cache-key2 +// RUN: grep llvmcas %t.dir/inc-cache-key1 +// RUN: diff -u %t.dir/inc-cache-key1 %t.dir/inc-cache-key2 + +// REMAP: -fprofile-instrument-use-path=/^testdir/a.profdata diff --git a/clang/test/CAS/plugin-cas.c b/clang/test/CAS/plugin-cas.c new file mode 100644 index 0000000000000..a1a0f9b3da405 --- /dev/null +++ b/clang/test/CAS/plugin-cas.c @@ -0,0 +1,58 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t && mkdir -p %t + +// RUN: %clang -cc1depscan -o %t/t1.rsp -fdepscan=inline -cc1-args \ +// RUN: -cc1 -emit-obj %s -fcas-path %t/cas \ +// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \ +// RUN: -fcas-plugin-option first-prefix=myfirst- -fcas-plugin-option second-prefix=mysecond- \ +// RUN: -fcas-plugin-option upstream-path=%t/cas-upstream +// RUN: %clang @%t/t1.rsp -o %t/t1.o -Rcompile-job-cache 2>&1 | FileCheck %s --check-prefix=CACHE-MISS + +// Clear the CAS and check the outputs can still be "downloaded" from upstream. +// RUN: rm -rf %t/cas +// RUN: %clang -cc1depscan -o %t/t2.rsp -fdepscan=inline -cc1-args \ +// RUN: -cc1 -emit-obj %s -fcas-path %t/cas \ +// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \ +// RUN: -fcas-plugin-option first-prefix=myfirst- -fcas-plugin-option second-prefix=mysecond- \ +// RUN: -fcas-plugin-option upstream-path=%t/cas-upstream +// RUN: %clang @%t/t2.rsp -o %t/t2.o -Rcompile-job-cache 2>&1 | FileCheck %s --check-prefix=CACHE-HIT +// RUN: diff %t/t1.o %t/t2.o + +// Check that it's a cache miss if outputs are not found in the upstream CAS. +// RUN: rm -rf %t/cas +// RUN: %clang -cc1depscan -o %t/t3.rsp -fdepscan=inline -cc1-args \ +// RUN: -cc1 -emit-obj %s -fcas-path %t/cas \ +// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \ +// RUN: -fcas-plugin-option first-prefix=myfirst- -fcas-plugin-option second-prefix=mysecond- \ +// RUN: -fcas-plugin-option upstream-path=%t/cas-upstream \ +// RUN: -fcas-plugin-option simulate-missing-objects +// RUN: %clang @%t/t3.rsp -o %t/t.o -Rcompile-job-cache 2>&1 | FileCheck %s --check-prefix=CACHE-NOTFOUND + +// CACHE-MISS: remark: compile job cache miss for 'myfirst-mysecond- +// CACHE-MISS: warning: some warning + +// Check that outputs are downloaded concurrently. +// CACHE-HIT: load_object_async downstream begin: +// CACHE-HIT-NEXT: load_object_async downstream begin: +// CACHE-HIT-NEXT: load_object_async downstream end: +// CACHE-HIT-NEXT: load_object_async downstream end: +// CACHE-HIT-NEXT: remark: compile job cache hit for 'myfirst-mysecond- +// CACHE-HIT-NEXT: warning: some warning + +// CACHE-NOTFOUND: remark: compile job cache backend did not find output 'main' for key +// CACHE-NOTFOUND: remark: compile job cache miss +// CACHE-NOTFOUND: warning: some warning + +// RUN: not %clang -cc1depscan -o %t/t.rsp -fdepscan=inline -cc1-args \ +// RUN: -cc1 %s -fcas-path %t/cas \ +// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \ +// RUN: -fcas-plugin-option no-such-option=2 2>&1 | FileCheck %s --check-prefix=FAIL-PLUGIN-OPT +// FAIL-PLUGIN-OPT: fatal error: CAS cannot be initialized from the specified '-fcas-*' options: unknown option: no-such-option + +// RUN: not %clang -cc1depscan -o %t/t.rsp -fdepscan=inline -cc1-args \ +// RUN: -cc1 %s -fcas-path %t/cas -fcas-plugin-path %t/non-existent 2>&1 | FileCheck %s --check-prefix=NOTEXISTENT +// NOTEXISTENT: fatal error: CAS cannot be initialized from the specified '-fcas-*' options + +#warning some warning +void test() {} diff --git a/clang/test/CAS/print-compile-job-cache-key.c b/clang/test/CAS/print-compile-job-cache-key.c new file mode 100644 index 0000000000000..f066c68281a5c --- /dev/null +++ b/clang/test/CAS/print-compile-job-cache-key.c @@ -0,0 +1,72 @@ +// REQUIRES: shell + +// RUN: rm -rf %t && mkdir -p %t +// RUN: llvm-cas --cas %t/cas --ingest %s > %t/casid +// +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas -fcas-fs @%t/casid -fcache-compile-job \ +// RUN: -Rcompile-job-cache-miss -emit-obj -o %t/output.o %s 2> %t/output.txt +// +// RUN: cat %t/output.txt | sed \ +// RUN: -e "s/^.*miss for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key +// +// RUN: not clang-cas-test -print-compile-job-cache-key -cas %t/cas 2>&1 | FileCheck %s -check-prefix=NO_KEY +// NO_KEY: missing compile-job cache key +// +// RUN: not clang-cas-test -print-compile-job-cache-key -cas %t/cas asdf 2>&1 | FileCheck %s -check-prefix=INVALID_KEY +// INVALID_KEY: invalid cas-id 'asdf' +// +// RUN: not clang-cas-test -print-compile-job-cache-key -cas %t/cas @%t/casid 2>&1 | FileCheck %s -check-prefix=NOT_A_KEY +// NOT_A_KEY: not a valid cache key +// +// RUN: clang-cas-test -print-compile-job-cache-key -cas %t/cas @%t/cache-key | FileCheck %s +// +// CHECK: command-line: llvmcas:// +// CHECK: -cc1 +// CHECK: -fcas-path llvm.cas.builtin.v2[BLAKE3] +// CHECK: -fcas-fs llvmcas:// +// CHECK: -x c {{.*}}print-compile-job-cache-key.c +// CHECK: computation: llvmcas:// +// CHECK: -cc1 +// CHECK: filesystem: llvmcas:// +// CHECK: file llvmcas:// +// CHECK: version: llvmcas:// +// CHECK: clang version + +// Print a key containing an include-tree. + +// RUN: %clang -cc1depscan -fdepscan=inline -fdepscan-include-tree -o %t/inc.rsp -cc1-args -cc1 -triple x86_64-apple-macos11 -emit-obj \ +// RUN: %s -o %t/output.o -fcas-path %t/cas +// RUN: %clang @%t/inc.rsp -Rcompile-job-cache 2> %t/output-tree.txt + +// RUN: cat %t/output-tree.txt | sed \ +// RUN: -e "s/^.*miss for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key-tree + +// RUN: clang-cas-test -print-compile-job-cache-key -cas %t/cas @%t/cache-key-tree | FileCheck %s -check-prefix=INCLUDE_TREE_KEY -check-prefix=INCLUDE_TREE -DSRC_FILE=%s +// +// INCLUDE_TREE_KEY: command-line: llvmcas:// +// INCLUDE_TREE_KEY: computation: llvmcas:// +// INCLUDE_TREE_KEY: include-tree: llvmcas:// +// INCLUDE_TREE: [[SRC_FILE]] llvmcas:// +// INCLUDE_TREE: Files: +// INCLUDE_TREE-NEXT: [[SRC_FILE]] llvmcas:// + +// RUN: cat %t/inc.rsp | sed \ +// RUN: -e "s/^.*\"-fcas-include-tree\" \"//" \ +// RUN: -e "s/\" .*$//" > %t/include-tree-id + +// RUN: clang-cas-test -print-include-tree -cas %t/cas @%t/include-tree-id | FileCheck %s -check-prefix=INCLUDE_TREE -DSRC_FILE=%s + +// Print key from plugin CAS. +// RUN: llvm-cas --cas plugin://%llvmshlibdir/libCASPluginTest%pluginext?ondisk-path=%t/cas-plugin --ingest %s > %t/casid-plugin +// RUN: %clang -cc1 -triple x86_64-apple-macos11 \ +// RUN: -fcas-path %t/cas-plugin -fcas-fs @%t/casid-plugin -fcache-compile-job \ +// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext \ +// RUN: -Rcompile-job-cache-miss -emit-obj -o %t/output.o %s 2> %t/output-plugin.txt +// RUN: cat %t/output-plugin.txt | sed \ +// RUN: -e "s/^.*miss for '//" \ +// RUN: -e "s/' .*$//" > %t/cache-key-plugin +// RUN: clang-cas-test -print-compile-job-cache-key -cas %t/cas @%t/cache-key-plugin \ +// RUN: -fcas-plugin-path %llvmshlibdir/libCASPluginTest%pluginext | FileCheck %s diff --git a/clang/test/CAS/remote-cache-incompatible-options.c b/clang/test/CAS/remote-cache-incompatible-options.c new file mode 100644 index 0000000000000..b15a3c98b18f6 --- /dev/null +++ b/clang/test/CAS/remote-cache-incompatible-options.c @@ -0,0 +1,3 @@ +// RUN: not %clang_cc1 -triple x86_64-apple-macos11 -fcompilation-caching-service-path %t -fcas-backend -fcasid-output -emit-obj %s -o %t.o 2>&1 | FileCheck %s +// CHECK: error: '-fcas-backend' is incompatible with remote caching backend +// CHECK: error: '-fcasid-output' is incompatible with remote caching backend diff --git a/clang/test/CAS/remote-cache-service.c b/clang/test/CAS/remote-cache-service.c new file mode 100644 index 0000000000000..401886b3557ee --- /dev/null +++ b/clang/test/CAS/remote-cache-service.c @@ -0,0 +1,41 @@ +// REQUIRES: remote-cache-service + +// Need a short path for the unix domain socket (and unique for this test file). +// RUN: rm -f %{remote-cache-dir}/%basename_t +// RUN: rm -rf %t && mkdir -p %t + +// Baseline to check we got expected outputs. +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -MMD -MT dependencies -MF %t/t.d --serialize-diagnostics %t/t.dia +// Adding `LLBUILD_TASK_ID` just to make sure there's no failure if that is set but `LLBUILD_CONTROL_FD` is not. +// RUN: llvm-remote-cache-test -socket-path=%{remote-cache-dir}/%basename_t -cache-path=%t/cache -- env LLBUILD_TASK_ID=1 LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t1.o -MMD -MT dependencies -MF %t/t1.d --serialize-diagnostics %t/t1.dia -Rcompile-job-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: llvm-remote-cache-test -socket-path=%{remote-cache-dir}/%basename_t -cache-path=%t/cache -- env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t2.o -MMD -MT dependencies -MF %t/t2.d --serialize-diagnostics %t/t2.dia -Rcompile-job-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CACHE-HIT + +// CACHE-MISS: remark: compile job cache miss +// CACHE-MISS: warning: some warning + +// CACHE-HIT: remark: compile job cache hit +// CACHE-HIT: warning: some warning + +// RUN: diff %t/t1.o %t/t2.o +// RUN: diff %t/t.o %t/t1.o + +// RUN: diff %t/t1.d %t/t2.d +// RUN: diff %t/t.d %t/t1.d + +// RUN: c-index-test -read-diagnostics %t/t1.dia 2>&1 | FileCheck %s --check-prefix=SERIAL_DIAG-MISS --check-prefix=SERIAL_DIAG-COMMON +// RUN: c-index-test -read-diagnostics %t/t2.dia 2>&1 | FileCheck %s --check-prefix=SERIAL_DIAG-HIT --check-prefix=SERIAL_DIAG-COMMON +// SERIAL_DIAG-MISS: warning: compile job cache miss +// SERIAL_DIAG-HIT: warning: compile job cache hit +// SERIAL_DIAG-COMMON: warning: some warning + +// Verify the outputs did not go into the on-disk ObjectStore. +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t2.o -MMD -MT dependencies -MF %t/t2.d --serialize-diagnostics %t/t2.dia -Rcompile-job-cache \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CACHE-MISS + +#warning some warning +void test() {} diff --git a/clang/test/CAS/remote-timing-remarks.c b/clang/test/CAS/remote-timing-remarks.c new file mode 100644 index 0000000000000..0059c5cb0a8c9 --- /dev/null +++ b/clang/test/CAS/remote-timing-remarks.c @@ -0,0 +1,23 @@ +// REQUIRES: remote-cache-service + +// Need a short path for the unix domain socket (and unique for this test file). +// RUN: rm -f %{remote-cache-dir}/%basename_t +// RUN: rm -rf %t && mkdir -p %t + +// RUN: llvm-remote-cache-test -socket-path=%{remote-cache-dir}/%basename_t -cache-path=%t/cache -- \ +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Rcompile-job-cache -Rcompile-job-cache-timing 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: llvm-remote-cache-test -socket-path=%{remote-cache-dir}/%basename_t -cache-path=%t/cache -- \ +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Rcompile-job-cache -Rcompile-job-cache-timing 2>&1 | FileCheck %s --check-prefix=CACHE-HIT + +// CACHE-MISS: remark: compile job dependency scanning time: +// CACHE-MISS: remark: compile job cache backend key query time: +// CACHE-MISS: remark: compile job cache miss +// CACHE-MISS: remark: compile job cache backend store artifacts time: +// CACHE-MISS: remark: compile job cache backend key update time: + +// CACHE-HIT: remark: compile job dependency scanning time: +// CACHE-HIT: remark: compile job cache backend key query time: +// CACHE-HIT: remark: compile job cache hit +// CACHE-HIT: remark: compile job cache backend load artifacts time: diff --git a/clang/test/CAS/rewrite-includes.c b/clang/test/CAS/rewrite-includes.c new file mode 100644 index 0000000000000..0af58696181ea --- /dev/null +++ b/clang/test/CAS/rewrite-includes.c @@ -0,0 +1,86 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-include-tree-full -cas-path %t/cas > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module-name dummy > %t/dummy.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name Mod > %t/mod.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name Spurious > %t/spurious.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/dummy.rsp +// RUN: %clang @%t/mod.rsp +// RUN: %clang @%t/spurious.rsp +// RUN: %clang @%t/tu.rsp -frewrite-includes -w -E -o - | FileCheck %s + +// CHECK: int bar();{{$}} +// CHECK-NEXT: #if defined(__CLANG_REWRITTEN_INCLUDES) /* test.h expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: #include "test.h"{{$}} +// CHECK-NEXT: #else /* test.h expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: # 2 "{{.*[/\\]}}main.c"{{$}} +// CHECK-NEXT: # 1 "{{.*[/\\]}}test.h" 1{{$}} +// CHECK-NEXT: #if 0 /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: #include "dummy.h"{{$}} +// CHECK-NEXT: #endif /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: # 1 "{{.*[/\\]}}test.h"{{$}} +// CHECK-NEXT: #pragma clang module import dummy /* clang -frewrite-includes: implicit import */{{$}} +// CHECK-NEXT: # 2 "{{.*[/\\]}}test.h"{{$}} +// CHECK-NEXT: #endif /* test.h expanded by -frewrite-includes */ +// CHECK-NEXT: # 3 "{{.*[/\\]}}main.c" 2{{$}} +// CHECK-NEXT: int foo();{{$}} +// CHECK-NEXT: #if 0 /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: #include "dummy.h"{{$}} +// CHECK-NEXT: #endif /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: # 4 "{{.*[/\\]}}main.c"{{$}} +// CHECK-NEXT: #pragma clang module import dummy /* clang -frewrite-includes: implicit import */{{$}} +// CHECK-NEXT: # 5 "{{.*[/\\]}}main.c"{{$}} +// CHECK-NEXT: #if defined(__CLANG_REWRITTEN_INCLUDES) /* Missing.h expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: #include {{$}} +// CHECK-NEXT: #else /* Missing.h expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: # 5 "{{.*[/\\]}}main.c"{{$}} +// CHECK-NEXT: # 1 "{{.*[/\\]}}frameworks/Spurious.framework/Headers/Missing.h" 1{{$}} +// CHECK-NEXT: /* empty */{{$}} +// CHECK-NEXT: #endif /* Missing.h expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: # 6 "{{.*[/\\]}}main.c" 2{{$}} +// CHECK-NEXT: #if 0 /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: #include {{$}} +// CHECK-NEXT: #endif /* expanded by -frewrite-includes */{{$}} +// CHECK-NEXT: # 6 "{{.*[/\\]}}main.c"{{$}} +// CHECK-NEXT: #pragma clang module import Mod /* clang -frewrite-includes: implicit import */{{$}} +// CHECK-NEXT: # 7 "{{.*[/\\]}}main.c"{{$}} + +//--- cdb.json.template +[ + { + "directory": "DIR", + "command": "clang -fsyntax-only -fmodules DIR/main.c -F DIR/frameworks -I DIR -fmodules-cache-path=DIR/module-cache", + "file": "DIR/t.c" + } +] + +//--- dummy.h +extern int dummy; + +//--- module.modulemap +module dummy { header "dummy.h" } +module Mod { header "Mod.h" } +//--- frameworks/Spurious.framework/Modules/module.modulemap +framework module Spurious { + umbrella header "Spurious.h" + module * { export * } +} +//--- frameworks/Spurious.framework/Headers/Spurious.h +#include +//--- frameworks/Spurious.framework/Headers/Missing.h +/* empty */ +//--- Mod.h +typedef int mod_int; +//--- test.h +#include "dummy.h" +//--- main.c +int bar(); +#include "test.h" +int foo(); +#include "dummy.h" +#include +#include diff --git a/clang/test/CAS/test-for-deterministic-module.c b/clang/test/CAS/test-for-deterministic-module.c new file mode 100644 index 0000000000000..bd62e08f83e3f --- /dev/null +++ b/clang/test/CAS/test-for-deterministic-module.c @@ -0,0 +1,27 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: llvm-cas --cas %t.cas --ingest %t > %t/casid +// +// RUN: not %clang_cc1 -triple x86_64-apple-macos11 \ +// RUN: -fmodules -fmodule-name=A -fno-implicit-modules \ +// RUN: -emit-module %t/module.modulemap -o %t/A.pcm \ +// RUN: -fcas-path %t.cas -fcas-fs @%t/casid \ +// RUN: -fcache-compile-job -Rcompile-job-cache &> %t/A.out.txt +// RUN: FileCheck %s --input-file=%t/A.out.txt + +// CHECK: remark: compile job cache miss +// CHECK: error: encountered non-reproducible token, caching failed +// CHECK: error: encountered non-reproducible token, caching failed +// CHECK: error: encountered non-reproducible token, caching failed + +//--- module.modulemap +module A { header "A.h" } + +//--- A.h +void getit(const char **p1, const char **p2, const char **p3) { + *p1 = __DATE__; + *p2 = __TIMESTAMP__; + *p3 = __TIME__; +} diff --git a/clang/test/CAS/test-for-deterministic-outputs.c b/clang/test/CAS/test-for-deterministic-outputs.c new file mode 100644 index 0000000000000..c730d9a464005 --- /dev/null +++ b/clang/test/CAS/test-for-deterministic-outputs.c @@ -0,0 +1,57 @@ +// RUN: rm -rf %t && mkdir -p %t + +// This compiles twice with replay disabled, ensuring that we get the same outputs for the same key. + +// Under clang-cache + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas CLANG_CACHE_TEST_DETERMINISTIC_OUTPUTS=1 CLANG_CACHE_REDACT_TIME_MACROS=1 %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Rcompile-job-cache 2> %t/out.txt +// RUN: FileCheck %s --check-prefix=CACHE-SKIPPED --input-file=%t/out.txt + +// Under clang driver + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas CLANG_CACHE_TEST_DETERMINISTIC_OUTPUTS=1 CLANG_CACHE_REDACT_TIME_MACROS=1 \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Rcompile-job-cache \ +// RUN: -fdepscan=inline -Xclang -fcas-path -Xclang %t/cas 2> %t/out_driver.txt +// RUN: FileCheck %s --check-prefix=CACHE-SKIPPED --input-file=%t/out_driver.txt + +// CACHE-SKIPPED: remark: compile job cache skipped +// CACHE-SKIPPED: remark: compile job cache skipped + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Rcompile-job-cache -Wreproducible-caching -serialize-diagnostics %t/t.dia 2> %t/out.txt +// RUN: FileCheck %s --check-prefix=CACHE-WARN --input-file=%t/out.txt -DREMARK=remark +// RUN: c-index-test -read-diagnostics %t/t.dia 2>&1 | FileCheck %s --check-prefix=CACHE-WARN -DREMARK=warning + +/// Check still a cache miss. +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Rcompile-job-cache -Wreproducible-caching 2> %t/out.txt +// RUN: FileCheck %s --check-prefix=CACHE-WARN --input-file=%t/out.txt -DREMARK=remark + +// CACHE-WARN: [[REMARK]]: compile job cache miss +// CACHE-WARN: warning: encountered non-reproducible token, caching will be skipped +// CACHE-WARN: warning: encountered non-reproducible token, caching will be skipped +// CACHE-WARN: warning: encountered non-reproducible token, caching will be skipped +// CACHE-WARN: [[REMARK]]: compile job cache skipped + +/// Check -Werror doesn't actually error when we use the launcher. +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Werror -Rcompile-job-cache 2> %t/out.txt +// RUN: FileCheck %s --check-prefix=NOERROR --input-file=%t/out.txt +// RUN: not env LLVM_CACHE_CAS_PATH=%t/cas CLANG_CACHE_CHECK_REPRODUCIBLE_CACHING_ISSUES=1 %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Rcompile-job-cache 2> %t/out.txt +// RUN: FileCheck %s --check-prefix=ERROR --input-file=%t/out.txt + +// NOERROR-NOT: error: +// ERROR: error: encountered non-reproducible token, caching will be skipped + +// Verify we don't double output without caching enabled. +// RUN: env CLANG_CACHE_TEST_DETERMINISTIC_OUTPUTS=1 %clang -E %s | FileCheck %s -check-prefix=NO_CACHE +// NO_CACHE: getit +// NO_CACHE-NOT: getit + +void getit(const char **p1, const char **p2, const char **p3) { + *p1 = __DATE__; + *p2 = __TIMESTAMP__; + *p3 = __TIME__; +} diff --git a/clang/test/CAS/timing-remarks.c b/clang/test/CAS/timing-remarks.c new file mode 100644 index 0000000000000..d5a60876ce959 --- /dev/null +++ b/clang/test/CAS/timing-remarks.c @@ -0,0 +1,17 @@ +// RUN: rm -rf %t && mkdir -p %t + +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Rcompile-job-cache -Rcompile-job-cache-timing 2>&1 | FileCheck %s --check-prefix=CACHE-MISS +// RUN: env LLVM_CACHE_CAS_PATH=%t/cas %clang-cache \ +// RUN: %clang -target x86_64-apple-macos11 -c %s -o %t/t.o -Rcompile-job-cache -Rcompile-job-cache-timing 2>&1 | FileCheck %s --check-prefix=CACHE-HIT + +// CACHE-MISS: remark: compile job dependency scanning time: +// CACHE-MISS: remark: compile job cache backend key query time: +// CACHE-MISS: remark: compile job cache miss +// CACHE-MISS: remark: compile job cache backend store artifacts time: +// CACHE-MISS: remark: compile job cache backend key update time: + +// CACHE-HIT: remark: compile job dependency scanning time: +// CACHE-HIT: remark: compile job cache backend key query time: +// CACHE-HIT: remark: compile job cache hit +// CACHE-HIT: remark: compile job cache backend load artifacts time: diff --git a/clang/test/CAS/warn-reproducible-caching.c b/clang/test/CAS/warn-reproducible-caching.c new file mode 100644 index 0000000000000..35e10049f37b8 --- /dev/null +++ b/clang/test/CAS/warn-reproducible-caching.c @@ -0,0 +1,24 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: split-file %s %t + +// No warning in normal invocation. +// RUN: %clang -target x86_64-apple-macos11 -fsyntax-only %t/t.c -isystem %t/sys 2>&1 | FileCheck %s --check-prefix=NOWARN --allow-empty +// NOWARN-NOT: warning + +// Warning in cached invocation even if coming from system header. -Werror does not affect it. +// RUN: %clang -target x86_64-apple-macos11 -c %t/t.c -o %t/t.o -isystem %t/sys -Werror \ +// RUN: -fdepscan=inline -Xclang -fcas-path -Xclang %t/cas -Xclang -fcache-compile-job 2>&1 | FileCheck %s --check-prefix=WARN +// WARN: warning: encountered non-reproducible token + +// Error if explicitly turned into error. +// RUN: not %clang -target x86_64-apple-macos11 -c %t/t.c -o %t/t.o -isystem %t/sys -Werror=reproducible-caching \ +// RUN: -fdepscan=inline -Xclang -fcas-path -Xclang %t/cas -Xclang -fcache-compile-job 2>&1 | FileCheck %s --check-prefix=ERROR +// ERROR: error: encountered non-reproducible token + +//--- t.c +#include + +//--- sys/sys.h +const char *foo() { + return __DATE__; +} diff --git a/clang/test/CAS/zero-ar-date.c b/clang/test/CAS/zero-ar-date.c new file mode 100644 index 0000000000000..6147dbbb65028 --- /dev/null +++ b/clang/test/CAS/zero-ar-date.c @@ -0,0 +1,40 @@ +// Test that clang tells the linker to create reproducible executables, in +// spite of the Darwin executable's debug map having a field for each object +// file's timestamp. +// +// FIXME: Should be split up into various tests outside of clang/test/CAS. + +// Needs 'shell' for touch and 'system-darwin' for calling through to the +// linker. +// REQUIRES: shell +// REQUIRES: system-darwin + +// RUN: rm -rf %t +// RUN: mkdir %t + +// Confirm "-greproducible" is passed through to -cc1. +// RUN: %clang -greproducible -target x86_64-apple-macos11.0 -x c -o %t/t.o -c %s -### 2>&1 \ +// RUN: | FileCheck %s -check-prefix=CHECK-CC1 +// CHECK-CC1: "-greproducible" + +// Build an object file with debug info. Confirm the link command sets +// ZERO_AR_DATE to make the output reproducible. +// RUN: %clang -greproducible -target x86_64-apple-macos11.0 -x c -o %t/t.o -c %s +// RUN: %clang -greproducible -target x86_64-apple-macos11.0 -o %t/exec %t/t.o -### 2>&1 \ +// RUN: | FileCheck %s -check-prefix=CHECK-LINK +// CHECK-LINK: env "ZERO_AR_DATE=1" "{{[^"]*}}/ld" + +// Set the modtime to the start of January 1st and build an executable. +// RUN: touch -t 202001010000.00 %t/t.o +// RUN: %clang -greproducible -target x86_64-apple-macos11.0 -o %t/exec %t/t.o +// RUN: cp %t/exec %t/exec1 + +// Set the modtime to the end of January 31st and build an executable. +// RUN: touch -t 202012312359.59 %t/t.o +// RUN: %clang -greproducible -target x86_64-apple-macos11.0 -o %t/exec %t/t.o +// RUN: cp %t/exec %t/exec2 + +// Confirm the executables match. +// RUN: diff %t/exec1 %t/exec2 + +int main(void) { return 0; } diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt index 0d11fd2c07eb5..324440a4314d8 100644 --- a/clang/test/CMakeLists.txt +++ b/clang/test/CMakeLists.txt @@ -15,11 +15,13 @@ llvm_canonicalize_cmake_booleans( ENABLE_BACKTRACES LLVM_BUILD_EXAMPLES LLVM_BYE_LINK_INTO_TOOLS + LLVM_CAS_ENABLE_REMOTE_CACHE LLVM_ENABLE_PLUGINS LLVM_ENABLE_ZLIB LLVM_ENABLE_ZSTD LLVM_ENABLE_PER_TARGET_RUNTIME_DIR LLVM_ENABLE_THREADS + LLVM_ENABLE_ONDISK_CAS LLVM_ENABLE_REVERSE_ITERATION LLVM_LINK_LLVM_DYLIB LLVM_WITH_Z3 @@ -69,12 +71,15 @@ endif () list(APPEND CLANG_TEST_DEPS apinotes-test c-index-test + cache-build-session clang + clang-cas-test clang-fuzzer-dictionary clang-resource-headers clang-format clang-tblgen clang-offload-bundler + clang-refactor-test clang-import-test clang-refactor clang-diff @@ -131,10 +136,13 @@ if( NOT CLANG_BUILT_STANDALONE ) list(APPEND CLANG_TEST_DEPS llvm-config FileCheck count not + CASPluginTest llc llvm-ar llvm-as llvm-bcanalyzer + llvm-cas + llvm-cas-dump llvm-cat llvm-cxxfilt llvm-dis @@ -165,6 +173,9 @@ if( NOT CLANG_BUILT_STANDALONE ) if(TARGET llvm-lto) list(APPEND CLANG_TEST_DEPS llvm-lto) endif() + if(TARGET llvm-remote-cache-test) + list(APPEND CLANG_TEST_DEPS llvm-remote-cache-test) + endif() endif() if(CLANG_ENABLE_STATIC_ANALYZER) diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p3.cpp index 5f715a1ec21d3..43396d4610ba5 100644 --- a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p3.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.unused/p3.cpp @@ -14,7 +14,7 @@ void f() { int x; // expected-warning {{unused variable}} typedef int I; // expected-warning {{unused typedef 'I'}} E1 e; - switch (e) { // expected-warning {{enumeration value 'UsedEnumVal' not handled in switch}} + switch (e) { // expected-warning {{enumeration value 'UsedEnumVal' not handled in switch}} expected-note {{add missing}} } // Should not warn about these due to not being used. diff --git a/clang/test/ClangScanDeps/Inputs/SDK/usr/include/stdlib.h b/clang/test/ClangScanDeps/Inputs/SDK/usr/include/stdlib.h new file mode 100644 index 0000000000000..a1bf1a8b29450 --- /dev/null +++ b/clang/test/ClangScanDeps/Inputs/SDK/usr/include/stdlib.h @@ -0,0 +1 @@ +typedef __SIZE_TYPE__ size_t; diff --git a/clang/test/ClangScanDeps/Inputs/modules_cdb.json b/clang/test/ClangScanDeps/Inputs/modules_cdb.json index a0c5123cd2124..987451ca2f7ea 100644 --- a/clang/test/ClangScanDeps/Inputs/modules_cdb.json +++ b/clang/test/ClangScanDeps/Inputs/modules_cdb.json @@ -1,7 +1,7 @@ [ { "directory": "DIR", - "command": "clang -E DIR/modules_cdb_input2.cpp -IInputs -D INCLUDE_HEADER2 -MD -MF DIR/modules_cdb2.d -fmodules -fcxx-modules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps", + "command": "clang -E DIR/modules_cdb_input2.cpp -IInputs -D INCLUDE_HEADER2 -MD -MF DIR/modules_cdb2.d -fmodules -fcxx-modules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps -gmodules", "file": "DIR/modules_cdb_input2.cpp" }, { diff --git a/clang/test/ClangScanDeps/cas-case-sensitivity.c b/clang/test/ClangScanDeps/cas-case-sensitivity.c new file mode 100644 index 0000000000000..68915d62bfc82 --- /dev/null +++ b/clang/test/ClangScanDeps/cas-case-sensitivity.c @@ -0,0 +1,58 @@ +// REQUIRES: case_insensitive_build_dir,ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb1.json.template > %t/cdb1.json +// RUN: sed -e "s|DIR|%/t|g" %t/cdb2.json.template > %t/cdb2.json + +// RUN: clang-scan-deps -compilation-database %t/cdb1.json -cas-path %t/cas -format experimental-tree -mode preprocess-dependency-directives > %t/result1.txt +// RUN: clang-scan-deps -compilation-database %t/cdb2.json -cas-path %t/cas -format experimental-tree -mode preprocess > %t/result2.txt +// RUN: sed -e 's/^.*llvmcas/llvmcas/' -e 's/ for.*$//' %t/result1.txt > %t/casid1 +// RUN: sed -e 's/^.*llvmcas/llvmcas/' -e 's/ for.*$//' %t/result2.txt > %t/casid2 + +// RUN: llvm-cas --cas %t/cas --ls-tree-recursive @%t/casid1 | FileCheck -check-prefix=TREE %s -DPREFIX=%/t +// RUN: llvm-cas --cas %t/cas --ls-tree-recursive @%t/casid2 | FileCheck -check-prefix=TREE %s -DPREFIX=%/t + +// asdf: FileCheck -check-prefix=TREE %s -input-file %t/result1.txt -DPREFIX=%/t + +// TREE: file llvmcas://{{.*}} [[PREFIX]]/Header.h +// TREE: syml llvmcas://{{.*}} [[PREFIX]]/header.h -> Header +// TREE: file llvmcas://{{.*}} [[PREFIX]]/t{{[12]}}.c + +//--- cdb1.json.template +[ + { + "directory": "DIR", + "command": "clang -fsyntax-only DIR/t1.c", + "file": "DIR/t1.c" + } +] + +//--- cdb2.json.template +[ + { + "directory": "DIR", + "command": "clang -fsyntax-only DIR/t2.c", + "file": "DIR/t2.c" + } +] + +//--- t1.c +#include "header.h" +#include "Header.h" + +void bar1(void) { + foo(); +} + +//--- t2.c +#include "Header.h" +#include "header.h" + +void bar2(void) { + foo(); +} + +//--- Header.h +#pragma once +void foo(void); diff --git a/clang/test/ClangScanDeps/cas-fs-multiple-commands.c b/clang/test/ClangScanDeps/cas-fs-multiple-commands.c new file mode 100644 index 0000000000000..28bf3857cf334 --- /dev/null +++ b/clang/test/ClangScanDeps/cas-fs-multiple-commands.c @@ -0,0 +1,270 @@ +// Test scanning when the driver requires multiple jobs. E.g. with -save-temps +// there will be separate -E, -emit-llvm-bc, -S, and -cc1as jobs, which should +// each result in a "command" in the output. + +// REQUIRES: ondisk_cas + +// We use an x86_64-apple-darwin target to avoid host-dependent behaviour in +// the driver. Platforms without an integrated assembler have different commands +// REQUIRES: x86-registered-target + +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: mv %t/tu_define_foo_0.c %t/tu.c +// RUN: clang-scan-deps -format experimental-tree-full -cas-path %t/cas -module-files-dir %t/modules \ +// RUN: -- %clang -target x86_64-apple-darwin -c %t/tu.c -save-temps=obj -o %t/tu.o \ +// RUN: -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=%t/cache \ +// RUN: > %t/deps.0.json + +// RUN: cat %t/deps.0.json | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t + +// RUN: CLANG_CACHE_USE_CASFS_DEPSCAN=1 c-index-test core -scan-deps -working-dir %t -cas-path %t/cas -output-dir %t/modules -- \ +// RUN: %clang -target x86_64-apple-darwin -c %t/tu.c -save-temps=obj -o %t/tu.o \ +// RUN: -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=%t/cache \ +// RUN: > %t/deps.txt + +// RUN: cat %t/deps.txt | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t -check-prefix=CHECK-LIBCLANG + +// RUN: %deps-to-rsp %t/deps.0.json --module-name=Mod > %t/Mod.0.rsp +// RUN: %deps-to-rsp %t/deps.0.json --tu-index 0 --tu-cmd-index 0 > %t/tu-cpp.0.rsp +// RUN: %deps-to-rsp %t/deps.0.json --tu-index 0 --tu-cmd-index 1 > %t/tu-emit-ir.0.rsp +// RUN: %deps-to-rsp %t/deps.0.json --tu-index 0 --tu-cmd-index 2 > %t/tu-emit-asm.0.rsp +// RUN: %deps-to-rsp %t/deps.0.json --tu-index 0 --tu-cmd-index 3 > %t/tu-cc1as.0.rsp +// RUN: %clang @%t/Mod.0.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu-cpp.0.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu-emit-ir.0.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu-emit-asm.0.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu-cc1as.0.rsp +// RUN: mv %t/tu.i %t/tu.0.i +// RUN: mv %t/tu.bc %t/tu.0.bc +// RUN: mv %t/tu.s %t/tu.0.s +// RUN: mv %t/tu.o %t/tu.0.o + +// RUN: mv %t/tu_define_foo_1.c %t/tu.c +// RUN: clang-scan-deps -format experimental-tree-full -cas-path %t/cas -module-files-dir %t/modules \ +// RUN: -- %clang -target x86_64-apple-darwin -c %t/tu.c -save-temps=obj -o %t/tu.o \ +// RUN: -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=%t/cache \ +// RUN: > %t/deps.1.json + +// The dependency graph has identical structure, just the FS root ID and dependent cache keys are different. +// RUN: cat %t/deps.1.json | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t +// RUN: not diff %t/deps.1.json %t/deps.0.json + +// RUN: %deps-to-rsp %t/deps.1.json --module-name=Mod > %t/Mod.1.rsp +// RUN: %deps-to-rsp %t/deps.1.json --tu-index 0 --tu-cmd-index 0 > %t/tu-cpp.1.rsp +// RUN: %deps-to-rsp %t/deps.1.json --tu-index 0 --tu-cmd-index 1 > %t/tu-emit-ir.1.rsp +// RUN: %deps-to-rsp %t/deps.1.json --tu-index 0 --tu-cmd-index 2 > %t/tu-emit-asm.1.rsp +// RUN: %deps-to-rsp %t/deps.1.json --tu-index 0 --tu-cmd-index 3 > %t/tu-cc1as.1.rsp +// RUN: %clang @%t/Mod.1.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: %clang @%t/tu-cpp.1.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu-emit-ir.1.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: %clang @%t/tu-emit-asm.1.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: %clang @%t/tu-cc1as.1.rsp +// RUN: mv %t/tu.i %t/tu.1.i +// RUN: mv %t/tu.bc %t/tu.1.bc +// RUN: mv %t/tu.s %t/tu.1.s +// RUN: mv %t/tu.o %t/tu.1.o + +// RUN: diff %t/tu.1.i %t/tu.0.i +// RUN: diff %t/tu.1.bc %t/tu.0.bc +// RUN: diff %t/tu.1.s %t/tu.0.s +// RUN: diff %t/tu.1.o %t/tu.0.o + +// CACHE-HIT: remark: compile job cache hit +// CACHE-MISS: remark: compile job cache miss + +// CHECK: "modules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[M_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "casfs-root-id": "[[M_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "clang-module-deps": [] +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "command-line": [ +// CHECK: "-fcas-fs" +// CHECK-NEXT: "[[M_ROOT_ID]]" +// CHECK: ] +// CHECK: "name": "Mod" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: "translation-units": [ +// CHECK-NEXT: { +// CHECK: "commands": [ +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[CPP_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "casfs-root-id": "[[CPP_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "clang-context-hash": "{{.*}}" +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}} +// CHECK-NEXT: "module-name": "Mod" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-fs" +// CHECK-NEXT: "[[CPP_ROOT_ID]]" +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/tu.i" +// CHECK-NOT: "-fcas-input-file-cache-key" +// CHECK: "-E" +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "[[PREFIX]]/modules/{{.*}}/Mod-{{.*}}.pcm" +// CHECK-NEXT: "[[M_CACHE_KEY]]" +// CHECK: "-x" +// CHECK-NEXT: "c" +// CHECK: "[[PREFIX]]/tu.c" +// CHECK: "-fmodule-file={{.*}}[[PREFIX]]/modules/{{.*}}/Mod-{{.*}}.pcm" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/tu.c" +// CHECK-NEXT: ] +// CHECK: "input-file": "[[PREFIX]]/tu.c" +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[COMPILER_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "casfs-root-id": "{{.*}}" +// CHECK-NEXT: "clang-context-hash": "{{.*}}" +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}} +// CHECK-NEXT: "module-name": "Mod" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/tu.bc" +// CHECK: "-fcas-input-file-cache-key" +// CHECK-NEXT: "[[CPP_CACHE_KEY]]" +// CHECK: "-emit-llvm-bc" +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "[[PREFIX]]/modules/{{.*}}/Mod-{{.*}}.pcm" +// CHECK-NEXT: "[[M_CACHE_KEY]]" +// CHECK: "-x" +// CHECK-NEXT: "c-cpp-output" +// CHECK-NOT: "{{.*}}tu.i" +// CHECK: "-fmodule-file={{.*}}[[PREFIX]]/modules/{{.*}}/Mod-{{.*}}.pcm" +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]{{.}}tu.c" +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[BACKEND_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "casfs-root-id": "{{.*}}" +// CHECK-NEXT: "clang-context-hash": "{{.*}}" +// FIXME: This should be empty. +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}} +// CHECK-NEXT: "module-name": "Mod" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/tu.s" +// CHECK: "-fcas-input-file-cache-key" +// CHECK-NEXT: "[[COMPILER_CACHE_KEY]]" +// CHECK: "-S" +// CHECK: "-x" +// CHECK-NEXT: "ir" +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]{{.}}tu.c" +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "casfs-root-id": "{{.*}}" +// CHECK-NEXT: "clang-context-hash": "{{.*}}" +// FIXME: This should be empty. +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK: "module-name": "Mod" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-cc1as" +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/tu.o" +// FIXME: The integrated assembler should support caching too. +// CHECK: "[[PREFIX]]/tu.s" +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/tu.c" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] + +// CHECK-LIBCLANG: modules: +// CHECK-LIBCLANG-NEXT: module: +// CHECK-LIBCLANG-NEXT: name: Mod +// CHECK-LIBCLANG-NEXT: context-hash: {{.*}} +// CHECK-LIBCLANG-NEXT: cwd-ignored: 0 +// CHECK-LIBCLANG-NEXT: module-map-path: [[PREFIX]]/module.modulemap +// CHECK-LIBCLANG-NEXT: casfs-root-id: [[M_ROOT_ID:llvmcas://[[:xdigit:]]+]] +// CHECK-LIBCLANG-NEXT: cache-key: [[M_CACHE_KEY:llvmcas://[[:xdigit:]]+]] +// CHECK-LIBCLANG-NEXT: module-deps: +// CHECK-LIBCLANG-NEXT: file-deps: +// CHECK-LIBCLANG-NEXT: [[PREFIX]]/module.modulemap +// CHECK-LIBCLANG-NEXT: [[PREFIX]]/module.h +// CHECK-LIBCLANG-NEXT: build-args: -cc1 {{.*}} -fcas-fs [[M_ROOT_ID]] +// CHECK-LIBCLANG-NEXT: dependencies: +// CHECK-LIBCLANG-NEXT: command 0: +// CHECK-LIBCLANG-NEXT: context-hash: {{.*}} +// CHECK-LIBCLANG-NEXT: casfs-root-id: [[CPP_ROOT_ID:llvmcas://[[:xdigit:]]+]] +// CHECK-LIBCLANG-NEXT: cache-key: [[CPP_CACHE_KEY:llvmcas://[[:xdigit:]]+]] +// CHECK-LIBCLANG-NEXT: module-deps: +// CHECK-LIBCLANG-NEXT: Mod:{{.*}} +// CHECK-LIBCLANG-NEXT: file-deps: +// CHECK-LIBCLANG-NEXT: [[PREFIX]]/tu.c +// CHECK-LIBCLANG-NOT: -fcas-input-file-cache-key +// CHECK-LIBCLANG-NOT: {{.*}}tu.c +// CHECK-LIBCLANG-NEXT: build-args: -cc1 {{.*}} -o [[PREFIX]]/tu.i {{.*}} -E -fmodule-file-cache-key {{.*}} [[M_CACHE_KEY]] -x c {{.*}} -fmodule-file={{.*}}[[PREFIX]]/modules/Mod_{{.*}}.pcm +// CHECK-LIBCLANG-NEXT: command 1: +// CHECK-LIBCLANG-NEXT: context-hash: {{.*}} +// FIXME: This should be empty. +// CHECK-LIBCLANG-NEXT: casfs-root-id: {{.*}} +// CHECK-LIBCLANG-NEXT: cache-key: [[COMPILER_CACHE_KEY:llvmcas://[[:xdigit:]]+]] +// CHECK-LIBCLANG-NEXT: module-deps: +// CHECK-LIBCLANG-NEXT: Mod:{{.*}} +// CHECK-LIBCLANG-NEXT: file-deps: +// CHECK-LIBCLANG-NEXT: [[PREFIX]]/tu.c +// CHECK-LIBCLANG-NOT: -fcas-fs +// CHECK-LIBCLANG-NOT: {{.*}}tu.i +// CHECK-LIBCLANG-NEXT: build-args: -cc1 {{.*}} -o [[PREFIX]]/tu.bc {{.*}} -fcas-input-file-cache-key [[CPP_CACHE_KEY]] {{.*}} -emit-llvm-bc -fmodule-file-cache-key {{.*}} [[M_CACHE_KEY]] -x c-cpp-output {{.*}} -fmodule-file={{.*}}[[PREFIX]]/modules/Mod_{{.*}}.pcm +// CHECK-LIBCLANG-NEXT: command 2: +// CHECK-LIBCLANG-NEXT: context-hash: {{.*}} +// FIXME: This should be empty. +// CHECK-LIBCLANG-NEXT: casfs-root-id: {{.*}} +// CHECK-LIBCLANG-NEXT: cache-key: [[BACKEND_CACHE_KEY:llvmcas://[[:xdigit:]]+]] +// FIXME: This should be empty. +// CHECK-LIBCLANG-NEXT: module-deps: +// CHECK-LIBCLANG-NEXT: Mod:{{.*}} +// CHECK-LIBCLANG-NEXT: file-deps: +// CHECK-LIBCLANG-NEXT: [[PREFIX]]/tu.c +// CHECK-LIBCLANG-NEXT: build-args: -cc1 {{.*}} -o [[PREFIX]]/tu.s {{.*}} -fcas-input-file-cache-key [[COMPILER_CACHE_KEY]] {{.*}} -S -x ir +// CHECK-LIBCLANG-NEXT: command 3: +// CHECK-LIBCLANG-NEXT: context-hash: {{.*}} +// FIXME: This should be empty. +// CHECK-LIBCLANG-NEXT: casfs-root-id: {{.*}} +// FIXME: This should be empty. +// CHECK-LIBCLANG-NEXT: module-deps: +// CHECK-LIBCLANG-NEXT: Mod:{{.*}} +// CHECK-LIBCLANG-NEXT: file-deps: +// CHECK-LIBCLANG-NEXT: [[PREFIX]]/tu.c +// FIXME: The integrated assembler should support caching too. +// CHECK-LIBCLANG-NEXT: build-args: -cc1as {{.*}} -o [[PREFIX]]/tu.o [[PREFIX]]/tu.s + +//--- module.h +void bar(void); + +//--- module.modulemap +module Mod { header "module.h" } + +//--- tu_define_foo_0.c +#include "module.h" +#define FOO 0 +void tu_save_temps(void) { bar(); } + +//--- tu_define_foo_1.c +#include "module.h" +#define FOO 1 +void tu_save_temps(void) { bar(); } diff --git a/clang/test/ClangScanDeps/cas-fs-prefix-mapping.c b/clang/test/ClangScanDeps/cas-fs-prefix-mapping.c new file mode 100644 index 0000000000000..3f3c6dfbe6857 --- /dev/null +++ b/clang/test/ClangScanDeps/cas-fs-prefix-mapping.c @@ -0,0 +1,97 @@ +// Test path prefix-mapping when using a cas-fs with clang-scan-deps in +// tree, full-tree, and full dependencies modes. + +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%t|g" -e "s|CLANG|%clang|g" -e "s|SDK|%S/Inputs/SDK|g" %t/cdb.json.template > %t/cdb.json + +// == Tree +// Ensure the filesystem has the mapped paths. + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-tree -cas-path %t/cas \ +// RUN: -prefix-map=%t=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: | sed -E 's/tree ([^ ]+) for.*/\1/' > %t/tree_id.txt +// RUN: llvm-cas -cas %t/cas -ls-tree-recursive @%t/tree_id.txt > %t/tree_result.txt +// RUN: FileCheck %s -input-file %t/tree_result.txt -check-prefix=FILES + +// FILES: file llvmcas://{{.*}} /^sdk/usr/include/stdlib.h +// FILES: file llvmcas://{{.*}} /^src/t.c +// FILES: file llvmcas://{{.*}} /^src/top.h +// FILES: file llvmcas://{{.*}} /^tc/lib/clang/{{.*}}/include/stdarg.h + +// == Full Tree +// This should have the same filesystem as above, and we also check the command- +// line. + +// RUN: cat %t/tree_id.txt > %t/full_tree_result.txt +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-tree-full -cas-path %t/cas \ +// RUN: -prefix-map=%t=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: >> %t/full_tree_result.txt +// RUN: FileCheck %s -input-file %t/full_tree_result.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK + +// == Full +// Same as full tree. + +// RUN: cat %t/tree_id.txt > %t/full_result.txt +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full -cas-path %t/cas \ +// RUN: -prefix-map=%t=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: >> %t/full_result.txt +// RUN: FileCheck %s -input-file %t/full_result.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK + +// CHECK: [[MAPPED_FS_ID:llvmcas://[[:xdigit:]]+]] +// CHECK: "modules": [] +// CHECK: "translation-units": [ +// CHECK: { +// CHECK: "commands": [ +// CHECK: { +// CHECK: "casfs-root-id": "[[MAPPED_FS_ID]]" +// CHECK: "clang-module-deps": [] +// CHECK: "command-line": [ +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK: "-fcas-fs" +// CHECK-NEXT: "[[MAPPED_FS_ID]]" +// CHECK: "-fcas-fs-working-directory" +// CHECK-NEXT: "/^src" +// CHECK: "-x" +// CHECK-NEXT: "c" +// CHECK-NEXT: "/^src/t.c" +// CHECK: "-isysroot" +// CHECK-NEXT: "/^sdk" +// CHECK: "-resource-dir" +// CHECK-NEXT: "/^tc/lib/clang/{{.*}}" +// CHECK: "-isystem" +// CHECK-NEXT: "/^sdk/usr/local/include +// CHECK: "-isystem" +// CHECK-NEXT: "/^tc/lib/clang/{{.*}}/include" +// CHECK: "-internal-externc-isystem" +// CHECK-NEXT: "/^sdk/usr/include" +// CHECK: "-fdebug-compilation-dir=/^src" +// CHECK: "-fcoverage-compilation-dir=/^src" +// CHECK-NOT: [[PREFIX]] +// CHECK-NOT: [[SDK_PREFIX]] +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK: "[[PREFIX]]/t.c" +// CHECK: "[[PREFIX]]/top.h" +// CHECK: "{{.*}}include/stdarg.h" +// CHECK: "[[SDK_PREFIX]]/usr/include/stdlib.h" +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/t.c" + +//--- cdb.json.template +[ + { + "directory": "DIR", + "command": "CLANG -fsyntax-only DIR/t.c -target x86_64-apple-macos11 -isysroot SDK", + "file": "DIR/t.c" + } +] + +//--- t.c +#include "top.h" + +//--- top.h +#include +#include diff --git a/clang/test/ClangScanDeps/cas-trees.c b/clang/test/ClangScanDeps/cas-trees.c new file mode 100644 index 0000000000000..8c5af789f4fc5 --- /dev/null +++ b/clang/test/ClangScanDeps/cas-trees.c @@ -0,0 +1,143 @@ +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -cas-path %t/cas -format experimental-tree -mode preprocess-dependency-directives > %t/result1.txt +// RUN: clang-scan-deps -compilation-database %t/cdb.json -cas-path %t/cas -format experimental-tree -mode preprocess > %t/result2.txt +// RUN: diff -u %t/result1.txt %t/result2.txt +// RUN: FileCheck %s -input-file %t/result1.txt -DPREFIX=%/t + +// CHECK: tree {{.*}} for '[[PREFIX]]/t1.c' +// CHECK-NEXT: tree {{.*}} for '[[PREFIX]]/t2.c' + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -cas-path %t/cas -format experimental-tree-full -mode preprocess > %t/full_result.json +// RUN: cat %t/full_result.json | FileCheck %s -DPREFIX=%/t --check-prefix=FULL-TREE + +// FULL-TREE: { +// FULL-TREE-NEXT: "modules": [], +// FULL-TREE-NEXT: "translation-units": [ +// FULL-TREE-NEXT: { +// FULL-TREE: "cache-key": "[[T1_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// FULL-TREE-NEXT: "casfs-root-id": "[[T1_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// FULL-TREE-NEXT: "clang-context-hash": "{{[A-Z0-9]+}}", +// FULL-TREE-NEXT: "clang-module-deps": [], +// FULL-TREE-NEXT: "command-line": [ +// FULL-TREE: "-fcas-path" +// FULL-TREE-NEXT: "[[PREFIX]]{{.}}cas" +// FULL-TREE: "-fcas-fs" +// FULL-TREE-NEXT: "[[T1_ROOT_ID]]" +// FULL-TREE: "-fcache-compile-job" +// FULL-TREE: ], +// FULL-TREE: "file-deps": [ +// FULL-TREE-NEXT: "[[PREFIX]]/t1.c", +// FULL-TREE-NEXT: "[[PREFIX]]/top.h", +// FULL-TREE-NEXT: "[[PREFIX]]/n1.h" +// FULL-TREE-NEXT: ], +// FULL-TREE-NEXT: "input-file": "[[PREFIX]]/t1.c" +// FULL-TREE-NEXT: } +// FULL-TREE: { +// FULL-TREE: "cache-key": "[[T2_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// FULL-TREE-NEXT: "casfs-root-id": "[[T2_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// FULL-TREE-NEXT: "clang-context-hash": "{{[A-Z0-9]+}}", +// FULL-TREE-NEXT: "clang-module-deps": [], +// FULL-TREE-NEXT: "command-line": [ +// FULL-TREE: "-fcas-path" +// FULL-TREE-NEXT: "[[PREFIX]]{{.}}cas" +// FULL-TREE: "-fcas-fs" +// FULL-TREE-NEXT: "[[T2_ROOT_ID]]" +// FULL-TREE: "-fcache-compile-job" +// FULL-TREE: ], +// FULL-TREE: "file-deps": [ +// FULL-TREE-NEXT: "[[PREFIX]]/t2.c", +// FULL-TREE-NEXT: "[[PREFIX]]/n1.h" +// FULL-TREE-NEXT: ], +// FULL-TREE-NEXT: "input-file": "[[PREFIX]]/t2.c" +// FULL-TREE-NEXT: } + +// Build with caching +// RUN: %deps-to-rsp %t/full_result.json --tu-index 0 > %t/t1.cc1.rsp +// RUN: %deps-to-rsp %t/full_result.json --tu-index 1 > %t/t2.cc1.rsp +// RUN: %clang @%t/t1.cc1.rsp -Rcompile-job-cache 2> %t/t1-miss.err +// RUN: FileCheck %s -input-file=%t/t1-miss.err -check-prefix=CACHE-MISS +// RUN: %clang @%t/t1.cc1.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: %clang @%t/t2.cc1.rsp -Rcompile-job-cache 2> %t/t2-miss.err +// RUN: FileCheck %s -input-file=%t/t2-miss.err -check-prefix=CACHE-MISS +// RUN: %clang @%t/t2.cc1.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// CACHE-HIT: remark: compile job cache hit +// CACHE-MISS: remark: compile job cache miss + +// Check cache keys. +// RUN: cp %t/full_result.json %t/combined.txt +// RUN: cat %t/t1-miss.err >> %t/combined.txt +// RUN: cat %t/t2-miss.err >> %t/combined.txt +// RUN: FileCheck %s -input-file=%t/combined.txt -check-prefix=COMBINED + +// COMBINED: "commands": [ +// COMBINED-NEXT: { +// COMBINED-NEXT: "cache-key": "[[T1_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// COMBINED: } +// COMBINED: "commands": [ +// COMBINED: { +// COMBINED-NEXT: "cache-key": "[[T2_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// COMBINED: remark: compile job cache miss for '[[T1_CACHE_KEY]]' +// COMBINED-NEXT: remark: compile job cache miss for '[[T2_CACHE_KEY]]' + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -cas-path %t/cas -format experimental-tree -emit-cas-compdb | FileCheck %s -DPREFIX=%/t -DCLANG=%clang -check-prefix=COMPDB +// COMPDB: [ +// COMPDB: { +// COMPDB: "file": "[[PREFIX]]/t1.c", +// COMPDB: "directory": "[[PREFIX]]", +// COMPDB: "arguments": [ +// COMPDB: "[[CLANG]]", +// COMPDB: "-cc1", +// COMPDB: "-fcas-path", +// COMPDB: "[[PREFIX]]/cas", +// COMPDB: "-fcas-fs", +// COMPDB: { +// COMPDB: "file": "[[PREFIX]]/t2.c", +// COMPDB: "directory": "[[PREFIX]]", +// COMPDB: "arguments": [ + + +//--- cdb.json.template +[ + { + "directory": "DIR", + "command": "clang -fsyntax-only DIR/t1.c", + "file": "DIR/t1.c" + }, + { + "directory": "DIR", + "command": "clang -fsyntax-only DIR/t2.c", + "file": "DIR/t2.c" + } +] + +//--- t1.c +#include "top.h" +#include "n1.h" + +//--- t2.c +#include "n1.h" + +//--- top.h +#ifndef _TOP_H_ +#define _TOP_H_ + +#define WHATEVER 1 +#include "n1.h" + +struct S { + int x; +}; + +#endif + +//--- n1.h +#ifndef _N1_H_ +#define _N1_H_ + +int x1; + +#endif diff --git a/clang/test/ClangScanDeps/cwd-c-api.c b/clang/test/ClangScanDeps/cwd-c-api.c new file mode 100644 index 0000000000000..c1ecca6e8d426 --- /dev/null +++ b/clang/test/ClangScanDeps/cwd-c-api.c @@ -0,0 +1,42 @@ +// Test the current working directory C APIs. + +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: c-index-test core -scan-deps -working-dir %S -- %clang \ +// RUN: -c %t/main.c -fmodules -fmodules-cache-path=%t/module-cache \ +// RUN: 2>&1 > %t/no_cwd_opt.txt +// RUN: cat %t/no_cwd_opt.txt | FileCheck %s --check-prefix=NO-CWD-OPT + + +// RUN: c-index-test core -scan-deps -working-dir %S -optimize-cwd -- \ +// RUN: %clang \ +// RUN: -c %t/main.c -fmodules -fmodules-cache-path=%t/module-cache \ +// RUN: 2>&1 > %t/cwd_opt.txt +// RUN: cat %t/cwd_opt.txt | FileCheck %s --check-prefix=CWD-OPT + +//--- module.modulemap +module Mod { header "Mod.h" } + +//--- Mod.h +int foo(); + +//--- main.c +#include "Mod.h" + +int main() { + return foo(); +} + +// NO-CWD-OPT: modules: +// NO-CWD-OPT-NEXT: module: +// NO-CWD-OPT-NEXT: name: Mod +// NO-CWD-OPT-NEXT: context-hash:{{.*}} +// NO-CWD-OPT-NEXT: cwd-ignored: 0 + + +// CWD-OPT: modules: +// CWD-OPT-NEXT: module: +// CWD-OPT-NEXT: name: Mod +// CWD-OPT-NEXT: context-hash:{{.*}} +// CWD-OPT-NEXT: cwd-ignored: 1 diff --git a/clang/test/ClangScanDeps/error-c-api.cpp b/clang/test/ClangScanDeps/error-c-api.cpp new file mode 100644 index 0000000000000..b40319fcb6313 --- /dev/null +++ b/clang/test/ClangScanDeps/error-c-api.cpp @@ -0,0 +1,6 @@ +// RUN: not c-index-test core -scan-deps -working-dir %S -- clang_tool %s -I %S/Inputs 2>&1 | FileCheck %s + +#include "missing.h" + +// CHECK: error: failed to get dependencies +// CHECK-NEXT: 'missing.h' file not found diff --git a/clang/test/ClangScanDeps/flags-c-api.cpp b/clang/test/ClangScanDeps/flags-c-api.cpp new file mode 100644 index 0000000000000..55bd3c8b36212 --- /dev/null +++ b/clang/test/ClangScanDeps/flags-c-api.cpp @@ -0,0 +1,7 @@ +// RUN: c-index-test core -scan-deps -working-dir %S -- clang_tool %s -I %S/Inputs | FileCheck %s + +#include "header.h" + +// CHECK: file-deps: +// CHECK-NEXT: flags-c-api.cpp +// CHECK-NEXT: Inputs/header.h diff --git a/clang/test/ClangScanDeps/include-tree-multiple-commands.c b/clang/test/ClangScanDeps/include-tree-multiple-commands.c new file mode 100644 index 0000000000000..9fd17903fada3 --- /dev/null +++ b/clang/test/ClangScanDeps/include-tree-multiple-commands.c @@ -0,0 +1,278 @@ +// Test scanning when the driver requires multiple jobs. E.g. with -save-temps +// there will be separate -E, -emit-llvm-bc, -S, and -cc1as jobs, which should +// each result in a "command" in the output. + +// REQUIRES: ondisk_cas + +// We use an x86_64-apple-darwin target to avoid host-dependent behaviour in +// the driver. Platforms without an integrated assembler have different commands +// REQUIRES: x86-registered-target + +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: clang-scan-deps -format experimental-include-tree-full -cas-path %t/cas -module-files-dir %t/modules \ +// RUN: -- %clang -target x86_64-apple-darwin -c %t/src0/tu.c -save-temps=obj -o %t/dst0/tu.o -I %t/include \ +// RUN: -fdepscan-prefix-map=%t/src0=^src -fdepscan-prefix-map=%t/include=^include \ +// RUN: -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=%t/cache \ +// RUN: > %t/deps.0.json + +// RUN: cat %t/deps.0.json | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t -DSRC=%/t/src0 -DDST=%/t/dst0 + +// RUN: c-index-test core -scan-deps -working-dir %t -cas-path %t/cas -output-dir %t/modules -- \ +// RUN: %clang -target x86_64-apple-darwin -c %t/src0/tu.c -save-temps=obj -o %t/dst0/tu.o -I %t/include \ +// RUN: -fdepscan-prefix-map=%t/src0=^srcx \ +// RUN: -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=%t/cache \ +// RUN: > %t/deps.txt + +// RUN: cat %t/deps.txt | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t -DSRC=%/t/src0 -DDST=%/t/dst0 -check-prefix=CHECK-LIBCLANG + +// RUN: mkdir %t/dst0 +// RUN: %deps-to-rsp %t/deps.0.json --module-name=Mod > %t/Mod.0.rsp +// RUN: %deps-to-rsp %t/deps.0.json --tu-index 0 --tu-cmd-index 0 > %t/tu-cpp.0.rsp +// RUN: %deps-to-rsp %t/deps.0.json --tu-index 0 --tu-cmd-index 1 > %t/tu-emit-ir.0.rsp +// RUN: %deps-to-rsp %t/deps.0.json --tu-index 0 --tu-cmd-index 2 > %t/tu-emit-asm.0.rsp +// RUN: %deps-to-rsp %t/deps.0.json --tu-index 0 --tu-cmd-index 3 > %t/tu-cc1as.0.rsp +// RUN: %clang @%t/Mod.0.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu-cpp.0.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu-emit-ir.0.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu-emit-asm.0.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu-cc1as.0.rsp + +// RUN: clang-scan-deps -format experimental-include-tree-full -cas-path %t/cas -module-files-dir %t/modules \ +// RUN: -- %clang -target x86_64-apple-darwin -c %t/src1/tu.c -save-temps=obj -o %t/dst1/tu.o -I %t/include \ +// RUN: -fdepscan-prefix-map=%t/src1=^src -fdepscan-prefix-map=%t/include=^include \ +// RUN: -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=%t/cache \ +// RUN: > %t/deps.1.json + +// The dependency graph has identical structure, just the include-tree ID, dependent cache keys and prefix mappings are different. +// RUN: cat %t/deps.1.json | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t -DSRC=%/t/src1 -DDST=%/t/dst1 +// RUN: not diff %t/deps.1.json %t/deps.0.json + +// RUN: mkdir %t/dst1 +// RUN: %deps-to-rsp %t/deps.1.json --module-name=Mod > %t/Mod.1.rsp +// RUN: %deps-to-rsp %t/deps.1.json --tu-index 0 --tu-cmd-index 0 > %t/tu-cpp.1.rsp +// RUN: %deps-to-rsp %t/deps.1.json --tu-index 0 --tu-cmd-index 1 > %t/tu-emit-ir.1.rsp +// RUN: %deps-to-rsp %t/deps.1.json --tu-index 0 --tu-cmd-index 2 > %t/tu-emit-asm.1.rsp +// RUN: %deps-to-rsp %t/deps.1.json --tu-index 0 --tu-cmd-index 3 > %t/tu-cc1as.1.rsp +// RUN: %clang @%t/Mod.1.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: %clang @%t/tu-cpp.1.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu-emit-ir.1.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: %clang @%t/tu-emit-asm.1.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: %clang @%t/tu-cc1as.1.rsp + +// RUN: diff %t/dst1/tu.i %t/dst0/tu.i +// RUN: diff %t/dst1/tu.bc %t/dst0/tu.bc +// RUN: diff %t/dst1/tu.s %t/dst0/tu.s +// RUN: diff %t/dst1/tu.o %t/dst0/tu.o + +// CACHE-HIT: remark: compile job cache hit +// CACHE-MISS: remark: compile job cache miss + +// CHECK: "modules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[M_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "cas-include-tree-id": "[[M_INCLUDE_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "clang-module-deps": [] +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/include/module.modulemap" +// CHECK-NEXT: "command-line": [ +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[M_INCLUDE_TREE]]" +// CHECK: ] +// CHECK: "name": "Mod" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: "translation-units": [ +// CHECK-NEXT: { +// CHECK: "commands": [ +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[CPP_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "cas-include-tree-id": "[[CPP_INCLUDE_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "clang-context-hash": "{{.*}}" +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}} +// CHECK-NEXT: "module-name": "Mod" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-o" +// CHECK-NEXT: "[[DST]]/tu.i" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[CPP_INCLUDE_TREE]]" +// CHECK-NOT: "-fcas-input-file-cache-key" +// CHECK: "-E" +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "[[PREFIX]]/modules/{{.*}}/Mod-{{.*}}.pcm" +// CHECK-NEXT: "[[M_CACHE_KEY]]" +// CHECK: "-x" +// CHECK-NEXT: "c" +// CHECK-NOT: "{{.*}}tu.c" +// CHECK: "-fmodule-file={{.*}}[[PREFIX]]/modules/{{.*}}/Mod-{{.*}}.pcm" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[SRC]]/tu.c" +// CHECK-NEXT: "[[SRC]]/header.h" +// CHECK-NEXT: ] +// CHECK: "input-file": "[[SRC]]/tu.c" +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[COMPILER_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// FIXME: This should be empty. +// CHECK-NEXT: "cas-include-tree-id": "{{.*}}" +// CHECK-NEXT: "clang-context-hash": "{{.*}}" +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}} +// CHECK-NEXT: "module-name": "Mod" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-o" +// CHECK-NEXT: "[[DST]]/tu.bc" +// CHECK-NOT: "-fcas-include-tree" +// CHECK: "-fcas-input-file-cache-key" +// CHECK-NEXT: "[[CPP_CACHE_KEY]]" +// CHECK: "-emit-llvm-bc" +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "[[PREFIX]]/modules/{{.*}}/Mod-{{.*}}.pcm" +// CHECK-NEXT: "[[M_CACHE_KEY]]" +// CHECK: "-x" +// CHECK-NEXT: "c-cpp-output" +// CHECK-NOT: "{{.*}}tu.i" +// CHECK: "-fmodule-file={{.*}}[[PREFIX]]/modules/{{.*}}/Mod-{{.*}}.pcm" +// CHECK: ] +// CHECK: "input-file": "[[SRC]]/tu.c" +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[BACKEND_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// FIXME: This should be empty. +// CHECK-NEXT: "cas-include-tree-id": "{{.*}}" +// CHECK-NEXT: "clang-context-hash": "{{.*}}" +// FIXME: This should be empty. +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}} +// CHECK-NEXT: "module-name": "Mod" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-o" +// CHECK-NEXT: "[[DST]]/tu.s" +// CHECK: "-fcas-input-file-cache-key" +// CHECK-NEXT: "[[COMPILER_CACHE_KEY]]" +// CHECK: "-S" +// CHECK: "-x" +// CHECK-NEXT: "ir" +// CHECK: ] +// CHECK: "input-file": "[[SRC]]/tu.c" +// CHECK-NEXT: } +// CHECK-NEXT: { +// FIXME: This should be empty. +// CHECK-NEXT: "cas-include-tree-id": "{{.*}}" +// CHECK-NEXT: "clang-context-hash": "{{.*}}" +// FIXME: This should be empty. +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK: "module-name": "Mod" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-cc1as" +// CHECK: "-o" +// CHECK-NEXT: "[[DST]]/tu.o" +// FIXME: The integrated assembler should support caching too. +// CHECK: "[[DST]]/tu.s" +// CHECK: ] +// CHECK: "input-file": "[[SRC]]/tu.c" +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] + +// CHECK-LIBCLANG: modules: +// CHECK-LIBCLANG-NEXT: module: +// CHECK-LIBCLANG-NEXT: name: Mod +// CHECK-LIBCLANG-NEXT: context-hash: {{.*}} +// CHECK-LIBCLANG-NEXT: cwd-ignored: 0 +// CHECK-LIBCLANG-NEXT: module-map-path: [[PREFIX]]/include/module.modulemap +// CHECK-LIBCLANG-NEXT: include-tree-id: [[M_INCLUDE_TREE:llvmcas://[[:xdigit:]]+]] +// CHECK-LIBCLANG-NEXT: cache-key: [[M_CACHE_KEY:llvmcas://[[:xdigit:]]+]] +// CHECK-LIBCLANG-NEXT: module-deps: +// CHECK-LIBCLANG-NEXT: file-deps: +// CHECK-LIBCLANG-NEXT: [[PREFIX]]/include/module.modulemap +// CHECK-LIBCLANG-NEXT: [[PREFIX]]/include/module.h +// CHECK-LIBCLANG-NEXT: build-args: -cc1 {{.*}} -fcas-include-tree [[M_INCLUDE_TREE]] +// CHECK-LIBCLANG-NEXT: dependencies: +// CHECK-LIBCLANG-NEXT: command 0: +// CHECK-LIBCLANG-NEXT: context-hash: {{.*}} +// CHECK-LIBCLANG-NEXT: include-tree-id: [[CPP_INCLUDE_TREE:llvmcas://[[:xdigit:]]+]] +// CHECK-LIBCLANG-NEXT: cache-key: [[CPP_CACHE_KEY:llvmcas://[[:xdigit:]]+]] +// CHECK-LIBCLANG-NEXT: module-deps: +// CHECK-LIBCLANG-NEXT: Mod:{{.*}} +// CHECK-LIBCLANG-NEXT: file-deps: +// CHECK-LIBCLANG-NEXT: [[SRC]]/tu.c +// CHECK-LIBCLANG-NEXT: [[SRC]]/header.h +// CHECK-LIBCLANG-NOT: -fcas-input-file-cache-key +// CHECK-LIBCLANG-NOT: {{.*}}tu.c +// CHECK-LIBCLANG-NEXT: build-args: -cc1 {{.*}} -o [[DST]]/tu.i {{.*}} -E -fmodule-file-cache-key {{.*}} [[M_CACHE_KEY]] -x c {{.*}} -fmodule-file={{.*}}[[PREFIX]]/modules/Mod_{{.*}}.pcm +// CHECK-LIBCLANG-NEXT: command 1: +// CHECK-LIBCLANG-NEXT: context-hash: {{.*}} +// FIXME: This should be empty. +// CHECK-LIBCLANG-NEXT: include-tree-id: {{.*}} +// CHECK-LIBCLANG-NEXT: cache-key: [[COMPILER_CACHE_KEY:llvmcas://[[:xdigit:]]+]] +// CHECK-LIBCLANG-NEXT: module-deps: +// CHECK-LIBCLANG-NEXT: Mod:{{.*}} +// CHECK-LIBCLANG-NEXT: file-deps: +// CHECK-LIBCLANG-NEXT: [[SRC]]/tu.c +// CHECK-LIBCLANG-NEXT: [[SRC]]/header.h +// CHECK-LIBCLANG-NOT: -fcas-include-tree +// CHECK-LIBCLANG-NOT: {{.*}}tu.i +// CHECK-LIBCLANG-NEXT: build-args: -cc1 {{.*}} -o [[DST]]/tu.bc {{.*}} -fcas-input-file-cache-key [[CPP_CACHE_KEY]] {{.*}} -emit-llvm-bc -fmodule-file-cache-key {{.*}} [[M_CACHE_KEY]] -x c-cpp-output {{.*}} -fmodule-file={{.*}}[[PREFIX]]/modules/Mod_{{.*}}.pcm +// CHECK-LIBCLANG-NEXT: command 2: +// CHECK-LIBCLANG-NEXT: context-hash: {{.*}} +// FIXME: This should be empty. +// CHECK-LIBCLANG-NEXT: include-tree-id: {{.*}} +// CHECK-LIBCLANG-NEXT: cache-key: [[BACKEND_CACHE_KEY:llvmcas://[[:xdigit:]]+]] +// FIXME: This should be empty. +// CHECK-LIBCLANG-NEXT: module-deps: +// CHECK-LIBCLANG-NEXT: Mod:{{.*}} +// CHECK-LIBCLANG-NEXT: file-deps: +// CHECK-LIBCLANG-NEXT: [[SRC]]/tu.c +// CHECK-LIBCLANG-NEXT: [[SRC]]/header.h +// CHECK-LIBCLANG-NEXT: build-args: -cc1 {{.*}} -o [[DST]]/tu.s {{.*}} -fcas-input-file-cache-key [[COMPILER_CACHE_KEY]] {{.*}} -S -x ir +// CHECK-LIBCLANG-NEXT: command 3: +// CHECK-LIBCLANG-NEXT: context-hash: {{.*}} +// FIXME: This should be empty. +// CHECK-LIBCLANG-NEXT: include-tree-id: {{.*}} +// FIXME: This should be empty. +// CHECK-LIBCLANG-NEXT: module-deps: +// CHECK-LIBCLANG-NEXT: Mod:{{.*}} +// CHECK-LIBCLANG-NEXT: file-deps: +// CHECK-LIBCLANG-NEXT: [[SRC]]/tu.c +// CHECK-LIBCLANG-NEXT: [[SRC]]/header.h +// FIXME: The integrated assembler should support caching too. +// CHECK-LIBCLANG-NEXT: build-args: -cc1as {{.*}} -o [[DST]]/tu.o [[DST]]/tu.s + +//--- include/module.h +void bar(void); + +//--- include/module.modulemap +module Mod { header "module.h" } + +//--- src0/header.h +//--- src0/tu.c +#include "module.h" +#include "header.h" +#define FOO 0 +void tu_save_temps(void) { bar(); } + +//--- src1/header.h +//--- src1/tu.c +#include "module.h" +#include "header.h" +#define FOO 1 +void tu_save_temps(void) { bar(); } diff --git a/clang/test/ClangScanDeps/include-tree-pragma-system-header.c b/clang/test/ClangScanDeps/include-tree-pragma-system-header.c new file mode 100644 index 0000000000000..8b7b1d7c46d4b --- /dev/null +++ b/clang/test/ClangScanDeps/include-tree-pragma-system-header.c @@ -0,0 +1,40 @@ +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -cas-path %t/cas -format experimental-include-tree-full \ +// RUN: -compilation-database %t/cdb.json > %t/deps.json +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp + +// Confirm we match the non-include-tree build: +// RUN: %clang -fsyntax-only -Wextra-semi %t/tu.c 2>&1 | FileCheck %s +// RUN: %clang @%t/tu.rsp 2>&1 | FileCheck %s + +// CHECK: sys.h:1:7: warning: extra ';' +// CHECK-NOT: warning: extra ';' +// CHECK: tu.c:{{.*}}: warning: extra ';' + +//--- cdb.json.template +[{ + "directory": "DIR", + "command": "clang -fsyntax-only -Wextra-semi DIR/tu.c", + "file": "DIR/tu.c" +}] + +//--- sys.h +int x;; +#pragma clang system_header +int y;; +#include "other.h" + +//--- other.h +int z;; + +//--- tu.c +#include "sys.h" +int w;; + +int main() { + return x + y + z; +} \ No newline at end of file diff --git a/clang/test/ClangScanDeps/include-tree-prefix-mapping-pch-remap.c b/clang/test/ClangScanDeps/include-tree-prefix-mapping-pch-remap.c new file mode 100644 index 0000000000000..8c379ef60014d --- /dev/null +++ b/clang/test/ClangScanDeps/include-tree-prefix-mapping-pch-remap.c @@ -0,0 +1,22 @@ +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -format experimental-include-tree-full -cas-path %t/cas \ +// RUN: -prefix-map=%t=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc > %t/deps.json + +//--- cdb.json.template +[{ + "file": "tu.c", + "directory": "DIR", + "command": "clang DIR/tu.c -fsyntax-only -include DIR/prefix.h" +}] + +//--- prefix.h +// Note: this is a bit hacky, but we rely on the fact that dep directives lexing +// will not see #error during the scan. +#error "failed to find dependency directives" + +//--- tu.c diff --git a/clang/test/ClangScanDeps/include-tree-prefix-mapping.c b/clang/test/ClangScanDeps/include-tree-prefix-mapping.c new file mode 100644 index 0000000000000..e0842a7be6b98 --- /dev/null +++ b/clang/test/ClangScanDeps/include-tree-prefix-mapping.c @@ -0,0 +1,85 @@ +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%t|g" -e "s|CLANG|%clang|g" -e "s|SDK|%S/Inputs/SDK|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-include-tree -cas-path %t/cas \ +// RUN: -prefix-map=%t=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc > %t/result.txt +// RUN: FileCheck %s -input-file %t/result.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK + +// CHECK: {{.*}} - [[PREFIX]]/t.c +// CHECK-NOT: [[PREFIX]] +// CHECK-NOT: [[SDK_PREFIX]] +// CHECK: /^src{{[/\\]}}t.c +// CHECK: /^src{{[/\\]}}top.h +// CHECK: /^tc{{[/\\]}}lib{{[/\\]}}clang{{[/\\]}}{{.*}}{{[/\\]}}include{{[/\\]}}stdarg.h +// CHECK: /^sdk{{[/\\]}}usr{{[/\\]}}include{{[/\\]}}stdlib.h + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -format experimental-include-tree-full -cas-path %t/cas \ +// RUN: -prefix-map=%t=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc > %t/deps.json + +// RUN: cat %t/result.txt > %t/full.txt +// RUN: echo "FULL DEPS START" >> %t/full.txt +// RUN: cat %t/deps.json | sed 's:\\\\\?:/:g' >> %t/full.txt + +// RUN: FileCheck %s -DPREFIX=%/t -DSDK_PREFIX=%S/Inputs/SDK -check-prefix=FULL -input-file %t/full.txt + +// Capture the tree id from experimental-include-tree ; ensure that it matches +// the result from experimental-full. +// FULL: [[TREE_ID:llvmcas://[[:xdigit:]]+]] - [[PREFIX]]/t.c +// FULL: FULL DEPS START + +// FULL-NEXT: { +// FULL-NEXT: "modules": [] +// FULL-NEXT: "translation-units": [ +// FULL-NEXT: { +// FULL-NEXT: "commands": [ +// FULL-NEXT: { +// FULL: "clang-module-deps": [] +// FULL: "command-line": [ +// FULL-NEXT: "-cc1" +// FULL: "-fcas-path" +// FULL-NEXT: "[[PREFIX]]/cas" +// FULL: "-disable-free" +// FULL: "-fcas-include-tree" +// FULL-NEXT: "[[TREE_ID]]" +// FULL: "-fcache-compile-job" +// FULL: "-fsyntax-only" +// FULL: "-x" +// FULL-NEXT: "c" +// FULL: "-isysroot" +// FULL-NEXT: "/^sdk" +// FULL: ] +// FULL: "file-deps": [ +// FULL-DAG: "[[PREFIX]]/t.c" +// FULL-DAG: "[[PREFIX]]/top.h" +// FULL-DAG: "{{.*}}/stdarg.h" +// FULL-DAG: "[[SDK_PREFIX]]/usr/include/stdlib.h" +// FULL: ] +// FULL: "input-file": "[[PREFIX]]/t.c" +// FULL: } +// FULL: ] +// FULL: } +// FULL: ] +// FULL: } + +// Build the include-tree command +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/tu.rsp + +//--- cdb.json.template +[ + { + "directory": "DIR", + "command": "CLANG -fsyntax-only DIR/t.c -target x86_64-apple-macos11 -isysroot SDK", + "file": "DIR/t.c" + } +] + +//--- t.c +#include "top.h" + +//--- top.h +#include +#include diff --git a/clang/test/ClangScanDeps/include-tree-preserve-pch-path.c b/clang/test/ClangScanDeps/include-tree-preserve-pch-path.c new file mode 100644 index 0000000000000..c9331dea2dc51 --- /dev/null +++ b/clang/test/ClangScanDeps/include-tree-preserve-pch-path.c @@ -0,0 +1,65 @@ +// REQUIRES: ondisk_cas + +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb_pch.json.template > %t/cdb_pch.json +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: sed -e "s|DIR|%/t|g" %t/cdb_no_preserve.json.template > %t/cdb_no_preserve.json + +// RUN: clang-scan-deps -compilation-database %t/cdb_pch.json -format experimental-include-tree-full -cas-path %t/cas > %t/deps_pch.json +// RUN: FileCheck %s -input-file %t/deps_pch.json -DPREFIX=%/t + +// CHECK: "-fmodule-format=obj" +// CHECK: "-dwarf-ext-refs" + +// RUN: %deps-to-rsp %t/deps_pch.json --tu-index 0 > %t/pch.rsp +// RUN: %clang @%t/pch.rsp + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-include-tree-full -cas-path %t/cas > %t/deps_tu.json +// RUN: FileCheck %s -input-file %t/deps_tu.json -DPREFIX=%/t + +// RUN: %deps-to-rsp %t/deps_tu.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/tu.rsp + +// RUN: cat %t/tu.ll | FileCheck %s -check-prefix=LLVMIR -DPREFIX=%/t +// LLVMIR: !DICompileUnit({{.*}}, splitDebugFilename: "prefix.pch" + +// Extract include-tree casid +// RUN: cat %t/tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid + +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid | FileCheck %s -check-prefix=INCLUDE_TREE -DPREFIX=%/t +// INCLUDE_TREE: (PCH) [[PREFIX]]/prefix.pch llvmcas:// + +// RUN: clang-scan-deps -compilation-database %t/cdb_no_preserve.json -format experimental-include-tree-full -cas-path %t/cas > %t/deps_no_preserve.json +// RUN: FileCheck %s -input-file %t/deps_no_preserve.json -DPREFIX=%/t -check-prefix=NO_PRESERVE + +// Note: "raw" is the default format, so it will not show up in the arguments. +// NO_PRESERVE-NOT: "-fmodule-format= +// NO_PRESERVE-NOT: "-dwarf-ext-refs" + + +//--- cdb_pch.json.template +[{ + "directory": "DIR", + "command": "clang -x c-header DIR/prefix.h -target x86_64-apple-macos12 -o DIR/prefix.pch -gmodules -g -Xclang -finclude-tree-preserve-pch-path", + "file": "DIR/prefix.h" +}] + +//--- cdb.json.template +[{ + "directory": "DIR", + "command": "clang -S -emit-llvm DIR/tu.c -o DIR/tu.ll -include-pch DIR/prefix.pch -target x86_64-apple-macos12 -gmodules -g -Xclang -finclude-tree-preserve-pch-path", + "file": "DIR/tu.c" +}] + +//--- cdb_no_preserve.json.template +[{ + "directory": "DIR", + "command": "clang -S -emit-llvm DIR/tu.c -o DIR/tu.ll -include-pch DIR/prefix.pch -target x86_64-apple-macos12 -gmodules -g", + "file": "DIR/tu.c" +}] + +//--- prefix.h +struct S {}; + +//--- tu.c +struct S s; diff --git a/clang/test/ClangScanDeps/include-tree-with-pch.c b/clang/test/ClangScanDeps/include-tree-with-pch.c new file mode 100644 index 0000000000000..1c6d0e2791b7e --- /dev/null +++ b/clang/test/ClangScanDeps/include-tree-with-pch.c @@ -0,0 +1,113 @@ +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: %clang -x c-header %t/prefix.h -target x86_64-apple-macos12 -o %t/prefix.pch -fdepscan=inline -fdepscan-include-tree -Xclang -fcas-path -Xclang %t/cas +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-include-tree -cas-path %t/cas > %t/result.txt +// RUN: FileCheck %s -input-file %t/result.txt -DPREFIX=%/t + +// CHECK: {{.*}} - [[PREFIX]]/t.c +// CHECK-NEXT: (PCH) +// CHECK-NEXT: [[PREFIX]]/t.c +// CHECK-NEXT: 1:1 +// CHECK-NEXT: [[PREFIX]]/t.h +// CHECK-NEXT: Files: +// CHECK-NEXT: [[PREFIX]]/t.c +// CHECK-NEXT: [[PREFIX]]/t.h +// CHECK-NEXT: [[PREFIX]]/prefix.h +// CHECK-NEXT: [[PREFIX]]/n1.h +// CHECK-NEXT: [[PREFIX]]/n2.h +// CHECK-NEXT: [[PREFIX]]/n3.h +// CHECK-NOT: [[PREFIX]] + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-include-tree-full -cas-path %t/cas > %t/deps.json + +// RUN: cat %t/result.txt > %t/full.txt +// RUN: echo "FULL DEPS START" >> %t/full.txt +// RUN: cat %t/deps.json | sed 's:\\\\\?:/:g' >> %t/full.txt + +// RUN: FileCheck %s -DPREFIX=%/t -DCLANG=%clang -check-prefix=FULL -input-file %t/full.txt + +// Capture the tree id from experimental-include-tree ; ensure that it matches +// the result from experimental-full. +// FULL: [[TREE_ID:llvmcas://[[:xdigit:]]+]] - [[PREFIX]]/t.c +// FULL: FULL DEPS START + +// FULL-NEXT: { +// FULL-NEXT: "modules": [] +// FULL-NEXT: "translation-units": [ +// FULL-NEXT: { +// FULL-NEXT: "commands": [ +// FULL-NEXT: { +// FULL: "clang-module-deps": [] +// FULL: "command-line": [ +// FULL-NEXT: "-cc1" +// FULL: "-fcas-path" +// FULL-NEXT: "[[PREFIX]]/cas" +// FULL: "-disable-free" +// FULL: "-fcas-include-tree" +// FULL-NEXT: "[[TREE_ID]]" +// FULL: "-fcache-compile-job" +// FULL: "-fsyntax-only" +// FULL: "-x" +// FULL-NEXT: "c" +// FULL-NOT: "t.c" +// FULL: "-main-file-name" +// FULL-NEXT: "t.c" +// FULL-NOT: "t.c" +// FULL: ] +// FULL: "executable": "[[CLANG]]" +// FULL: "file-deps": [ +// FULL-NEXT: "[[PREFIX]]/t.c" +// FULL-NEXT: "[[PREFIX]]/t.h" +// FULL-NEXT: "[[PREFIX]]/prefix.pch" +// FULL-NEXT: ] +// FULL: "input-file": "[[PREFIX]]/t.c" +// FULL: } +// FULL: ] +// FULL: } +// FULL: ] +// FULL: } + +// Build the include-tree command +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/tu.rsp + +//--- prefix.h +#include "n1.h" +#import "n2.h" +#include "n3.h" + +//--- n1.h +#ifndef _N1_H_ +#define _N1_H_ + +int n1 = 0; + +#endif + +//--- n2.h +int n2 = 0; + +//--- n3.h +#pragma once +int n3 = 0; + +//--- cdb.json.template +[{ + "directory" : "DIR", + "command" : "clang -fsyntax-only DIR/t.c -target x86_64-apple-macos12 -isysroot DIR -Xclang -include-pch -Xclang DIR/prefix.pch", + "file" : "DIR/t.c" +}] + +//--- t.c +#include "t.h" +// Should not include due to macro guard. +#include "n1.h" +// Should not include due to #import from 'prefix.h'. +#include "n2.h" +// Should not include due to '#pragma once' +#include "n3.h" + +//--- t.h diff --git a/clang/test/ClangScanDeps/include-tree-working-directory.c b/clang/test/ClangScanDeps/include-tree-working-directory.c new file mode 100644 index 0000000000000..9da7f609c9ce3 --- /dev/null +++ b/clang/test/ClangScanDeps/include-tree-working-directory.c @@ -0,0 +1,58 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: mkdir -p %t/other +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-include-tree-full -cas-path %t/cas \ +// RUN: > %t/deps.json + +// Build the include-tree command +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/tu.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: ls %t/t.o +// RUN: ls %t/t.d +// RUN: ls %t/t.dia + +// CACHE-MISS: remark: compile job cache miss +// CACHE-HIT: remark: compile job cache hit + +// RUN: cat %t/tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid | FileCheck %s -DPREFIX=%/t + +// CHECK: [[PREFIX]]/t.c llvmcas:// +// CHECK: 1:1 llvmcas:// +// CHECK: 2:1 [[PREFIX]]/relative/h1.h llvmcas:// +// CHECK: Files: +// CHECK: [[PREFIX]]/t.c llvmcas:// +// CHECK: [[PREFIX]]/relative/h1.h llvmcas:// + +/// Using a different working directory should cache hit as well. +/// FIXME: Working directory affects some codegen options added by clang driver, preserve them to make sure the cache hit. +// RUN: sed -e "s|DIR|%/t|g" %t/cdb2.json.template > %t/cdb2.json +// RUN: clang-scan-deps -compilation-database %t/cdb2.json -format experimental-include-tree-full -cas-path %t/cas \ +// RUN: > %t/deps2.json +// RUN: %deps-to-rsp %t/deps2.json --tu-index 0 > %t/tu2.rsp +// RUN: %clang @%t/tu2.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-HIT + + +//--- cdb.json.template +[{ + "directory": "DIR/other", + "command": "clang -c t.c -I relative -working-directory DIR -o t.o -MD -serialize-diagnostics t.dia", + "file": "DIR/t.c" +}] + +//--- cdb2.json.template +[{ + "directory": "DIR/other", + "command": "clang -c DIR/t.c -I DIR/relative -working-directory DIR/other -o DIR/t2.o -MD -serialize-diagnostics DIR/t2.dia -fdebug-compilation-dir=DIR -fcoverage-compilation-dir=DIR", + "file": "DIR/t.c" +}] + +//--- relative/h1.h + +//--- t.c +#include "h1.h" diff --git a/clang/test/ClangScanDeps/include-tree.c b/clang/test/ClangScanDeps/include-tree.c new file mode 100644 index 0000000000000..29c64fa5f9edc --- /dev/null +++ b/clang/test/ClangScanDeps/include-tree.c @@ -0,0 +1,168 @@ +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file --leading-lines %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-include-tree -cas-path %t/cas > %t/result1.txt +// Try again to ensure a pre-populated CASDB doesn't change output. +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-include-tree -cas-path %t/cas > %t/result2.txt +// RUN: diff -u %t/result1.txt %t/result2.txt +// RUN: FileCheck %s -input-file %t/result1.txt -DPREFIX=%/t + +// Make sure order is as expected. +// RUN: FileCheck %s -input-file %t/result1.txt -DPREFIX=%/t -check-prefix ORDER + +// ORDER: {{.*}} - [[PREFIX]]/t.c +// ORDER-NEXT: [[PREFIX]]/t.c +// ORDER-NEXT: 1:1 +// ORDER-NEXT: [[PREFIX]]/top.h +// ORDER-NEXT: [[PREFIX]]/n1.h +// ORDER-NEXT: [[PREFIX]]/n2.h +// ORDER-NEXT: [[PREFIX]]/n3.h +// ORDER-NEXT: [[PREFIX]]/n3.h +// ORDER-NEXT: [[PREFIX]]/n2.h +// ORDER-NEXT: Files: +// ORDER-NEXT: [[PREFIX]]/t.c +// ORDER-NEXT: [[PREFIX]]/top.h +// ORDER-NEXT: [[PREFIX]]/n1.h +// ORDER-NEXT: [[PREFIX]]/n2.h +// ORDER-NEXT: [[PREFIX]]/n3.h +// ORDER-NOT: [[PREFIX]] + +// Full dependency output +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-include-tree-full -cas-path %t/cas > %t/deps.json + +// RUN: cat %t/result1.txt > %t/full.txt +// RUN: echo "FULL DEPS START" >> %t/full.txt +// RUN: cat %t/deps.json | sed 's:\\\\\?:/:g' >> %t/full.txt + +// RUN: FileCheck %s -DPREFIX=%/t -DCLANG=%clang -check-prefix=FULL -input-file %t/full.txt + +// Capture the tree id from experimental-include-tree ; ensure that it matches +// the result from experimental-full. +// FULL: [[TREE_ID:llvmcas://[[:xdigit:]]+]] - [[PREFIX]]/t.c +// FULL: FULL DEPS START + +// FULL-NEXT: { +// FULL-NEXT: "modules": [] +// FULL-NEXT: "translation-units": [ +// FULL-NEXT: { +// FULL-NEXT: "commands": [ +// FULL-NEXT: { +// FULL-NEXT: "cache-key": "[[TU_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// FULL: "clang-module-deps": [] +// FULL: "command-line": [ +// FULL-NEXT: "-cc1" +// FULL: "-fcas-path" +// FULL-NEXT: "[[PREFIX]]/cas" +// FULL: "-disable-free" +// FULL: "-fcas-include-tree" +// FULL-NEXT: "[[TREE_ID]]" +// FULL: "-fcache-compile-job" +// FULL: "-fsyntax-only" +// FULL: "-x" +// FULL-NEXT: "c" +// FULL-NOT: "t.c" +// FULL: "-main-file-name" +// FULL-NEXT: "t.c" +// FULL-NOT: "t.c" +// FULL: ] +// FULL: "executable": "[[CLANG]]" +// FULL: "file-deps": [ +// FULL-NEXT: "[[PREFIX]]/t.c" +// FULL-NEXT: "[[PREFIX]]/top.h" +// FULL-NEXT: "[[PREFIX]]/n1.h" +// FULL-NEXT: "[[PREFIX]]/n2.h" +// FULL-NEXT: "[[PREFIX]]/n3.h" +// FULL-NEXT: "[[PREFIX]]/n3.h" +// FULL-NEXT: "[[PREFIX]]/n2.h" +// FULL-NEXT: ] +// FULL: "input-file": "[[PREFIX]]/t.c" +// FULL: } +// FULL: ] +// FULL: } +// FULL: ] +// FULL: } + +// Build the include-tree command +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/tu.rsp -Rcompile-job-cache 2> %t/t.err + +// Check cache key. +// RUN: cp %t/full.txt %t/combined.txt +// RUN: cat %t/t.err >> %t/combined.txt +// RUN: FileCheck %s -input-file=%t/combined.txt -check-prefix=COMBINED + +// COMBINED: "commands": [ +// COMBINED-NEXT: { +// COMBINED-NEXT: "cache-key": "[[TU_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// COMBINED: remark: compile job cache miss for '[[TU_CACHE_KEY]]' + +//--- cdb.json.template +[ + { + "directory": "DIR", + "command": "clang -fsyntax-only DIR/t.c", + "file": "DIR/t.c" + } +] + +//--- t.c + +#include "top.h" // this is top +// CHECK: [[@LINE]]:1 [[PREFIX]]/top.h + +// Skipped because of macro guard. +#include "n1.h" + +#include "n3.h" +// CHECK-DAG: [[@LINE]]:1 [[PREFIX]]/n3.h + +#include "n2.h" +// CHECK-DAG: [[@LINE]]:1 [[PREFIX]]/n2.h + +//--- top.h +#ifndef _TOP_H_ +#define _TOP_H_ + +#if WHATEVER +typedef int MyT; +#endif + +#define WHATEVER 1 +#include "n1.h" +// CHECK-DAG: [[@LINE]]:1 [[PREFIX]]/n1.h + +#include "n3.h" +// CHECK-DAG: [[@LINE]]:1 [[PREFIX]]/n3.h + +#define ANOTHER 2 + +struct S { + int x; +}; + +#endif + +//--- n1.h +#pragma once + +int x1; +#include "n2.h" +// CHECK-DAG: [[@LINE]]:1 [[PREFIX]]/n2.h + +int x2; + +//--- n2.h +void foo(void); + +//--- n3.h +#ifndef _N3_H_ +#define _N3_H_ + +int x3; + +#endif + +// More stuff after following '#endif', invalidate the macro guard optimization. +#define THIS_INVALIDATES_THE_MACRO_GUARD 1 diff --git a/clang/test/ClangScanDeps/mllvm-double-option-error-c-api.c b/clang/test/ClangScanDeps/mllvm-double-option-error-c-api.c new file mode 100644 index 0000000000000..6e734f73c65ec --- /dev/null +++ b/clang/test/ClangScanDeps/mllvm-double-option-error-c-api.c @@ -0,0 +1,9 @@ +// RUN: c-index-test core -scan-deps -working-dir %S -- clang_tool -Dmz -mllvm -asan-instrumentation-with-call-threshold=0 -mllvm -asan-instrumentation-with-call-threshold=0 %s -I %S/Inputs | FileCheck %s + +#ifdef mz +#include "header.h" +#endif + +// CHECK: file-deps: +// CHECK-NEXT: mllvm-double-option-error-c-api.c +// CHECK-NEXT: Inputs/header.h diff --git a/clang/test/ClangScanDeps/modules-availability-check.c b/clang/test/ClangScanDeps/modules-availability-check.c new file mode 100644 index 0000000000000..0dc31c1d6519f --- /dev/null +++ b/clang/test/ClangScanDeps/modules-availability-check.c @@ -0,0 +1,44 @@ +// Check cas-fs-based caching works with availability check based on +// SDKSettings.json. + +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -j 1 \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: -cas-path %t/cas > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module-name=mod > %t/mod.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: not %clang @%t/mod.rsp 2>&1 | FileCheck %s + +// CHECK: error: 'fUnavail' is unavailable + +//--- cdb.json.template +[{ + "directory": "DIR", + "command": "clang -target x86_64-apple-macos11 -fsyntax-only DIR/tu.c -isysroot DIR/MacOSX11.0.sdk -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps", + "file": "DIR/tu.c" +}] + +//--- MacOSX11.0.sdk/SDKSettings.json +{ + "DefaultVariant": "macos", "DisplayName": "macOS 11", + "Version": "11.0", + "MaximumDeploymentTarget": "11.0.99" +} + +//--- module.modulemap +module mod { header "mod.h" } + +//--- mod.h +void fUnavail(void) __attribute__((availability(macOS, obsoleted = 10.15))); + +static inline void module(void) { + fUnavail(); +} + +//--- tu.c +#include "mod.h" diff --git a/clang/test/ClangScanDeps/modules-cas-context-hash.c b/clang/test/ClangScanDeps/modules-cas-context-hash.c new file mode 100644 index 0000000000000..b80709d5acda1 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas-context-hash.c @@ -0,0 +1,108 @@ +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb1.json.template > %t/cdb1.json +// RUN: sed "s|DIR|%/t|g" %t/cdb2.json.template > %t/cdb2.json + +// RUN: clang-scan-deps -compilation-database %t/cdb1.json -module-files-dir %t/outputs \ +// RUN: -cas-path %t/cas1 -format experimental-include-tree-full \ +// RUN: > %t/result.json +// RUN: echo "=====" >> %t/result.json +// RUN: clang-scan-deps -compilation-database %t/cdb2.json -module-files-dir %t/outputs \ +// RUN: -cas-path %t/cas2 -format experimental-include-tree-full \ +// RUN: >> %t/result.json + +// RUN: cat %t/result.json | FileCheck %s -DPREFIX=%/t + +// CHECK: "modules": [ +// CHECK: { +// CHECK: "cache-key": "llvmcas://[[KEY:[[:xdigit:]]+]]" +// CHECK: "context-hash": "[[HASH:[A-Z0-9]+]]", +// CHECK: "name": "Mod" +// CHECK: } +// CHECK: ] +// CHECK: "translation-units": [ +// CHECK: { +// CHECK: "commands": [ +// CHECK: { +// CHECK: "clang-module-deps": [ +// CHECK: { +// CHECK: "context-hash": "[[HASH]]" +// CHECK: "module-name": "Mod" +// CHECK: } +// CHECK: ], +// CHECK: "command-line": [ +// CHECK: "-fmodule-file-cache-key" +// CHECK: "[[PREFIX]]/outputs/[[HASH]]/Mod-[[HASH]].pcm" +// CHECK: "llvmcas://[[KEY]]" +// CHECK: ] + +// CHECK-LABEL: ===== + +// CHECK: "modules": [ +// CHECK: { +// CHECK: "cache-key": "llvmcas://[[KEY]]" +// CHECK: "context-hash": "[[HASH]]" +// CHECK: "name": "Mod" +// CHECK: } +// CHECK: "translation-units": [ +// CHECK: { +// CHECK: "commands": [ +// CHECK: { +// CHECK: "clang-module-deps": [ +// CHECK: { +// CHECK: "context-hash": "[[HASH]]" +// CHECK: "module-name": "Mod" +// CHECK: } +// CHECK: ], +// CHECK: "command-line": [ +// CHECK: "-fmodule-file-cache-key" +// CHECK: "[[PREFIX]]/outputs/[[HASH]]/Mod-[[HASH]].pcm" +// CHECK: "llvmcas://[[KEY]]" +// CHECK: ] + +//--- cdb1.json.template +[{ + "directory": "DIR", + "arguments": [ + "clang", + "-fsyntax-only", + "DIR/tu.c", + "-fmodules", + "-fimplicit-module-maps", + + "-Xclang", "-fcas-plugin-path", "-Xclang", "/1", + "-Xclang", "-fcas-plugin-option", "-Xclang", "a=x", + "-fmodules-cache-path=DIR/cache1", + "-fmessage-length=1", + "-fcolor-diagnostics", + ], + "file": "DIR/tu.c" +}] + +//--- cdb2.json.template +[{ + "directory": "DIR", + "arguments": [ + "clang", + "-fsyntax-only", + "DIR/tu.c", + "-fmodules", + "-fimplicit-module-maps", + + "-Xclang", "-fcas-plugin-path", "-Xclang", "/2", + "-Xclang", "-fcas-plugin-option", "-Xclang", "b=y", + "-fmodules-cache-path=DIR/cache2", + "-fmessage-length=2", + "-fno-color-diagnostics", + ], + "file": "DIR/tu.c" +}] + +//--- module.modulemap +module Mod { header "Mod.h" } + +//--- Mod.h + +//--- tu.c +#include "Mod.h" \ No newline at end of file diff --git a/clang/test/ClangScanDeps/modules-cas-fs-prefix-mapping-caching.c b/clang/test/ClangScanDeps/modules-cas-fs-prefix-mapping-caching.c new file mode 100644 index 0000000000000..230b21b83fae7 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas-fs-prefix-mapping-caching.c @@ -0,0 +1,67 @@ +// Test that we get cache hits across directories with modules. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: cp -r %t/dir1 %t/dir2 +// RUN: sed -e "s|DIR|%t/dir1|g" -e "s|CLANG|%clang|g" -e "s|SDK|%S/Inputs/SDK|g" %t/cdb.json.template > %t/dir1/cdb.json +// RUN: sed -e "s|DIR|%t/dir2|g" -e "s|CLANG|%clang|g" -e "s|SDK|%S/Inputs/SDK|g" %t/cdb.json.template > %t/dir2/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/dir1/cdb.json -format experimental-full \ +// RUN: -cas-path %t/cas -module-files-dir %t/dir1/modules \ +// RUN: -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: -prefix-map=%t/dir1/modules=/^modules -prefix-map=%t/dir1=/^src -optimize-args=none \ +// RUN: > %t/dir1.txt + +// RUN: clang-scan-deps -compilation-database %t/dir2/cdb.json -format experimental-full \ +// RUN: -cas-path %t/cas -module-files-dir %t/dir2/modules \ +// RUN: -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: -prefix-map=%t/dir2/modules=/^modules -prefix-map=%t/dir2=/^src -optimize-args=none \ +// RUN: > %t/dir2.txt + +// Extract individual commands. +// RUN: %deps-to-rsp %t/dir1.txt --module-name=B > %t/dir1/B.cc1.rsp +// RUN: %deps-to-rsp %t/dir1.txt --module-name=A > %t/dir1/A.cc1.rsp +// RUN: %deps-to-rsp %t/dir1.txt --tu-index 0 > %t/dir1/tu.cc1.rsp + +// RUN: %deps-to-rsp %t/dir2.txt --module-name=B > %t/dir2/B.cc1.rsp +// RUN: %deps-to-rsp %t/dir2.txt --module-name=A > %t/dir2/A.cc1.rsp +// RUN: %deps-to-rsp %t/dir2.txt --tu-index 0 > %t/dir2/tu.cc1.rsp + +// RUN: (cd %t/dir1; %clang @B.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: (cd %t/dir1; %clang @A.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: (cd %t/dir1; %clang @tu.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-MISS + +// CACHE-MISS: compile job cache miss + +// RUN: (cd %t/dir2; %clang @B.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: (cd %t/dir2; %clang @A.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: (cd %t/dir2; %clang @tu.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-HIT + +// CACHE-HIT: compile job cache hit + +// RUN: diff -r -u %t/dir1/modules %t/dir2/modules + +//--- cdb.json.template +[ + { + "directory": "DIR", + "command": "CLANG -fsyntax-only DIR/t.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/mcp -target x86_64-apple-macos11 -isysroot SDK -Rcompile-job-cache", + "file": "DIR/t.c" + } +] + +//--- dir1/t.c +#include "a.h" + +//--- dir1/module.modulemap +module A { header "a.h" } +module B { header "b.h" } + +//--- dir1/a.h +#include "b.h" + +//--- dir1/b.h +#include +#include diff --git a/clang/test/ClangScanDeps/modules-cas-fs-prefix-mapping.c b/clang/test/ClangScanDeps/modules-cas-fs-prefix-mapping.c new file mode 100644 index 0000000000000..24190438079f4 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas-fs-prefix-mapping.c @@ -0,0 +1,188 @@ +// Test path prefix-mapping when using a cas-fs with clang-scan-deps in +// modules. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%t|g" -e "s|CLANG|%clang|g" -e "s|SDK|%S/Inputs/SDK|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full \ +// RUN: -cas-path %t/cas -module-files-dir %t/modules \ +// RUN: -prefix-map=%t/modules=/^modules -prefix-map=%t=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: > %t/full_result.txt + +// Check the command-lines. +// RUN: FileCheck %s -input-file %t/full_result.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK + +// Extract individual commands. +// RUN: %deps-to-rsp %t/full_result.txt --module-name=B > %t/B.cc1.rsp +// RUN: %deps-to-rsp %t/full_result.txt --module-name=A > %t/A.cc1.rsp +// RUN: %deps-to-rsp %t/full_result.txt --tu-index 0 > %t/tu.cc1.rsp + +// Check the casfs. +// RUN: cat %t/B.cc1.rsp | sed -E 's/.* "-fcas-fs" "([^ ]+)" .*/\1/' > %t/B_id.txt +// RUN: cat %t/A.cc1.rsp | sed -E 's/.* "-fcas-fs" "([^ ]+)" .*/\1/' > %t/A_id.txt +// RUN: cat %t/tu.cc1.rsp | sed -E 's/.* "-fcas-fs" "([^ ]+)" .*/\1/' > %t/tu_id.txt +// RUN: llvm-cas -cas %t/cas -ls-tree-recursive @%t/B_id.txt > %t/B_fs.txt +// RUN: llvm-cas -cas %t/cas -ls-tree-recursive @%t/A_id.txt > %t/A_fs.txt +// RUN: llvm-cas -cas %t/cas -ls-tree-recursive @%t/tu_id.txt > %t/tu_fs.txt +// RUN: FileCheck %s -input-file %t/A_fs.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK -check-prefixes=FS_NEG,FS +// RUN: FileCheck %s -input-file %t/B_fs.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK -check-prefixes=FS_NEG,FS +// RUN: FileCheck %s -input-file %t/tu_fs.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK -check-prefixes=FS_NEG,FS + +// FS_NEG-NOT: [[PREFIX]] +// FS_NEG-NOT: [[SDK_PREFIX]] +// FS_NEG-NOT: .pcm{{$}} +// FS: file llvmcas://{{.*}} /^sdk/usr/include/stdlib.h +// FS: file llvmcas://{{.*}} /^src/a.h +// FS: file llvmcas://{{.*}} /^src/b.h +// FS: file llvmcas://{{.*}} /^src/module.modulemap +// FS: file llvmcas://{{.*}} /^tc/lib/clang/{{.*}}/include/stdarg.h + +// Check that it builds. +// RUN: %clang @%t/B.cc1.rsp +// RUN: %clang @%t/A.cc1.rsp +// RUN: %clang @%t/tu.cc1.rsp + +// CHECK: { +// CHECK: "modules": [ +// CHECK: { +// CHECK: "casfs-root-id": "[[A_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [ +// CHECK: { +// CHECK: "module-name": "B" +// CHECK: } +// CHECK: ] +// CHECK: "clang-modulemap-file": "[[PREFIX]]/module.modulemap" +// CHECK: "command-line": [ +// CHECK: "-fcas-path" +// CHECK: "[[PREFIX]]/cas" +// CHECK: "-fcas-fs" +// CHECK: "[[A_ROOT_ID]]" +// CHECK: "-fcas-fs-working-directory" +// CHECK: "/^src" +// CHECK: "-fmodule-map-file=/^src/module.modulemap" +// CHECK: "-o" +// CHECK: "[[PREFIX]]/modules/{{.*}}/A-{{.*}}.pcm" +// CHECK: "-fmodule-file-cache-key" +// CHECK: "/^modules/{{.*}}/B-[[B_CONTEXT_HASH:[^.]+]].pcm" +// CHECK: "llvmcas://{{.*}}" +// CHECK: "-x" +// CHECK: "c" +// CHECK: "/^src/module.modulemap" +// CHECK: "-isysroot" +// CHECK: "/^sdk" +// CHECK: "-resource-dir" +// CHECK: "/^tc/lib/clang/{{.*}}" +// CHECK: "-fmodule-file=B=/^modules/{{.*}}/B-[[B_CONTEXT_HASH]].pcm" +// CHECK: "-isystem" +// CHECK: "/^tc/lib/clang/{{.*}}/include" +// CHECK: "-internal-externc-isystem" +// CHECK: "/^sdk/usr/include" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK: "[[PREFIX]]/module.modulemap" +// CHECK: "[[PREFIX]]/a.h" +// CHECK: ] +// CHECK: "name": "A" +// CHECK: } +// CHECK: { +// CHECK: "casfs-root-id": "[[B_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [], +// CHECK: "clang-modulemap-file": "[[PREFIX]]/module.modulemap" +// CHECK: "command-line": [ +// CHECK: "-fcas-path" +// CHECK: "[[PREFIX]]/cas" +// CHECK: "-fcas-fs" +// CHECK: "[[B_ROOT_ID]]" +// CHECK: "-fcas-fs-working-directory" +// CHECK: "/^src" +// CHECK: "-o" +// CHECK: "[[PREFIX]]/modules/{{.*}}/B-[[B_CONTEXT_HASH]].pcm" +// CHECK: "-x" +// CHECK: "c" +// CHECK: "/^src/module.modulemap" +// CHECK: "-isysroot" +// CHECK: "/^sdk" +// CHECK: "-resource-dir" +// CHECK: "/^tc/lib/clang/{{.*}}" +// CHECK: "-isystem" +// CHECK: "/^tc/lib/clang/{{.*}}/include" +// CHECK: "-internal-externc-isystem" +// CHECK: "/^sdk/usr/include" +// CHECK: ] +// CHECK: "context-hash": "[[B_CONTEXT_HASH]]" +// CHECK: "file-deps": [ +// Note: PREFIX, SDK_PREFIX and toolchain path are unordered +// CHECK-DAG: "[[PREFIX]]/module.modulemap" +// CHECK-DAG: "[[PREFIX]]/b.h" +// CHECK-DAG: "{{.*}}/include/stdarg.h" +// CHECK-DAG: "[[SDK_PREFIX]]/usr/include/stdlib.h" +// CHECK: ] +// CHECK: "name": "B" +// CHECK: } +// CHECK: ] +// CHECK: "translation-units": [ +// CHECK: { +// CHECK: "commands": [ +// CHECK: { +// CHECK: "casfs-root-id": "[[TU_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [ +// CHECK: { +// CHECK: "module-name": "A" +// CHECK: } +// CHECK: ] +// CHECK: "command-line": [ +// CHECK: "-fcas-path" +// CHECK: "[[PREFIX]]/cas" +// CHECK: "-fcas-fs" +// CHECK: "[[TU_ROOT_ID]]" +// CHECK: "-fcas-fs-working-directory" +// CHECK: "/^src" +// CHECK: "-fmodule-map-file=/^src/module.modulemap" +// CHECK: "-fmodule-file-cache-key" +// CHECK: "/^modules/{{.*}}A-{{.*}}.pcm" +// CHECK: "llvmcas://{{.*}}" +// CHECK: "-x" +// CHECK: "c" +// CHECK: "/^src/t.c" +// CHECK: "-isysroot" +// CHECK: "/^sdk" +// CHECK: "-resource-dir" +// CHECK: "/^tc/lib/clang/{{.*}}" +// CHECK: "-fmodule-file=A=/^modules/{{.*}}/A-{{.*}}.pcm" +// CHECK: "-isystem" +// CHECK: "/^tc/lib/clang/{{.*}}/include" +// CHECK: "-internal-externc-isystem" +// CHECK: "/^sdk/usr/include" +// CHECK: ], +// CHECK: "file-deps": [ +// CHECK: "[[PREFIX]]/t.c" +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/t.c" +// CHECK: } + + +//--- cdb.json.template +[ + { + "directory": "DIR", + "command": "CLANG -fsyntax-only DIR/t.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/mcp -target x86_64-apple-macos11 -isysroot SDK", + "file": "DIR/t.c" + } +] + +//--- t.c +#include "a.h" + +//--- module.modulemap +module A { header "a.h" } +module B { header "b.h" } + +//--- a.h +#include "b.h" + +//--- b.h +#include +#include diff --git a/clang/test/ClangScanDeps/modules-cas-fs-symlink-dir-from-module.c b/clang/test/ClangScanDeps/modules-cas-fs-symlink-dir-from-module.c new file mode 100644 index 0000000000000..ed6c4a6a96db0 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas-fs-symlink-dir-from-module.c @@ -0,0 +1,68 @@ +// Check that the path of an "affecting" modulemap file matches what is captured +// in the cas fs. + +// REQUIRES: shell, ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.in > %t/cdb.json +// RUN: ln -s module %t/include/symlink-to-module + +// RUN: clang-scan-deps -cas-path %t/cas -compilation-database %t/cdb.json -j 1 \ +// RUN: -format experimental-full -mode=preprocess-dependency-directives \ +// RUN: -optimize-args=header-search -module-files-dir %t/build > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module-name=Mod > %t/mod.cc1.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name=Other > %t/other.cc1.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name=Foo > %t/foo.cc1.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name=Foo_Private > %t/foo_private.cc1.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name=Test > %t/test.cc1.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.cc1.rsp + +// RUN: %clang @%t/mod.cc1.rsp +// RUN: %clang @%t/other.cc1.rsp +// RUN: %clang @%t/foo.cc1.rsp +// RUN: %clang @%t/foo_private.cc1.rsp +// RUN: %clang @%t/test.cc1.rsp +// RUN: %clang @%t/tu.cc1.rsp + +//--- cdb.json.in +[{ + "directory": "DIR", + "command": "clang -fsyntax-only DIR/test.c -F DIR/Frameworks -I DIR/include -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache", + "file": "DIR/test.c" +}] + +//--- include/module/module.modulemap +module Mod { header "mod.h" export * } + +//--- include/module/mod.h + +//--- include/module.modulemap +module Other { header "other.h" export * } + +//--- include/other.h +#include "symlink-to-module/mod.h" +#include "module/mod.h" + +//--- Frameworks/Foo.framework/Modules/module.modulemap +framework module Foo { header "Foo.h" export * } +//--- Frameworks/Foo.framework/Modules/module.private.modulemap +framework module Foo_Private { header "Priv.h" export * } + +//--- Frameworks/Foo.framework/Headers/Foo.h +#include "module/mod.h" + +//--- Frameworks/Foo.framework/PrivateHeaders/Priv.h +#include +#include "other.h" + +//--- module.modulemap +module Test { header "test.h" export * } + +//--- test.h +#include +#include + +//--- test.c +#include "test.h" diff --git a/clang/test/ClangScanDeps/modules-cas-fs-vfsoverlay.c b/clang/test/ClangScanDeps/modules-cas-fs-vfsoverlay.c new file mode 100644 index 0000000000000..aa92651ce9a72 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas-fs-vfsoverlay.c @@ -0,0 +1,52 @@ +// Check cas-fs-based caching works with vfsoverlay files. + +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: sed -e "s|DIR|%/t|g" %t/vfs.yaml.template > %t/vfs.yaml + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -j 1 \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: -cas-path %t/cas > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module-name=A > %t/A.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/A.rsp +// RUN: %clang @%t/tu.rsp + +//--- cdb.json.template +[{ + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.c -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps -ivfsoverlay DIR/vfs.yaml", + "file": "DIR/tu.c" +}] + +//--- vfs.yaml.template +{ + "version": 0, + "case-sensitive": "false", + "roots": [ + { + "name": "DIR/A", + "type": "directory" + "contents": [ + { + "external-contents": "DIR/elsewhere1/A.modulemap", + "name": "module.modulemap", + "type": "file" + } + ] + } + ] +} + +//--- elsewhere1/A.modulemap +module A { header "A.h" } + +//--- A/A.h +typedef int A_t; + +//--- tu.c +#include "A/A.h" +A_t a = 0; diff --git a/clang/test/ClangScanDeps/modules-cas-full-by-mod-name.c b/clang/test/ClangScanDeps/modules-cas-full-by-mod-name.c new file mode 100644 index 0000000000000..957e383e2cb99 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas-full-by-mod-name.c @@ -0,0 +1,101 @@ +// UNSUPPORTED: target=powerpc64-ibm-aix{{.*}} +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t + +//--- module.modulemap +module root { header "root.h" } +module direct { header "direct.h" } +module transitive { header "transitive.h" } +//--- root.h +#include "direct.h" +#include "root/textual.h" +//--- direct.h +#include "transitive.h" +//--- transitive.h +// empty + +//--- root/textual.h +// This is here to verify that the "root" directory doesn't clash with name of +// the "root" module. + +//--- cdb.json.template +[{ + "file": "", + "directory": "DIR", + "command": "clang -fmodules -fmodules-cache-path=DIR/cache -I DIR -x c" +}] + +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -format experimental-full -module-name=root > %t/result.json +// RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s + +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[DIRECT_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "casfs-root-id": "[[LEFT_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "transitive" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "{{.*transitive-.*\.pcm}}" +// CHECK-NEXT: "[[TRANSITIVE_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/direct.h" +// CHECK-NEXT: ], +// CHECK-NEXT: "link-libraries": [], +// CHECK-NEXT: "name": "direct" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[ROOT_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "casfs-root-id": "[[ROOT_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "direct" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "{{.*direct-.*\.pcm}}" +// CHECK-NEXT: "[[DIRECT_CACHE_KEY]]" +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/root.h" +// CHECK-NEXT: "[[PREFIX]]/root/textual.h" +// CHECK-NEXT: ], +// CHECK-NEXT: "link-libraries": [], +// CHECK-NEXT: "name": "root" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[TRANSITIVE_CACHE_KEY]]" +// CHECK-NEXT: "casfs-root-id": "[[LEFT_ROOT_ID]]" +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/transitive.h" +// CHECK-NEXT: ], +// CHECK-NEXT: "link-libraries": [], +// CHECK-NEXT: "name": "transitive" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "translation-units": [] +// CHECK-NEXT: } diff --git a/clang/test/ClangScanDeps/modules-cas-implicit-module-cache.c b/clang/test/ClangScanDeps/modules-cas-implicit-module-cache.c new file mode 100644 index 0000000000000..054790d3ef1be --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas-implicit-module-cache.c @@ -0,0 +1,49 @@ +// Check that the implicit modules build that the scanner performs detects +// missing cas objects. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// Scan to populate module cache + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives + +// Clear cas and re-scan + +// RUN: rm -rf %t/cas + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// Build module and TU + +// RUN: %deps-to-rsp %t/deps.json --module-name Mod > %t/Mod.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/Mod.rsp +// RUN: %clang @%t/tu.rsp + +//--- cdb.json.template +[{ + "directory" : "DIR", + "command" : "clang_tool -fsyntax-only DIR/tu.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache", + "file" : "DIR/tu.c" +}] + +//--- module.modulemap +module Mod { header "Mod.h" } + +//--- Mod.h +void mod(void); + +//--- tu.c +#include "Mod.h" +void tu(void) { + mod(); +} diff --git a/clang/test/ClangScanDeps/modules-cas-module-cache-hash.c b/clang/test/ClangScanDeps/modules-cas-module-cache-hash.c new file mode 100644 index 0000000000000..c4d4f5b289cb6 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas-module-cache-hash.c @@ -0,0 +1,33 @@ +// Check that using the same module cache does not cause errors when switching +// between cas-fs and include-tree. + +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full | FileCheck %s -check-prefix=INCLUDE_TREE + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full | FileCheck %s -check-prefix=CAS_FS + +// INCLUDE_TREE: "-fcas-include-tree" +// CAS_FS: "-fcas-fs" + +//--- cdb.json.template +[{ + "file": "DIR/tu.c", + "directory": "DIR", + "command": "clang -fsyntax-only -fmodules -fimplicit-modules -fmodules-cache-path=DIR/mcp DIR/tu.c" +}] + +//--- module.modulemap +module M { header "M.h" } + +//--- M.h + +//--- tu.c +#include "M.h" diff --git a/clang/test/ClangScanDeps/modules-cas-trees-cwd.c b/clang/test/ClangScanDeps/modules-cas-trees-cwd.c new file mode 100644 index 0000000000000..de2611c3c3655 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas-trees-cwd.c @@ -0,0 +1,61 @@ +// Ensure the working directory is correctly captured in cas-fs when compiling +// with caching from outside the source directory. +// FIXME: ideally we could further canonicalize the working directory when it +// is irrelevant to the compilation, but for now ensure we can compile at all. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: mkdir -p %t/B + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module-name Mod > %t/Mod.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp + +// RUN: %clang @%t/Mod.rsp +// RUN: %clang @%t/tu.rsp + +// Check specifics of the command-line +// RUN: cat %t/deps.json | FileCheck %s -DPREFIX=%/t + +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK: "command-line": [ +// CHECK: "-fcas-fs-working-directory" +// CHECK-NEXT: "[[PREFIX]]/B" +// CHECK: ] +// CHECK: "name": "Mod" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "translation-units": [ +// CHECK: { +// CHECK: "commands": [ +// CHECK: { +// CHECK: "command-line": [ +// CHECK: "-fcas-fs-working-directory" +// CHECK-NEXT: "[[PREFIX]]/B" +// CHECK: ] + +//--- cdb.json.template +[{ + "directory" : "DIR/B", + "command" : "clang_tool -fsyntax-only DIR/A/tu.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/A/module-cache -Rcompile-job-cache", + "file" : "DIR/A/tu.c" +}] + +//--- A/module.modulemap +module Mod { header "Mod.h" } + +//--- A/Mod.h +#pragma once +void Top(void); + +//--- A/tu.c +#include "Mod.h" diff --git a/clang/test/ClangScanDeps/modules-cas-trees-exclude-files-from-casfs.c b/clang/test/ClangScanDeps/modules-cas-trees-exclude-files-from-casfs.c new file mode 100644 index 0000000000000..8c10be783d318 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas-trees-exclude-files-from-casfs.c @@ -0,0 +1,85 @@ +// Check that files that should not impact the module build are not included in +// the cas-fs, which can cause spurious rebuilds. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: sed "s|DIR|%/t|g" %t/cdb_cache2.json.template > %t/cdb_cache2.json +// RUN: sed "s|DIR|%/t|g" %t/cdb_timestamp.json.template > %t/cdb_timestamp.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// Changing module cache path should not affect results. +// RUN: clang-scan-deps -compilation-database %t/cdb_cache2.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_cache2.json +// RUN: diff -u %t/deps_cache2.json %t/deps.json + +// .pcm.timestamp files created by -fmodules-validate-once-per-build-session should not affect results +// RUN: touch %t/session +// RUN: clang-scan-deps -compilation-database %t/cdb_timestamp.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_pre_timestamp.json +// RUN: touch %t/Top.h +// RUN: clang-scan-deps -compilation-database %t/cdb_timestamp.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_post_timestamp.json +// RUN: diff -u %t/deps_pre_timestamp.json %t/deps_post_timestamp.json + +//--- cdb.json.template +[{ + "directory" : "DIR", + "command" : "clang_tool -fsyntax-only DIR/tu.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache", + "file" : "DIR/tu.c" +}] + +//--- cdb_cache2.json.template +[{ + "directory" : "DIR", + "command" : "clang_tool -fsyntax-only DIR/tu.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache2 -Rcompile-job-cache", + "file" : "DIR/tu.c" +}] + +//--- cdb_timestamp.json.template +[{ + "directory" : "DIR", + "command" : "clang_tool -fsyntax-only DIR/tu.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache2 -fmodules-validate-once-per-build-session -fbuild-session-file=DIR/session -Rcompile-job-cache", + "file" : "DIR/tu.c" +}] + +//--- module.modulemap +module Top { header "Top.h" export * } +module Left { header "Left.h" export * } +module Right { header "Right.h" export * } + +//--- Top.h +#pragma once +void Top(void); + +//--- Left.h +#pragma once +#include "Top.h" +void Left(void); + +//--- Right.h +#pragma once +#include "Top.h" +void Right(void); + +//--- tu.c +#include "Left.h" +#include "Right.h" + +void tu(void) { + Top(); + Left(); + Right(); +} diff --git a/clang/test/ClangScanDeps/modules-cas-trees-input-files.c b/clang/test/ClangScanDeps/modules-cas-trees-input-files.c new file mode 100644 index 0000000000000..89f4f14b9909e --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas-trees-input-files.c @@ -0,0 +1,92 @@ +// Ensure all files references by a cached module can be found. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb_pch.json.template > %t/cdb_pch.json +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// == Scan PCH +// RUN: clang-scan-deps -compilation-database %t/cdb_pch.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_pch.json + +// == Build PCH +// RUN: %deps-to-rsp %t/deps_pch.json --module-name A > %t/A.rsp +// RUN: %deps-to-rsp %t/deps_pch.json --tu-index 0 > %t/pch.rsp + +// RUN: %clang @%t/A.rsp +// RUN: %clang @%t/pch.rsp + +// == Scan TU, including PCH +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// == Build TU, including PCH +// RUN: %deps-to-rsp %t/deps.json --module-name C > %t/C.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp + +// RUN: %clang @%t/C.rsp +// RUN: %clang @%t/tu.rsp + +//--- cdb_pch.json.template +[ + { + "directory" : "DIR", + "command" : "clang_tool -I DIR -x c-header DIR/prefix.h -o DIR/prefix.h.pch -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache", + "file" : "DIR/prefix.h" + }, +] + +//--- cdb.json.template +[ + { + "directory" : "DIR", + "command" : "clang_tool -I DIR -fsyntax-only DIR/tu.c -include DIR/prefix.h -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache", + "file" : "DIR/tu.c" + }, +] + +// The test below is a specific instance that was failing in the past. The test +// complexity is required to trigger looking up an input file of a module that +// would not be automatically visited when importing that module during +// dependency scanning, because it is only triggered incidentally. The anatomy +// of the specific test case is: +// * module A has a reference to B's modulemap but does not import B +// because it was for an excluded header +// * module C imports A and performs macro expansion of module_macro +// * looking up the location for the macro definition does binary search and +// incidentally deserializes the SLocEntry for B's modulemap. With small +// modules such as in this test case, the binary search is *likely* to hit +// that specific SLocEntry, and it did at the time this test was written. +// * module A must be "prebuilt"; otherwise the scanner will visit all inputs +// during implicit module validation; so we load it via PCH. + +//--- A/module.modulemap +module A { header "A.h" } + +//--- A/A.h +#include "B/B.h" +#define module_macro int + +//--- B/module.modulemap +module B { exclude header "B.h" } + +//--- B/B.h + +//--- module.modulemap +module C { header "C.h" } + +//--- C.h +#include "A/A.h" +module_macro x; + +//--- prefix.h +#include "A/A.h" + +//--- tu.c +#include "C.h" diff --git a/clang/test/ClangScanDeps/modules-cas-trees-with-pch.c b/clang/test/ClangScanDeps/modules-cas-trees-with-pch.c new file mode 100644 index 0000000000000..d936e618ba770 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas-trees-with-pch.c @@ -0,0 +1,230 @@ +// Check that we can scan pch + modules with caching enabled and build the +// resulting commands. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb_pch.json.template > %t/cdb_pch.json +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// == Scan PCH +// RUN: clang-scan-deps -compilation-database %t/cdb_pch.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_pch.json + +// == Check specifics of the command-line +// RUN: cat %t/deps_pch.json | FileCheck %s -DPREFIX=%/t -check-prefix=PCH + +// == Build PCH +// RUN: %deps-to-rsp %t/deps_pch.json --module-name A > %t/A.rsp +// RUN: %deps-to-rsp %t/deps_pch.json --module-name B > %t/B.rsp +// RUN: %deps-to-rsp %t/deps_pch.json --tu-index 0 > %t/pch.rsp + +// RUN: %clang @%t/B.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/A.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// Ensure we load pcms from action cache +// RUN: rm -rf %t/outputs +// RUN: %clang @%t/pch.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/pch.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-HIT + +// == Scan TU, including PCH +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// == Check specifics of the command-line +// RUN: cat %t/deps.json | FileCheck %s -DPREFIX=%/t + +// == Build TU, including PCH +// RUN: %deps-to-rsp %t/deps.json --module-name C > %t/C.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp + +// RUN: %clang @%t/C.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// Ensure we load pcms from action cache +// RUN: rm -rf %t/outputs +// RUN: %clang @%t/tu.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-HIT + +// PCH: { +// PCH-NEXT: "modules": [ +// PCH-NEXT: { +// PCH: "casfs-root-id": "[[A_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// PCH: "clang-module-deps": [ +// PCH: { +// PCH: "module-name": "B" +// PCH: } +// PCH-NEXT: ] +// PCH: "command-line": [ +// PCH-NEXT: "-cc1" +// PCH: "-fcas-path" +// PCH-NEXT: "[[PREFIX]]{{.}}cas" +// PCH: "-fcas-fs" +// PCH-NEXT: "[[A_ROOT_ID]]" +// PCH: "-o" +// PCH-NEXT: "[[A_PCM:.*outputs.*A-.*\.pcm]]" +// PCH: "-fcache-compile-job" +// PCH: "-emit-module" +// PCH: "-fmodule-file-cache-key" +// PCH: "[[B_PCM:.*outputs.*B-.*\.pcm]]" +// PCH: "[[B_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// PCH: "-fmodule-file={{(B=)?}}[[B_PCM]]" +// PCH: ] +// PCH: "file-deps": [ +// PCH-NEXT: "[[PREFIX]]{{.}}module.modulemap" +// PCH-NEXT: "[[PREFIX]]{{.}}A.h" +// PCH-NEXT: ] +// PCH: "name": "A" +// PCH: } +// PCH-NEXT: { +// PCH: "casfs-root-id": "[[B_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// PCH: "clang-module-deps": [] +// PCH: "command-line": [ +// PCH-NEXT: "-cc1" +// PCH: "-fcas-path" +// PCH-NEXT: "[[PREFIX]]{{.}}cas" +// PCH: "-fcas-fs" +// PCH-NEXT: "[[B_ROOT_ID]]" +// PCH: "-o" +// PCH-NEXT: "[[B_PCM]]" +// PCH: "-fcache-compile-job" +// PCH: "-emit-module" +// PCH: ] +// PCH: "file-deps": [ +// PCH-NEXT: "[[PREFIX]]{{.}}module.modulemap" +// PCH-NEXT: "[[PREFIX]]{{.}}B.h" +// PCH-NEXT: ] +// PCH: "name": "B" +// PCH: } +// PCH-NEXT: ] +// PCH: "translation-units": [ +// PCH: { +// PCH: "commands": [ +// PCH: { +// PCH: "casfs-root-id": "[[PCH_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// PCH: "clang-module-deps": [ +// PCH: { +// PCH: "module-name": "A" +// PCH: } +// PCH-NEXT: ] +// PCH: "command-line": [ +// PCH-NEXT: "-cc1" +// PCH: "-fcas-path" +// PCH-NEXT: "[[PREFIX]]{{.}}cas" +// PCH: "-fcas-fs" +// PCH-NEXT: "[[PCH_ROOT_ID]]" +// PCH: "-fno-pch-timestamp" +// PCH: "-fcache-compile-job" +// PCH: "-emit-pch" +// PCH: "-fmodule-file-cache-key" +// PCH: "[[A_PCM]]" +// PCH: "{{llvmcas://[[:xdigit:]]+}}" +// PCH: "-fmodule-file={{(A=)?}}[[A_PCM]]" +// PCH: ] +// PCH: "file-deps": [ +// PCH-NEXT: "[[PREFIX]]{{.}}prefix.h" +// PCH-NEXT: ] +// PCH: } + +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK: "casfs-root-id": "[[C_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [] +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]{{.}}cas" +// CHECK: "-fcas-fs" +// CHECK-NEXT: "[[C_ROOT_ID]]" +// CHECK: "-o" +// CHECK-NEXT: "[[C_PCM:.*outputs.*C-.*\.pcm]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodule-file={{(B=)?}}[[B_PCM:.*outputs.*B-.*\.pcm]]" +// CHECK: "-fmodule-file-cache-key" +// CHECK: "[[B_PCM]]" +// CHECK: "[[B_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]{{.}}module.modulemap" +// CHECK-NEXT: "[[PREFIX]]{{.}}C.h" +// CHECK-NEXT: ] +// CHECK: "name": "C" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "translation-units": [ +// CHECK: { +// CHECK: "commands": [ +// CHECK: { +// CHECK: "casfs-root-id": "[[TU_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [ +// CHECK: { +// CHECK: "module-name": "C" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]{{.}}cas" +// CHECK: "-fcas-fs" +// CHECK-NEXT: "[[TU_ROOT_ID]]" +// CHECK: "-fno-pch-timestamp" +// CHECK: "-fcache-compile-job" +// CHECK: "-fmodule-file-cache-key" +// CHECK: "[[C_PCM]]" +// CHECK: "{{llvmcas://[[:xdigit:]]+}}" +// CHECK: "-fmodule-file={{(C=)?}}[[C_PCM]]" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]{{.}}tu.c" +// CHECK-NEXT: "[[PREFIX]]{{.}}prefix.h.pch" +// CHECK-NEXT: ] +// CHECK: } + +// CACHE-HIT: remark: compile job cache hit +// CACHE-MISS: remark: compile job cache miss + +//--- cdb_pch.json.template +[ + { + "directory" : "DIR", + "command" : "clang_tool -x c-header DIR/prefix.h -o DIR/prefix.h.pch -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache", + "file" : "DIR/prefix.h" + }, +] + +//--- cdb.json.template +[ + { + "directory" : "DIR", + "command" : "clang_tool -fsyntax-only DIR/tu.c -include DIR/prefix.h -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache", + "file" : "DIR/tu.c" + }, +] + +//--- module.modulemap +module A { header "A.h" export * } +module B { header "B.h" } +module C { header "C.h" export * } + +//--- A.h +#include "B.h" + +//--- B.h +void B(void); + +//--- C.h +#include "B.h" +void B(void); + +//--- prefix.h +#include "A.h" + +//--- tu.c +#include "C.h" +void tu(void) { + B(); +} diff --git a/clang/test/ClangScanDeps/modules-cas-trees.c b/clang/test/ClangScanDeps/modules-cas-trees.c new file mode 100644 index 0000000000000..f1cab97f9ed89 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas-trees.c @@ -0,0 +1,217 @@ +// Check that we can scan modules with caching enabled and build the resulting +// commands. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// Full and tree-full modes are identical here. +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_tree.json +// RUN: diff -u %t/deps_tree.json %t/deps.json + +// Disabling/re-enabling the cas should not be cached in the implicit pcms +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_no_cas.json +// RUN: cat %t/deps_no_cas.json | FileCheck %s -check-prefix=NO_CAS +// NO_CAS-NOT: fcas +// NO_CAS-NOT: faction-cache +// NO_CAS-NOT: fcache-compile-job +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_cas_2.json +// RUN: diff -u %t/deps_cas_2.json %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module-name Top > %t/Top.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name Left > %t/Left.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name Right > %t/Right.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp + +// Missing pcm in action cache +// RUN: not %clang @%t/Left.rsp 2> %t/error.txt +// RUN: cat %t/error.txt | FileCheck %s -check-prefix=MISSING +// MISSING: error: CAS cannot load module with key '{{.*}}' from -fmodule-file-cache-key: no such entry in action cache + +// Build everything +// RUN: %clang @%t/Top.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/Left.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/Left.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: %clang @%t/Right.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/Right.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// Ensure we load pcms from action cache +// RUN: rm -rf %t/outputs +// RUN: %clang @%t/tu.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu.rsp 2>&1 | FileCheck %s -check-prefix=CACHE-HIT + +// CACHE-HIT: remark: compile job cache hit +// CACHE-MISS: remark: compile job cache miss + +// Check specifics of the command-line +// RUN: cat %t/deps.json | FileCheck %s -DPREFIX=%/t + +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK: "cache-key": "[[LEFT_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK: "casfs-root-id": "[[LEFT_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [ +// CHECK: { +// CHECK: "module-name": "Top" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]{{.}}cas" +// CHECK: "-fcas-fs" +// CHECK-NEXT: "[[LEFT_ROOT_ID]]" +// CHECK: "-o" +// CHECK-NEXT: "[[LEFT_PCM:.*outputs.*Left-.*\.pcm]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodule-file-cache-key" +// CHECK: "[[TOP_PCM:.*outputs.*Top-.*\.pcm]]" +// CHECK: "[[TOP_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK: "-fmodule-file={{(Top=)?}}[[TOP_PCM]]" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]{{.}}module.modulemap" +// CHECK-NEXT: "[[PREFIX]]{{.}}Left.h" +// CHECK-NEXT: ] +// CHECK: "name": "Left" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "cache-key": "[[RIGHT_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK: "casfs-root-id": "[[RIGHT_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [ +// CHECK: { +// CHECK: "module-name": "Top" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]{{.}}cas" +// CHECK: "-fcas-fs" +// CHECK-NEXT: "[[RIGHT_ROOT_ID]]" +// CHECK: "-o" +// CHECK-NEXT: "[[RIGHT_PCM:.*outputs.*Right-.*\.pcm]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodule-file-cache-key" +// CHECK: "[[TOP_PCM]]" +// CHECK: "[[TOP_CACHE_KEY]]" +// CHECK: "-fmodule-file={{(Top=)?}}[[TOP_PCM]]" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]{{.}}module.modulemap" +// CHECK-NEXT: "[[PREFIX]]{{.}}Right.h" +// CHECK: ] +// CHECK: "name": "Right" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "cache-key": "[[TOP_CACHE_KEY]]" +// CHECK: "casfs-root-id": "[[TOP_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [] +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]{{.}}cas" +// CHECK: "-fcas-fs" +// CHECK-NEXT: "[[TOP_ROOT_ID]]" +// CHECK: "-o" +// CHECK-NEXT: "[[TOP_PCM]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]{{.}}module.modulemap" +// CHECK-NEXT: "[[PREFIX]]{{.}}Top.h" +// CHECK: ] +// CHECK: "name": "Top" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "translation-units": [ +// CHECK: { +// CHECK: "commands": [ +// CHECK: { +// CHECK: "cache-key": "[[TU_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK: "casfs-root-id": "[[TU_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [ +// CHECK: { +// CHECK: "module-name": "Left" +// CHECK: } +// CHECK: { +// CHECK: "module-name": "Right" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]{{.}}cas" +// CHECK: "-fcas-fs" +// CHECK-NEXT: "[[TU_ROOT_ID]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-fmodule-file-cache-key" +// CHECK: "[[LEFT_PCM]]" +// CHECK: "[[LEFT_CACHE_KEY]]" +// CHECK: "-fmodule-file-cache-key" +// CHECK: "[[RIGHT_PCM]]" +// CHECK: "[[RIGHT_CACHE_KEY]]" +// CHECK: "-fmodule-file={{(Left=)?}}[[LEFT_PCM]]" +// CHECK: "-fmodule-file={{(Right=)?}}[[RIGHT_PCM]]" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]{{.}}tu.c" +// CHECK-NEXT: ] +// CHECK: } + + + +//--- cdb.json.template +[{ + "directory" : "DIR", + "command" : "clang_tool -fsyntax-only DIR/tu.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache", + "file" : "DIR/tu.c" +}] + +//--- module.modulemap +module Top { header "Top.h" export * } +module Left { header "Left.h" export * } +module Right { header "Right.h" export * } + +//--- Top.h +#pragma once +void Top(void); + +//--- Left.h +#pragma once +#include "Top.h" +void Left(void); + +//--- Right.h +#pragma once +#include "Top.h" +void Right(void); + +//--- tu.c +#include "Left.h" +#include "Right.h" + +void tu(void) { + Top(); + Left(); + Right(); +} diff --git a/clang/test/ClangScanDeps/modules-cas.cpp b/clang/test/ClangScanDeps/modules-cas.cpp new file mode 100644 index 0000000000000..3070dabdd3c1c --- /dev/null +++ b/clang/test/ClangScanDeps/modules-cas.cpp @@ -0,0 +1,34 @@ +// REQUIRES: ondisk_cas +// RUN: rm -rf %t.dir +// RUN: rm -rf %t.cdb +// RUN: rm -rf %t_clangcl.cdb +// RUN: rm -rf %t.module-cache +// RUN: rm -rf %t.module-cache_clangcl +// RUN: mkdir -p %t.dir +// RUN: cp %s %t.dir/modules_cdb_input.cpp +// RUN: cp %s %t.dir/modules_cdb_input2.cpp +// RUN: mkdir %t.dir/Inputs +// RUN: cp %S/Inputs/header.h %t.dir/Inputs/header.h +// RUN: cp %S/Inputs/header2.h %t.dir/Inputs/header2.h +// RUN: cp %S/Inputs/module.modulemap %t.dir/Inputs/module.modulemap +// RUN: sed -e "s|DIR|%/t.dir|g" %S/Inputs/modules_cdb.json > %t.cdb +// +// RUN: clang-scan-deps -cas-path %t.dir/cas -format experimental-tree -compilation-database %t.cdb -j 1 -mode preprocess-dependency-directives | \ +// RUN: FileCheck %s + +#include "header.h" + +/// Check simple output tree output. There are 4 entries in the cdb, +/// First one is for modules_cdb_input2.cpp +// CHECK: tree llvmcas://{{[[:xdigit:]]+}} +// CHECK-SAME: modules_cdb_input2.cpp + +/// Second one is for modules_cdb_input1.cpp, but it needs to load the header and the module. +// CHECK: tree llvmcas://{{[[:xdigit:]]+}} +// CHECK-SAME: modules_cdb_input.cpp + +/// Third and fourth only need to load module, thus they return the same hash. +// CHECK: tree [[HASH:llvmcas://[[:xdigit:]]+]] +// CHECK-SAME: modules_cdb_input.cpp +// CHECK: tree {{.*}}[[HASH]] +// CHECK-SAME: modules_cdb_input.cpp diff --git a/clang/test/ClangScanDeps/modules-include-tree-api-notes.c b/clang/test/ClangScanDeps/modules-include-tree-api-notes.c new file mode 100644 index 0000000000000..42a3c9a617897 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-api-notes.c @@ -0,0 +1,93 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// Extract the include-tree commands +// RUN: %deps-to-rsp %t/deps.json --module-name Top > %t/Top.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name Left > %t/Left.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp + +// Extract include-tree casids +// RUN: cat %t/Top.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Top.casid +// RUN: cat %t/Left.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Left.casid +// RUN: cat %t/tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Top.casid | FileCheck %s -check-prefix=WITH-APINOTES +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Left.casid | FileCheck %s -check-prefix=WITHOUT-APINOTES +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid | FileCheck %s -check-prefix=WITHOUT-APINOTES + +// WITH-APINOTES: APINotes: +// WITH-APINOTES-NEXT: llvmcas:// +// WITH-APINOTES-NEXT: Name: Top +// WITH-APINOTES-NEXT: Functions: +// WITH-APINOTES-NEXT: - Name: top +// WITH-APINOTES-NEXT: Availability: none +// WITH-APINOTES-NEXT: AvailabilityMsg: "don't use this" +// WITHOUT-APINOTES-NOT: APINotes: + +// Build the include-tree commands +// RUN: %clang @%t/Top.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_MISS +// Ensure the pcm comes from the action cache +// RUN: rm -rf %t/outputs +// RUN: %clang @%t/Left.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_MISS +// RUN: rm -rf %t/outputs +// RUN: %clang @%t/tu.rsp -verify -fno-cache-compile-job + +// Check cache hits +// RUN: %clang @%t/Top.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_HIT +// RUN: %clang @%t/Left.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_HIT + +// CACHE_MISS: compile job cache miss +// CACHE_HIT: compile job cache hit + +// Check incompatible with -fapinotes +// RUN: not %clang -cc1 -fcas-include-tree @%t/Top.casid -fapinotes \ +// RUN: -fcas-path %t/cas -fsyntax-only 2>&1 | \ +// RUN: FileCheck %s -check-prefix=INCOMPATIBLE + +// INCOMPATIBLE: error: missing '-x' input kind when using '-fcas-include-tree' +// INCOMPATIBLE: error: passing incompatible option '-fapinotes' with '-fcas-include-tree' + +//--- cdb.json.template +[{ + "file": "DIR/tu.m", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.m -I DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache -fapinotes-modules -iapinotes-modules DIR" +}] + +//--- module.modulemap +module Top { header "Top.h" export *} +module Left { header "Left.h" export *} + +//--- Top.h +#pragma once +struct Top { + int x; +}; +void top(void); + +//--- Left.h +#include "Top.h" +void left(void); + +//--- tu.m +#import "Left.h" + +void tu(void) { + top(); // expected-error {{'top' is unavailable: don't use this}} + left(); +} +// expected-note@Top.h:5{{'top' has been explicitly marked unavailable here}} + +//--- Top.apinotes +Name: Top +Functions: + - Name: top + Availability: none + AvailabilityMsg: "don't use this" diff --git a/clang/test/ClangScanDeps/modules-include-tree-by-mod-name.c b/clang/test/ClangScanDeps/modules-include-tree-by-mod-name.c new file mode 100644 index 0000000000000..cd5edf5120320 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-by-mod-name.c @@ -0,0 +1,101 @@ +// UNSUPPORTED: target=powerpc64-ibm-aix{{.*}} +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t + +//--- module.modulemap +module root { header "root.h" } +module direct { header "direct.h" } +module transitive { header "transitive.h" } +//--- root.h +#include "direct.h" +#include "root/textual.h" +//--- direct.h +#include "transitive.h" +//--- transitive.h +// empty + +//--- root/textual.h +// This is here to verify that the "root" directory doesn't clash with name of +// the "root" module. + +//--- cdb.json.template +[{ + "file": "", + "directory": "DIR", + "command": "clang -fmodules -fmodules-cache-path=DIR/cache -I DIR -x c" +}] + +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -format experimental-include-tree-full -module-name=root > %t/result.json +// RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck -DPREFIX=%/t %s + +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[DIRECT_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "cas-include-tree-id": "[[LEFT_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "transitive" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "{{.*transitive-.*\.pcm}}" +// CHECK-NEXT: "[[TRANSITIVE_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/direct.h" +// CHECK-NEXT: ], +// CHECK-NEXT: "link-libraries": [], +// CHECK-NEXT: "name": "direct" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[ROOT_CACHE_KEY:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "cas-include-tree-id": "[[ROOT_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "direct" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "{{.*direct-.*\.pcm}}" +// CHECK-NEXT: "[[DIRECT_CACHE_KEY]]" +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/root.h" +// CHECK-NEXT: "[[PREFIX]]/root/textual.h" +// CHECK-NEXT: ], +// CHECK-NEXT: "link-libraries": [], +// CHECK-NEXT: "name": "root" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key": "[[TRANSITIVE_CACHE_KEY]]" +// CHECK-NEXT: "cas-include-tree-id": "[[TRANSITIVE_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/transitive.h" +// CHECK-NEXT: ], +// CHECK-NEXT: "link-libraries": [], +// CHECK-NEXT: "name": "transitive" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "translation-units": [] +// CHECK-NEXT: } diff --git a/clang/test/ClangScanDeps/modules-include-tree-dependency-file.c b/clang/test/ClangScanDeps/modules-include-tree-dependency-file.c new file mode 100644 index 0000000000000..fa5c123975401 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-dependency-file.c @@ -0,0 +1,62 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -format experimental-include-tree-full > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module-name Mod_Private > %t/private.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name Mod > %t/mod.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/private.rsp +// RUN: %clang @%t/mod.rsp +// RUN: %clang @%t/tu.rsp -dependency-dot %t/tu.dot +/// Check dependency file is generated. +// RUN: find %t/module-cache -name "*.d" | wc -l | grep 2 +// RUN: FileCheck %s -input-file=%t/tu.d + +// CHECK: dependencies: +// CHECK-DAG: tu.m +// CHECK-DAG: A.h + +// RUN: FileCheck %s -input-file=%t/tu.dot -check-prefix DOT +// DOT: digraph "dependencies" +// DOT-DAG: [[TU:header_[0-9]+]] [ shape="box", label="{{.*}}{{/|\\}}tu.m"]; +// DOT-DAG: [[HEADER:header_[0-9]+]] [ shape="box", label="{{.*}}{{/|\\}}A.h"]; +// DOT-DAG: [[PCM:header_[0-9]+]] [ shape="box", label="{{.*}}{{/|\\}}Mod-{{.*}}.pcm"]; +// DOT-DAG: [[TU]] -> [[HEADER]] +// DOT-DAG: [[HEADER]] -> [[PCM]] + +//--- cdb.json.template +[{ + "file": "DIR/tu.m", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.m -F DIR -I DIR -fmodule-name=A -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -MMD -MT dependencies -MF DIR/tu.d" +}] + +//--- Mod.framework/Modules/module.modulemap +framework module Mod { header "Mod.h" } + +//--- Mod.framework/Modules/module.private.modulemap +framework module Mod_Private { header "Priv.h" } + +//--- module.modulemap +module A { + header "A.h" + export * +} + +//--- A.h +#include + +//--- Mod.framework/Headers/Mod.h +#include +void pub(void); + +//--- Mod.framework/PrivateHeaders/Priv.h +void priv(void); + +//--- tu.m +#import "A.h" diff --git a/clang/test/ClangScanDeps/modules-include-tree-diag-opts.c b/clang/test/ClangScanDeps/modules-include-tree-diag-opts.c new file mode 100644 index 0000000000000..d21f752fe4360 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-diag-opts.c @@ -0,0 +1,29 @@ +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -format experimental-include-tree-full -cas-path %t/cas > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module-name Mod > %t/Mod.rsp + +// RUN: %clang @%t/Mod.rsp +// RUN: %clang @%t/Mod.rsp -fmessage-length=8 +// RUN: %clang @%t/Mod.rsp -fcolor-diagnostics +// RUN: %clang @%t/Mod.rsp -ferror-limit 1 + +//--- cdb.json.template +[{ + "file": "DIR/tu.c", + "directory": "DIR", + "command": "clang -Xclang -fcache-disable-replay -fsyntax-only DIR/tu.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}] + +//--- module.modulemap +module Mod { header "Mod.h" } + +//--- Mod.h + +//--- tu.c +#include "Mod.h" diff --git a/clang/test/ClangScanDeps/modules-include-tree-export-as.c b/clang/test/ClangScanDeps/modules-include-tree-export-as.c new file mode 100644 index 0000000000000..64c51eb46c9c0 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-export-as.c @@ -0,0 +1,67 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// Extract the include-tree commands +// RUN: %deps-to-rsp %t/deps.json --module-name Top > %t/Top.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name Left > %t/Left.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp + +// Extract include-tree casids +// RUN: cat %t/Top.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Top.casid +// RUN: cat %t/Left.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Left.casid +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Top.casid | FileCheck %s -check-prefix=TOP +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Left.casid | FileCheck %s -check-prefix=LEFT + +// TOP: Module Map: +// TOP-NEXT: Top +// TOP-NEXT: export_as Left +// LEFT: Module Map: +// LEFT-NEXT Left +// LEFT-NOT: export_as + +//--- cdb.json.template +[{ + "file": "DIR/tu.m", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.m -I DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache -fapinotes-modules -iapinotes-modules DIR" +}] + +//--- module.modulemap +module Top { + header "Top.h" + export_as Left + export * +} +module Left { + header "Left.h" + export * +} + +//--- Top.h +#pragma once +struct Top { + int x; +}; +void top(void); + +//--- Left.h +#include "Top.h" +void left(void); + +//--- tu.m +#import "Left.h" + +void tu(void) { + top(); // expected-error {{'top' is unavailable: don't use this}} + left(); +} +// expected-note@Top.h:5{{'top' has been explicitly marked unavailable here}} + diff --git a/clang/test/ClangScanDeps/modules-include-tree-exports.c b/clang/test/ClangScanDeps/modules-include-tree-exports.c new file mode 100644 index 0000000000000..0884e2852320d --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-exports.c @@ -0,0 +1,128 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -j 1 \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// Extract the include-tree commands +// RUN: %deps-to-rsp %t/deps.json --module-name TwoSubs > %t/TwoSubs.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name ExportExplicit > %t/ExportExplicit.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name ExportWildcard > %t/ExportWildcard.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name ExportGlobalWildcard > %t/ExportGlobalWildcard.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name NoExports > %t/NoExports.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu_export_explicit.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 1 > %t/tu_export_wildcard.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 2 > %t/tu_export_global_wildcard.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 3 > %t/tu_export_none.rsp + +// Build +// RUN: %clang @%t/TwoSubs.rsp +// RUN: %clang @%t/ExportExplicit.rsp +// RUN: %clang @%t/ExportWildcard.rsp +// RUN: %clang @%t/ExportGlobalWildcard.rsp +// RUN: %clang @%t/NoExports.rsp +// RUN: not %clang @%t/tu_export_explicit.rsp 2>&1 | FileCheck %s -check-prefix=tu_export_explicit +// RUN: %clang @%t/tu_export_wildcard.rsp 2>&1 | FileCheck %s -check-prefix=tu_export_wildcard -allow-empty +// RUN: %clang @%t/tu_export_global_wildcard.rsp 2>&1 | FileCheck %s -check-prefix=tu_export_global_wildcard -allow-empty +// RUN: not %clang @%t/tu_export_none.rsp 2>&1 | FileCheck %s -check-prefix=tu_export_none + +//--- cdb.json.template +[ +{ + "file": "DIR/tu_export_explicit.c", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu_export_explicit.c -I DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}, +{ + "file": "DIR/tu_export_wildcard.c", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu_export_wildcard.c -I DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}, +{ + "file": "DIR/tu_export_global_wildcard.c", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu_export_global_wildcard.c -I DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}, +{ + "file": "DIR/tu_export_none.c", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu_export_none.c -I DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}, +] + +//--- module.modulemap +module TwoSubs { + module Sub1 { header "Sub1.h" } + module Sub2 { header "Sub2.h" } +} + +module ExportExplicit { + header "Import.h" + export TwoSubs.Sub2 +} + +module ExportWildcard { + header "Import.h" + export TwoSubs.* +} + +module ExportGlobalWildcard { + header "Import.h" + export * +} + +module NoExports { + header "Import.h" +} + +//--- Sub1.h +void sub1(void); + +//--- Sub2.h +void sub2(void); + +//--- Import.h +#include "Sub1.h" +#include "Sub2.h" + +//--- tu_export_explicit.c +#pragma clang module import ExportExplicit +void tu1(void) { + sub2(); + // tu_export_explicit-NOT: error + sub1(); + // tu_export_explicit: error: call to undeclared function 'sub1' + // tu_export_explicit: error: missing '#include "Sub1.h"' +} + +//--- tu_export_wildcard.c +#pragma clang module import ExportWildcard +void tu1(void) { + sub1(); + sub2(); + // tu_export_wildcard-NOT: error +} + +//--- tu_export_global_wildcard.c +#pragma clang module import ExportGlobalWildcard +void tu1(void) { + sub1(); + sub2(); + // tu_export_global_wildcard-NOT: error +} + +//--- tu_export_none.c +#pragma clang module import NoExports +void tu1(void) { + sub1(); + // tu_export_none: error: call to undeclared function 'sub1' + // tu_export_none: error: missing '#include "Sub1.h"' + sub2(); + // tu_export_none: error: call to undeclared function 'sub2' + // tu_export_none: error: missing '#include "Sub2.h"' +} diff --git a/clang/test/ClangScanDeps/modules-include-tree-has-include-umbrella-header.c b/clang/test/ClangScanDeps/modules-include-tree-has-include-umbrella-header.c new file mode 100644 index 0000000000000..d53c33604a68d --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-has-include-umbrella-header.c @@ -0,0 +1,60 @@ +// This test checks that __has_include() in a module does +// not clobber #include in importers of said module. + +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file %s %t + +//--- cdb.json.template +[{ + "file": "DIR/tu.c", + "directory": "DIR", + "command": "clang DIR/tu.c -fmodules -fmodules-cache-path=DIR/cache -I DIR/modules -F DIR/frameworks -o DIR/tu.o" +}] + +//--- frameworks/FW.framework/Modules/module.private.modulemap +framework module FW_Private { + umbrella header "A.h" + module * { export * } +} +//--- frameworks/FW.framework/PrivateHeaders/A.h +#include +//--- frameworks/FW.framework/PrivateHeaders/B.h +#include "dependency.h" + +//--- modules/module.modulemap +module Poison { header "poison.h" } +module Import { header "import.h" } +module Dependency { header "dependency.h" } +//--- modules/poison.h +#if __has_include() +#define HAS_B 1 +#else +#define HAS_B 0 +#endif +//--- modules/import.h +#include +//--- modules/dependency.h + +//--- tu.c +#include "poison.h" + +#if __has_include() +#endif + +#include "import.h" + +#include + +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-include-tree-full -cas-path %t/cas > %t/deps.json +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: cat %t/tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid | FileCheck %s -DPREFIX=%/t + +// Let's check that the TU actually imports FW_Private.B instead of treating FW/B.h as textual. +// CHECK: [[PREFIX]]/tu.c llvmcas:// +// CHECK-NEXT: 1:1 llvmcas:// +// CHECK-NEXT: 2:1 (Module) Poison +// CHECK-NEXT: 7:1 (Module) Import +// CHECK-NEXT: 9:1 (Module) FW_Private.B diff --git a/clang/test/ClangScanDeps/modules-include-tree-implementation-private.c b/clang/test/ClangScanDeps/modules-include-tree-implementation-private.c new file mode 100644 index 0000000000000..9bbb37da68f40 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-implementation-private.c @@ -0,0 +1,76 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// RUN: FileCheck %s -input-file %t/deps.json -check-prefix=NO_MODULES +// NO_MODULES: "modules": [] + +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: cat %t/tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid | FileCheck %s -DPREFIX=%/t +// RUN: %clang @%t/tu.rsp +// +// RUN: FileCheck %s -input-file=%t/tu.d -check-prefix DEPS + +// DEPS: dependencies: +// DEPS-DAG: tu.m +// DEPS-DAG: Mod.h +// DEPS-DAG: Priv.h + +// CHECK: [[PREFIX]]/tu.m llvmcas:// +// CHECK: 1:1 llvmcas:// +// CHECK: 2:1 [[PREFIX]]/Mod.framework/Headers/Mod.h llvmcas:// +// CHECK: Submodule: Mod +// CHECK: 3:1 [[PREFIX]]/Mod.framework/PrivateHeaders/Priv.h llvmcas:// +// CHECK: Submodule: Mod_Private +// CHECK: 4:1 (Module for visibility only) Mod +// CHECK: 5:1 (Module for visibility only) Mod_Private +// CHECK: Module Map: +// CHECK: Mod (framework) +// CHECK: link Mod (framework) +// CHECK: Mod_Private (framework) +// CHECK: link Mod (framework) + +// CHECK: Files: +// CHECK: [[PREFIX]]/tu.m llvmcas:// +// CHECK-NOT: [[PREFIX]]/module.modulemap +// CHECK: [[PREFIX]]/Mod.framework/Headers/Mod.h llvmcas:// +// CHECK-NOT: [[PREFIX]]/module.modulemap +// CHECK: [[PREFIX]]/Mod.framework/PrivateHeaders/Priv.h llvmcas:// +// CHECK-NOT: [[PREFIX]]/module.modulemap + +//--- cdb.json.template +[{ + "file": "DIR/tu.m", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.m -F DIR -fmodule-name=Mod -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -MMD -MT dependencies -MF DIR/tu.d" +}] + +//--- Mod.framework/Modules/module.modulemap +framework module Mod { header "Mod.h" } + +//--- Mod.framework/Modules/module.private.modulemap +framework module Mod_Private { header "Priv.h" } + +//--- Mod.framework/Headers/Mod.h +void pub(void); + +//--- Mod.framework/PrivateHeaders/Priv.h +void priv(void); + +//--- tu.m +#import +#import +#import +#import +void tu(void) { + pub(); + priv(); +} diff --git a/clang/test/ClangScanDeps/modules-include-tree-implementation-transitive.c b/clang/test/ClangScanDeps/modules-include-tree-implementation-transitive.c new file mode 100644 index 0000000000000..9bed48624b3e4 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-implementation-transitive.c @@ -0,0 +1,50 @@ +// REQUIRES: ondisk_cas + +// This test checks that imports of transitively-loaded implementation module +// are not marked as spurious. + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.in > %t/cdb.json + +//--- cdb.json.in +[{ + "file": "DIR/tu.m", + "directory": "DIR", + "command": "clang -c DIR/tu.m -o DIR/tu.o -F DIR/frameworks -fmodules -fmodule-name=FW -fmodules-cache-path=DIR/module-cache" +}] + +//--- frameworks/FW.framework/Modules/module.modulemap +framework module FW { umbrella header "FW.h" } +//--- frameworks/FW.framework/Headers/FW.h +#include +//--- frameworks/FW.framework/Headers/Sub.h + +//--- module.modulemap +module Mod { header "Mod.h" } +//--- Mod.h +#include +//--- tu.m +#include "Mod.h" +#include + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -format experimental-include-tree-full -cas-path %t/cas \ +// RUN: -module-files-dir %t/cas-outputs > %t/cas-deps.json + +// RUN: %deps-to-rsp %t/cas-deps.json --module-name=FW > %t/cas-FW.cc1.rsp +// RUN: %deps-to-rsp %t/cas-deps.json --module-name=Mod > %t/cas-Mod.cc1.rsp +// RUN: %deps-to-rsp %t/cas-deps.json --tu-index=0 > %t/cas-tu.rsp + +// RUN: cat %t/cas-tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid > %t/tu-include-tree.txt +// RUN: FileCheck %s -input-file %t/tu-include-tree.txt -DPREFIX=%/t +// CHECK: [[PREFIX]]/tu.m llvmcas:// +// CHECK-NEXT: 1:1 llvmcas:// +// CHECK-NEXT: 2:1 (Module) Mod +// CHECK-NEXT: 3:1 [[PREFIX]]/frameworks/FW.framework/Headers/Sub.h llvmcas://{{.*}} +// CHECK-NEXT: Submodule: FW + +// RUN: %clang @%t/cas-FW.cc1.rsp +// RUN: %clang @%t/cas-Mod.cc1.rsp +// RUN: %clang @%t/cas-tu.rsp diff --git a/clang/test/ClangScanDeps/modules-include-tree-implementation-via-spurious.c b/clang/test/ClangScanDeps/modules-include-tree-implementation-via-spurious.c new file mode 100644 index 0000000000000..5302847a8bcd3 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-implementation-via-spurious.c @@ -0,0 +1,74 @@ +// REQUIRES: ondisk_cas + +// This test checks that we correctly handle situations where a spurious modular +// dependency (1) turns otherwise textual dependency into modular (2). +// +// (1) For example #include , where the framework Spurious +// has an umbrella header that does not include Missing.h, making it a textual +// include instead. +// +// (2) For example when compiling the implementation file of a module Mod, +// #included headers belonging to Mod are treated textually, unless some other +// module already depends on Mod in its modular form. + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.in > %t/cdb.json + +//--- cdb.json.in +[{ + "file": "DIR/tu.m", + "directory": "DIR", + "command": "clang -c DIR/tu.m -o DIR/tu.o -F DIR/frameworks -I DIR/include -fmodule-name=Mod -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}] + +//--- frameworks/Spurious.framework/Modules/module.modulemap +framework module Spurious { + umbrella header "Spurious.h" + module * { export * } +} +//--- frameworks/Spurious.framework/Headers/Spurious.h +#include +//--- frameworks/Spurious.framework/Headers/Missing.h + +//--- include/module.modulemap +module Mod { header "Mod.h" } +//--- include/Mod.h +typedef int mod_int; + +//--- tu.m +#include +#include +static mod_int x; + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -format experimental-full \ +// RUN: -module-files-dir %t/outputs > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module-name=Mod > %t/Mod.cc1.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name=Spurious > %t/Spurious.cc1.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index=0 > %t/tu.rsp + +// RUN: %clang @%t/Mod.cc1.rsp +// RUN: %clang @%t/Spurious.cc1.rsp +// RUN: %clang @%t/tu.rsp + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -format experimental-include-tree-full -cas-path %t/cas \ +// RUN: -module-files-dir %t/cas-outputs > %t/cas-deps.json + +// RUN: %deps-to-rsp %t/cas-deps.json --module-name=Mod > %t/cas-Mod.cc1.rsp +// RUN: %deps-to-rsp %t/cas-deps.json --module-name=Spurious > %t/cas-Spurious.cc1.rsp +// RUN: %deps-to-rsp %t/cas-deps.json --tu-index=0 > %t/cas-tu.rsp + +// RUN: cat %t/cas-tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid > %t/tu-include-tree.txt +// RUN: FileCheck %s -input-file %t/tu-include-tree.txt -DPREFIX=%/t +// CHECK: [[PREFIX]]/tu.m llvmcas:// +// CHECK-NEXT: 1:1 llvmcas:// +// CHECK-NEXT: 2:1 (Spurious import) (Module) Spurious.Missing [[PREFIX]]/frameworks/Spurious.framework/Headers/Missing.h llvmcas:// +// CHECK-NEXT: 3:1 (Module for visibility only) Mod + +// RUN: %clang @%t/cas-Mod.cc1.rsp +// RUN: %clang @%t/cas-Spurious.cc1.rsp +// RUN: %clang @%t/cas-tu.rsp diff --git a/clang/test/ClangScanDeps/modules-include-tree-implementation.c b/clang/test/ClangScanDeps/modules-include-tree-implementation.c new file mode 100644 index 0000000000000..282742ad803f1 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-implementation.c @@ -0,0 +1,70 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -j 1 \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// RUN: FileCheck %s -input-file %t/deps.json -check-prefix=NO_MODULES +// NO_MODULES: "modules": [] + +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: cat %t/tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid | FileCheck %s -DPREFIX=%/t +// RUN: %clang @%t/tu.rsp + +// CHECK: [[PREFIX]]/tu.c llvmcas://{{[[:xdigit:]]+}} +// CHECK: 1:1 llvmcas://{{[[:xdigit:]]+}} + +// Note: this is surprising, but correct: when building the implementation files +// of a module, the first include is textual but still uses the submodule +// machinery. The second include is treated as a module import (unless in a PCH) +// but will not actually import the module only trigger visibility changes. + +// CHECK: 2:1 [[PREFIX]]/Mod.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: Submodule: Mod +// CHECK: 3:1 (Module for visibility only) Mod + +// CHECK: Files: +// CHECK: [[PREFIX]]/tu.c llvmcas://{{[[:xdigit:]]+}} +// CHECK-NOT: [[PREFIX]]/module.modulemap +// CHECK: [[PREFIX]]/Mod.h llvmcas://{{[[:xdigit:]]+}} +// CHECK-NOT: [[PREFIX]]/module.modulemap + +// RUN: %deps-to-rsp %t/deps.json --tu-index 1 > %t/tu_missing_module.rsp +// RUN: %clang @%t/tu_missing_module.rsp + +//--- cdb.json.template +[ +{ + "file": "DIR/tu.c", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.c -I DIR -fmodule-name=Mod -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}, +{ + "file": "DIR/tu_missing_module.c", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu_missing_module.c -I DIR -fmodule-name=NonExistent -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +} +] + +//--- module.modulemap +module Mod { header "Mod.h" } + +//--- Mod.h +#pragma once +void top(void); + +//--- tu.c +#include "Mod.h" +#include "Mod.h" +void tu(void) { + top(); +} + +//--- tu_missing_module.c + diff --git a/clang/test/ClangScanDeps/modules-include-tree-inferred.m b/clang/test/ClangScanDeps/modules-include-tree-inferred.m new file mode 100644 index 0000000000000..df0668d54e8bd --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-inferred.m @@ -0,0 +1,48 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module-name Mod > %t/Mod.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: cat %t/Mod.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Mod.casid +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Mod.casid | FileCheck %s -DPREFIX=%/t +// RUN: %clang @%t/Mod.rsp +// RUN: %clang @%t/tu.rsp + +// CHECK: llvmcas:// +// CHECK: 1:1 llvmcas:// +// CHECK: 2:1 [[PREFIX]]/Mod.framework/Headers/Mod.h llvmcas:// +// CHECK: Submodule: Mod +// CHECK: Module Map: +// CHECK: Mod (framework) +// CHECK: link Mod (framework) +// CHECK: Files: +// CHECK-NOT: [[PREFIX]]/module.modulemap +// CHECK: [[PREFIX]]/Mod.framework/Headers/Mod.h llvmcas:// +// CHECK-NOT: [[PREFIX]]/module.modulemap + +//--- cdb.json.template +[{ + "file": "DIR/tu.m", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.m -F DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}] + +//--- module.modulemap +framework module * {} + +//--- Mod.framework/Headers/Mod.h +void pub(void); + +//--- tu.m +#import +void tu(void) { + pub(); +} diff --git a/clang/test/ClangScanDeps/modules-include-tree-missing-submodule.c b/clang/test/ClangScanDeps/modules-include-tree-missing-submodule.c new file mode 100644 index 0000000000000..81408220f3e99 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-missing-submodule.c @@ -0,0 +1,92 @@ +// Ensure we fallback to textual inclusion for headers in incomplete umbrellas. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: sed "s|DIR|%/t|g" %t/cdb_pch.json.template > %t/cdb_pch.json + +// RUN: clang-scan-deps -compilation-database %t/cdb_pch.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_pch.json + +// RUN: %deps-to-rsp %t/deps_pch.json --module-name Foo > %t/Foo.rsp +// RUN: %deps-to-rsp %t/deps_pch.json --tu-index 0 > %t/pch.rsp +// RUN: %clang @%t/Foo.rsp +// RUN: %clang @%t/pch.rsp + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp + +// Extract include-tree casids +// RUN: cat %t/Foo.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Foo.casid +// RUN: cat %t/tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid + +// RUN: echo "MODULE Foo" > %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Foo.casid >> %t/result.txt +// RUN: echo "TRANSLATION UNIT" >> %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid >> %t/result.txt +// RUN: FileCheck %s -input-file %t/result.txt -DPREFIX=%/t + +// CHECK-LABEL: MODULE Foo +// CHECK: llvmcas:// +// CHECK: 1:1 llvmcas:// +// CHECK: 2:1 [[PREFIX]]/Foo.framework/Headers/Foo.h llvmcas:// +// CHECK: Submodule: Foo +// CHECK-NOT: Bar +// CHECK: Module Map: +// CHECK: Foo (framework) +// CHECK-NOT: Bar +// CHECK: module * +// CHECK-NOT: Bar + +// CHECK-LABEL: TRANSLATION UNIT +// CHECK: (PCH) llvmcas:// +// CHECK: [[PREFIX]]/tu.c llvmcas:// +// CHECK: 1:1 llvmcas:// +// CHECK: 2:1 (Spurious import) (Module) Foo.Bar [[PREFIX]]/Foo.framework/Headers/Bar.h llvmcas:// + +// RUN: %clang @%t/tu.rsp + +//--- cdb_pch.json.template +[{ + "file": "DIR/prefix.h", + "directory": "DIR", + "command": "clang -x c-header DIR/prefix.h -o DIR/prefix.h.pch -F DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache" +}] + +//--- cdb.json.template +[{ + "file": "DIR/tu.c", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.c -include prefix.h -F DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache" +}] + +//--- Foo.framework/Modules/module.modulemap +framework module Foo { + umbrella header "Foo.h" + module * { export * } +} + +//--- Foo.framework/Headers/Foo.h +// Do not import Bar.h +void foo(void); + +//--- Foo.framework/Headers/Bar.h +void bar(void); + +//--- prefix.h +#include + +//--- tu.c +#include +// FIXME: -Wincomplete-umbrella warning +void tu(void) { + bar(); +} diff --git a/clang/test/ClangScanDeps/modules-include-tree-pch-with-private.c b/clang/test/ClangScanDeps/modules-include-tree-pch-with-private.c new file mode 100644 index 0000000000000..3104a5b68badc --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-pch-with-private.c @@ -0,0 +1,122 @@ +// Test importing a private module whose public module was previously imported +// via a PCH. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: sed "s|DIR|%/t|g" %t/cdb_pch.json.template > %t/cdb_pch.json + +// RUN: clang-scan-deps -compilation-database %t/cdb_pch.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_pch.json + +// RUN: %deps-to-rsp %t/deps_pch.json --module-name Mod > %t/Mod.rsp +// RUN: %deps-to-rsp %t/deps_pch.json --module-name Indirect1 > %t/Indirect1.rsp +// RUN: %deps-to-rsp %t/deps_pch.json --tu-index 0 > %t/pch.rsp +// RUN: %clang @%t/Mod.rsp +// RUN: %clang @%t/Indirect1.rsp +// RUN: %clang @%t/pch.rsp + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module-name Mod_Private > %t/Mod_Private.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name Indirect2 > %t/Indirect2.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/Mod_Private.rsp +// RUN: %clang @%t/Indirect2.rsp +// RUN: %clang @%t/tu.rsp + +// Extract include-tree casids +// RUN: cat %t/Indirect2.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Indirect.casid +// RUN: cat %t/tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid + +// RUN: echo "MODULE Indirect2" > %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Indirect.casid >> %t/result.txt +// RUN: echo "TRANSLATION UNIT" >> %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid >> %t/result.txt + +// Explicitly check that Mod_Private is imported as a module and not a header. +// RUN: FileCheck %s -DPREFIX=%/t -input-file %t/result.txt + +// CHECK-LABEL: MODULE Indirect2 +// CHECK: llvmcas:// +// CHECK: 1:1 llvmcas:// +// CHECK: 2:1 [[PREFIX]]/indirect2.h llvmcas:// +// CHECK: Submodule: Indirect2 +// CHECK: 2:1 (Module) Indirect1 +// CHECK: 3:1 (Module) Mod_Private + +// CHECK-LABEL: TRANSLATION UNIT +// CHECK: (PCH) llvmcas:// +// CHECK: [[PREFIX]]/tu.m llvmcas:// +// CHECK: 1:1 llvmcas:// +// CHECK: 2:1 (Module) Mod_Private +// CHECK: 3:1 (Module) Indirect2 + +//--- cdb.json.template +[{ + "file": "DIR/tu.m", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.m -include prefix.h -F DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}] + +//--- cdb_pch.json.template +[{ + "file": "DIR/prefix.h", + "directory": "DIR", + "command": "clang -x objective-c-header DIR/prefix.h -o DIR/prefix.h.pch -F DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}] + +//--- Mod.framework/Modules/module.modulemap +framework module Mod { header "Mod.h" } + +//--- Mod.framework/Modules/module.private.modulemap +framework module Mod_Private { header "Priv.h" } + +//--- Mod.framework/Headers/Mod.h +void pub(void); + +//--- Mod.framework/PrivateHeaders/Priv.h +void priv(void); + +//--- module.modulemap +module Indirect1 { + header "indirect1.h" + export * +} +module Indirect2 { + header "indirect2.h" + export * +} + +//--- indirect1.h +#import + +//--- indirect2.h +#import "indirect1.h" +#import + +static inline void indirect(void) { + pub(); + priv(); +} + +//--- prefix.h +#import +#import "indirect1.h" + +//--- tu.m +#import +#import "indirect2.h" + +void tu(void) { + pub(); + priv(); + indirect(); +} diff --git a/clang/test/ClangScanDeps/modules-include-tree-prefix-map.c b/clang/test/ClangScanDeps/modules-include-tree-prefix-map.c new file mode 100644 index 0000000000000..8eb7da7be4bea --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-prefix-map.c @@ -0,0 +1,362 @@ +// REQUIRES: ondisk_cas +// REQUIRES: x86-registered-target + +// RUN: rm -rf %t +// RUN: split-file %s %t/dir1 +// RUN: cp -r %t/dir1 %t/dir2 +// RUN: sed -e "s|DIR|%/t/dir1|g" -e "s|CLANG|%clang|g" %t/dir1/cdb.json.template > %t/cdb1.json +// RUN: sed -e "s|DIR|%/t/dir2|g" -e "s|CLANG|%clang|g" %t/dir1/cdb.json.template > %t/cdb2.json + +// RUN: clang-scan-deps -compilation-database %t/cdb1.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/dir1/outputs \ +// RUN: -prefix-map=%t/dir1/outputs=/^modules -prefix-map=%t/dir1=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives -optimize-args=none \ +// RUN: > %t/deps.json + +// Extract the include-tree commands +// RUN: %deps-to-rsp %t/deps.json --module-name Top > %t/Top.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name Left > %t/Left.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name Right > %t/Right.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name System > %t/System.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp + +// Extract include-tree casids +// RUN: cat %t/Top.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Top.casid +// RUN: cat %t/Left.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Left.casid +// RUN: cat %t/Right.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Right.casid +// RUN: cat %t/System.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/System.casid +// RUN: cat %t/tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid + +// RUN: echo "MODULE Top" > %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Top.casid >> %t/result.txt +// RUN: echo "MODULE Left" >> %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Left.casid >> %t/result.txt +// RUN: echo "MODULE Right" >> %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Right.casid >> %t/result.txt +// RUN: echo "TRANSLATION UNIT" >> %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid >> %t/result.txt + +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/System.casid | grep '' | sed 's|.* llvmcas|llvmcas|' > %t/system-module-includes.casid +// RUN: echo "System module-includes" >> %t/result.txt +// RUN: llvm-cas -cas %t/cas -cat-blob @%t/system-module-includes.casid >> %t/result.txt + +// RUN: FileCheck %s -input-file %t/result.txt -DPREFIX=%/t -check-prefix=NO_PATHS +// NO_PATHS-NOT: [[PREFIX]] + +// RUN: cat %t/deps.json >> %t/result.txt + +// RUN: FileCheck %s -input-file %t/result.txt -DPREFIX=%/t + +// CHECK-LABEL: MODULE Top +// CHECK: llvmcas://{{[[:xdigit:]]+}} +// CHECK: 1:1 llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 /^src/Top.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: Module Map: +// CHECK: Top +// CHECK: export * +// CHECK: Files: +// CHECK-NOT: module.modulemap +// CHECK: /^src/Top.h llvmcas://{{[[:xdigit:]]+}} +// CHECK-NOT: module.modulemap + +// CHECK-LABEL: MODULE Left +// CHECK: llvmcas://{{[[:xdigit:]]+}} +// CHECK: 1:1 llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 /^src/Left.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 (Module) Top +// CHECK: Module Map: +// CHECK: Left +// CHECK: export * +// CHECK: Files: +// CHECK-NOT: module.modulemap +// CHECK: /^src/Left.h llvmcas://{{[[:xdigit:]]+}} +// CHECK-NOT: module.modulemap +// CHECK: /^src/Top.h llvmcas://{{[[:xdigit:]]+}} + +// CHECK-LABEL: MODULE Right +// CHECK: llvmcas://{{[[:xdigit:]]+}} +// CHECK: 1:1 llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 /^src/Right.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 (Module) Top +// CHECK: Module Map: +// CHECK: Right +// CHECK: export * +// CHECK: Files: +// CHECK-NOT: module.modulemap +// CHECK: /^src/Right.h llvmcas://{{[[:xdigit:]]+}} +// CHECK-NOT: module.modulemap +// CHECK: /^src/Top.h llvmcas://{{[[:xdigit:]]+}} + +// CHECK-LABEL: TRANSLATION UNIT +// CHECK: /^src/tu.m llvmcas://{{[[:xdigit:]]+}} +// CHECK: 1:1 llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 (Module) Left +// CHECK: 3:1 (Module) Right + +// Note: the modules with explicit imports are imported via parser and are not +// recorded in the include-tree; it's handled entirely by fmodule-map-file, +// fmodule-file, and fmodule-file-cache-key options. + +// CHECK-NOT: Module Map +// CHECK: Files: +// CHECK-NOT: module.modulemap +// CHECK: /^src/Left.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: /^src/Top.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: /^src/Right.h llvmcas://{{[[:xdigit:]]+}} + +// CHECK-LABEL: System module-includes +// CHECK-NEXT: #import "sys.h" +// CHECK-NEXT: #import "stdbool.h" + +// CHECK-NEXT: { +// CHECK-NEXT "modules": [ +// CHECK-NEXT { +// CHECK: "cas-include-tree-id": "[[LEFT_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [ +// CHECK: { +// CHECK: "module-name": "Top" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "clang-modulemap-file": "[[PREFIX]]/dir1/module.modulemap" +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK-NOT: -fmodule-map-file +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/dir1/outputs/{{.*}}/Left-{{.*}}.pcm" +// CHECK: "-disable-free" +// CHECK: "-fno-pch-timestamp" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[LEFT_TREE]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "/^modules/{{.*}}/Top-{{.*}}.pcm" +// CHECK-NEXT: "llvmcas://{{[[:xdigit:]]+}}" +// CHECK: "-fmodule-file=Top=/^modules/{{.*}}/Top-{{.*}}.pcm" +// CHECK: "-fmodules" +// CHECK: "-fmodule-name=Left" +// CHECK: "-fno-implicit-modules" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/dir1/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/dir1/Left.h" +// CHECK: ] +// CHECK: "name": "Left" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "cas-include-tree-id": "[[RIGHT_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK: "module-name": "Top" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "clang-modulemap-file": "[[PREFIX]]/dir1/module.modulemap" +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK-NOT: -fmodule-map-file +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/dir1/outputs/{{.*}}/Right-{{.*}}.pcm" +// CHECK: "-disable-free" +// CHECK: "-fno-pch-timestamp" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[RIGHT_TREE]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodule-file-cache-key +// CHECK-NEXT: "/^modules/{{.*}}/Top-{{.*}}.pcm" +// CHECK-NEXT: "llvmcas://{{[[:xdigit:]]+}}" +// CHECK: "-fmodule-file=Top=/^modules/{{.*}}/Top-{{.*}}.pcm" +// CHECK: "-fmodules" +// CHECK: "-fmodule-name=Right" +// CHECK: "-fno-implicit-modules" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/dir1/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/dir1/Right.h" +// CHECK-NEXT: ] +// CHECK: "name": "Right" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "cas-include-tree-id": "[[SYS_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [] +// CHECK: "clang-modulemap-file": "[[PREFIX]]/dir1/System/module.modulemap" +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/dir1/outputs/{{.*}}/System-{{.*}}.pcm" +// CHECK: "-disable-free" +// CHECK: "-fno-pch-timestamp" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[SYS_TREE]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodules" +// CHECK: "-fmodule-name=System" +// CHECK: "-fno-implicit-modules" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-DAG: "[[PREFIX]]/dir1/System/module.modulemap" +// CHECK-DAG: "[[PREFIX]]/dir1/System/sys.h" +// CHECK-DAG: "{{.*}}/stdbool.h" +// CHECK: ] +// CHECK: "name": "System" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "cas-include-tree-id": "[[TOP_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [] +// CHECK: "clang-modulemap-file": "[[PREFIX]]/dir1/module.modulemap" +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/dir1/outputs/{{.*}}/Top-{{.*}}.pcm" +// CHECK: "-disable-free" +// CHECK: "-fno-pch-timestamp" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[TOP_TREE]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodules" +// CHECK: "-fmodule-name=Top" +// CHECK: "-fno-implicit-modules" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/dir1/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/dir1/Top.h" +// CHECK-NEXT: ] +// CHECK: "name": "Top" +// CHECK: } +// CHECK: ] +// CHECK-NEXT: "translation-units": [ +// CHECK-NEXT: { +// CHECK-NEXT: "commands": [ +// CHECK-NEXT: { +// CHECK: "cas-include-tree-id": "[[TU_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK: "module-name": "Left" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "module-name": "Right" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "module-name": "System" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK-NOT: -fmodule-map-file +// CHECK: "-disable-free" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[TU_TREE]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-fsyntax-only" +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "/^modules/{{.*}}/Left-{{.*}}.pcm" +// CHECK-NEXT: "llvmcas://{{[[:xdigit:]]+}}" +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "/^modules/{{.*}}/Right-{{.*}}.pcm" +// CHECK-NEXT: "llvmcas://{{[[:xdigit:]]+}}" +// CHECK: "-fmodule-file=Left=/^modules/{{.*}}/Left-{{.*}}.pcm" +// CHECK: "-fmodule-file=Right=/^modules/{{.*}}/Right-{{.*}}.pcm" +// CHECK: "-fmodules" +// CHECK: "-fno-implicit-modules" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/dir1/tu.m" +// CHECK-NEXT: ] +// CHECK: "input-file": "[[PREFIX]]/dir1/tu.m" +// CHECK: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } + +// Build the include-tree commands +// RUN: %clang @%t/Top.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_MISS +// RUN: %clang @%t/Left.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_MISS +// RUN: %clang @%t/Right.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_MISS +// RUN: %clang @%t/System.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_MISS +// RUN: %clang @%t/tu.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_MISS + +// Scan in a different directory +// RUN: clang-scan-deps -compilation-database %t/cdb2.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/dir2/outputs \ +// RUN: -prefix-map=%t/dir2/outputs=/^modules -prefix-map=%t/dir2=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives -optimize-args=none \ +// RUN: > %t/deps2.json + +// RUN: %deps-to-rsp %t/deps2.json --module-name Top > %t/Top2.rsp +// RUN: %deps-to-rsp %t/deps2.json --module-name Left > %t/Left2.rsp +// RUN: %deps-to-rsp %t/deps2.json --module-name Right > %t/Right2.rsp +// RUN: %deps-to-rsp %t/deps2.json --module-name System > %t/System2.rsp +// RUN: %deps-to-rsp %t/deps2.json --tu-index 0 > %t/tu2.rsp + +// Check cache hits +// RUN: %clang @%t/Top2.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_HIT +// RUN: %clang @%t/Left2.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_HIT +// RUN: %clang @%t/Right2.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_HIT +// RUN: %clang @%t/System2.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_HIT +// RUN: %clang @%t/tu2.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_HIT + +// CACHE_MISS: compile job cache miss +// CACHE_HIT: compile job cache hit + +//--- cdb.json.template +[{ + "file": "DIR/tu.m", + "directory": "DIR", + "command": "CLANG -target x86_64-apple-darwin10 -fsyntax-only DIR/tu.m -I DIR -isystem DIR/System -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache -Xclang -fbuiltin-headers-in-system-modules" +}] + +//--- module.modulemap +module Top { header "Top.h" export *} +module Left { header "Left.h" export *} +module Right { header "Right.h" export *} + +//--- Top.h +#pragma once +struct Top { + int x; +}; +void top(void); + +//--- Left.h +#include "Top.h" +void left(void); + +//--- Right.h +#include "Top.h" +void right(void); + +//--- System/module.modulemap +module System [system] { + header "sys.h" + header "stdbool.h" +} + +//--- System/sys.h +#include +bool sys(void); + +//--- tu.m +#import "Left.h" +#import +#import + +void tu(void) { + top(); + left(); + right(); + bool b = sys(); + (void)b; +} diff --git a/clang/test/ClangScanDeps/modules-include-tree-submodules.c b/clang/test/ClangScanDeps/modules-include-tree-submodules.c new file mode 100644 index 0000000000000..91b92443258f3 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-submodules.c @@ -0,0 +1,147 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -j 1 \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// Extract the include-tree commands +// RUN: %deps-to-rsp %t/deps.json --module-name TwoSubs > %t/TwoSubs.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name WithExplicit > %t/WithExplicit.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu1.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 1 > %t/tu2.rsp + +// Extract include-tree casids +// RUN: cat %t/TwoSubs.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/TwoSubs.casid +// RUN: cat %t/WithExplicit.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/WithExplicit.casid +// RUN: cat %t/tu1.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu1.casid +// RUN: cat %t/tu2.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu2.casid + +// RUN: echo "MODULE TwoSubs" > %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/TwoSubs.casid >> %t/result.txt +// RUN: echo "MODULE WithExplicit" >> %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/WithExplicit.casid >> %t/result.txt +// RUN: echo "TRANSLATION UNIT 1" >> %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu1.casid >> %t/result.txt +// RUN: echo "TRANSLATION UNIT 2" >> %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu2.casid >> %t/result.txt + +// RUN: FileCheck %s -input-file %t/result.txt -DPREFIX=%/t + +// Build the include-tree commands +// RUN: %clang @%t/TwoSubs.rsp +// RUN: %clang @%t/WithExplicit.rsp +// RUN: not %clang @%t/tu1.rsp 2>&1 | FileCheck %s -check-prefix=TU1 +// RUN: not %clang @%t/tu2.rsp 2>&1 | FileCheck %s -check-prefix=TU2 + +// CHECK: MODULE TwoSubs +// CHECK: 2:1 [[PREFIX]]/Sub1.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: Submodule: TwoSubs.Sub1 +// CHECK: 3:1 [[PREFIX]]/Sub2.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: Submodule: TwoSubs.Sub2 + +// CHECK: MODULE WithExplicit +// CHECK: 2:1 [[PREFIX]]/TopLevel.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: Submodule: WithExplicit +// CHECK: 3:1 [[PREFIX]]/Implicit.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: Submodule: WithExplicit.Implicit +// CHECK: 4:1 [[PREFIX]]/Explicit.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: Submodule: WithExplicit.Explicit + +// CHECK: TRANSLATION UNIT 1 +// CHECK: [[PREFIX]]/tu1.c llvmcas://{{[[:xdigit:]]+}} +// CHECK: 1:1 llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 (Module) TwoSubs.Sub1 +// CHECK: 3:1 (Module) WithExplicit + +// CHECK: TRANSLATION UNIT 2 +// CHECK: [[PREFIX]]/tu2.c llvmcas://{{[[:xdigit:]]+}} +// CHECK: 1:1 llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 (Module) TwoSubs.Sub2 +// CHECK: 3:1 (Module) WithExplicit.Explicit + +//--- cdb.json.template +[ +{ + "file": "DIR/tu1.c", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu1.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}, +{ + "file": "DIR/tu2.c", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu2.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}, +] + +//--- module.modulemap +module TwoSubs { + module Sub1 { header "Sub1.h" } + module Sub2 { header "Sub2.h" } +} + +module WithExplicit { + header "TopLevel.h" + module Implicit { header "Implicit.h" } + explicit module Explicit { header "Explicit.h" } +} + +//--- Sub1.h +void sub1(void); + +//--- Sub2.h +void sub2(void); + +//--- TopLevel.h +void top(void); + +//--- Implicit.h +void implicit(void); + +//--- Explicit.h +void explicit(void); + +//--- tu1.c +#include "Sub1.h" +#include "TopLevel.h" + +void tu1(void) { + top(); + sub1(); + implicit(); + sub2(); +// TU1-NOT: error: +// TU1: error: call to undeclared function 'sub2' +// TU1: error: missing '#include "Sub2.h"' +// TU1: note: declaration here is not visible + explicit(); +// TU1: error: call to undeclared function 'explicit' +// TU1: error: missing '#include "Explicit.h"' +// TU1: Explicit.h:1:6: note: declaration here is not visible +} + +//--- tu2.c +#include "Sub2.h" +#include "Explicit.h" + +void tu2(void) { + sub2(); + explicit(); +// TU2-NOT: error: + top(); +// TU2: error: call to undeclared function 'top' +// TU2: error: missing '#include "TopLevel.h"' +// TU2: note: declaration here is not visible + sub1(); +// TU2: error: call to undeclared function 'sub1' +// TU2: error: missing '#include "Sub1.h"' +// TU2: note: declaration here is not visible + implicit(); +// TU2: error: call to undeclared function 'implicit' +// TU2: error: missing '#include "Implicit.h"' +// TU2: note: declaration here is not visible +} diff --git a/clang/test/ClangScanDeps/modules-include-tree-vfsoverlay.c b/clang/test/ClangScanDeps/modules-include-tree-vfsoverlay.c new file mode 100644 index 0000000000000..ac985578a71a0 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-vfsoverlay.c @@ -0,0 +1,100 @@ +// Check include-tree-based caching works with vfsoverlay files. + +// REQUIRES: ondisk_cas +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: sed -e "s|DIR|%/t|g" %t/vfs.yaml.template > %t/vfs.yaml + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -j 1 \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: -cas-path %t/cas > %t/deps.json + +// RUN: %deps-to-rsp %t/deps.json --module-name=A > %t/A.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/A.rsp +// RUN: %clang @%t/tu.rsp + +// Extract include-tree casids +// RUN: cat %t/A.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/A.casid +// RUN: cat %t/tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid + +// RUN: echo "MODULE A" > %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/A.casid >> %t/result.txt +// RUN: echo "TRANSLATION UNIT" >> %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid >> %t/result.txt + +// RUN: FileCheck %s -input-file %t/result.txt -DPREFIX=%/t + +// CHECK-LABEL: MODULE A +// CHECK: llvmcas:// +// CHECK: 2:1 [[PREFIX]]/elsewhere2/A.h llvmcas:// +// CHECK: Submodule: A +// CHECK: Module Map: +// CHECK: A (framework) +// CHECK: link A (framework) +// CHECK: Files: +// CHECK-NOT: modulemap +// CHECK: [[PREFIX]]/elsewhere2/A.h llvmcas:// +// CHECK-NOT: modulemap + +// CHECK-LABEL: TRANSLATION UNIT +// CHECK: Files: +// CHECK-NOT: .modulemap +// CHECK-NOT: .yaml +// CHECK: [[PREFIX]]/elsewhere2/A.h llvmcas:// +// CHECK-NOT: .modulemap +// CHECK-NOT: .yaml + +//--- cdb.json.template +[{ + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.c -fmodules -fmodules-cache-path=DIR/module-cache -fimplicit-modules -fimplicit-module-maps -ivfsoverlay DIR/vfs.yaml -F DIR", + "file": "DIR/tu.c" +}] + +//--- vfs.yaml.template +{ + "version": 0, + "case-sensitive": "false", + "roots": [ + { + "name": "DIR/A.framework", + "type": "directory" + "contents": [ + { + "name": "Modules", + "type": "directory" + "contents": [ + { + "external-contents": "DIR/elsewhere1/A.modulemap", + "name": "module.modulemap", + "type": "file" + } + ] + }, + { + "name": "Headers", + "type": "directory" + "contents": [ + { + "external-contents": "DIR/elsewhere2/A.h", + "name": "A.h", + "type": "file" + } + ] + } + ] + } + ] +} + +//--- elsewhere1/A.modulemap +framework module A { header "A.h" } + +//--- elsewhere2/A.h +typedef int A_t; + +//--- tu.c +#include "A/A.h" +A_t a = 0; diff --git a/clang/test/ClangScanDeps/modules-include-tree-with-pch.c b/clang/test/ClangScanDeps/modules-include-tree-with-pch.c new file mode 100644 index 0000000000000..d8efe277c35ab --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-with-pch.c @@ -0,0 +1,156 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: sed "s|DIR|%/t|g" %t/cdb_pch.json.template > %t/cdb_pch.json + +// Scan PCH +// RUN: clang-scan-deps -compilation-database %t/cdb_pch.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps_pch.json + +// Build PCH +// RUN: %deps-to-rsp %t/deps_pch.json --module-name Top > %t/Top.rsp +// RUN: %deps-to-rsp %t/deps_pch.json --module-name Left > %t/Left.rsp +// RUN: %deps-to-rsp %t/deps_pch.json --tu-index 0 > %t/pch.rsp +// RUN: %clang @%t/Top.rsp +// RUN: %clang @%t/Left.rsp +// RUN: rm -rf %t/outputs +// RUN: %clang @%t/pch.rsp +// RUN: rm -rf %t/outputs + +// Scan TU with PCH +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// Build TU +// RUN: %deps-to-rsp %t/deps.json --module-name Right > %t/Right.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/Right.rsp +// RUN: rm -rf %t/outputs +// RUN: %clang @%t/tu.rsp + +// RUN: FileCheck %s -input-file %t/deps.json -DPREFIX=%/t + +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK: "clang-module-deps": [] +// CHECK: "clang-modulemap-file": "[[PREFIX]]/module.modulemap" +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/outputs/{{.*}}/Right-{{.*}}.pcm" +// CHECK: "-disable-free" +// CHECK: "-fno-pch-timestamp" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[RIGHT_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodule-file=[[PREFIX]]/outputs/{{.*}}/Top-{{.*}}.pcm" +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "[[PREFIX]]/{{.*}}/Top-{{.*}}.pcm" +// CHECK-NEXT: "llvmcas://{{[[:xdigit:]]+}}" +// CHECK: "-x" +// CHECK-NEXT: "c" +// CHECK: "-fmodules" +// CHECK: "-fmodule-name=Right" +// CHECK: "-fno-implicit-modules" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/Right.h" +// CHECK-NEXT: ] +// CHECK: "name": "Right" +// CHECK: } +// CHECK-NOT: "clang-modulemap-file" +// CHECK: ] +// CHECK-NEXT: "translation-units": [ +// CHECK-NEXT: { +// CHECK-NEXT: "commands": [ +// CHECK-NEXT: { +// CHECK: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK: "module-name": "Right" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK-NOT: -fmodule-map-file= +// CHECK: "-disable-free" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "llvmcas://{{[[:xdigit:]]+}}" +// CHECK: "-fcache-compile-job" +// CHECK: "-fsyntax-only" +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "[[PREFIX]]/outputs/{{.*}}/Right-{{.*}}.pcm" +// CHECK-NEXT: "llvmcas://{{[[:xdigit:]]+}}" +// CHECK: "-x" +// CHECK-NEXT: "c" +// CHECK: "-fmodule-file=Right=[[PREFIX]]/outputs/{{.*}}/Right-{{.*}}.pcm" +// CHECK: "-fmodules" +// CHECK: "-fno-implicit-modules" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/tu.c" +// CHECK-NEXT: "[[PREFIX]]/prefix.h.pch" +// CHECK-NEXT: ] +// CHECK: "input-file": "[[PREFIX]]/tu.c" +// CHECK: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } + +//--- cdb_pch.json.template +[{ + "file": "DIR/prefix.h", + "directory": "DIR", + "command": "clang -x c-header DIR/prefix.h -o DIR/prefix.h.pch -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}] + +//--- cdb.json.template +[{ + "file": "DIR/tu.c", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.c -include DIR/prefix.h -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache" +}] + +//--- module.modulemap +module Top { header "Top.h" export *} +module Left { header "Left.h" export *} +module Right { header "Right.h" export *} + +//--- Top.h +#pragma once +struct Top { int x; }; + +//--- Left.h +#pragma once +#include "Top.h" +struct Left { struct Top top; }; + +//--- Right.h +#pragma once +#include "Top.h" +struct Right { struct Top top; }; + +//--- prefix.h +#include "Left.h" + +//--- tu.c +#include "Right.h" + +void tu(void) { + struct Left _left; + struct Right _right; + struct Top _top; +} diff --git a/clang/test/ClangScanDeps/modules-include-tree-working-directory.c b/clang/test/ClangScanDeps/modules-include-tree-working-directory.c new file mode 100644 index 0000000000000..0c6e24472f5a6 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree-working-directory.c @@ -0,0 +1,43 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: mkdir -p %t/other +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-include-tree-full -cas-path %t/cas \ +// RUN: > %t/deps.json + +// Build the include-tree command +// RUN: %deps-to-rsp %t/deps.json --module H > %t/H.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp +// RUN: %clang @%t/H.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/H.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: %clang @%t/tu.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: %clang @%t/tu.rsp -Rcompile-job-cache 2>&1 | FileCheck %s -check-prefix=CACHE-HIT + +// CACHE-MISS: remark: compile job cache miss +// CACHE-HIT: remark: compile job cache hit + +// RUN: cat %t/H.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/H.casid +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/H.casid | FileCheck %s -DPREFIX=%/t + +// CHEK:C +// CHECK: 2:1 [[PREFIX]]/relative/h1.h llvmcas:// +// CHECK: Files: +// CHECK: [[PREFIX]]/relative/h1.h llvmcas:// + +//--- cdb.json.template +[{ + "directory": "DIR/other", + "command": "clang -fsyntax-only t.c -I relative -working-directory DIR -fmodules -fimplicit-modules -fimplicit-module-maps", + "file": "DIR/t.c" +}] + +//--- relative/h1.h + +//--- relative/module.modulemap +module H { header "h1.h" } + +//--- t.c +#include "h1.h" diff --git a/clang/test/ClangScanDeps/modules-include-tree.c b/clang/test/ClangScanDeps/modules-include-tree.c new file mode 100644 index 0000000000000..de61673523e53 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-include-tree.c @@ -0,0 +1,368 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json + +// RUN: clang-scan-deps -compilation-database %t/cdb.json \ +// RUN: -cas-path %t/cas -module-files-dir %t/outputs \ +// RUN: -format experimental-include-tree-full -mode preprocess-dependency-directives \ +// RUN: > %t/deps.json + +// Extract the include-tree commands +// RUN: %deps-to-rsp %t/deps.json --module-name Top > %t/Top.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name Left > %t/Left.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name Right > %t/Right.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name ZAtImport > %t/ZAtImport.rsp +// RUN: %deps-to-rsp %t/deps.json --module-name ZPragmaImport > %t/ZPragmaImport.rsp +// RUN: %deps-to-rsp %t/deps.json --tu-index 0 > %t/tu.rsp + +// Extract include-tree casids +// RUN: cat %t/Top.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Top.casid +// RUN: cat %t/Left.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Left.casid +// RUN: cat %t/Right.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/Right.casid +// RUN: cat %t/tu.rsp | sed -E 's|.*"-fcas-include-tree" "(llvmcas://[[:xdigit:]]+)".*|\1|' > %t/tu.casid + +// RUN: echo "MODULE Top" > %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Top.casid >> %t/result.txt +// RUN: echo "MODULE Left" >> %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Left.casid >> %t/result.txt +// RUN: echo "MODULE Right" >> %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/Right.casid >> %t/result.txt +// RUN: echo "TRANSLATION UNIT" >> %t/result.txt +// RUN: clang-cas-test -cas %t/cas -print-include-tree @%t/tu.casid >> %t/result.txt +// RUN: cat %t/deps.json >> %t/result.txt + +// RUN: FileCheck %s -input-file %t/result.txt -DPREFIX=%/t + +// CHECK-LABEL: MODULE Top +// CHECK: llvmcas://{{[[:xdigit:]]+}} +// CHECK: 1:1 llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 [[PREFIX]]/Top.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: Module Map: +// CHECK: Top +// CHECK: export * +// CHECK: Files: +// CHECK-NOT: [[PREFIX]]/module.modulemap +// CHECK: [[PREFIX]]/Top.h llvmcas://{{[[:xdigit:]]+}} +// CHECK-NOT: [[PREFIX]]/module.modulemap + +// CHECK-LABEL: MODULE Left +// CHECK: llvmcas://{{[[:xdigit:]]+}} +// CHECK: 1:1 llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 [[PREFIX]]/Left.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 (Module) Top +// CHECK: Module Map: +// CHECK: Left +// CHECK: export * +// CHECK: Files: +// CHECK-NOT: [[PREFIX]]/module.modulemap llvmcas://{{[[:xdigit:]]+}} +// CHECK: [[PREFIX]]/Left.h llvmcas://{{[[:xdigit:]]+}} +// CHECK-NOT: [[PREFIX]]/module.modulemap llvmcas://{{[[:xdigit:]]+}} +// CHECK: [[PREFIX]]/Top.h llvmcas://{{[[:xdigit:]]+}} + +// CHECK-LABEL: MODULE Right +// CHECK: llvmcas://{{[[:xdigit:]]+}} +// CHECK: 1:1 llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 [[PREFIX]]/Right.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 (Module) Top +// CHECK: Module Map: +// CHECK: Right +// CHECK: export * +// CHECK: Files: +// CHECK-NOT: [[PREFIX]]/module.modulemap llvmcas://{{[[:xdigit:]]+}} +// CHECK: [[PREFIX]]/Right.h llvmcas://{{[[:xdigit:]]+}} +// CHECK-NOT: [[PREFIX]]/module.modulemap llvmcas://{{[[:xdigit:]]+}} +// CHECK: [[PREFIX]]/Top.h llvmcas://{{[[:xdigit:]]+}} + +// CHECK-LABEL: TRANSLATION UNIT +// CHECK: [[PREFIX]]/tu.m llvmcas://{{[[:xdigit:]]+}} +// CHECK: 1:1 llvmcas://{{[[:xdigit:]]+}} +// CHECK: 2:1 (Module) Left +// CHECK: 3:1 (Module) Right + +// Note: the modules with explicit imports are imported via parser and are not +// recorded in the include-tree; it's handled entirely by fmodule-map-file, +// fmodule-file, and fmodule-file-cache-key options. + +// CHECK-NOT: Module Map +// CHECK: Files: +// CHECK-NOT: [[PREFIX]]/module.modulemap llvmcas://{{[[:xdigit:]]+}} +// CHECK: [[PREFIX]]/Left.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: [[PREFIX]]/Top.h llvmcas://{{[[:xdigit:]]+}} +// CHECK: [[PREFIX]]/Right.h llvmcas://{{[[:xdigit:]]+}} + +// CHECK: { +// CHECK-NEXT "modules": [ +// CHECK-NEXT { +// CHECK: "cas-include-tree-id": "[[LEFT_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [ +// CHECK: { +// CHECK: "module-name": "Top" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "clang-modulemap-file": "[[PREFIX]]/module.modulemap" +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK-NOT: -fmodule-map-file +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/outputs/{{.*}}/Left-{{.*}}.pcm" +// CHECK: "-disable-free" +// CHECK: "-fno-pch-timestamp" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[LEFT_TREE]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "[[PREFIX]]/outputs/{{.*}}/Top-{{.*}}.pcm" +// CHECK-NEXT: "llvmcas://{{[[:xdigit:]]+}}" +// CHECK: "-fmodule-file=Top=[[PREFIX]]/outputs/{{.*}}/Top-{{.*}}.pcm" +// CHECK: "-fmodules" +// CHECK: "-fmodule-name=Left" +// CHECK: "-fno-implicit-modules" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/Left.h" +// CHECK: ] +// CHECK: "name": "Left" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "cas-include-tree-id": "[[RIGHT_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK: "module-name": "Top" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "clang-modulemap-file": "[[PREFIX]]/module.modulemap" +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK-NOT: -fmodule-map-file +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/outputs/{{.*}}/Right-{{.*}}.pcm" +// CHECK: "-disable-free" +// CHECK: "-fno-pch-timestamp" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[RIGHT_TREE]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodule-file-cache-key +// CHECK-NEXT: "[[PREFIX]]/outputs/{{.*}}/Top-{{.*}}.pcm" +// CHECK-NEXT: "llvmcas://{{[[:xdigit:]]+}}" +// CHECK: "-fmodule-file=Top=[[PREFIX]]/outputs/{{.*}}/Top-{{.*}}.pcm" +// CHECK: "-fmodules" +// CHECK: "-fmodule-name=Right" +// CHECK: "-fno-implicit-modules" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/Right.h" +// CHECK-NEXT: ] +// CHECK: "name": "Right" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "cas-include-tree-id": "[[TOP_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [] +// CHECK: "clang-modulemap-file": "[[PREFIX]]/module.modulemap" +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/outputs/{{.*}}/Top-{{.*}}.pcm" +// CHECK: "-disable-free" +// CHECK: "-fno-pch-timestamp" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[TOP_TREE]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodules" +// CHECK: "-fmodule-name=Top" +// CHECK: "-fno-implicit-modules" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/Top.h" +// CHECK-NEXT: ] +// CHECK: "name": "Top" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "cas-include-tree-id": "[[AT_IMPORT_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [] +// CHECK: "clang-modulemap-file": "[[PREFIX]]/module.modulemap" +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/outputs/{{.*}}/ZAtImport-{{.*}}.pcm" +// CHECK: "-disable-free" +// CHECK: "-fno-pch-timestamp" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[AT_IMPORT_TREE]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodules" +// CHECK: "-fmodule-name=ZAtImport" +// CHECK: "-fno-implicit-modules" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/AtImport.h" +// CHECK-NEXT: ] +// CHECK: "name": "ZAtImport" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "cas-include-tree-id": "[[PRAGMA_IMPORT_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [] +// CHECK: "clang-modulemap-file": "[[PREFIX]]/module.modulemap" +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK: "-o" +// CHECK-NEXT: "[[PREFIX]]/outputs/{{.*}}/ZPragmaImport-{{.*}}.pcm" +// CHECK: "-disable-free" +// CHECK: "-fno-pch-timestamp" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[PRAGMA_IMPORT_TREE]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodules" +// CHECK: "-fmodule-name=ZPragmaImport" +// CHECK: "-fno-implicit-modules" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/module.modulemap" +// CHECK-NEXT: "[[PREFIX]]/PragmaImport.h" +// CHECK-NEXT: ] +// CHECK: "name": "ZPragmaImport" +// CHECK: } +// CHECK: ] +// CHECK-NEXT: "translation-units": [ +// CHECK-NEXT: { +// CHECK-NEXT: "commands": [ +// CHECK-NEXT: { +// CHECK: "cas-include-tree-id": "[[TU_TREE:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK: "module-name": "Left" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "module-name": "Right" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "module-name": "ZAtImport" +// CHECK: } +// CHECK-NEXT: { +// CHECK: "module-name": "ZPragmaImport" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK-NOT: -fmodule-map-file +// CHECK: "-disable-free" +// CHECK: "-fcas-include-tree" +// CHECK-NEXT: "[[TU_TREE]]" +// CHECK: "-fcache-compile-job" +// CHECK: "-fsyntax-only" +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "[[PREFIX]]/outputs/{{.*}}/Left-{{.*}}.pcm" +// CHECK-NEXT: "llvmcas://{{[[:xdigit:]]+}}" +// CHECK: "-fmodule-file-cache-key" +// CHECK-NEXT: "[[PREFIX]]/outputs/{{.*}}/Right-{{.*}}.pcm" +// CHECK-NEXT: "llvmcas://{{[[:xdigit:]]+}}" +// CHECK: "-fmodule-file=Left=[[PREFIX]]/outputs/{{.*}}/Left-{{.*}}.pcm" +// CHECK: "-fmodule-file=Right=[[PREFIX]]/outputs/{{.*}}/Right-{{.*}}.pcm" +// CHECK: "-fmodules" +// CHECK: "-fno-implicit-modules" +// CHECK: ] +// CHECK: "file-deps": [ +// CHECK-NEXT: "[[PREFIX]]/tu.m" +// CHECK-NEXT: ] +// CHECK: "input-file": "[[PREFIX]]/tu.m" +// CHECK: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } + +// Build the include-tree commands +// RUN: %clang @%t/Top.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_MISS +// Ensure the pcm comes from the action cache +// RUN: rm -rf %t/outputs +// RUN: %clang @%t/Left.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_MISS +// RUN: rm -rf %t/outputs +// RUN: %clang @%t/Right.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_MISS +// RUN: rm -rf %t/outputs +// RUN: %clang @%t/ZAtImport.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_MISS +// RUN: rm -rf %t/outputs +// RUN: %clang @%t/ZPragmaImport.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_MISS +// RUN: rm -rf %t/outputs +// RUN: %clang @%t/tu.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_MISS + +// Check cache hits +// RUN: %clang @%t/Top.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_HIT +// RUN: %clang @%t/Left.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_HIT +// RUN: %clang @%t/Right.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_HIT +// RUN: %clang @%t/ZAtImport.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_HIT +// RUN: %clang @%t/ZPragmaImport.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_HIT +// RUN: %clang @%t/tu.rsp 2>&1 | FileCheck %s -check-prefix=CACHE_HIT + +// CACHE_MISS: compile job cache miss +// CACHE_HIT: compile job cache hit + +//--- cdb.json.template +[{ + "file": "DIR/tu.m", + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.m -I DIR -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/module-cache -Rcompile-job-cache" +}] + +//--- module.modulemap +module Top { header "Top.h" export *} +module Left { header "Left.h" export *} +module Right { header "Right.h" export *} +module ZAtImport { header "AtImport.h" } +module ZPragmaImport { header "PragmaImport.h" } + +//--- Top.h +#pragma once +struct Top { + int x; +}; +void top(void); + +//--- Left.h +#include "Top.h" +void left(void); + +//--- Right.h +#include "Top.h" +void right(void); + +//--- AtImport.h +void at_import(void); + +//--- PragmaImport.h +void pragma_import(void); + +//--- tu.m +#import "Left.h" +#import +@import ZAtImport; +#pragma clang module import ZPragmaImport + +void tu(void) { + top(); + left(); + right(); + at_import(); + pragma_import(); +} diff --git a/clang/test/ClangScanDeps/modules-index-store-path.c b/clang/test/ClangScanDeps/modules-index-store-path.c new file mode 100644 index 0000000000000..d4a6803381e58 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-index-store-path.c @@ -0,0 +1,45 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full \ +// RUN: -module-files-dir %t/build > %t/result.json + +// RUN: cat %t/result.json | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK-NOT: "-index-unit-output-path" +// CHECK: ] + +// RUN: %deps-to-rsp %t/result.json --module-name=Mod > %t/Mod.cc1.rsp +// RUN: %deps-to-rsp %t/result.json --tu-index=0 > %t/tu.cc1.rsp +// RUN: %clang @%t/Mod.cc1.rsp -pedantic -Werror +// RUN: %clang @%t/tu.cc1.rsp -pedantic -Werror +// RUN: c-index-test core -print-unit %t/index | FileCheck %s -DPREFIX=%/t -check-prefix=INDEX +// INDEX-DAG: out-file: [[PREFIX]]{{/|\\}}build/{{.*(/|\\)}}Mod-{{.*}}.pcm +// INDEX-DAG: out-file: /tu.o + +//--- cdb.json.template +[ + { + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.c -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -index-store-path DIR/index -index-unit-output-path /tu.o -o DIR/tu.o", + "file": "DIR/tu.c" + } +] + +//--- module.modulemap +module Mod { header "Mod.h" } + +//--- Mod.h +void mod(void); + +//--- tu.c +#include "Mod.h" +void tu(void) { + mod(); +} diff --git a/clang/test/ClangScanDeps/modules-order-c-api.c b/clang/test/ClangScanDeps/modules-order-c-api.c new file mode 100644 index 0000000000000..9ed2747d4ff5d --- /dev/null +++ b/clang/test/ClangScanDeps/modules-order-c-api.c @@ -0,0 +1,46 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +// Scan repeatedly +// RUN: c-index-test core -scan-deps -working-dir %S -- clang_tool -c %t/main.c -fmodules -fmodules-cache-path=%t/module-cache -fimplicit-modules -fimplicit-module-maps 2>&1 > %t/output1 +// RUN: c-index-test core -scan-deps -working-dir %S -- clang_tool -c %t/main.c -fmodules -fmodules-cache-path=%t/module-cache -fimplicit-modules -fimplicit-module-maps 2>&1 > %t/output2 +// RUN: c-index-test core -scan-deps -working-dir %S -- clang_tool -c %t/main.c -fmodules -fmodules-cache-path=%t/module-cache -fimplicit-modules -fimplicit-module-maps 2>&1 > %t/output3 + +// Ensure the output is identical each time +// RUN: diff %t/output1 %t/output2 +// RUN: diff %t/output1 %t/output3 + +// And that module dependencies are in topological order. +// RUN: FileCheck --input-file %t/output1 %s +// CHECK: modules +// CHECK-DAG: name: FromMod1 +// CHECK-DAG: name: FromMod2 +// CHECK: name: FromMain1 + +//--- module.modulemap +module FromMain1 { header "FromMain1.h" } +module FromMain2 { header "FromMain2.h" } +module FromMod1 { header "FromMod1.h" } +module FromMod2 { header "FromMod2.h" } + +//--- FromMain1.h +#include "FromMod1.h" +#include "FromMod2.h" + +//--- FromMain2.h +void fromMain2(void); + +//--- FromMod1.h +void fromMod1(void); + +//--- FromMod2.h +void fromMod2(void); + +//--- main.c +#include "FromMain1.h" +#include "FromMain2.h" +void m() { + fromMod1(); + fromMod2(); + fromMain2(); +} diff --git a/clang/test/ClangScanDeps/modules-outputs-c-api.c b/clang/test/ClangScanDeps/modules-outputs-c-api.c new file mode 100644 index 0000000000000..4b82423f2ebc5 --- /dev/null +++ b/clang/test/ClangScanDeps/modules-outputs-c-api.c @@ -0,0 +1,93 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: c-index-test core -scan-deps -working-dir %t -output-dir %t/out -- \ +// RUN: clang_tool -c %t/tu.c -fmodules -fmodules-cache-path=%t/cache \ +// RUN: -fimplicit-modules -fimplicit-module-maps \ +// RUN: -serialize-diagnostics %t/tu.diag -MD -MF %t/tu.d -o %t/tu.o \ +// RUN: | FileCheck %s -DPREFIX=%/t -check-prefix=NONE + +// NONE: build-args: +// NONE-NOT: -MT +// NONE-NOT: -serialize-diagnostics-file +// NONE-NOT: -dependency-file +// NONE: build-args: +// NONE-SAME: -fmodule-file={{(Mod=)?}}[[PREFIX]]/out/Mod_{{.*}}.pcm + +// RUN: c-index-test core -scan-deps -working-dir %t -output-dir %t/out -serialize-diagnostics -- \ +// RUN: clang_tool -c %t/tu.c -fmodules -fmodules-cache-path=%t/cache \ +// RUN: -fimplicit-modules -fimplicit-module-maps \ +// RUN: -serialize-diagnostics %t/tu.diag -MD -MF %t/tu.d -o %t/tu.o \ +// RUN: | FileCheck %s -DPREFIX=%/t -check-prefix=DIAGS + +// DIAGS: build-args: +// DIAGS-NOT: -MT +// DIAGS-NOT: -dependency-file +// DIAGS-SAME: -serialize-diagnostic-file [[PREFIX]]/out/Mod_{{.*}}.diag +// DIAGS-NOT: -MT +// DIAGS-NOT: -dependency-file +// DIAGS: build-args: +// DIAGS-SAME: -fmodule-file={{(Mod=)?}}[[PREFIX]]/out/Mod_{{.*}}.pcm + +// RUN: c-index-test core -scan-deps -working-dir %t -output-dir %t/out -dependency-file -- \ +// RUN: clang_tool -c %t/tu.c -fmodules -fmodules-cache-path=%t/cache \ +// RUN: -fimplicit-modules -fimplicit-module-maps \ +// RUN: -serialize-diagnostics %t/tu.diag -MD -MF %t/tu.d -o %t/tu.o \ +// RUN: | FileCheck %s -DPREFIX=%/t -check-prefix=DEPS + +// DEPS: build-args: +// DEPS-NOT: -serialize-diagnostic-file +// DEPS-SAME: -MT [[PREFIX]]/out/Mod_{{.*}}.pcm +// DEPS-NOT: -serialize-diagnostic-file +// DEPS-SAME: -dependency-file [[PREFIX]]/out/Mod_{{.*}}.d +// DEPS-NOT: -serialize-diagnostic-file +// DEPS: build-args: +// DEPS-SAME: -fmodule-file={{(Mod=)?}}[[PREFIX]]/out/Mod_{{.*}}.pcm + +// RUN: c-index-test core -scan-deps -working-dir %t -output-dir %t/out -dependency-file -dependency-target foo -- \ +// RUN: clang_tool -c %t/tu.c -fmodules -fmodules-cache-path=%t/cache \ +// RUN: -fimplicit-modules -fimplicit-module-maps \ +// RUN: -serialize-diagnostics %t/tu.diag -MD -MF %t/tu.d -o %t/tu.o \ +// RUN: | FileCheck %s -DPREFIX=%/t -check-prefix=DEPS_MT1 + +// DEPS_MT1: build-args: +// DEPS_MT1-NOT: -serialize-diagnostic-file +// DEPS_MT1-SAME: -MT foo +// DEPS_MT1-NOT: -serialize-diagnostic-file +// DEPS_MT1: build-args: +// DEPS_MT1-SAME: -fmodule-file={{(Mod=)?}}[[PREFIX]]/out/Mod_{{.*}}.pcm + +// RUN: c-index-test core -scan-deps -working-dir %t -output-dir %t/out -dependency-file -dependency-target foo -dependency-target bar -- \ +// RUN: clang_tool -c %t/tu.c -fmodules -fmodules-cache-path=%t/cache \ +// RUN: -fimplicit-modules -fimplicit-module-maps \ +// RUN: -serialize-diagnostics %t/tu.diag -MD -MF %t/tu.d -o %t/tu.o \ +// RUN: | FileCheck %s -DPREFIX=%/t -check-prefix=DEPS_MT2 + +// DEPS_MT2: build-args: +// DEPS_MT2-NOT: -serialize-diagnostic-file +// DEPS_MT2-SAME: -MT foo +// DEPS_MT2-SAME: -MT bar +// DEPS_MT2-NOT: -serialize-diagnostic-file +// DEPS_MT2: build-args: +// DEPS_MT2-SAME: -fmodule-file={{(Mod=)?}}[[PREFIX]]/out/Mod_{{.*}}.pcm + +// RUN: echo 'this_target_name_is_longer_than_the_256_byte_initial_buffer_size_to_test_that_we_alloc_and_call_again_with_a_sufficient_buffer_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_end' > %t/target-name.txt +// RUN: cat %t/target-name.txt > %t/long.txt +// RUN: c-index-test core -scan-deps -working-dir %t -output-dir %t/out -dependency-file \ +// RUN: -dependency-target @%t/target-name.txt -- \ +// RUN: clang_tool -c %t/tu.c -fmodules -fmodules-cache-path=%t/cache \ +// RUN: -fimplicit-modules -fimplicit-module-maps \ +// RUN: -serialize-diagnostics %t/tu.diag -MD -MF %t/tu.d -o %t/tu.o \ +// RUN: >> %t/long.txt +// RUN: FileCheck %s -check-prefix=LONG_OUT < %t/long.txt + +// LONG_OUT: [[TARGET:this_target_.*_end]] +// LONG_OUT: -MT [[TARGET]] + +//--- module.modulemap +module Mod { header "Mod.h" } + +//--- Mod.h + +//--- tu.c +#include "Mod.h" diff --git a/clang/test/ClangScanDeps/modules-pch-cas-fs-prefix-mapping-caching.c b/clang/test/ClangScanDeps/modules-pch-cas-fs-prefix-mapping-caching.c new file mode 100644 index 0000000000000..3341c048b87ee --- /dev/null +++ b/clang/test/ClangScanDeps/modules-pch-cas-fs-prefix-mapping-caching.c @@ -0,0 +1,105 @@ +// Test that we get cache hits across directories with modules and PCH. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: cp -r %t/dir1 %t/dir2 +// RUN: sed -e "s|DIR|%t/dir1|g" -e "s|CLANG|%clang|g" -e "s|SDK|%S/Inputs/SDK|g" %t/cdb.json.template > %t/dir1/cdb.json +// RUN: sed -e "s|DIR|%t/dir1|g" -e "s|CLANG|%clang|g" -e "s|SDK|%S/Inputs/SDK|g" %t/cdb_pch.json.template > %t/dir1/cdb_pch.json +// RUN: sed -e "s|DIR|%t/dir2|g" -e "s|CLANG|%clang|g" -e "s|SDK|%S/Inputs/SDK|g" %t/cdb.json.template > %t/dir2/cdb.json +// RUN: sed -e "s|DIR|%t/dir2|g" -e "s|CLANG|%clang|g" -e "s|SDK|%S/Inputs/SDK|g" %t/cdb_pch.json.template > %t/dir2/cdb_pch.json + +// == Scan PCH +// RUN: clang-scan-deps -compilation-database %t/dir1/cdb_pch.json -format experimental-full -optimize-args=none \ +// RUN: -cas-path %t/cas -module-files-dir %t/dir1/modules \ +// RUN: -prefix-map=%t/dir1/modules=/^modules -prefix-map=%t/dir1=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: > %t/pch_dir1.txt + +// RUN: clang-scan-deps -compilation-database %t/dir2/cdb_pch.json -format experimental-full -optimize-args=none \ +// RUN: -cas-path %t/cas -module-files-dir %t/dir2/modules \ +// RUN: -prefix-map=%t/dir2/modules=/^modules -prefix-map=%t/dir2=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: > %t/pch_dir2.txt + +// == Build PCH +// RUN: %deps-to-rsp %t/pch_dir1.txt --module-name=B > %t/dir1/B.cc1.rsp +// RUN: %deps-to-rsp %t/pch_dir1.txt --module-name=A > %t/dir1/A.cc1.rsp +// RUN: %deps-to-rsp %t/pch_dir1.txt --tu-index 0 > %t/dir1/pch.cc1.rsp +// RUN: (cd %t/dir1; %clang @B.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: (cd %t/dir1; %clang @A.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: (cd %t/dir1; %clang @pch.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-MISS + +// CACHE-MISS: compile job cache miss + +// RUN: %deps-to-rsp %t/pch_dir2.txt --module-name=B > %t/dir2/B.cc1.rsp +// RUN: %deps-to-rsp %t/pch_dir2.txt --module-name=A > %t/dir2/A.cc1.rsp +// RUN: %deps-to-rsp %t/pch_dir2.txt --tu-index 0 > %t/dir2/pch.cc1.rsp +// RUN: (cd %t/dir2; %clang @B.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: (cd %t/dir2; %clang @A.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: (cd %t/dir2; %clang @pch.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-HIT + +// CACHE-HIT: compile job cache hit + +// == Scan TU, including PCH +// RUN: clang-scan-deps -compilation-database %t/dir1/cdb.json -format experimental-full -optimize-args=none \ +// RUN: -cas-path %t/cas -module-files-dir %t/dir1/modules \ +// RUN: -prefix-map=%t/dir1/modules=/^modules -prefix-map=%t/dir1=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: > %t/dir1.txt + +// RUN: clang-scan-deps -compilation-database %t/dir2/cdb.json -format experimental-full -optimize-args=none \ +// RUN: -cas-path %t/cas -module-files-dir %t/dir2/modules \ +// RUN: -prefix-map=%t/dir2/modules=/^modules -prefix-map=%t/dir2=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: > %t/dir2.txt + +// == Build TU +// RUN: %deps-to-rsp %t/dir1.txt --module-name=C > %t/dir1/C.cc1.rsp +// RUN: %deps-to-rsp %t/dir1.txt --tu-index 0 > %t/dir1/tu.cc1.rsp +// RUN: (cd %t/dir1; %clang @C.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-MISS +// RUN: (cd %t/dir1; %clang @tu.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-MISS + +// RUN: %deps-to-rsp %t/dir2.txt --module-name=C > %t/dir2/C.cc1.rsp +// RUN: %deps-to-rsp %t/dir2.txt --tu-index 0 > %t/dir2/tu.cc1.rsp +// RUN: (cd %t/dir2; %clang @C.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-HIT +// RUN: (cd %t/dir2; %clang @tu.cc1.rsp) 2>&1 | FileCheck %s -check-prefix=CACHE-HIT + +// RUN: diff -u %t/dir1/prefix.h.pch %t/dir2/prefix.h.pch +// RUN: diff -r -u %t/dir1/modules %t/dir2/modules + +//--- cdb.json.template +[ + { + "directory": "DIR", + "command": "CLANG -fsyntax-only DIR/t.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/mcp -target x86_64-apple-macos11 -isysroot SDK -include DIR/prefix.h -Rcompile-job-cache", + "file": "DIR/t.c" + } +] + +//--- cdb_pch.json.template +[ + { + "directory" : "DIR", + "command" : "CLANG -x c-header DIR/prefix.h -o DIR/prefix.h.pch -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/mcp -target x86_64-apple-macos11 -isysroot SDK -Rcompile-job-cache", + "file" : "DIR/prefix.h" + }, +] + +//--- dir1/t.c +#include "c.h" + +//--- dir1/prefix.h +#include "a.h" + +//--- dir1/module.modulemap +module A { header "a.h" } +module B { header "b.h" } +module C { header "c.h" } + +//--- dir1/a.h +#include "b.h" + +//--- dir1/b.h +#include +#include + +//--- dir1/c.h +#include "b.h" diff --git a/clang/test/ClangScanDeps/modules-pch-cas-fs-prefix-mapping.c b/clang/test/ClangScanDeps/modules-pch-cas-fs-prefix-mapping.c new file mode 100644 index 0000000000000..aee657ce58e3d --- /dev/null +++ b/clang/test/ClangScanDeps/modules-pch-cas-fs-prefix-mapping.c @@ -0,0 +1,310 @@ +// Test path prefix-mapping when using a cas-fs with clang-scan-deps in +// modules with a PCH. + +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%t|g" -e "s|CLANG|%clang|g" -e "s|SDK|%S/Inputs/SDK|g" %t/cdb.json.template > %t/cdb.json +// RUN: sed -e "s|DIR|%t|g" -e "s|CLANG|%clang|g" -e "s|SDK|%S/Inputs/SDK|g" %t/cdb_pch.json.template > %t/cdb_pch.json + +// == Scan PCH +// RUN: clang-scan-deps -compilation-database %t/cdb_pch.json -format experimental-full \ +// RUN: -cas-path %t/cas -module-files-dir %t/modules \ +// RUN: -prefix-map=%t/modules=/^modules -prefix-map=%t=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: > %t/pch_result.txt + +// == Check specifics of the PCH command-line +// RUN: FileCheck %s -input-file %t/pch_result.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK -check-prefix=PCH + +// == Build PCH +// RUN: %deps-to-rsp %t/pch_result.txt --module-name=B > %t/B.cc1.rsp +// RUN: %deps-to-rsp %t/pch_result.txt --module-name=A > %t/A.cc1.rsp +// RUN: %deps-to-rsp %t/pch_result.txt --tu-index 0 > %t/pch.cc1.rsp +// RUN: %clang @%t/B.cc1.rsp +// RUN: %clang @%t/A.cc1.rsp +// Ensure we load pcms from action cache +// RUN: rm -rf %t/modules +// RUN: %clang @%t/pch.cc1.rsp + +// == Scan TU, including PCH +// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full \ +// RUN: -cas-path %t/cas -module-files-dir %t/modules \ +// RUN: -prefix-map=%t/modules=/^modules -prefix-map=%t=/^src -prefix-map-sdk=/^sdk -prefix-map-toolchain=/^tc \ +// RUN: > %t/result.txt + +// == Check specifics of the TU command-line +// RUN: FileCheck %s -input-file %t/result.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK + +// == Build TU +// RUN: %deps-to-rsp %t/result.txt --module-name=C > %t/C.cc1.rsp +// RUN: %deps-to-rsp %t/result.txt --tu-index 0 > %t/tu.cc1.rsp +// RUN: %clang @%t/C.cc1.rsp +// RUN: %clang @%t/tu.cc1.rsp + +// == Check the casfs. +// RUN: cat %t/A.cc1.rsp | sed -E 's/.* "-fcas-fs" "([^ ]+)" .*/\1/' > %t/A_id.txt +// RUN: cat %t/B.cc1.rsp | sed -E 's/.* "-fcas-fs" "([^ ]+)" .*/\1/' > %t/B_id.txt +// RUN: cat %t/C.cc1.rsp | sed -E 's/.* "-fcas-fs" "([^ ]+)" .*/\1/' > %t/C_id.txt +// RUN: cat %t/pch.cc1.rsp | sed -E 's/.* "-fcas-fs" "([^ ]+)" .*/\1/' > %t/pch_id.txt +// RUN: cat %t/tu.cc1.rsp | sed -E 's/.* "-fcas-fs" "([^ ]+)" .*/\1/' > %t/tu_id.txt + +// RUN: llvm-cas -cas %t/cas -ls-tree-recursive @%t/A_id.txt > %t/A_fs.txt +// RUN: llvm-cas -cas %t/cas -ls-tree-recursive @%t/B_id.txt > %t/B_fs.txt +// RUN: llvm-cas -cas %t/cas -ls-tree-recursive @%t/C_id.txt > %t/C_fs.txt +// RUN: llvm-cas -cas %t/cas -ls-tree-recursive @%t/pch_id.txt > %t/pch_fs.txt +// RUN: llvm-cas -cas %t/cas -ls-tree-recursive @%t/tu_id.txt > %t/tu_fs.txt + +// RUN: FileCheck %s -input-file %t/A_fs.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK -check-prefixes=FS_NEG,FS +// RUN: FileCheck %s -input-file %t/B_fs.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK -check-prefixes=FS_NEG,FS +// RUN: FileCheck %s -input-file %t/C_fs.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK -check-prefixes=FS_NEG,FS +// RUN: FileCheck %s -input-file %t/pch_fs.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK -check-prefixes=FS_NEG,FS +// RUN: FileCheck %s -input-file %t/tu_fs.txt -DPREFIX=%t -DSDK_PREFIX=%S/Inputs/SDK -check-prefixes=FS_NEG,FS + +// FS_NEG-NOT: [[PREFIX]] +// FS_NEG-NOT: [[SDK_PREFIX]] +// FS_NEG-NOT: .pcm{{$}} +// FS: file llvmcas://{{.*}} /^sdk/usr/include/stdlib.h +// FS: file llvmcas://{{.*}} /^src/a.h +// FS: file llvmcas://{{.*}} /^src/b.h +// FS: file llvmcas://{{.*}} /^src/module.modulemap +// FS: file llvmcas://{{.*}} /^tc/lib/clang/{{.*}}/include/stdarg.h + +// Check that it builds. +// RUN: %clang @%t/B.cc1.rsp +// RUN: %clang @%t/A.cc1.rsp +// RUN: %clang @%t/tu.cc1.rsp + +// PCH: { +// PCH: "modules": [ +// PCH: { +// PCH: "casfs-root-id": "[[A_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// PCH: "clang-module-deps": [ +// PCH: { +// PCH: "module-name": "B" +// PCH: } +// PCH: ] +// PCH: "clang-modulemap-file": "[[PREFIX]]/module.modulemap" +// PCH: "command-line": [ +// PCH: "-fcas-path" +// PCH: "[[PREFIX]]/cas" +// PCH: "-fcas-fs" +// PCH: "[[A_ROOT_ID]]" +// PCH: "-fcas-fs-working-directory" +// PCH: "/^src" +// PCH: "-fmodule-map-file=/^src/module.modulemap" +// PCH: "-o" +// PCH: "[[PREFIX]]/modules/{{.*}}/A-{{.*}}.pcm" +// PCH: "-fmodule-file-cache-key" +// PCH: "/^modules/{{.*}}/B-[[B_CONTEXT_HASH:[^.]+]].pcm" +// PCH: "llvmcas://{{.*}}" +// PCH: "-x" +// PCH: "c" +// PCH: "/^src/module.modulemap" +// PCH: "-isysroot" +// PCH: "/^sdk" +// PCH: "-resource-dir" +// PCH: "/^tc/lib/clang/{{.*}}" +// PCH: "-fmodule-file=B=/^modules/{{.*}}/B-[[B_CONTEXT_HASH]].pcm" +// PCH: "-isystem" +// PCH: "/^tc/lib/clang/{{.*}}/include" +// PCH: "-internal-externc-isystem" +// PCH: "/^sdk/usr/include" +// PCH: ] +// PCH: "file-deps": [ +// PCH: "[[PREFIX]]/module.modulemap" +// PCH: "[[PREFIX]]/a.h" +// PCH: ] +// PCH: "name": "A" +// PCH: } +// PCH: { +// PCH: "casfs-root-id": "[[B_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// PCH: "clang-module-deps": [], +// PCH: "clang-modulemap-file": "[[PREFIX]]/module.modulemap" +// PCH: "command-line": [ +// PCH: "-fcas-path" +// PCH: "[[PREFIX]]/cas" +// PCH: "-fcas-fs" +// PCH: "[[B_ROOT_ID]]" +// PCH: "-fcas-fs-working-directory" +// PCH: "/^src" +// PCH: "-o" +// PCH: "[[PREFIX]]/modules/{{.*}}/B-[[B_CONTEXT_HASH]].pcm" +// PCH: "-x" +// PCH: "c" +// PCH: "/^src/module.modulemap" +// PCH: "-isysroot" +// PCH: "/^sdk" +// PCH: "-resource-dir" +// PCH: "/^tc/lib/clang/{{.*}}" +// PCH: "-isystem" +// PCH: "/^tc/lib/clang/{{.*}}/include" +// PCH: "-internal-externc-isystem" +// PCH: "/^sdk/usr/include" +// PCH: ] +// PCH: "context-hash": "[[B_CONTEXT_HASH]]" +// PCH: "file-deps": [ +// Note: PREFIX, SDK_PREFIX and toolchain path are unordered +// PCH-DAG: "[[PREFIX]]/module.modulemap" +// PCH-DAG: "[[PREFIX]]/b.h" +// PCH-DAG: "[[SDK_PREFIX]]/usr/include/stdlib.h" +// PCH-DAG: "{{.*}}/include/stdarg.h" +// PCH: ] +// PCH: "name": "B" +// PCH: } +// PCH: ] +// PCH: "translation-units": [ +// PCH: { +// PCH: "commands": [ +// PCH: { +// PCH: "casfs-root-id": "[[TU_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// PCH: "clang-module-deps": [ +// PCH: { +// PCH: "module-name": "A" +// PCH: } +// PCH: ] +// PCH: "command-line": [ +// PCH: "-fcas-path" +// PCH: "[[PREFIX]]/cas" +// PCH: "-fcas-fs" +// PCH: "[[TU_ROOT_ID]]" +// PCH: "-fcas-fs-working-directory" +// PCH: "/^src" +// PCH: "-fmodule-map-file=/^src/module.modulemap" +// PCH: "-fmodule-file-cache-key" +// PCH: "/^modules/{{.*}}A-{{.*}}.pcm" +// PCH: "llvmcas://{{.*}}" +// PCH: "-x" +// PCH: "c-header" +// PCH: "/^src/prefix.h" +// PCH: "-isysroot" +// PCH: "/^sdk" +// PCH: "-resource-dir" +// PCH: "/^tc/lib/clang/{{.*}}" +// PCH: "-fmodule-file=A=/^modules/{{.*}}/A-{{.*}}.pcm" +// PCH: "-isystem" +// PCH: "/^tc/lib/clang/{{.*}}/include" +// PCH: "-internal-externc-isystem" +// PCH: "/^sdk/usr/include" +// PCH: ], +// PCH: "file-deps": [ +// PCH: "[[PREFIX]]/prefix.h" +// PCH: ] +// PCH: "input-file": "[[PREFIX]]/prefix.h" +// PCH: } + +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK: "casfs-root-id": "[[C_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [] +// CHECK: "command-line": [ +// CHECK-NEXT: "-cc1" +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]{{.}}cas" +// CHECK: "-fcas-fs" +// CHECK-NEXT: "[[C_ROOT_ID]]" +// CHECK: "-fcas-fs-working-directory" +// CHECK-NEXT: "/^src" +// CHECK: "-fcache-compile-job" +// CHECK: "-emit-module" +// CHECK: "-fmodule-file=/^modules/{{.*}}/B-{{.*}}.pcm" +// CHECK: "-fmodule-file-cache-key" +// CHECK: "/^modules/{{.*}}/B-{{.*}}.pcm" +// CHECK: "llvmcas://{{.*}}" +// CHECK: "-x" +// CHECK-NEXT: "c" +// CHECK-NEXT: "/^src/module.modulemap" +// CHECK: "-isysroot" +// CHECK-NEXT: "/^sdk" +// CHECK: "-resource-dir" +// CHECK-NEXT: "/^tc/lib/clang/{{.*}}" +// CHECK-NOT: [[PREFIX]] +// CHECK-NOT: [[SDK_PREFIX]] +// CHECK: ] +// CHECK: "name": "C" +// CHECK: } +// CHECK-NEXT: ] +// CHECK: "translation-units": [ +// CHECK: { +// CHECK: "commands": [ +// CHECK: { +// CHECK: "casfs-root-id": "[[TU_ROOT_ID:llvmcas://[[:xdigit:]]+]]" +// CHECK: "clang-module-deps": [ +// CHECK: { +// CHECK: "module-name": "C" +// CHECK: } +// CHECK: ] +// CHECK: "command-line": [ +// CHECK: "-fcas-path" +// CHECK-NEXT: "[[PREFIX]]/cas" +// CHECK: "-fcas-fs" +// CHECK-NEXT: "[[TU_ROOT_ID]]" +// CHECK: "-fcas-fs-working-directory" +// CHECK-NEXT: "/^src" +// CHECK: "-fmodule-map-file=/^src/module.modulemap" +// CHECK: "-fmodule-file-cache-key" +// CHECK: "/^modules/{{.*}}C-{{.*}}.pcm" +// CHECK: "llvmcas://{{.*}}" +// CHECK: "-x" +// CHECK-NEXT: "c" +// CHECK-NEXT: "/^src/t.c" +// CHECK: "-isysroot" +// CHECK-NEXT: "/^sdk" +// CHECK: "-resource-dir" +// CHECK-NEXT: "/^tc/lib/clang/{{.*}}" +// CHECK: "-fmodule-file=C=/^modules/{{.*}}/C-{{.*}}.pcm" +// CHECK: "-isystem" +// CHECK-NEXT: "/^sdk/usr/local/include" +// CHECK: "-isystem" +// CHECK-NEXT: "/^tc/lib/clang/{{.*}}/include" +// CHECK: "-internal-externc-isystem" +// CHECK-NEXT: "/^sdk/usr/include" +// CHECK: "-include-pch" +// CHECK-NEXT: "/^src/prefix.h.pch" +// CHECK: ], +// CHECK: "file-deps": [ +// CHECK: "[[PREFIX]]/t.c" +// CHECK: "[[PREFIX]]/prefix.h.pch" +// CHECK: ] +// CHECK: "input-file": "[[PREFIX]]/t.c" +// CHECK: } + +//--- cdb.json.template +[ + { + "directory": "DIR", + "command": "CLANG -fsyntax-only DIR/t.c -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/mcp -target x86_64-apple-macos11 -isysroot SDK -include DIR/prefix.h", + "file": "DIR/t.c" + } +] + +//--- cdb_pch.json.template +[ + { + "directory" : "DIR", + "command" : "CLANG -x c-header DIR/prefix.h -o DIR/prefix.h.pch -fmodules -fimplicit-modules -fimplicit-module-maps -fmodules-cache-path=DIR/mcp -target x86_64-apple-macos11 -isysroot SDK", + "file" : "DIR/prefix.h" + }, +] + +//--- t.c +#include "c.h" + +//--- prefix.h +#include "a.h" + +//--- module.modulemap +module A { header "a.h" } +module B { header "b.h" } +module C { header "c.h" } + +//--- a.h +#include "b.h" + +//--- b.h +#include +#include + +//--- c.h +#include "b.h" diff --git a/clang/test/ClangScanDeps/optimize-vfs-pch-tree.m b/clang/test/ClangScanDeps/optimize-vfs-pch-tree.m new file mode 100644 index 0000000000000..0c2ea25c40907 --- /dev/null +++ b/clang/test/ClangScanDeps/optimize-vfs-pch-tree.m @@ -0,0 +1,209 @@ +// Check that tracking of VFSs works with PCH. +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s|DIR|%/t|g" %t/build/compile-commands-pch.json.in > %t/build/compile-commands-pch.json +// RUN: sed -e "s|DIR|%/t|g" %t/build/compile-commands-tu.json.in > %t/build/compile-commands-tu.json +// RUN: sed -e "s|DIR|%/t|g" %t/build/compile-commands-tu1.json.in > %t/build/compile-commands-tu1.json +// RUN: sed -e "s|DIR|%/t|g" %t/build/pch-overlay.yaml.in > %t/build/pch-overlay.yaml + +// RUN: clang-scan-deps -compilation-database %t/build/compile-commands-pch.json \ +// RUN: -j 1 -format experimental-include-tree-full -cas-path %t/cas \ +// RUN: --optimize-args=vfs,header-search > %t/pch-deps.db +// RUN: %deps-to-rsp %t/pch-deps.db --module-name=A > %t/A.rsp +// RUN: %deps-to-rsp %t/pch-deps.db --module-name=B > %t/B.rsp +// RUN: %deps-to-rsp %t/pch-deps.db --tu-index=0 > %t/pch.rsp +// RUN: %clang @%t/A.rsp +// RUN: %clang @%t/B.rsp +// RUN: %clang @%t/pch.rsp + +// RUN: clang-scan-deps -compilation-database %t/build/compile-commands-tu.json \ +// RUN: -j 1 -format experimental-include-tree-full -cas-path %t/cas \ +// RUN: --optimize-args=vfs,header-search > %t/tu-deps.db +// RUN: %deps-to-rsp %t/tu-deps.db --module-name=C > %t/C.rsp +// RUN: %deps-to-rsp %t/tu-deps.db --tu-index=0 > %t/tu.rsp +// RUN: %clang @%t/C.rsp +// RUN: %clang @%t/tu.rsp + +// Next test is to verify that a module that doesn't use the VFS, that depends +// on the PCH's A, which does use the VFS, still records that it needs the VFS. +// This avoids a fatal error when emitting diagnostics. + +// RUN: clang-scan-deps -compilation-database %t/build/compile-commands-tu1.json \ +// RUN: -j 1 -format experimental-include-tree-full -cas-path %t/cas \ +// RUN: --optimize-args=vfs,header-search > %t/tu1-deps.db +// RUN: %deps-to-rsp %t/tu1-deps.db --tu-index=0 > %t/tu1.rsp +// Reuse existing B +// RUN: %deps-to-rsp %t/tu1-deps.db --module-name=E > %t/E.rsp +// RUN: %deps-to-rsp %t/tu1-deps.db --module-name=D > %t/D.rsp +// The build of D depends on B which depend on the prebuilt A. D will only build +// if it has A's VFS, as it needs to emit a diagnostic showing the content of A. +// RUN: %clang @%t/E.rsp +// RUN: %clang @%t/D.rsp -verify +// RUN: %clang @%t/tu1.rsp +// RUN: cat %t/tu1-deps.db | sed 's:\\\\\?:/:g' | FileCheck %s -DPREFIX=%/t + +// Check that D has the overlay, but E doesn't. +// CHECK: { +// CHECK-NEXT: "modules": [ +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key" +// CHECK-NEXT: "cas-include-tree-id" +// CHECK-NEXT: "clang-module-deps": [ +// CHECK-NEXT: { +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "module-name": "E" +// CHECK-NEXT: } +// CHECK-NEXT: ], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/modules/D/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK-NOT: "-ivfsoverlay" +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK: ], +// CHECK: "name": "D" +// CHECK-NEXT: }, +// CHECK-NEXT: { +// CHECK-NEXT: "cache-key" +// CHECK-NEXT: "cas-include-tree-id" +// CHECK-NEXT: "clang-module-deps": [], +// CHECK-NEXT: "clang-modulemap-file": "[[PREFIX]]/modules/E/module.modulemap", +// CHECK-NEXT: "command-line": [ +// CHECK-NOT: "-ivfsoverlay" +// CHECK: ], +// CHECK-NEXT: "context-hash": "{{.*}}", +// CHECK-NEXT: "file-deps": [ +// CHECK: ], +// CHECK: "name": "E" +// CHECK-NEXT: } + +//--- build/compile-commands-pch.json.in + +[ +{ + "directory": "DIR", + "command": "clang -x objective-c-header DIR/pch.h -I DIR/modules/A -I DIR/modules/B -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -o DIR/pch.h.pch -ivfsoverlay DIR/build/pch-overlay.yaml", + "file": "DIR/pch.h" +} +] + +//--- build/compile-commands-tu.json.in + +[ +{ + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu.m -I DIR/modules/A -I DIR/modules/B -I DIR/modules/C -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -include DIR/pch.h -o DIR/tu.o -ivfsoverlay DIR/build/pch-overlay.yaml", + "file": "DIR/tu.m" +} +] + +//--- build/compile-commands-tu1.json.in + +[ +{ + "directory": "DIR", + "command": "clang -fsyntax-only DIR/tu1.m -I DIR/modules/B -I DIR/modules/D -I DIR/modules/E -fmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -include DIR/pch.h -o DIR/tu1.o -ivfsoverlay DIR/build/pch-overlay.yaml", + "file": "DIR/tu1.m" +} +] + +//--- build/pch-overlay.yaml.in + +{ + "version":0, + "case-sensitive":"false", + "roots":[ + { + "contents":[ + { + "external-contents":"DIR/build/module.modulemap", + "name":"module.modulemap", + "type":"file" + }, + { + "external-contents":"DIR/build/A.h", + "name":"A.h", + "type":"file" + } + ], + "name":"DIR/modules/A", + "type":"directory" + } + ] +} + +//--- pch.h +#include + +//--- build/module.modulemap + +module A { + umbrella header "A.h" +} + +//--- build/A.h + +typedef int A_t __attribute__((deprecated("yep, it's depr"))); + +//--- modules/B/module.modulemap + +module B { + umbrella header "B.h" + export * +} + +//--- modules/B/B.h +#include + +typedef int B_t; + +//--- modules/C/module.modulemap + +module C { + umbrella header "C.h" +} + +//--- modules/C/C.h +#include + +typedef int C_t; + +//--- tu.m + +#include + +A_t a = 0; +B_t b = 0; +C_t c = 0; + +//--- modules/D/module.modulemap + +module D { + umbrella header "D.h" + export * +} + +//--- modules/D/D.h +#include +#include + +typedef A_t D_t; // expected-warning{{'A_t' is deprecated}} +// expected-note@*:* {{marked deprecated here}} + +//--- modules/E/module.modulemap + +module E { + umbrella header "E.h" +} + +//--- modules/E/E.h +typedef int E_t; + +//--- tu1.m + +#include + +D_t d = 0; +E_t e = 0; diff --git a/clang/test/ClangScanDeps/simple-c-api.cpp b/clang/test/ClangScanDeps/simple-c-api.cpp new file mode 100644 index 0000000000000..bfc92ff32a136 --- /dev/null +++ b/clang/test/ClangScanDeps/simple-c-api.cpp @@ -0,0 +1,7 @@ +// RUN: c-index-test core -scan-deps -working-dir %S -- clang_tool %s -I %S/Inputs | FileCheck %s + +#include "header.h" + +// CHECK: file-deps: +// CHECK-NEXT: simple-c-api.cpp +// CHECK-NEXT: Inputs/header.h diff --git a/clang/test/ClangScanDeps/stable-dirs-c-api.c b/clang/test/ClangScanDeps/stable-dirs-c-api.c new file mode 100644 index 0000000000000..977b9796fa482 --- /dev/null +++ b/clang/test/ClangScanDeps/stable-dirs-c-api.c @@ -0,0 +1,25 @@ +// REQUIRES: shell +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: c-index-test core -scan-deps -working-dir %S -- %clang \ +// RUN: -c %t/client.c -fmodules -fmodules-cache-path=%t/module-cache \ +// RUN: -isysroot %t/Sysroot -I %t/Sysroot/usr/include 2>&1 | FileCheck %s \ +// RUN: -implicit-check-not error: -implicit-check-not=warning: + +//--- Sysroot/usr/include/A/module.modulemap +module A { + umbrella "." +} + +//--- Sysroot/usr/include/A/A.h +typedef int A_t; + +//--- client.c +#include + + +// CHECK: module: +// CHECK-NEXT: name: A +// CHECK: is-in-stable-directories: 1 + diff --git a/clang/test/CodeGen/allow-ubsan-check.c b/clang/test/CodeGen/allow-ubsan-check.c index e225fb63f08eb..5e3436378d02e 100644 --- a/clang/test/CodeGen/allow-ubsan-check.c +++ b/clang/test/CodeGen/allow-ubsan-check.c @@ -1,10 +1,13 @@ // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 // +// FIXME: `-fno-split-cold-code` is required because swift clang enables `HotColdSplittingPass` by default which +// causes a divergence in IR from upstream (rdar://143354376). +// // We can't use -fsanitize-skip-hot-cutoff because that includes both -ubsan-guard-checks and //-lower-allow-check-percentile-cutoff. -// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -O1 -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null,local-bounds -mllvm -ubsan-guard-checks | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -O1 -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null,local-bounds -mllvm -ubsan-guard-checks -fno-split-cold-code | FileCheck %s // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -O1 -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null,local-bounds -mllvm -ubsan-guard-checks -fsanitize-trap=signed-integer-overflow,integer-divide-by-zero,null,local-bounds | FileCheck %s --check-prefixes=TR -// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -O1 -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null,local-bounds -mllvm -ubsan-guard-checks -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,null,local-bounds | FileCheck %s --check-prefixes=REC +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -O1 -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null,local-bounds -mllvm -ubsan-guard-checks -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,null,local-bounds -fno-split-cold-code | FileCheck %s --check-prefixes=REC // CHECK-LABEL: define dso_local noundef i32 @div( diff --git a/clang/test/CodeGen/attr-availability-new.c b/clang/test/CodeGen/attr-availability-new.c new file mode 100644 index 0000000000000..2fa50fd84ea8c --- /dev/null +++ b/clang/test/CodeGen/attr-availability-new.c @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -fvisibility=hidden "-triple" "x86_64-apple-macos11.0" -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -fvisibility=hidden "-triple" "x86_64-apple-macos10.15" -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-OLD %s + +__attribute__((availability(macos,introduced=10.16))) +void f0(void); + +__attribute__((availability(macos,introduced=11.0))) +void f1(void); + +__attribute__((availability(macos,introduced=12.0))) +void f2(void); + +// CHECK-OLD: declare extern_weak void @f0 +// CHECK-OLD: declare extern_weak void @f1 +// CHECK-OLD: declare extern_weak void @f2 + +// CHECK: declare void @f0 +// CHECK: declare void @f1 +// CHECK: declare extern_weak void @f2 + +void test() { + f0(); + f1(); + f2(); +} diff --git a/clang/test/CodeGen/attr-counted-by.c b/clang/test/CodeGen/attr-counted-by.c index dfdf06587f0e2..d413b005e1bcf 100644 --- a/clang/test/CodeGen/attr-counted-by.c +++ b/clang/test/CodeGen/attr-counted-by.c @@ -1,8 +1,8 @@ // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3 -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -DCOUNTED_BY -O2 -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITH-ATTR %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -DCOUNTED_BY -O2 -Wall -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITH-ATTR %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITHOUT-ATTR %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -Wall -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITHOUT-ATTR %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-split-cold-code -DCOUNTED_BY -O2 -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITH-ATTR %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-split-cold-code -DCOUNTED_BY -O2 -Wall -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITH-ATTR %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-split-cold-code -O2 -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITHOUT-ATTR %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-split-cold-code -O2 -Wall -fstrict-flex-arrays=3 -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITHOUT-ATTR %s #if !__has_attribute(counted_by) #error "has attribute broken" @@ -2059,30 +2059,13 @@ size_t test32_bdos(struct annotated_with_array *ptr, int index) { return __bdos(&ptr->flags[index]); } -// SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test33( -// SANITIZE-WITH-ATTR-SAME: ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { -// SANITIZE-WITH-ATTR-NEXT: entry: -// SANITIZE-WITH-ATTR-NEXT: ret i64 -1 -// -// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test33( -// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readnone [[PTR:%.*]]) local_unnamed_addr #[[ATTR3]] { -// NO-SANITIZE-WITH-ATTR-NEXT: entry: -// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 -1 -// -// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test33( -// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] { -// SANITIZE-WITHOUT-ATTR-NEXT: entry: -// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1 -// -// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test33( -// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readnone [[PTR:%.*]]) local_unnamed_addr #[[ATTR1]] { -// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry: -// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1 -// +// XXX: Commenting out the test that is ill-formed in downstream. +#if 0 size_t test33(struct annotated *ptr) { // Don't handle '&ptr->array' like normal. - return __bdos(&*&*&*&ptr->array); + return __bdos(&*&*&*&ptr->array); // error: cannot take address } +#endif struct multi_subscripts { unsigned long flags[42][42]; diff --git a/clang/test/CodeGen/attr-transparent-stepping-method.cpp b/clang/test/CodeGen/attr-transparent-stepping-method.cpp new file mode 100644 index 0000000000000..5eb91b90bd65b --- /dev/null +++ b/clang/test/CodeGen/attr-transparent-stepping-method.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s + +void bar(void) {} + +struct A { +[[clang::transparent_stepping()]] +void foo(void) { + bar(); +} +}; + +int main() { + A().foo(); +} + +// CHECK: DISubprogram(name: "foo"{{.*}} DISPFlagIsTransparentStepping diff --git a/clang/test/CodeGen/attr-transparent-stepping.c b/clang/test/CodeGen/attr-transparent-stepping.c new file mode 100644 index 0000000000000..6ae42dce78123 --- /dev/null +++ b/clang/test/CodeGen/attr-transparent-stepping.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s + +void bar(void) {} + +__attribute__((transparent_stepping)) +void foo(void) { + bar(); +} + +// CHECK: DISubprogram(name: "foo"{{.*}} DISPFlagIsTransparentStepping diff --git a/clang/test/CodeGen/feature-availability.c b/clang/test/CodeGen/feature-availability.c new file mode 100644 index 0000000000000..c732a5fb4873b --- /dev/null +++ b/clang/test/CodeGen/feature-availability.c @@ -0,0 +1,130 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx -fblocks -ffeature-availability=feature1:on -ffeature-availability=feature2:off -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-macosx -fblocks -emit-llvm -o - -DUSE_DOMAIN %s | FileCheck --check-prefixes=CHECK,DOMAIN %s + +// RUN: %clang_cc1 -triple arm64-apple-macosx -fblocks -ffeature-availability=feature1:on -ffeature-availability=feature2:off -emit-pch -o %t %s +// RUN: %clang_cc1 -triple arm64-apple-macosx -fblocks -ffeature-availability=feature1:on -ffeature-availability=feature2:off -include-pch %t -emit-llvm -o - %s | FileCheck %s + +// RUN: %clang_cc1 -triple arm64-apple-macosx -fblocks -emit-pch -o %t -DUSE_DOMAIN %s +// RUN: %clang_cc1 -triple arm64-apple-macosx -fblocks -include-pch %t -emit-llvm -o - -DUSE_DOMAIN %s | FileCheck --check-prefixes=CHECK,DOMAIN %s + +// CHECK: %[[STRUCT_S0:.*]] = type { i32 } +// CHECK: @g0 = external global i32, align 4 +// CHECK-NOT: @g1 +// CHECK-NOT: @g2 + +#ifndef HEADER +#define HEADER + +#include + +#define AVAIL 0 + +#ifdef USE_DOMAIN +// DOMAIN: @g3 = extern_weak global i32, align 4 + +static struct __AvailabilityDomain feature1 __attribute__((availability_domain(feature1))) = {__AVAILABILITY_DOMAIN_ENABLED, 0}; +static struct __AvailabilityDomain feature2 __attribute__((availability_domain(feature2))) = {__AVAILABILITY_DOMAIN_DISABLED, 0}; +#endif + +__attribute__((availability(domain:feature1, AVAIL))) int func0(void); +__attribute__((availability(domain:feature2, AVAIL))) int func1(void); +int func2(void); + +__attribute__((availability(domain:feature1, AVAIL))) extern int g0; +__attribute__((availability(domain:feature2, AVAIL))) int g1 = 100; +__attribute__((availability(domain:feature2, AVAIL))) int g2; + +struct __attribute__((availability(domain:feature1, AVAIL))) S0 { + int d0; +}; + +// CHECK-LABEL: define void @test0() +// CHECK-NOT: br +// CHECK: call i32 @func0() +// CHECK: store i32 123, ptr @g0, align 4 +// CHECK-NOT: func1() +// CHECK-NOT: func2() +void test0(void) { + if (__builtin_available(domain:feature1)) { + func0(); + g0 = 123; + } + + if (__builtin_available(domain:feature2)) { + func1(); + g1 = 123; + } + + if (__builtin_available(domain:feature1)) + if (__builtin_available(domain:feature2)) { + func2(); + } +} + +// CHECK-LABEL: define void @test1() +__attribute__((availability(domain:feature1, AVAIL))) +void test1(void) { +} + +// CHECK-NOT: @test2( +__attribute__((availability(domain:feature2, AVAIL))) +void test2(void) { +} + +// CHECK-LABEL: define void @test3( +// CHECK: %[[D0:.*]] = getelementptr inbounds nuw %[[STRUCT_S0]], ptr %{{.*}}, i32 0, i32 0 +// CHECK: store i32 134, ptr %[[D0]], align 4 +__attribute__((availability(domain:feature1, AVAIL))) +void test3(struct S0 *s0) { + s0->d0 = 134; +} + +#ifdef USE_DOMAIN +// DOMAIN-LABEL: define void @test4() +// DOMAIN: %[[CALL:.*]] = call i32 @pred1() +// DOMAIN-NEXT: %[[TOBOOL:.*]] = icmp ne i32 %[[CALL]], 0 +// DOMAIN-NEXT: br i1 %[[TOBOOL]], label %[[IF_THEN:.*]], label %[[IF_END:.*]] +// +// DOMAIN: [[IF_THEN]]: +// DOMAIN-NEXT: %[[CALL1:.*]] = call i32 @func3() +// DOMAIN-NEXT: store i32 1, ptr @g3, align 4 +// DOMAIN-NEXT: br label %[[IF_END]] +// +// DOMAIN: [[IF_END]]: +// DOMAIN-NEXT: ret void + +int pred1(void); +static struct __AvailabilityDomain feature3 __attribute__((availability_domain(feature3))) = {__AVAILABILITY_DOMAIN_DYNAMIC, pred1}; +__attribute__((availability(domain:feature3, AVAIL))) int func3(void); +__attribute__((availability(domain:feature3, AVAIL))) extern int g3; + +void test4(void) { + if (__builtin_available(domain:feature3)) { + func3(); + g3 = 1; + } +} + +// DOMAIN: declare extern_weak i32 @func3() + +#endif + +// CHECK-LABEL: define void @test5() +// CHECK: br label %[[L1:.*]] +// CHECK: [[L1]]: +// CHECK-NEXT: call i32 @func0() +// CHECK-NEXT: ret void + +void test5(void) { + if (__builtin_available(domain:feature1)) { + goto L1; +L1: + func0(); + } else { + goto L2; +L2: + func2(); + } +} + +#endif /* HEADER */ diff --git a/clang/test/CodeGen/ms-intrinsics-rotations.c b/clang/test/CodeGen/ms-intrinsics-rotations.c index b1bb2e6eb0011..30428b12aa333 100644 --- a/clang/test/CodeGen/ms-intrinsics-rotations.c +++ b/clang/test/CodeGen/ms-intrinsics-rotations.c @@ -12,10 +12,17 @@ // RUN: | FileCheck %s --check-prefixes CHECK,CHECK-32BIT-LONG // RUN: %clang_cc1 -ffreestanding -fms-extensions -fms-compatibility -fms-compatibility-version=17.00 \ // RUN: -triple x86_64--linux -emit-llvm %s -o - \ -// RUN: | FileCheck %s --check-prefixes CHECK,CHECK-64BIT-LONG +// RUN: | FileCheck %s --check-prefixes CHECK,CHECK-32BIT-LONG // RUN: %clang_cc1 -ffreestanding -fms-extensions \ // RUN: -triple x86_64--darwin -emit-llvm %s -o - \ -// RUN: | FileCheck %s --check-prefixes CHECK,CHECK-64BIT-LONG +// RUN: | FileCheck %s --check-prefixes CHECK,CHECK-32BIT-LONG + +// LP64 targets use 'long' as 'int' for MS intrinsics (-fms-extensions) +#ifdef __LP64__ +#define LONG int +#else +#define LONG long +#endif // rotate left @@ -40,15 +47,12 @@ unsigned int test_rotl(unsigned int value, int shift) { // CHECK: [[R:%.*]] = call i32 @llvm.fshl.i32(i32 [[X:%.*]], i32 [[X]], i32 [[Y:%.*]]) // CHECK: ret i32 [[R]] -unsigned long test_lrotl(unsigned long value, int shift) { +unsigned LONG test_lrotl(unsigned LONG value, int shift) { return _lrotl(value, shift); } // CHECK-32BIT-LONG: i32 @test_lrotl // CHECK-32BIT-LONG: [[R:%.*]] = call i32 @llvm.fshl.i32(i32 [[X:%.*]], i32 [[X]], i32 [[Y:%.*]]) // CHECK-32BIT-LONG: ret i32 [[R]] -// CHECK-64BIT-LONG: i64 @test_lrotl -// CHECK-64BIT-LONG: [[R:%.*]] = call i64 @llvm.fshl.i64(i64 [[X:%.*]], i64 [[X]], i64 [[Y:%.*]]) -// CHECK-64BIT-LONG: ret i64 [[R]] unsigned __int64 test_rotl64(unsigned __int64 value, int shift) { return _rotl64(value, shift); @@ -80,15 +84,12 @@ unsigned int test_rotr(unsigned int value, int shift) { // CHECK: [[R:%.*]] = call i32 @llvm.fshr.i32(i32 [[X:%.*]], i32 [[X]], i32 [[Y:%.*]]) // CHECK: ret i32 [[R]] -unsigned long test_lrotr(unsigned long value, int shift) { +unsigned LONG test_lrotr(unsigned LONG value, int shift) { return _lrotr(value, shift); } // CHECK-32BIT-LONG: i32 @test_lrotr // CHECK-32BIT-LONG: [[R:%.*]] = call i32 @llvm.fshr.i32(i32 [[X:%.*]], i32 [[X]], i32 [[Y:%.*]]) // CHECK-32BIT-LONG: ret i32 [[R]] -// CHECK-64BIT-LONG: i64 @test_lrotr -// CHECK-64BIT-LONG: [[R:%.*]] = call i64 @llvm.fshr.i64(i64 [[X:%.*]], i64 [[X]], i64 [[Y:%.*]]) -// CHECK-64BIT-LONG: ret i64 [[R]] unsigned __int64 test_rotr64(unsigned __int64 value, int shift) { return _rotr64(value, shift); diff --git a/clang/test/CodeGen/ptrauth-abi-version.c b/clang/test/CodeGen/ptrauth-abi-version.c new file mode 100644 index 0000000000000..299bd9401a885 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-abi-version.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 %s -triple arm64e-apple-ios -disable-llvm-passes -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-NONE +// RUN: %clang_cc1 %s -fptrauth-kernel-abi-version -triple arm64e-apple-ios -disable-llvm-passes -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-WITH --check-prefix=CHECK-ZEROK +// RUN: %clang_cc1 %s -fptrauth-abi-version=0 -triple arm64e-apple-ios -disable-llvm-passes -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-WITH --check-prefix=CHECK-ZERO +// RUN: %clang_cc1 %s -fptrauth-abi-version=0 -fptrauth-kernel-abi-version -triple arm64e-apple-ios -disable-llvm-passes -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-WITH --check-prefix=CHECK-ZEROK +// RUN: %clang_cc1 %s -fptrauth-abi-version=5 -triple arm64e-apple-ios -disable-llvm-passes -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-WITH --check-prefix=CHECK-FIVE +// RUN: %clang_cc1 %s -fptrauth-abi-version=5 -fptrauth-kernel-abi-version -triple arm64e-apple-ios -disable-llvm-passes -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-WITH --check-prefix=CHECK-FIVEK + +int f(void) { + return 0; +} +// CHECK-NONE-NOT: ptrauth.abi-version +// CHECK-WITH: !llvm.module.flags = !{{{.*}} ![[ABI_VERSION_REF:[0-9]+]]} +// CHECK-WITH: ![[ABI_VERSION_REF]] = !{i32 6, !"ptrauth.abi-version", ![[ABI_VERSION_VAR:[0-9]+]]} +// CHECK-WITH: ![[ABI_VERSION_VAR]] = !{![[ABI_VERSION_VAL:[0-9]+]]} +// CHECK-ZERO: ![[ABI_VERSION_VAL]] = !{i32 0, i1 false} +// CHECK-ZEROK: ![[ABI_VERSION_VAL]] = !{i32 0, i1 true} +// CHECK-FIVE: ![[ABI_VERSION_VAL]] = !{i32 5, i1 false} +// CHECK-FIVEK: ![[ABI_VERSION_VAL]] = !{i32 5, i1 true} diff --git a/clang/test/CodeGen/ptrauth-blocks.c b/clang/test/CodeGen/ptrauth-blocks.c new file mode 100644 index 0000000000000..bdaeeb1d08c3c --- /dev/null +++ b/clang/test/CodeGen/ptrauth-blocks.c @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -fblocks -emit-llvm %s -o - | FileCheck %s + +void (^blockptr)(void); + +// CHECK: [[INVOCATION_1:@.*]] = private constant { ptr, i32, i64, i64 } { ptr {{@.*}}, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ ptr, i32, i32, ptr, ptr }, ptr [[GLOBAL_BLOCK_1:@.*]], i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: [[GLOBAL_BLOCK_1]] = internal constant { ptr, i32, i32, ptr, ptr } { ptr @_NSConcreteGlobalBlock, i32 1342177280, i32 0, ptr [[INVOCATION_1]], +void (^globalblock)(void) = ^{}; + +// CHECK-LABEL: define void @test_block_call() +void test_block_call() { + // CHECK: [[T0:%.*]] = load ptr, ptr @blockptr, + // CHECK-NEXT: [[FNADDR:%.*]] = getelementptr inbounds {{.*}}, ptr [[T0]], i32 0, i32 3 + // CHECK-NEXT: [[T1:%.*]] = load ptr, ptr [[FNADDR]], + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint ptr [[FNADDR]] to i64 + // CHECK-NEXT: call void [[T1]](ptr noundef [[T0]]) [ "ptrauth"(i32 0, i64 [[DISC]]) ] + blockptr(); +} + +void use_block(int (^)(void)); + +// CHECK-LABEL: define void @test_block_literal( +void test_block_literal(int i) { + // CHECK: [[I:%.*]] = alloca i32, + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:.*]], align + // CHECK: [[FNPTRADDR:%.*]] = getelementptr inbounds nuw [[BLOCK_T]], ptr [[BLOCK]], i32 0, i32 3 + // CHECK-NEXT: [[DISCRIMINATOR:%.*]] = ptrtoint ptr [[FNPTRADDR]] to i64 + // CHECK-NEXT: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr {{@.*}} to i64), i32 0, i64 [[DISCRIMINATOR]]) + // CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK-NEXT: store ptr [[T0]], ptr [[FNPTRADDR]] + use_block(^{return i;}); +} + +struct A { + int value; +}; +struct A *createA(void); diff --git a/clang/test/CodeGen/ptrauth-function-init-wrapper-globals.c b/clang/test/CodeGen/ptrauth-function-init-wrapper-globals.c new file mode 100644 index 0000000000000..e927916457639 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-function-init-wrapper-globals.c @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 %s -mllvm -ptrauth-emit-wrapper-globals=1 -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s +// RUN: %clang_cc1 -xc++ %s -mllvm -ptrauth-emit-wrapper-globals=1 -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefixes=CHECK,CXX + +#ifdef __cplusplus +extern "C" { +#endif + +void f(void); + +// CHECK: @f.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @f, i32 0, i64 0, i64 0 }, section "llvm.ptrauth" + +#ifdef __cplusplus + +// CXX-LABEL: define internal void @__cxx_global_var_init() +// CXX: store ptr getelementptr inbounds (i32, ptr @f.ptrauth, i64 2), ptr @_ZL2fp, align 8 + +__attribute__((used)) +void (*const fp)(void) = (void (*)(void))((int *)&f + 2); // Error in C mode. + +#endif + +// CHECK-LABEL: define void @t1() +void t1() { + // CHECK: [[PF:%.*]] = alloca ptr + // CHECK: store ptr getelementptr inbounds (i32, ptr @f.ptrauth, i64 2), ptr [[PF]] + + void (*pf)(void) = (void (*)(void))((int *)&f + 2); + (void)pf; +} + +#ifdef __cplusplus +} +#endif diff --git a/clang/test/CodeGen/ptrauth-function-init.c b/clang/test/CodeGen/ptrauth-function-init.c index bf8ee53364ecc..79e12fb7b717c 100644 --- a/clang/test/CodeGen/ptrauth-function-init.c +++ b/clang/test/CodeGen/ptrauth-function-init.c @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 %s -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s -// RUN: %clang_cc1 %s -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s -// RUN: %clang_cc1 -xc++ %s -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefixes=CHECK,CXX -// RUN: %clang_cc1 -xc++ %s -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefixes=CHECK,CXX +// RUN: %clang_cc1 %s -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s +// RUN: %clang_cc1 %s -mllvm -ptrauth-emit-wrapper-globals=0 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s +// RUN: %clang_cc1 -xc++ %s -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefixes=CHECK,CXX +// RUN: %clang_cc1 -xc++ %s -mllvm -ptrauth-emit-wrapper-globals=0 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefixes=CHECK,CXX #ifdef __cplusplus extern "C" { diff --git a/clang/test/CodeGen/ptrauth-function-type-discriminator-cast.c b/clang/test/CodeGen/ptrauth-function-type-discriminator-cast.c index 1a1dce6f4a66e..97e6bfe7fea3f 100644 --- a/clang/test/CodeGen/ptrauth-function-type-discriminator-cast.c +++ b/clang/test/CodeGen/ptrauth-function-type-discriminator-cast.c @@ -1,19 +1,25 @@ // RUN: %clang_cc1 %s -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefixes=CHECK,TYPE // RUN: %clang_cc1 %s -fptrauth-function-pointer-type-discrimination -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefixes=CHECK,TYPE // RUN: %clang_cc1 %s -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefixes=CHECK,ZERO // RUN: %clang_cc1 %s -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -disable-llvm-passes -emit-llvm -o- | FileCheck %s --check-prefixes=CHECK,ZERO // RUN: %clang_cc1 %s -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -disable-llvm-passes -emit-llvm -xc++ -o- | FileCheck %s --check-prefixes=CHECK,CHECKCXX,TYPE,TYPECXX // RUN: %clang_cc1 %s -fptrauth-function-pointer-type-discrimination -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -disable-llvm-passes -emit-llvm -xc++ -o- | FileCheck %s --check-prefixes=CHECK,CHECKCXX,TYPE,TYPECXX #ifdef __cplusplus diff --git a/clang/test/CodeGen/ptrauth-function-type-discriminator.c b/clang/test/CodeGen/ptrauth-function-type-discriminator.c index 0952c1abf6c07..868e8507a89dd 100644 --- a/clang/test/CodeGen/ptrauth-function-type-discriminator.c +++ b/clang/test/CodeGen/ptrauth-function-type-discriminator.c @@ -1,17 +1,23 @@ // RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -disable-llvm-passes -emit-llvm %s -o- | FileCheck --check-prefixes=CHECK,CHECKC %s // RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple arm64e-apple-ios13 -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -disable-llvm-passes -emit-llvm -xc++ %s -o- | FileCheck --check-prefix=CHECK %s // RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-pch %s -o %t.ast // RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -emit-llvm -x ast -o - %t.ast | FileCheck --check-prefixes=CHECK,CHECKC %s // RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -disable-llvm-passes -emit-llvm %s -o- | FileCheck --check-prefixes=CHECK,CHECKC %s // RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -disable-llvm-passes -emit-llvm -xc++ %s -o- | FileCheck --check-prefix=CHECK %s // RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-pch %s -o %t.ast // RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -emit-llvm -x ast -o - %t.ast | FileCheck --check-prefixes=CHECK,CHECKC %s #ifdef __cplusplus diff --git a/clang/test/CodeGen/ptrauth-init-fini.c b/clang/test/CodeGen/ptrauth-init-fini.c index 1e8953961d64e..d1e577b43d65e 100644 --- a/clang/test/CodeGen/ptrauth-init-fini.c +++ b/clang/test/CodeGen/ptrauth-init-fini.c @@ -1,18 +1,23 @@ // REQUIRES: aarch64-registered-target // RUN: %clang_cc1 -triple aarch64-elf -target-feature +pauth -fptrauth-calls -fptrauth-init-fini \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -emit-llvm %s -o - | FileCheck --check-prefix=SIGNED %s // RUN: %clang_cc1 -triple aarch64-elf -target-feature +pauth -fptrauth-calls -fptrauth-init-fini \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -fptrauth-init-fini-address-discrimination -emit-llvm %s -o - | FileCheck --check-prefix=ADDRDISC %s // RUN: %clang_cc1 -triple aarch64-elf -target-feature +pauth -fptrauth-calls \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -emit-llvm %s -o - | FileCheck --check-prefix=UNSIGNED %s // RUN: %clang_cc1 -triple aarch64-elf -target-feature +pauth -fptrauth-calls -fptrauth-init-fini-address-discrimination \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -emit-llvm %s -o - | FileCheck --check-prefix=UNSIGNED %s // RUN: %clang_cc1 -triple aarch64-elf -target-feature +pauth -fptrauth-init-fini \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -emit-llvm %s -o - | FileCheck --check-prefix=UNSIGNED %s // SIGNED: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr ptrauth (ptr @foo, i32 0, i64 55764), ptr null }] diff --git a/clang/test/CodeGen/ptrauth-intrinsic-sign-constant.c b/clang/test/CodeGen/ptrauth-intrinsic-sign-constant.c index bab39897b9427..2827ea672cd9d 100644 --- a/clang/test/CodeGen/ptrauth-intrinsic-sign-constant.c +++ b/clang/test/CodeGen/ptrauth-intrinsic-sign-constant.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple aarch64-elf -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-intrinsics -mllvm -ptrauth-emit-wrapper-globals=false -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple aarch64-elf -fptrauth-intrinsics -mllvm -ptrauth-emit-wrapper-globals=false -emit-llvm %s -o - | FileCheck %s extern int external; diff --git a/clang/test/CodeGen/ptrauth-intrinsics.c b/clang/test/CodeGen/ptrauth-intrinsics.c index 50bf1898e4b37..3cc6e3f322284 100644 --- a/clang/test/CodeGen/ptrauth-intrinsics.c +++ b/clang/test/CodeGen/ptrauth-intrinsics.c @@ -21,6 +21,16 @@ void test_auth() { fnptr = __builtin_ptrauth_auth(fnptr, 0, ptr_discriminator); } +// CHECK-LABEL: define {{.*}}void @test_auth_peephole() +void test_auth_peephole() { + // CHECK: [[PTR:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: [[DISC0:%.*]] = load ptr, ptr @ptr_discriminator, + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint ptr [[DISC0]] to i64 + // CHECK-NEXT: call void [[PTR]]() [ "ptrauth"(i32 0, i64 [[DISC]]) ] + // CHECK-NEXT: ret void + __builtin_ptrauth_auth(fnptr, 0, ptr_discriminator)(); +} + // CHECK-LABEL: define {{.*}}void @test_strip() void test_strip() { // CHECK: [[PTR:%.*]] = load ptr, ptr @fnptr, diff --git a/clang/test/CodeGen/ptrauth-qualifier-blocks.c b/clang/test/CodeGen/ptrauth-qualifier-blocks.c index 62da59cf327f6..f460da205cac7 100644 --- a/clang/test/CodeGen/ptrauth-qualifier-blocks.c +++ b/clang/test/CodeGen/ptrauth-qualifier-blocks.c @@ -82,9 +82,15 @@ void test_block_address_byref_capture() { // CHECK: store i32 33554432, // CHECK: store i32 48, // CHECK: [[COPY_HELPER_FIELD:%.*]] = getelementptr inbounds nuw [[BYREF_T]], ptr [[BYREF]], i32 0, i32 4 - // CHECK: store ptr @__Block_byref_object_copy_, ptr [[COPY_HELPER_FIELD]], align + // CHECK: [[T0:%.*]] = ptrtoint ptr [[COPY_HELPER_FIELD]] to i64 + // CHECK: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @__Block_byref_object_copy_ to i64), i32 0, i64 [[T0]]) + // CHECK: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr + // CHECK: store ptr [[T2]], ptr [[COPY_HELPER_FIELD]], align // CHECK: [[DISPOSE_HELPER_FIELD:%.*]] = getelementptr inbounds nuw [[BYREF_T]], ptr [[BYREF]], i32 0, i32 5 - // CHECK: store ptr @__Block_byref_object_dispose_, ptr [[DISPOSE_HELPER_FIELD]], align + // CHECK: [[T0:%.*]] = ptrtoint ptr [[DISPOSE_HELPER_FIELD]] to i64 + // CHECK: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @__Block_byref_object_dispose_ to i64), i32 0, i64 [[T0]]) + // CHECK: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr + // CHECK: store ptr [[T2]], ptr [[DISPOSE_HELPER_FIELD]], align // flags - copy/dispose required // CHECK: store i32 1107296256, ptr __block struct A * __ptrauth(1, 1, 60) ptr = createA(); diff --git a/clang/test/CodeGen/ptrauth-qualifier-const-init.c b/clang/test/CodeGen/ptrauth-qualifier-const-init.c index 174f328628f19..df0f2f1645bf0 100644 --- a/clang/test/CodeGen/ptrauth-qualifier-const-init.c +++ b/clang/test/CodeGen/ptrauth-qualifier-const-init.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -mllvm -ptrauth-emit-wrapper-globals=false -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -mllvm -ptrauth-emit-wrapper-globals=false -emit-llvm %s -o - | FileCheck %s // Constant initializers for data pointers. extern int external_int; diff --git a/clang/test/CodeGen/ptrauth-qualifier-function.c b/clang/test/CodeGen/ptrauth-qualifier-function.c index cd25b77a01548..7e594a0b5dbbc 100644 --- a/clang/test/CodeGen/ptrauth-qualifier-function.c +++ b/clang/test/CodeGen/ptrauth-qualifier-function.c @@ -1,9 +1,9 @@ -// RUN: %clang_cc1 %s -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck --check-prefixes=CHECK,TYPE %s -// RUN: %clang_cc1 %s -fptrauth-function-pointer-type-discrimination -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck --check-prefixes=CHECK,TYPE %s -// RUN: %clang_cc1 %s -triple arm64-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck --check-prefixes=CHECK,ZERO %s -// RUN: %clang_cc1 %s -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck --check-prefixes=CHECK,ZERO %s -// RUN: %clang_cc1 -xc++ %s -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck --check-prefixes=CHECK,TYPE,CHECK-CXX %s -// RUN: %clang_cc1 -xc++ %s -fptrauth-function-pointer-type-discrimination -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -emit-llvm -o- | FileCheck --check-prefixes=CHECK,TYPE,CHECK-CXX %s +// RUN: %clang_cc1 %s -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -mllvm -ptrauth-emit-wrapper-globals=false -emit-llvm -o- | FileCheck --check-prefixes=CHECK,TYPE %s +// RUN: %clang_cc1 %s -fptrauth-function-pointer-type-discrimination -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -mllvm -ptrauth-emit-wrapper-globals=false -emit-llvm -o- | FileCheck --check-prefixes=CHECK,TYPE %s +// RUN: %clang_cc1 %s -triple arm64-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -mllvm -ptrauth-emit-wrapper-globals=false -emit-llvm -o- | FileCheck --check-prefixes=CHECK,ZERO %s +// RUN: %clang_cc1 %s -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -mllvm -ptrauth-emit-wrapper-globals=false -emit-llvm -o- | FileCheck --check-prefixes=CHECK,ZERO %s +// RUN: %clang_cc1 -xc++ %s -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios13 -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -mllvm -ptrauth-emit-wrapper-globals=false -emit-llvm -o- | FileCheck --check-prefixes=CHECK,TYPE,CHECK-CXX %s +// RUN: %clang_cc1 -xc++ %s -fptrauth-function-pointer-type-discrimination -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -disable-llvm-passes -mllvm -ptrauth-emit-wrapper-globals=false -emit-llvm -o- | FileCheck --check-prefixes=CHECK,TYPE,CHECK-CXX %s #ifdef __cplusplus extern "C" { diff --git a/clang/test/CodeGen/ptrauth-qualifier-loadstore.c b/clang/test/CodeGen/ptrauth-qualifier-loadstore.c index db259ed950fec..f4a3b534a05ee 100644 --- a/clang/test/CodeGen/ptrauth-qualifier-loadstore.c +++ b/clang/test/CodeGen/ptrauth-qualifier-loadstore.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s -// RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -mllvm -ptrauth-emit-wrapper-globals=false -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -mllvm -ptrauth-emit-wrapper-globals=false -emit-llvm %s -o - | FileCheck %s #define IQ __ptrauth(1,0,50) #define AQ __ptrauth(1,1,50) @@ -206,23 +206,6 @@ void test_load_data_i() { // Data with address-discriminated qualifiers. -// CHECK-LABEL: define {{.*}}void @test_store_data_a_constant() -void test_store_data_a_constant() { -// CHECK: [[V:%.*]] = alloca ptr, -// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64 -// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50) -// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @external_int to i64), i32 1, i64 [[NEWDISC]]) -// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to ptr -// CHECK-NEXT: store ptr [[T0]], ptr [[V]], - int * AQ aqpi = &external_int; -// CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[V]] to i64 -// CHECK-NEXT: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 50) -// CHECK-NEXT: [[SIGN:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @external_int to i64), i32 1, i64 [[NEWDISC]]) -// CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGN]] to ptr -// CHECK-NEXT: store ptr [[T0]], ptr [[V]], - aqpi = &external_int; -} - // CHECK-LABEL: define {{.*}}void @test_store_data_au() void test_store_data_au() { // CHECK: [[V:%.*]] = alloca ptr, diff --git a/clang/test/CodeGen/ptrauth-qualifier.c b/clang/test/CodeGen/ptrauth-qualifier.c new file mode 100644 index 0000000000000..718ed723a9298 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-qualifier.c @@ -0,0 +1,87 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s + +// Constant initializers for data pointers. +extern int external_int; + +// CHECK: [[PTRAUTH_G1:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 0, i64 56 }, section "llvm.ptrauth" +// CHECK: @g1 = global ptr [[PTRAUTH_G1]] +int * __ptrauth(1,0,56) g1 = &external_int; + +// CHECK: [[PTRAUTH_G2:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr @g2 to i64), i64 1272 }, section "llvm.ptrauth" +// CHECK: @g2 = global ptr [[PTRAUTH_G2]] +int * __ptrauth(1,1,1272) g2 = &external_int; + +// CHECK: @g3 = global ptr null +int * __ptrauth(1,1,871) g3 = 0; + +// FIXME: should we make a ptrauth constant for this absolute symbol? +// CHECK: @g4 = global ptr inttoptr (i64 1230 to ptr) +int * __ptrauth(1,1,1902) g4 = (int*) 1230; + +// CHECK: [[PTRAUTH_GA0:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr @ga to i64), i64 712 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_GA1:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr getelementptr inbounds ([3 x ptr], ptr @ga, i32 0, i32 1) to i64), i64 712 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_GA2:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr getelementptr inbounds ([3 x ptr], ptr @ga, i32 0, i32 2) to i64), i64 712 }, section "llvm.ptrauth" +// CHECK: @ga = global [3 x ptr] [ptr [[PTRAUTH_GA0]], ptr [[PTRAUTH_GA1]], ptr [[PTRAUTH_GA2]]] +int * __ptrauth(1,1,712) ga[3] = { &external_int, &external_int, &external_int }; + +struct A { + int * __ptrauth(1,0,431) f0; + int * __ptrauth(1,0,9182) f1; + int * __ptrauth(1,0,783) f2; +}; +// CHECK: [[PTRAUTH_GS0:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 0, i64 431 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_GS1:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 0, i64 9182 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_GS2:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 0, i64 783 }, section "llvm.ptrauth" +// CHECK: @gs1 = global %struct.A { ptr [[PTRAUTH_GS0]], ptr [[PTRAUTH_GS1]], ptr [[PTRAUTH_GS2]] } +struct A gs1 = { &external_int, &external_int, &external_int }; + +struct B { + int * __ptrauth(1,1,1276) f0; + int * __ptrauth(1,1,23674) f1; + int * __ptrauth(1,1,163) f2; +}; +// CHECK: [[PTRAUTH_GS0:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr @gs2 to i64), i64 1276 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_GS1:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 1) to i64), i64 23674 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_GS2:@external_int.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_int, i32 1, i64 ptrtoint (ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 2) to i64), i64 163 }, section "llvm.ptrauth" +// CHECK: @gs2 = global %struct.B { ptr [[PTRAUTH_GS0]], ptr [[PTRAUTH_GS1]], ptr [[PTRAUTH_GS2]] } +struct B gs2 = { &external_int, &external_int, &external_int }; + +// Constant initializers for function pointers. +extern void external_function(void); +typedef void (*fpt)(void); + +// CHECK: [[PTRAUTH_F1:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 0, i64 56 }, section "llvm.ptrauth" +// CHECK: @f1 = global ptr [[PTRAUTH_F1]] +fpt __ptrauth(1,0,56) f1 = &external_function; + +// CHECK: [[PTRAUTH_F2:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr @f2 to i64), i64 1272 }, section "llvm.ptrauth" +// CHECK: @f2 = global ptr [[PTRAUTH_F2]] +fpt __ptrauth(1,1,1272) f2 = &external_function; + +// CHECK: [[PTRAUTH_FA0:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr @fa to i64), i64 712 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_FA1:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr getelementptr inbounds ([3 x ptr], ptr @fa, i32 0, i32 1) to i64), i64 712 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_FA2:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr getelementptr inbounds ([3 x ptr], ptr @fa, i32 0, i32 2) to i64), i64 712 }, section "llvm.ptrauth" +// CHECK: @fa = global [3 x ptr] [ptr [[PTRAUTH_FA0]], ptr [[PTRAUTH_FA1]], ptr [[PTRAUTH_FA2]]] +fpt __ptrauth(1,1,712) fa[3] = { &external_function, &external_function, &external_function }; + +struct C { + fpt __ptrauth(1,0,431) f0; + fpt __ptrauth(1,0,9182) f1; + fpt __ptrauth(1,0,783) f2; +}; +// CHECK: [[PTRAUTH_FS0:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 0, i64 431 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_FS1:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 0, i64 9182 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_FS2:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 0, i64 783 }, section "llvm.ptrauth" +// CHECK: @fs1 = global %struct.C { ptr [[PTRAUTH_FS0]], ptr [[PTRAUTH_FS1]], ptr [[PTRAUTH_FS2]] } +struct C fs1 = { &external_function, &external_function, &external_function }; + +struct D { + fpt __ptrauth(1,1,1276) f0; + fpt __ptrauth(1,1,23674) f1; + fpt __ptrauth(1,1,163) f2; +}; +// CHECK: [[PTRAUTH_FS0:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr @fs2 to i64), i64 1276 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_FS1:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr getelementptr inbounds (%struct.D, ptr @fs2, i32 0, i32 1) to i64), i64 23674 }, section "llvm.ptrauth" +// CHECK: [[PTRAUTH_FS2:@external_function.ptrauth.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 1, i64 ptrtoint (ptr getelementptr inbounds (%struct.D, ptr @fs2, i32 0, i32 2) to i64), i64 163 }, section "llvm.ptrauth" +// CHECK: @fs2 = global %struct.D { ptr [[PTRAUTH_FS0]], ptr [[PTRAUTH_FS1]], ptr [[PTRAUTH_FS2]] } +struct D fs2 = { &external_function, &external_function, &external_function }; diff --git a/clang/test/CodeGen/ptrauth-weak_import-wrapper-globals.c b/clang/test/CodeGen/ptrauth-weak_import-wrapper-globals.c new file mode 100644 index 0000000000000..1156d9835cdc4 --- /dev/null +++ b/clang/test/CodeGen/ptrauth-weak_import-wrapper-globals.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s + +extern void foo() __attribute__((weak_import)); + +// CHECK-LABEL: define void @bar() +// CHECK: icmp ne ptr @foo.ptrauth, null +// CHECK: br i1 +void bar() { + if (foo) + foo(); +} diff --git a/clang/test/CodeGen/ptrauth-weak_import.c b/clang/test/CodeGen/ptrauth-weak_import.c index 1f53747a2640e..84b77ec3d35e1 100644 --- a/clang/test/CodeGen/ptrauth-weak_import.c +++ b/clang/test/CodeGen/ptrauth-weak_import.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s extern void foo() __attribute__((weak_import)); diff --git a/clang/test/CodeGen/ptrauth.c b/clang/test/CodeGen/ptrauth.c new file mode 100644 index 0000000000000..a68617a19a323 --- /dev/null +++ b/clang/test/CodeGen/ptrauth.c @@ -0,0 +1,100 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck -check-prefix=CHECK -check-prefix=NOPCH %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-pch %s -o %t.ast +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -x ast -o - %t.ast | FileCheck -check-prefix=CHECK -check-prefix=PCH %s + +#define FNPTRKEY 0 + +void (*fnptr)(void); +long discriminator; + +extern void external_function(void); +// CHECK: [[EXTERNAL_FUNCTION:@.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @fptr1 = global ptr [[EXTERNAL_FUNCTION]] +void (*fptr1)(void) = external_function; +// CHECK: @fptr2 = global ptr [[EXTERNAL_FUNCTION]] +void (*fptr2)(void) = &external_function; + +// CHECK: [[SIGNED:@.*]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 2, i64 0, i64 26 }, section "llvm.ptrauth", align 8 +// CHECK: @fptr3 = global ptr [[SIGNED]] +void (*fptr3)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, 26); + +// CHECK: @fptr4 = global ptr [[SIGNED:@.*]], +// CHECK: [[SIGNED]] = private constant { ptr, i32, i64, i64 } { ptr @external_function, i32 2, i64 ptrtoint (ptr @fptr4 to i64), i64 26 }, section "llvm.ptrauth", align 8 +void (*fptr4)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, __builtin_ptrauth_blend_discriminator(&fptr4, 26)); + +// CHECK-LABEL: define void @test_call() +void test_call() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 0, i64 0) ] + fnptr(); +} + +// CHECK-LABEL: define void @test_direct_call() +void test_direct_call() { + // CHECK: call void @test_call(){{$}} + test_call(); +} + +void abort(); +// CHECK-LABEL: define void @test_direct_builtin_call() +void test_direct_builtin_call() { + // CHECK: call void @abort() {{#[0-9]+$}} + abort(); +} + +// CHECK-LABEL: define void @test_sign_unauthenticated_peephole() +void test_sign_unauthenticated_peephole() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: call void [[T0]](){{$}} + // CHECK-NEXT: ret void + __builtin_ptrauth_sign_unauthenticated(fnptr, FNPTRKEY, 0)(); +} + +// This peephole doesn't kick in because it's incorrect when ABI pointer +// authentication is enabled. +// CHECK-LABEL: define void @test_auth_peephole() +void test_auth_peephole() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: [[T1:%.*]] = load i64, ptr @discriminator, + // CHECK-NEXT: [[T2:%.*]] = ptrtoint ptr [[T0]] to i64 + // CHECK-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[T2]], i32 0, i64 [[T1]]) + // CHECK-NEXT: [[T4:%.*]] = inttoptr i64 [[T3]] to ptr + // CHECK-NEXT: call void [[T4]]() [ "ptrauth"(i32 0, i64 0) ] + // CHECK-NEXT: ret void + __builtin_ptrauth_auth(fnptr, 0, discriminator)(); +} + +// CHECK-LABEL: define void @test_auth_and_resign_peephole() +void test_auth_and_resign_peephole() { + // CHECK: [[T0:%.*]] = load ptr, ptr @fnptr, + // CHECK-NEXT: [[T1:%.*]] = load i64, ptr @discriminator, + // CHECK-NEXT: call void [[T0]]() [ "ptrauth"(i32 2, i64 [[T1]]) ] + // CHECK-NEXT: ret void + __builtin_ptrauth_auth_and_resign(fnptr, 2, discriminator, FNPTRKEY, 0)(); +} + +// CHECK-LABEL: define ptr @test_function_pointer() +// CHECK: [[EXTERNAL_FUNCTION]] +void (*test_function_pointer())(void) { + return external_function; +} + +// rdar://34562484 - Handle IR types changing in the caching mechanism. +struct InitiallyIncomplete; +extern struct InitiallyIncomplete returns_initially_incomplete(void); +// CHECK-LABEL: define void @use_while_incomplete() +void use_while_incomplete() { + // NOPCH: [[VAR:%.*]] = alloca ptr, + // NOPCH-NEXT: store ptr @returns_initially_incomplete.ptrauth, ptr [[VAR]], + // PCH: [[VAR:%.*]] = alloca ptr, + // PCH-NEXT: store ptr @returns_initially_incomplete.ptrauth, ptr [[VAR]], + struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +} +struct InitiallyIncomplete { int x; }; +// CHECK-LABEL: define void @use_while_complete() +void use_while_complete() { + // CHECK: [[VAR:%.*]] = alloca ptr, + // CHECK-NEXT: store ptr @returns_initially_incomplete.ptrauth, ptr [[VAR]], + // CHECK-NEXT: ret void + struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +} diff --git a/clang/test/CodeGen/split-cold-code.c b/clang/test/CodeGen/split-cold-code.c new file mode 100644 index 0000000000000..ad7c543507f00 --- /dev/null +++ b/clang/test/CodeGen/split-cold-code.c @@ -0,0 +1,40 @@ +// === New PM (ditto) === +// No splitting at -O0. +// RUN: %clang_cc1 -O0 -fsplit-cold-code -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s +// +// No splitting at -Oz. +// RUN: %clang_cc1 -Oz -fsplit-cold-code -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s +// +// Split by default. +// RUN: %clang_cc1 -O3 -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s +// +// No splitting when it's explicitly disabled. +// RUN: %clang_cc1 -O3 -fno-split-cold-code -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s +// +// No splitting when LLVM passes are disabled. +// RUN: %clang_cc1 -O3 -fsplit-cold-code -disable-llvm-passes -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s +// +// Split at -O1. +// RUN: %clang_cc1 -O1 -fsplit-cold-code -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s +// +// Split at -Os. +// RUN: %clang_cc1 -Os -fsplit-cold-code -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s +// +// Split at -O2. +// RUN: %clang_cc1 -O2 -fsplit-cold-code -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s +// +// Split at -O3. +// RUN: %clang_cc1 -O3 -fsplit-cold-code -fdebug-pass-manager \ +// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s + +// NEWPM-NO-SPLIT-NOT: HotColdSplit + +// NEWPM-SPLIT: HotColdSplit diff --git a/clang/test/CodeGen/swift-async-call-conv.c b/clang/test/CodeGen/swift-async-call-conv.c index 39511698bbae9..0f8c521ccfc5e 100644 --- a/clang/test/CodeGen/swift-async-call-conv.c +++ b/clang/test/CodeGen/swift-async-call-conv.c @@ -3,12 +3,14 @@ // RUN: %clang_cc1 -no-enable-noundef-analysis -triple armv7-apple-darwin9 -emit-llvm -o - %s | FileCheck %s // RUN: %clang_cc1 -no-enable-noundef-analysis -triple armv7s-apple-ios9 -emit-llvm -o - %s | FileCheck %s // RUN: %clang_cc1 -no-enable-noundef-analysis -triple armv7k-apple-ios9 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -no-enable-noundef-analysis -triple i386-apple-watchos2 -emit-llvm -o - %s | FileCheck %s // RUN: %clang_cc1 -no-enable-noundef-analysis -x c++ -triple x86_64-apple-darwin10 -target-cpu core2 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY // RUN: %clang_cc1 -no-enable-noundef-analysis -x c++ -triple arm64-apple-ios9 -target-cpu cyclone -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY // RUN: %clang_cc1 -no-enable-noundef-analysis -x c++ -triple armv7-apple-darwin9 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY // RUN: %clang_cc1 -no-enable-noundef-analysis -x c++ -triple armv7s-apple-ios9 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY // RUN: %clang_cc1 -no-enable-noundef-analysis -x c++ -triple armv7k-apple-ios9 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY +// RUN: %clang_cc1 -no-enable-noundef-analysis -x c++ -triple i386-apple-watchos2 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CPPONLY // Test tail call behavior when a swiftasynccall function is called // from another swiftasynccall function. diff --git a/clang/test/CodeGen/swift-call-conv.c b/clang/test/CodeGen/swift-call-conv.c index c5972541b4b17..53443ed8cc555 100644 --- a/clang/test/CodeGen/swift-call-conv.c +++ b/clang/test/CodeGen/swift-call-conv.c @@ -1,5 +1,4 @@ // RUN: %clang_cc1 -triple aarch64-unknown-windows-msvc -emit-llvm %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple x86_64-unknown-windows-msvc -emit-llvm %s -o - | FileCheck %s // REQUIRES: aarch64-registered-target,arm-registered-target,x86-registered-target diff --git a/clang/test/CodeGen/ubsan-function.cpp b/clang/test/CodeGen/ubsan-function.cpp index 76d4237383f83..a1068fa440183 100644 --- a/clang/test/CodeGen/ubsan-function.cpp +++ b/clang/test/CodeGen/ubsan-function.cpp @@ -3,6 +3,8 @@ // RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm -o - %s -fsanitize=function -fno-sanitize-recover=all | FileCheck %s --check-prefixes=CHECK,GNU,64 // RUN: %clang_cc1 -triple aarch64_be-linux-gnu -emit-llvm -o - %s -fsanitize=function -fno-sanitize-recover=all | FileCheck %s --check-prefixes=CHECK,GNU,64 // RUN: %clang_cc1 -triple arm-none-eabi -emit-llvm -o - %s -fsanitize=function -fno-sanitize-recover=all | FileCheck %s --check-prefixes=CHECK,ARM,GNU,32 +// RUN: %clang_cc1 -triple apple-macosx-x86_64 -emit-llvm -o - %s -fsanitize=function -fno-sanitize-recover=all | FileCheck %s --check-prefixes=CHECK,64 + // RUN: %clang_cc1 -triple arm64e-apple-ios -emit-llvm -o - %s -fsanitize=function -fno-sanitize-recover=all -fptrauth-calls | FileCheck %s --check-prefixes=CHECK,GNU,64,AUTH // RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm -o - %s -fsanitize=function -fno-sanitize-recover=all -fptrauth-calls | FileCheck %s --check-prefixes=CHECK,GNU,64,AUTH diff --git a/clang/test/CodeGen/ubsan-trap-merge.c b/clang/test/CodeGen/ubsan-trap-merge.c index b06420950d941..e325e56cd4583 100644 --- a/clang/test/CodeGen/ubsan-trap-merge.c +++ b/clang/test/CodeGen/ubsan-trap-merge.c @@ -6,16 +6,20 @@ // N.B. although the clang driver defaults to -fsanitize-merge=undefined, // clang_cc1 defaults to non-merge. (This is similar to -fsanitize-recover, for // which the default is also applied at the driver level only.) +// +// FIXME: -fno-split-cold-code` is required because swift clang enables `HotColdSplittingPass` by default which +// causes a divergence in IR from upstream (rdar://143354376). +// // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 %s -o - -fsanitize-trap=signed-integer-overflow | FileCheck %s --check-prefixes=TRAP-NOMERGE -// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 %s -o - | FileCheck %s --check-prefixes=HANDLER-NOMERGE +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 %s -o - -fno-split-cold-code | FileCheck %s --check-prefixes=HANDLER-NOMERGE // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 %s -o - -fsanitize-minimal-runtime | FileCheck %s --check-prefixes=MINRT-NOMERGE // // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fno-sanitize-merge=signed-integer-overflow %s -o - -fsanitize-trap=signed-integer-overflow | FileCheck %s --check-prefixes=TRAP-NOMERGE -// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fno-sanitize-merge=signed-integer-overflow %s -o - | FileCheck %s --check-prefixes=HANDLER-NOMERGE +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fno-sanitize-merge=signed-integer-overflow %s -o - -fno-split-cold-code | FileCheck %s --check-prefixes=HANDLER-NOMERGE // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fno-sanitize-merge=signed-integer-overflow %s -o - -fsanitize-minimal-runtime | FileCheck %s --check-prefixes=MINRT-NOMERGE // // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fsanitize-merge=signed-integer-overflow %s -o - -fsanitize-trap=signed-integer-overflow | FileCheck %s --check-prefixes=TRAP-MERGE -// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fsanitize-merge=signed-integer-overflow %s -o - | FileCheck %s --check-prefixes=HANDLER-MERGE +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fsanitize-merge=signed-integer-overflow %s -o - -fno-split-cold-code | FileCheck %s --check-prefixes=HANDLER-MERGE // RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fsanitize-merge=signed-integer-overflow %s -o - -fsanitize-minimal-runtime | FileCheck %s --check-prefixes=MINRT-MERGE // // REQUIRES: x86-registered-target diff --git a/clang/test/CodeGenCXX/microsoft-abi-typeid.cpp b/clang/test/CodeGenCXX/microsoft-abi-typeid.cpp index 24a19cd856602..e8f3e9c58747f 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-typeid.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-typeid.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -emit-llvm -O1 -o - -triple=i386-pc-win32 %s -fexceptions -fcxx-exceptions | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -O1 -o - -triple=i386-pc-win32 %s -fexceptions -fcxx-exceptions -fno-split-cold-code | FileCheck %s struct type_info; namespace std { using ::type_info; } diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2-wrapper-globals.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2-wrapper-globals.cpp new file mode 100644 index 0000000000000..b29a6c8b20bb5 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2-wrapper-globals.cpp @@ -0,0 +1,105 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fapple-kext -fno-rtti -emit-llvm -o - %s | FileCheck %s + +// CHECK: @_ZTV1A = unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr null, ptr @_ZNK1A3abcEv.ptrauth, ptr null] } +// CHECK: @_ZTV4Base = unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr null, ptr @_ZNK4Base3abcEv.ptrauth, ptr null] } +// CHECK: @_ZTV8Derived2 = unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr null, ptr null, ptr @_ZNK8Derived23efgEv.ptrauth, ptr null] } +// CHECK: @_ZTV2D2 = unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr null, ptr null, ptr @_ZNK2D23abcEv.ptrauth, ptr null] } + +struct A { + virtual const char* abc(void) const; +}; + +const char* A::abc(void) const {return "A"; }; + +struct B : virtual A { + virtual void VF(); +}; + +void B::VF() {} + +void FUNC(B* p) { +// CHECK: [[T1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV1A, i64 2) +// CHECK-NEXT: [[BT1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV1A, i64 2) to i64), i64 12401) +// CHECK-NEXT: [[T2:%.*]] = call noundef ptr [[T1]](ptr noundef {{.*}}) [ "ptrauth"(i32 0, i64 [[BT1]]) ] + const char* c = p->A::abc(); +} + + +// Test2 +struct Base { virtual char* abc(void) const; }; + +char* Base::abc() const { return 0; } + +struct Derived : public Base { +}; + +void FUNC1(Derived* p) { +// CHECK: [[U1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV4Base, i64 2) +// CHECK-NEXT: [[BU1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV4Base, i64 2) to i64), i64 64320) +// CHECK-NEXT: [[U2:%.*]] = call noundef ptr [[U1]](ptr noundef {{.*}}) [ "ptrauth"(i32 0, i64 [[BU1]]) ] + char* c = p->Base::abc(); +} + + +// Test3 +struct Base2 { }; + +struct Derived2 : virtual Base2 { + virtual char* efg(void) const; +}; + +char* Derived2::efg(void) const { return 0; } + +void FUNC2(Derived2* p) { +// CHECK: [[V1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV8Derived2, i64 3) +// CHECK-NEXT: [[BV1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV8Derived2, i64 3) to i64), i64 36603) +// CHECK-NEXT: [[V2:%.*]] = call noundef ptr [[V1]](ptr noundef {{.*}}) [ "ptrauth"(i32 0, i64 [[BV1]]) ] + char* c = p->Derived2::efg(); +} + +// Test4 +struct Base3 { }; + +struct D1 : virtual Base3 { +}; + +struct D2 : virtual Base3 { + virtual char *abc(void) const; +}; + +struct Sub : D1, D2 { +}; + +char* D2::abc(void) const { return 0; } + +void FUNC3(Sub* p) { +// CHECK: [[W1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV2D2, i64 3) +// CHECK-NEXT: [[BW1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV2D2, i64 3) to i64), i64 20222) +// CHECK-NEXT: [[W2:%.*]] = call noundef ptr [[W1]](ptr noundef {{.*}}) [ "ptrauth"(i32 0, i64 [[BW1]]) ] + char* c = p->D2::abc(); +} + + +// Test4 +struct Base4 { virtual void abc(); }; + +void Base4::abc() {} + +struct Derived4 : public Base4 { + void abc() override; +}; + +void Derived4::abc() {} + +void FUNC4(Derived4* p) { +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 426) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + p->abc(); +} diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp index c2f20d56b0a6b..06760f1af390a 100644 --- a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp +++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-2.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fapple-kext -fno-rtti -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fapple-kext -fno-rtti -emit-llvm -o - %s | FileCheck %s // CHECK: @_ZTV1A = unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr null, ptr ptrauth (ptr @_ZNK1A3abcEv, i32 0, i64 12401, ptr getelementptr inbounds ({ [4 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 2)), ptr null] }, align 8 // CHECK: @_ZTV4Base = unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr null, ptr ptrauth (ptr @_ZNK4Base3abcEv, i32 0, i64 64320, ptr getelementptr inbounds ({ [4 x ptr] }, ptr @_ZTV4Base, i32 0, i32 0, i32 2)), ptr null] }, align 8 diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-wrapper-globals.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-wrapper-globals.cpp new file mode 100644 index 0000000000000..22318a4a5724a --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call-wrapper-globals.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fapple-kext -emit-llvm -o - %s | FileCheck %s + +// CHECK: @_ZTV5TemplIiE = internal unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTI5TemplIiE, ptr @_ZN5TemplIiE1fEv.ptrauth, ptr @_ZN5TemplIiE1gEv.ptrauth, ptr null] } + +struct Base { + virtual void abc(void) const; +}; + +void Base::abc(void) const {} + +void FUNC(Base* p) { + p->Base::abc(); +} + +// CHECK: getelementptr inbounds (ptr, ptr @_ZTV4Base, i64 2) +// CHECK-NOT: call void @_ZNK4Base3abcEv + +template +struct Templ { + virtual void f() {} + virtual void g() {} +}; +template +struct SubTempl : public Templ { + virtual void f() {} // override + virtual void g() {} // override +}; + +void f(SubTempl* t) { + // Qualified calls go through the (qualified) vtable in apple-kext mode. + // Since t's this pointer points to SubTempl's vtable, the call needs + // to load Templ's vtable. Hence, Templ::g needs to be + // instantiated in this TU, for it's referenced by the vtable. + // (This happens only in apple-kext mode; elsewhere virtual calls can always + // use the vtable pointer off this instead of having to load the vtable + // symbol.) + t->Templ::f(); +} + +// CHECK: getelementptr inbounds (ptr, ptr @_ZTV5TemplIiE, i64 2) +// CHECK: define internal void @_ZN5TemplIiE1fEv(ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %this) +// CHECK: define internal void @_ZN5TemplIiE1gEv(ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %this) diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp index 996829a14d7d1..37ec0ca1b885d 100644 --- a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp +++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-call.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fapple-kext -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fapple-kext -emit-llvm -o - %s | FileCheck %s // CHECK: @_ZTV5TemplIiE = internal unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTI5TemplIiE, ptr ptrauth (ptr @_ZN5TemplIiE1fEv, i32 0, i64 22189, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 2)), ptr ptrauth (ptr @_ZN5TemplIiE1gEv, i32 0, i64 9912, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 3)), ptr null] }, align 8 diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call-wrapper-globals.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call-wrapper-globals.cpp new file mode 100644 index 0000000000000..f10ed46aabdd8 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call-wrapper-globals.cpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++98 -fptrauth-calls -fapple-kext -fno-rtti -disable-O0-optnone -emit-llvm -o - %s | FileCheck %s + +// CHECK: @_ZTV5TemplIiE = internal unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr null, ptr @_ZN5TemplIiED1Ev.ptrauth, ptr @_ZN5TemplIiED0Ev.ptrauth, ptr @_ZN5TemplIiE1fEv.ptrauth, ptr @_ZN5TemplIiE1gEv.ptrauth, ptr null] } + +struct B1 { + virtual ~B1(); +}; + +B1::~B1() {} + +void DELETE(B1 *pb1) { + pb1->B1::~B1(); +} +// CHECK-LABEL: define void @_ZN2B1D0Ev +// CHECK: [[T1:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV2B1, i64 2) +// CHECK-NEXT: [[B1:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV2B1, i64 2) to i64), i64 14635) +// CHECK-NEXT: call noundef ptr [[T1]](ptr noundef nonnull align 8 dereferenceable(8) [[T2:%.*]]) [ "ptrauth"(i32 0, i64 [[B1]]) ] +// CHECK-LABEL: define void @_Z6DELETEP2B1 +// CHECK: [[T3:%.*]] = load ptr, ptr getelementptr inbounds (ptr, ptr @_ZTV2B1, i64 2) +// CHECK-NEXT: [[B3:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds (ptr, ptr @_ZTV2B1, i64 2) to i64), i64 14635) +// CHECK-NEXT: call noundef ptr [[T3]](ptr noundef nonnull align 8 dereferenceable(8) [[T4:%.*]]) [ "ptrauth"(i32 0, i64 [[B3]]) + +template +struct Templ { + virtual ~Templ(); // Out-of-line so that the destructor doesn't cause a vtable + virtual void f() {} + virtual void g() {} +}; +template +struct SubTempl : public Templ { + virtual ~SubTempl() {} // override + virtual void f() {} // override + virtual void g() {} // override +}; + +void f(SubTempl* t) { + // Qualified calls go through the (qualified) vtable in apple-kext mode. + // Since t's this pointer points to SubTempl's vtable, the call needs + // to load Templ's vtable. Hence, Templ::g needs to be + // instantiated in this TU, for it's referenced by the vtable. + // (This happens only in apple-kext mode; elsewhere virtual calls can always + // use the vtable pointer off this instead of having to load the vtable + // symbol.) + t->Templ::~Templ(); +} + +// CHECK: getelementptr inbounds (ptr, ptr @_ZTV5TemplIiE, i64 2) +// CHECK: declare void @_ZN5TemplIiED0Ev(ptr noundef nonnull align 8 dereferenceable(8)) +// CHECK: define internal void @_ZN5TemplIiE1fEv(ptr noundef nonnull align 8 dereferenceable(8) %this) +// CHECK: define internal void @_ZN5TemplIiE1gEv(ptr noundef nonnull align 8 dereferenceable(8) %this) diff --git a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp index 7bcf1fbfdb9de..a9d6d7a6a8514 100644 --- a/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp +++ b/clang/test/CodeGenCXX/ptrauth-apple-kext-indirect-virtual-dtor-call.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++98 -fptrauth-calls -fapple-kext -fno-rtti -disable-O0-optnone -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -std=c++98 -fptrauth-calls -fapple-kext -fno-rtti -disable-O0-optnone -emit-llvm -o - %s | FileCheck %s // CHECK: @_ZTV5TemplIiE = internal unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr null, ptr ptrauth (ptr @_ZN5TemplIiED1Ev, i32 0, i64 57986, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 2)), ptr ptrauth (ptr @_ZN5TemplIiED0Ev, i32 0, i64 22856, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 3)), ptr ptrauth (ptr @_ZN5TemplIiE1fEv, i32 0, i64 22189, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 4)), ptr ptrauth (ptr @_ZN5TemplIiE1gEv, i32 0, i64 9912, ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV5TemplIiE, i32 0, i32 0, i32 5)), ptr null] }, align 8 diff --git a/clang/test/CodeGenCXX/ptrauth-global-constant-initializers.cpp b/clang/test/CodeGenCXX/ptrauth-global-constant-initializers.cpp index 9ce9def6156ef..00e68aa32dedd 100644 --- a/clang/test/CodeGenCXX/ptrauth-global-constant-initializers.cpp +++ b/clang/test/CodeGenCXX/ptrauth-global-constant-initializers.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fno-rtti -fptrauth-vtable-pointer-type-discrimination \ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fno-rtti -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-vtable-pointer-address-discrimination -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,DARWIN -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fno-rtti -fptrauth-vtable-pointer-type-discrimination \ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple aarch64-linux-gnu -fptrauth-calls -fno-rtti -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-vtable-pointer-address-discrimination -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,ELF // CHECK: %struct.Base1 = type { ptr } diff --git a/clang/test/CodeGenCXX/ptrauth-member-function-pointer-wrapper-globals.cpp b/clang/test/CodeGenCXX/ptrauth-member-function-pointer-wrapper-globals.cpp new file mode 100644 index 0000000000000..74c3a0c32ba24 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-member-function-pointer-wrapper-globals.cpp @@ -0,0 +1,413 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG %s +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK %s + +// CHECK: %[[STRUCT_A0:.*]] = type { [4 x i32] } +// CHECK: %[[STRUCT_A1:.*]] = type { [8 x i32] } +// CHECK: %[[STRUCT_TRIVIALS:.*]] = type { [4 x i32] } + +// CHECK: @_ZN5Base011nonvirtual0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 0, i64 [[TYPEDISC0:22163]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC0]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual3Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC0]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base016virtual_variadicEiz_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base016virtual_variadicEiz_vfpthunk_, i32 0, i64 0, i64 34368 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base011nonvirtual0Ev.ptrauth.1 = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 0, i64 [[TYPEDISC1:35591]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.2 = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth.3 = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual3Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived011nonvirtual5Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived011nonvirtual5Ev, i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived08virtual6Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived08virtual6Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC1]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived010return_aggEv_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived010return_aggEv_vfpthunk_, i32 0, i64 0, i64 64418 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived04sretEv_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived04sretEv_vfpthunk_, i32 0, i64 0, i64 28187 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_, i32 0, i64 0, i64 8992 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base18virtual7Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base18virtual7Ev_vfpthunk_, i32 0, i64 0, i64 [[TYPEDISC2:61596]] }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN8Derived18virtual7Ev_vfpthunk_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived18virtual7Ev_vfpthunk_, i32 0, i64 0, i64 25206 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.4 = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 0, i64 25206 }, section "llvm.ptrauth", align 8 + +// CHECK: @gmethod0 = global { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base011nonvirtual0Ev.ptrauth.1 to i64), i64 0 }, align 8 +// CHECK: @_ZN8Derived011nonvirtual5Ev.ptrauth.6 = private constant { ptr, i32, i64, i64 } { ptr @_ZN8Derived011nonvirtual5Ev, i32 0, i64 0, i64 [[TYPEDISC0]] }, section "llvm.ptrauth", align 8 +// CHECK: @gmethod1 = global { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived011nonvirtual5Ev.ptrauth.6 to i64), i64 0 }, align 8 +// CHECK: @gmethod2 = global { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth to i64), i64 0 }, align 8 + +// CHECK: @_ZTV5Base0 = unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTI5Base0, ptr @_ZN5Base08virtual1Ev.ptrauth, ptr @_ZN5Base08virtual3Ev.ptrauth, ptr @_ZN5Base016virtual_variadicEiz.ptrauth] }, align 8 +// CHECK: @_ZN5Base08virtual1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 2) to i64), i64 55600 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base08virtual3Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base08virtual3Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 3) to i64), i64 53007 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN5Base016virtual_variadicEiz.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN5Base016virtual_variadicEiz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 4) to i64), i64 7464 }, section "llvm.ptrauth", align 8 + +struct Base0 { + void nonvirtual0(); + virtual void virtual1(); + virtual void virtual3(); + virtual void virtual_variadic(int, ...); +}; + +struct A0 { + int d[4]; +}; + +struct A1 { + int d[8]; +}; + +struct __attribute__((trivial_abi)) TrivialS { + TrivialS(const TrivialS &); + ~TrivialS(); + int p[4]; +}; + +struct Derived0 : Base0 { + void virtual1() override; + void nonvirtual5(); + virtual void virtual6(); + virtual A0 return_agg(); + virtual A1 sret(); + virtual void trivial_abi(TrivialS); +}; + +struct Base1 { + virtual void virtual7(); +}; + +struct Derived1 : Base0, Base1 { + void virtual1() override; + void virtual7() override; +}; + +typedef void (Base0::*MethodTy0)(); +typedef void (Base0::*VariadicMethodTy0)(int, ...); +typedef void (Derived0::*MethodTy1)(); + +// CHECK: define void @_ZN5Base08virtual1Ev( + +// CHECK: define void @_Z5test0v() +// CHECK: %[[METHOD0:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[VARMETHOD1:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD2:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD3:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD4:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD5:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD6:.*]] = alloca { i64, i64 }, align 8 +// CHECK-NEXT: %[[METHOD7:.*]] = alloca { i64, i64 }, align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base011nonvirtual0Ev.ptrauth to i64), i64 0 }, ptr %[[METHOD0]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD0]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD0]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base016virtual_variadicEiz_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[VARMETHOD1]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base011nonvirtual0Ev.ptrauth.1 to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.2 to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual3Ev_vfpthunk_.ptrauth.3 to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived011nonvirtual5Ev.ptrauth to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived08virtual6Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD2]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived010return_aggEv_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD3]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived04sretEv_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD4]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD5]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base18virtual7Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD6]], align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN8Derived18virtual7Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD7]], align 8 +// CHECK-NEXT: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth.4 to i64), i64 0 }, ptr %[[METHOD7]], align 8 +// CHECK: ret void + +// CHECK: define linkonce_odr hidden void @_ZN5Base08virtual1Ev_vfpthunk_(ptr noundef %[[THIS:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK-NEXT: %[[V0:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK-NEXT: %[[VTABLE:.*]] = load ptr, ptr %[[THIS1]], align 8 +// CHECK-NEXT: %[[V2:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK-NEXT: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 2, i64 0) +// CHECK-NEXT: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK-NEXT: %[[VFN:.*]] = getelementptr inbounds ptr, ptr %[[V4]], i64 0 +// CHECK-NEXT: %[[V5:.*]] = load ptr, ptr %[[VFN]], align 8 +// CHECK-NEXT: %[[V6:.*]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK-NEXT: %[[V7:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V6]], i64 55600) +// CHECK-NEXT: musttail call void %[[V5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %[[V0]]) [ "ptrauth"(i32 0, i64 %[[V7]]) ] +// CHECK-NEXT: ret void + +// CHECK: define linkonce_odr hidden void @_ZN5Base08virtual3Ev_vfpthunk_(ptr noundef %{{.*}}) +// CHECK: load ptr, ptr %{{.*}}, align 8 +// CHECK: load ptr, ptr %{{.*}}, align 8 +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %{{.*}}, align 8 +// CHECK: %[[V2:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[V3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V2]], i32 2, i64 0) +// CHECK: %[[V4:.*]] = inttoptr i64 %[[V3]] to ptr +// CHECK: getelementptr inbounds ptr, ptr %[[V4]], i64 1 +// CHECK: call i64 @llvm.ptrauth.blend(i64 %{{.*}}, i64 53007) + +// CHECK: define linkonce_odr hidden void @_ZN5Base016virtual_variadicEiz_vfpthunk_(ptr noundef %[[THIS:.*]], i32 noundef %0, ...) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK-NEXT: %[[_ADDR:.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: store i32 %0, ptr %[[_ADDR]], align 4 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK-NEXT: %[[V1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK-NEXT: %[[V2:.*]] = load i32, ptr %[[_ADDR]], align 4 +// CHECK-NEXT: %[[VTABLE:.*]] = load ptr, ptr %[[THIS1]], align 8 +// CHECK-NEXT: %[[V4:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK-NEXT: %[[V5:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V4]], i32 2, i64 0) +// CHECK-NEXT: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK-NEXT: %[[VFN:.*]] = getelementptr inbounds ptr, ptr %[[V6]], i64 2 +// CHECK-NEXT: %[[V7:.*]] = load ptr, ptr %[[VFN]], align 8 +// CHECK-NEXT: %[[V8:.*]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK-NEXT: %[[V9:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V8]], i64 7464) +// CHECK-NEXT: musttail call void (ptr, i32, ...) %[[V7]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %[[V1]], i32 noundef %[[V2]], ...) [ "ptrauth"(i32 0, i64 %[[V9]]) ] +// CHECK-NEXT: ret void + +// CHECK: define linkonce_odr hidden void @_ZN8Derived08virtual6Ev_vfpthunk_(ptr noundef %[[THIS:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %[[THIS1]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 2, i64 0) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[VFN:.*]] = getelementptr inbounds ptr, ptr %[[V3]], i64 3 +// CHECK: %[[V5:.*]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: call i64 @llvm.ptrauth.blend(i64 %[[V5]], i64 55535) + +// Check that the return value of the musttail call isn't copied to a temporary. + +// CHECK: define linkonce_odr hidden [2 x i64] @_ZN8Derived010return_aggEv_vfpthunk_(ptr noundef %{{.*}}) +// CHECK: %[[CALL:.*]] = musttail call [2 x i64] %{{.*}}(ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %{{.*}}) [ "ptrauth"(i32 0, i64 %{{.*}}) ] +// CHECK-NEXT: ret [2 x i64] %[[CALL]] + +// Check that the sret pointer passed to the caller is forwarded to the musttail +// call. + +// CHECK: define linkonce_odr hidden void @_ZN8Derived04sretEv_vfpthunk_(ptr dead_on_unwind noalias writable sret(%struct.A1) align 4 %[[AGG_RESULT:.*]], ptr noundef %{{.*}}) +// CHECK: musttail call void %{{.*}}(ptr dead_on_unwind writable sret(%struct.A1) align 4 %[[AGG_RESULT]], ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %{{.*}}) [ "ptrauth"(i32 0, i64 %{{.*}}) ] +// CHECK-NEXT: ret void + +// Check that the thunk function doesn't destruct the trivial_abi argument. + +// CHECK: define linkonce_odr hidden void @_ZN8Derived011trivial_abiE8TrivialS_vfpthunk_(ptr noundef %{{.*}}, [2 x i64] %{{.*}}) +// NODEBUG-NOT: call +// CHECK: call i64 @llvm.ptrauth.auth( +// NODEBUG-NOT: call +// CHECK: call i64 @llvm.ptrauth.blend( +// NODEBUG-NOT: call +// CHECK: musttail call void +// CHECK-NEXT: ret void + +// CHECK: define linkonce_odr hidden void @_ZN5Base18virtual7Ev_vfpthunk_(ptr noundef %[[THIS:.*]]) +// CHECK: entry: +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %[[THIS1]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 2, i64 0) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: getelementptr inbounds ptr, ptr %[[V3]], i64 0 + +// CHECK: define linkonce_odr hidden void @_ZN8Derived18virtual7Ev_vfpthunk_(ptr noundef %[[THIS:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: load ptr, ptr %[[THIS_ADDR]], align 8 +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %[[THIS1]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 2, i64 0) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: getelementptr inbounds ptr, ptr %[[V3]], i64 3 + +void Base0::virtual1() {} + +void test0() { + MethodTy0 method0; + method0 = &Base0::nonvirtual0; + method0 = &Base0::virtual1; + method0 = &Base0::virtual3; + + VariadicMethodTy0 varmethod1; + varmethod1 = &Base0::virtual_variadic; + + MethodTy1 method2; + method2 = &Derived0::nonvirtual0; + method2 = &Derived0::virtual1; + method2 = &Derived0::virtual3; + method2 = &Derived0::nonvirtual5; + method2 = &Derived0::virtual6; + + A0 (Derived0::*method3)(); + method3 = &Derived0::return_agg; + + A1 (Derived0::*method4)(); + method4 = &Derived0::sret; + + void (Derived0::*method5)(TrivialS); + method5 = &Derived0::trivial_abi; + + void (Base1::*method6)(); + method6 = &Base1::virtual7; + + void (Derived1::*method7)(); + method7 = &Derived1::virtual7; + method7 = &Derived1::virtual1; +} + +// CHECK: define void @_Z5test1P5Base0MS_FvvE(ptr noundef %[[A0:.*]], [2 x i64] %[[A1_COERCE:.*]]) +// CHECK: %[[A1:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[A0_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[A1_ADDR:.*]] = alloca { i64, i64 }, align 8 +// CHECK: store [2 x i64] %[[A1_COERCE]], ptr %[[A1]], align 8 +// CHECK: %[[A11:.*]] = load { i64, i64 }, ptr %[[A1]], align 8 +// CHECK: store ptr %[[A0]], ptr %[[A0_ADDR]], align 8 +// CHECK: store { i64, i64 } %[[A11]], ptr %[[A1_ADDR]], align 8 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[A0_ADDR]], align 8 +// CHECK: %[[V2:.*]] = load { i64, i64 }, ptr %[[A1_ADDR]], align 8 +// CHECK: %[[MEMPTR_ADJ:.*]] = extractvalue { i64, i64 } %[[V2]], 1 +// CHECK: %[[MEMPTR_ADJ_SHIFTED:.*]] = ashr i64 %[[MEMPTR_ADJ]], 1 +// CHECK: %[[V4:.*]] = getelementptr inbounds i8, ptr %[[V1]], i64 %[[MEMPTR_ADJ_SHIFTED]] +// CHECK: %[[MEMPTR_PTR:.*]] = extractvalue { i64, i64 } %[[V2]], 0 +// CHECK: %[[V5:.*]] = and i64 %[[MEMPTR_ADJ]], 1 +// CHECK: %[[MEMPTR_ISVIRTUAL:.*]] = icmp ne i64 %[[V5]], 0 +// CHECK: br i1 %[[MEMPTR_ISVIRTUAL]] + +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %[[V4]], align 8 +// CHECK: %[[V7:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[V8:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V7]], i32 2, i64 0) +// CHECK: %[[V9:.*]] = inttoptr i64 %[[V8]] to ptr +// CHECK: %[[V10:.*]] = trunc i64 %[[MEMPTR_PTR]] to i32 +// CHECK: %[[V11:.*]] = zext i32 %[[V10]] to i64 +// CHECK: %[[V12:.*]] = getelementptr i8, ptr %[[V9]], i64 %[[V11]] +// CHECK: %[[MEMPTR_VIRTUALFN:.*]] = load ptr, ptr %[[V12]], align 8 +// CHECK: br + +// CHECK: %[[MEMPTR_NONVIRTUALFN:.*]] = inttoptr i64 %[[MEMPTR_PTR]] to ptr +// CHECK: br + +// CHECK: %[[V14:.*]] = phi ptr [ %[[MEMPTR_VIRTUALFN]], {{.*}} ], [ %[[MEMPTR_NONVIRTUALFN]], {{.*}} ] +// CHECK: %[[V15:.*]] = phi i64 [ 0, {{.*}} ], [ [[TYPEDISC0]], {{.*}} ] +// CHECK: call void %[[V14]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %[[V4]]) [ "ptrauth"(i32 0, i64 %[[V15]]) ] +// CHECK: ret void + +void test1(Base0 *a0, MethodTy0 a1) { + (a0->*a1)(); +} + +// CHECK: define void @_Z15testConversion0M5Base0FvvEM8Derived0FvvE([2 x i64] %[[METHOD0_COERCE:.*]], [2 x i64] %[[METHOD1_COERCE:.*]]) +// CHECK: %[[METHOD0:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[METHOD1:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[METHOD0_ADDR:.*]] = alloca { i64, i64 }, align 8 +// CHECK: %[[METHOD1_ADDR:.*]] = alloca { i64, i64 }, align 8 +// CHECK: store [2 x i64] %[[METHOD0_COERCE]], ptr %[[METHOD0]], align 8 +// CHECK: %[[METHOD01:.*]] = load { i64, i64 }, ptr %[[METHOD0]], align 8 +// CHECK: store [2 x i64] %[[METHOD1_COERCE]], ptr %[[METHOD1]], align 8 +// CHECK: %[[METHOD12:.*]] = load { i64, i64 }, ptr %[[METHOD1]], align 8 +// CHECK: store { i64, i64 } %[[METHOD01]], ptr %[[METHOD0_ADDR]], align 8 +// CHECK: store { i64, i64 } %[[METHOD12]], ptr %[[METHOD1_ADDR]], align 8 +// CHECK: %[[V2:.*]] = load { i64, i64 }, ptr %[[METHOD0_ADDR]], align 8 +// CHECK: %[[MEMPTR_PTR:.*]] = extractvalue { i64, i64 } %[[V2]], 0 +// CHECK: %[[MEMPTR_ADJ:.*]] = extractvalue { i64, i64 } %[[V2]], 1 +// CHECK: %[[V3:.*]] = and i64 %[[MEMPTR_ADJ]], 1 +// CHECK: %[[IS_VIRTUAL_OFFSET:.*]] = icmp ne i64 %[[V3]], 0 +// CHECK: br i1 %[[IS_VIRTUAL_OFFSET]] + +// CHECK: %[[V4:.*]] = inttoptr i64 %[[MEMPTR_PTR]] to ptr +// CHECK: %[[V5:.*]] = icmp ne ptr %[[V4]], null +// CHECK: br i1 %[[V5]] + +// CHECK: %[[V6:.*]] = ptrtoint ptr %[[V4]] to i64 +// CHECK: %[[V7:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V6]], i32 0, i64 [[TYPEDISC0]], i32 0, i64 [[TYPEDISC1]]) +// CHECK: %[[V8:.*]] = inttoptr i64 %[[V7]] to ptr +// CHECK: br + +// CHECK: %[[V9:.*]] = phi ptr [ null, {{.*}} ], [ %[[V8]], {{.*}} ] +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[V9]] to i64 +// CHECK: %[[V11:.*]] = insertvalue { i64, i64 } %[[V2]], i64 %[[V1]], 0 +// CHECK: br + +// CHECK: %[[V12:.*]] = phi { i64, i64 } [ %[[V2]], {{.*}} ], [ %[[V11]], {{.*}} ] +// CHECK: store { i64, i64 } %[[V12]], ptr %[[METHOD1_ADDR]], align 8 +// CHECK: ret void + +void testConversion0(MethodTy0 method0, MethodTy1 method1) { + method1 = method0; +} + +// CHECK: define void @_Z15testConversion1M5Base0FvvE( +// CHECK: call i64 @llvm.ptrauth.resign(i64 %{{.*}}, i32 0, i64 [[TYPEDISC0]], i32 0, i64 [[TYPEDISC1]]) + +void testConversion1(MethodTy0 method0) { + MethodTy1 method1 = reinterpret_cast(method0); +} + +// CHECK: define void @_Z15testConversion2M8Derived0FvvE( +// CHECK: call i64 @llvm.ptrauth.resign(i64 %{{.*}}, i32 0, i64 [[TYPEDISC1]], i32 0, i64 [[TYPEDISC0]]) + +void testConversion2(MethodTy1 method1) { + MethodTy0 method0 = static_cast(method1); +} + +// CHECK: define void @_Z15testConversion3M8Derived0FvvE( +// CHECK: call i64 @llvm.ptrauth.resign(i64 %{{.*}}, i32 0, i64 [[TYPEDISC1]], i32 0, i64 [[TYPEDISC0]]) + +void testConversion3(MethodTy1 method1) { + MethodTy0 method0 = reinterpret_cast(method1); +} + +// No need to call @llvm.ptrauth.resign if the source member function +// pointer is a constant. + +// CHECK: define void @_Z15testConversion4v( +// CHECK: %[[METHOD0:.*]] = alloca { i64, i64 }, align 8 +// CHECK: store { i64, i64 } { i64 ptrtoint (ptr @_ZN5Base08virtual1Ev_vfpthunk_.ptrauth to i64), i64 0 }, ptr %[[METHOD0]], align 8 +// CHECK: ret void + +void testConversion4() { + MethodTy0 method0 = reinterpret_cast(&Derived0::virtual1); +} + +// This code used to crash. +namespace testNonVirtualThunk { + struct R {}; + + struct B0 { + virtual void bar(); + }; + + struct B1 { + virtual R foo(); + }; + + struct D : B0, B1 { + virtual R foo(); + }; + + D d; +} + +// CHECK: define internal void @_ZN22TestAnonymousNamespace12_GLOBAL__N_11S3fooEv_vfpthunk_( + +namespace TestAnonymousNamespace { +namespace { +struct S { + virtual void foo(){}; +}; +} // namespace + +void test() { + auto t = &S::foo; +} +} // namespace TestAnonymousNamespace + +// CHECK: define void @_Z39test_builtin_ptrauth_type_discriminatorv() +// CHECK: store i32 [[TYPEDISC0]], ptr % +// CHECK: store i32 [[TYPEDISC1]], ptr % +// CHECK: store i32 [[TYPEDISC2]], ptr % + +void test_builtin_ptrauth_type_discriminator() { + unsigned d; + d = __builtin_ptrauth_type_discriminator(decltype(&Base0::virtual1)); + d = __builtin_ptrauth_type_discriminator(decltype(&Derived0::virtual6)); + d = __builtin_ptrauth_type_discriminator(decltype(&Base1::virtual7)); +} + +MethodTy1 gmethod0 = reinterpret_cast(&Base0::nonvirtual0); +MethodTy0 gmethod1 = reinterpret_cast(&Derived0::nonvirtual5); +MethodTy0 gmethod2 = reinterpret_cast(&Derived0::virtual1); + +// CHECK: define void @_Z15testConvertNullv( +// CHECK: %[[T:.*]] = alloca { i64, i64 }, +// store { i64, i64 } zeroinitializer, { i64, i64 }* %[[T]], + +void testConvertNull() { + VariadicMethodTy0 t = (VariadicMethodTy0)(MethodTy0{}); +} diff --git a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp index e9436f11b5106..ff50225105cf4 100644 --- a/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp +++ b/clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp @@ -1,16 +1,16 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,DARWIN %s -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++17 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,DARWIN,CXX17 %s -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK,DARWIN %s -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 1 -o - %s | FileCheck %s -check-prefix=STACK-PROT -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 2 -o - %s | FileCheck %s -check-prefix=STACK-PROT -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 3 -o - %s | FileCheck %s -check-prefix=STACK-PROT - -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,ELF %s -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++17 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,ELF,CXX17 %s -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK,ELF %s -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 1 -o - %s | FileCheck %s -check-prefix=STACK-PROT -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 2 -o - %s | FileCheck %s -check-prefix=STACK-PROT -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 3 -o - %s | FileCheck %s -check-prefix=STACK-PROT +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,DARWIN %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++17 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,DARWIN,CXX17 %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK,DARWIN %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 1 -o - %s | FileCheck %s -check-prefix=STACK-PROT +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 2 -o - %s | FileCheck %s -check-prefix=STACK-PROT +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 3 -o - %s | FileCheck %s -check-prefix=STACK-PROT + +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,ELF %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++17 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,ELF,CXX17 %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK,ELF %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 1 -o - %s | FileCheck %s -check-prefix=STACK-PROT +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 2 -o - %s | FileCheck %s -check-prefix=STACK-PROT +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 3 -o - %s | FileCheck %s -check-prefix=STACK-PROT // CHECK: @gmethod0 = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 [[TYPEDISC1:35591]]) to i64), i64 0 }, align 8 diff --git a/clang/test/CodeGenCXX/ptrauth-rtti-layout-wrapper-globals.cpp b/clang/test/CodeGenCXX/ptrauth-rtti-layout-wrapper-globals.cpp new file mode 100644 index 0000000000000..0f3288d4013bb --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-rtti-layout-wrapper-globals.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 %s -I%S -triple=arm64-apple-ios -fptrauth-calls -std=c++11 -emit-llvm -o - | FileCheck %s +#include + +struct A { int a; }; + +// CHECK: @_ZTI1A = linkonce_odr hidden constant { ptr, ptr } { ptr @_ZTVN10__cxxabiv117__class_type_infoE.ptrauth, ptr inttoptr (i64 add (i64 ptrtoint (ptr @_ZTS1A to i64), i64 -9223372036854775808) to ptr) } +// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE = external global [0 x ptr] +// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTS1A = linkonce_odr hidden constant [3 x i8] c"1A\00" + +auto ATI = typeid(A); diff --git a/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp b/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp index b50e0908f9db8..84735abcc8bf5 100644 --- a/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp +++ b/clang/test/CodeGenCXX/ptrauth-rtti-layout.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 %s -I%S -triple=arm64-apple-ios -fptrauth-calls -std=c++11 -emit-llvm -o - | FileCheck --check-prefix=DARWIN %s -// RUN: %clang_cc1 %s -I%S -triple=aarch64-linux-gnu -fptrauth-calls -std=c++11 -emit-llvm -o - | FileCheck --check-prefix=ELF %s +// RUN: %clang_cc1 %s -mllvm -ptrauth-emit-wrapper-globals=0 -I%S -triple=arm64-apple-ios -fptrauth-calls -std=c++11 -emit-llvm -o - | FileCheck --check-prefix=DARWIN %s +// RUN: %clang_cc1 %s -mllvm -ptrauth-emit-wrapper-globals=0 -I%S -triple=aarch64-linux-gnu -fptrauth-calls -std=c++11 -emit-llvm -o - | FileCheck --check-prefix=ELF %s #include diff --git a/clang/test/CodeGenCXX/ptrauth-static-destructors.cpp b/clang/test/CodeGenCXX/ptrauth-static-destructors.cpp index 634450bf62ea9..4a01f83390606 100644 --- a/clang/test/CodeGenCXX/ptrauth-static-destructors.cpp +++ b/clang/test/CodeGenCXX/ptrauth-static-destructors.cpp @@ -1,26 +1,33 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ // RUN: | FileCheck %s --check-prefix=CXAATEXIT // RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -fno-use-cxa-atexit | FileCheck %s --check-prefixes=ATEXIT,ATEXIT_DARWIN // RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: | FileCheck %s --check-prefix=CXAATEXIT // RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -fno-use-cxa-atexit | FileCheck %s --check-prefixes=ATEXIT,ATEXIT_ELF // RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -fptrauth-function-pointer-type-discrimination -o - | FileCheck %s --check-prefix=CXAATEXIT_DISC // RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -fptrauth-function-pointer-type-discrimination -fno-use-cxa-atexit \ // RUN: | FileCheck %s --check-prefixes=ATEXIT_DISC,ATEXIT_DISC_DARWIN // RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -emit-llvm -std=c++11 %s \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -fptrauth-function-pointer-type-discrimination -o - | FileCheck %s --check-prefix=CXAATEXIT_DISC // RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -emit-llvm -std=c++11 %s -o - \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -fptrauth-function-pointer-type-discrimination -fno-use-cxa-atexit \ // RUN: | FileCheck %s --check-prefixes=ATEXIT_DISC,ATEXIT_DISC_ELF diff --git a/clang/test/CodeGenCXX/ptrauth-throw.cpp b/clang/test/CodeGenCXX/ptrauth-throw.cpp index 0e6091a370223..f1eeb3dac360d 100644 --- a/clang/test/CodeGenCXX/ptrauth-throw.cpp +++ b/clang/test/CodeGenCXX/ptrauth-throw.cpp @@ -1,8 +1,5 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK -// RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECKDISC - -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK -// RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple aarch64-linux-gnu -fptrauth-calls -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECKDISC +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECKDISC class Foo { public: @@ -10,10 +7,10 @@ class Foo { } }; -// CHECK-LABEL: define{{.*}} void @_Z1fv() +// CHECK-LABEL: define void @_Z1fv() // CHECK: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTI3Foo, ptr ptrauth (ptr @_ZN3FooD1Ev, i32 0)) -// CHECKDISC-LABEL: define{{.*}} void @_Z1fv() +// CHECKDISC-LABEL: define void @_Z1fv() // CHECKDISC: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTI3Foo, ptr ptrauth (ptr @_ZN3FooD1Ev, i32 0, i64 10942)) void f() { @@ -21,10 +18,10 @@ void f() { } // __cxa_throw is defined to take its destructor as "void (*)(void *)" in the ABI. -// CHECK-LABEL: define{{.*}} void @__cxa_throw({{.*}}) +// CHECK-LABEL: define void @__cxa_throw({{.*}}) // CHECK: call void {{%.*}}(ptr noundef {{%.*}}) [ "ptrauth"(i32 0, i64 0) ] -// CHECKDISC-LABEL: define{{.*}} void @__cxa_throw({{.*}}) +// CHECKDISC-LABEL: define void @__cxa_throw({{.*}}) // CHECKDISC: call void {{%.*}}(ptr noundef {{%.*}}) [ "ptrauth"(i32 0, i64 10942) ] extern "C" void __cxa_throw(void *exception, void *, void (*dtor)(void *)) { diff --git a/clang/test/CodeGenCXX/ptrauth-type-info-vtable.cpp b/clang/test/CodeGenCXX/ptrauth-type-info-vtable.cpp index f4396e4027039..17cad323574a2 100644 --- a/clang/test/CodeGenCXX/ptrauth-type-info-vtable.cpp +++ b/clang/test/CodeGenCXX/ptrauth-type-info-vtable.cpp @@ -1,16 +1,19 @@ // RUN: %clang_cc1 -DENABLE_TID=0 -I%S -std=c++11 -triple=arm64e-apple-darwin \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -fptrauth-calls -fptrauth-intrinsics \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NODISC // RUN: %clang_cc1 -DENABLE_TID=0 -I%S -std=c++11 -triple=aarch64-linux-gnu \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -fptrauth-calls -fptrauth-intrinsics \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-vtable-pointer-address-discrimination \ // RUN: %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,NODISC // RUN: %clang_cc1 -DENABLE_TID=1 -I%S -std=c++11 -triple=arm64e-apple-darwin \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -fptrauth-calls -fptrauth-intrinsics \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-vtable-pointer-address-discrimination \ @@ -18,6 +21,7 @@ // RUN: %s -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK,DISC // RUN: %clang_cc1 -DENABLE_TID=1 -I%S -std=c++11 -triple=aarch64-linux-gnu \ +// RUN: -mllvm -ptrauth-emit-wrapper-globals=0 \ // RUN: -fptrauth-calls -fptrauth-intrinsics \ // RUN: -fptrauth-vtable-pointer-type-discrimination \ // RUN: -fptrauth-vtable-pointer-address-discrimination \ diff --git a/clang/test/CodeGenCXX/ptrauth-virtual-function-wrapper-globals.cpp b/clang/test/CodeGenCXX/ptrauth-virtual-function-wrapper-globals.cpp new file mode 100644 index 0000000000000..33ba6504ea359 --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-virtual-function-wrapper-globals.cpp @@ -0,0 +1,596 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - | FileCheck %s + +// Check virtual function pointers in vtables are signed and their relocation +// structures are emitted. + +// CHECK: %[[CLASS_B1:.*]] = type { ptr } + +// CHECK: @_ZTV2B1 = unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI2B1, ptr @_ZN2B12m0Ev.ptrauth] }, align 8 +// CHECK: @_ZTV2B1.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr] }, ptr @_ZTV2B1, i32 0, i32 0, i32 2), i32 2, i64 0, i64 0 }, section "llvm.ptrauth" +// CHECK: @g_B1 = global %[[CLASS_B1]] { ptr @_ZTV2B1.ptrauth } + +// CHECK: @_ZTV2B0 = unnamed_addr constant { [7 x ptr] } { [7 x ptr] [{{.*}} ptr @_ZN2B02m0Ev.ptrauth, ptr @_ZN2B02m1Ev.ptrauth, ptr @_ZN2B02m2Ev.ptrauth, ptr @_ZN2B0D1Ev.ptrauth, ptr @_ZN2B0D0Ev.ptrauth] } +// CHECK: @_ZN2B02m0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B02m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 2) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B02m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 3) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B02m2Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 4) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZN2B0D1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B0D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 5) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZN2B0D0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B0D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 6) to i64), i64 21295 }, section "llvm.ptrauth" + +// CHECK: @_ZTV2D0 = unnamed_addr constant { [9 x ptr] } { [9 x ptr] [{{.*}} ptr @_ZN2D02m0Ev.ptrauth, ptr @_ZTch0_h4_N2D02m1Ev.ptrauth, ptr @_ZN2B02m2Ev.ptrauth.1, ptr @_ZN2D0D1Ev.ptrauth, ptr @_ZN2D0D0Ev.ptrauth, ptr @_ZN2D02m1Ev.ptrauth, ptr @_ZN2D02m3Ev.ptrauth] } +// CHECK: @_ZN2D02m0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D02m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 2) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTch0_h4_N2D02m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTch0_h4_N2D02m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 3) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.1 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B02m2Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 4) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZN2D0D1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D0D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 5) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZN2D0D0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D0D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 6) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZN2D02m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D02m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 7) to i64), i64 35045 }, section "llvm.ptrauth" +// CHECK: @_ZN2D02m3Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D02m3Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 8) to i64), i64 10565 }, section "llvm.ptrauth" + +// CHECK: @_ZTV2D1 = unnamed_addr constant { [8 x ptr] } { [8 x ptr] [{{.*}} ptr @_ZN2D12m0Ev.ptrauth, ptr @_ZTch0_h4_N2D12m1Ev.ptrauth, ptr @_ZN2B02m2Ev.ptrauth.2, ptr @_ZN2D1D1Ev.ptrauth, ptr @_ZN2D1D0Ev.ptrauth, ptr @_ZN2D12m1Ev.ptrauth] } +// CHECK: @_ZN2D12m0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D12m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 2) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTch0_h4_N2D12m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTch0_h4_N2D12m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 3) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.2 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B02m2Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 4) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZN2D1D1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D1D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 5) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZN2D1D0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D1D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 6) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZN2D12m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D12m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 7) to i64), i64 52864 }, section "llvm.ptrauth" + +// CHECK: @_ZTV2D2 = unnamed_addr constant { [9 x ptr], [8 x ptr] } { [9 x ptr] [{{.*}} ptr @_ZN2D22m0Ev.ptrauth, ptr @_ZTch0_h4_N2D22m1Ev.ptrauth, ptr @_ZN2B02m2Ev.ptrauth.3, ptr @_ZN2D2D1Ev.ptrauth, ptr @_ZN2D2D0Ev.ptrauth, ptr @_ZN2D22m1Ev.ptrauth, ptr @_ZN2D22m3Ev.ptrauth], [8 x ptr] [{{.*}} ptr @_ZThn16_N2D22m0Ev.ptrauth, ptr @_ZTchn16_h4_N2D22m1Ev.ptrauth, ptr @_ZN2B02m2Ev.ptrauth.4, ptr @_ZThn16_N2D2D1Ev.ptrauth, ptr @_ZThn16_N2D2D0Ev.ptrauth, ptr @_ZThn16_N2D22m1Ev.ptrauth] } +// CHECK: @_ZN2D22m0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D22m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 2) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTch0_h4_N2D22m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTch0_h4_N2D22m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 3) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.3 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B02m2Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 4) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZN2D2D1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D2D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 5) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZN2D2D0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D2D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 6) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZN2D22m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D22m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 7) to i64), i64 35045 }, section "llvm.ptrauth" +// CHECK: @_ZN2D22m3Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D22m3Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 8) to i64), i64 10565 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D22m0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZThn16_N2D22m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 2) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTchn16_h4_N2D22m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTchn16_h4_N2D22m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 3) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.4 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B02m2Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 4) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D2D1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZThn16_N2D2D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 5) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D2D0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZThn16_N2D2D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 6) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D22m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZThn16_N2D22m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 7) to i64), i64 52864 }, section "llvm.ptrauth" + +// CHECK: @_ZTV2D3 = unnamed_addr constant { [7 x ptr], [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 32 to ptr), ptr null, ptr @_ZTI2D3, ptr @_ZN2D32m0Ev.ptrauth, ptr @_ZN2D32m1Ev.ptrauth, ptr @_ZN2D3D1Ev.ptrauth, ptr @_ZN2D3D0Ev.ptrauth], [7 x ptr] [ptr inttoptr (i64 16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr @_ZTI2D3, ptr @_ZThn16_N2D32m0Ev.ptrauth, ptr @_ZThn16_N2D32m1Ev.ptrauth, ptr @_ZThn16_N2D3D1Ev.ptrauth, ptr @_ZThn16_N2D3D0Ev.ptrauth], [11 x ptr] [ptr inttoptr (i64 -32 to ptr), ptr null, ptr inttoptr (i64 -32 to ptr), ptr inttoptr (i64 -32 to ptr), ptr inttoptr (i64 -32 to ptr), ptr @_ZTI2D3, ptr @_ZTv0_n24_N2D32m0Ev.ptrauth, ptr @_ZTcv0_n32_h4_N2D32m1Ev.ptrauth, ptr @_ZN2B02m2Ev.ptrauth.11, ptr @_ZTv0_n48_N2D3D1Ev.ptrauth, ptr @_ZTv0_n48_N2D3D0Ev.ptrauth] } + +// CHECK: @_ZTC2D30_2V0 = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 32 to ptr), ptr null, ptr @_ZTI2V0, ptr @_ZN2V02m0Ev.ptrauth.12, ptr @_ZN2V02m1Ev.ptrauth.13, ptr @_ZN2V0D1Ev.ptrauth.14, ptr @_ZN2V0D0Ev.ptrauth.15], [11 x ptr] [ptr inttoptr (i64 -32 to ptr), ptr null, ptr inttoptr (i64 -32 to ptr), ptr inttoptr (i64 -32 to ptr), ptr inttoptr (i64 -32 to ptr), ptr @_ZTI2V0, ptr @_ZTv0_n24_N2V02m0Ev.ptrauth.16, ptr @_ZTcv0_n32_h4_N2V02m1Ev.ptrauth.17, ptr @_ZN2B02m2Ev.ptrauth.18, ptr @_ZTv0_n48_N2V0D1Ev.ptrauth.19, ptr @_ZTv0_n48_N2V0D0Ev.ptrauth.20] } + +// CHECK: @_ZN2V02m0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V02m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 3) to i64), i64 44578 }, section "llvm.ptrauth" +// CHECK: @_ZN2V02m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V02m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 4) to i64), i64 30766 }, section "llvm.ptrauth" +// CHECK: @_ZN2V0D1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V0D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 5) to i64), i64 57279 }, section "llvm.ptrauth" +// CHECK: @_ZN2V0D0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V0D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 6) to i64), i64 62452 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n24_N2V02m0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n24_N2V02m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 6) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTcv0_n32_h4_N2V02m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTcv0_n32_h4_N2V02m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 7) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.5 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B02m2Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 8) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V0D1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N2V0D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 9) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V0D0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N2V0D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 10) to i64), i64 21295 }, section "llvm.ptrauth" + +// CHECK: @_ZTC2D316_2V1 = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 16 to ptr), ptr null, ptr @_ZTI2V1, ptr @_ZN2V12m0Ev.ptrauth.21, ptr @_ZN2V12m1Ev.ptrauth.22, ptr @_ZN2V1D1Ev.ptrauth.23, ptr @_ZN2V1D0Ev.ptrauth.24], [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr null, ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr @_ZTI2V1, ptr @_ZTv0_n24_N2V12m0Ev.ptrauth.25, ptr @_ZTcv0_n32_h4_N2V12m1Ev.ptrauth.26, ptr @_ZN2B02m2Ev.ptrauth.27, ptr @_ZTv0_n48_N2V1D1Ev.ptrauth.28, ptr @_ZTv0_n48_N2V1D0Ev.ptrauth.29] } +// CHECK: @_ZN2V12m0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V12m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 3) to i64), i64 49430 }, section "llvm.ptrauth" +// CHECK: @_ZN2V12m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V12m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 4) to i64), i64 57119 }, section "llvm.ptrauth" +// CHECK: @_ZN2V1D1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V1D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 5) to i64), i64 60799 }, section "llvm.ptrauth" +// CHECK: @_ZN2V1D0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V1D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 6) to i64), i64 52565 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n24_N2V12m0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n24_N2V12m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 6) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTcv0_n32_h4_N2V12m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTcv0_n32_h4_N2V12m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 7) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.6 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B02m2Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 8) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V1D1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N2V1D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 9) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V1D0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N2V1D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 10) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZN2D32m0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D32m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 0, i32 3) to i64), i64 44578 }, section "llvm.ptrauth" +// CHECK: @_ZN2D32m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D32m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 0, i32 4) to i64), i64 30766 }, section "llvm.ptrauth" +// CHECK: @_ZN2D3D1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D3D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 0, i32 5) to i64), i64 57279 }, section "llvm.ptrauth" +// CHECK: @_ZN2D3D0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN2D3D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 0, i32 6) to i64), i64 62452 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D32m0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZThn16_N2D32m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 1, i32 3) to i64), i64 49430 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D32m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZThn16_N2D32m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 1, i32 4) to i64), i64 57119 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D3D1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZThn16_N2D3D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 1, i32 5) to i64), i64 60799 }, section "llvm.ptrauth" +// CHECK: @_ZThn16_N2D3D0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZThn16_N2D3D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 1, i32 6) to i64), i64 52565 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n24_N2D32m0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n24_N2D32m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 6) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTcv0_n32_h4_N2D32m1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTcv0_n32_h4_N2D32m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 7) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.11 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B02m2Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 8) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2D3D1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N2D3D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 9) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2D3D0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N2D3D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [7 x ptr], [11 x ptr] }, ptr @_ZTV2D3, i32 0, i32 2, i32 10) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZN2V02m0Ev.ptrauth.12 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V02m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 3) to i64), i64 44578 }, section "llvm.ptrauth" +// CHECK: @_ZN2V02m1Ev.ptrauth.13 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V02m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 4) to i64), i64 30766 }, section "llvm.ptrauth" +// CHECK: @_ZN2V0D1Ev.ptrauth.14 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V0D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 5) to i64), i64 57279 }, section "llvm.ptrauth" +// CHECK: @_ZN2V0D0Ev.ptrauth.15 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V0D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 0, i32 6) to i64), i64 62452 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n24_N2V02m0Ev.ptrauth.16 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n24_N2V02m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 6) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTcv0_n32_h4_N2V02m1Ev.ptrauth.17 = private constant { ptr, i32, i64, i64 } { ptr @_ZTcv0_n32_h4_N2V02m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 7) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.18 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B02m2Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 8) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V0D1Ev.ptrauth.19 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N2V0D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 9) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V0D0Ev.ptrauth.20 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N2V0D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D30_2V0, i32 0, i32 1, i32 10) to i64), i64 21295 }, section "llvm.ptrauth" +// CHECK: @_ZN2V12m0Ev.ptrauth.21 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V12m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 3) to i64), i64 49430 }, section "llvm.ptrauth" +// CHECK: @_ZN2V12m1Ev.ptrauth.22 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V12m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 4) to i64), i64 57119 }, section "llvm.ptrauth" +// CHECK: @_ZN2V1D1Ev.ptrauth.23 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V1D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 5) to i64), i64 60799 }, section "llvm.ptrauth" +// CHECK: @_ZN2V1D0Ev.ptrauth.24 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2V1D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 0, i32 6) to i64), i64 52565 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n24_N2V12m0Ev.ptrauth.25 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n24_N2V12m0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 6) to i64), i64 53119 }, section "llvm.ptrauth" +// CHECK: @_ZTcv0_n32_h4_N2V12m1Ev.ptrauth.26 = private constant { ptr, i32, i64, i64 } { ptr @_ZTcv0_n32_h4_N2V12m1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 7) to i64), i64 15165 }, section "llvm.ptrauth" +// CHECK: @_ZN2B02m2Ev.ptrauth.27 = private constant { ptr, i32, i64, i64 } { ptr @_ZN2B02m2Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 8) to i64), i64 43073 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V1D1Ev.ptrauth.28 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N2V1D1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 9) to i64), i64 25525 }, section "llvm.ptrauth" +// CHECK: @_ZTv0_n48_N2V1D0Ev.ptrauth.29 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N2V1D0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC2D316_2V1, i32 0, i32 1, i32 10) to i64), i64 21295 }, section "llvm.ptrauth" + + +struct S0 { + int f; +}; + +struct S1 { + int f; +}; + +struct S2 : S0, S1 { + int f; +}; + +class B0 { +public: + virtual void m0(); + virtual S1 *m1(); + virtual void m2(); + virtual ~B0(); + int f; +}; + +class B1 { +public: + virtual void m0(); +}; + +class D0 : public B0 { +public: + void m0() override; + S2 *m1() override; + virtual void m3(); + int f; +}; + +class D1 : public B0 { +public: + void m0() override; + S2 *m1() override; + int f; +}; + +class D2 : public D0, public D1 { +public: + void m0() override; + S2 *m1() override; + void m3() override; + int f; +}; + +class V0 : public virtual B0 { +public: + void m0() override; + S2 *m1() override; + int f; +}; + +class V1 : public virtual B0 { +public: + void m0() override; + S2 *m1() override; + ~V1(); + int f; +}; + +class D3 : public V0, public V1 { +public: + void m0() override; + S2 *m1() override; + int f; +}; + +B1 g_B1; + +void B0::m0() {} + +void B1::m0() {} + +void D0::m0() {} + +void D1::m0() {} + +void D2::m0() {} + +void D3::m0() {} + +V1::~V1() { + m1(); +} + +// Check sign/authentication of vtable pointers and authentication of virtual +// functions. + +// CHECK-LABEL: define noundef ptr @_ZN2V1D2Ev( +// CHECK: %[[THIS1:.*]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T1:[0-9]+]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[T2:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T1]], i32 2, i64 0) +// CHECK: %[[T3:[0-9]+]] = inttoptr i64 %[[T2]] to ptr +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[T3]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 %[[T6]], i32 2, i64 0) +// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T7]] to ptr +// CHECK: store ptr %[[SIGNED_VTADDR]], ptr %[[THIS1]] + +// CHECK-LABEL: define void @_Z8testB0m0P2B0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 53119) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testB0m0(B0 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testB0m1P2B0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 1 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 15165) +// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testB0m1(B0 *a) { + a->m1(); +} + +// CHECK-LABEL: define void @_Z8testB0m2P2B0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 2 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 43073) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testB0m2(B0 *a) { + a->m2(); +} + +// CHECK-LABEL: define void @_Z8testD0m0P2D0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 53119) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD0m0(D0 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testD0m1P2D0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 5 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 35045) +// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD0m1(D0 *a) { + a->m1(); +} + +// CHECK-LABEL: define void @_Z8testD0m2P2D0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 2 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 43073) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD0m2(D0 *a) { + a->m2(); +} + +// CHECK-LABEL: define void @_Z8testD0m3P2D0( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 6 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 10565) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD0m3(D0 *a) { + a->m3(); +} + + +// CHECK-LABEL: define void @_Z8testD1m0P2D1( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 53119) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD1m0(D1 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testD1m1P2D1( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 5 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 52864) +// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(16) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD1m1(D1 *a) { + a->m1(); +} + +// CHECK-LABEL: define void @_Z8testD1m2P2D1( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 2 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 43073) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD1m2(D1 *a) { + a->m2(); +} + + +// CHECK-LABEL: define void @_Z8testD2m0P2D2( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 53119) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(36) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD2m0(D2 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testD2m1P2D2( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 5 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 35045) +// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(36) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD2m1(D2 *a) { + a->m1(); +} + +// CHECK-LABEL: define void @_Z10testD2m2D0P2D2( +// CHECK: call void @_ZN2B02m2Ev(ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}){{$}} + +void testD2m2D0(D2 *a) { + a->D0::m2(); +} + +// CHECK-LABEL: define void @_Z10testD2m2D1P2D2( +// CHECK: call void @_ZN2B02m2Ev(ptr noundef nonnull align {{[0-9]+}} dereferenceable(12) %{{.*}}){{$}} + +void testD2m2D1(D2 *a) { + a->D1::m2(); +} + +// CHECK-LABEL: define void @_Z8testD2m3P2D2( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 6 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 10565) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(36) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD2m3(D2 *a) { + a->m3(); +} + +// CHECK-LABEL: define void @_Z8testD3m0P2D3( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 0 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 44578) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD3m0(D3 *a) { + a->m0(); +} + +// CHECK-LABEL: define void @_Z8testD3m1P2D3( +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T0]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 1 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 30766) +// CHECK: call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD3m1(D3 *a) { + a->m1(); +} + +// CHECK: define void @_Z8testD3m2P2D3(ptr noundef %[[A:.*]]) +// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[A]], ptr %[[A_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8 +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %[[V0]], align 8 +// CHECK: %[[V1:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V1]], i32 2, i64 0) +// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr +// CHECK: %[[VBASE_OFFSET_PTR:.*]] = getelementptr i8, ptr %[[V3]], i64 -24 +// CHECK: %[[VBASE_OFFSET:.*]] = load i64, ptr %[[VBASE_OFFSET_PTR]], align 8 +// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 %[[VBASE_OFFSET]] +// CHECK: %[[VTABLE1:.*]] = load ptr, ptr %[[ADD_PTR]], align 8 +// CHECK: %[[V4:.*]] = ptrtoint ptr %[[VTABLE1]] to i64 +// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[V4]], i32 2, i64 0) +// CHECK: %[[V6:.*]] = inttoptr i64 %[[V5]] to ptr +// CHECK: %[[VFN:.*]] = getelementptr inbounds ptr, ptr %[[V6]], i64 2 +// CHECK: %[[V7:.*]] = load ptr, ptr %[[VFN]], align 8 +// CHECK: %[[V8:.*]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[V9:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V8]], i64 43073) +// CHECK: call void %[[V7]](ptr noundef nonnull align 8 dereferenceable(12) %[[ADD_PTR]]) [ "ptrauth"(i32 0, i64 %[[V9]]) ] + +void testD3m2(D3 *a) { + a->m2(); +} + +// CHECK-LABEL: define void @_Z17testD3Destructor0P2D3( +// CHECK: load ptr, ptr +// CHECK: %[[VTABLE:.*]] = load ptr, ptr %{{.*}} +// CHECK: %[[T2:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T2]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:[a-z]+]] = getelementptr inbounds ptr, ptr %[[T4]], i64 3 +// CHECK: %[[T5:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 62452) +// CHECK: call void %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD3Destructor0(D3 *a) { + delete a; +} + +// CHECK-LABEL: define void @_Z17testD3Destructor1P2D3( +// CHECK: %[[T6:.*]] = load ptr, ptr % +// CHECK: %[[VTABLE0:[a-z0-9]+]] = load ptr, ptr % +// CHECK: %[[T2:[0-9]+]] = ptrtoint ptr %[[VTABLE0]] to i64 +// CHECK: %[[T3:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T2]], i32 2, i64 0) +// CHECK: %[[T4:[0-9]+]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[COMPLETE_OFFSET_PTR:.*]] = getelementptr inbounds i64, ptr %[[T4]], i64 -2 +// CHECK: %[[T5:[0-9]+]] = load i64, ptr %[[COMPLETE_OFFSET_PTR]] +// CHECK: %[[T7:[0-9]+]] = getelementptr inbounds i8, ptr %[[T6]], i64 %[[T5]] +// CHECK: %[[VTABLE1:[a-z0-9]+]] = load ptr, ptr %[[T6]] +// CHECK: %[[T9:[0-9]+]] = ptrtoint ptr %[[VTABLE1]] to i64 +// CHECK: %[[T10:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T9]], i32 2, i64 0) +// CHECK: %[[T11:[0-9]+]] = inttoptr i64 %[[T10]] to ptr +// CHECK: %[[VFN:[a-z0-9]+]] = getelementptr inbounds ptr, ptr %[[T11]], i64 2 +// CHECK: %[[T12:[0-9]+]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T13:[0-9]+]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T14:[0-9]+]] = call i64 @llvm.ptrauth.blend(i64 %[[T13]], i64 57279) +// CHECK: %call = call noundef ptr %[[T12]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 %[[T14]]) ] +// CHECK: call void @_ZdlPv(ptr noundef %[[T7]]) + +void testD3Destructor1(D3 *a) { + ::delete a; +} + +// CHECK-LABEL: define void @_Z17testD3Destructor2P2D3( +// CHECK: load ptr, ptr +// CHECK: %[[VTABLE:.*]] = load ptr, ptr % +// CHECK: %[[T2:.*]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T3:.*]] = call i64 @llvm.ptrauth.auth(i64 %[[T2]], i32 2, i64 0) +// CHECK: %[[T4:.*]] = inttoptr i64 %[[T3]] to ptr +// CHECK: %[[VFN:.*]] = getelementptr inbounds ptr, ptr %[[T4]], i64 2 +// CHECK: %[[T5:.*]] = load ptr, ptr %[[VFN]] +// CHECK: %[[T6:.*]] = ptrtoint ptr %[[VFN]] to i64 +// CHECK: %[[T7:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[T6]], i64 57279) +// CHECK: %call = call noundef ptr %[[T5]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(32) %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 %[[T7]]) ] + +void testD3Destructor2(D3 *a) { + a->~D3(); +} + +void materializeConstructors() { + B0 B0; + B1 B1; + D0 D0; + D1 D1; + D2 D2; + D3 D3; + V0 V0; + V1 V1; +} + +// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2B0C2Ev( +// CHECK: %[[THIS:.*]] = load ptr, ptr % +// CHECK: %[[T0:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 40) ({ [7 x ptr] }, ptr @_ZTV2B0, i32 0, i32 0, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T0]] to ptr +// CHECK: store ptr %[[SIGNED_VTADDR]], ptr %[[THIS]] + +// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2D0C2Ev( +// CHECK: %[[T0:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 56) ({ [9 x ptr] }, ptr @_ZTV2D0, i32 0, i32 0, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T0]] to ptr +// CHECK: store ptr %[[SIGNED_VTADDR]], ptr %[[THIS]] + +// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2D1C2Ev( +// CHECK: %[[T0:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 48) ({ [8 x ptr] }, ptr @_ZTV2D1, i32 0, i32 0, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[SIGNED_VTADDR:[0-9]+]] = inttoptr i64 %[[T0]] to ptr +// CHECK: store ptr %[[SIGNED_VTADDR]], ptr %[[THIS]] + +// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2D2C2Ev( +// CHECK: %[[SLOT0:.*]] = load ptr, ptr +// CHECK: %[[SIGN_VTADDR0:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 56) ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 0, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[T1:[0-9]+]] = inttoptr i64 %[[SIGN_VTADDR0]] to ptr +// CHECK: store ptr %[[T1]], ptr %[[SLOT0]] +// CHECK: %[[T3:[a-z0-9.]+]] = getelementptr inbounds i8, ptr %[[SLOT0]], i64 16 +// CHECK: %[[SIGN_VTADDR1:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr getelementptr inbounds inrange(-16, 48) ({ [9 x ptr], [8 x ptr] }, ptr @_ZTV2D2, i32 0, i32 1, i32 2) to i64), i32 2, i64 0) +// CHECK: %[[T5:[0-9]+]] = inttoptr i64 %[[SIGN_VTADDR1]] to ptr +// CHECK: store ptr %[[T5]], ptr %[[T3]] + +// CHECK-LABEL: define linkonce_odr noundef ptr @_ZN2V0C2Ev( +// CHECK: %[[THIS1]] = load ptr, ptr % +// CHECK: %[[VTT:[a-z0-9]+]] = load ptr, ptr %{{.*}} +// CHECK: %[[T0:[0-9]+]] = load ptr, ptr %[[VTT]] +// CHECK: %[[T1:[0-9]+]] = ptrtoint ptr %[[T0]] to i64 +// CHECK: %[[T2:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T1]], i32 2, i64 0) +// CHECK: %[[T3:[0-9]+]] = inttoptr i64 %[[T2]] to ptr +// CHECK: %[[VTADDR0:[0-9]+]] = ptrtoint ptr %[[T3]] to i64 +// CHECK: %[[T7:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 %[[VTADDR0]], i32 2, i64 0) +// CHECK: %[[SIGN_VTADDR0:[0-9]+]] = inttoptr i64 %[[T7]] to ptr +// CHECK: store ptr %[[SIGN_VTADDR0]], ptr %[[SLOT0]] +// CHECK: %[[T9:[0-9]+]] = getelementptr inbounds ptr, ptr %[[VTT]], i64 1 +// CHECK: %[[T10:[0-9]+]] = load ptr, ptr %[[T9]] +// CHECK: %[[T11:[0-9]+]] = ptrtoint ptr %[[T10]] to i64 +// CHECK: %[[T12:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T11]], i32 2, i64 0) +// CHECK: %[[T13:[0-9]+]] = inttoptr i64 %[[T12]] to ptr +// CHECK: %[[VTABLE:[a-z]+]] = load ptr, ptr %[[THIS1]] +// CHECK: %[[T15:[0-9]+]] = ptrtoint ptr %[[VTABLE]] to i64 +// CHECK: %[[T16:[0-9]+]] = call i64 @llvm.ptrauth.auth(i64 %[[T15]], i32 2, i64 0) +// CHECK: %[[T17:[0-9]+]] = inttoptr i64 %[[T16]] to ptr +// CHECK: %[[VBASE_OFFSET_PTR:[a-z.]+]] = getelementptr i8, ptr %[[T17]], i64 -24 +// CHECK: %[[VBASE_OFFSET:[a-z.]+]] = load i64, ptr %[[VBASE_OFFSET_PTR]] +// CHECK: %[[T20:[a-z.]+]] = getelementptr inbounds i8, ptr %[[THIS1]], i64 %[[VBASE_OFFSET]] +// CHECK: %[[VTADDR1:[0-9]+]] = ptrtoint ptr %[[T13]] to i64 +// CHECK: %[[T23:[0-9]+]] = call i64 @llvm.ptrauth.sign(i64 %[[VTADDR1]], i32 2, i64 0) +// CHECK: %[[SIGN_VTADDR1:[0-9]+]] = inttoptr i64 %[[T23]] to ptr +// CHECK: store ptr %[[SIGN_VTADDR1]], ptr %[[T20]] diff --git a/clang/test/CodeGenCXX/ptrauth-virtual-function.cpp b/clang/test/CodeGenCXX/ptrauth-virtual-function.cpp index 4aa24738d8ce3..3d06b4e385a0b 100644 --- a/clang/test/CodeGenCXX/ptrauth-virtual-function.cpp +++ b/clang/test/CodeGenCXX/ptrauth-virtual-function.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - | FileCheck --check-prefixes=CHECK,DARWIN %s -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -emit-llvm -std=c++11 %s -o - | FileCheck --check-prefixes=CHECK,ELF %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - | FileCheck --check-prefixes=CHECK,DARWIN %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple aarch64-linux-gnu -fptrauth-calls -emit-llvm -std=c++11 %s -o - | FileCheck --check-prefixes=CHECK,ELF %s // Check virtual function pointers in vtables are signed. diff --git a/clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk-wrapper-globals.cpp b/clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk-wrapper-globals.cpp new file mode 100644 index 0000000000000..0dec9b399463c --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk-wrapper-globals.cpp @@ -0,0 +1,347 @@ +// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -fptrauth-intrinsics -fptrauth-calls -fptrauth-vtable-pointer-type-discrimination -emit-llvm -no-enable-noundef-analysis -O0 -disable-llvm-passes -o - | FileCheck --check-prefix=CHECK %s + +// The actual vtable construction +// CHECK: @_ZTV1C = unnamed_addr constant { [5 x ptr], [11 x ptr] } { [5 x ptr] [ptr inttoptr (i64 8 to ptr), ptr null, ptr @_ZTI1C, ptr @_ZN1CD1Ev.ptrauth, ptr @_ZN1CD0Ev.ptrauth], [11 x ptr] [ptr inttoptr (i64 -8 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1C, ptr @_ZN1A1fEv.ptrauth.2, ptr @_ZN1A1gEv.ptrauth.3, ptr @_ZN1A1hEz.ptrauth.4, ptr @_ZTv0_n48_N1CD1Ev.ptrauth, ptr @_ZTv0_n48_N1CD0Ev.ptrauth] }, align 8 +// CHECK: @_ZTT1C = unnamed_addr constant [2 x ptr] [ptr @_ZTV1C.ptrauth, ptr @_ZTV1C.ptrauth.1], align 8 +// CHECK: @_ZTV1D = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 8 to ptr), ptr null, ptr @_ZTI1D, ptr @_ZN1DD1Ev.ptrauth, ptr @_ZN1DD0Ev.ptrauth, ptr @_ZN1D1gEv.ptrauth, ptr @_ZN1D1hEz.ptrauth], [11 x ptr] [ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr null, ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1D, ptr @_ZN1A1fEv.ptrauth.6, ptr @_ZTv0_n32_N1D1gEv.ptrauth, ptr @_ZTv0_n40_N1D1hEz.ptrauth, ptr @_ZTv0_n48_N1DD1Ev.ptrauth, ptr @_ZTv0_n48_N1DD0Ev.ptrauth] }, align 8 +// CHECK: @_ZTT1D = unnamed_addr constant [2 x ptr] [ptr @_ZTV1D.ptrauth, ptr @_ZTV1D.ptrauth.5], align 8 +// CHECK: @_ZTV1F = unnamed_addr constant { [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] } { [6 x ptr] [ptr inttoptr (i64 32 to ptr), ptr inttoptr (i64 16 to ptr), ptr null, ptr @_ZTI1F, ptr @_ZN1FD1Ev.ptrauth, ptr @_ZN1FD0Ev.ptrauth], [7 x ptr] [ptr inttoptr (i64 8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1F, ptr @_ZThn8_N1FD1Ev.ptrauth, ptr @_ZThn8_N1FD0Ev.ptrauth, ptr @_ZN1D1gEv.ptrauth.28, ptr @_ZN1D1hEz.ptrauth.29], [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr null, ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1F, ptr @_ZN1A1fEv.ptrauth.30, ptr @_ZTv0_n32_N1D1gEv.ptrauth.31, ptr @_ZTv0_n40_N1D1hEz.ptrauth.32, ptr @_ZTv0_n48_N1FD1EvU11__vtptrauthILj0Lb0Lj62866E.ptrauth, ptr @_ZTv0_n48_N1FD0EvU11__vtptrauthILj0Lb0Lj62866E.ptrauth], [11 x ptr] [ptr inttoptr (i64 -32 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -32 to ptr), ptr @_ZTI1F, ptr @_ZN1E1fEv.ptrauth.33, ptr @_ZN1E1gEv.ptrauth.34, ptr @_ZN1E1hEz.ptrauth.35, ptr @_ZTv0_n48_N1FD1Ev.ptrauth, ptr @_ZTv0_n48_N1FD0Ev.ptrauth] }, align 8 +// CHECK: @_ZTT1F = unnamed_addr constant [8 x ptr] [ptr @_ZTV1F.ptrauth, ptr @_ZTC1F0_1C.ptrauth, ptr @_ZTC1F0_1C.ptrauth.23, ptr @_ZTC1F8_1D.ptrauth, ptr @_ZTC1F8_1D.ptrauth.24, ptr @_ZTV1F.ptrauth.25, ptr @_ZTV1F.ptrauth.26, ptr @_ZTV1F.ptrauth.27], align 8 +// CHECK: @_ZTV1G = unnamed_addr constant { [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] } { [6 x ptr] [ptr inttoptr (i64 16 to ptr), ptr inttoptr (i64 24 to ptr), ptr null, ptr @_ZTI1G, ptr @_ZN1GD1Ev.ptrauth, ptr @_ZN1GD0Ev.ptrauth], [7 x ptr] [ptr inttoptr (i64 16 to ptr), ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1G, ptr @_ZThn8_N1GD1Ev.ptrauth, ptr @_ZThn8_N1GD0Ev.ptrauth, ptr @_ZN1D1gEv.ptrauth.73, ptr @_ZN1D1hEz.ptrauth.74], [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1G, ptr @_ZN1E1fEv.ptrauth.75, ptr @_ZN1E1gEv.ptrauth.76, ptr @_ZN1E1hEz.ptrauth.77, ptr @_ZTv0_n48_N1GD1Ev.ptrauth, ptr @_ZTv0_n48_N1GD0Ev.ptrauth], [11 x ptr] [ptr inttoptr (i64 -24 to ptr), ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr null, ptr inttoptr (i64 -24 to ptr), ptr @_ZTI1G, ptr @_ZN1A1fEv.ptrauth.78, ptr @_ZTv0_n32_N1D1gEv.ptrauth.79, ptr @_ZTv0_n40_N1D1hEz.ptrauth.80, ptr @_ZTv0_n48_N1GD1EvU11__vtptrauthILj0Lb0Lj62866E.ptrauth, ptr @_ZTv0_n48_N1GD0EvU11__vtptrauthILj0Lb0Lj62866E.ptrauth] }, align 8 +// CHECK: @_ZTT1G = unnamed_addr constant [8 x ptr] [ptr @_ZTV1G.ptrauth, ptr @_ZTC1G0_1C.ptrauth, ptr @_ZTC1G0_1C.ptrauth.68, ptr @_ZTC1G8_1D.ptrauth, ptr @_ZTC1G8_1D.ptrauth.69, ptr @_ZTV1G.ptrauth.70, ptr @_ZTV1G.ptrauth.71, ptr @_ZTV1G.ptrauth.72], align 8 +// CHECK: @_ZTV1A = unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr @_ZTI1A, ptr @_ZN1A1fEv.ptrauth, ptr @_ZN1A1gEv.ptrauth, ptr @_ZN1A1hEz.ptrauth, ptr @_ZN1AD1Ev.ptrauth, ptr @_ZN1AD0Ev.ptrauth] }, align 8 +// CHECK: @_ZTI1A = constant { ptr, ptr } { ptr @_ZTVN10__cxxabiv117__class_type_infoE.ptrauth, ptr @_ZTS1A }, align 8 +// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE = external global [0 x ptr] +// CHECK: @_ZTVN10__cxxabiv117__class_type_infoE.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTS1A = constant [3 x i8] c"1A\00", align 1 +// CHECK: @_ZN1A1fEv.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 2) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1gEv.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 3) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1hEz.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 4) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1AD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1AD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 5) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1AD0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1AD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 6) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTV1C.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-24, 16) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 0, i32 3), i32 2, i64 0, i64 0 }, section "llvm.ptrauth" +// CHECK: @_ZTV1C.ptrauth.1 = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-48, 40) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 6), i32 2, i64 0, i64 0 }, section "llvm.ptrauth" +// CHECK: @_ZTI1C = constant { ptr, ptr, i32, i32, ptr, i64 } { ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE.ptrauth, ptr @_ZTS1C, i32 0, i32 1, ptr @_ZTI1B, i64 -6141 }, align 8 +// CHECK: @_ZTVN10__cxxabiv121__vmi_class_type_infoE = external global [0 x ptr] +// CHECK: @_ZTVN10__cxxabiv121__vmi_class_type_infoE.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 2), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTS1C = constant [3 x i8] c"1C\00", align 1 +// CHECK: @_ZTI1B = linkonce_odr hidden constant { ptr, ptr, ptr } { ptr @_ZTVN10__cxxabiv120__si_class_type_infoE.ptrauth, ptr inttoptr (i64 add (i64 ptrtoint (ptr @_ZTS1B to i64), i64 -9223372036854775808) to ptr), ptr @_ZTI1A }, align 8 +// CHECK: @_ZTVN10__cxxabiv120__si_class_type_infoE = external global [0 x ptr] +// CHECK: @_ZTVN10__cxxabiv120__si_class_type_infoE.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv120__si_class_type_infoE, i64 2), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTS1B = linkonce_odr hidden constant [3 x i8] c"1B\00", align 1 +// CHECK: @_ZN1CD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 0, i32 3) to i64), i64 31214 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1CD0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 0, i32 4) to i64), i64 8507 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.2 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1gEv.ptrauth.3 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1hEz.ptrauth.4 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTV1C, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTV1D.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-24, 32) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 3), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTV1D.ptrauth.5 = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-48, 40) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 6), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTI1D = constant { ptr, ptr, i32, i32, ptr, i64 } { ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE.ptrauth, ptr @_ZTS1D, i32 0, i32 1, ptr @_ZTI1B, i64 -6141 }, align 8 +// CHECK: @_ZTS1D = constant [3 x i8] c"1D\00", align 1 +// CHECK: @_ZN1DD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 3) to i64), i64 59423 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1DD0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 4) to i64), i64 25900 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1gEv.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 5) to i64), i64 59070 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1hEz.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 6) to i64), i64 65100 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.6 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n32_N1D1gEv.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n40_N1D1hEz.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTV1E = unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr @_ZTI1E, ptr @_ZN1E1fEv.ptrauth, ptr @_ZN1E1gEv.ptrauth, ptr @_ZN1E1hEz.ptrauth, ptr @_ZN1ED1Ev.ptrauth, ptr @_ZN1ED0Ev.ptrauth] }, align 8 +// CHECK: @_ZTI1E = constant { ptr, ptr } { ptr @_ZTVN10__cxxabiv117__class_type_infoE.ptrauth, ptr @_ZTS1E }, align 8 +// CHECK: @_ZTS1E = constant [3 x i8] c"1E\00", align 1 +// CHECK: @_ZN1E1fEv.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1E1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 2) to i64), i64 28408 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1E1gEv.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1E1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 3) to i64), i64 22926 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1E1hEz.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1E1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 4) to i64), i64 9832 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1ED1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1ED1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 5) to i64), i64 5817 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1ED0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1ED0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1E, i32 0, i32 0, i32 6) to i64), i64 26464 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTC1F0_1C = unnamed_addr constant { [5 x ptr], [11 x ptr] } { [5 x ptr] [ptr inttoptr (i64 16 to ptr), ptr null, ptr @_ZTI1C, ptr @_ZN1CD1Ev.ptrauth.97, ptr @_ZN1CD0Ev.ptrauth.98], [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1C, ptr @_ZN1A1fEv.ptrauth.99, ptr @_ZN1A1gEv.ptrauth.100, ptr @_ZN1A1hEz.ptrauth.101, ptr @_ZTv0_n48_N1CD1Ev.ptrauth.102, ptr @_ZTv0_n48_N1CD0Ev.ptrauth.103] }, align 8 +// CHECK: @_ZN1CD1Ev.ptrauth.7 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 0, i32 3) to i64), i64 31214 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1CD0Ev.ptrauth.8 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 0, i32 4) to i64), i64 8507 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.9 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1gEv.ptrauth.10 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1hEz.ptrauth.11 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD1Ev.ptrauth.12 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD0Ev.ptrauth.13 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTC1F8_1D = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 8 to ptr), ptr null, ptr @_ZTI1D, ptr @_ZN1DD1Ev.ptrauth.104, ptr @_ZN1DD0Ev.ptrauth.105, ptr @_ZN1D1gEv.ptrauth.106, ptr @_ZN1D1hEz.ptrauth.107], [11 x ptr] [ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr inttoptr (i64 -8 to ptr), ptr null, ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1D, ptr @_ZN1A1fEv.ptrauth.108, ptr @_ZTv0_n32_N1D1gEv.ptrauth.109, ptr @_ZTv0_n40_N1D1hEz.ptrauth.110, ptr @_ZTv0_n48_N1DD1Ev.ptrauth.111, ptr @_ZTv0_n48_N1DD0Ev.ptrauth.112] }, align 8 +// CHECK: @_ZN1DD1Ev.ptrauth.14 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 3) to i64), i64 59423 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1DD0Ev.ptrauth.15 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 4) to i64), i64 25900 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1gEv.ptrauth.16 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 5) to i64), i64 59070 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1hEz.ptrauth.17 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 6) to i64), i64 65100 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.18 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n32_N1D1gEv.ptrauth.19 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n40_N1D1hEz.ptrauth.20 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD1Ev.ptrauth.21 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD0Ev.ptrauth.22 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTV1F.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-32, 16) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 0, i32 4), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTC1F0_1C.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-24, 16) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 0, i32 3), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTC1F0_1C.ptrauth.23 = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-48, 40) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 6), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTC1F8_1D.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-24, 32) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 3), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTC1F8_1D.ptrauth.24 = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-48, 40) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 6), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTV1F.ptrauth.25 = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-48, 40) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 6), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTV1F.ptrauth.26 = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-24, 32) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 3), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTV1F.ptrauth.27 = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-48, 40) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 6), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTI1F = constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64, ptr, i64 } { ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE.ptrauth, ptr @_ZTS1F, i32 3, i32 3, ptr @_ZTI1C, i64 2, ptr @_ZTI1D, i64 2050, ptr @_ZTI1E, i64 -8189 }, align 8 +// CHECK: @_ZTS1F = constant [3 x i8] c"1F\00", align 1 +// CHECK: @_ZN1FD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1FD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 0, i32 4) to i64), i64 31214 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1FD0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1FD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 0, i32 5) to i64), i64 8507 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZThn8_N1FD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZThn8_N1FD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 3) to i64), i64 59423 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZThn8_N1FD0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZThn8_N1FD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 4) to i64), i64 25900 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1gEv.ptrauth.28 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 5) to i64), i64 59070 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1hEz.ptrauth.29 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 1, i32 6) to i64), i64 65100 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.30 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n32_N1D1gEv.ptrauth.31 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n40_N1D1hEz.ptrauth.32 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1FD1EvU11__vtptrauthILj0Lb0Lj62866E.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1FD1EvU11__vtptrauthILj0Lb0Lj62866E, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1FD0EvU11__vtptrauthILj0Lb0Lj62866E.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1FD0EvU11__vtptrauthILj0Lb0Lj62866E, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 2, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1E1fEv.ptrauth.33 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1E1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 6) to i64), i64 28408 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1E1gEv.ptrauth.34 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1E1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 7) to i64), i64 22926 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1E1hEz.ptrauth.35 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1E1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 8) to i64), i64 9832 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1FD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1FD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 9) to i64), i64 5817 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1FD0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1FD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1F, i32 0, i32 3, i32 10) to i64), i64 26464 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1CD1Ev.ptrauth.36 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 0, i32 3) to i64), i64 31214 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1CD0Ev.ptrauth.37 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 0, i32 4) to i64), i64 8507 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.38 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1gEv.ptrauth.39 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1hEz.ptrauth.40 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD1Ev.ptrauth.41 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD0Ev.ptrauth.42 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1DD1Ev.ptrauth.43 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 3) to i64), i64 59423 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1DD0Ev.ptrauth.44 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 4) to i64), i64 25900 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1gEv.ptrauth.45 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 5) to i64), i64 59070 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1hEz.ptrauth.46 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 6) to i64), i64 65100 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.47 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n32_N1D1gEv.ptrauth.48 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n40_N1D1hEz.ptrauth.49 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD1Ev.ptrauth.50 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD0Ev.ptrauth.51 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTC1G0_1C = unnamed_addr constant { [5 x ptr], [11 x ptr] } { [5 x ptr] [ptr inttoptr (i64 24 to ptr), ptr null, ptr @_ZTI1C, ptr @_ZN1CD1Ev.ptrauth.113, ptr @_ZN1CD0Ev.ptrauth.114], [11 x ptr] [ptr inttoptr (i64 -24 to ptr), ptr null, ptr null, ptr null, ptr inttoptr (i64 -24 to ptr), ptr @_ZTI1C, ptr @_ZN1A1fEv.ptrauth.115, ptr @_ZN1A1gEv.ptrauth.116, ptr @_ZN1A1hEz.ptrauth.117, ptr @_ZTv0_n48_N1CD1Ev.ptrauth.118, ptr @_ZTv0_n48_N1CD0Ev.ptrauth.119] }, align 8 +// CHECK: @_ZN1CD1Ev.ptrauth.52 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 0, i32 3) to i64), i64 31214 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1CD0Ev.ptrauth.53 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 0, i32 4) to i64), i64 8507 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.54 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1gEv.ptrauth.55 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1hEz.ptrauth.56 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD1Ev.ptrauth.57 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD0Ev.ptrauth.58 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTC1G8_1D = unnamed_addr constant { [7 x ptr], [11 x ptr] } { [7 x ptr] [ptr inttoptr (i64 16 to ptr), ptr null, ptr @_ZTI1D, ptr @_ZN1DD1Ev.ptrauth.120, ptr @_ZN1DD0Ev.ptrauth.121, ptr @_ZN1D1gEv.ptrauth.122, ptr @_ZN1D1hEz.ptrauth.123], [11 x ptr] [ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr inttoptr (i64 -16 to ptr), ptr null, ptr inttoptr (i64 -16 to ptr), ptr @_ZTI1D, ptr @_ZN1A1fEv.ptrauth.124, ptr @_ZTv0_n32_N1D1gEv.ptrauth.125, ptr @_ZTv0_n40_N1D1hEz.ptrauth.126, ptr @_ZTv0_n48_N1DD1Ev.ptrauth.127, ptr @_ZTv0_n48_N1DD0Ev.ptrauth.128] }, align 8 +// CHECK: @_ZN1DD1Ev.ptrauth.59 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 3) to i64), i64 59423 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1DD0Ev.ptrauth.60 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 4) to i64), i64 25900 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1gEv.ptrauth.61 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 5) to i64), i64 59070 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1hEz.ptrauth.62 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 6) to i64), i64 65100 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.63 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n32_N1D1gEv.ptrauth.64 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n40_N1D1hEz.ptrauth.65 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD1Ev.ptrauth.66 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD0Ev.ptrauth.67 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTV1G.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-32, 16) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 0, i32 4), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTC1G0_1C.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-24, 16) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 0, i32 3), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTC1G0_1C.ptrauth.68 = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-48, 40) ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 6), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTC1G8_1D.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-24, 32) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 3), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTC1G8_1D.ptrauth.69 = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-48, 40) ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 6), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTV1G.ptrauth.70 = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-48, 40) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 6), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTV1G.ptrauth.71 = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-48, 40) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 6), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTV1G.ptrauth.72 = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds inrange(-24, 32) ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 3), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTI1G = constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64, ptr, i64 } { ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE.ptrauth, ptr @_ZTS1G, i32 3, i32 3, ptr @_ZTI1E, i64 -8189, ptr @_ZTI1C, i64 2, ptr @_ZTI1D, i64 2050 }, align 8 +// CHECK: @_ZTS1G = constant [3 x i8] c"1G\00", align 1 +// CHECK: @_ZN1GD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1GD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 0, i32 4) to i64), i64 31214 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1GD0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1GD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 0, i32 5) to i64), i64 8507 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZThn8_N1GD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZThn8_N1GD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 3) to i64), i64 59423 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZThn8_N1GD0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZThn8_N1GD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 4) to i64), i64 25900 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1gEv.ptrauth.73 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 5) to i64), i64 59070 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1hEz.ptrauth.74 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 1, i32 6) to i64), i64 65100 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1E1fEv.ptrauth.75 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1E1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 6) to i64), i64 28408 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1E1gEv.ptrauth.76 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1E1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 7) to i64), i64 22926 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1E1hEz.ptrauth.77 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1E1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 8) to i64), i64 9832 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1GD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1GD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 9) to i64), i64 5817 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1GD0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1GD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 2, i32 10) to i64), i64 26464 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.78 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n32_N1D1gEv.ptrauth.79 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n40_N1D1hEz.ptrauth.80 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1GD1EvU11__vtptrauthILj0Lb0Lj62866E.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1GD1EvU11__vtptrauthILj0Lb0Lj62866E, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1GD0EvU11__vtptrauthILj0Lb0Lj62866E.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1GD0EvU11__vtptrauthILj0Lb0Lj62866E, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [6 x ptr], [7 x ptr], [11 x ptr], [11 x ptr] }, ptr @_ZTV1G, i32 0, i32 3, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1CD1Ev.ptrauth.81 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 0, i32 3) to i64), i64 31214 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1CD0Ev.ptrauth.82 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 0, i32 4) to i64), i64 8507 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.83 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1gEv.ptrauth.84 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1hEz.ptrauth.85 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD1Ev.ptrauth.86 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD0Ev.ptrauth.87 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1DD1Ev.ptrauth.88 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 3) to i64), i64 59423 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1DD0Ev.ptrauth.89 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 4) to i64), i64 25900 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1gEv.ptrauth.90 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 5) to i64), i64 59070 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1hEz.ptrauth.91 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 6) to i64), i64 65100 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.92 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n32_N1D1gEv.ptrauth.93 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n40_N1D1hEz.ptrauth.94 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD1Ev.ptrauth.95 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD0Ev.ptrauth.96 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1CD1Ev.ptrauth.97 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 0, i32 3) to i64), i64 31214 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1CD0Ev.ptrauth.98 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 0, i32 4) to i64), i64 8507 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.99 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1gEv.ptrauth.100 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1hEz.ptrauth.101 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD1Ev.ptrauth.102 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD0Ev.ptrauth.103 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1F0_1C, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1DD1Ev.ptrauth.104 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 3) to i64), i64 59423 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1DD0Ev.ptrauth.105 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 4) to i64), i64 25900 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1gEv.ptrauth.106 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 5) to i64), i64 59070 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1hEz.ptrauth.107 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 0, i32 6) to i64), i64 65100 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.108 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n32_N1D1gEv.ptrauth.109 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n40_N1D1hEz.ptrauth.110 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD1Ev.ptrauth.111 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD0Ev.ptrauth.112 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1F8_1D, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1CD1Ev.ptrauth.113 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 0, i32 3) to i64), i64 31214 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1CD0Ev.ptrauth.114 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 0, i32 4) to i64), i64 8507 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.115 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1gEv.ptrauth.116 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1hEz.ptrauth.117 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD1Ev.ptrauth.118 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1CD0Ev.ptrauth.119 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1CD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [5 x ptr], [11 x ptr] }, ptr @_ZTC1G0_1C, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1DD1Ev.ptrauth.120 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 3) to i64), i64 59423 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1DD0Ev.ptrauth.121 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 4) to i64), i64 25900 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1gEv.ptrauth.122 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 5) to i64), i64 59070 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1D1hEz.ptrauth.123 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 0, i32 6) to i64), i64 65100 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1fEv.ptrauth.124 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 6) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n32_N1D1gEv.ptrauth.125 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n32_N1D1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 7) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n40_N1D1hEz.ptrauth.126 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n40_N1D1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 8) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD1Ev.ptrauth.127 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 9) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTv0_n48_N1DD0Ev.ptrauth.128 = private constant { ptr, i32, i64, i64 } { ptr @_ZTv0_n48_N1DD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr], [11 x ptr] }, ptr @_ZTC1G8_1D, i32 0, i32 1, i32 10) to i64), i64 63674 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZTV1B = linkonce_odr unnamed_addr constant { [7 x ptr] } { [7 x ptr] [ptr null, ptr @_ZTI1B, ptr @_ZN1A1fEv.ptrauth.129, ptr @_ZN1A1gEv.ptrauth.130, ptr @_ZN1A1hEz.ptrauth.131, ptr @_ZN1BD1Ev.ptrauth, ptr @_ZN1BD0Ev.ptrauth] }, align 8 +// CHECK: @_ZN1A1fEv.ptrauth.129 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1fEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 2) to i64), i64 55636 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1gEv.ptrauth.130 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1gEv, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 3) to i64), i64 19402 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1A1hEz.ptrauth.131 = private constant { ptr, i32, i64, i64 } { ptr @_ZN1A1hEz, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 4) to i64), i64 31735 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1BD1Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1BD1Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 5) to i64), i64 2043 }, section "llvm.ptrauth", align 8 +// CHECK: @_ZN1BD0Ev.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @_ZN1BD0Ev, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ [7 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 6) to i64), i64 63674 }, section "llvm.ptrauth", align 8 + + +extern "C" int printf(const char *format, ...); + +class A { +public: + A() {} + virtual int f(); + virtual int g(); + virtual int h(...); + virtual ~A() {} + +public: + bool necessary_field; +}; + +class B : public A { +public: + B() : A() {} + virtual ~B() {} +}; + +class C : public virtual B { +public: + C() : B() {} + ~C(); +}; + +class D : public virtual B { +public: + D() : B() {} + ~D(); + virtual int g(); + virtual int h(...); +}; + +class E { +public: + virtual int f(); + virtual int g(); + virtual int h(...); + virtual ~E(){}; +}; + +class F : public C, public D, public virtual E { + ~F(); +}; + +class G : public virtual E, public C, public D { + ~G(); +}; + +C::~C() {} +D::~D() {} +F::~F() {} +G::~G() {} +int E::f() { return 1; } +int A::f() { return 0; } +int E::g() { return 1; } +int A::g() { return 0; } +int D::g() { return 0; } + +int E::h(...) { return 1; } +int A::h(...) { return 0; } +int D::h(...) { return 0; } + +int main() { + A *ans = new C(); + delete ans; + + B *b = new D(); + b->f(); + b->g(); + b->h(1,2,3); + b = new C(); + b->f(); + b->h(1,2,3); + b = new C(); + b->f(); + b->h(1,2,3); + b = new F(); + b->f(); + b->g(); + b->h(1,2,3); + + ans = new B(); + delete ans; + + ans = new F(); + ans->f(); + ans->g(); + ans->h(1,2,3); + delete ans; + + E *e = new F(); + e->f(); + e->g(); + e->h(1,2,3); + delete e; + e = new G(); + e->f(); + e->g(); + e->h(1,2,3); + delete e; +} + +// And check the thunks +// CHECK: ptr @_ZTv0_n48_N1CD1Ev(ptr %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866) + +// CHECK: void @_ZTv0_n48_N1CD0Ev(ptr %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866) + +// CHECK: ptr @_ZTv0_n48_N1DD1Ev(ptr %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866) + +// CHECK: void @_ZTv0_n48_N1DD0Ev(ptr %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866) + +// CHECK: void @_ZTv0_n48_N1FD0EvU11__vtptrauthILj0Lb0Lj62866E(ptr %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866) + +// CHECK: void @_ZTv0_n48_N1FD0Ev(ptr %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 12810) + +// CHECK: void @_ZTv0_n48_N1GD0Ev(ptr %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 12810) + +// CHECK: void @_ZTv0_n48_N1GD0EvU11__vtptrauthILj0Lb0Lj62866E(ptr %this) +// CHECK: [[TEMP:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[TEMP:%.*]], i32 2, i64 62866) diff --git a/clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk.cpp b/clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk.cpp index b5c15a29eb6b9..d044b6698752d 100644 --- a/clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk.cpp +++ b/clang/test/CodeGenCXX/ptrauth-vtable-virtual-inheritance-thunk.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple arm64-apple-ios -disable-llvm-passes -fptrauth-intrinsics -fptrauth-calls \ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 %s -x c++ -std=c++11 -triple arm64-apple-ios -disable-llvm-passes -fptrauth-intrinsics -fptrauth-calls \ // RUN: -fptrauth-vtable-pointer-type-discrimination -emit-llvm -O0 -o - | FileCheck --check-prefixes=CHECK,DARWIN %s -// RUN: %clang_cc1 %s -x c++ -std=c++11 -triple aarch64-linux-gnu -disable-llvm-passes -fptrauth-intrinsics -fptrauth-calls \ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 %s -x c++ -std=c++11 -triple aarch64-linux-gnu -disable-llvm-passes -fptrauth-intrinsics -fptrauth-calls \ // RUN: -fptrauth-vtable-pointer-type-discrimination -emit-llvm -O0 -o - | FileCheck --check-prefixes=CHECK,ELF %s // The actual vtable construction diff --git a/clang/test/CodeGenCXX/ptrauth-wrapper-globals.cpp b/clang/test/CodeGenCXX/ptrauth-wrapper-globals.cpp new file mode 100644 index 0000000000000..cf40ce0207eff --- /dev/null +++ b/clang/test/CodeGenCXX/ptrauth-wrapper-globals.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 -fexceptions -fcxx-exceptions -o - %s | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=1 -triple aarch64-linux-gnu -fptrauth-calls -emit-llvm -std=c++11 -fexceptions -fcxx-exceptions -o - %s | FileCheck %s + +void f(void); +auto &f_ref = f; + +// CHECK: define {{(dso_local )?}}void @_Z1gv( +// CHECK: call void @_Z1fv.ptrauth() [ "ptrauth"(i32 0, i64 0) ] + +void g() { f_ref(); } + +void foo1(); + +void test_terminate() noexcept { + foo1(); +} + +// CHECK: define {{(dso_local )?}}void @_ZSt9terminatev() #[[ATTR4:.*]] { + +namespace std { + void terminate() noexcept { + } +} + +// CHECK: attributes #[[ATTR4]] = {{{.*}}"ptrauth-calls"{{.*}}} diff --git a/clang/test/CodeGenCXX/ptrauth.cpp b/clang/test/CodeGenCXX/ptrauth.cpp index b0c069f43969b..5565c745d8bbb 100644 --- a/clang/test/CodeGenCXX/ptrauth.cpp +++ b/clang/test/CodeGenCXX/ptrauth.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 -fexceptions -fcxx-exceptions -o - %s | FileCheck %s -// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -emit-llvm -std=c++11 -fexceptions -fcxx-exceptions -o - %s | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 -fexceptions -fcxx-exceptions -o - %s | FileCheck %s +// RUN: %clang_cc1 -mllvm -ptrauth-emit-wrapper-globals=0 -triple aarch64-linux-gnu -fptrauth-calls -emit-llvm -std=c++11 -fexceptions -fcxx-exceptions -o - %s | FileCheck %s void f(void); auto &f_ref = f; diff --git a/clang/test/CodeGenObjC/availability-check.m b/clang/test/CodeGenObjC/availability-check.m index eceaf973119fb..a664257d8f4de 100644 --- a/clang/test/CodeGenObjC/availability-check.m +++ b/clang/test/CodeGenObjC/availability-check.m @@ -26,6 +26,15 @@ void use_at_available(void) { // CHECK: br i1 true if (__builtin_available(macos 10.11, *)) ; + + // CHECK: call i32 @__isPlatformVersionAtLeast(i32 1, i32 10, i32 16, i32 0) + // CHECK-NEXT: icmp ne + if (__builtin_available(macos 10.16, *)) + ; + // CHECK: call i32 @__isPlatformVersionAtLeast(i32 1, i32 11, i32 0, i32 0) + // CHECK-NEXT: icmp ne + if (__builtin_available(macos 11.0, *)) + ; } // CHECK: declare i32 @__isPlatformVersionAtLeast(i32, i32, i32, i32) diff --git a/clang/test/CodeGenObjC/feature-availability.m b/clang/test/CodeGenObjC/feature-availability.m new file mode 100644 index 0000000000000..04d6b31197b89 --- /dev/null +++ b/clang/test/CodeGenObjC/feature-availability.m @@ -0,0 +1,179 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx -fblocks -ffeature-availability=feature1:on -ffeature-availability=feature2:off -ffeature-availability=feature3:on -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-macosx -fblocks -emit-llvm -o - -DUSE_DOMAIN %s | FileCheck %s + +#include + +#define AVAIL 0 + +#ifdef USE_DOMAIN +int pred1(void); + +static struct __AvailabilityDomain feature1 __attribute__((availability_domain(feature1))) = {__AVAILABILITY_DOMAIN_ENABLED, 0}; +static struct __AvailabilityDomain feature2 __attribute__((availability_domain(feature2))) = {__AVAILABILITY_DOMAIN_DISABLED, 0}; +static struct __AvailabilityDomain feature3 __attribute__((availability_domain(feature3))) = {__AVAILABILITY_DOMAIN_ENABLED, 0}; +#endif + +// CHECK: @"OBJC_CLASS_$_C0" = global %struct._class_t { ptr @"OBJC_METACLASS_$_C0", ptr null, ptr @_objc_empty_cache, ptr @_objc_empty_vtable, ptr @"_OBJC_CLASS_RO_$_C0" }, section "__DATA, __objc_data", align 8 +// CHECK-NEXT: @"OBJC_METACLASS_$_C0" = global %struct._class_t { ptr @"OBJC_METACLASS_$_C0", ptr @"OBJC_CLASS_$_C0", ptr @_objc_empty_cache, ptr @_objc_empty_vtable, ptr @"_OBJC_METACLASS_RO_$_C0" }, section "__DATA, __objc_data", align 8 +// CHECK-NEXT: @OBJC_CLASS_NAME_ = private unnamed_addr constant [3 x i8] c"C0\00", section "__TEXT,__objc_classname,cstring_literals", align 1 +// CHECK-NEXT: @"_OBJC_METACLASS_RO_$_C0" = internal global %struct._class_ro_t { i32 3, i32 40, i32 40, ptr null, ptr @OBJC_CLASS_NAME_, ptr null, ptr null, ptr null, ptr null, ptr null }, section "__DATA, __objc_const", align 8 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_ = private unnamed_addr constant [3 x i8] c"m0\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_TYPE_ = private unnamed_addr constant [8 x i8] c"v16@0:8\00", section "__TEXT,__objc_methtype,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.1 = private unnamed_addr constant [3 x i8] c"m1\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.2 = private unnamed_addr constant [6 x i8] c"prop0\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_TYPE_.3 = private unnamed_addr constant [8 x i8] c"i16@0:8\00", section "__TEXT,__objc_methtype,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.4 = private unnamed_addr constant [10 x i8] c"setProp0:\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_TYPE_.5 = private unnamed_addr constant [11 x i8] c"v20@0:8i16\00", section "__TEXT,__objc_methtype,cstring_literals", align 1 +// CHECK-NEXT: @"_OBJC_$_INSTANCE_METHODS_C0" = internal global { i32, i32, [4 x %struct._objc_method] } { i32 24, i32 7, [4 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_, ptr @OBJC_METH_VAR_TYPE_, ptr @"\01-[C0 m0]" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME_.1, ptr @OBJC_METH_VAR_TYPE_, ptr @"\01-[C0 m1]" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME_.2, ptr @OBJC_METH_VAR_TYPE_.3, ptr @"\01-[C0 prop0]" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME_.4, ptr @OBJC_METH_VAR_TYPE_.5, ptr @"\01-[C0 setProp0:]" }] }, section "__DATA, __objc_const", align 8 +// CHECK-NEXT: @"OBJC_IVAR_$_C0.ivar0" = constant i32 0, section "__DATA, __objc_ivar", align 4 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.6 = private unnamed_addr constant [6 x i8] c"ivar0\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_TYPE_.7 = private unnamed_addr constant [2 x i8] c"i\00", section "__TEXT,__objc_methtype,cstring_literals", align 1 +// CHECK-NEXT: @"OBJC_IVAR_$_C0._prop0" = hidden constant i32 4, section "__DATA, __objc_ivar", align 4 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.8 = private unnamed_addr constant [7 x i8] c"_prop0\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @"_OBJC_$_INSTANCE_VARIABLES_C0" = internal global { i32, i32, [2 x %struct._ivar_t] } { i32 32, i32 2, [2 x %struct._ivar_t] [%struct._ivar_t { ptr @"OBJC_IVAR_$_C0.ivar0", ptr @OBJC_METH_VAR_NAME_.6, ptr @OBJC_METH_VAR_TYPE_.7, i32 2, i32 4 }, %struct._ivar_t { ptr @"OBJC_IVAR_$_C0._prop0", ptr @OBJC_METH_VAR_NAME_.8, ptr @OBJC_METH_VAR_TYPE_.7, i32 2, i32 4 }] }, section "__DATA, __objc_const", align 8 +// CHECK-NEXT: @OBJC_PROP_NAME_ATTR_ = private unnamed_addr constant [6 x i8] c"prop0\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_PROP_NAME_ATTR_.9 = private unnamed_addr constant [11 x i8] c"Ti,V_prop0\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @"_OBJC_$_PROP_LIST_C0" = internal global { i32, i32, [1 x %struct._prop_t] } { i32 16, i32 1, [1 x %struct._prop_t] [%struct._prop_t { ptr @OBJC_PROP_NAME_ATTR_, ptr @OBJC_PROP_NAME_ATTR_.9 }] }, section "__DATA, __objc_const", align 8 +// CHECK-NEXT: @"_OBJC_CLASS_RO_$_C0" = internal global %struct._class_ro_t { i32 2, i32 0, i32 8, ptr null, ptr @OBJC_CLASS_NAME_, ptr @"_OBJC_$_INSTANCE_METHODS_C0", ptr null, ptr @"_OBJC_$_INSTANCE_VARIABLES_C0", ptr null, ptr @"_OBJC_$_PROP_LIST_C0" }, section "__DATA, __objc_const", align 8 +// CHECK-NEXT: @OBJC_CLASS_NAME_.10 = private unnamed_addr constant [5 x i8] c"Cat0\00", section "__TEXT,__objc_classname,cstring_literals", align 1 +// CHECK-NEXT: @"OBJC_METACLASS_$_NSObject" = external global %struct._class_t +// CHECK-NEXT: @OBJC_CLASS_NAME_.11 = private unnamed_addr constant [3 x i8] c"C2\00", section "__TEXT,__objc_classname,cstring_literals", align 1 +// CHECK-NEXT: @"_OBJC_METACLASS_RO_$_C2" = internal global %struct._class_ro_t { i32 1, i32 40, i32 40, ptr null, ptr @OBJC_CLASS_NAME_.11, ptr null, ptr null, ptr null, ptr null, ptr null }, section "__DATA, __objc_const", align 8 +// CHECK-NEXT: @"OBJC_METACLASS_$_C2" = global %struct._class_t { ptr @"OBJC_METACLASS_$_NSObject", ptr @"OBJC_METACLASS_$_NSObject", ptr @_objc_empty_cache, ptr @_objc_empty_vtable, ptr @"_OBJC_METACLASS_RO_$_C2" }, section "__DATA, __objc_data", align 8 +// CHECK-NEXT: @"OBJC_CLASS_$_NSObject" = external global %struct._class_t +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.12 = private unnamed_addr constant [6 x i8] c"ivar1\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_TYPE_.13 = private unnamed_addr constant [8 x i8] c"@16@0:8\00", section "__TEXT,__objc_methtype,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.14 = private unnamed_addr constant [10 x i8] c"setIvar1:\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_TYPE_.15 = private unnamed_addr constant [11 x i8] c"v24@0:8@16\00", section "__TEXT,__objc_methtype,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.16 = private unnamed_addr constant [6 x i8] c"ivar3\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.17 = private unnamed_addr constant [10 x i8] c"setIvar3:\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.18 = private unnamed_addr constant [6 x i8] c"ivar4\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.19 = private unnamed_addr constant [10 x i8] c"setIvar4:\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @"_OBJC_$_INSTANCE_METHODS_C2" = internal global { i32, i32, [6 x %struct._objc_method] } { i32 24, i32 8, [6 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_.12, ptr @OBJC_METH_VAR_TYPE_.13, ptr @"\01-[C2 ivar1]" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME_.14, ptr @OBJC_METH_VAR_TYPE_.15, ptr @"\01-[C2 setIvar1:]" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME_.16, ptr @OBJC_METH_VAR_TYPE_.13, ptr @"\01-[C2 ivar3]" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME_.17, ptr @OBJC_METH_VAR_TYPE_.15, ptr @"\01-[C2 setIvar3:]" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME_.18, ptr @OBJC_METH_VAR_TYPE_.13, ptr @"\01-[C2 ivar4]" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME_.19, ptr @OBJC_METH_VAR_TYPE_.15, ptr @"\01-[C2 setIvar4:]" }] }, section "__DATA, __objc_const", align 8 +// CHECK-NEXT: @"OBJC_IVAR_$_C2._ivar1" = hidden constant i32 8, section "__DATA, __objc_ivar", align 4 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.20 = private unnamed_addr constant [7 x i8] c"_ivar1\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_METH_VAR_TYPE_.21 = private unnamed_addr constant [2 x i8] c"@\00", section "__TEXT,__objc_methtype,cstring_literals", align 1 +// CHECK-NEXT: @"OBJC_IVAR_$_C2._ivar3" = hidden constant i32 16, section "__DATA, __objc_ivar", align 4 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.22 = private unnamed_addr constant [7 x i8] c"_ivar3\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @"OBJC_IVAR_$_C2._ivar4" = hidden constant i32 24, section "__DATA, __objc_ivar", align 4 +// CHECK-NEXT: @OBJC_METH_VAR_NAME_.23 = private unnamed_addr constant [7 x i8] c"_ivar4\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @"_OBJC_$_INSTANCE_VARIABLES_C2" = internal global { i32, i32, [3 x %struct._ivar_t] } { i32 32, i32 3, [3 x %struct._ivar_t] [%struct._ivar_t { ptr @"OBJC_IVAR_$_C2._ivar1", ptr @OBJC_METH_VAR_NAME_.20, ptr @OBJC_METH_VAR_TYPE_.21, i32 3, i32 8 }, %struct._ivar_t { ptr @"OBJC_IVAR_$_C2._ivar3", ptr @OBJC_METH_VAR_NAME_.22, ptr @OBJC_METH_VAR_TYPE_.21, i32 3, i32 8 }, %struct._ivar_t { ptr @"OBJC_IVAR_$_C2._ivar4", ptr @OBJC_METH_VAR_NAME_.23, ptr @OBJC_METH_VAR_TYPE_.21, i32 3, i32 8 }] }, section "__DATA, __objc_const", align 8 +// CHECK-NEXT: @OBJC_PROP_NAME_ATTR_.24 = private unnamed_addr constant [6 x i8] c"ivar1\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_PROP_NAME_ATTR_.25 = private unnamed_addr constant [11 x i8] c"T@,V_ivar1\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_PROP_NAME_ATTR_.26 = private unnamed_addr constant [6 x i8] c"ivar3\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_PROP_NAME_ATTR_.27 = private unnamed_addr constant [11 x i8] c"T@,V_ivar3\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_PROP_NAME_ATTR_.28 = private unnamed_addr constant [6 x i8] c"ivar4\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @OBJC_PROP_NAME_ATTR_.29 = private unnamed_addr constant [11 x i8] c"T@,V_ivar4\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK-NEXT: @"_OBJC_$_PROP_LIST_C2" = internal global { i32, i32, [3 x %struct._prop_t] } { i32 16, i32 3, [3 x %struct._prop_t] [%struct._prop_t { ptr @OBJC_PROP_NAME_ATTR_.24, ptr @OBJC_PROP_NAME_ATTR_.25 }, %struct._prop_t { ptr @OBJC_PROP_NAME_ATTR_.26, ptr @OBJC_PROP_NAME_ATTR_.27 }, %struct._prop_t { ptr @OBJC_PROP_NAME_ATTR_.28, ptr @OBJC_PROP_NAME_ATTR_.29 }] }, section "__DATA, __objc_const", align 8 +// CHECK-NEXT: @"_OBJC_CLASS_RO_$_C2" = internal global %struct._class_ro_t { i32 0, i32 8, i32 32, ptr null, ptr @OBJC_CLASS_NAME_.11, ptr @"_OBJC_$_INSTANCE_METHODS_C2", ptr null, ptr @"_OBJC_$_INSTANCE_VARIABLES_C2", ptr null, ptr @"_OBJC_$_PROP_LIST_C2" }, section "__DATA, __objc_const", align 8 +// CHECK-NEXT: @"OBJC_CLASS_$_C2" = global %struct._class_t { ptr @"OBJC_METACLASS_$_C2", ptr @"OBJC_CLASS_$_NSObject", ptr @_objc_empty_cache, ptr @_objc_empty_vtable, ptr @"_OBJC_CLASS_RO_$_C2" }, section "__DATA, __objc_data", align 8 +// CHECK: @"OBJC_LABEL_CLASS_$" = private global [3 x ptr] [ptr @"OBJC_CLASS_$_C0", ptr @"OBJC_CLASS_$_C2", ptr @"OBJC_CLASS_$_C3"], section "__DATA,__objc_classlist,regular,no_dead_strip", align 8 + +@interface NSObject { + id a; +} +@end + +__attribute__((availability(domain:feature2, AVAIL))) int unavailable_func1(void); +__attribute__((availability(domain:feature3, AVAIL))) int func3(void); + +__attribute__((availability(domain:feature1, AVAIL))) +@interface C0 { + int ivar0 __attribute__((availability(domain:feature3, AVAIL))); + int unavailable_ivar1 __attribute__((availability(domain:feature2, AVAIL))); +} +@property int prop0 __attribute__((availability(domain:feature3, AVAIL))); +@property int unavailable_prop1 __attribute__((availability(domain:feature2, AVAIL))); +-(void)m0; +-(void)m1 __attribute__((availability(domain:feature3, AVAIL))); +-(void)unavailable_m2 __attribute__((availability(domain:feature2, AVAIL))); +@end + +// CHECK: define internal void @"\01-[C0 m0]"( +// CHECK: define internal void @"\01-[C0 m1]"( +// CHECK: call i32 @func3() +// CHECK-NOT: [C0 m2] + +@implementation C0 +-(void)m0 { +} +-(void)m1 { + func3(); +} +-(void)unavailable_m2 { + unavailable_func1(); +} +@end + +@interface C0(Cat0) +@end + +@implementation C0(Cat0) +@end + +__attribute__((availability(domain:feature2, AVAIL))) +@interface unavailable_C1 +-(void)unavailable_m1; +@end + +// CHECK-NOT: [unavailable_C1 m1] +@implementation unavailable_C1 +-(void)unavailable_m1 { +} +@end + +@interface unavailable_C1(Cat1) +@end + +@implementation unavailable_C1(Cat1) +@end + +@interface C2 : NSObject +@property id ivar1 __attribute__((availability(domain:feature1, AVAIL))); +@property id unavailable_ivar2 __attribute__((availability(domain:feature2, AVAIL))); +@property id ivar3 __attribute__((availability(domain:feature3, AVAIL))); +@property id ivar4; +@end + +// CHECK: define internal ptr @"\01-[C2 ivar1]"(ptr noundef %[[SELF:.*]], ptr noundef %{{.*}}) +// CHECK: %[[RETVAL:.*]] = alloca ptr, align 8 +// CHECK: %[[SELF_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[SELF:.*]], ptr %[[SELF_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[SELF_ADDR]], align 8 +// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 8 +// CHECK: %[[LOAD:.*]] = load atomic i64, ptr %[[ADD_PTR]] unordered, align 8 +// CHECK: store i64 %[[LOAD]], ptr %[[RETVAL]], align 8 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[RETVAL]], align 8 +// CHECK: ret ptr %[[V1]] + +// CHECK: define internal ptr @"\01-[C2 ivar3]"(ptr noundef %[[SELF:.*]], ptr noundef %{{.*}}) +// CHECK: %[[RETVAL:.*]] = alloca ptr, align 8 +// CHECK: %[[SELF_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[SELF:.*]], ptr %[[SELF_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[SELF_ADDR]], align 8 +// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 16 +// CHECK: %[[LOAD:.*]] = load atomic i64, ptr %[[ADD_PTR]] unordered, align 8 +// CHECK: store i64 %[[LOAD]], ptr %[[RETVAL]], align 8 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[RETVAL]], align 8 +// CHECK: ret ptr %[[V1]] + +// CHECK: define internal ptr @"\01-[C2 ivar4]"(ptr noundef %[[SELF:.*]], ptr noundef %{{.*}}) +// CHECK: %[[RETVAL:.*]] = alloca ptr, align 8 +// CHECK: %[[SELF_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[SELF:.*]], ptr %[[SELF_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[SELF_ADDR]], align 8 +// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 24 +// CHECK: %[[LOAD:.*]] = load atomic i64, ptr %[[ADD_PTR]] unordered, align 8 +// CHECK: store i64 %[[LOAD]], ptr %[[RETVAL]], align 8 +// CHECK: %[[V1:.*]] = load ptr, ptr %[[RETVAL]], align 8 +// CHECK: ret ptr %[[V1]] + +@implementation C2 +@end + +@interface C3 : NSObject +@property id prop0 __attribute__((availability(domain:feature2, AVAIL))); +@end + +@implementation C3 +@end diff --git a/clang/test/CodeGenObjC/forward-declare-protocol-gnu.m b/clang/test/CodeGenObjC/forward-declare-protocol-gnu.m index 3731fb078eea9..b57a4a48d4a00 100644 --- a/clang/test/CodeGenObjC/forward-declare-protocol-gnu.m +++ b/clang/test/CodeGenObjC/forward-declare-protocol-gnu.m @@ -3,11 +3,9 @@ // Regression test: check that we don't crash when referencing a forward-declared protocol. @protocol P; -@interface I

-@end - -@implementation I - -@end +Protocol *getProtocol(void) +{ + return @protocol(P); +} // CHECK: @.objc_protocol diff --git a/clang/test/CodeGenObjC/forward-protocol-metadata-symbols.m b/clang/test/CodeGenObjC/forward-protocol-metadata-symbols.m index 4c326dd0b4f73..699db4af38ce1 100644 --- a/clang/test/CodeGenObjC/forward-protocol-metadata-symbols.m +++ b/clang/test/CodeGenObjC/forward-protocol-metadata-symbols.m @@ -2,7 +2,7 @@ @interface NSObject @end -@protocol P0 @end +@protocol P0; @interface A : NSObject +(Class) getClass; @@ -18,8 +18,8 @@ int main(void) { } // CHECK: @"_OBJC_PROTOCOL_$_P0" = weak hidden global -// CHECK: @"_OBJC_LABEL_PROTOCOL_$_P0" = weak hidden global // CHECK: @"_OBJC_CLASS_PROTOCOLS_$_A" = internal global +// CHECK: @"_OBJC_LABEL_PROTOCOL_$_P0" = weak hidden global // CHECK: @"_OBJC_PROTOCOL_REFERENCE_$_P0" = weak hidden global // CHECK: llvm.used = appending global [3 x ptr] @@ -32,7 +32,7 @@ int main(void) { // CHECK-SAME: OBJC_METH_VAR_NAME_ // CHECK-SAME: OBJC_METH_VAR_TYPE_ // CHECK-SAME: "_OBJC_$_CLASS_METHODS_A" -// CHECK-SAME: OBJC_CLASS_NAME_.1 // CHECK-SAME: "_OBJC_CLASS_PROTOCOLS_$_A" +// CHECK-SAME: OBJC_CLASS_NAME_.1 // CHECK-SAME: "OBJC_LABEL_CLASS_$" // CHECK-SAME: section "llvm.metadata" diff --git a/clang/test/CodeGenObjC/hidden-visibility.m b/clang/test/CodeGenObjC/hidden-visibility.m index 0c0dad0064806..f5ea52fcffe06 100644 --- a/clang/test/CodeGenObjC/hidden-visibility.m +++ b/clang/test/CodeGenObjC/hidden-visibility.m @@ -16,7 +16,7 @@ @implementation I @end -@protocol Prot0 @end +@protocol Prot0; id f0(void) { return @protocol(Prot0); diff --git a/clang/test/CodeGenObjC/link-errors.m b/clang/test/CodeGenObjC/link-errors.m index 08225627835f2..92ba5fcc5f85b 100644 --- a/clang/test/CodeGenObjC/link-errors.m +++ b/clang/test/CodeGenObjC/link-errors.m @@ -10,7 +10,7 @@ -(id) alloc; -(id) init; @end -@protocol P @end +@protocol P; @interface A : Root @end diff --git a/clang/test/CodeGenObjC/nontrivial-c-struct-property.m b/clang/test/CodeGenObjC/nontrivial-c-struct-property.m index 4ddb16c6e3381..31cd09ef8d28d 100644 --- a/clang/test/CodeGenObjC/nontrivial-c-struct-property.m +++ b/clang/test/CodeGenObjC/nontrivial-c-struct-property.m @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=CHECK-DISABLE-PTRAUTH %s +// RUN: %clang_cc1 -triple arm64-apple-ios11 -fobjc-arc -fptrauth-calls -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=CHECK-ENABLE-PTRAUTH %s + @interface SuperClass @end @@ -27,6 +29,9 @@ -(void)setP1:(S0)s0 { // CHECK: %[[STRUCT_S0:.*]] = type { ptr } +// CHECK-ENABLE-PTRAUTH: @__copy_constructor_8_8_s0.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @__copy_constructor_8_8_s0, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", +// CHECK-ENABLE-PTRAUTH: @__move_assignment_8_8_s0.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @__move_assignment_8_8_s0, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", + // Check that parameters of user-defined setters are destructed. // CHECK-LABEL: define internal void @"\01-[C setP1:]"( @@ -62,12 +67,14 @@ -(void)setP1:(S0)s0 { // CHECK: ret void // CHECK-LABEL: define internal i64 @"\01-[C atomic0]"( -// CHECK: call void @objc_copyCppObjectAtomic({{.*}}, {{.*}}, ptr noundef @__copy_constructor_8_8_s0) +// CHECK-DISABLE-PTRAUTH: call void @objc_copyCppObjectAtomic({{.*}}, {{.*}}, ptr noundef @__copy_constructor_8_8_s0) +// CHECK-ENABLE-PTRAUTH: call void @objc_copyCppObjectAtomic({{.*}}, {{.*}}, ptr noundef @__copy_constructor_8_8_s0.ptrauth) // CHECK-NOT: call // CHECK: ret i64 // CHECK-LABEL: define internal void @"\01-[C setAtomic0:]"( -// CHECK: call void @objc_copyCppObjectAtomic({{.*}}, {{.*}}, ptr noundef @__move_assignment_8_8_s0) +// CHECK-DISABLE-PTRAUTH: call void @objc_copyCppObjectAtomic({{.*}}, {{.*}}, ptr noundef @__move_assignment_8_8_s0) +// CHECK-ENABLE-PTRAUTH: call void @objc_copyCppObjectAtomic({{.*}}, {{.*}}, ptr noundef @__move_assignment_8_8_s0.ptrauth) // CHECK-NOT: call // CHECK: ret void diff --git a/clang/test/CodeGenObjC/protocol-comdat.m b/clang/test/CodeGenObjC/protocol-comdat.m index 79a1d5535576b..401a73bd42410 100644 --- a/clang/test/CodeGenObjC/protocol-comdat.m +++ b/clang/test/CodeGenObjC/protocol-comdat.m @@ -4,8 +4,8 @@ @protocol P - (void) method; @end -@protocol Q @end -@protocol R @end +@protocol Q; +@protocol R; @interface I

@end diff --git a/clang/test/CodeGenObjC/protocols-lazy.m b/clang/test/CodeGenObjC/protocols-lazy.m index 5e5e78c4be75e..1c66e2eb3ea9e 100644 --- a/clang/test/CodeGenObjC/protocols-lazy.m +++ b/clang/test/CodeGenObjC/protocols-lazy.m @@ -18,10 +18,7 @@ @protocol P2 -im1; @end // RUN: grep OBJC_PROTOCOL_P3 %t | count 3 // RUN: not grep OBJC_PROTOCOL_INSTANCE_METHODS_P3 %t @protocol P3; -@interface UserP3 -@end -@implementation UserP3 -@end +void f1() { id x = @protocol(P3); } // Definition triggered by class reference. // RUN: grep OBJC_PROTOCOL_P4 %t | count 3 @@ -34,16 +31,10 @@ @implementation I0 -im1 { return 0; }; @end // RUN: grep OBJC_PROTOCOL_P5 %t | count 3 // RUN: grep OBJC_PROTOCOL_INSTANCE_METHODS_P5 %t | count 3 @protocol P5; -@interface UserP5 // This generates a forward - // reference, which has to be - // updated on the next line. -@end -@protocol P5 -im1; @end -@implementation UserP5 - -- im1 { __builtin_unreachable(); } - -@end +void f2() { id x = @protocol(P5); } // This generates a forward + // reference, which has to be + // updated on the next line. +@protocol P5 -im1; @end // Protocol reference following definition. // RUN: grep OBJC_PROTOCOL_P6 %t | count 4 diff --git a/clang/test/CodeGenObjC/protocols.m b/clang/test/CodeGenObjC/protocols.m index 8da804d2eb894..661cee4b979f0 100644 --- a/clang/test/CodeGenObjC/protocols.m +++ b/clang/test/CodeGenObjC/protocols.m @@ -22,8 +22,7 @@ +(int) maxValue; -(int) conformsTo: (id) x; @end -@protocol P0 -@end +@protocol P0; @protocol P1 +(void) classMethodReq0; diff --git a/clang/test/CodeGenObjC/ptrauth-attr-exception.m b/clang/test/CodeGenObjC/ptrauth-attr-exception.m new file mode 100644 index 0000000000000..6932600a4d3fc --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-attr-exception.m @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -fexceptions -fobjc-exceptions -o - %s | FileCheck %s + +__attribute__((objc_root_class)) +@interface Root { + Class isa; +} +@end + +__attribute__((objc_exception)) +@interface A : Root +@end + +@implementation A +@end + +// CHECK: @objc_ehtype_vtable.ptrauth = private constant { ptr, i32, i64, i64 } { ptr getelementptr inbounds (ptr, ptr @objc_ehtype_vtable, i32 2), i32 2, i64 0, i64 0 }, section "llvm.ptrauth", align 8 +// CHECK: @"OBJC_EHTYPE_$_A" = global {{%.*}} { ptr @objc_ehtype_vtable.ptrauth, diff --git a/clang/test/CodeGenObjC/ptrauth-blocks.m b/clang/test/CodeGenObjC/ptrauth-blocks.m new file mode 100644 index 0000000000000..115a64e400d9c --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-blocks.m @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -fptrauth-calls -fobjc-arc -fblocks -fobjc-runtime=ios-7 -triple arm64-apple-ios -emit-llvm %s -o - | FileCheck %s + +void (^blockptr)(void); + +// CHECK: [[INVOCATION_1:@.*]] = private constant { ptr, i32, i64, i64 } { ptr {{@.*}}, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ ptr, i32, i32, ptr, ptr }, ptr [[GLOBAL_BLOCK_1:@.*]], i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: [[GLOBAL_BLOCK_1]] = internal constant { ptr, i32, i32, ptr, ptr } { ptr @_NSConcreteGlobalBlock, i32 1342177280, i32 0, ptr [[INVOCATION_1]], +void (^globalblock)(void) = ^{}; + +// CHECK: [[COPYDISPOSE_COPY:@.*]] = private constant { ptr, i32, i64, i64 } { ptr {{@.*}}, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i64, i64, ptr, ptr, ptr, i64 }, ptr [[COPYDISPOSE_DESCRIPTOR:@.*]], i32 0, i32 2) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: [[COPYDISPOSE_DISPOSE:@.*]] = private constant { ptr, i32, i64, i64 } { ptr {{@.*}}, i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i64, i64, ptr, ptr, ptr, i64 }, ptr [[COPYDISPOSE_DESCRIPTOR]], i32 0, i32 3) to i64), i64 0 }, section "llvm.ptrauth" +// CHECK: [[COPYDISPOSE_DESCRIPTOR:@.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, ptr, ptr, ptr, i64 } { i64 0, i64 40, ptr [[COPYDISPOSE_COPY]], ptr [[COPYDISPOSE_DISPOSE]], + +@interface A +- (int) count; +@end + +// CHECK-LABEL: define void @test_block_call() +void test_block_call() { + // CHECK: [[BLOCK:%.*]] = load ptr, ptr @blockptr, + // CHECK-NEXT: [[FNADDR:%.*]] = getelementptr inbounds {{.*}}, ptr [[BLOCK]], i32 0, i32 3 + // CHECK-NEXT: [[T0:%.*]] = load ptr, ptr [[FNADDR]], + // CHECK-NEXT: [[DISC:%.*]] = ptrtoint ptr [[FNADDR]] to i64 + // CHECK-NEXT: call void [[T0]](ptr noundef [[BLOCK]]) [ "ptrauth"(i32 0, i64 [[DISC]]) ] + blockptr(); +} + +void use_block(int (^)(void)); + +// CHECK-LABEL: define void @test_block_literal( +void test_block_literal(int i) { + // CHECK: [[I:%.*]] = alloca i32, + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:.*]], align + // CHECK: [[FNPTRADDR:%.*]] = getelementptr inbounds nuw [[BLOCK_T]], ptr [[BLOCK]], i32 0, i32 3 + // CHECK-NEXT: [[DISCRIMINATOR:%.*]] = ptrtoint ptr [[FNPTRADDR]] to i64 + // CHECK-NEXT: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr {{@.*}} to i64), i32 0, i64 [[DISCRIMINATOR]]) + // CHECK-NEXT: [[T0:%.*]] = inttoptr i64 [[SIGNED]] to ptr + // CHECK-NEXT: store ptr [[T0]], ptr [[FNPTRADDR]] + use_block(^{return i;}); +} + +// CHECK-LABEL: define void @test_copy_destroy +void test_copy_destroy(A *a) { + // CHECK: [[COPYDISPOSE_DESCRIPTOR]] + use_block(^{return [a count];}); +} + +// CHECK-LABEL: define void @test_byref_copy_destroy +void test_byref_copy_destroy(A *a) { + // CHECK: [[COPY_FIELD:%.*]] = getelementptr inbounds nuw [[BYREF_T:%.*]], ptr [[BYREF:%.*]], i32 0, i32 4 + // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[COPY_FIELD]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr {{@.*}} to i64), i32 0, i64 [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr + // CHECK-NEXT: store ptr [[T2]], ptr [[COPY_FIELD]], align 8 + // CHECK: [[DISPOSE_FIELD:%.*]] = getelementptr inbounds nuw [[BYREF_T]], ptr [[BYREF]], i32 0, i32 5 + // CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr [[DISPOSE_FIELD]] to i64 + // CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr {{@.*}} to i64), i32 0, i64 [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr + // CHECK-NEXT: store ptr [[T2]], ptr [[DISPOSE_FIELD]], align 8 + __block A *aweak = a; + use_block(^{return [aweak count];}); +} diff --git a/clang/test/CodeGenObjC/ptrauth-method-list.m b/clang/test/CodeGenObjC/ptrauth-method-list.m new file mode 100644 index 0000000000000..8cde7f26145fe --- /dev/null +++ b/clang/test/CodeGenObjC/ptrauth-method-list.m @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fptrauth-calls -fobjc-arc -fblocks -fobjc-runtime=ios-7 -triple arm64-apple-ios -emit-llvm -o - %s | FileCheck %s + +// CHECK: @"\01+[C pm1].ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"\01+[C pm1]", i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_CLASS_METHODS_C", i32 0, i32 2, i32 0, i32 2) to i64), i64 0 }, section "llvm.ptrauth", +// CHECK: @"\01+[C m1].ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"\01+[C m1]", i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_CLASS_METHODS_C", i32 0, i32 2, i32 1, i32 2) to i64), i64 0 }, section "llvm.ptrauth", +// CHECK: @"_OBJC_$_CLASS_METHODS_C" = internal global { i32, i32, [2 x %struct._objc_method] } { i32 24, i32 2, [2 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_, ptr @OBJC_METH_VAR_TYPE_, ptr @"\01+[C pm1].ptrauth" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME{{.*}}, ptr @OBJC_METH_VAR_TYPE_, ptr @"\01+[C m1].ptrauth" }] }, section "__DATA, __objc_const", +// CHECK: @"\01-[C pm0].ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"\01-[C pm0]", i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_INSTANCE_METHODS_C", i32 0, i32 2, i32 0, i32 2) to i64), i64 0 }, section "llvm.ptrauth", +// CHECK: @"\01-[C m0].ptrauth" = private constant { ptr, i32, i64, i64 } { ptr @"\01-[C m0]", i32 0, i64 ptrtoint (ptr getelementptr inbounds ({ i32, i32, [2 x %struct._objc_method] }, ptr @"_OBJC_$_INSTANCE_METHODS_C", i32 0, i32 2, i32 1, i32 2) to i64), i64 0 }, section "llvm.ptrauth", +// CHECK: @"_OBJC_$_INSTANCE_METHODS_C" = internal global { i32, i32, [2 x %struct._objc_method] } { i32 24, i32 2, [2 x %struct._objc_method] [%struct._objc_method { ptr @OBJC_METH_VAR_NAME_.3, ptr @OBJC_METH_VAR_TYPE_, ptr @"\01-[C pm0].ptrauth" }, %struct._objc_method { ptr @OBJC_METH_VAR_NAME{{.*}} ptr @OBJC_METH_VAR_TYPE_, ptr @"\01-[C m0].ptrauth" }] }, section "__DATA, __objc_const", + +@protocol P +- (void) pm0; ++ (void) pm1; +@end + +@interface C

+- (void) m0; ++ (void) m1; +@end + +@implementation C +- (void) pm0 {} ++ (void) pm1 {} +- (void) m0 {} ++ (void) m1 {} +@end + +void test_method_list(C *c) { + [c m0]; + [C m1]; +} diff --git a/clang/test/CodeGenObjC/synchronized.m b/clang/test/CodeGenObjC/synchronized.m index 25feb958c73ab..2627e8a76e693 100644 --- a/clang/test/CodeGenObjC/synchronized.m +++ b/clang/test/CodeGenObjC/synchronized.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -emit-llvm -triple i686-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -o - %s -O2 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -triple i686-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -o - %s -O2 -fno-split-cold-code | FileCheck %s @interface MyClass { diff --git a/clang/test/CodeGenObjCXX/exceptions-legacy.mm b/clang/test/CodeGenObjCXX/exceptions-legacy.mm index b6361f234d381..f70a02684edac 100644 --- a/clang/test/CodeGenObjCXX/exceptions-legacy.mm +++ b/clang/test/CodeGenObjCXX/exceptions-legacy.mm @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fexceptions -fobjc-exceptions -O2 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple i386-apple-darwin10 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm -fexceptions -fobjc-exceptions -O2 -fno-split-cold-code -o - %s | FileCheck %s // Test we maintain at least a basic amount of interoperation between // ObjC and C++ exceptions in the legacy runtime. diff --git a/clang/test/CodeGenObjCXX/ptrauth-property-object-reference.mm b/clang/test/CodeGenObjCXX/ptrauth-property-object-reference.mm new file mode 100644 index 0000000000000..4c82c1ad55643 --- /dev/null +++ b/clang/test/CodeGenObjCXX/ptrauth-property-object-reference.mm @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 %s -triple arm64-apple-ios11.0 -fobjc-runtime=ios-11.0 -fptrauth-calls -emit-llvm -o - | FileCheck %s + +extern int DEFAULT(); + +struct TCPPObject +{ + TCPPObject(); + ~TCPPObject(); + TCPPObject(const TCPPObject& inObj, int i = DEFAULT()); + TCPPObject& operator=(const TCPPObject& inObj); + int filler[64]; +}; + + +@interface MyDocument +{ +@private + TCPPObject _cppObject; + TCPPObject _cppObject1; +} +@property (assign, readwrite, atomic) const TCPPObject MyProperty; +@property (assign, readwrite, atomic) const TCPPObject MyProperty1; +@end + +@implementation MyDocument + @synthesize MyProperty = _cppObject; + @synthesize MyProperty1 = _cppObject1; +@end + +// CHECK-LABEL: @__copy_helper_atomic_property_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @__copy_helper_atomic_property_, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 + +// CHECK-LABEL: @__assign_helper_atomic_property_.ptrauth = private constant { ptr, i32, i64, i64 } { ptr @__assign_helper_atomic_property_, i32 0, i64 0, i64 0 }, section "llvm.ptrauth", align 8 + +// CHECK-LABEL: define internal void @__copy_helper_atomic_property_(ptr noundef %0, ptr noundef %1) # +// CHECK: [[TWO:%.*]] = load ptr, ptr [[ADDR:%.*]], align 8 +// CHECK: [[THREE:%.*]] = load ptr, ptr [[ADDR1:%.*]], align 8 +// CHECK: [[CALL:%.*]] = call noundef i32 @_Z7DEFAULTv() +// CHECK: call noundef ptr @_ZN10TCPPObjectC1ERKS_i(ptr noundef nonnull align {{[0-9]+}} dereferenceable(256) [[TWO]], ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[THREE]], i32 noundef [[CALL]]) +// CHECK: ret void + +// CHECK: define internal void @"\01-[MyDocument MyProperty]"(ptr dead_on_unwind noalias writable sret(%{{.*}} align 4 %[[AGG_RESULT:.*]], ptr noundef %[[SELF:.*]], +// CHECK: %[[RESULT_PTR:.*]] = alloca ptr, align 8 +// CHECK: %[[SELF_ADDR:.*]] = alloca ptr, align 8 +// CHECK: store ptr %[[AGG_RESULT]], ptr %[[RESULT_PTR]], align 8 +// CHECK: store ptr %[[SELF]], ptr %[[SELF_ADDR]], align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[SELF_ADDR]], align 8 +// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 0 +// CHECK: call void @objc_copyCppObjectAtomic(ptr noundef %[[AGG_RESULT]], ptr noundef %[[ADD_PTR]], ptr noundef @__copy_helper_atomic_property_.ptrauth) + +// CHECK-LABEL: define internal void @__assign_helper_atomic_property_(ptr noundef %0, ptr noundef %1) # +// CHECK: [[THREE:%.*]] = load ptr, ptr [[ADDR1:%.*]], align 8 +// CHECK: [[TWO:%.*]] = load ptr, ptr [[ADDR:%.*]], align 8 +// CHECK: [[CALL:%.*]] = call noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) ptr @_ZN10TCPPObjectaSERKS_(ptr noundef nonnull align {{[0-9]+}} dereferenceable(256) [[TWO]], ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[THREE]]) +// CHECK: ret void + +// CHECK: define internal void @"\01-[MyDocument setMyProperty:]"(ptr noundef %[[SELF:.*]], ptr noundef %{{.*}}, ptr noundef %[[MYPROPERTY:.*]]) +// CHECK: %[[SELF_ADDR:.*]] = alloca ptr, align 8 +// CHECK: %[[MYPROPERTY_INDIRECT:.*]]_addr = alloca ptr, align 8 +// CHECK: store ptr %[[SELF]], ptr %[[SELF_ADDR]], align 8 +// CHECK: store ptr %[[MYPROPERTY]], ptr %[[MYPROPERTY_INDIRECT]]_addr, align 8 +// CHECK: %[[V0:.*]] = load ptr, ptr %[[SELF_ADDR]], align 8 +// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 0 +// CHECK: call void @objc_copyCppObjectAtomic(ptr noundef %[[ADD_PTR]], ptr noundef %[[MYPROPERTY]], ptr noundef @__assign_helper_atomic_property_.ptrauth) +// CHECK: ret void diff --git a/clang/test/Driver/Inputs/MacOSX10.14.versioned.sdk/SDKSettings.json b/clang/test/Driver/Inputs/MacOSX10.14.versioned.sdk/SDKSettings.json new file mode 100644 index 0000000000000..90b25e1f1d674 --- /dev/null +++ b/clang/test/Driver/Inputs/MacOSX10.14.versioned.sdk/SDKSettings.json @@ -0,0 +1,25 @@ +{ + "Version":"10.14", + "VersionMap" : { + "macOS_iOSMac" : { + "10.14.4" : "12.4", + "10.14.3" : "12.3", + "10.14.2" : "12.2", + "10.14.1" : "12.1", + "10.15" : "13.0", + "10.14" : "12.0", + "10.14.5" : "12.5", + "10.15.1" : "13.2" + }, + "iOSMac_macOS" : { + "13.0" : "10.15", + "12.3" : "10.14.3", + "12.0" : "10.14", + "12.4" : "10.14.4", + "12.1" : "10.14.1", + "12.5" : "10.14.5", + "12.2" : "10.14.2", + "13.2" : "10.15.1" + } + } +} diff --git a/clang/test/Driver/apple-clang-no-lsan.c b/clang/test/Driver/apple-clang-no-lsan.c new file mode 100644 index 0000000000000..54787e3ddba2c --- /dev/null +++ b/clang/test/Driver/apple-clang-no-lsan.c @@ -0,0 +1,7 @@ +// Apple-Clang: Don't support LSan +// REQUIRES: system-darwin +// RUN: not %clang -fsanitize=leak %s -o %t 2>&1 | FileCheck %s +// CHECK: unsupported option '-fsanitize=leak' +int main() { + return 0; +} diff --git a/clang/test/Driver/apple-silicon-arch.c b/clang/test/Driver/apple-silicon-arch.c index b1201fa2d7ddb..2fa74434abe82 100644 --- a/clang/test/Driver/apple-silicon-arch.c +++ b/clang/test/Driver/apple-silicon-arch.c @@ -1,6 +1,9 @@ // RUN: env SDKROOT="/" %clang -arch arm64 -c -### %s 2>&1 | \ // RUN: FileCheck %s +// RUN: env SDKROOT="/" %clang -arch arm64e -c -### %s 2>&1 | \ +// RUN: FileCheck --check-prefix=ARM64E %s // // REQUIRES: apple-silicon-mac // // CHECK: "-triple" "arm64-apple-macosx{{[0-9.]+}}" +// ARM64E: "-triple" "arm64e-apple-macosx{{[0-9.]+}}" diff --git a/clang/test/Driver/arc-exceptions.m b/clang/test/Driver/arc-exceptions.m index c1dd02d59988c..4501ccd073823 100644 --- a/clang/test/Driver/arc-exceptions.m +++ b/clang/test/Driver/arc-exceptions.m @@ -1,5 +1,5 @@ -// RUN: %clang -### -x objective-c --target=x86_64-apple-macos10.6 -fobjc-arc -fsyntax-only %s 2> %t.log +// RUN: %clang -### -x objective-c -arch x86_64 -fobjc-arc -fsyntax-only %s 2> %t.log // RUN: grep objective-c %t.log // RUN: not grep "fobjc-arc-exceptions" %t.log -// RUN: %clang -### -x objective-c++ --target=x86_64-apple-macos10.6 -fobjc-arc -fsyntax-only %s 2> %t.log +// RUN: %clang -### -x objective-c++ -arch x86_64 -fobjc-arc -fsyntax-only %s 2> %t.log // RUN: grep "fobjc-arc-exceptions" %t.log diff --git a/clang/test/Driver/arch-arm64e-abi-versioning.c b/clang/test/Driver/arch-arm64e-abi-versioning.c new file mode 100644 index 0000000000000..f3b8604a508ce --- /dev/null +++ b/clang/test/Driver/arch-arm64e-abi-versioning.c @@ -0,0 +1,20 @@ +// Check the ABI version support. + +// RUN: %clang -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix NOKERNELABIVERSION +// RUN: %clang -mkernel -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix KERNELABIVERSION +// RUN: %clang -fapple-kext -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION-DEFAULT --check-prefix KERNELABIVERSION +// +// RUN: %clang -fptrauth-abi-version=5 -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix NOKERNELABIVERSION +// RUN: %clang -fptrauth-abi-version=5 -mkernel -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix KERNELABIVERSION +// RUN: %clang -fptrauth-abi-version=5 -fapple-kext -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix KERNELABIVERSION +// RUN: %clang -fptrauth-abi-version=5 -fptrauth-kernel-abi-version -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix KERNELABIVERSION + +// RUN: %clang -fno-ptrauth-abi-version -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix NOABIVERSION --check-prefix NOKERNELABIVERSION +// RUN: %clang -fptrauth-abi-version=5 -fno-ptrauth-abi-version -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix NOABIVERSION --check-prefix NOKERNELABIVERSION +// RUN: %clang -fno-ptrauth-abi-version -fptrauth-abi-version=5 -target arm64e-apple-darwin -c %s -### 2>&1 | FileCheck %s --check-prefix ABIVERSION --check-prefix NOKERNELABIVERSION + +// ABIVERSION: "-fptrauth-abi-version=5" +// ABIVERSION-DEFAULT: "-fptrauth-abi-version=0" +// NOABIVERSION-NOT: fptrauth-abi-version +// KERNELABIVERSION: "-fptrauth-kernel-abi-version" +// NOKERNELABIVERSION-NOT: fptrauth-kernel-abi-version diff --git a/clang/test/Driver/arch-arm64e.c b/clang/test/Driver/arch-arm64e.c index 0fb12d4dcc5e3..36154f909df80 100644 --- a/clang/test/Driver/arch-arm64e.c +++ b/clang/test/Driver/arch-arm64e.c @@ -7,6 +7,7 @@ // NONE-NOT: "-fptrauth-returns" // NONE-NOT: "-fptrauth-indirect-gotos" // NONE-NOT: "-fptrauth-auth-traps" +// NONE-NOT: "-fptrauth-soft" // RUN: %clang -target arm64-apple-darwin -fptrauth-calls -c %s -### 2>&1 | FileCheck %s --check-prefix CALL // CALL: "-cc1"{{.*}} {{.*}} "-fptrauth-calls" @@ -23,6 +24,9 @@ // RUN: %clang -target arm64-apple-darwin -fptrauth-auth-traps -c %s -### 2>&1 | FileCheck %s --check-prefix TRAPS // TRAPS: "-cc1"{{.*}} {{.*}} "-fptrauth-auth-traps" +// RUN: %clang -target arm64-apple-darwin -fptrauth-soft -c %s -### 2>&1 | FileCheck %s --check-prefix SOFT +// SOFT: "-cc1"{{.*}} {{.*}} "-fptrauth-soft" + // Check the arm64e defaults. diff --git a/clang/test/Driver/arm-arch-darwin.c b/clang/test/Driver/arm-arch-darwin.c index f6d3f88a3f8d1..55089619d1e71 100644 --- a/clang/test/Driver/arm-arch-darwin.c +++ b/clang/test/Driver/arm-arch-darwin.c @@ -1,10 +1,6 @@ // On Darwin, arch should override CPU for triple purposes // RUN: %clang -target armv7m-apple-darwin -arch armv7m -mcpu=cortex-m4 -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-V7M-DARWIN %s // CHECK-V7M-DARWIN: "-cc1"{{.*}} "-triple" "thumbv7m-{{.*}} "-target-cpu" "cortex-m4" +// RUN: %clang -target armv7m -arch armv7m -mcpu=cortex-m4 -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-V7M-OVERRIDDEN %s +// CHECK-V7M-OVERRIDDEN: "-cc1"{{.*}} "-triple" "thumbv7em-{{.*}} "-target-cpu" "cortex-m4" -/// -arch is unsupported for non-Darwin targets. -// RUN: not %clang --target=armv7m -arch armv7m -mcpu=cortex-m4 -### -c %s 2>&1 | FileCheck -check-prefix=ERR %s -// ERR: unsupported option '-arch' for target 'armv7m' - -// RUN: not %clang --target=aarch64-linux-gnu -arch arm64 -### -c %s 2>&1 | FileCheck -check-prefix=ERR2 %s -// ERR2: unsupported option '-arch' for target 'aarch64-linux-gnu' diff --git a/clang/test/Driver/darwin-ld.c b/clang/test/Driver/darwin-ld.c index f0ca411430cc7..da34741b8501d 100644 --- a/clang/test/Driver/darwin-ld.c +++ b/clang/test/Driver/darwin-ld.c @@ -169,6 +169,12 @@ // LINK_IOSSIM_PROFILE: libclang_rt.profile_iossim.a // LINK_IOSSIM_PROFILE: libclang_rt.iossim.a +// RUN: not %clang -target x86_64-apple-ios13-macabi -mlinker-version=400 -fprofile-instr-generate -### %t.o 2> %t.log +// RUN: FileCheck -check-prefix=LINK_MACABI_PROFILE %s < %t.log +// LINK_MACABI_PROFILE: {{ld(.exe)?"}} +// LINK_MACABI_PROFILE: libclang_rt.profile_osx.a + + // RUN: %clang -target arm64-apple-tvos8.3 -fuse-ld= -mlinker-version=400 -mtvos-version-min=8.3 -resource-dir=%S/Inputs/resource_dir -### %t.o 2> %t.log // RUN: FileCheck -check-prefix=LINK_TVOS_ARM64 %s < %t.log // LINK_TVOS_ARM64: {{ld(.exe)?"}} diff --git a/clang/test/Driver/fdefine-target-os-macros.c b/clang/test/Driver/fdefine-target-os-macros.c index a4de51e8e7244..d448e3a839f9b 100644 --- a/clang/test/Driver/fdefine-target-os-macros.c +++ b/clang/test/Driver/fdefine-target-os-macros.c @@ -5,7 +5,8 @@ // RUN: %clang -### --target=x86_64-pc-win32 %s 2>&1 | FileCheck %s --check-prefix=NON-DARWIN-DEFAULT // NON-DARWIN-DEFAULT-NOT: "-fdefine-target-os-macros" -// RUN: %clang -dM -E --target=arm64-apple-macos %s 2>&1 \ +// RUN: %clang -dM -E --target=arm64-apple-macos \ +// RUN: -fdefine-target-os-macros %s 2>&1 \ // RUN: | FileCheck %s -DMAC=1 \ // RUN: -DOSX=1 \ // RUN: -DIPHONE=0 \ @@ -21,7 +22,8 @@ // RUN: -DLINUX=0 \ // RUN: -DUNIX=0 -// RUN: %clang -dM -E --target=arm64-apple-ios %s 2>&1 \ +// RUN: %clang -dM -E --target=arm64-apple-ios \ +// RUN: -fdefine-target-os-macros %s 2>&1 \ // RUN: | FileCheck %s -DMAC=1 \ // RUN: -DOSX=0 \ // RUN: -DIPHONE=1 \ @@ -37,7 +39,8 @@ // RUN: -DLINUX=0 \ // RUN: -DUNIX=0 -// RUN: %clang -dM -E --target=arm64-apple-ios-macabi %s 2>&1 \ +// RUN: %clang -dM -E --target=arm64-apple-ios-macabi \ +// RUN: -fdefine-target-os-macros %s 2>&1 \ // RUN: | FileCheck %s -DMAC=1 \ // RUN: -DOSX=0 \ // RUN: -DIPHONE=1 \ @@ -53,7 +56,8 @@ // RUN: -DLINUX=0 \ // RUN: -DUNIX=0 -// RUN: %clang -dM -E --target=arm64-apple-ios-simulator %s 2>&1 \ +// RUN: %clang -dM -E --target=arm64-apple-ios-simulator \ +// RUN: -fdefine-target-os-macros %s 2>&1 \ // RUN: | FileCheck %s -DMAC=1 \ // RUN: -DOSX=0 \ // RUN: -DIPHONE=1 \ @@ -69,7 +73,8 @@ // RUN: -DLINUX=0 \ // RUN: -DUNIX=0 -// RUN: %clang -dM -E --target=arm64-apple-tvos %s 2>&1 \ +// RUN: %clang -dM -E --target=arm64-apple-tvos \ +// RUN: -fdefine-target-os-macros %s 2>&1 \ // RUN: | FileCheck %s -DMAC=1 \ // RUN: -DOSX=0 \ // RUN: -DIPHONE=1 \ @@ -85,7 +90,8 @@ // RUN: -DLINUX=0 \ // RUN: -DUNIX=0 -// RUN: %clang -dM -E --target=arm64-apple-tvos-simulator %s 2>&1 \ +// RUN: %clang -dM -E --target=arm64-apple-tvos-simulator \ +// RUN: -fdefine-target-os-macros %s 2>&1 \ // RUN: | FileCheck %s -DMAC=1 \ // RUN: -DOSX=0 \ // RUN: -DIPHONE=1 \ @@ -101,7 +107,8 @@ // RUN: -DLINUX=0 \ // RUN: -DUNIX=0 -// RUN: %clang -dM -E --target=arm64-apple-watchos %s 2>&1 \ +// RUN: %clang -dM -E --target=arm64-apple-watchos \ +// RUN: -fdefine-target-os-macros %s 2>&1 \ // RUN: | FileCheck %s -DMAC=1 \ // RUN: -DOSX=0 \ // RUN: -DIPHONE=1 \ @@ -117,7 +124,8 @@ // RUN: -DLINUX=0 \ // RUN: -DUNIX=0 -// RUN: %clang -dM -E --target=arm64-apple-watchos-simulator %s 2>&1 \ +// RUN: %clang -dM -E --target=arm64-apple-watchos-simulator \ +// RUN: -fdefine-target-os-macros %s 2>&1 \ // RUN: | FileCheck %s -DMAC=1 \ // RUN: -DOSX=0 \ // RUN: -DIPHONE=1 \ @@ -133,7 +141,8 @@ // RUN: -DLINUX=0 \ // RUN: -DUNIX=0 -// RUN: %clang -dM -E --target=arm64-apple-xros %s 2>&1 \ +// RUN: %clang -dM -E --target=arm64-apple-xros \ +// RUN: -fdefine-target-os-macros %s 2>&1 \ // RUN: | FileCheck %s -DMAC=1 \ // RUN: -DOSX=0 \ // RUN: -DIPHONE=1 \ @@ -149,7 +158,8 @@ // RUN: -DLINUX=0 \ // RUN: -DUNIX=0 -// RUN: %clang -dM -E --target=arm64-apple-xros-simulator %s 2>&1 \ +// RUN: %clang -dM -E --target=arm64-apple-xros-simulator \ +// RUN: -fdefine-target-os-macros %s 2>&1 \ // RUN: | FileCheck %s -DMAC=1 \ // RUN: -DOSX=0 \ // RUN: -DIPHONE=1 \ @@ -165,7 +175,8 @@ // RUN: -DLINUX=0 \ // RUN: -DUNIX=0 -// RUN: %clang -dM -E --target=arm64-apple-driverkit %s 2>&1 \ +// RUN: %clang -dM -E --target=arm64-apple-driverkit \ +// RUN: -fdefine-target-os-macros %s 2>&1 \ // RUN: | FileCheck %s -DMAC=1 \ // RUN: -DOSX=0 \ // RUN: -DIPHONE=0 \ diff --git a/clang/test/Driver/fdepscan-prefix-map-sdk.c b/clang/test/Driver/fdepscan-prefix-map-sdk.c new file mode 100644 index 0000000000000..421148c1a1a6d --- /dev/null +++ b/clang/test/Driver/fdepscan-prefix-map-sdk.c @@ -0,0 +1,8 @@ +// RUN: %clang -fdepscan-prefix-map-sdk=/^sdk -### %s 2>&1 | FileCheck %s -check-prefix=NONE +// RUN: %clang -fdepscan-prefix-map-sdk=/^sdk -isysroot relative -### %s 2>&1 | FileCheck %s -check-prefix=NONE + +// NONE-NOT: -fdepscan-prefix-map + +// RUN: %clang -fdepscan-prefix-map-sdk=/^sdk -isysroot /sys/path -### %s 2>&1 | FileCheck %s +// RUN: %clang -fdepscan-prefix-map-sdk=/^sdk --sysroot /sys/path -### %s 2>&1 | FileCheck %s +// CHECK: -fdepscan-prefix-map=/sys/path=/^sdk diff --git a/clang/test/Driver/fdepscan-prefix-map-toolchain.c b/clang/test/Driver/fdepscan-prefix-map-toolchain.c new file mode 100644 index 0000000000000..1bdbdfe1ccd78 --- /dev/null +++ b/clang/test/Driver/fdepscan-prefix-map-toolchain.c @@ -0,0 +1,15 @@ +// RUN: %clang -fdepscan-prefix-map-toolchain=/^tc -resource-dir '' -### %s 2>&1 | FileCheck %s -check-prefix=NONE +// RUN: %clang -fdepscan-prefix-map-toolchain=/^tc -resource-dir relative -### %s 2>&1 | FileCheck %s -check-prefix=NONE +// RUN: %clang -fdepscan-prefix-map-toolchain=/^tc -resource-dir /lib/clang/10 -### %s 2>&1 | FileCheck %s -check-prefix=NONE + +// NONE-NOT: -fdepscan-prefix-map + +// RUN: %clang -fdepscan-prefix-map-toolchain=/^tc -resource-dir /tc/10 -### %s 2>&1 | FileCheck %s +// RUN: %clang -fdepscan-prefix-map-toolchain=/^tc -resource-dir /tc/lib/clang/10 -### %s 2>&1 | FileCheck %s +// RUN: %clang -fdepscan-prefix-map-toolchain=/^tc -resource-dir /tc/usr/lib/clang/10 -### %s 2>&1 | FileCheck %s + +// CHECK: -fdepscan-prefix-map=/tc=/^tc + +// Implicit resource-dir +// RUN: %clang -fdepscan-prefix-map-toolchain=/^tc -### %s 2>&1 | FileCheck %s -check-prefix=CHECK_IMPLICIT +// CHECK_IMPLICIT: -fdepscan-prefix-map={{.*}}=/^tc diff --git a/clang/test/Driver/fdepscan-prefix-map.c b/clang/test/Driver/fdepscan-prefix-map.c new file mode 100644 index 0000000000000..0a4bc473ef1d6 --- /dev/null +++ b/clang/test/Driver/fdepscan-prefix-map.c @@ -0,0 +1,8 @@ +// RUN: not %clang -fdepscan-prefix-map=/^bad -### %s 2>&1 | FileCheck %s -check-prefix=INVALID +// RUN: not %clang -fdepscan-prefix-map==/^bad -### %s 2>&1 | FileCheck %s -check-prefix=INVALID +// RUN: not %clang -fdepscan-prefix-map=relative=/^bad -### %s 2>&1 | FileCheck %s -check-prefix=INVALID +// RUN: not %clang -fdepscan-prefix-map=/=/^bad -### %s 2>&1 | FileCheck %s -check-prefix=INVALID +// INVALID: error: invalid argument '{{.*}}/^bad' to -fdepscan-prefix-map= + +// RUN: %clang -fdepscan-prefix-map=/good=/^good -### %s 2>&1 | FileCheck %s +// CHECK: "-fdepscan-prefix-map=/good=/^good" diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c index eb72140fb1315..dc39871b51f56 100644 --- a/clang/test/Driver/fsanitize.c +++ b/clang/test/Driver/fsanitize.c @@ -639,26 +639,26 @@ // RUN: not %clang --target=i386-pc-openbsd -fsanitize=memory %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-MSAN-OPENBSD // CHECK-MSAN-OPENBSD: unsupported option '-fsanitize=memory' for target 'i386-pc-openbsd' -// RUN: %clang --target=x86_64-apple-darwin -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-X86-64-DARWIN -// CHECK-LSAN-X86-64-DARWIN-NOT: unsupported option +// RUN: not %clang --target=x86_64-apple-darwin -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-X86-64-DARWIN +// CHECK-LSAN-X86-64-DARWIN: unsupported option -// RUN: %clang --target=x86_64-apple-ios-simulator -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-X86-64-IOSSIMULATOR -// CHECK-LSAN-X86-64-IOSSIMULATOR-NOT: unsupported option +// RUN: not %clang --target=x86_64-apple-ios-simulator -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-X86-64-IOSSIMULATOR +// CHECK-LSAN-X86-64-IOSSIMULATOR: unsupported option -// RUN: %clang --target=x86_64-apple-tvos-simulator -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-X86-64-TVOSSIMULATOR -// CHECK-LSAN-X86-64-TVOSSIMULATOR-NOT: unsupported option +// RUN: not %clang --target=x86_64-apple-tvos-simulator -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-X86-64-TVOSSIMULATOR +// CHECK-LSAN-X86-64-TVOSSIMULATOR: unsupported option -// RUN: %clang --target=i386-apple-darwin -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-I386-DARWIN -// CHECK-LSAN-I386-DARWIN-NOT: unsupported option +// RUN: not %clang --target=i386-apple-darwin -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-I386-DARWIN +// CHECK-LSAN-I386-DARWIN: unsupported option -// RUN: %clang --target=arm-apple-ios -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-ARM-IOS -// CHECK-LSAN-ARM-IOS-NOT: unsupported option +// RUN: not %clang --target=arm-apple-ios -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-ARM-IOS +// CHECK-LSAN-ARM-IOS: unsupported option -// RUN: %clang --target=i386-apple-ios-simulator -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-I386-IOSSIMULATOR -// CHECK-LSAN-I386-IOSSIMULATOR-NOT: unsupported option +// RUN: not %clang --target=i386-apple-ios-simulator -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-I386-IOSSIMULATOR +// CHECK-LSAN-I386-IOSSIMULATOR: unsupported option -// RUN: %clang --target=i386-apple-tvos-simulator -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-I386-TVOSSIMULATOR -// CHECK-LSAN-I386-TVOSSIMULATOR-NOT: unsupported option +// RUN: not %clang --target=i386-apple-tvos-simulator -fsanitize=leak %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-LSAN-I386-TVOSSIMULATOR +// CHECK-LSAN-I386-TVOSSIMULATOR: unsupported option // RUN: %clang --target=x86_64-linux-gnu -fvisibility=hidden -fsanitize=cfi -flto -resource-dir=%S/Inputs/resource_dir -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI diff --git a/clang/test/Driver/linux-musl-header-search.cpp b/clang/test/Driver/linux-musl-header-search.cpp deleted file mode 100644 index acff5b71aa65a..0000000000000 --- a/clang/test/Driver/linux-musl-header-search.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// RUN: %clang -### %s -fsyntax-only 2>&1 \ -// RUN: --target=x86_64-linux-musl -stdlib=libc++ \ -// RUN: -ccc-install-dir %S/Inputs/basic_linux_tree/usr/bin \ -// RUN: -resource-dir=%S/Inputs/resource_dir \ -// RUN: --sysroot=%S/Inputs/basic_linux_libcxx_tree \ -// RUN: | FileCheck --check-prefix=CHECK-X86-64-LIBCXX %s - -// RESOURCE_DIR/include comes after /usr/include on linux-musl. -// This is different from a glibc-based distribution. -// CHECK-X86-64-LIBCXX: "-resource-dir" "[[RESOURCE_DIR:[^"]+]]" -// CHECK-X86-64-LIBCXX: "-isysroot" "[[SYSROOT:[^"]+]]" -// CHECK-X86-64-LIBCXX: "-internal-isystem" "[[SYSROOT]]{{/|\\\\}}usr{{/|\\\\}}include{{/|\\\\}}c++{{/|\\\\}}v1" -// CHECK-X86-64-LIBCXX: "-internal-isystem" "[[SYSROOT]]/usr/local/include" -// CHECK-X86-64-LIBCXX: "-internal-externc-isystem" "[[SYSROOT]]/usr/include" -// CHECK-X86-64-LIBCXX: "-internal-isystem" "[[RESOURCE_DIR]]{{/|\\\\}}include" - -// RUN: %clang -### %s -fsyntax-only -nobuiltininc 2>&1 \ -// RUN: --target=x86_64-linux-musl \ -// RUN: -ccc-install-dir %S/Inputs/basic_linux_tree/usr/bin \ -// RUN: -resource-dir=%S/Inputs/resource_dir \ -// RUN: --sysroot=%S/Inputs/basic_linux_libcxx_tree \ -// RUN: | FileCheck --check-prefix=CHECK-NOBUILTININC %s - -// CHECK-NOBUILTININC: "-resource-dir" "[[RESOURCE_DIR:[^"]+]]" -// CHECK-NOBUILTININC-NOT: "-internal-isystem" "[[RESOURCE_DIR]]{{/|\\\\}}include" - -// RUN: %clang -### %s -fsyntax-only -nostdlibinc 2>&1 \ -// RUN: --target=x86_64-linux-musl \ -// RUN: -ccc-install-dir %S/Inputs/basic_linux_tree/usr/bin \ -// RUN: -resource-dir=%S/Inputs/resource_dir \ -// RUN: --sysroot=%S/Inputs/basic_linux_libcxx_tree \ -// RUN: | FileCheck --check-prefix=CHECK-NOSTDLIBINC %s - -// CHECK-NOSTDLIBINC: "-resource-dir" "[[RESOURCE_DIR:[^"]+]]" -// CHECK-NOSTDLIBINC-NOT: "-internal-externc-isystem" -// CHECK-NOSTDLIBINC-NOT: "-internal-isystem" -// CHECK-NOSTDLIBINC: "-internal-isystem" "[[RESOURCE_DIR]]{{/|\\\\}}include" -// CHECK-NOSTDLIBINC-NOT: "-internal-externc-isystem" -// CHECK-NOSTDLIBINC-NOT: "-internal-isystem" diff --git a/clang/test/Driver/lld-repro.c b/clang/test/Driver/lld-repro.c index 0e6340865b738..9fb307ffb39f7 100644 --- a/clang/test/Driver/lld-repro.c +++ b/clang/test/Driver/lld-repro.c @@ -1,6 +1,11 @@ // REQUIRES: lld // UNSUPPORTED: target={{.*-(ps4|ps5)}}, target={{.*}}-zos{{.*}} +// Swift LLVM fork downstream change start +// lld from the Swift LLVM fork does not support linking Darwin Mach-O files +// UNSUPPORTED: * +// Swift LLVM fork downstream change end + // RUN: echo "-nostartfiles -nostdlib -fuse-ld=lld -gen-reproducer=error -fcrash-diagnostics-dir=%t" \ // RUN: | sed -e 's/\\/\\\\/g' > %t.rsp diff --git a/clang/test/Driver/sanitizer-ld.c b/clang/test/Driver/sanitizer-ld.c index befd322d027c9..588df290cf94d 100644 --- a/clang/test/Driver/sanitizer-ld.c +++ b/clang/test/Driver/sanitizer-ld.c @@ -993,15 +993,14 @@ // CHECK-ASAN-DARWIN106-CXX: libclang_rt.asan_osx_dynamic.dylib // CHECK-ASAN-DARWIN106-CXX-NOT: -lc++abi -// RUN: %clangxx -fsanitize=leak -### %s 2>&1 \ +// Apple-Clang: Don't support LSan +// RUN: not %clangxx -fsanitize=leak -### %s 2>&1 \ // RUN: -mmacos-version-min=10.6 \ // RUN: --target=x86_64-apple-darwin13.4.0 -fuse-ld=ld -stdlib=platform \ // RUN: -resource-dir=%S/Inputs/resource_dir \ // RUN: --sysroot=%S/Inputs/basic_linux_tree \ // RUN: | %{filecheck} --check-prefix=CHECK-LSAN-DARWIN106-CXX -// CHECK-LSAN-DARWIN106-CXX: "{{.*}}ld{{(.exe)?}}" -// CHECK-LSAN-DARWIN106-CXX: libclang_rt.lsan_osx_dynamic.dylib -// CHECK-LSAN-DARWIN106-CXX-NOT: -lc++abi +// CHECK-LSAN-DARWIN106-CXX: unsupported option '-fsanitize=leak' // RUN: %clang -### %s 2>&1 \ // RUN: --target=x86_64-unknown-linux -fuse-ld=ld -fsanitize=safe-stack \ diff --git a/clang/test/FixIt/fixit-fill-in-missing-switch-cases.cpp b/clang/test/FixIt/fixit-fill-in-missing-switch-cases.cpp new file mode 100644 index 0000000000000..20b948f46195e --- /dev/null +++ b/clang/test/FixIt/fixit-fill-in-missing-switch-cases.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fdiagnostics-parseable-fixits -std=c++11 %s 2>&1 | FileCheck %s + +enum Color { + Black, + Blue, + White, + Gold +}; + +void fillInCases(Color c) { + switch (c) { + case Black: + break; + } +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"case Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" + switch (c) { + } +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" +} + +enum class NoDenseMap: long long { + Baddie = 0x7fffffffffffffffLL, + BigBaddie = -0x7fffffffffffffffLL-1 +}; + +void fillInAllCases(NoDenseMap v) { + switch (v) { + } +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"case NoDenseMap::Baddie:\n<#code#>\nbreak;\ncase NoDenseMap::BigBaddie:\n<#code#>\nbreak;\n" +} + diff --git a/clang/test/FixIt/fixit-fill-in-protocol-requirements.m b/clang/test/FixIt/fixit-fill-in-protocol-requirements.m new file mode 100644 index 0000000000000..f030e66c8824d --- /dev/null +++ b/clang/test/FixIt/fixit-fill-in-protocol-requirements.m @@ -0,0 +1,113 @@ +// RUN: %clang_cc1 -triple=x86_64-apple-macos10.10 -verify -Wno-objc-root-class -fallow-editor-placeholders %s +// RUN: %clang_cc1 -triple=x86_64-apple-macos10.10 -fdiagnostics-parseable-fixits -fallow-editor-placeholders %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple=x86_64-apple-macos10.12 -fdiagnostics-parseable-fixits -fallow-editor-placeholders %s 2>&1 | FileCheck --check-prefix=AVAILABLE %s + +@protocol P1 + +- (void)p2Method; + +@end + +@protocol P2 + +- (void)p1Method; + +@end + +@interface I + +@end + +@implementation I // expected-warning {{class 'I' does not conform to protocols 'P2' and 'P1'}} expected-note {{add stubs for missing protocol requirements}} + +@end +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"- (void)p2Method { \n <#code#>\n}\n\n- (void)p1Method { \n <#code#>\n}\n\n" + +@protocol P3 + ++ (void)p3ClassMethod; + +@end + +@interface I (Category) + +@end + +@implementation I (Category) // expected-warning {{category 'Category' does not conform to protocol 'P3'}} expected-note {{add stubs for missing protocol requirements}} + +@end +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"+ (void)p3ClassMethod { \n <#code#>\n}\n\n" + +@protocol P4 + +- (void)anotherMethod; + +@end + +@interface ThreeProtocols +@end +@implementation ThreeProtocols // expected-warning {{class 'ThreeProtocols' does not conform to protocols 'P2', 'P1' and 'P3'}} expected-note {{add stubs for missing protocol requirements}} +@end + +@interface FourProtocols +@end +@implementation FourProtocols // expected-warning {{class 'FourProtocols' does not conform to protocols 'P2', 'P1', 'P3', ...}} expected-note {{add stubs for missing protocol requirements}} +@end + +// Unavailable methods +@protocol TakeAvailabilityIntoAccount + +- (void)unavailableMethod __attribute__((availability(macos,unavailable))); ++ (void)notYetAvailableMethod __attribute__((availability(macos,introduced=10.11))); +- (void)availableMethod; +- (void)deprecatedMethod __attribute__((availability(macos,introduced=10.0, deprecated=10.6))); + +@end + +@interface ImplementsAllAvailable +@end + +@implementation ImplementsAllAvailable // expected-warning {{class 'ImplementsAllAvailable' does not conform to protocol 'TakeAvailabilityIntoAccount'}} expected-note {{add stubs for missing protocol requirements}} + +- (void)availableMethod { } +- (void)deprecatedMethod { } + +@end + +@interface FixitJustAvailable +@end + +@implementation FixitJustAvailable // expected-warning {{class 'FixitJustAvailable' does not conform to protocol 'TakeAvailabilityIntoAccount'}} expected-note {{add stubs for missing protocol requirements}} + +@end +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"- (void)availableMethod { \n <#code#>\n}\n\n- (void)deprecatedMethod { \n <#code#>\n}\n\n" +// AVAILABLE: fix-it:{{.*}}:{[[@LINE-2]]:1-[[@LINE-2]]:1}:"- (void)availableMethod { \n <#code#>\n}\n\n- (void)deprecatedMethod { \n <#code#>\n}\n\n+ (void)notYetAvailableMethod { \n <#code#>\n}\n\n" + +@protocol PReq1 +-(void)reqZ1; +@end + +@protocol PReq2 +-(void)reqZ1; +-(void)reqA2; +-(void)reqA1; +@end + +// Ensure optional cannot hide required methods with the same selector. +@protocol POpt +@optional +-(void)reqZ1; +-(void)reqA2; +-(void)reqA1; +@end + +@interface MultiReqOpt +@end +@implementation MultiReqOpt // expected-warning {{class 'MultiReqOpt' does not conform to protocols 'PReq1' and 'PReq2'}} expected-note {{add stubs}} +@end +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:" +// Z1 is first due to being from the first listed protocol, PReq1 +// CHECK-SAME: - (void)reqZ1 +// A1 is before A2 because of secondary sort by name. +// CHECK-SAME: - (void)reqA1 +// CHECK-SAME: - (void)reqA2 diff --git a/clang/test/FixIt/fixit-fill-in-switch-crash.cpp b/clang/test/FixIt/fixit-fill-in-switch-crash.cpp new file mode 100644 index 0000000000000..635fe818addd9 --- /dev/null +++ b/clang/test/FixIt/fixit-fill-in-switch-crash.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -verify -std=c++11 %s +// RUN: not %clang_cc1 -fdiagnostics-parseable-fixits -std=c++11 %s 2>&1 | FileCheck %s + +enum Color { + Black, Red +}; + +void dontCrashOnEmptySubStmt(Color c) { // expected-note {{to match this '{'}} + switch (c) { // expected-note {{to match this '{'}} \ + // expected-warning {{enumeration value 'Red' not handled in switch}} \ + // expected-note {{add missing switch cases}} + case Black: // CHECK: fix-it:{{.*}}:{[[@LINE+3]]:10-[[@LINE+3]]:10}:"case Red:\n<#code#>\nbreak;\n" + // expected-error@+2 {{expected expression}} + // expected-error@+1 2 {{expected '}'}} + case // diff --git a/clang/test/Frontend/Inputs/macro_defined_type.h b/clang/test/Frontend/Inputs/macro_defined_type.h new file mode 100644 index 0000000000000..fa90b5e9e6a1c --- /dev/null +++ b/clang/test/Frontend/Inputs/macro_defined_type.h @@ -0,0 +1,4 @@ +#pragma clang system_header + +#define _SYS_NODEREF __attribute__((noderef)) +#define _SYS_LIBCPP_FLOAT_ABI __attribute__((pcs("aapcs"))) diff --git a/clang/test/Frontend/darwin-eabi.c b/clang/test/Frontend/darwin-eabi.c index 9d62632891cbe..27471e6cfb0e6 100644 --- a/clang/test/Frontend/darwin-eabi.c +++ b/clang/test/Frontend/darwin-eabi.c @@ -1,6 +1,6 @@ -// RUN: %clang --target=armv6m-apple-darwin -dM -E %s | FileCheck %s -// RUN: %clang --target=armv7m-apple-darwin -dM -E %s | FileCheck %s -// RUN: %clang --target=armv7em-apple-darwin -dM -E %s | FileCheck %s +// RUN: %clang -arch armv6m -dM -E %s | FileCheck %s +// RUN: %clang -arch armv7m -dM -E %s | FileCheck %s +// RUN: %clang -arch armv7em -dM -E %s | FileCheck %s // RUN: %clang_cc1 -triple thumbv7m-apple-unknown-macho -dM -E %s | FileCheck %s // CHECK-NOT: __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ diff --git a/clang/test/Frontend/macro_defined_type.cpp b/clang/test/Frontend/macro_defined_type.cpp index 71a0ff18477d8..e796d9b37132c 100644 --- a/clang/test/Frontend/macro_defined_type.cpp +++ b/clang/test/Frontend/macro_defined_type.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-linux-gnu %s +// RUN: %clang_cc1 -I%S/Inputs -fsyntax-only -verify -triple x86_64-linux-gnu %s + +#include #define NODEREF __attribute__((noderef)) @@ -14,12 +16,26 @@ void Func() { auto NODEREF auto_i2 = i; // expected-warning{{'noderef' can only be used on an array or pointer type}} } +// The diagnostic message is hard-coded as 'noderef' so using a system macro doesn't change the behavior +void Func_system_macro() { + int _SYS_NODEREF i; // expected-warning{{'noderef' can only be used on an array or pointer type}} + int _SYS_NODEREF *i_ptr; + + auto _SYS_NODEREF *auto_i_ptr2 = i_ptr; + auto _SYS_NODEREF auto_i2 = i; // expected-warning{{'noderef' can only be used on an array or pointer type}} +} + + // Added test for fix for P41835 #define _LIBCPP_FLOAT_ABI __attribute__((pcs("aapcs"))) struct A { _LIBCPP_FLOAT_ABI int operator()() throw(); // expected-warning{{'pcs' calling convention is not supported for this target}} }; +struct A_system_macro { + _SYS_LIBCPP_FLOAT_ABI int operator()() throw(); // expected-warning{{'_SYS_LIBCPP_FLOAT_ABI' calling convention is not supported for this target}} +}; + // Added test for fix for PR43315 #define a __attribute__((__cdecl__, __regparm__(0))) int(a b)(); diff --git a/clang/test/Headers/stdarg-cxx-modules.cpp b/clang/test/Headers/stdarg-cxx-modules.cpp index 113ece4fb64b3..b7fbd39eb9ce0 100644 --- a/clang/test/Headers/stdarg-cxx-modules.cpp +++ b/clang/test/Headers/stdarg-cxx-modules.cpp @@ -1,9 +1,11 @@ // RUN: rm -fR %t // RUN: split-file %s %t -// RUN: cd %t -// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-user-header h1.h -// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-user-header h2.h -fmodule-file=h1.pcm -// RUN: %clang_cc1 -std=c++20 -fsyntax-only main.cpp -fmodule-file=h1.pcm -fmodule-file=h2.pcm + +/// FIXME: Using absolute path and explicitly specifying output files since +/// downstream branch has module file bypass for inode reuse problem on linux file systems. +// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-user-header %t/h1.h -o %t/h1.pcm +// RUN: %clang_cc1 -std=c++20 -emit-header-unit -xc++-user-header %t/h2.h -fmodule-file=%t/h1.pcm -o %t/h2.pcm +// RUN: %clang_cc1 -std=c++20 -fsyntax-only %t/main.cpp -fmodule-file=%t/h1.pcm -fmodule-file=%t/h2.pcm //--- h1.h #include diff --git a/clang/test/Index/Core/scan-deps-by-mod-name.m b/clang/test/Index/Core/scan-deps-by-mod-name.m new file mode 100644 index 0000000000000..805c7069029b1 --- /dev/null +++ b/clang/test/Index/Core/scan-deps-by-mod-name.m @@ -0,0 +1,32 @@ +// Use driver arguments. +// RUN: rm -rf %t.mcp +// RUN: echo %S > %t.result +// RUN: echo %S > %t_v2.result +// +// RUN: c-index-test core --scan-deps-by-mod-name -output-dir %t -module-name=ModA -working-dir %S -- %clang -c -I %S/Inputs/module \ +// RUN: -fmodules -fmodules-cache-path=%t.mcp \ +// RUN: -o FoE.o -x objective-c >> %t.result +// RUN: cat %t.result | sed 's/\\/\//g' | FileCheck %s -DOUTPUTS=%/t + +// CHECK: [[PREFIX:.*]] +// CHECK-NEXT: modules: +// CHECK-NEXT: module: +// CHECK-NEXT: name: ModA +// CHECK-NEXT: context-hash: [[HASH_MOD_A:[A-Z0-9]+]] +// CHECK-NEXT: cwd-ignored: 0 +// CHECK-NEXT: module-map-path: [[PREFIX]]/Inputs/module/module.modulemap +// CHECK-NEXT: module-deps: +// CHECK-NEXT: file-deps: +// CHECK-NEXT: [[PREFIX]]/Inputs/module/module.modulemap +// CHECK-NEXT: [[PREFIX]]/Inputs/module/ModA.h +// CHECK-NEXT: [[PREFIX]]/Inputs/module/SubModA.h +// CHECK-NEXT: [[PREFIX]]/Inputs/module/SubSubModA.h +// CHECK-NEXT: build-args: {{.*}} -emit-module {{.*}} -fmodule-name=ModA {{.*}} -fno-implicit-modules {{.*}} +// CHECK-NEXT: dependencies: +// CHECK-NEXT: command 0: +// CHECK-NEXT: context-hash: +// CHECK-NEXT: module-deps: +// CHECK-NEXT: ModA:[[HASH_MOD_A]] +// CHECK-NEXT: file-deps: +// CHECK-NEXT: {{.*}}ModA-{{.*}}.input +// CHECK-NEXT: build-args: -cc1 {{.*}} -fmodule-file={{(ModA=)?}}{{.*}}ModA_{{.*}}.pcm diff --git a/clang/test/Index/Core/scan-deps-cas.m b/clang/test/Index/Core/scan-deps-cas.m new file mode 100644 index 0000000000000..cfbf1974b547f --- /dev/null +++ b/clang/test/Index/Core/scan-deps-cas.m @@ -0,0 +1,109 @@ +// REQUIRES: ondisk_cas + +// RUN: rm -rf %t + +// RUN: c-index-test core --scan-deps -working-dir %S -output-dir=%t -cas-path %t/cas \ +// RUN: -- %clang -c -I %S/Inputs/module \ +// RUN: -fmodules -fmodules-cache-path=%t/mcpit \ +// RUN: -o FoE.o -x objective-c %s > %t.result +// RUN: cat %t.result | sed 's/\\/\//g' | FileCheck %s -DPREFIX=%S -DOUTPUTS=%/t -check-prefix=INCLUDE_TREE + +// RUN: env CLANG_CACHE_USE_CASFS_DEPSCAN=1 c-index-test core --scan-deps -working-dir %S -output-dir=%t -cas-path %t/cas \ +// RUN: -- %clang -c -I %S/Inputs/module \ +// RUN: -fmodules -fmodules-cache-path=%t/mcp \ +// RUN: -o FoE.o -x objective-c %s > %t.casfs.result +// RUN: cat %t.casfs.result | sed 's/\\/\//g' | FileCheck %s -DPREFIX=%S -DOUTPUTS=%/t + +// RUN: env CLANG_CACHE_USE_INCLUDE_TREE=1 c-index-test core --scan-deps -working-dir %S -output-dir=%t -cas-path %t/cas \ +// RUN: -- %clang -c -I %S/Inputs/module \ +// RUN: -fmodules -fmodules-cache-path=%t/mcpit \ +// RUN: -o FoE.o -x objective-c %s > %t.includetree.result +// RUN: cat %t.includetree.result | sed 's/\\/\//g' | FileCheck %s -DPREFIX=%S -DOUTPUTS=%/t -check-prefix=INCLUDE_TREE + +// RUN: c-index-test core --scan-deps -working-dir %S -output-dir=%t \ +// RUN: -- %clang -c -I %S/Inputs/module \ +// RUN: -fmodules -fmodules-cache-path=%t/mcp \ +// RUN: -o FoE.o -x objective-c %s | FileCheck %s -check-prefix=NO_CAS +// NO_CAS-NOT: fcas +// NO_CAS-NOT: faction-cache +// NO_CAS-NOT: fcache-compile-job + +#include "ModA.h" + +// CHECK: modules: +// CHECK-NEXT: module: +// CHECK-NEXT: name: ModA +// CHECK-NEXT: context-hash: [[HASH_MOD_A:[A-Z0-9]+]] +// CHECK-NEXT: cwd-ignored: 0 +// CHECK-NEXT: module-map-path: [[PREFIX]]/Inputs/module/module.modulemap +// CHECK-NEXT: casfs-root-id: [[CASFS_MODA_ROOT_ID:llvmcas://[[:xdigit:]]+]] +// CHECK-NEXT: cache-key: [[CASFS_MODA_CACHE_KEY:llvmcas://[[:xdigit:]]+]] +// CHECK-NEXT: module-deps: +// CHECK-NEXT: file-deps: +// CHECK-NEXT: [[PREFIX]]/Inputs/module/module.modulemap +// CHECK-NEXT: [[PREFIX]]/Inputs/module/ModA.h +// CHECK-NEXT: [[PREFIX]]/Inputs/module/SubModA.h +// CHECK-NEXT: [[PREFIX]]/Inputs/module/SubSubModA.h +// CHECK-NEXT: build-args: +// CHECK-SAME: -cc1 +// CHECK-SAME: -fcas-path +// CHECK-SAME: -fcas-fs [[CASFS_MODA_ROOT_ID]] +// CHECK-SAME: -fcache-compile-job +// CHECK-SAME: -emit-module +// CHECK-SAME: -fmodule-name=ModA +// CHECK-SAME: -fno-implicit-modules + +// CHECK-NEXT: dependencies: +// CHECK-NEXT: command 0: +// CHECK-NEXT: context-hash: [[HASH_TU:[A-Z0-9]+]] +// CHECK-NEXT: casfs-root-id: [[CASFS_TU_ROOT_ID:llvmcas://[[:xdigit:]]+]] +// CHECK-NEXT: cache-key: [[CASFS_TU_CACHE_KEY:llvmcas://[[:xdigit:]]+]] +// CHECK-NEXT: module-deps: +// CHECK-NEXT: ModA:[[HASH_MOD_A]] +// CHECK-NEXT: file-deps: +// CHECK-NEXT: [[PREFIX]]/scan-deps-cas.m +// CHECK-NEXT: build-args: +// CHECK-SAME: -cc1 +// CHECK-SAME: -fcas-path +// CHECK-SAME: -fcas-fs [[CASFS_TU_ROOT_ID]] +// CHECK-SAME: -fcache-compile-job +// CHECK-SAME: -fmodule-file-cache-key [[PCM:.*ModA_.*pcm]] llvmcas://{{[[:xdigit:]]+}} +// CHECK-SAME: -fmodule-file={{(ModA=)?}}[[PCM]] + + +// INCLUDE_TREE: modules: +// INCLUDE_TREE-NEXT: module: +// INCLUDE_TREE-NEXT: name: ModA +// INCLUDE_TREE-NEXT: context-hash: [[HASH_MOD_A:[A-Z0-9]+]] +// INCLUDE_TREE-NEXT: cwd-ignored: 0 +// INCLUDE_TREE-NEXT: module-map-path: [[PREFIX]]/Inputs/module/module.modulemap +// INCLUDE_TREE-NEXT: include-tree-id: [[ModA_INCLUDE_TREE_ID:llvmcas://[[:xdigit:]]+]] +// INCLUDE_TREE-NEXT: cache-key: [[ModA_CACHE_KEY:llvmcas://[[:xdigit:]]+]] +// INCLUDE_TREE-NEXT: module-deps: +// INCLUDE_TREE-NEXT: file-deps: +// INCLUDE_TREE-NEXT: [[PREFIX]]/Inputs/module/module.modulemap +// INCLUDE_TREE-NEXT: [[PREFIX]]/Inputs/module/ModA.h +// INCLUDE_TREE-NEXT: [[PREFIX]]/Inputs/module/SubModA.h +// INCLUDE_TREE-NEXT: [[PREFIX]]/Inputs/module/SubSubModA.h +// INCLUDE_TREE-NEXT: build-args: +// INCLUDE_TREE-SAME: -cc1 +// INCLUDE_TREE-SAME: -fcas-path +// INCLUDE_TREE-SAME: -fcas-include-tree [[ModA_INCLUDE_TREE_ID]] +// INCLUDE_TREE-SAME: -fcache-compile-job + +// INCLUDE_TREE: dependencies: +// INCLUDE_TREE-NEXT: command 0: +// INCLUDE_TREE-NEXT: context-hash: [[HASH_TU:[A-Z0-9]+]] +// INCLUDE_TREE-NEXT: include-tree-id: [[INC_TU_INCLUDE_TREE_ID:llvmcas://[[:xdigit:]]+]] +// INCLUDE_TREE-NEXT: cache-key: [[INC_TU_CACHE_KEY:llvmcas://[[:xdigit:]]+]] +// INCLUDE_TREE-NEXT: module-deps: +// INCLUDE_TREE-NEXT: ModA:[[HASH_MOD_A]] +// INCLUDE_TREE-NEXT: file-deps: +// INCLUDE_TREE-NEXT: [[PREFIX]]/scan-deps-cas.m +// INCLUDE_TREE-NEXT: build-args: +// INCLUDE_TREE-SAME: -cc1 +// INCLUDE_TREE-SAME: -fcas-path +// INCLUDE_TREE-SAME: -fcas-include-tree [[INC_TU_INCLUDE_TREE_ID]] +// INCLUDE_TREE-SAME: -fcache-compile-job +// INCLUDE_TREE-SAME: -fmodule-file-cache-key [[PCM:.*ModA_.*pcm]] [[ModA_CACHE_KEY]] +// INCLUDE_TREE-SAME: -fmodule-file={{(ModA=)?}}[[PCM]] diff --git a/clang/test/Index/Core/scan-deps-with-diags.m b/clang/test/Index/Core/scan-deps-with-diags.m new file mode 100644 index 0000000000000..680ecbe48967a --- /dev/null +++ b/clang/test/Index/Core/scan-deps-with-diags.m @@ -0,0 +1,6 @@ +// RUN: not c-index-test core --scan-deps -working-dir %S -output-dir=%t -- \ +// RUN: %clang -c %s -o %t/t.o 2> %t.err.txt +// RUN: FileCheck -input-file=%t.err.txt %s + +// CHECK: [[@LINE+1]]:10: fatal error: 'not-existent.h' file not found +#include "not-existent.h" diff --git a/clang/test/Index/Core/scan-deps.m b/clang/test/Index/Core/scan-deps.m new file mode 100644 index 0000000000000..f9dd2c4d07152 --- /dev/null +++ b/clang/test/Index/Core/scan-deps.m @@ -0,0 +1,66 @@ +// Use driver arguments. +// RUN: rm -rf %t.mcp +// RUN: echo %S > %t.result +// RUN: echo %S > %t_savetemps.result +// RUN: echo %S > %t_v3.result +// +// RUN: c-index-test core --scan-deps -working-dir %S -output-dir=%t -- %clang -c -I %S/Inputs/module \ +// RUN: -fmodules -fmodules-cache-path=%t.mcp \ +// RUN: -o FoE.o -x objective-c %s >> %t.result +// RUN: cat %t.result | sed 's/\\/\//g' | FileCheck %s -DOUTPUTS=%/t --check-prefixes=CHECK,CC1 + +// RUN: c-index-test core --scan-deps -working-dir %S -output-dir=%t -- %clang -c -I %S/Inputs/module \ +// RUN: -fmodules -fmodules-cache-path=%t.mcp -save-temps=obj \ +// RUN: -o FoE.o -x objective-c %s >> %t_savetemps.result +// RUN: cat %t_savetemps.result | sed 's/\\/\//g' | FileCheck %s -DOUTPUTS=%/t --check-prefixes=CHECK,SAVETEMPS + +@import ModA; + +// CHECK: [[PREFIX:.*]] +// CHECK-NEXT: modules: +// CHECK-NEXT: module: +// CHECK-NEXT: name: ModA +// CHECK-NEXT: context-hash: [[HASH_MOD_A:[A-Z0-9]+]] +// CHECK-NEXT: cwd-ignored: 0 +// CHECK-NEXT: module-map-path: [[PREFIX]]/Inputs/module/module.modulemap +// CHECK-NEXT: module-deps: +// CHECK-NEXT: file-deps: +// CHECK-NEXT: [[PREFIX]]/Inputs/module/module.modulemap +// CHECK-NEXT: [[PREFIX]]/Inputs/module/ModA.h +// CHECK-NEXT: [[PREFIX]]/Inputs/module/SubModA.h +// CHECK-NEXT: [[PREFIX]]/Inputs/module/SubSubModA.h +// CHECK-NEXT: build-args: {{.*}} -emit-module {{.*}} -fmodule-name=ModA {{.*}} -fno-implicit-modules {{.*}} + +// CHECK-NEXT: dependencies: +// CHECK-NEXT: command 0: +// CHECK-NEXT: context-hash: [[HASH_TU:[A-Z0-9]+]] +// CHECK-NEXT: module-deps: +// CHECK-NEXT: ModA:[[HASH_MOD_A]] +// CHECK-NEXT: file-deps: +// CHECK-NEXT: [[PREFIX]]/scan-deps.m +// CC1-NEXT: build-args: -cc1 {{.*}} -fmodule-file={{(ModA=)?}}{{.*}}ModA_{{.*}}.pcm +// SAVETEMPS-NEXT: build-args: -cc1 {{.*}} -E {{.*}} -fmodule-file={{(ModA=)?}}{{.*}}ModA_{{.*}}.pcm + +// SAVETEMPS-NEXT: command 1: +// SAVETEMPS-NEXT: context-hash: [[HASH_TU]] +// SAVETEMPS-NEXT: module-deps: +// SAVETEMPS-NEXT: ModA:[[HASH_MOD_A]] +// SAVETEMPS-NEXT: file-deps: +// SAVETEMPS-NEXT: [[PREFIX]]/scan-deps.m +// SAVETEMPS-NEXT: build-args: -cc1 {{.*}} -emit-llvm-bc {{.*}} -fmodule-file={{(ModA=)?}}{{.*}}ModA_{{.*}}.pcm + +// SAVETEMPS-NEXT: command 2: +// SAVETEMPS-NEXT: context-hash: [[HASH_TU]] +// SAVETEMPS-NEXT: module-deps: +// SAVETEMPS-NEXT: ModA:[[HASH_MOD_A]] +// SAVETEMPS-NEXT: file-deps: +// SAVETEMPS-NEXT: [[PREFIX]]/scan-deps.m +// SAVETEMPS-NEXT: build-args: -cc1 {{.*}} -S + +// SAVETEMPS-NEXT: command 3: +// SAVETEMPS-NEXT: context-hash: [[HASH_TU]] +// SAVETEMPS-NEXT: module-deps: +// SAVETEMPS-NEXT: ModA:[[HASH_MOD_A]] +// SAVETEMPS-NEXT: file-deps: +// SAVETEMPS-NEXT: [[PREFIX]]/scan-deps.m +// SAVETEMPS-NEXT: build-args: -cc1as diff --git a/clang/test/Index/Store/Inputs/IndexMacrosMod.h b/clang/test/Index/Store/Inputs/IndexMacrosMod.h new file mode 100644 index 0000000000000..da980f2064fa8 --- /dev/null +++ b/clang/test/Index/Store/Inputs/IndexMacrosMod.h @@ -0,0 +1,9 @@ +#ifndef INDEXMACROSMODULE_H +#define INDEXMACROSMODULE_H + +#define INDEX_MACROS_MODULE 1 +#undef INDEX_MACROS_MODULE +#define INDEX_MACROS_MODULE 2 +int x = INDEX_MACROS_MODULE; + +#endif // INDEXMACROSMODULE_H diff --git a/clang/test/Index/Store/Inputs/explicit-modules/DependencyA.h b/clang/test/Index/Store/Inputs/explicit-modules/DependencyA.h new file mode 100644 index 0000000000000..98f98fbe6f97d --- /dev/null +++ b/clang/test/Index/Store/Inputs/explicit-modules/DependencyA.h @@ -0,0 +1 @@ +int dependencyAVersion(void); diff --git a/clang/test/Index/Store/Inputs/explicit-modules/module.modulemap b/clang/test/Index/Store/Inputs/explicit-modules/module.modulemap new file mode 100644 index 0000000000000..111f7f7ff450c --- /dev/null +++ b/clang/test/Index/Store/Inputs/explicit-modules/module.modulemap @@ -0,0 +1,3 @@ +module DependencyA { + header "DependencyA.h" +} diff --git a/clang/test/Index/Store/Inputs/head.h b/clang/test/Index/Store/Inputs/head.h new file mode 100644 index 0000000000000..6ac174dd19e6c --- /dev/null +++ b/clang/test/Index/Store/Inputs/head.h @@ -0,0 +1,3 @@ + +extern void test1_func(void); +extern void test2_func(void); diff --git a/clang/test/Index/Store/Inputs/json.c.json b/clang/test/Index/Store/Inputs/json.c.json new file mode 100644 index 0000000000000..498022d230855 --- /dev/null +++ b/clang/test/Index/Store/Inputs/json.c.json @@ -0,0 +1,151 @@ +{ + "files": [ + "/test1.o", + "/Inputs/test1.c", + "/Inputs/head.h", + "/test2.o", + "/Inputs/test2.c", + "/test3.o", + "/Inputs/test3.cpp" + ], + "symbols": [ + { + "kind": "function", + "lang": "C", + "usr": "c:@F@test1_func", + "name": "test1_func", + "codegen": "_test1_func", + "roles": "Decl,Def" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@test2_func", + "name": "test2_func", + "codegen": "_test2_func", + "roles": "Decl,Def" + }, + { + "kind": "class", + "lang": "C++", + "usr": "c:@S@Base", + "name": "Base", + "roles": "Def,Ref,RelBase,RelCont" + }, + { + "kind": "class", + "lang": "C++", + "usr": "c:@S@Sub", + "name": "Sub", + "roles": "Def", + "rel-roles": "RelBase,RelCont" + } + ], + "records": [ + { + "occurrences": [ + { + "symbol": 0, + "line": 3, + "col": 6, + "roles": "Def" + } + ] + }, + { + "occurrences": [ + { + "symbol": 0, + "line": 2, + "col": 13, + "roles": "Decl" + }, + { + "symbol": 1, + "line": 3, + "col": 13, + "roles": "Decl" + } + ] + }, + { + "occurrences": [ + { + "symbol": 1, + "line": 3, + "col": 6, + "roles": "Def" + } + ] + }, + { + "occurrences": [ + { + "symbol": 2, + "line": 1, + "col": 7, + "roles": "Def" + }, + { + "symbol": 3, + "line": 2, + "col": 7, + "roles": "Def" + }, + { + "symbol": 2, + "line": 2, + "col": 20, + "roles": "Ref,RelBase,RelCont", + "relations": [ + { + "symbol": 3, + "rel-roles": "RelBase,RelCont" + } + ] + + } + ] + } + ], + "units": [ + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 0, + "sources": [ + { + "file": 1, + "records": [0] + }, + { + "file": 2, + "records": [1] + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 3, + "sources": [ + { + "file": 4, + "records": [2] + }, + { + "file": 2, + "records": [1] + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 5, + "sources": [ + { + "file": 6, + "records": [3] + } + ] + } + ] +} diff --git a/clang/test/Index/Store/Inputs/macro-only-guards.h b/clang/test/Index/Store/Inputs/macro-only-guards.h new file mode 100644 index 0000000000000..33eb8a5786fd1 --- /dev/null +++ b/clang/test/Index/Store/Inputs/macro-only-guards.h @@ -0,0 +1,6 @@ +#ifndef MACRO_ONLY_GUARDS +#define MACRO_ONLY_GUARDS + +// deliberately empty + +#endif diff --git a/clang/test/Index/Store/Inputs/macro.h b/clang/test/Index/Store/Inputs/macro.h new file mode 100644 index 0000000000000..029cc79a3479e --- /dev/null +++ b/clang/test/Index/Store/Inputs/macro.h @@ -0,0 +1,5 @@ +#ifdef MACRO_1 +#define MACRO_FROM_HEADER 1 +#else +#define MACRO_FROM_HEADER 1 +#endif diff --git a/clang/test/Index/Store/Inputs/module.modulemap b/clang/test/Index/Store/Inputs/module.modulemap new file mode 100644 index 0000000000000..d1bc253232f9e --- /dev/null +++ b/clang/test/Index/Store/Inputs/module.modulemap @@ -0,0 +1,4 @@ +module IndexMacrosMod { + header "IndexMacrosMod.h" + export * +} diff --git a/clang/test/Index/Store/Inputs/module/ModDep.h b/clang/test/Index/Store/Inputs/module/ModDep.h new file mode 100644 index 0000000000000..e96ef5440f43a --- /dev/null +++ b/clang/test/Index/Store/Inputs/module/ModDep.h @@ -0,0 +1,3 @@ +#include "ModTop.h" + +void ModDep_func(ModTopStruct s); diff --git a/clang/test/Index/Store/Inputs/module/ModSystem.h b/clang/test/Index/Store/Inputs/module/ModSystem.h new file mode 100644 index 0000000000000..0419f97804b5d --- /dev/null +++ b/clang/test/Index/Store/Inputs/module/ModSystem.h @@ -0,0 +1,4 @@ + +typedef struct {} ModSystemStruct; + +void ModSystem_func(void); diff --git a/clang/test/Index/Store/Inputs/module/ModTop.h b/clang/test/Index/Store/Inputs/module/ModTop.h new file mode 100644 index 0000000000000..60c56868bbffe --- /dev/null +++ b/clang/test/Index/Store/Inputs/module/ModTop.h @@ -0,0 +1,4 @@ + +typedef struct {} ModTopStruct; + +void ModTop_func(void); diff --git a/clang/test/Index/Store/Inputs/module/ModTopSub1.h b/clang/test/Index/Store/Inputs/module/ModTopSub1.h new file mode 100644 index 0000000000000..e1e3cf3ec54bc --- /dev/null +++ b/clang/test/Index/Store/Inputs/module/ModTopSub1.h @@ -0,0 +1 @@ +void ModTopSub1_func(void); diff --git a/clang/test/Index/Store/Inputs/module/ModTopSub2.h b/clang/test/Index/Store/Inputs/module/ModTopSub2.h new file mode 100644 index 0000000000000..39d37f12f0e1b --- /dev/null +++ b/clang/test/Index/Store/Inputs/module/ModTopSub2.h @@ -0,0 +1 @@ +// This header has no symbols, intended to show up as file dependency. diff --git a/clang/test/Index/Store/Inputs/module/module.modulemap b/clang/test/Index/Store/Inputs/module/module.modulemap new file mode 100644 index 0000000000000..ada2f38ef76e9 --- /dev/null +++ b/clang/test/Index/Store/Inputs/module/module.modulemap @@ -0,0 +1,12 @@ +module ModTop { + header "ModTop.h" + export * + module Sub1 { + header "ModTopSub1.h" + } + module Sub2 { + header "ModTopSub2.h" + } +} +module ModDep { header "ModDep.h" export * } +module ModSystem [system] { header "ModSystem.h" export * } diff --git a/clang/test/Index/Store/Inputs/overlay.yaml b/clang/test/Index/Store/Inputs/overlay.yaml new file mode 100644 index 0000000000000..7b55b30f4bedd --- /dev/null +++ b/clang/test/Index/Store/Inputs/overlay.yaml @@ -0,0 +1,6 @@ +{ + 'version': 0, + 'roots': [{ 'type': 'file', 'name': 'OUT_DIR/using-overlay.h', + 'external-contents': 'INPUT_DIR/using-overlay.h' + }] +} diff --git a/clang/test/Index/Store/Inputs/print-unit.h b/clang/test/Index/Store/Inputs/print-unit.h new file mode 100644 index 0000000000000..62039c47219b5 --- /dev/null +++ b/clang/test/Index/Store/Inputs/print-unit.h @@ -0,0 +1,2 @@ +#include "head.h" +#include "using-overlay.h" diff --git a/clang/test/Index/Store/Inputs/sys/another.h b/clang/test/Index/Store/Inputs/sys/another.h new file mode 100644 index 0000000000000..555b99b0ce36f --- /dev/null +++ b/clang/test/Index/Store/Inputs/sys/another.h @@ -0,0 +1,2 @@ + +extern void sys_another_func(void); diff --git a/clang/test/Index/Store/Inputs/sys/syshead.h b/clang/test/Index/Store/Inputs/sys/syshead.h new file mode 100644 index 0000000000000..8941fd6997af7 --- /dev/null +++ b/clang/test/Index/Store/Inputs/sys/syshead.h @@ -0,0 +1,4 @@ + +#include "another.h" + +extern void sys_test1_func(void); diff --git a/clang/test/Index/Store/Inputs/test1.c b/clang/test/Index/Store/Inputs/test1.c new file mode 100644 index 0000000000000..505711d181d34 --- /dev/null +++ b/clang/test/Index/Store/Inputs/test1.c @@ -0,0 +1,3 @@ +#include "head.h" + +void test1_func(void) {} diff --git a/clang/test/Index/Store/Inputs/test2.c b/clang/test/Index/Store/Inputs/test2.c new file mode 100644 index 0000000000000..333b8aef67d52 --- /dev/null +++ b/clang/test/Index/Store/Inputs/test2.c @@ -0,0 +1,3 @@ +#include "head.h" + +void test2_func(void) {} diff --git a/clang/test/Index/Store/Inputs/test3.cpp b/clang/test/Index/Store/Inputs/test3.cpp new file mode 100644 index 0000000000000..06334a1706b41 --- /dev/null +++ b/clang/test/Index/Store/Inputs/test3.cpp @@ -0,0 +1,2 @@ +class Base {}; +class Sub : public Base {}; diff --git a/clang/test/Index/Store/Inputs/using-overlay.h b/clang/test/Index/Store/Inputs/using-overlay.h new file mode 100644 index 0000000000000..bb361c3d58285 --- /dev/null +++ b/clang/test/Index/Store/Inputs/using-overlay.h @@ -0,0 +1 @@ +void using_overlay(void); diff --git a/clang/test/Index/Store/assembly-invocation.c b/clang/test/Index/Store/assembly-invocation.c new file mode 100644 index 0000000000000..ab9c197a5391b --- /dev/null +++ b/clang/test/Index/Store/assembly-invocation.c @@ -0,0 +1,3 @@ +// Make sure it doesn't crash. +// RUN: %clang -target x86_64-apple-macosx10.7 -S %s -o %t.s +// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -c %t.s -o %t.o diff --git a/clang/test/Index/Store/crash-msguid-hash.cpp b/clang/test/Index/Store/crash-msguid-hash.cpp new file mode 100644 index 0000000000000..ef81e30de8257 --- /dev/null +++ b/clang/test/Index/Store/crash-msguid-hash.cpp @@ -0,0 +1,25 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 %s -std=c++1z -index-store-path %t/idx -fms-extensions +// Should not crash. + +class VirtualBase +{ +public: + VirtualBase() noexcept {} + virtual ~VirtualBase() noexcept {} +}; + +template< class MostDerivedInterface, const _GUID& MostDerivedInterfaceIID = __uuidof( MostDerivedInterface ) > +class TGuidHolder : public VirtualBase +{ +}; + +struct __declspec(uuid("12345678-1234-5678-ABCD-12345678ABCD")) GUIDInterfaceClass {}; + +class Widget : public TGuidHolder< GUIDInterfaceClass > +{ +public: + Widget(); + ~Widget() noexcept {} +}; + diff --git a/clang/test/Index/Store/empty-unit.c b/clang/test/Index/Store/empty-unit.c new file mode 100644 index 0000000000000..6394bc422a824 --- /dev/null +++ b/clang/test/Index/Store/empty-unit.c @@ -0,0 +1,20 @@ +void foo(int i); + +// RUN: rm -rf %t/idx +// RUN: %clang_cc1 -index-store-path %t/idx %s -o %t.o +// RUN: touch %t.empty + +// RUN: cp %t.empty $(find %t/idx -name "empty-unit.c*o*") +// RUN: not c-index-test core -print-unit %t/idx 2> %t.err +// RUN: FileCheck %s -input-file %t.err -check-prefix ERR-UNIT +// ERR-UNIT: error loading unit: empty file + +// Also check for empty record files. +// RUN: rm -rf %t/idx2 +// RUN: %clang_cc1 -index-store-path %t/idx2 %s -o %t.o +// RUN: cp %t.empty $(find %t/idx2 -name "empty-unit.c-*") +// RUN: not c-index-test core -print-record %t/idx2 2> %t2.err +// RUN: FileCheck %s -input-file %t2.err -check-prefix ERR-RECORD +// ERR-RECORD: error loading record: empty file + +// REQUIRES: shell diff --git a/clang/test/Index/Store/external-source-symbol-hash.m b/clang/test/Index/Store/external-source-symbol-hash.m new file mode 100644 index 0000000000000..1b4f89d9251f3 --- /dev/null +++ b/clang/test/Index/Store/external-source-symbol-hash.m @@ -0,0 +1,47 @@ +// RUN: rm -rf %t.idx +// RUN: %clang_cc1 %s -index-store-path %t.idx -D USE_EXTERNAL +// RUN: c-index-test core -print-record %t.idx | FileCheck %s +// RUN: %clang_cc1 %s -index-store-path %t.idx +// RUN: find %t.idx/*/records -name "external-source-symbol-hash*" | count 2 + +#ifdef USE_EXTERNAL +# define EXT_DECL(mod_name) __attribute__((external_source_symbol(language="Swift", defined_in=mod_name))) +#else +# define EXT_DECL(mod_name) +#endif + +#define NS_ENUM(_name, _type) enum _name:_type _name; enum _name : _type + +// Forward declarations should pick up the attribute from later decls +@protocol P1; +// CHECK: [[@LINE-1]]:11 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Ref | rel: 0 +@class I2; +// CHECK: [[@LINE-1]]:8 | class/Swift | c:@M@other_module@objc(cs)I2 | Ref | rel: 0 +enum E3: int; +// CHECK: [[@LINE-1]]:6 | enum/Swift | c:@M@third_module@E@E3 | Decl | rel: 0 + +void test(id first, I2 *second, enum E3 third) {} +// CHECK: [[@LINE-1]]:14 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Ref,RelCont | rel: 1 +// CHECK: [[@LINE-2]]:25 | class/Swift | c:@M@other_module@objc(cs)I2 | Ref,RelCont | rel: 1 +// CHECK: [[@LINE-3]]:42 | enum/Swift | c:@M@third_module@E@E3 | Ref,RelCont | rel: 1 + +EXT_DECL("some_module") +@protocol P1 +// CHECK: [[@LINE-1]]:11 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Decl | rel: 0 +-(void)method; +// CHECK: [[@LINE-1]]:8 | instance-method(protocol)/Swift | c:@M@some_module@objc(pl)P1(im)method | Decl,Dyn,RelChild | rel: 1 +@end + +EXT_DECL("other_module") +@interface I2 +// CHECK: [[@LINE-1]]:12 | class/Swift | c:@M@other_module@objc(cs)I2 | Decl | rel: 0 +-(void)method; +// CHECK: [[@LINE-1]]:8 | instance-method/Swift | c:@M@other_module@objc(cs)I2(im)method | Decl,Dyn,RelChild | rel: 1 +@end + + +typedef NS_ENUM(E3, int) { +// CHECK: [[@LINE-1]]:17 | enum/Swift | c:@M@third_module@E@E3 | Def | rel: 0 + firstCase = 1, + // CHECK: [[@LINE-1]]:3 | enumerator/Swift | c:@M@third_module@E@E3@firstCase | Def,RelChild | rel: 1 +} EXT_DECL("third_module"); diff --git a/clang/test/Index/Store/handle-prebuilt-module.m b/clang/test/Index/Store/handle-prebuilt-module.m new file mode 100644 index 0000000000000..7ed226d94a95d --- /dev/null +++ b/clang/test/Index/Store/handle-prebuilt-module.m @@ -0,0 +1,24 @@ +// REQUIRES: x86-registered-target +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: %clang -target x86_64-apple-macos10.7 -fsyntax-only %s -o %t.o -index-store-path %t/idx1 -fmodules -fmodules-cache-path=%t/mcp -I %S/Inputs/module -Rindex-store 2> %t.err1 +// RUN: %clang -target x86_64-apple-macos10.7 -fsyntax-only %s -o %t.o -index-store-path %t/idx2 -fmodules -fmodules-cache-path=%t/mcp -I %S/Inputs/module -Rindex-store 2> %t.err2 +// RUN: %clang -target x86_64-apple-macos10.7 -fsyntax-only %s -o %t.o -index-store-path %t/idx2 -fmodules -fmodules-cache-path=%t/mcp -I %S/Inputs/module -Rindex-store 2> %t.err3 +// RUN: FileCheck -input-file=%t.err1 -check-prefix=CREATING_MODULES %s -allow-empty +// RUN: FileCheck -input-file=%t.err2 -check-prefix=CREATING_INDEX_DATA_FROM_MODULE_FILES %s +// RUN: FileCheck -input-file=%t.err3 -check-prefix=EXISTING_INDEX_DATA_FROM_MODULE_FILES %s -allow-empty +// RUN: c-index-test core -print-unit %t/idx1 > %t/all-units1.txt +// RUN: c-index-test core -print-unit %t/idx2 > %t/all-units2.txt +// RUN: c-index-test core -print-record %t/idx1 > %t/all-records1.txt +// RUN: c-index-test core -print-record %t/idx2 > %t/all-records2.txt +// RUN: diff -u %t/all-units1.txt %t/all-units2.txt +// RUN: diff -u %t/all-records1.txt %t/all-records2.txt + +@import ModDep; + +// CREATING_MODULES-NOT: remark: + +// CREATING_INDEX_DATA_FROM_MODULE_FILES: remark: producing index data for module file {{.*}}ModDep{{.*}}.pcm +// CREATING_INDEX_DATA_FROM_MODULE_FILES: remark: producing index data for module file {{.*}}ModTop{{.*}}.pcm + +// EXISTING_INDEX_DATA_FROM_MODULE_FILES-NOT: remark: diff --git a/clang/test/Index/Store/index-macros-module-from-pcm.c b/clang/test/Index/Store/index-macros-module-from-pcm.c new file mode 100644 index 0000000000000..5c807fef97b9a --- /dev/null +++ b/clang/test/Index/Store/index-macros-module-from-pcm.c @@ -0,0 +1,14 @@ +// Compile without indexing first, then again with indexing which will index +// the .pcm itself. +// RUN: rm -rf %t-from-module +// RUN: %clang_cc1 %s -I %S/Inputs -fmodules -fimplicit-module-maps -fmodules-cache-path=%t-from-module/mcp +// RUN: %clang_cc1 %s -I %S/Inputs -fmodules -fimplicit-module-maps -fmodules-cache-path=%t-from-module/mcp -index-store-path %t-from-module/idx +// RUN: c-index-test core -print-record %t-from-module/idx | FileCheck %s + +#pragma clang module import IndexMacrosMod +// Note: only the latest definition is found when indexing from the PCM file. + +// CHECK-NOT: INDEXMACROSMODULE_H +// CHECK: macro/C | INDEX_MACROS_MODULE | [[USR2:.*@macro@INDEX_MACROS_MODULE]] | | Def +// CHECK: 6:9 | macro/C | [[USR2]] | Def | +// CHECK-NOT: INDEXMACROSMODULE_H diff --git a/clang/test/Index/Store/index-macros-module.c b/clang/test/Index/Store/index-macros-module.c new file mode 100644 index 0000000000000..16f886b448920 --- /dev/null +++ b/clang/test/Index/Store/index-macros-module.c @@ -0,0 +1,14 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 %s -I %S/Inputs -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/mcp -index-store-path %t/idx +// RUN: c-index-test core -print-record %t/idx | FileCheck %s + +#pragma clang module import IndexMacrosMod +// CHECK: macro/C | INDEX_MACROS_MODULE | [[USR1:.*@macro@INDEX_MACROS_MODULE]] | | Def,Undef - +// CHECK: macro/C | INDEX_MACROS_MODULE | [[USR2:.*@macro@INDEX_MACROS_MODULE]] | | Def,Ref - + +// CHECK-NOT: INDEXMACROSMODULE_H +// CHECK: 4:9 | macro/C | [[USR1]] | Def | +// CHECK: 5:8 | macro/C | [[USR1]] | Undef | +// CHECK: 6:9 | macro/C | [[USR2]] | Def | +// CHECK: 7:9 | macro/C | [[USR2]] | Ref | +// CHECK-NOT: INDEXMACROSMODULE_H diff --git a/clang/test/Index/Store/index-macros.c b/clang/test/Index/Store/index-macros.c new file mode 100644 index 0000000000000..17f26e03872cf --- /dev/null +++ b/clang/test/Index/Store/index-macros.c @@ -0,0 +1,44 @@ +// RUN: rm -rf %t.idx +// RUN: %clang_cc1 %s -index-store-path %t.idx +// RUN: c-index-test core -print-record %t.idx | FileCheck %s + +// RUN: rm -rf %t.idx.ignore +// RUN: %clang_cc1 %s -index-store-path %t.idx.ignore -index-ignore-macros +// RUN: c-index-test core -print-record %t.idx.ignore | FileCheck %s -check-prefix DISABLED +// DISABLED-NOT: macro/C +// DISABLED-NOT: X1 + +// CHECK: macro/C | X1 | [[X1_USR:.*@macro@X1]] | | Def,Ref,Undef - +// CHECK: macro/C | DEF | [[DEF_USR:.*@macro@DEF]] | | Def,Ref - +// CHECK: macro/C | REDEF | [[REDEF_USR1:.*@macro@REDEF]] | | Def,Undef - +// CHECK: macro/C | REDEF | [[REDEF_USR2:.*@macro@REDEF]] | | Def - + +// CHECK: [[@LINE+1]]:9 | macro/C | [[X1_USR]] | Def | +#define X1 1 +// CHECK: [[@LINE+1]]:9 | macro/C | [[DEF_USR]] | Def | +#define DEF(x) int x + +// CHECK: [[@LINE+1]]:8 | macro/C | [[X1_USR]] | Ref +#ifdef X1 +#endif + +// CHECK: [[@LINE+1]]:8 | macro/C | [[X1_USR]] | Undef | +#undef X1 + +// CHECK: [[@LINE+1]]:9 | macro/C | [[REDEF_USR1]] | Def | +#define REDEF +// CHECK: [[@LINE+1]]:8 | macro/C | [[REDEF_USR1]] | Undef | +#undef REDEF +// CHECK: [[@LINE+1]]:9 | macro/C | [[REDEF_USR2]] | Def | +#define REDEF + +// FIXME: index references to builtin macros. Adding a test since this was +// crashing at one point. +// CHECK-NOT: [[@LINE+1]]:5 | macro/C | __LINE__ +#if __LINE__ == 41 +#endif + +// Macro references currently not supported. +// CHECK: [[@LINE+2]]:1 | macro/C | [[DEF_USR]] | Ref | rel: 0 +// CHECK: [[@LINE+1]]:5 | variable/C | c:@i | Def | +DEF(i); diff --git a/clang/test/Index/Store/json-with-module.m b/clang/test/Index/Store/json-with-module.m new file mode 100644 index 0000000000000..778b5e0e67c84 --- /dev/null +++ b/clang/test/Index/Store/json-with-module.m @@ -0,0 +1,9 @@ +// RUN: rm -rf %t.idx %t.mcp +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -fmodules -fmodules-cache-path=%t.mcp -Xclang -fdisable-module-hash -I %S/Inputs/module +// RUN: c-index-test core -aggregate-json %t.idx -o %t.json +// RUN: sed -e "s:%S::g" -e "s:%T::g" %t.json > %t.final.json +// RUN: diff -u %s.json %t.final.json + +@import ModDep; + +// REQUIRES: shell diff --git a/clang/test/Index/Store/json-with-module.m.json b/clang/test/Index/Store/json-with-module.m.json new file mode 100644 index 0000000000000..5c9bf3a397243 --- /dev/null +++ b/clang/test/Index/Store/json-with-module.m.json @@ -0,0 +1,151 @@ +{ + "files": [ + "/json-with-module.m.tmp.mcp/ModDep.pcm", + "/json-with-module.m.tmp.mcp/ModTop.pcm", + "/Inputs/module/ModDep.h", + "/Inputs/module/ModTop.h", + "/Inputs/module/ModTopSub1.h", + "/Inputs/module/ModTopSub2.h", + "/json-with-module.m.tmp.o", + "/json-with-module.m", + "/Inputs/module/module.modulemap" + ], + "symbols": [ + { + "kind": "function", + "lang": "C", + "usr": "c:@F@ModDep_func", + "name": "ModDep_func", + "roles": "Decl", + "rel-roles": "RelCont" + }, + { + "kind": "type-alias", + "lang": "C", + "usr": "c:@T@ModTopStruct", + "name": "ModTopStruct", + "roles": "Def,Ref,RelCont" + }, + { + "kind": "struct", + "lang": "C", + "usr": "c:@SA@ModTopStruct", + "name": "", + "roles": "Def" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@ModTop_func", + "name": "ModTop_func", + "roles": "Decl" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@ModTopSub1_func", + "name": "ModTopSub1_func", + "roles": "Decl" + } + ], + "records": [ + { + "occurrences": [ + { + "symbol": 0, + "line": 3, + "col": 6, + "roles": "Decl" + }, + { + "symbol": 1, + "line": 3, + "col": 18, + "roles": "Ref,RelCont", + "relations": [ + { + "symbol": 0, + "rel-roles": "RelCont" + } + ] + + } + ] + }, + { + "occurrences": [ + { + "symbol": 2, + "line": 2, + "col": 9, + "roles": "Def" + }, + { + "symbol": 1, + "line": 2, + "col": 19, + "roles": "Def" + }, + { + "symbol": 3, + "line": 4, + "col": 6, + "roles": "Decl" + } + ] + }, + { + "occurrences": [ + { + "symbol": 4, + "line": 1, + "col": 6, + "roles": "Decl" + } + ] + } + ], + "units": [ + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 0, + "unit-dependencies": [1], + "sources": [ + { + "file": 2, + "records": [0] + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 1, + "sources": [ + { + "file": 3, + "records": [1] + }, + { + "file": 4, + "records": [2] + }, + { + "file": 5 + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 6, + "unit-dependencies": [0], + "sources": [ + { + "file": 7 + }, + { + "file": 8 + } + ] + } + ] +} diff --git a/clang/test/Index/Store/json-with-pch.c b/clang/test/Index/Store/json-with-pch.c new file mode 100644 index 0000000000000..6524d3368a47a --- /dev/null +++ b/clang/test/Index/Store/json-with-pch.c @@ -0,0 +1,12 @@ +// RUN: rm -rf %t.idx +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -x c-header %S/Inputs/head.h -o %t.h.pch -index-store-path %t.idx +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -include %t.h -Werror +// RUN: c-index-test core -aggregate-json %t.idx -o %t.json +// RUN: sed -e "s:%S::g" -e "s:%T::g" %t.json > %t.final.json +// RUN: diff -u %s.json %t.final.json + +int main() { + test1_func(); +} + +// REQUIRES: shell diff --git a/clang/test/Index/Store/json-with-pch.c.json b/clang/test/Index/Store/json-with-pch.c.json new file mode 100644 index 0000000000000..605f33efd9573 --- /dev/null +++ b/clang/test/Index/Store/json-with-pch.c.json @@ -0,0 +1,96 @@ +{ + "files": [ + "/json-with-pch.c.tmp.h.pch", + "/Inputs/head.h", + "/json-with-pch.c.tmp.o", + "/json-with-pch.c" + ], + "symbols": [ + { + "kind": "function", + "lang": "C", + "usr": "c:@F@test1_func", + "name": "test1_func", + "roles": "Decl,Ref,Call,RelCall,RelCont" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@test2_func", + "name": "test2_func", + "roles": "Decl" + }, + { + "kind": "function", + "lang": "C", + "usr": "c:@F@main", + "name": "main", + "roles": "Def", + "rel-roles": "RelCall,RelCont" + } + ], + "records": [ + { + "occurrences": [ + { + "symbol": 0, + "line": 2, + "col": 13, + "roles": "Decl" + }, + { + "symbol": 1, + "line": 3, + "col": 13, + "roles": "Decl" + } + ] + }, + { + "occurrences": [ + { + "symbol": 2, + "line": 8, + "col": 5, + "roles": "Def" + }, + { + "symbol": 0, + "line": 9, + "col": 3, + "roles": "Ref,Call,RelCall,RelCont", + "relations": [ + { + "symbol": 2, + "rel-roles": "RelCall,RelCont" + } + ] + + } + ] + } + ], + "units": [ + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 0, + "sources": [ + { + "file": 1, + "records": [0] + } + ] + }, + { + "triple": "x86_64-apple-macosx10.7.0", + "out-file": 2, + "unit-dependencies": [0], + "sources": [ + { + "file": 3, + "records": [1] + } + ] + } + ] +} diff --git a/clang/test/Index/Store/json.c b/clang/test/Index/Store/json.c new file mode 100644 index 0000000000000..97cc675679670 --- /dev/null +++ b/clang/test/Index/Store/json.c @@ -0,0 +1,10 @@ +// RUN: rm -rf %t.idx +// RUN: mkdir -p %t.o +// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -arch x86_64 -mmacosx-version-min=10.7 -c %S/Inputs/test1.c -o %t.o/test1.o +// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -arch x86_64 -mmacosx-version-min=10.7 -c %S/Inputs/test2.c -o %t.o/test2.o +// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -arch x86_64 -mmacosx-version-min=10.7 -c %S/Inputs/test3.cpp -o %t.o/test3.o +// RUN: c-index-test core -aggregate-json %t.idx -o %t.json +// RUN: sed -e "s:%S::g" -e "s:%t.o::g" %t.json > %t.final.json +// RUN: diff -u %S/Inputs/json.c.json %t.final.json + +// REQUIRES: shell diff --git a/clang/test/Index/Store/print-record.mm b/clang/test/Index/Store/print-record.mm new file mode 100644 index 0000000000000..cbca273481968 --- /dev/null +++ b/clang/test/Index/Store/print-record.mm @@ -0,0 +1,28 @@ +// RUN: rm -rf %t.idx +// RUN: %clang_cc1 %s -index-store-path %t.idx +// RUN: c-index-test core -print-record %t.idx | FileCheck %s + + + +@class MyCls; + +@interface MyCls +@end + +// CHECK: [[@LINE+2]]:6 | function/C | c:@F@foo#*$objc(cs)MyCls# | Decl | rel: 0 +// CHECK: [[@LINE+1]]:10 | class/ObjC | c:objc(cs)MyCls | Ref,RelCont | rel: 1 +void foo(MyCls *p); + + +// RANGE-NOT: before_range +void before_range(); + +// RANGE: [[@LINE+1]]:6 | function/C | c:@F@in_range1# | Decl +void in_range1(); +// RANGE: [[@LINE+1]]:6 | function/C | c:@F@in_range2# | Decl +void in_range2(); + +// RANGE-NOT: after_range +void after_range(); + +// RUN: c-index-test core -print-record %t.idx -filepath %s:21:23 | FileCheck -check-prefix=RANGE %s diff --git a/clang/test/Index/Store/print-unit-remapped-hash.c b/clang/test/Index/Store/print-unit-remapped-hash.c new file mode 100644 index 0000000000000..1ffe1e974c591 --- /dev/null +++ b/clang/test/Index/Store/print-unit-remapped-hash.c @@ -0,0 +1,22 @@ +// UNSUPPORTED: system-windows + +#include "print-unit.h" +#include "syshead.h" + +void foo(int i); + +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: cd %t && %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -fdebug-prefix-map=%S/Inputs=INPUT_ROOT -fdebug-prefix-map=%S=SRC_ROOT -fdebug-prefix-map=%t=BUILD_ROOT -index-store-path %t/idx %S/print-unit-remapped.c -triple x86_64-apple-macosx10.8 +// RUN: c-index-test core -print-unit %t/idx > %t/units.out + +// Relative paths should work as well - the unit name, main-path, and out-file should not change. +// RUN: rm -rf %t/idx +// RUN: cd %S && %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -fdebug-prefix-map=%S=SRC_ROOT -index-store-path %t/idx print-unit-remapped.c -o print-unit-remapped.c.o -triple x86_64-apple-macosx10.8 +// RUN: c-index-test core -print-unit %t/idx >> %t/units.out + +// RUN: FileCheck %s -input-file %t/units.out + +// CHECK: print-unit-remapped.c.o-[[OUT_HASH:.*$]] +// CHECK: print-unit-remapped.c.o-[[OUT_HASH]] +// CHECK-NOT: print-unit-remapped.c.o- diff --git a/clang/test/Index/Store/print-unit-remapped.c b/clang/test/Index/Store/print-unit-remapped.c new file mode 100644 index 0000000000000..1e5ef6687b80a --- /dev/null +++ b/clang/test/Index/Store/print-unit-remapped.c @@ -0,0 +1,51 @@ +#include "print-unit.h" +#include "syshead.h" + +// XFAIL: !rdar108885594 + +void foo(int i); + +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: cd %t && %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -fdebug-prefix-map=%S/Inputs=INPUT_ROOT -fdebug-prefix-map=%S=SRC_ROOT -fdebug-prefix-map=%t=BUILD_ROOT -index-store-path %t/idx %s -triple x86_64-apple-macosx10.8 +// RUN: c-index-test core -print-unit %t/idx | FileCheck --check-prefixes=ABSOLUTE,ALL %s + +// Relative paths should work as well - the unit name, main-path, and out-file should not change. +// RUN: rm -rf %t +// RUN: cd %S && %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -fdebug-prefix-map=%S=SRC_ROOT -index-store-path %t/idx print-unit-remapped.c -o print-unit-remapped.c.o -triple x86_64-apple-macosx10.8 +// RUN: c-index-test core -print-unit %t/idx | FileCheck --check-prefixes=RELATIVE,ALL %s + +// ABSOLUTE: print-unit-remapped.c.o-{{.+}} +// RELATIVE: print-unit-remapped.c.o-{{.+}} +// ALL: provider: clang- +// ALL: is-system: 0 +// ALL: has-main: 1 +// ALL: main-path: SRC_ROOT{{/|\\}}print-unit-remapped.c +// ABSOLUTE: work-dir: BUILD_ROOT +// RELATIVE: work-dir: SRC_ROOT +// ALL: out-file: SRC_ROOT{{/|\\}}print-unit-remapped.c.o +// ALL: target: x86_64-apple-macosx10.8 +// ALL: is-debug: 1 +// ALL: DEPEND START +// ALL: Record | user | SRC_ROOT{{/|\\}}print-unit-remapped.c | print-unit-remapped.c- +// ABSOLUTE: Record | user | INPUT_ROOT{{/|\\}}head.h | head.h- +// ABSOLUTE: Record | user | INPUT_ROOT{{/|\\}}using-overlay.h | using-overlay.h- +// ABSOLUTE: Record | system | INPUT_ROOT{{/|\\}}sys{{/|\\}}syshead.h | syshead.h- +// ABSOLUTE: Record | system | INPUT_ROOT{{/|\\}}sys{{/|\\}}another.h | another.h- +// ABSOLUTE: File | user | INPUT_ROOT{{/|\\}}print-unit.h{{$}} +// RELATIVE: Record | user | SRC_ROOT{{/|\\}}Inputs{{/|\\}}head.h | head.h- +// RELATIVE: Record | user | SRC_ROOT{{/|\\}}Inputs{{/|\\}}using-overlay.h | using-overlay.h- +// RELATIVE: Record | system | SRC_ROOT{{/|\\}}Inputs{{/|\\}}sys{{/|\\}}syshead.h | syshead.h- +// RELATIVE: Record | system | SRC_ROOT{{/|\\}}Inputs{{/|\\}}sys{{/|\\}}another.h | another.h- +// RELATIVE: File | user | SRC_ROOT{{/|\\}}Inputs{{/|\\}}print-unit.h{{$}} +// ALL: DEPEND END (6) +// ALL: INCLUDE START +// ABSOLUTE: SRC_ROOT{{/|\\}}print-unit-remapped.c:1 | INPUT_ROOT{{/|\\}}print-unit.h +// ABSOLUTE: SRC_ROOT{{/|\\}}print-unit-remapped.c:2 | INPUT_ROOT{{/|\\}}sys{{/|\\}}syshead.h +// ABSOLUTE: INPUT_ROOT{{/|\\}}print-unit.h:1 | INPUT_ROOT{{/|\\}}head.h +// ABSOLUTE: INPUT_ROOT{{/|\\}}print-unit.h:2 | INPUT_ROOT{{/|\\}}using-overlay.h +// RELATIVE: SRC_ROOT{{/|\\}}print-unit-remapped.c:1 | SRC_ROOT{{/|\\}}Inputs{{/|\\}}print-unit.h +// RELATIVE: SRC_ROOT{{/|\\}}print-unit-remapped.c:2 | SRC_ROOT{{/|\\}}Inputs{{/|\\}}sys{{/|\\}}syshead.h +// RELATIVE: SRC_ROOT{{/|\\}}Inputs{{/|\\}}print-unit.h:1 | SRC_ROOT{{/|\\}}Inputs{{/|\\}}head.h +// RELATIVE: SRC_ROOT{{/|\\}}Inputs{{/|\\}}print-unit.h:2 | SRC_ROOT{{/|\\}}Inputs{{/|\\}}using-overlay.h +// ALL: INCLUDE END (4) diff --git a/clang/test/Index/Store/print-unit-roundtrip-remapping.c b/clang/test/Index/Store/print-unit-roundtrip-remapping.c new file mode 100644 index 0000000000000..391684cc9ec98 --- /dev/null +++ b/clang/test/Index/Store/print-unit-roundtrip-remapping.c @@ -0,0 +1,44 @@ +#include "print-unit.h" +#include "syshead.h" + +void foo(int i); + +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: cd %t && %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -fdebug-prefix-map=%S=SRC_ROOT -fdebug-prefix-map=%t=BUILD_ROOT -index-store-path %t/idx %s -triple x86_64-apple-macosx10.8 +// RUN: c-index-test core -print-unit %t/idx -index-store-prefix-map SRC_ROOT=%S -index-store-prefix-map BUILD_ROOT=$PWD | FileCheck %s + +// CHECK: print-unit-roundtrip-remapping.c.o-{{.+}} +// CHECK: provider: clang- +// CHECK: is-system: 0 +// CHECK: has-main: 1 +// CHECK-NOT: main-path: SRC_ROOT{{/|\\}}print-unit-roundtrip-remapping.c +// CHECK: main-path: {{.*}}{{/|\\}}print-unit-roundtrip-remapping.c +// CHECK-NOT: work-dir: BUILD_ROOT +// CHECK: out-file: SRC_ROOT{{/|\\}}print-unit-roundtrip-remapping.c.o +// CHECK: target: x86_64-apple-macosx10.8 +// CHECK: is-debug: 1 +// CHECK: DEPEND START +// CHECK-NOT: Record | user | SRC_ROOT{{/|\\}}print-unit-roundtrip-remapping.c | print-unit-roundtrip-remapping.c- +// CHECK: Record | user | {{.*}}{{/|\\}}print-unit-roundtrip-remapping.c | print-unit-roundtrip-remapping.c- +// CHECK-NOT: Record | user | SRC_ROOT{{/|\\}}Inputs{{/|\\}}head.h | head.h- +// CHECK: Record | user | {{.*}}{{/|\\}}Inputs{{/|\\}}head.h | head.h- +// CHECK-NOT: Record | user | SRC_ROOT{{/|\\}}Inputs{{/|\\}}using-overlay.h | using-overlay.h- +// CHECK: Record | user | {{.*}}{{/|\\}}Inputs{{/|\\}}using-overlay.h | using-overlay.h- +// CHECK-NOT: Record | system | SRC_ROOT{{/|\\}}Inputs{{/|\\}}sys{{/|\\}}syshead.h | syshead.h- +// CHECK: Record | system | {{.*}}{{/|\\}}Inputs{{/|\\}}sys{{/|\\}}syshead.h | syshead.h- +// CHECK-NOT: Record | system | SRC_ROOT{{/|\\}}Inputs{{/|\\}}sys{{/|\\}}another.h | another.h- +// CHECK: Record | system | {{.*}}{{/|\\}}Inputs{{/|\\}}sys{{/|\\}}another.h | another.h- +// CHECK-NOT: File | user | SRC_ROOT{{/|\\}}Inputs{{/|\\}}print-unit.h{{$}} +// CHECK: File | user | {{.*}}{{/|\\}}Inputs{{/|\\}}print-unit.h{{$}} +// CHECK: DEPEND END (6) +// CHECK: INCLUDE START +// CHECK-NOT: SRC_ROOT{{/|\\}}print-unit-roundtrip-remapping.c:1 | SRC_ROOT{{/|\\}}Inputs{{/|\\}}print-unit.h +// CHECK: {{.*}}{{/|\\}}print-unit-roundtrip-remapping.c:1 | {{.*}}{{/|\\}}Inputs{{/|\\}}print-unit.h +// CHECK-NOT: SRC_ROOT{{/|\\}}print-unit-roundtrip-remapping.c:2 | SRC_ROOT{{/|\\}}Inputs{{/|\\}}sys{{/|\\}}syshead.h +// CHECK: {{.*}}{{/|\\}}print-unit-roundtrip-remapping.c:2 | {{.*}}{{/|\\}}Inputs{{/|\\}}sys{{/|\\}}syshead.h +// CHECK-NOT: SRC_ROOT{{/|\\}}Inputs{{/|\\}}print-unit.h:1 | SRC_ROOT{{/|\\}}Inputs{{/|\\}}head.h +// CHECK: {{.*}}{{/|\\}}Inputs{{/|\\}}print-unit.h:1 | {{.*}}{{/|\\}}Inputs{{/|\\}}head.h +// CHECK-NOT: SRC_ROOT{{/|\\}}Inputs{{/|\\}}print-unit.h:2 | SRC_ROOT{{/|\\}}Inputs{{/|\\}}using-overlay.h +// CHECK: {{.*}}{{/|\\}}Inputs{{/|\\}}print-unit.h:2 | {{.*}}{{/|\\}}Inputs{{/|\\}}using-overlay.h +// CHECK: INCLUDE END (4) diff --git a/clang/test/Index/Store/print-unit.c b/clang/test/Index/Store/print-unit.c new file mode 100644 index 0000000000000..933905acc21f7 --- /dev/null +++ b/clang/test/Index/Store/print-unit.c @@ -0,0 +1,39 @@ + + +#include "print-unit.h" +#include "syshead.h" + +void foo(int i); + +// RUN: rm -rf %t +// RUN: %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -index-store-path %t/idx %s -triple x86_64-apple-macosx10.8 +// RUN: c-index-test core -print-unit %t/idx | FileCheck %s +// RUN: %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -index-store-path %t/idx_opt1 %s -triple x86_64-apple-macosx10.8 -O2 +// RUN: c-index-test core -print-unit %t/idx_opt1 | FileCheck %s -check-prefix=OPT +// RUN: %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -index-store-path %t/idx_opt2 %s -triple x86_64-apple-macosx10.8 -Os +// RUN: c-index-test core -print-unit %t/idx_opt2 | FileCheck %s -check-prefix=OPT + +// CHECK: print-unit.c.o +// CHECK: provider: clang- +// CHECK: is-system: 0 +// CHECK: has-main: 1 +// CHECK: main-path: {{.*}}{{/|\\}}print-unit.c +// CHECK: out-file: {{.*}}{{/|\\}}print-unit.c.o +// CHECK: target: x86_64-apple-macosx10.8 +// CHECK: is-debug: 1 +// CHECK: DEPEND START +// CHECK: Record | user | {{.*}}{{/|\\}}print-unit.c | print-unit.c- +// CHECK: Record | user | {{.*}}{{/|\\}}Inputs{{/|\\}}head.h | head.h- +// CHECK: Record | user | {{.*}}{{/|\\}}Inputs{{/|\\}}using-overlay.h | using-overlay.h- +// CHECK: Record | system | {{.*}}{{/|\\}}Inputs{{/|\\}}sys{{/|\\}}syshead.h | syshead.h- +// CHECK: Record | system | {{.*}}{{/|\\}}Inputs{{/|\\}}sys{{/|\\}}another.h | another.h- +// CHECK: File | user | {{.*}}{{/|\\}}Inputs{{/|\\}}print-unit.h{{$}} +// CHECK: DEPEND END (6) +// CHECK: INCLUDE START +// CHECK: {{.*}}{{/|\\}}print-unit.c:3 | {{.*}}{{/|\\}}Inputs{{/|\\}}print-unit.h +// CHECK: {{.*}}{{/|\\}}print-unit.c:4 | {{.*}}{{/|\\}}Inputs{{/|\\}}sys{{/|\\}}syshead.h +// CHECK: {{.*}}{{/|\\}}Inputs{{/|\\}}print-unit.h:1 | {{.*}}{{/|\\}}Inputs{{/|\\}}head.h +// CHECK: {{.*}}{{/|\\}}Inputs{{/|\\}}print-unit.h:2 | {{.*}}{{/|\\}}Inputs{{/|\\}}using-overlay.h +// CHECK: INCLUDE END (4) + +// OPT: is-debug: 0 diff --git a/clang/test/Index/Store/print-units-explicit-modules-ignore-pcms.m b/clang/test/Index/Store/print-units-explicit-modules-ignore-pcms.m new file mode 100644 index 0000000000000..f6fd5c594cb48 --- /dev/null +++ b/clang/test/Index/Store/print-units-explicit-modules-ignore-pcms.m @@ -0,0 +1,41 @@ +// RUN: rm -rf %t %t.idxdep %t.idxignore %t.idx %t.mcp %t.o + +// ------------------ Build explicit PCM DependencyA, indexing PCM flag does not disable indexing + +// RUN: %clang_cc1 -x objective-c -std=gnu11 -triple x86_64-apple-macosx10.8 \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t.mcp -fdisable-module-hash \ +// RUN: -fmodule-name=DependencyA -emit-module %S/Inputs/explicit-modules/module.modulemap -o %t/DependencyA.pcm \ +// RUN: -index-store-path %t.idxdep -index-ignore-pcms +// RUN: c-index-test core -print-unit %t.idxdep | FileCheck %s --check-prefixes=DEPA + +// ------------------ Build without indexing imported PCMs + +// RUN: %clang_cc1 -std=gnu11 -triple x86_64-apple-macosx10.8 \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t.mcp -fdisable-module-hash \ +// RUN: -fmodule-file=%t/DependencyA.pcm %s -o %t.o \ +// RUN: -index-store-path %t.idxignore -index-ignore-pcms +// RUN: c-index-test core -print-unit %t.idxignore | FileCheck %s --check-prefixes=MAIN,IGNORE + +// ------------------ Build with indexing of imported PCMs + +// RUN: %clang_cc1 -std=gnu11 -triple x86_64-apple-macosx10.8 \ +// RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t.mcp -fdisable-module-hash \ +// RUN: -fmodule-file=%t/DependencyA.pcm %s -o %t.o \ +// RUN: -index-store-path %t.idx +// RUN: c-index-test core -print-unit %t.idx | FileCheck %s --check-prefixes=DEPA,MAIN,INDEXMAIN + +@import DependencyA; + +int fetchDependencyAVersion() { + return dependencyAVersion(); +} + +// IGNORE-NOT: DependencyA.pcm +// DEPA: DependencyA.pcm +// DEPA: module-name: DependencyA + +// MAIN: print-units-explicit-modules-ignore-pcms.m.tmp.o +// MAIN: DEPEND START +// INDEXMAIN: Unit | user | DependencyA | {{.*}}{{/|\\}}DependencyA.pcm | DependencyA.pcm +// IGNORE-NOT: Unit | user | DependencyA | {{.*}}{{/|\\}}DependencyA.pcm | DependencyA.pcm +// IGNORE: Unit | user | DependencyA | {{.*}}{{/|\\}}DependencyA.pcm diff --git a/clang/test/Index/Store/print-units-with-modules.m b/clang/test/Index/Store/print-units-with-modules.m new file mode 100644 index 0000000000000..38b5c2a0cb6e5 --- /dev/null +++ b/clang/test/Index/Store/print-units-with-modules.m @@ -0,0 +1,58 @@ +// REQUIRES: x86-registered-target +// RUN: rm -rf %t.idx %t.mcp +// RUN: %clang -target x86_64-apple-macos10.7 -c %s -o %t.o -index-store-path %t.idx -fmodules -fmodules-cache-path=%t.mcp -Xclang -fdisable-module-hash -I %S/Inputs/module +// RUN: c-index-test core -print-unit %t.idx | FileCheck %s + +@import ModDep; +@import ModSystem; + +// CHECK: ModDep.pcm +// CHECK: provider: clang- +// CHECK: is-system: 0 +// CHECK: is-module: 1 +// CHECK: module-name: ModDep +// CHECK: has-main: 0 +// CHECK: main-path: {{$}} +// CHECK: out-file: {{.*}}{{/|\\}}ModDep.pcm +// CHECK: DEPEND START +// CHECK: Unit | user | ModTop | {{.*}}{{/|\\}}ModTop.pcm | ModTop.pcm +// CHECK: Record | user | ModDep | {{.*}}{{/|\\}}Inputs{{/|\\}}module{{/|\\}}ModDep.h | ModDep.h +// CHECK: DEPEND END (2) + +// CHECK: ModSystem.pcm +// CHECK: is-system: 1 +// CHECK: is-module: 1 +// CHECK: module-name: ModSystem +// CHECK: has-main: 0 +// CHECK: main-path: {{$}} +// CHECK: out-file: {{.*}}{{/|\\}}ModSystem.pcm +// CHECK: DEPEND START +// CHECK: Record | system | ModSystem | {{.*}}{{/|\\}}Inputs{{/|\\}}module{{/|\\}}ModSystem.h | ModSystem.h +// CHECK: DEPEND END (1) + +// CHECK: ModTop.pcm +// CHECK: is-system: 0 +// CHECK: is-module: 1 +// CHECK: module-name: ModTop +// CHECK: has-main: 0 +// CHECK: main-path: {{$}} +// CHECK: out-file: {{.*}}{{/|\\}}ModTop.pcm +// CHECK: DEPEND START +// CHECK: Record | user | ModTop | {{.*}}{{/|\\}}Inputs{{/|\\}}module{{/|\\}}ModTop.h | ModTop.h +// CHECK: Record | user | ModTop.Sub1 | {{.*}}{{/|\\}}Inputs{{/|\\}}module{{/|\\}}ModTopSub1.h | ModTopSub1.h +// CHECK: File | user | ModTop.Sub2 | {{.*}}{{/|\\}}Inputs{{/|\\}}module{{/|\\}}ModTopSub2.h{{$}} +// CHECK: DEPEND END (3) + +// CHECK: print-units-with-modules.m.tmp.o +// CHECK: is-system: 0 +// CHECK: is-module: 0 +// CHECK: module-name: +// CHECK: has-main: 1 +// CHECK: main-path: {{.*}}{{/|\\}}print-units-with-modules.m +// CHECK: out-file: {{.*}}{{/|\\}}print-units-with-modules.m.tmp.o +// CHECK: DEPEND START +// CHECK: Unit | user | ModDep | {{.*}}{{/|\\}}ModDep.pcm | ModDep.pcm +// CHECK: Unit | system | ModSystem | {{.*}}{{/|\\}}ModSystem.pcm | ModSystem.pcm +// CHECK: File | user | {{.*}}{{/|\\}}print-units-with-modules.m{{$}} +// CHECK: File | user | {{.*}}{{/|\\}}Inputs{{/|\\}}module{{/|\\}}module.modulemap{{$}} +// CHECK: DEPEND END (4) diff --git a/clang/test/Index/Store/print-units-with-pch.c b/clang/test/Index/Store/print-units-with-pch.c new file mode 100644 index 0000000000000..94c46bd601df0 --- /dev/null +++ b/clang/test/Index/Store/print-units-with-pch.c @@ -0,0 +1,27 @@ +// RUN: rm -rf %t.idx +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -x c-header %S/Inputs/head.h -o %t.h.pch -index-store-path %t.idx +// RUN: %clang -target x86_64-apple-darwin -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -include %t.h -Werror +// RUN: c-index-test core -print-unit %t.idx | FileCheck %s + +int main() { + test1_func(); +} + +// CHECK: print-units-with-pch.c.tmp.h.pch +// CHECK: is-system: 0 +// CHECK: has-main: 0 +// CHECK: main-path: {{$}} +// CHECK: out-file: {{.*}}{{/|\\}}print-units-with-pch.c.tmp.h.pch +// CHECK: DEPEND START +// CHECK: Record | user | {{.*}}{{/|\\}}Inputs{{/|\\}}head.h | head.h +// CHECK: DEPEND END (1) + +// CHECK: print-units-with-pch.c.tmp.o +// CHECK: is-system: 0 +// CHECK: has-main: 1 +// CHECK: main-path: {{.*}}{{/|\\}}print-units-with-pch.c +// CHECK: out-file: {{.*}}{{/|\\}}print-units-with-pch.c.tmp.o +// CHECK: DEPEND START +// CHECK: Unit | user | {{.*}}{{/|\\}}print-units-with-pch.c.tmp.h.pch | print-units-with-pch.c.tmp.h.pch +// CHECK: Record | user | {{.*}}{{/|\\}}print-units-with-pch.c | print-units-with-pch.c +// CHECK: DEPEND END (2) diff --git a/clang/test/Index/Store/record-hash-crash-invalid-name.cpp b/clang/test/Index/Store/record-hash-crash-invalid-name.cpp new file mode 100644 index 0000000000000..5c049567de024 --- /dev/null +++ b/clang/test/Index/Store/record-hash-crash-invalid-name.cpp @@ -0,0 +1,15 @@ +// Makes sure it doesn't crash. + +// RUN: rm -rf %t +// RUN: not %clang_cc1 %s -index-store-path %t/idx -std=c++14 +// RUN: c-index-test core -print-record %t/idx | FileCheck %s + +namespace rdar32474406 { +void foo(); +typedef void (*Func_t)(); +// CHECK: [[@LINE+4]]:1 | type-alias/C | c:record-hash-crash-invalid-name.cpp@N@rdar32474406@T@Func_t | Ref,RelCont | rel: 1 +// CHECK-NEXT: RelCont | c:@N@rdar32474406 +// CHECK: [[@LINE+2]]:14 | function/C | c:@N@rdar32474406@F@foo# | Ref,RelCont | rel: 1 +// CHECK-NEXT: RelCont | c:@N@rdar32474406 +Func_t[] = { foo }; // invalid decomposition +} diff --git a/clang/test/Index/Store/record-hash-crash.cpp b/clang/test/Index/Store/record-hash-crash.cpp new file mode 100644 index 0000000000000..68501edfef9d4 --- /dev/null +++ b/clang/test/Index/Store/record-hash-crash.cpp @@ -0,0 +1,29 @@ +// Makes sure it doesn't crash. + +// RUN: rm -rf %t +// RUN: %clang_cc1 %s -index-store-path %t/idx -std=c++14 +// RUN: c-index-test core -print-record %t/idx | FileCheck %s + +namespace crash1 { +// CHECK: [[@LINE+1]]:6 | function/C +auto getit() { return []() {}; } +} + +namespace crash2 { +// CHECK: [[@LINE+2]]:7 | class(Gen)/C++ | c:@N@crash2@ST>1#T@Foo | Decl,RelChild | rel: 1 +template +class Foo; // canonical decl + +// CHECK: [[@LINE+2]]:7 | class(Gen)/C++ | c:@N@crash2@ST>1#T@Foo | Def,RelChild | rel: 1 +template +class Foo {}; + +// CHECK: [[@LINE+2]]:8 | struct(Gen)/C++ | c:@N@crash2@ST>1#t>1#pT@Wrapper | Def,RelChild | rel: 1 +template